pax_global_header00006660000000000000000000000064146360423550014522gustar00rootroot0000000000000052 comment=cb8ff67d983bdee23f690851277e9d4bf10171d4 jfree-jfreechart-cb8ff67/000077500000000000000000000000001463604235500154375ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/.gitignore000066400000000000000000000002031463604235500174220ustar00rootroot00000000000000/nbproject/private/ /target/ /javadoc/ # Eclipse files # /.classpath /.project /.settings # Intellij Idea .idea *.iml *.ipr *.iwsjfree-jfreechart-cb8ff67/README.md000066400000000000000000002072761463604235500167340ustar00rootroot00000000000000JFreeChart ========== Version 1.5.5, 23 June 2024. [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.jfree/jfreechart/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.jfree/jfreechart) Overview -------- JFreeChart is a comprehensive free chart library for the Java(tm) platform that can be used on the client-side (JavaFX and Swing) or the server side (with export to multiple formats including SVG, PNG and PDF). ![JFreeChart sample](http://jfree.org/jfreechart/images/coffee_prices.png) The home page for the project is: http://www.jfree.org/jfreechart JFreeChart requires JDK 8 or later. If JavaFX support is required, you need to also include the JFreeChart-FX extensions: https://github.com/jfree/jfreechart-fx The library is licensed under the terms of the GNU Lesser General Public License (LGPL) version 2.1 or later. Using JFreeChart ---------------- To use JFreeChart in your projects, add the following dependency to your build tool: org.jfree jfreechart 1.5.5 Building JFreeChart ------------------- You can build JFreeChart using Maven with the following command (issued from the root directory of the project): mvn clean verify The build requires JDK 8 or later. Demos ----- A small set of demo applications can be found in the following projects here at GitHub: * [JFree-Demos](https://github.com/jfree/jfree-demos "JFree-Demos Project Page at GitHub") * [JFree-FXDemos](https://github.com/jfree/jfree-fxdemos "JFree-FXDemos Project Page at GitHub") History ------- ##### Version 1.5.5 (23 June 2024) - added `XYBezierRenderer` by Javier Robes ([#286](https://github.com/jfree/jfreechart/pull/286)) - fix cross-hair painting ([#340](https://github.com/jfree/jfreechart/issues/340)) - parameter range check for `CompassPlot` ([#397](https://github.com/jfree/jfreechart/pull/397)) Note: some (supposed) security vulnerabilities have been reported for v1.5.4: - [CVE-2023-52070](https://nvd.nist.gov/vuln/detail/CVE-2023-52070) : an `ArrayIndexOutOfBoundsException` in `CompassPlot`\ No fix is considered necessary, however ([#397](https://github.com/jfree/jfreechart/pull/397)) has been applied. - [CVE-2024-22949](https://nvd.nist.gov/vuln/detail/CVE-2024-22949) : a possible `NullPointerException` in `CategoryLineAnnotation`\ No fix is considered necessary. - [CVE-2024-23076](https://nvd.nist.gov/vuln/detail/CVE-2024-23076) : a possible `NullPointerException` in `BubbleXYItemLabelGenerator`\ No fix is considered necessary. ##### Version 1.5.4 (8 January 2023) - add new methods to access maps for datasets, renderers and axes in plots ([#201](https://github.com/jfree/jfreechart/issues/201)); - add option to truncate item labels and compute contrast colors (PR [#225](https://github.com/jfree/jfreechart/pull/225)) - add new annotations (PR [#226](https://github.com/jfree/jfreechart/pull/226)) - add new panel for background rendering of charts (PR [#233](https://github.com/jfree/jfreechart/pull/233)) - fix tick label font for `LogAxis` with number format override ([#98](https://github.com/jfree/jfreechart/issues/98)); - added `valueVisible` flag to `MeterPlot` ([#231](https://github.com/jfree/jfreechart/pull/231)); - added `get/setWebFillAlpha()` methods to SpiderWebPlot ([#279](https://github.com/jfree/jfreechart/pull/279)) - add argument checks for annotations ([#223](https://github.com/jfree/jfreechart/issues/223)); - removed `JFreeChartResources.java` file ([#239](https://github.com/jfree/jfreechart/issues/239)) ##### Version 1.5.3 (21 February 2021) - add new `FlowPlot` class for drawing Sankey charts; - throw exception in `DefaultPieDataset` for invalid index ([#212](https://github.com/jfree/jfreechart/issues/212)); - fix `isJFreeSVGAvailable()` method (bug [#207](https://github.com/jfree/jfreechart/issues/207)); - replaced some usages of `ObjectUtils` with the JDK `Objects` class; - remove unused `URLUtilities` class; - removed @since 1.0.x Javadoc tags since these do not provide useful information anymore. ##### Version 1.5.2 (31 December 2020) - observe series visibility flag in `ClusteredXYBarRenderer` ([#89](https://github.com/jfree/jfreechart/issues/89)); - apply rendering hints to overlays ([#187](https://github.com/jfree/jfreechart/issues/187)); - don't draw disabled outlines on crosshair labels ([#189](https://github.com/jfree/jfreechart/pull/189)); - observe axis visibility flag in `PeriodAxis` ([#198](https://github.com/jfree/jfreechart/issues/198)); - ensure label offsets are used in `CrosshairOverlay` ([#190](https://github.com/jfree/jfreechart/pull/190)); - remove alpha channel from copy-to-clipboard ([#182](https://github.com/jfree/jfreechart/issues/182)); - set flag in XML parser to avoid XML eXternal Entity injection (XXE) ([#130](https://github.com/jfree/jfreechart/issues/130)); - update French translations ([#186](https://github.com/jfree/jfreechart/pull/186)); - fix "Save_as" entries in localisation files ([#184](https://github.com/jfree/jfreechart/pull/184)); - add flags for visibility of outliers in `BoxAndWhiskerRenderer` ([#79](https://github.com/jfree/jfreechart/pull/79)); - deprecated `PiePlot3D` and related methods, for 3D pie charts, use Orson Charts (https://github.com/jfree/orson-charts). ##### Version 1.5.1 (29 October 2020) - add `DeviationStepRenderer` ([#173](https://github.com/jfree/jfreechart/pull/173)) - modify buffer in `ChartPanel` to handle high DPI displays ([#170](https://github.com/jfree/jfreechart/issues/170)); - fix for exception on extreme zoom on `NumberAxis` ([#64](https://github.com/jfree/jfreechart/issues/64)); - fix for `LayeredBarRenderer` ([#169](https://github.com/jfree/jfreechart/issues/169)); - add Catalan translations ([#117](https://github.com/jfree/jfreechart/pull/117)); - add automatic module name `org.jfree.jfreechart`; - migrate to JUnit 5; - raise minimum requirement to Java 8 or later. ##### Version 1.5.0 (5 November 2017) - all JavaFX classes moved to a separate project; - added cleaner method to create histograms in `ChartFactory`; - JFreeSVG updated to version 3.2; - OrsonPDF updated to version 1.7; - JCommon removed as a dependency, and required classes incorporated directly; - improvements to `XYStepRenderer` ([#4](https://github.com/jfree/jfreechart/pull/4)); - bug #36 fix for crosshairs with multiple datasets / axes; - bug #25 fix for `DateAxis.previousStandardDate()` method; - bug #19 fix for default time zone in `SegmentedDateAxis`; - SourceForge #1147 improve performance of `CategoryPlot` mapping datasets to axes; - moved SWT code out into separate projects; - moved demo programs to a separate project; - dropped the Ant build; ##### Version 1.0.19 (31-Jul-2014) - fixed clipping issues for combined plots in JavaFX; - fixed a memory leak in the new JavaFX `ChartCanvas` class; - `CombinedDomainXYPlot` and `CombinedRangeXYPlot` now take into account the pannable flags in the subplots; - `FastScatterPlot` panning direction is corrected; - added rendering hints to sharpen gridlines and borders in most output formats; - JFreeSVG updated to version 2.0; - included a preview of JSFreeChart, a 2D chart library written in JavaScript that is conceptually similar to JFreeChart but runs directly in a web browser. ##### Version 1.0.18 (3-Jul-2014) - added JavaFX support via `FXGraphics2D`; - improved `LogAxis` labelling; - improved numeric tick labelling; - center text support in `RingPlot`; - `stepPoint` attribute in the `XYStepAreaRenderer`; - other minor improvements and bug fixes. ##### Version 1.0.17 (22-Nov-2013) - Enhanced `XYSplineRenderer` with new area fill (contributed by Klaus Rheinwald); - added a notify flag to all datasets that extend `AbstractDataset`; - extended `TimeSeriesCollection` to validate `TimeSeries` keys for uniqueness; - added a new `DirectionalGradientPaintTransformer` (by Peter Kolb); - updated `OHLCSeries`; - added `HMSNumberFormat`; - updated JCommon to version 1.0.21 (includes rotated text improvements) and fixed some minor bugs. ###### Bug Fixes - 977 : Multithreading issue with `DateAxis`; - 1084 : `BorderArrangement.add()` possible `ClassCastException`; - 1099 : `XYSeriesCollection.removeSeries(int)` does not deregister listener; - 1109 : `WaterfallBarRenderer` uses wrong color for diff 0. ##### Version 1.0.16 (13-Sep-2013) *** THIS RELEASE REQUIRES JDK/JRE 1.6.0 OR LATER. *** - Provided subscript/superscript support for axis labels (via `AttributedString`); - new axis label positioning options; - simplified `ChartFactory` methods; - added new methods to `DatasetUtilities` to interpolate y-values in `XYDatasets`; - added URLs to labels on `CategoryAxis`; - seamless integration with JFreeSVG (http://www.jfree.org/jfreesvg/) and OrsonPDF (http://www.object-refinery.com/pdf/); - improved the consistency of the `SWTGraphics2D` implementation; All the JUnit tests have been upgraded to JUnit 4. ###### Bug Fixes - 1107 : Fixed TimeZone issue in `PeriodAxis`; Also fixed a line drawing issue with the `StackedXYAreaRenderer`, and a memory leak in the SWT `ChartComposite` class. ##### Version 1.0.15 (4-Jul-2013) - Added support for non-visible series in `XYBarRenderer`; - minor gridlines in `PolarPlot`; - legend item ordering; - chart editor enhancements; - updates to `StandardDialScale`; - localisation files for Japanese; - refactored parameter checks. This release also fixes a minor security flaw in the `DisplayChart` class, detected and reported by OSI Security: http://www.osisecurity.com.au/advisories/jfreechart-path-disclosure ###### Patches - 3500621 : `LegendTitle` order attribute (by Simon Kaczor); - 3463807 : `ChartComposite` does not dispose popup (by Sebastiao Correia); - 3204823 : `PaintAlpha` for 3D effects (by Dave Law); ###### Bug Fixes - 3561093 : Rendering anomaly for `XYPlots`; - 3555275 : `ValueAxis.reserveSpace()` problem for axes with fixed dimension; - 3521736 : `DeviationRenderer` optimisation (by Milan Ramaiya); - 3514487 : `SWTGraphics2D` `get/setStroke()` problem; - 3508799 : `DefaultPolarItemRenderer` does not populate `seriesKey` in `LegendItem`; - 3482106 : Missing text in `SWTGraphics2D` (by Kevin Xu); - 3484408 : Maven fixes (Martin Hoeller); - 3484403 : `DateAxis` endless loop (by Martin Hoeller); - 3446965 : `TimeSeries` calculates range incorrectly in `addOrUpdate()`; - 3445507 : `TimeSeriesCollection.findRangeBounds()` regression; - 3425881 : `XYDifferenceRenderer` fix (by Patrick Schlott/Christoph Schroeder); - 2963199 : SWT print job (by Jonas Rüttimann); - 2879650 : Path disclosure vulnerability in `DisplayChart` servlet; Also fixed a rendering issue for polar charts using an inverted axis. ##### Version 1.0.14 (20-Nov-2011) This release contains: - support for multiple and logarithmic axes with `PolarPlot`; - optional drop-shadows in plot rendering; - fitting polynomial functions to a data series; - some performance improvements in the `TimeSeriesCollection` class; - mouse wheel rotation of pie charts; - improved Maven support. ###### Patches - 3435734 : Fix lines overlapping item labels (by Martin Hoeller); - 3421088 : Bugfix for misalignment in `BoxAndWhiskerRenderer`; - 2952086 : Enhancement for finding bounds in `XYZDatasets`; - 2954302 : `CategoryPointerAnnotation` line calculation; - 2902842 : `HistogramDataset.addSeries()` fires change change event (by Thomas A Caswell); - 2868608 : Whisker width attribute for `BoxAndWhiskerRenderer` (by Peter Becker); - 2868585 : Added `useOutlinePaint` flag for `BoxAndWhiskerRenderer` (by Peter Becker); - 2850344 : `PolarPlot` enhancements (by Martin Hoeller); - 2795746 : Support for polynomial regression; - 2791407 : Fixes for `findRangeBounds()` in various renderers. ###### Bug Fixes - 3440237 : Shadows always visible; - 3432721 : `PolarPlot` doesn't work with logarithmic axis; - 3433405 : `LineChart3D` - Problem with Item Labels; - 3429707 : `LogAxis` endless loop; - 3428870 : Missing argument check in `TextAnnotation`; - 3418287 : `RelativeDateFormatTest.java` is locale dependent; - 3353913 : Localisation fixes for `ChartPanel`, `CompassPlot` and `PiePlot3D`; - 3237879 : `RingPlot` should respect `getSectionOutlineVisible()`; - 3190615 : Added missing `clear()` method in `CategoryTableXYDataset`; - 3165708 : `PolarChartPanel` localisation fix; - 3072674 : Bad handling of `NaN` in `DefaultStatisticalCategoryDataset`; - 3035289 : `StackedXYBarRenderer` should respect series/item visible flags; - 3026341 : Check for null in `getDomain/RangeBounds()` for `XYShapeRenderer`; - 2947660 : `AbstractCategoryRenderer` fix null check in `getLegendItems()`; - 2946521 : `StandardDialScale` check `majorTickIncrement` argument; - 2876406 : `TimeTableXYDataset` should support `Comparable` for series keys; - 2868557 : `BoxAndWhiskerRenderer` should fire change event in `setMedianVisible()`; - 2849731 : For `IntervalCategoryDataset` and `IntervalXYDataset`, fix `iterateRangeBounds()` in `DatasetUtilities`; - 2840132 : `AbstractXYItemRenderer` `drawAnnotations` doesn't set renderer index; - 2810220 : Offset problem in `StatisticalBarRenderer`; - 2802014 : Dial value border too small; - 2781844 : `XYPointerAnnotation` arrow drawing; - 1937486 : `AreaRenderer` doesn't respect `AreaRendererEndType.LEVEL`; Also fixed: - use of simple label offset in `PiePlot`; - cached `minY` and `maxY` in `TimeSeries.createCopy()`; - scaling issues for charts copied to the clipboard; - use of timezone in `TimeTableXYDataset` constructor; - duplicate series names in `XYSeriesCollection`; - `HistogramDataset` fires a change event in `addSeries()`; - check visibility of main chart title before drawing it; - fixed serialization of `PowerFunction2D`, `NormalDistributionFunction2D`, and `LineFunction2D`; - item label positioning for the `AreaRenderer` class when the plot has an horizontal orientation. ##### Version 1.0.13 (17-Apr-2009) > SPECIAL NOTICE: There will be a birds-of-a-feather session for JFreeChart at this year's JavaOne conference in San Francisco. The session is scheduled for 6.45pm to 7.35pm on Wednesday 3 June. This release contains: - updates to the `ChartPanel` class to support copying charts to the clipboard, panning and mouse-wheel zooming, and an overlay mechanism that supports crosshairs; - enhancements to the auto-range calculation for axes, providing the ability to use subranges only and also to skip hidden series; - updates for many of the `CategoryItemRenderer` implementations to ensure that they respect the `seriesVisible` flags; - an improvement to the `TimeSeries` class so that it is no longer necessary to specify the time period type in the constructor; - a new `SamplingXYLineRenderer` for improving the performance of time series charts with large datasets; - the `XYSeries/XYSeriesCollection` classes now cache the minimum and maximum data values to improve the performance of charts with large datasets; - entities are now created for the chart, data area and axes, allowing mouse clicks to be detected for these regions; - added a bar alignment factor to the `XYBarRenderer` class; - a new `errorIndicatorStroke` field for the `StatisticalLineAndShapeRenderer` and `XYErrorRenderer` classes; - added a new `HeatMapDataset` interface, `DefaultHeatMapDataset` implementation, and a `HeatMapUtilities` class to make it easier to create heat map charts; - there is a new flag to allow an `XYDataImageAnnotation` to be included in the automatic range calculation for the axes; - additional attributes in the `XYTextAnnotation` class; - added a `sampleFunction2DToSeries()` method to the `DatasetUtilities` class; - some changes to the `ChartPanel` class that help to work around a regression in JRE 1.6.0_10 relating to drawing in XOR mode. Regarding this final point: * the default value for the `useBuffer` flag has changed to true, which means that all charts will, by default, be rendered into an off-screen image before being displayed in the `ChartPanel`; * the zoom rectangle is drawn using XOR mode *only* when the `useBuffer` flag has been set to false. For most usage, this should improve performance (but at the cost of using more memory for each `ChartPanel` used in your application); ###### Bug Fixes - 2690293 : Problem with Javascript escape characters; - 2617557 : `StandardDialScale` ignored `tickLabelPaint`; - 2612649 : `Stroke` selection in plot property editor; - 2583891 : `SWTGraphics2D.fillPolygon()` not implemented; - 2564636 : `Month` constructor ignores Locale; - 2502355 : `ChartPanel` sending multiple events; - 2490803 : `PeriodAxis.setRange()` method doesn't take into account that the axis displays whole periods; In addition, a bug in the `SlidingCategoryDataset` class has been fixed, the correct outline paint is now used by `GradientXYBarPainter`, a new method has been added to the `ImageMapUtilities` class to escape special characters in Javascript strings to avoid problems with the OverLIB and DynamicDrive tooltips, and there are some important fixes in the `LogAxis` class. This release passes 2110 JUnit tests (0 failures) on JRE 1.6.0_12. ##### Version 1.0.12 (31-Dec-2008) This release adds - support for minor tick marks; - mapping datasets to more than one axis; - an important fix for the `XYSeries` class (relating to the `addOrUpdate()` method); - plus numerous other bug fixes. This release passes 1996 JUnit test (0 failures) on JRE 1.6.0_10. ###### API Adjustments - `CategoryPlot` : added `mapDatasetToDomainAxes()` and `mapDatasetToRangeAxes()` methods; - `Month` : added a new constructor `Month(Date, TimeZone, Locale)` and deprecated `Month(Date, TimeZone)`; - `Quarter` : added a new constructor `Quarter(Date, TimeZone, Locale)` and deprecated `Quarter(Date, TimeZone)`; - `XYPlot` : added `mapDatasetToDomainAxes()` and `mapDatasetToRangeAxes()` methods; - `Year` : added a new constructor `Year(Date, TimeZone, Locale)` and deprecated `Year(Date, TimeZone)`; ###### Bug Fixes - 2471906 : `XYAreaRenderer` with dashed outline - performance problem; - 2452078 : `StackedAreaChart` has gaps; - 2275695 : `NullPointerException` for `SubCategoryAxis` on plot with null dataset; - 2221495 : `XYLineAnnotation` with dashed stroke; - 2216511 : `SWTBarChartDemo1` throws `RuntimeException`; - 2201869 : `DateAxis` tick label position error; - 2121818 : Label link lines for very thin `RingPlot`; - 2113627 : `XYStepRenderer` item labels; - 1955483 : `XYSeries.addOrUpdate()` problem. Also fixed `StackedXYBarRenderer` which was ignoring the `shadowsVisible` attribute. ##### Version 1.0.11 (18-Sep-2008) This release features: - a new chart theming mechanism to allow charts to be restyled conveniently; - a new `BarPainter` mechanism to enhance the appearance of bar charts; - a new `XYShapeRenderer` class; - a scaling facility for the `XYDrawableAnnotation` for drawing images within specific data coordinates; - some new classes (`XYTaskDataset`, `XYDataImageAnnotation` and `XYTitleAnnotation`); - a modification to the `Year` class to support an extended range; - various bug fixes and API improvements. There is an important bug fix for the `StackedBarRenderer3D` class (see bug 2031407). This release passes 1,961 JUnit tests (0 failures) on JRE 1.6.0_07. ###### API Adjustments - `AbstractRenderer` - added `clearSeriesPaints()` and `clearSeriesStrokes()` methods; - `BarRenderer` - added `shadowPaint` attribute; - `CategoryAxis` - added `getCategoryMiddle()` method; - `CategoryPlot` - added `getRendererCount()` method; - `ChartFactory` - added `get/setChartTheme()` methods; - `ChartPanel` - increased default maximum drawing width and height; - `ChartTheme` - new interface; - `ChartUtilities` - added `applyCurrentTheme()` method; - `CompositeTitle` - added `backgroundPaint` attribute; - `GradientBarPainter` - new class; - `LegendTitle` - added `getWrapper()` method; - `OHLCSeriesCollection` - added `xPosition` attribute; - `PaintScaleLegend` - new subdivisions field; - `PiePlot` - added `autoPopulate` flags, and methods to clear section attributes; - `Plot` - added `setDrawingSupplier()` method; - `RegularTimePeriod` - the `DEFAULT_TIME_ZONE` field has been deprecated in this release; - `RelativeDateFormat` - added methods to control formatting of hours and minutes - see patch 2033092; - `StandardChartTheme` - new class; - `XYItemRendererState` - new methods; - `XYPlot` - added `getRendererCount()` method; - `XYShapeRenderer` - new class; - `XYTaskDataset` - new class. ###### Patches - 1997549 : Status calls to `XYItemRendererState` [Ulrich Voigt]; - 2006826 : `CompositeTitle` drawing fix; - 2033092 : Additional formatters for `RelativeDateFormat` [Cole Markham]; ###### Bug Fixes - 1994355 : `ChartComposite` listener type; - 2031407 : Incorrect rendering in `StackedBarRenderer3D`; - 2033721 : `WaferMapRenderer`; - 2051168 : No key in `LegendItemEntity` for pie charts; Also fixed drawing of alternate grid bands in `SymbolAxis`, the `totalWeight` calculation in the `CombinedXXXPlot` classes, a `NullPointerException` in the `XYPlot` class when drawing quadrants, outline visibility in the `CategoryPlot` class, and auto-range calculations with `XYBarRenderer`. ##### Version 1.0.10 (8-Jun-2008) This release contains various bug fixes and minor enhancements to JFreeChart. - PiePlot labelling has been enhanced (new curve options, and more robust bounds checking); - The BoxAndWhiskerRenderer now has a maximumBarWidth attribute; - the XYStepRenderer has a new stepPoint attribute; - The RelativeDateFormat class has new options; - There are new dataset classes (SlidingCategoryDataset and SlidingGanttDataset) that permit a subset of categories to be plotted, and allow charts based on these datasets to simulate scrolling. - There is a new ShortTextTitle class. This release passes 1,929 JUnit tests (0 failures) on JRE 1.6.0_03. ###### API Adjustments: - BoxAndWhiskerRenderer - added maximumBarWidth attribute (see patch 1866446); - ChartPanel - the zoomPoint attribute has been changed from Point to Point2D; - DatasetUtilities - iterateCategoryRangeBounds() is deprecated, the method has been renamed iterateRangeBounds(CategoryDataset) for consistency; - DefaultKeyedValue - the constructor now prevents a null key; - LogFormat - now has a 'powerLabel' attribute; - ShortTextTitle - a new title class; - SlidingCategoryDataset - new class; - SlidingGanttDataset - new class; - TimeSeriesCollection - getSeries(String) changed to getSeries(Comparable); - XIntervalSeriesCollection - added series removal methods; - YIntervalSeriesCollection - added series removal methods; - XYIntervalSeriesCollection - added series removal methods; `PublicCloneable` is now implemented by a number of classes that didn't previously implement the interface - this should improve the reliability of chart cloning. ###### Patches - 1943021 : Fix for MultiplePiePlot [Brian Cabana]; - 1925366 : Speed improvement for DatasetUtilities [Rafal Skalny]; - 1918209 : LogAxis createTickLabel() changed from private to protected [Andrew Mickish]; - 1914411 : Simplification of plot event notification [Richard West]; - 1913751 : XYPlot and CategoryPlot addMarker() methods with optional notification [Richard West]; - 1902418 : Bug fix for LogAxis vertical labels [Andrew Mickish]; - 1901599 : Fixes for XYTitleAnnotation [Andrew Mickish]; - 1891849 : New curve option for pie chart label links [Martin Hilpert]; - 1874890 : Added step point to XYStepRenderer [Ulrich Voigt]; - 1873328 : Enhancements to RelativeDateFormat [Michael Siemer]; - 1871902 : PolarPlot now has angleTickUnit attribute [Martin Hoeller]; - 1868745 : Fix label anchor points on LogAxis [Andrew Mickish]; - 1866446 : Added maximumBarWidth to BoxAndWhiskerRenderer [Rob Van der Sanden]; ###### Bug Fixes - 1932146 - PeriodAxis.setRange() doesn't notify listeners; - 1927239 - Fix calculation of cumulative range; - 1926517 - Bugs in data range calculation for combined plots; - 1920854 - PiePlot3D labels drawn multiple times; - 1897580 - Fix for DefaultIntervalCategoryDataset; - 1892419 - Wrong default for minor tick count in LogAxis; - 1880114 - VectorRenderer doesn't work for horizontal plot orientation; - 1873160 - DialPlot clipping issues; - 1868521 - Problem saving charts to JPEG format; - 1864222 - Error on TimeSeries createCopy() method; The `DatasetUtilities.sampleFunction2D()` has been changed to sample the correct number of points - you should check any code that calls this method. The `XYBlockRenderer` class now generates entities. Bugs in the `removeDomainMarker()` and `removeRangeMarker()` methods in the `CategoryPlot` and `XYPlot` classes have been fixed. A bug in the `TimePeriodValues` range calculation has been fixed. Fixes were applied to the `clone()` methods in the `TaskSeries` and `TaskSeriesCollection` classes. ###### New Experimental Features Two new classes `CombinedCategoryPlot` and `CombinedXYPlot` have been added to the 'experimental' source tree - these were contributed by Richard West (see patch 1924543). ##### Version 1.0.9 (4-Jan-2008) This release contains an important security patch as well as various bug fixes and minor enhancements. Regarding the security patch, please see the following advisory: http://www.rapid7.com/advisories/R7-0031.jsp Note that the fix incorporated in the special JFreeChart 1.0.8a release was flawed in that it broke the URLs in the HTML image maps generated by JFreeChart. Further amendments have been made in this release to fix this problem. ###### API Adjustments A number of classes have new methods. Nothing has been removed or deprecated: - HashUtilities - added hashCode() methods for BooleanList, PaintList and StrokeList; - ImageMapUtilities - added htmlEscape(String); - IntervalMarker - added new constructor; - Range - added intersects(Range) and scale(Range, double); - TextTitle - added protected methods arrangeNN(), arrangeFN() and arrangeRN(); - XYDataItem - added getXValue() and getYValue() methods; - XYPlot - added setFixedDomainAxisSpace(AxisSpace, boolean) and setFixedRangeAxisSpace(AxisSpace, boolean); - XYSeriesCollection - added getSeries(Comparable) method. ###### Bug Fixes - 1852525 - CandlestickChart.getCategoryPlot() - ClassCastException; - 1851416 - testGetFirstMillisecondWithTimeZone fails in 1.0.8a; - 1849333 - 1.0.8a breaks URLs in HTML image maps; - 1848961 - GroupedStackedBarRenderer works only for primary dataset; - 1846063 - Endless loop in paint of XYPlot; - 1840139 - Cross-site scripting vulnerabilities in image map code; - 1837979 - Background image not shown with SWT; - 1460195 - ChartEntity.getImageMapAreaTag() needs nohref; - 1400917 - OverLIBToolTipTagFragmentGenerator not escaping single quote; - 1363043 - Escape Image Map Data; - 1178601 - AbstractRenderer.hashcode() method returns the same value; In addition, a bug in the constructor for the Week class has been fixed. ##### Version 1.0.8 (23-Nov-2007) This release is primarily a bug fix release: - a problem with pie chart labeling; - a regression in the `DefaultCategoryDataset` class (and underlying `KeyedValues2D` class); - a cloning bug in the `TimeSeries` class. In addition: - the `StatisticalBarRenderer` class has a new `errorIndicatorStroke` field and has been updated to support gradients; - the `StandardDialScale` has had some missing accessor methods implemented; - an override field in the `StandardXYItemRenderer` class has been deprecated; - some warnings reported by FindBugs 1.3.0 have been addressed. ##### Version 1.0.7 (14-Nov-2007) This release features - new classes `DialPlot` and `LogAxis` (previously in experimental); - initial support for minor tick units; - a new anchored zooming option for the `ChartPanel` class; - optional simple labeling on pie charts; - improvements to the "statistical" datasets and underlying data structures; - and numerous bug fixes. ###### API Adjustments - `CategoryAxis` - added `getCategorySeriesMiddle()` method; - `CategoryPlot` - added methods to remove markers; - `ChartPanel` - added `defaultDirectoryForSaveAs` attribute; - `DialPlot` - new class, an alternative to `MeterPlot`; - `LogAxis` - new class, an alternative to `LogarithmicAxis`; - `NumberTick` - new constructor that allows specification of the tick type; - `NumberTickUnit` - new constructor to specify the minor tick count; - `SymbolAxis` - new methods `get/setGridBandAlternatePaint()`; - `TickType` - new class; - `TickUnit` - added `minorTickCount` attribute; - `ValueTick` - added `tickType` attribute; - `StandardPieSectionLabelGenerator` - new constructors accepting a Locale; - `StandardPieToolTipGenerator` - likewise; - `CategoryPlot` - added `getRangeAxisIndex()`, `zoomDomainAxes()` and `zoomRangeAxes()` methods; - `FastScatterPlot` - added new zooming methods; - `PiePlot` - new attributes to support simple labeling; - `PlotUtilities` - new class; - `PolarPlot` - added new zooming methods; - `ThermometerPlot` - likewise; - `XYPlot` - added methods to remove markers (patch 1823697--same as for `CategoryPlot`), and added new zooming methods; - `Zoomable` - added new zooming methods to this interface; - `LineAndShapeRenderer` - added `useSeriesOffset` and `itemMargin` attributes; - `MinMaxCategoryRenderer` - implemented `equals()`; - `XYSplineAndShapeRenderer` - new class; - `LogFormat` - new class; - `ChartFactory` - new pie and ring chart creation methods that accept a `Locale`; - `ChartPanel` - added `zoomAroundAnchor` attribute; - `Series` - added `isEmpty()` method; - `BoxAndWhiskerItem` - new convenience constructor; - `DefaultBoxAndWhiskerCategoryDataset` - new remove methods; - `DefaultStatisticalCategoryDataset` - likewise; - `MeanAndStandardDeviation` - added new value accessor methods; - `TimeTableXYDataset` - added `clear()` method; - `Week` - added new constructor; - `KeyedObjects` - added `insertValue()` and `clear()` methods; - `KeyedObjects2D` - added `clear()` method. ###### Patches - 1823724 - updated `XYDifferenceRenderer` to support item labels; - 1827829 - fixed possible `NullPointerException` in `XYBarRenderer`; ###### Bug Fixes - 1767315 - `GrayPaintScale.getPaint()` uses wrong value; - 1775452 - Inverted `XYBarRenderer` does not render margins correctly; - 1802195 - `Marker.listenerList` serializable; - 1779941 - `StatisticalBarRenderer` NPE; - 1766646 - `XYBlockRenderer` can't handle empty datasets; - 1763413 - `PeriodAxis` labels fail to display with setInverted; - 1737953 - Zoom doesn't work on `LogAxis` (Demo1); - 1749124 - `JFreeChart` not added as `TitleChangeListener`; ##### Version 1.0.6 (15-Jun-2007) This release features: - a new `VectorRenderer` (previously in experimental); - a generalised `XYDifferenceRenderer`; - better support for hotspots on legend items; - improved performance for time series charts displaying subsets of data; - support for `GradientPaint` in plot backgrounds; - plus the usual slew of bug fixes and minor feature additions. ###### API Adjustments - `CategoryItemEntity` - replaced row and column index attributes with row and column key attributes; - `CategoryItemRenderer` - numerous series override settings have been deprecated; - `DefaultPieDataset` - added `insertValues()` method; - `HexNumberFormat` - new class; - `LegendItem` - added dataset and seriesKey attributes; - `Plot` - added new `fillBackground()` method to support `GradientPaint`, and added `is/setOutlineVisible()` methods; - `QuarterDateFormat` - added `GREEK_QUARTERS` field plus a new constructor; - `SimpleHistogramDataset` - added `clearObservations()` and `removeAllBins()` methods; - `TimeSeriesCollection` - added `indexOf()` method; - `URLUtilities` - new class; - `XYItemRenderer` - numerous series override settings have been deprecated; - `XYSeriesCollection` - added indexOf() method. ###### Bug Fixes - 1735508 - `ClusteredXYBarRenderer` fails with inverted x-axis; - 1726404 - `ChartComposite` tooltips; - 1713474 - `StackedBarRenderer3D` doesn't fill shadows; - 1713401 - `StackedBarRenderer3D` doesn't check `drawBarOutline` flag; - 1701822 - `DefaultBoxAndWhiskerCategoryDataset` doesn't follow contracts; - 1698965 - NPE in `CombinedDomainXYPlot`; - 1690994 - `HideSeriesDemo1` does not work; - 1690654 - Bug in `removeValue()` of `DefaultKeyedValues2D`; - 1562701 - `LegendItemEntity` needs dataset index; - 1486299 - Use `URLEncoder.encode()` for URL generators; Plus the following bugs that didn't have entries in the database: - `BarRenderer` - check for series visibility in `getLegendItem()`; - `ChartPanel` - use correct insets for painting chart buffer to screen, update UI for popup menu if LookAndFeel changes; - `DateAxis` - fixed boundary cases for `previousStandardDate()` method; - `LineBorder` - only draw border if area has positive dimensions; - `JFreeChart` - should register as a listener with the default legend; - `StandardXYItemRenderer` - fixed a problem where chart entities are created for non-visible items; - `TimePeriodValuesCollection.getDomainBounds()` now computes the bounds correctly; - `XYLineAndShapeRenderer` - fixed a problem where chart entities are created for non-visible items; - `XYLine3DRenderer` - `equals()` implemented, and serialization fixed; - `XYTitleAnnotation` - fixed `equals()` method; - various resource usage bugs in the experimental `ChartComposite` class; ##### Version 1.0.5 (23-Mar-2007) This release features: - a new `DeviationRenderer` class; - support for item labels in `StackedXYBarRenderer`; - tooltips and URLs in the `CategoryStepRenderer`; and - many bug fixes. ###### API Adjustments - `AbstractCategoryItemRenderer` - added `createState()` method; - `StackedXYBarRenderer` - added `get/setRenderAsPercentages()` methods; - `XYIntervalSeries` - added `getXLowValue()`, `getXHighValue()`, `getYLowValue()` and `getYHighValue()`; - `YIntervalSeries` - added `getYLowValue()` and `getYHighValue()` methods; ###### Bug Fixes - 1681777 - `DefaultCategoryDataset` does not clone data; - 1672552 - Zoom rectangle is lost when the chart is repainted; - 1671645 - `Crosshair` incorrectly positioned in horizontal orientation; - 1669302 - Tick labels in vertical symbol axis; - 1669218 - `CategoryPlot.setDomainAxisLocation()` ignores parameter; - 1667750 - Clip region not restored in `Spider` and `MeterPlot`; - 1663380 - OutputStream not closed; - 1659627 - `IntervalMarker` with `Double.POSITIVE_INFINITY` bound; - 1647269 - `IntervalMarker` with `Double.MAX_VALUE as upper` bound; - 1594477 - `XYBarRenderer` does not render bars on `LogarithmicAxis`; - 1459958 - Log axis zoom function problem; - 880597 - Zooming `ChartPanel` with log axes; - 764561 - Dynamic chart zoom buggy. Also fixed numerous bugs in equals(), cloning and serialization implementations. ##### Version 1.0.4 (9-Feb-2007) This release features: - a new `XYBlockRenderer` class; - URLs for pie chart labels in HTML image maps; - a new dataset implementation for open-high-low-close charts; - support for gradient paint in `ClusteredXYBarRenderer`, `StackedXYBarRenderer` and legend graphics; - a new anchor attribute for `XYImageAnnotation`; - improvements to the experimental SWT support; plus - a number of additions to the API for usability; and - many bug fixes. ###### API Adjustments - `DateAxis` - added `get/setTimeZone()` methods; - `DefaultXYDataset` - now implements `PublicCloneable`; - `StackedXYAreaRenderer2` - added `get/setRoundXValues()` methods; - `StandardXYItemLabelGenerator` - added new constructor; - `StandardXYToolTipGenerator` - added new constructor; - `XYBarDataset` - added `getUnderlyingDataset()` and `get/setBarWidth()` methods; - `XYDifferenceRenderer` - added `roundXCoordinates` attribute; - `XYImageAnnotation` - added an image anchor attribute, a new constructor, and several accessor methods; - `XYSeries` - added `toArray()` method; ###### Bug Fixes - 1654215 - `XYPlot` renderer with no corresponding dataset; - 1652640 - `RangeMarkers` do not update properly; - 1649686 - `Crosshairs` for `StackedXYAreaRenderer`; - 1647749 - `IllegalArgumentException` in `SWTAxisEditor`; - 1644877 - Replacing series data in `DefaultXYDataset`; - 1644010 - `DateAxis.nextStandardDate()` ignores timezone; - 1638678 - `DateAxis` code uses the default calendar; - 1629382 - Tests fail for jfreechart-1.0.3; - 1624067 - `StandardXYToolTipGenerator` missing constructor; - 1616583 - Serialize `ChartDeleter`; - 1612770 - Popup menu in wrong position for SWT `ChartComposite`; - 1611872 - `Minute.previous()` returns null for minute == 0; - 1608371 - Tick labels overlap with custom `NumberFormat`; - 1606205 - Draw shared axis last on combined plots; - 1605207 - `IntervalMarker` exceeds bounds of data area; - 1605202 - `SpiderWebPlot` method access; - 1599652 - Inverted `StackedBar3D` problem; - 1598394 - `XYBarDataset` hiding its proxied object; - 1564967 - Crosshairs on `XYDifferenceRenderer`; - 1245305 - `NullPointerException` in `writeImageMap()`; - 1086307 - Crosshairs on plots with multiple axes. Also fixed numerous bugs in `equals()` and `clone()` methods throughout the API. ##### Version 1.0.3 (17-Nov-2006) This release features: - several new `IntervalXYDataset` implementations; - some significant refactoring of the time period classes (to improve performance and correctness); - modifications to the `PiePlot` class to support reordering of dataset items; - a new event mechanism to allow updating of markers, plus - many other enhancements, bug fixes and documentation updates. A new `DialPlot` implementation has been added to the 'experimental' sources. We are looking for people to test this code and provide feedback, so that we can stabilize the API and add this code to the main JFreeChart API. ###### API adjustments The following adjustments have been made to the API: - `CategoryLabelEntity` - new class; - `CategoryPointerAnnotation` - new class; - `ChartPanel`: added new public method `doEditChartProperties()`; - `ComparableObjectItem`, `ComparableObjectSeries` - new classes; - `CrosshairState`: added several new accessor methods; - `DefaultPieDataset`: added `sortByKeys()` and `sortByValues()` methods; - `Markers`: a change event mechanism has been added to the `Marker` class and its subclasses; - `StackedAreaRenderer`: added `get/setRenderAsPercentages()` methods; - `XIntervalDataItem`, `XIntervalSeries` and `XIntervalSeriesCollection` - new classes; - `XYErrorRenderer`: new class; - `XYInterval`, `XYIntervalDataItem`, `XYIntervalSeries` and `XYIntervalSeriesCollection` - new classes; - `YInterval`, `YIntervalDataItem`, `YIntervalSeries`, `YIntervalSeriesCollection` and `YWithXInterval` - new classes. ###### Bug Fixes - 1578293 - Unused methods in `JDBCXYDataset`; - 1572478 - `BoxAndWhiskerRenderer` potential `NullPointerException`; - 1569094 - `XYStepRenderer` with horizontal orientation; - 1565168 - Crosshair position incorrect; - 1564977 - `DateAxis` missing initial tick label; - 1562759 - `StatisticalLineAndShapeRenderer` constructor ignores arguments; - 1557141 - Bad locale in `ServletUtilities`; - 1550045 - `TimeSeries.removeAgedItems()` method problems; - 1549218 - Chart not displaying when all data values are the same and large; - 1450447 - `Marker.setAlpha()` ignored; Also fixed URL generation for legend items, tick mark positioning on the `DateAxis`, the `equals()` method in the `AreaRenderer` class, hardcoded outline attributes in the `XYBubbleRenderer`, and potential `NullPointerExceptions` in the `ChartPanel` class. ##### Version 1.0.2 (25-Aug-2006) ###### API adjustments The following adjustments have been made to the API (there should be no breakage of applications coded to the 1.0.0 or 1.0.1 API): - `CategoryToPieDataset`: added accessor methods for underlying dataset, extract type and index (feature request 1477915); - `DefaultXYDataset`: New dataset implementation that uses double[] arrays; - `DefaultXYZDataset`: New dataset implementation that uses double[] arrays; - `LegendItemBlockContainer`: New container used in legends (enables legend item entities again); - `MultiplePiePlot`: Added new fields `aggregatedItemsKey` and `aggregatedItemsPaint`, plus accessor methods - see bug 1190647; - `SpiderWebPlot`: Added new fields `toolTipGenerator` and `urlGenerator`, plus accessor methods (see patch 1463455); - `StackedBarRenderer3D`: Added new flag (`renderAsPercentages`), plus accessor methods, that controls whether the data items are displayed as values or percentages. Two new constructors are also added (see patch 1459313); - `XYPolygonAnnotation`: Added new accessor methods. ###### Patches - 1459313 - Add `renderAsPercentages` option to `StackedBarRenderer3D`; - 1462727 - Modify `SpiderWebPlot` to support zero values; - 1463455 - Modify `SpiderWebPlot` to support mouse clicks, tool tips and URLs; ###### Bug Fixes - 1514904 - Background image alpha in `Plot` class; - 1499140 - `ClusteredXYBarRenderer` with margin not drawing correctly; - 1494936 - `LineAndShapeRenderer` generates entity for non-visible item; - 1493199 - NPE drawing `SpiderWebPlot` with null info; - 1480978 - `AbstractPieItemLabelGenerator.clone()` doesn't clone `percentFormat`; - 1472942 - `DateAxis.equals()` broken; - 1468794 - `StatisticalLineAndShapeRenderer` doesn't draw error bars correctly when the plot has a horizontal orientation; - `AbstractCategoryItemRenderer` doesn't check `seriesVisibleInLegend` flag before creating new item; - 1440415 - Bad distribution of pie chart section labels; - 1440346 - Bad manifest entry for JCommon in JFreeChart jar file; - 1435461 - `NumberAxis.equals()` ignores `rangeType` field; - 1435160 - `XYPointerAnnotation.equals()` ignores x and y fields; - 1398672 - `LegendItemEntities` not working; - 1380480 - `StandardXYItemRenderer` problems with `Double.NaN`; - 1190647 - Legend and section color mismatch for `MultiplePiePlot`. ###### Miscellaneous Changes - Updated `CandlestickRenderer`, `CyclicXYItemRenderer`, `HighLowRenderer`, `XYStepAreaRenderer` and `TimeSeriesURLGenerator` to call dataset methods that return double primitive only; - Updated `XYPolygonAnnotation`, adding new accessor methods and fixing problems in the `equals()/hashCode()` methods; - `ChartFactory.createStackedXYAreaChart()` now uses `StackedXYAreaRenderer2`, for better handling of negative values; - Added crosshair support for `XYBarRenderer`. ###### Experimental Code In this release, some new (incomplete) classes have been included in the `org.jfree.experimental.*` namespace. These classes are not part of the standard API, but are included for developers to experiment with and provide feedback on. Hopefully in the future, refined versions of these classes will be incorporated into the main library. PLEASE NOTE THAT THE API FOR THESE CLASSES IS SUBJECT TO CHANGE. ##### Version 1.0.1 (27-Jan-2006) This is primarily a bug fix release. In addition, there are some API adjustments (there should be no breakage of applications coded to the 1.0.0 API). ###### API adjustments - `BarRenderer`: added a new flag (`includeBaseInRange`), plus accessor methods, that controls whether or not the base value for the bar is included in the range calculated by the `findRangeBounds()` method; - `BubbleXYItemLabelGenerator`: new class; - `Range`: added a new method `expandToInclude(Range, double)`, this is used by the `BarRenderer` class; - `TaskSeriesCollection`: added two new methods, `getSeries(int)` and `getSeries(Comparable)`; - `TimeSeriesCollection`: the `domainIsPointsInTime` flag has been deprecated. The flag serves no function now that renderers are used to calculate the domain bounds, so you can safely delete any calls to the `setDomainIsPointsInTime()` method; - `XYPlot`: added a new `getAnnotations()` method; - `XYSeries`: the `update(int, Number)` method has been deprecated and a new method `updateByIndex(int, Number)` has been added; ###### Bug fixes - 1243050 - `XYBarRenderer` doesn't show entire range of values for a `TimeSeriesCollection`; - 1373371 - `XYBubbleRenderer` doesn't support item labels; - 1374222 - `BarRenderer` contains JDK 1.4 specific code; - 1374328 - `LegendItem` serialization problem; - 1377239 - Bad argument checking in `Quarter` constructor; - 1379331 - Incorrect drawing of `TextTitle` at LEFT or RIGHT position; - 1386328 - `RingPlot` entity incorrect; - 1400442 - Inconsistent treatment of null and zero values in `PiePlot`; - 1401856 - Bad rendering for non-zero base values in `BarRenderer`; - 1403043 - `NullPointerException` in `CategoryAxis`; - 1408904 - `NumberAxis3D` assumes `CategoryPlot`; - 1415480 - `XYTextAnnotation` equals() method doesn't check (x, y); ##### Version 1.0.0 (2-Dec-2005) - the first stable release of the JFreeChart class library, all future releases in the 1.0.x series will aim to maintain backward compatibility with this release; ##### Version 1.0.0-rc3 (28-Nov-2005) - the third "release candidate" for version 1.0.0, this release fixes some issues with the 1.0.0-rc2 release (mainly concerning packaging of resource bundles for localisation). - if no significant problems are reported in the next few days, the 1.0.0 "final" release will be posted on 2-Dec-2005. ##### Version 1.0.0-rc2 (25-Nov-2005) - the second "release candidate" for version 1.0.0. If no problems are reported, 1.0.0 "final" will be released on 2-Dec-2005. ##### Version 1.0.0-rc1 (2-Jun-2005) - this is a "release candidate" for version 1.0.0. If no significant API problems are reported, this release will be re-released as version 1.0.0. ##### Version 1.0.0-pre2 (10-Mar-2005) ##### Version 1.0.0-pre1 (29-Nov-2004) ##### Version 0.9.21 (9-Sep-2004) - added new axes: `PeriodAxis` and `ModuloAxis`. - split `org.jfree.data` and `org.jfree.chart.renderer` into subpackages for 'category' and 'xy' charts. - Sun PNG encoder is now used, if available. - a new demo application makes it easier to preview the chart types that JFreeChart can create. - added a new series visibility flag to the `AbstractRenderer` class. - added support for `GradientPaint` in interval markers. ##### Version 0.9.20 (7-Jun-2004) - primarily bug fixes. ##### Version 0.9.19 (28-May-2004) - added methods to `XYDataset` that return double primitives; - removed distinction between "primary" and "secondary" datasets, renderers and axes; - added fixed legend item options to `CategoryPlot` and `XYPlot`; - legend changes by Barek Naveh; - removed Log4j dependency; - many, many bug fixes; ##### Version 0.9.18 (15-Apr-2004) - new legend anchor options; - fixed broken JPEG export; - fixed title size problems; - various other bug fixes; ##### Version 0.9.17 (26-Mar-2004) - pie chart enhancements for labelling, shading and multiple pie charts (2D or 3D) on a single plot; - new `PolarPlot` class added; - `XYSeries` can now be sorted or unsorted; - `createBufferedImage()` method can now scale charts; - domain and range markers now support intervals; - item labels are now supported by some `XYItemRenderers`; - tooltip and item label generators now use `MessageFormat` class; - added new `XYBarDataset` class; - added transparency support to PNG export; - numerous other small enhancements and bug fixes; ##### Version 0.9.16 (09-Jan-2004) - this release contains bug fixes and some minor feature enhancements (title and category label wrapping, legend shape scaling, enhanced performance for the `DefaultTableXYDataset` class); - added Spanish localisation files; ##### Version 0.9.15 (28-Nov-2003) - the focus of this release is bug fixes - quite a number of issues have been resolved, please check the bug database for details; - added a new Wafer Map chart type; - added a cyclic axis; - added localisation files for _ru; ##### Version 0.9.14 (17-Nov-2003) - implemented zooming for the `FastScatterPlot` class; - added item label support for stacked bar charts, and new fall back options for item labels that don't fit within bars; - modified the `CategoryAxis` class to allow additional options for the alignment and rotation of category labels; - addition of the `AxisState` class, used in the drawing of axes to eliminate a bug when multiple threads draw the same axis simultaneously; - provided additional attributes in the `DateTickUnit` class to improve labelling on a segmented `DateAxis`; - added support for `GradientPaint` in bar charts; - updated the `PNGEncoder`; - fixes for tick label positioning on axes; - various Javadoc updates; - numerous bug fixes; ##### Version 0.9.13 (26-Sep-2003) - various enhancements to the stacked area XY charts; - added a completion indicator for the Gantt chart; - range and domain markers can now be placed in the foreground or the background; - more fixes for cloning and serialization; - fixed mouse event bug for combined charts; - fixed bugs in the PngEncoder class; - incorporated .properties files that were missing from the 0.9.12 distribution; ##### Version 0.9.12 (11-Sep-2003) - extended box-and-whisker plots to work with the CategoryPlot class as well as the XYPlot class (based on work by David Browning); - added a new LayeredBarRenderer (by Arnaud Lelievre); - added support for stacked area charts with the XYPlot class (thanks to Richard Atkinson); - improved HTML image map support (thanks to Richard Atkinson); - added localized resources for the chart property editors (thanks to Arnaud Lelievre). Current translations include French and Portugese (thanks to Eduardo Ramalho); - added facility for setting all rendering hints; - improved support for cloning and serialization; - fixed a bug in the XYSeries class that prevented the TableXYDataset from functioning correctly; - improved date axis labelling with segmented time lines; - fixed several bugs in the secondary dataset/axis/renderer code; - fixed bugs in the JDBCCategoryDataset class; - numerous other bug fixes; ##### Version 0.9.11 (8-Aug-2003) - added support for box-and-whisker plots, thanks to David Browning; - lots of bug fixes; ##### Version 0.9.10 (25-Jul-2003) - added support for multiple secondary axes, datasets and renderers; - minor feature enhancements and bug fixes; ##### Version 0.9.9 (10-Jul-2003) PLEASE NOTE THAT MAJOR CHANGES HAVE BEEN MADE IN THIS RELEASE AND ONE OR TWO FEATURES MAY BE BROKEN. PLEASE REPORT BUGS SO THEY CAN BE FIXED FOR THE NEXT RELEASE. - merged the HorizontalCategoryPlot and VerticalCategoryPlot classes, into the CategoryPlot class; - merged the horizontal and vertical axis classes; - merged the horizontal and vertical renderer classes; - CategoryPlot and XYPlot now support both horizontal and vertical orientation via the setOrientation(...) method; - merged horizontal and vertical methods in the ChartFactory class; - created new combined plot classes: CombinedDomainCategoryPlot, CombinedRangeCategoryPlot, CombinedDomainXYPlot and CombinedRangeXYPlot (these can all be drawn with a horizontal or vertical orientation); - Bill Kelemen has enhanced the DateAxis class to handle segmented timelines. This can be used, for example, to skip weekends for daily stock price charts; - Richard Atkinson has updated the ServletUtilities class; - Bryan Scott has added an XYDatasetTableModel class for presenting datasets in a JTable; - modified XYPlot to allow renderers to use multiple passes through the dataset; - added new XYDifferenceRenderer; - added support for colored bands between gridlines in XYPlot; - added new XYDrawableAnnotation class; - added a new attribute to control the order of dataset rendering in a CategoryPlot; - extended the value label mechanism for the renderers, to allow better (per series) control over label generation, positioning and visibility; - CategoryItemTooltipGenerator has been renamed CategoryItemLabelGenerator, since it is now being used to generated item labels as well as tooltips; - there is now support for horizontal stacked 3D bar charts; - added support for range markers against secondary axis in a CategoryPlot; - added labels to domain and range markers; - added a new HistogramDataset class (contributed by Jelai Wang) to make it easier to create histograms with JFreeChart; - moved the DrawingSupplier into the plot class, renderers now reference the supplier from the plot (parent plot for combined and overlaid charts). This means that renderers now share a single DrawingSupplier by default, which simplifies the creation of combined charts; - changed the ColorBarAxis classes that extended the NumberAxis class, to a single ColorBar class that wraps a ValueAxis (may have broken one or two things in the process); - Barak Naveh has contributed new classes MatrixSeries and MatrixSeriesCollection, along with demos: BubblyBubblesDemo.java and BubblyBubblesDemo2.java; - the TextTitle class now has a background paint attribute; - the StandardLegend class now generates LegendEntity objects if a ChartRenderingInfo instance is supplied to the draw(...) method; - extended the CategoryTextAnnotation class to take into account a category anchor point. See the SurveyResultsDemo.java application for an example; - included numerous bug fixes; ##### Version 0.9.8 (24-Apr-2003) - changed package naming from com.jrefinery.* to org.jfree.*; - added new TimePeriodValuesCollection class; - added MIME type code to ServletUtilities class; - reversed the order of PieDataset and KeyedValuesDataset in the class hierarchy; - reversed the order of CategoryDataset and KeyedValues2DDataset in the class hierarchy; - minor bug fixes; ##### Version 0.9.7 (11-Apr-2003) - added a new ValueDataset interface and DefaultValueDataset class, and changed the CompassPlot class to use this instead of MeterDataset; - added DataUtilities class, to support creation of Pareto charts (new demo included); - updated writeImageMap method as suggested by Xavier Poinsard (see Feature Request 688079); - implemented Serializable for most classes (this is likely to require further testing); - incorporated contour plot updates from David M. O'Donnell; - added new CategoryTextAnnotation and XYLineAnnotation classes; - added new HorizontalCategoryAxis3D class contributed by Klaus Rheinwald; Bug fixes: - added a workaround for JVM crash (a JDK bug) in pie charts with small sections (see bug report 620031); - fixed minor bug in HorizontalCategoryPlot constructor (see bug report 702248); - added code to ensure HorizontalNumberAxis3D is not drawn if it is not visible (see bug report 702466); - added small fix for suppressed chart change events (see bug report 690865); - added pieIndex parameter to tooltip and URL generators for pie charts; - fixed bug in getLastMillisecond() method for the Second class and the getFirstMillisecond() method for the Year class (picked up in JUnit tests); - in TextTitle, changed width used for relative spacing to fix bug 703050; ##### Version 0.9.6 (17-Feb-2003) Bug fixes: - fixed null pointer exception in DefaultCategoryDataset; - fixed update problem for PaintTable, StrokeTable and ShapeTable objects; - added methods to control colors in PiePlot (these were inadvertantly removed in the changes made for 0.9.5); - fixed auto-range update problem for secondary axis; - fixed missing category labels in the overlaid category plot; - fixed constructors for symbolic axes; - corrected error in Javadoc generation (Ant script); ##### Version 0.9.5 (6-Feb-2003) PLEASE NOTE THAT MAJOR CHANGES TO THE JFREECHART API HAVE BEEN MADE IN THIS RELEASE! - added support for secondary axes, datasets and renderers; - added new data interfaces (Value, Values, Values2D, KeyedValues and KeyedValues2D) and incorporated these into the existing PieDataset and CategoryDataset interfaces. - modified the CategoryDataset interface to be more symmetrical, data is organised in rows and columns (as before) but can now be accessed by row/column index or row/column key. - added support for reading PieDatasets and CategoryDatasets from XML files. - created separate packages for the axes (com.jrefinery.chart.axis), plots (com.jrefinery.chart.plot) and renderers (com.jrefinery.chart.renderer). - series attributes (paint, outline paint, stroke and shape) are now controlled by the renderer classes using lookup tables. Introduced the DrawingSupplier interface (and DefaultDrawingSupplier class) which is used to populate the lookup tables from a common source (necessary to coordinate complex combined charts). - the chart legend can now display shapes corresponding to series. - moved responsibility for category distribution to the CategoryAxis class, which tidies up the code in the CategoryPlot classes. - gridlines are now controlled by the CategoryPlot and XYPlot classes, not the axes (included in this change is the addition of gridlines for the CategoryPlot domain values). - changed the list of titles in the JFreeChart class to a title and a list of subtitles. - added new renderers for XYPlot (XYBubbleRenderer and YIntervalRenderer). - modified Gantt chart to display sub-tasks. - added ContourPlot class (still experimental) by David M. O'Donnell. - introduced new MovingAverage class. - ChartMouseEvent now includes source chart. - numerous bug fixes. - lots of Javadoc updates. ##### Version 0.9.4 (18-Oct-2002) Added a new stacked area chart (contributed by Dan Rivett) and a compass plot (contributed by Bryan Scott). Updated the ThermometerPlot class. Added a new XYDotRenderer for scatter plots. Modified combined and overlaid plots to use the series colors specified in the sub plot rather than the parent plot (this makes it easier to align the colors in the legend). Added Regression class for linear and power regressions. BasicTimeSeries can now automatically drop "old" data. Some clean-up work in the code for tooltips and the event listener mechanism. Richard Atkinson has incorporated some useful extensions for servlets/JSP developers. Ran Checkstyle and corrected issues reported for most classes. Checkstyle is a free utility that you can download from: http://checkstyle.sourceforge.net Fixed bugs and updated documentation. API changes include: - added tickMarkPaint to Axis constructor (also affects subclasses); - added getLegendItems() to Plot, and deprecated getLegendItemLabels(); - added getLegendItem(int) to XYItemRenderer and CategoryItemRenderer. - most 'protected' member variables have been changed to 'private'. ##### Version 0.9.3 (4-Sep-2002) - Added multiple pie charts based on `CategoryDataset`; - Updated logarithmic axes; - Improved URL support for image map generation; - Moved the `com.jrefinery.data` package from JCommon to JFreeChart. - Added simple framework for chart annotations; - Improved control over renderers; - Duplicate x-values now allowed in `XYSeries`; - Optional category label skipping in category axes; - Added `CategoriesPaint` attribute to `AbstractCategoryItemRenderer`; - Added new attributes to `MeterPlot` class; - Updated 3D pie chart to observe start angle and direction, and also foreground alpha < 1.0; - Improved Javadoc comments; - New demo applications, including: `AnnotationDemo1`, `EventFrequencyDemo`, `JDBCCategoryChartDemo`, `JDBCPieChartDemo`, `JDBCXYChartDemo` and `MinMaxCategoryPlotDemo`. Bug fixes: - negative percentages on `PiePlot`; - added listener notification to `setXXXAxis(...)` methods; - fixed `DomainInfo` method name clash; - added `DomainIsPointsInTime` flag to `TimeSeriesCollection` to give better control over auto range on axis for time series charts; - axis margins for date axes are no longer hard-coded; - fix for ordering of categories in `JdbcCategoryDataset`; - added check for `null` axis in mouse click handler. The CVS repository at SourceForge has also been restructured to match the distribution directory layout. ##### Version 0.9.2 (28-Jun-2002) - `PiePlot` now has `startAngle` and `direction` attributes; - added support for image map generation; - added a new `Pie3DPlot` class; - added label drawing code to bar renderers; - added optional range markers to horizontal number axis; - added bar clipping to avoid PRExceptions in bar charts; - `JFreeChartDemo` has been modified and now includes examples of the dial and thermometer plots. ######Bug fixes - auto range for `VerticalNumberAxis` when zero is forced to be included in the range; - fixed null pointer exception in `StackedVerticalBarRenderer3D`; - added get/set methods for min/max chart drawing dimensions in `ChartPanel`; - `HorizontalIntervalBarRenderer` now handles single category; - `verticalTickLabels` now possible in `HorizontalNumberAxis3D`; - removed unnecessary imports; ##### Version 0.9.1 (14-Jun-2002) Bug fixes and Javadoc updates. - fixed auto range calculation for category plots; - fixed event notification for `XYPlot`; - fixed auto axis range for Gantt charts; - check for null popup menu in `ChartPanel.mouseDragged`; - new checks for null info in renderers; - range markers now drawn only if in visible axis range; ##### Version 0.9.0 (7-Jun-2002) - new plots including an area chart, a horizontal 3D bar chart, a Gantt chart and a thermometer chart; - combination plots have been reworked to provide a simpler framework, and extends to allow category plots to be combined; - there is now a facility to add a `ChartMouseListener` to the `ChartPanel` (formerly `JFreeChartPanel`); - an interactive zooming feature (experimental at this point) is now available for `XYPlots`; - a new Polish translation has been added; - several fixes have been applied to the default tool tip generators; - a workaround has been added to fix the alignment between time series charts and the date axis; - there are some improvements to the `VerticalLogarithmicAxis` class, and now a corresponding `HorizontalLogarithmicAxis` class; - additional demonstration applications have been added; - fixed the popup menu bug. ##### Version 0.8.1 (5-Apr-2002) - Localised resource bundles for French, German and Spanish languages (thanks to Anthony Boulestreau, Thomas Meier and Hans-Jurgen Greiner for the translations); - an area XY plot and meter chart contributed by Hari; - symbol charts contributed by Anthony Boulestreau; - an improved `CandleStickRenderer` class from Sylvain Vieujot; - updated servlet code from Bryan Scott; - `XYItemRenderers` now have a change listener mechanism and therefore do not have to be immutable; - additional demonstration applications for individual chart types; - minor bug fixes. ##### Version 0.8.0 (22-Mar-2002) - all the category plots are now controlled through the one class (`CategoryPlot`) with plug-in renderers; - added a `ResourceBundle` for user interface items that require localisation; - added a logarithmic axis class contributed by Mike Duffy and some new JDBC and servlet code contributed by Bryan Scott; - updated the JCommon class library to improve handling of time periods in different time zones. ##### Version 0.7.4 (6-Mar-2002) - bug fixes in the JCommon Class Library; - various Javadoc comment updates; - some minor changes to the code; - added new domain name (http://www.object-refinery.com) in the source headers. ##### Version 0.7.3 (14-Feb-2002) Bug fixes. ##### Version 0.7.2 (8-Feb-2002) - integrated the `WindPlot` code from Achilleus Mantzios; - added an optional background image for the `JFreeChart` class; - added an optional background image for the `Plot` class; - added alpha-transparency for the plot foreground and background; - added new pie chart label types that show values; - fixed a bug with the legend that results in a loop at small chart sizes; - added some tooltip methods that were missing from the previous version; - changed the `Insets` class on chart titles to a new `Spacer` class that will allow for relative or absolute insets (the plan is to eventually replace all `Insets` in the `JFreeChart` classes); - fixed a bug in the `setAutoRangeIncludesZero` method of the `NumberAxis` class; - added the instructions that were missing from the copies of the GNU Lesser General Public Licence included with JFreeChart. ##### Version 0.7.1 (25-Jan-2002) - added tooltips, crosshairs and zooming functions, thanks to Jonathan Nash and Hans-Jurgen Greiner for contributing the code that these features are based on; - moved the combination charts into the package `com.jrefinery.chart.combination`; - made a number of other small API changes and fixed some bugs; - removed the Javadoc HTML from the download to save space (you can regenerate it from the source code if you need it). ##### Version 0.7.0 (11-Dec-2001) - new combination plots developed by Bill Kelemen; - added Wolfgang Irler's servlet demo to the standard download; - the About window in the demo application now includes a list of developers that have contributed to the project. ##### Version 0.6.0 (27-Nov-2001) - new plots including scatter plot, stacked bar charts and 3D bar charts; - improved pie chart; - data interfaces and classes moved to the JCommon class library; - new properties to control spacing on bar charts; - new auto-tick mechanism; - `JFreeChartPanel` now incorporates buffering, and popup menu; - Javadocs revised; - fixed numerous bugs from version 0.5.6; - demo application updated. CONTRIBUTORS ------------ JFreeChart wouldn't be half the library that it is today without the contributions (large and small) that have been made by the developers listed below: - Eric Alexander - Richard Atkinson - David Basten - David Berry - Chris Boek - Zoheb Borbora - Anthony Boulestreau - Jeremy Bowman - Nicolas Brodu - Jody Brownell - David Browning - Soren Caspersen - Thomas A Caswell - Chuanhao Chiu - Brian Cole - Pascal Collet - Martin Cordova - Paolo Cova - Greg Darke - Mike Duffy - Don Elliott - Rune Fauske - Jonathan Gabbai - Serge V. Grachov - Daniel Gredler - Joao Guilherme Del Valle - Hans-Jurgen Greiner - Nick Guenther - Aiman Han - Cameron Hayne - Martin Hoeller (xS+S) - Jon Iles - Wolfgang Irler - Sergei Ivanov - Nina Jeliazkova - Adriaan Joubert - Darren Jung - Xun Kang - Bill Kelemen - Norbert Kiesel - Petr Kopac - Gideon Krause - Dave Law; - Pierre-Marie Le Biot - Simon Legner - Arnaud Lelievre - Wolfgang Lenhard - Leo Leung - David Li - Yan Liu - Tin Luu - Craig MacFarlane - Achilleus Mantzios - John Matthews - Thomas Meier - Jim Moore - Jonathan Nash - Barak Naveh - David M. O'Donnell - Krzysztof Paz - Eric Penfold - Tomer Peretz - Xavier Poinsard - Andrzej Porebski - Viktor Rajewski - Eduardo Ramalho - Michael Rauch - Klaus Rheinwald - Cameron Riley - Dan Rivett - Lukasz Rzeszotarski - Scott Sams - Michel Santos - Thierry Saura - Patrick Schlott - Andreas Schneider - Christoph Schroeder - Jean-Luc SCHWAB - Bryan Scott - Tobias Selb - Darshan Shah - Mofeed Shahin - Michael Siemer - Pady Srinivasan - Greg Steckman - Roger Studner - Gerald Struck - Irv Thomae - Eric Thomas - Rich Unger - Daniel van Enckevort - Laurence Vanhelsuwe - Sylvain Vieujot - Jelai Wang - Mark Watson - Alex Weber - Richard West - Matthew Wright - Benoit Xhenseval - Christian W. Zuckschwerdt - Hari - Sam (oldman) It is possible that I have missed someone on this list, if that applies to you, please e-mail me. Dave Gilbert (dave@jfree.org) JFreeChart Project Leader jfree-jfreechart-cb8ff67/licence-LGPL.txt000066400000000000000000000634761463604235500203560ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! jfree-jfreechart-cb8ff67/pom.xml000066400000000000000000000230011463604235500167500ustar00rootroot00000000000000 4.0.0 JFreeChart jfreechart org.jfree 1.5.5 jar JFree.org http://www.jfree.org/ 2001 JFreeChart is a class library, written in Java, for generating charts. Utilising the Java2D API, it supports a wide range of chart types including bar charts, pie charts, line charts, XY-plots, time series plots, Sankey charts and more. http://www.jfree.org/jfreechart/ https://github.com/jfree/jfreechart/issues GitHub Issues scm:git:git:https://github.com/jfree/jfreechart.git https://github.com/jfree/jfreechart David Gilbert dave@jfree.org GNU Lesser General Public Licence http://www.gnu.org/licenses/lgpl.txt repo javax.servlet servlet-api 2.5 provided org.junit.jupiter junit-jupiter-api 5.10.2 test org.junit.jupiter junit-jupiter-engine 5.10.2 test nl.jqno.equalsverifier equalsverifier 3.16.1 test ossrh https://oss.sonatype.org/content/repositories/snapshots src/test/java org.apache.maven.plugins maven-clean-plugin 3.4.0 org.apache.maven.plugins maven-resources-plugin 3.3.1 ${project.build.sourceEncoding} org.apache.maven.plugins maven-compiler-plugin 3.13.0 1.8 1.8 ${project.build.sourceEncoding} true true org.apache.maven.plugins maven-jar-plugin 3.4.2 org.jfree.jfreechart org.apache.maven.plugins maven-javadoc-plugin 3.7.0 true 8 true org.apache.maven.plugins maven-surefire-plugin 3.3.0 **/*Test.java **/JFreeChartTestSuite.java **/*PackageTests.java maven-failsafe-plugin 3.3.0 org.apache.maven.plugins maven-install-plugin 3.1.2 org.apache.maven.plugins maven-surefire-report-plugin 3.3.0 org.apache.maven.plugins maven-jxr-plugin 3.0.0 org.codehaus.mojo cobertura-maven-plugin 2.5.1 UTF-8 1.8 1.8 release org.apache.maven.plugins maven-gpg-plugin 3.2.4 sign-artifacts verify sign org.sonatype.plugins nexus-staging-maven-plugin 1.7.0 true ossrh https://oss.sonatype.org/ false org.apache.maven.plugins maven-javadoc-plugin 3.7.0 true 8 true attach-javadoc jar org.apache.maven.plugins maven-source-plugin 3.3.1 attach-sources jar-no-fork jfree-jfreechart-cb8ff67/src/000077500000000000000000000000001463604235500162265ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/000077500000000000000000000000001463604235500171525ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/000077500000000000000000000000001463604235500200735ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/000077500000000000000000000000001463604235500206625ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/000077500000000000000000000000001463604235500217555ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/000077500000000000000000000000001463604235500230565ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ChartColor.java000066400000000000000000000204201463604235500257570ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * ChartColor.java * --------------- * (C) Copyright 2003-present, by Cameron Riley and Contributors. * * Original Author: Cameron Riley; * Contributor(s): David Gilbert; * Yuri Blankenstein; * */ package org.jfree.chart; import java.awt.Color; import java.awt.Paint; /** * Class to extend the number of Colors available to the charts. This * extends the java.awt.Color object and extends the number of final * Colors publicly accessible. */ public class ChartColor extends Color { /** A very dark red color. */ public static final Color VERY_DARK_RED = new Color(0x80, 0x00, 0x00); /** A dark red color. */ public static final Color DARK_RED = new Color(0xc0, 0x00, 0x00); /** A light red color. */ public static final Color LIGHT_RED = new Color(0xFF, 0x40, 0x40); /** A very light red color. */ public static final Color VERY_LIGHT_RED = new Color(0xFF, 0x80, 0x80); /** A very dark yellow color. */ public static final Color VERY_DARK_YELLOW = new Color(0x80, 0x80, 0x00); /** A dark yellow color. */ public static final Color DARK_YELLOW = new Color(0xC0, 0xC0, 0x00); /** A light yellow color. */ public static final Color LIGHT_YELLOW = new Color(0xFF, 0xFF, 0x40); /** A very light yellow color. */ public static final Color VERY_LIGHT_YELLOW = new Color(0xFF, 0xFF, 0x80); /** A very dark green color. */ public static final Color VERY_DARK_GREEN = new Color(0x00, 0x80, 0x00); /** A dark green color. */ public static final Color DARK_GREEN = new Color(0x00, 0xC0, 0x00); /** A light green color. */ public static final Color LIGHT_GREEN = new Color(0x40, 0xFF, 0x40); /** A very light green color. */ public static final Color VERY_LIGHT_GREEN = new Color(0x80, 0xFF, 0x80); /** A very dark cyan color. */ public static final Color VERY_DARK_CYAN = new Color(0x00, 0x80, 0x80); /** A dark cyan color. */ public static final Color DARK_CYAN = new Color(0x00, 0xC0, 0xC0); /** A light cyan color. */ public static final Color LIGHT_CYAN = new Color(0x40, 0xFF, 0xFF); /** Aa very light cyan color. */ public static final Color VERY_LIGHT_CYAN = new Color(0x80, 0xFF, 0xFF); /** A very dark blue color. */ public static final Color VERY_DARK_BLUE = new Color(0x00, 0x00, 0x80); /** A dark blue color. */ public static final Color DARK_BLUE = new Color(0x00, 0x00, 0xC0); /** A light blue color. */ public static final Color LIGHT_BLUE = new Color(0x40, 0x40, 0xFF); /** A very light blue color. */ public static final Color VERY_LIGHT_BLUE = new Color(0x80, 0x80, 0xFF); /** A very dark magenta/purple color. */ public static final Color VERY_DARK_MAGENTA = new Color(0x80, 0x00, 0x80); /** A dark magenta color. */ public static final Color DARK_MAGENTA = new Color(0xC0, 0x00, 0xC0); /** A light magenta color. */ public static final Color LIGHT_MAGENTA = new Color(0xFF, 0x40, 0xFF); /** A very light magenta color. */ public static final Color VERY_LIGHT_MAGENTA = new Color(0xFF, 0x80, 0xFF); /** * Creates a Color with an opaque sRGB with red, green and blue values in * range 0-255. * * @param r the red component in range 0x00-0xFF. * @param g the green component in range 0x00-0xFF. * @param b the blue component in range 0x00-0xFF. */ public ChartColor(int r, int g, int b) { super(r, g, b); } /** * Convenience method to return an array of {@code Paint} objects that * represent the pre-defined colors in the {@code Color} and * {@code ChartColor} objects. * * @return An array of objects with the {@code Paint} interface. * @see #createDefaultColorArray() */ public static Paint[] createDefaultPaintArray() { return createDefaultColorArray(); } /** * Convenience method to return an array of {@code Color} objects that * represent the pre-defined colors in the {@code Color} and * {@code ChartColor} objects. * * @return An array of objects with the {@code Color} interface. */ public static Color[] createDefaultColorArray() { return new Color[] { new Color(0xFF, 0x55, 0x55), new Color(0x55, 0x55, 0xFF), new Color(0x55, 0xFF, 0x55), new Color(0xFF, 0xFF, 0x55), new Color(0xFF, 0x55, 0xFF), new Color(0x55, 0xFF, 0xFF), Color.PINK, Color.GRAY, ChartColor.DARK_RED, ChartColor.DARK_BLUE, ChartColor.DARK_GREEN, ChartColor.DARK_YELLOW, ChartColor.DARK_MAGENTA, ChartColor.DARK_CYAN, Color.DARK_GRAY, ChartColor.LIGHT_RED, ChartColor.LIGHT_BLUE, ChartColor.LIGHT_GREEN, ChartColor.LIGHT_YELLOW, ChartColor.LIGHT_MAGENTA, ChartColor.LIGHT_CYAN, Color.LIGHT_GRAY, ChartColor.VERY_DARK_RED, ChartColor.VERY_DARK_BLUE, ChartColor.VERY_DARK_GREEN, ChartColor.VERY_DARK_YELLOW, ChartColor.VERY_DARK_MAGENTA, ChartColor.VERY_DARK_CYAN, ChartColor.VERY_LIGHT_RED, ChartColor.VERY_LIGHT_BLUE, ChartColor.VERY_LIGHT_GREEN, ChartColor.VERY_LIGHT_YELLOW, ChartColor.VERY_LIGHT_MAGENTA, ChartColor.VERY_LIGHT_CYAN }; } /** * Creates an array of {@link Color#darker() darker} {@code colors} to use * for e.g. borders. * * @param colors original colors * @return a new array containing {@link Color#darker() darker} instances of * the original colors. */ public static Color[] createDarkerColorArray(Color[] colors) { final Color[] result = new Color[colors.length]; for (int i = 0; i < colors.length; i++) { result[i] = colors[i].darker(); } return result; } /** * Returns either {@link Color#BLACK black} or {@link Color#WHITE white}, * depending on the provided {@code color} to achieve the best contrast for * e.g. text labels.
* The {@code color} is * converted * from RGB to YIQ and the luminance value (Y; ≈[0 .. 255]) is * used to determine if {@code color} is closer to either {@link Color#BLACK * black} or {@link Color#WHITE white}. * * @param color the color for which the contrasted color is computed * @return either {@link Color#BLACK black} or {@link Color#WHITE white}, * depending on {@code color} */ public static Color getContrastColor(Color color) { // From Wikipedia: The Y component represents the luma information, and // is the only component used by black-and-white television receivers. final double luminanceY = 0.299 * color.getRed() + 0.587 * color.getGreen() + 0.114 * color.getBlue(); return luminanceY >= 128 ? Color.BLACK : Color.WHITE; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ChartFactory.java000066400000000000000000002723611463604235500263250ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * ChartFactory.java * ----------------- * (C) Copyright 2001-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Serge V. Grachov; * Joao Guilherme Del Valle; * Bill Kelemen; * Jon Iles; * Jelai Wang; * Richard Atkinson; * David Browning (for Australian Institute of Marine Science); * Benoit Xhenseval; * */ package org.jfree.chart; import java.awt.Color; import java.awt.Font; import java.text.DateFormat; import java.text.NumberFormat; import java.util.Iterator; import java.util.List; import java.util.Locale; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.DateAxis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.labels.BoxAndWhiskerToolTipGenerator; import org.jfree.chart.labels.HighLowItemLabelGenerator; import org.jfree.chart.labels.IntervalCategoryToolTipGenerator; import org.jfree.chart.labels.ItemLabelAnchor; import org.jfree.chart.labels.ItemLabelPosition; import org.jfree.chart.labels.PieToolTipGenerator; import org.jfree.chart.labels.StandardCategoryToolTipGenerator; import org.jfree.chart.labels.StandardPieSectionLabelGenerator; import org.jfree.chart.labels.StandardPieToolTipGenerator; import org.jfree.chart.labels.StandardXYToolTipGenerator; import org.jfree.chart.labels.StandardXYZToolTipGenerator; import org.jfree.chart.labels.XYToolTipGenerator; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.Marker; import org.jfree.chart.plot.MultiplePiePlot; import org.jfree.chart.plot.PiePlot; import org.jfree.chart.plot.PiePlot3D; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PolarPlot; import org.jfree.chart.plot.RingPlot; import org.jfree.chart.plot.ValueMarker; import org.jfree.chart.plot.WaferMapPlot; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.DefaultPolarItemRenderer; import org.jfree.chart.renderer.WaferMapRenderer; import org.jfree.chart.renderer.category.AreaRenderer; import org.jfree.chart.renderer.category.BarRenderer; import org.jfree.chart.renderer.category.BoxAndWhiskerRenderer; import org.jfree.chart.renderer.category.CategoryItemRenderer; import org.jfree.chart.renderer.category.GanttRenderer; import org.jfree.chart.renderer.category.GradientBarPainter; import org.jfree.chart.renderer.category.LineAndShapeRenderer; import org.jfree.chart.renderer.category.StackedAreaRenderer; import org.jfree.chart.renderer.category.StackedBarRenderer; import org.jfree.chart.renderer.category.StandardBarPainter; import org.jfree.chart.renderer.category.WaterfallBarRenderer; import org.jfree.chart.renderer.xy.CandlestickRenderer; import org.jfree.chart.renderer.xy.GradientXYBarPainter; import org.jfree.chart.renderer.xy.HighLowRenderer; import org.jfree.chart.renderer.xy.StackedXYAreaRenderer2; import org.jfree.chart.renderer.xy.StandardXYBarPainter; import org.jfree.chart.renderer.xy.WindItemRenderer; import org.jfree.chart.renderer.xy.XYAreaRenderer; import org.jfree.chart.renderer.xy.XYBarRenderer; import org.jfree.chart.renderer.xy.XYBoxAndWhiskerRenderer; import org.jfree.chart.renderer.xy.XYBubbleRenderer; import org.jfree.chart.renderer.xy.XYItemRenderer; import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; import org.jfree.chart.renderer.xy.XYStepAreaRenderer; import org.jfree.chart.renderer.xy.XYStepRenderer; import org.jfree.chart.title.TextTitle; import org.jfree.chart.ui.Layer; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.ui.TextAnchor; import org.jfree.chart.urls.PieURLGenerator; import org.jfree.chart.urls.StandardCategoryURLGenerator; import org.jfree.chart.urls.StandardPieURLGenerator; import org.jfree.chart.urls.StandardXYURLGenerator; import org.jfree.chart.urls.StandardXYZURLGenerator; import org.jfree.chart.urls.XYURLGenerator; import org.jfree.chart.util.Args; import org.jfree.chart.util.TableOrder; import org.jfree.data.category.CategoryDataset; import org.jfree.data.category.IntervalCategoryDataset; import org.jfree.data.general.DefaultPieDataset; import org.jfree.data.general.PieDataset; import org.jfree.data.general.WaferMapDataset; import org.jfree.data.statistics.BoxAndWhiskerCategoryDataset; import org.jfree.data.statistics.BoxAndWhiskerXYDataset; import org.jfree.data.xy.IntervalXYDataset; import org.jfree.data.xy.OHLCDataset; import org.jfree.data.xy.TableXYDataset; import org.jfree.data.xy.WindDataset; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYZDataset; /** * A collection of utility methods for creating some standard charts with * JFreeChart. */ public abstract class ChartFactory { /** The chart theme. */ private static ChartTheme currentTheme = new StandardChartTheme("JFree"); /** * Returns the current chart theme used by the factory. * * @return The chart theme. * * @see #setChartTheme(ChartTheme) * @see ChartUtils#applyCurrentTheme(JFreeChart) */ public static ChartTheme getChartTheme() { return currentTheme; } /** * Sets the current chart theme. This will be applied to all new charts * created via methods in this class. * * @param theme the theme ({@code null} not permitted). * * @see #getChartTheme() * @see ChartUtils#applyCurrentTheme(JFreeChart) */ public static void setChartTheme(ChartTheme theme) { Args.nullNotPermitted(theme, "theme"); currentTheme = theme; // here we do a check to see if the user is installing the "Legacy" // theme, and reset the bar painters in that case... if (theme instanceof StandardChartTheme) { StandardChartTheme sct = (StandardChartTheme) theme; if (sct.getName().equals("Legacy")) { BarRenderer.setDefaultBarPainter(new StandardBarPainter()); XYBarRenderer.setDefaultBarPainter(new StandardXYBarPainter()); } else { BarRenderer.setDefaultBarPainter(new GradientBarPainter()); XYBarRenderer.setDefaultBarPainter(new GradientXYBarPainter()); } } } /** * Creates a pie chart with default settings. *

* The chart object returned by this method uses a {@link PiePlot} instance * as the plot. * * @param title the chart title ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * @param legend a flag specifying whether or not a legend is required. * @param tooltips configure chart to generate tool tips? * @param locale the locale ({@code null} not permitted). * * @return A pie chart. */ public static JFreeChart createPieChart(String title, PieDataset dataset, boolean legend, boolean tooltips, Locale locale) { PiePlot plot = new PiePlot(dataset); plot.setLabelGenerator(new StandardPieSectionLabelGenerator(locale)); plot.setInsets(new RectangleInsets(0.0, 5.0, 5.0, 5.0)); if (tooltips) { plot.setToolTipGenerator(new StandardPieToolTipGenerator(locale)); } JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); currentTheme.apply(chart); return chart; } /** * Creates a pie chart with default settings. *

* The chart object returned by this method uses a {@link PiePlot} instance * as the plot. * * @param title the chart title ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * * @return A pie chart. */ public static JFreeChart createPieChart(String title, PieDataset dataset) { return createPieChart(title, dataset, true, true, false); } /** * Creates a pie chart with default settings. *

* The chart object returned by this method uses a {@link PiePlot} instance * as the plot. * * @param title the chart title ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * @param legend a flag specifying whether or not a legend is required. * @param tooltips configure chart to generate tool tips? * @param urls configure chart to generate URLs? * * @return A pie chart. */ public static JFreeChart createPieChart(String title, PieDataset dataset, boolean legend, boolean tooltips, boolean urls) { PiePlot plot = new PiePlot(dataset); plot.setLabelGenerator(new StandardPieSectionLabelGenerator()); plot.setInsets(new RectangleInsets(0.0, 5.0, 5.0, 5.0)); if (tooltips) { plot.setToolTipGenerator(new StandardPieToolTipGenerator()); } if (urls) { plot.setURLGenerator(new StandardPieURLGenerator()); } JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); currentTheme.apply(chart); return chart; } /** * Creates a pie chart with default settings that compares 2 datasets. * The colour of each section will be determined by the move from the value * for the same key in {@code previousDataset}. ie if value1 > * value2 then the section will be in green (unless * {@code greenForIncrease} is {@code false}, in which case it * would be {@code red}). Each section can have a shade of red or * green as the difference can be tailored between 0% (black) and * percentDiffForMaxScale% (bright red/green). *

* For instance if {@code percentDiffForMaxScale} is 10 (10%), a * difference of 5% will have a half shade of red/green, a difference of * 10% or more will have a maximum shade/brightness of red/green. *

* The chart object returned by this method uses a {@link PiePlot} instance * as the plot. *

* Written by Benoit * Xhenseval. * * @param title the chart title ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * @param previousDataset the dataset for the last run, this will be used * to compare each key in the dataset * @param percentDiffForMaxScale scale goes from bright red/green to black, * percentDiffForMaxScale indicate the change * required to reach top scale. * @param greenForIncrease an increase since previousDataset will be * displayed in green (decrease red) if true. * @param legend a flag specifying whether or not a legend is required. * @param tooltips configure chart to generate tool tips? * @param locale the locale ({@code null} not permitted). * @param subTitle displays a subtitle with colour scheme if true * @param showDifference create a new dataset that will show the % * difference between the two datasets. * * @return A pie chart. */ public static JFreeChart createPieChart(String title, PieDataset dataset, PieDataset previousDataset, int percentDiffForMaxScale, boolean greenForIncrease, boolean legend, boolean tooltips, Locale locale, boolean subTitle, boolean showDifference) { PiePlot plot = new PiePlot(dataset); plot.setLabelGenerator(new StandardPieSectionLabelGenerator(locale)); plot.setInsets(new RectangleInsets(0.0, 5.0, 5.0, 5.0)); if (tooltips) { plot.setToolTipGenerator(new StandardPieToolTipGenerator(locale)); } List keys = dataset.getKeys(); DefaultPieDataset series = null; if (showDifference) { series = new DefaultPieDataset(); } double colorPerPercent = 255.0 / percentDiffForMaxScale; for (Iterator it = keys.iterator(); it.hasNext();) { Comparable key = (Comparable) it.next(); Number newValue = dataset.getValue(key); Number oldValue = previousDataset.getValue(key); if (oldValue == null) { if (greenForIncrease) { plot.setSectionPaint(key, Color.GREEN); } else { plot.setSectionPaint(key, Color.RED); } if (showDifference) { assert series != null; // suppresses compiler warning series.setValue(key + " (+100%)", newValue); } } else { double percentChange = (newValue.doubleValue() / oldValue.doubleValue() - 1.0) * 100.0; double shade = (Math.abs(percentChange) >= percentDiffForMaxScale ? 255 : Math.abs(percentChange) * colorPerPercent); if (greenForIncrease && newValue.doubleValue() > oldValue.doubleValue() || !greenForIncrease && newValue.doubleValue() < oldValue.doubleValue()) { plot.setSectionPaint(key, new Color(0, (int) shade, 0)); } else { plot.setSectionPaint(key, new Color((int) shade, 0, 0)); } if (showDifference) { assert series != null; // suppresses compiler warning series.setValue(key + " (" + (percentChange >= 0 ? "+" : "") + NumberFormat.getPercentInstance().format( percentChange / 100.0) + ")", newValue); } } } if (showDifference) { plot.setDataset(series); } JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); if (subTitle) { TextTitle subtitle = new TextTitle("Bright " + (greenForIncrease ? "red" : "green") + "=change >=-" + percentDiffForMaxScale + "%, Bright " + (!greenForIncrease ? "red" : "green") + "=change >=+" + percentDiffForMaxScale + "%", new Font("SansSerif", Font.PLAIN, 10)); chart.addSubtitle(subtitle); } currentTheme.apply(chart); return chart; } /** * Creates a pie chart with default settings that compares 2 datasets. * The colour of each section will be determined by the move from the value * for the same key in {@code previousDataset}. ie if value1 > * value2 then the section will be in green (unless * {@code greenForIncrease} is {@code false}, in which case it * would be {@code red}). Each section can have a shade of red or * green as the difference can be tailored between 0% (black) and * percentDiffForMaxScale% (bright red/green). *

* For instance if {@code percentDiffForMaxScale} is 10 (10%), a * difference of 5% will have a half shade of red/green, a difference of * 10% or more will have a maximum shade/brightness of red/green. *

* The chart object returned by this method uses a {@link PiePlot} instance * as the plot. *

* Written by Benoit * Xhenseval. * * @param title the chart title ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * @param previousDataset the dataset for the last run, this will be used * to compare each key in the dataset * @param percentDiffForMaxScale scale goes from bright red/green to black, * percentDiffForMaxScale indicate the change * required to reach top scale. * @param greenForIncrease an increase since previousDataset will be * displayed in green (decrease red) if true. * @param legend a flag specifying whether or not a legend is required. * @param tooltips configure chart to generate tool tips? * @param urls configure chart to generate URLs? * @param subTitle displays a subtitle with colour scheme if true * @param showDifference create a new dataset that will show the % * difference between the two datasets. * * @return A pie chart. */ public static JFreeChart createPieChart(String title, PieDataset dataset, PieDataset previousDataset, int percentDiffForMaxScale, boolean greenForIncrease, boolean legend, boolean tooltips, boolean urls, boolean subTitle, boolean showDifference) { PiePlot plot = new PiePlot(dataset); plot.setLabelGenerator(new StandardPieSectionLabelGenerator()); plot.setInsets(new RectangleInsets(0.0, 5.0, 5.0, 5.0)); if (tooltips) { plot.setToolTipGenerator(new StandardPieToolTipGenerator()); } if (urls) { plot.setURLGenerator(new StandardPieURLGenerator()); } List keys = dataset.getKeys(); DefaultPieDataset series = null; if (showDifference) { series = new DefaultPieDataset(); } double colorPerPercent = 255.0 / percentDiffForMaxScale; for (Iterator it = keys.iterator(); it.hasNext();) { Comparable key = (Comparable) it.next(); Number newValue = dataset.getValue(key); Number oldValue = previousDataset.getValue(key); if (oldValue == null) { if (greenForIncrease) { plot.setSectionPaint(key, Color.GREEN); } else { plot.setSectionPaint(key, Color.RED); } if (showDifference) { assert series != null; // suppresses compiler warning series.setValue(key + " (+100%)", newValue); } } else { double percentChange = (newValue.doubleValue() / oldValue.doubleValue() - 1.0) * 100.0; double shade = (Math.abs(percentChange) >= percentDiffForMaxScale ? 255 : Math.abs(percentChange) * colorPerPercent); if (greenForIncrease && newValue.doubleValue() > oldValue.doubleValue() || !greenForIncrease && newValue.doubleValue() < oldValue.doubleValue()) { plot.setSectionPaint(key, new Color(0, (int) shade, 0)); } else { plot.setSectionPaint(key, new Color((int) shade, 0, 0)); } if (showDifference) { assert series != null; // suppresses compiler warning series.setValue(key + " (" + (percentChange >= 0 ? "+" : "") + NumberFormat.getPercentInstance().format( percentChange / 100.0) + ")", newValue); } } } if (showDifference) { plot.setDataset(series); } JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); if (subTitle) { TextTitle subtitle = new TextTitle("Bright " + (greenForIncrease ? "red" : "green") + "=change >=-" + percentDiffForMaxScale + "%, Bright " + (!greenForIncrease ? "red" : "green") + "=change >=+" + percentDiffForMaxScale + "%", new Font("SansSerif", Font.PLAIN, 10)); chart.addSubtitle(subtitle); } currentTheme.apply(chart); return chart; } /** * Creates a ring chart with default settings. *

* The chart object returned by this method uses a {@link RingPlot} * instance as the plot. * * @param title the chart title ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * @param legend a flag specifying whether or not a legend is required. * @param tooltips configure chart to generate tool tips? * @param locale the locale ({@code null} not permitted). * * @return A ring chart. */ public static JFreeChart createRingChart(String title, PieDataset dataset, boolean legend, boolean tooltips, Locale locale) { RingPlot plot = new RingPlot(dataset); plot.setLabelGenerator(new StandardPieSectionLabelGenerator(locale)); plot.setInsets(new RectangleInsets(0.0, 5.0, 5.0, 5.0)); if (tooltips) { plot.setToolTipGenerator(new StandardPieToolTipGenerator(locale)); } JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); currentTheme.apply(chart); return chart; } /** * Creates a ring chart with default settings. *

* The chart object returned by this method uses a {@link RingPlot} * instance as the plot. * * @param title the chart title ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * @param legend a flag specifying whether or not a legend is required. * @param tooltips configure chart to generate tool tips? * @param urls configure chart to generate URLs? * * @return A ring chart. */ public static JFreeChart createRingChart(String title, PieDataset dataset, boolean legend, boolean tooltips, boolean urls) { RingPlot plot = new RingPlot(dataset); plot.setLabelGenerator(new StandardPieSectionLabelGenerator()); plot.setInsets(new RectangleInsets(0.0, 5.0, 5.0, 5.0)); if (tooltips) { plot.setToolTipGenerator(new StandardPieToolTipGenerator()); } if (urls) { plot.setURLGenerator(new StandardPieURLGenerator()); } JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); currentTheme.apply(chart); return chart; } /** * Creates a chart that displays multiple pie plots. The chart object * returned by this method uses a {@link MultiplePiePlot} instance as the * plot. * * @param title the chart title ({@code null} permitted). * @param dataset the dataset ({@code null} permitted). * @param order the order that the data is extracted (by row or by column) * ({@code null} not permitted). * @param legend include a legend? * @param tooltips generate tooltips? * @param urls generate URLs? * * @return A chart. */ public static JFreeChart createMultiplePieChart(String title, CategoryDataset dataset, TableOrder order, boolean legend, boolean tooltips, boolean urls) { Args.nullNotPermitted(order, "order"); MultiplePiePlot plot = new MultiplePiePlot(dataset); plot.setDataExtractOrder(order); plot.setBackgroundPaint(null); plot.setOutlineStroke(null); if (tooltips) { PieToolTipGenerator tooltipGenerator = new StandardPieToolTipGenerator(); PiePlot pp = (PiePlot) plot.getPieChart().getPlot(); pp.setToolTipGenerator(tooltipGenerator); } if (urls) { PieURLGenerator urlGenerator = new StandardPieURLGenerator(); PiePlot pp = (PiePlot) plot.getPieChart().getPlot(); pp.setURLGenerator(urlGenerator); } JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); currentTheme.apply(chart); return chart; } /** * Creates a 3D pie chart using the specified dataset. The chart object * returned by this method uses a {@link PiePlot3D} instance as the * plot. * * @param title the chart title ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * @param legend a flag specifying whether or not a legend is required. * @param tooltips configure chart to generate tool tips? * @param locale the locale ({@code null} not permitted). * * @return A pie chart. * * @deprecated For 3D pie charts, use Orson Charts (https://github.com/jfree/orson-charts). */ public static JFreeChart createPieChart3D(String title, PieDataset dataset, boolean legend, boolean tooltips, Locale locale) { Args.nullNotPermitted(locale, "locale"); PiePlot3D plot = new PiePlot3D(dataset); plot.setInsets(new RectangleInsets(0.0, 5.0, 5.0, 5.0)); if (tooltips) { plot.setToolTipGenerator(new StandardPieToolTipGenerator(locale)); } JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); currentTheme.apply(chart); return chart; } /** * Creates a 3D pie chart using the specified dataset. The chart object * returned by this method uses a {@link PiePlot3D} instance as the * plot. * * @param title the chart title ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * * @return A pie chart. * * @deprecated For 3D pie charts, use Orson Charts (https://github.com/jfree/orson-charts). */ public static JFreeChart createPieChart3D(String title, PieDataset dataset) { return createPieChart3D(title, dataset, true, true, false); } /** * Creates a 3D pie chart using the specified dataset. The chart object * returned by this method uses a {@link PiePlot3D} instance as the * plot. * * @param title the chart title ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * @param legend a flag specifying whether or not a legend is required. * @param tooltips configure chart to generate tool tips? * @param urls configure chart to generate URLs? * * @return A pie chart. * @deprecated For 3D pie charts, use Orson Charts (https://github.com/jfree/orson-charts). */ public static JFreeChart createPieChart3D(String title, PieDataset dataset, boolean legend, boolean tooltips, boolean urls) { PiePlot3D plot = new PiePlot3D(dataset); plot.setInsets(new RectangleInsets(0.0, 5.0, 5.0, 5.0)); if (tooltips) { plot.setToolTipGenerator(new StandardPieToolTipGenerator()); } if (urls) { plot.setURLGenerator(new StandardPieURLGenerator()); } JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); currentTheme.apply(chart); return chart; } /** * Creates a chart that displays multiple pie plots. The chart object * returned by this method uses a {@link MultiplePiePlot} instance as the * plot. * * @param title the chart title ({@code null} permitted). * @param dataset the dataset ({@code null} permitted). * @param order the order that the data is extracted (by row or by column) * ({@code null} not permitted). * @param legend include a legend? * @param tooltips generate tooltips? * @param urls generate URLs? * * @return A chart. */ public static JFreeChart createMultiplePieChart3D(String title, CategoryDataset dataset, TableOrder order, boolean legend, boolean tooltips, boolean urls) { Args.nullNotPermitted(order, "order"); MultiplePiePlot plot = new MultiplePiePlot(dataset); plot.setDataExtractOrder(order); plot.setBackgroundPaint(null); plot.setOutlineStroke(null); JFreeChart pieChart = new JFreeChart(new PiePlot3D(null)); TextTitle seriesTitle = new TextTitle("Series Title", new Font("SansSerif", Font.BOLD, 12)); seriesTitle.setPosition(RectangleEdge.BOTTOM); pieChart.setTitle(seriesTitle); pieChart.removeLegend(); pieChart.setBackgroundPaint(null); plot.setPieChart(pieChart); if (tooltips) { PieToolTipGenerator tooltipGenerator = new StandardPieToolTipGenerator(); PiePlot pp = (PiePlot) plot.getPieChart().getPlot(); pp.setToolTipGenerator(tooltipGenerator); } if (urls) { PieURLGenerator urlGenerator = new StandardPieURLGenerator(); PiePlot pp = (PiePlot) plot.getPieChart().getPlot(); pp.setURLGenerator(urlGenerator); } JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); currentTheme.apply(chart); return chart; } /** * Creates a bar chart with a vertical orientation. The chart object * returned by this method uses a {@link CategoryPlot} instance as the * plot, with a {@link CategoryAxis} for the domain axis, a * {@link NumberAxis} as the range axis, and a {@link BarRenderer} as the * renderer. * * @param title the chart title ({@code null} permitted). * @param categoryAxisLabel the label for the category axis * ({@code null} permitted). * @param valueAxisLabel the label for the value axis * ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * * @return A bar chart. */ public static JFreeChart createBarChart(String title, String categoryAxisLabel, String valueAxisLabel, CategoryDataset dataset) { return createBarChart(title, categoryAxisLabel, valueAxisLabel, dataset, PlotOrientation.VERTICAL, true, true, false); } /** * Creates a bar chart. The chart object returned by this method uses a * {@link CategoryPlot} instance as the plot, with a {@link CategoryAxis} * for the domain axis, a {@link NumberAxis} as the range axis, and a * {@link BarRenderer} as the renderer. * * @param title the chart title ({@code null} permitted). * @param categoryAxisLabel the label for the category axis * ({@code null} permitted). * @param valueAxisLabel the label for the value axis * ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * @param orientation the plot orientation (horizontal or vertical) * ({@code null} not permitted). * @param legend a flag specifying whether or not a legend is required. * @param tooltips configure chart to generate tool tips? * @param urls configure chart to generate URLs? * * @return A bar chart. */ public static JFreeChart createBarChart(String title, String categoryAxisLabel, String valueAxisLabel, CategoryDataset dataset, PlotOrientation orientation, boolean legend, boolean tooltips, boolean urls) { Args.nullNotPermitted(orientation, "orientation"); CategoryAxis categoryAxis = new CategoryAxis(categoryAxisLabel); ValueAxis valueAxis = new NumberAxis(valueAxisLabel); BarRenderer renderer = new BarRenderer(); if (orientation == PlotOrientation.HORIZONTAL) { ItemLabelPosition position1 = new ItemLabelPosition( ItemLabelAnchor.OUTSIDE3, TextAnchor.CENTER_LEFT); renderer.setDefaultPositiveItemLabelPosition(position1); ItemLabelPosition position2 = new ItemLabelPosition( ItemLabelAnchor.OUTSIDE9, TextAnchor.CENTER_RIGHT); renderer.setDefaultNegativeItemLabelPosition(position2); } else if (orientation == PlotOrientation.VERTICAL) { ItemLabelPosition position1 = new ItemLabelPosition( ItemLabelAnchor.OUTSIDE12, TextAnchor.BOTTOM_CENTER); renderer.setDefaultPositiveItemLabelPosition(position1); ItemLabelPosition position2 = new ItemLabelPosition( ItemLabelAnchor.OUTSIDE6, TextAnchor.TOP_CENTER); renderer.setDefaultNegativeItemLabelPosition(position2); } if (tooltips) { renderer.setDefaultToolTipGenerator( new StandardCategoryToolTipGenerator()); } if (urls) { renderer.setDefaultItemURLGenerator( new StandardCategoryURLGenerator()); } CategoryPlot plot = new CategoryPlot(dataset, categoryAxis, valueAxis, renderer); plot.setOrientation(orientation); JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); currentTheme.apply(chart); return chart; } /** * Creates a stacked bar chart with default settings. The chart object * returned by this method uses a {@link CategoryPlot} instance as the * plot, with a {@link CategoryAxis} for the domain axis, a * {@link NumberAxis} as the range axis, and a {@link StackedBarRenderer} * as the renderer. * * @param title the chart title ({@code null} permitted). * @param domainAxisLabel the label for the category axis * ({@code null} permitted). * @param rangeAxisLabel the label for the value axis * ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * * @return A stacked bar chart. */ public static JFreeChart createStackedBarChart(String title, String domainAxisLabel, String rangeAxisLabel, CategoryDataset dataset) { return createStackedBarChart(title, domainAxisLabel, rangeAxisLabel, dataset, PlotOrientation.VERTICAL, true, true, false); } /** * Creates a stacked bar chart with default settings. The chart object * returned by this method uses a {@link CategoryPlot} instance as the * plot, with a {@link CategoryAxis} for the domain axis, a * {@link NumberAxis} as the range axis, and a {@link StackedBarRenderer} * as the renderer. * * @param title the chart title ({@code null} permitted). * @param domainAxisLabel the label for the category axis * ({@code null} permitted). * @param rangeAxisLabel the label for the value axis * ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * @param orientation the orientation of the chart (horizontal or * vertical) ({@code null} not permitted). * @param legend a flag specifying whether or not a legend is required. * @param tooltips configure chart to generate tool tips? * @param urls configure chart to generate URLs? * * @return A stacked bar chart. */ public static JFreeChart createStackedBarChart(String title, String domainAxisLabel, String rangeAxisLabel, CategoryDataset dataset, PlotOrientation orientation, boolean legend, boolean tooltips, boolean urls) { Args.nullNotPermitted(orientation, "orientation"); CategoryAxis categoryAxis = new CategoryAxis(domainAxisLabel); ValueAxis valueAxis = new NumberAxis(rangeAxisLabel); StackedBarRenderer renderer = new StackedBarRenderer(); if (tooltips) { renderer.setDefaultToolTipGenerator( new StandardCategoryToolTipGenerator()); } if (urls) { renderer.setDefaultItemURLGenerator( new StandardCategoryURLGenerator()); } CategoryPlot plot = new CategoryPlot(dataset, categoryAxis, valueAxis, renderer); plot.setOrientation(orientation); JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); currentTheme.apply(chart); return chart; } /** * Creates an area chart with default settings. The chart object returned * by this method uses a {@link CategoryPlot} instance as the plot, with a * {@link CategoryAxis} for the domain axis, a {@link NumberAxis} as the * range axis, and an {@link AreaRenderer} as the renderer. * * @param title the chart title ({@code null} permitted). * @param categoryAxisLabel the label for the category axis * ({@code null} permitted). * @param valueAxisLabel the label for the value axis ({@code null} * permitted). * @param dataset the dataset for the chart ({@code null} permitted). * * @return An area chart. */ public static JFreeChart createAreaChart(String title, String categoryAxisLabel, String valueAxisLabel, CategoryDataset dataset) { return createAreaChart(title, categoryAxisLabel, valueAxisLabel, dataset, PlotOrientation.VERTICAL, true, true, false); } /** * Creates an area chart with default settings. The chart object returned * by this method uses a {@link CategoryPlot} instance as the plot, with a * {@link CategoryAxis} for the domain axis, a {@link NumberAxis} as the * range axis, and an {@link AreaRenderer} as the renderer. * * @param title the chart title ({@code null} permitted). * @param categoryAxisLabel the label for the category axis * ({@code null} permitted). * @param valueAxisLabel the label for the value axis ({@code null} * permitted). * @param dataset the dataset for the chart ({@code null} permitted). * @param orientation the plot orientation ({@code null} not * permitted). * @param legend a flag specifying whether or not a legend is required. * @param tooltips configure chart to generate tool tips? * @param urls configure chart to generate URLs? * * @return An area chart. */ public static JFreeChart createAreaChart(String title, String categoryAxisLabel, String valueAxisLabel, CategoryDataset dataset, PlotOrientation orientation, boolean legend, boolean tooltips, boolean urls) { Args.nullNotPermitted(orientation, "orientation"); CategoryAxis categoryAxis = new CategoryAxis(categoryAxisLabel); categoryAxis.setCategoryMargin(0.0); ValueAxis valueAxis = new NumberAxis(valueAxisLabel); AreaRenderer renderer = new AreaRenderer(); if (tooltips) { renderer.setDefaultToolTipGenerator( new StandardCategoryToolTipGenerator()); } if (urls) { renderer.setDefaultItemURLGenerator( new StandardCategoryURLGenerator()); } CategoryPlot plot = new CategoryPlot(dataset, categoryAxis, valueAxis, renderer); plot.setOrientation(orientation); JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); currentTheme.apply(chart); return chart; } /** * Creates a stacked area chart with default settings. The chart object * returned by this method uses a {@link CategoryPlot} instance as the * plot, with a {@link CategoryAxis} for the domain axis, a * {@link NumberAxis} as the range axis, and a {@link StackedAreaRenderer} * as the renderer. * * @param title the chart title ({@code null} permitted). * @param categoryAxisLabel the label for the category axis * ({@code null} permitted). * @param valueAxisLabel the label for the value axis ({@code null} * permitted). * @param dataset the dataset for the chart ({@code null} permitted). * * @return A stacked area chart. */ public static JFreeChart createStackedAreaChart(String title, String categoryAxisLabel, String valueAxisLabel, CategoryDataset dataset) { return createStackedAreaChart(title, categoryAxisLabel, valueAxisLabel, dataset, PlotOrientation.VERTICAL, true, true, false); } /** * Creates a stacked area chart with default settings. The chart object * returned by this method uses a {@link CategoryPlot} instance as the * plot, with a {@link CategoryAxis} for the domain axis, a * {@link NumberAxis} as the range axis, and a {@link StackedAreaRenderer} * as the renderer. * * @param title the chart title ({@code null} permitted). * @param categoryAxisLabel the label for the category axis * ({@code null} permitted). * @param valueAxisLabel the label for the value axis ({@code null} * permitted). * @param dataset the dataset for the chart ({@code null} permitted). * @param orientation the plot orientation (horizontal or vertical) * ({@code null} not permitted). * @param legend a flag specifying whether or not a legend is required. * @param tooltips configure chart to generate tool tips? * @param urls configure chart to generate URLs? * * @return A stacked area chart. */ public static JFreeChart createStackedAreaChart(String title, String categoryAxisLabel, String valueAxisLabel, CategoryDataset dataset, PlotOrientation orientation, boolean legend, boolean tooltips, boolean urls) { Args.nullNotPermitted(orientation, "orientation"); CategoryAxis categoryAxis = new CategoryAxis(categoryAxisLabel); categoryAxis.setCategoryMargin(0.0); ValueAxis valueAxis = new NumberAxis(valueAxisLabel); StackedAreaRenderer renderer = new StackedAreaRenderer(); if (tooltips) { renderer.setDefaultToolTipGenerator( new StandardCategoryToolTipGenerator()); } if (urls) { renderer.setDefaultItemURLGenerator( new StandardCategoryURLGenerator()); } CategoryPlot plot = new CategoryPlot(dataset, categoryAxis, valueAxis, renderer); plot.setOrientation(orientation); JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); currentTheme.apply(chart); return chart; } /** * Creates a line chart with default settings. The chart object returned * by this method uses a {@link CategoryPlot} instance as the plot, with a * {@link CategoryAxis} for the domain axis, a {@link NumberAxis} as the * range axis, and a {@link LineAndShapeRenderer} as the renderer. * * @param title the chart title ({@code null} permitted). * @param categoryAxisLabel the label for the category axis * ({@code null} permitted). * @param valueAxisLabel the label for the value axis ({@code null} * permitted). * @param dataset the dataset for the chart ({@code null} permitted). * * @return A line chart. */ public static JFreeChart createLineChart(String title, String categoryAxisLabel, String valueAxisLabel, CategoryDataset dataset) { return createLineChart(title, categoryAxisLabel, valueAxisLabel, dataset, PlotOrientation.VERTICAL, true, true, false); } /** * Creates a line chart with default settings. The chart object returned * by this method uses a {@link CategoryPlot} instance as the plot, with a * {@link CategoryAxis} for the domain axis, a {@link NumberAxis} as the * range axis, and a {@link LineAndShapeRenderer} as the renderer. * * @param title the chart title ({@code null} permitted). * @param categoryAxisLabel the label for the category axis * ({@code null} permitted). * @param valueAxisLabel the label for the value axis ({@code null} * permitted). * @param dataset the dataset for the chart ({@code null} permitted). * @param orientation the chart orientation (horizontal or vertical) * ({@code null} not permitted). * @param legend a flag specifying whether or not a legend is required. * @param tooltips configure chart to generate tool tips? * @param urls configure chart to generate URLs? * * @return A line chart. */ public static JFreeChart createLineChart(String title, String categoryAxisLabel, String valueAxisLabel, CategoryDataset dataset, PlotOrientation orientation, boolean legend, boolean tooltips, boolean urls) { Args.nullNotPermitted(orientation, "orientation"); CategoryAxis categoryAxis = new CategoryAxis(categoryAxisLabel); ValueAxis valueAxis = new NumberAxis(valueAxisLabel); LineAndShapeRenderer renderer = new LineAndShapeRenderer(true, false); if (tooltips) { renderer.setDefaultToolTipGenerator( new StandardCategoryToolTipGenerator()); } if (urls) { renderer.setDefaultItemURLGenerator( new StandardCategoryURLGenerator()); } CategoryPlot plot = new CategoryPlot(dataset, categoryAxis, valueAxis, renderer); plot.setOrientation(orientation); JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); currentTheme.apply(chart); return chart; } /** * Creates a Gantt chart using the supplied attributes plus default values * where required. The chart object returned by this method uses a * {@link CategoryPlot} instance as the plot, with a {@link CategoryAxis} * for the domain axis, a {@link DateAxis} as the range axis, and a * {@link GanttRenderer} as the renderer. * * @param title the chart title ({@code null} permitted). * @param categoryAxisLabel the label for the category axis * ({@code null} permitted). * @param dateAxisLabel the label for the date axis * ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * * @return A Gantt chart. */ public static JFreeChart createGanttChart(String title, String categoryAxisLabel, String dateAxisLabel, IntervalCategoryDataset dataset) { return createGanttChart(title, categoryAxisLabel, dateAxisLabel, dataset, true, true, false); } /** * Creates a Gantt chart using the supplied attributes plus default values * where required. The chart object returned by this method uses a * {@link CategoryPlot} instance as the plot, with a {@link CategoryAxis} * for the domain axis, a {@link DateAxis} as the range axis, and a * {@link GanttRenderer} as the renderer. * * @param title the chart title ({@code null} permitted). * @param categoryAxisLabel the label for the category axis * ({@code null} permitted). * @param dateAxisLabel the label for the date axis * ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * @param legend a flag specifying whether or not a legend is required. * @param tooltips configure chart to generate tool tips? * @param urls configure chart to generate URLs? * * @return A Gantt chart. */ public static JFreeChart createGanttChart(String title, String categoryAxisLabel, String dateAxisLabel, IntervalCategoryDataset dataset, boolean legend, boolean tooltips, boolean urls) { CategoryAxis categoryAxis = new CategoryAxis(categoryAxisLabel); DateAxis dateAxis = new DateAxis(dateAxisLabel); CategoryItemRenderer renderer = new GanttRenderer(); if (tooltips) { renderer.setDefaultToolTipGenerator( new IntervalCategoryToolTipGenerator( "{3} - {4}", DateFormat.getDateInstance())); } if (urls) { renderer.setDefaultItemURLGenerator( new StandardCategoryURLGenerator()); } CategoryPlot plot = new CategoryPlot(dataset, categoryAxis, dateAxis, renderer); plot.setOrientation(PlotOrientation.HORIZONTAL); JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); currentTheme.apply(chart); return chart; } /** * Creates a waterfall chart. The chart object returned by this method * uses a {@link CategoryPlot} instance as the plot, with a * {@link CategoryAxis} for the domain axis, a {@link NumberAxis} as the * range axis, and a {@link WaterfallBarRenderer} as the renderer. * * @param title the chart title ({@code null} permitted). * @param categoryAxisLabel the label for the category axis * ({@code null} permitted). * @param valueAxisLabel the label for the value axis ({@code null} * permitted). * @param dataset the dataset for the chart ({@code null} permitted). * @param orientation the plot orientation (horizontal or vertical) * ({@code null} NOT permitted). * @param legend a flag specifying whether or not a legend is required. * @param tooltips configure chart to generate tool tips? * @param urls configure chart to generate URLs? * * @return A waterfall chart. */ public static JFreeChart createWaterfallChart(String title, String categoryAxisLabel, String valueAxisLabel, CategoryDataset dataset, PlotOrientation orientation, boolean legend, boolean tooltips, boolean urls) { Args.nullNotPermitted(orientation, "orientation"); CategoryAxis categoryAxis = new CategoryAxis(categoryAxisLabel); categoryAxis.setCategoryMargin(0.0); ValueAxis valueAxis = new NumberAxis(valueAxisLabel); WaterfallBarRenderer renderer = new WaterfallBarRenderer(); if (orientation == PlotOrientation.HORIZONTAL) { ItemLabelPosition position = new ItemLabelPosition( ItemLabelAnchor.CENTER, TextAnchor.CENTER, TextAnchor.CENTER, Math.PI / 2.0); renderer.setDefaultPositiveItemLabelPosition(position); renderer.setDefaultNegativeItemLabelPosition(position); } else if (orientation == PlotOrientation.VERTICAL) { ItemLabelPosition position = new ItemLabelPosition( ItemLabelAnchor.CENTER, TextAnchor.CENTER, TextAnchor.CENTER, 0.0); renderer.setDefaultPositiveItemLabelPosition(position); renderer.setDefaultNegativeItemLabelPosition(position); } if (tooltips) { StandardCategoryToolTipGenerator generator = new StandardCategoryToolTipGenerator(); renderer.setDefaultToolTipGenerator(generator); } if (urls) { renderer.setDefaultItemURLGenerator( new StandardCategoryURLGenerator()); } CategoryPlot plot = new CategoryPlot(dataset, categoryAxis, valueAxis, renderer); plot.clearRangeMarkers(); Marker baseline = new ValueMarker(0.0); baseline.setPaint(Color.BLACK); plot.addRangeMarker(baseline, Layer.FOREGROUND); plot.setOrientation(orientation); JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); currentTheme.apply(chart); return chart; } /** * Creates a polar plot for the specified dataset (x-values interpreted as * angles in degrees). The chart object returned by this method uses a * {@link PolarPlot} instance as the plot, with a {@link NumberAxis} for * the radial axis. * * @param title the chart title ({@code null} permitted). * @param dataset the dataset ({@code null} permitted). * @param legend legend required? * @param tooltips tooltips required? * @param urls URLs required? * * @return A chart. */ public static JFreeChart createPolarChart(String title, XYDataset dataset, boolean legend, boolean tooltips, boolean urls) { PolarPlot plot = new PolarPlot(); plot.setDataset(dataset); NumberAxis rangeAxis = new NumberAxis(); rangeAxis.setAxisLineVisible(false); rangeAxis.setTickMarksVisible(false); rangeAxis.setTickLabelInsets(new RectangleInsets(0.0, 0.0, 0.0, 0.0)); plot.setAxis(rangeAxis); plot.setRenderer(new DefaultPolarItemRenderer()); JFreeChart chart = new JFreeChart( title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); currentTheme.apply(chart); return chart; } /** * Creates a scatter plot with default settings. The chart object * returned by this method uses an {@link XYPlot} instance as the plot, * with a {@link NumberAxis} for the domain axis, a {@link NumberAxis} * as the range axis, and an {@link XYLineAndShapeRenderer} as the * renderer. * * @param title the chart title ({@code null} permitted). * @param xAxisLabel a label for the X-axis ({@code null} permitted). * @param yAxisLabel a label for the Y-axis ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * * @return A scatter plot. */ public static JFreeChart createScatterPlot(String title, String xAxisLabel, String yAxisLabel, XYDataset dataset) { return createScatterPlot(title, xAxisLabel, yAxisLabel, dataset, PlotOrientation.VERTICAL, true, true, false); } /** * Creates a scatter plot with default settings. The chart object * returned by this method uses an {@link XYPlot} instance as the plot, * with a {@link NumberAxis} for the domain axis, a {@link NumberAxis} * as the range axis, and an {@link XYLineAndShapeRenderer} as the * renderer. * * @param title the chart title ({@code null} permitted). * @param xAxisLabel a label for the X-axis ({@code null} permitted). * @param yAxisLabel a label for the Y-axis ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * @param orientation the plot orientation (horizontal or vertical) * ({@code null} NOT permitted). * @param legend a flag specifying whether or not a legend is required. * @param tooltips configure chart to generate tool tips? * @param urls configure chart to generate URLs? * * @return A scatter plot. */ public static JFreeChart createScatterPlot(String title, String xAxisLabel, String yAxisLabel, XYDataset dataset, PlotOrientation orientation, boolean legend, boolean tooltips, boolean urls) { Args.nullNotPermitted(orientation, "orientation"); NumberAxis xAxis = new NumberAxis(xAxisLabel); xAxis.setAutoRangeIncludesZero(false); NumberAxis yAxis = new NumberAxis(yAxisLabel); yAxis.setAutoRangeIncludesZero(false); XYPlot plot = new XYPlot(dataset, xAxis, yAxis, null); XYToolTipGenerator toolTipGenerator = null; if (tooltips) { toolTipGenerator = new StandardXYToolTipGenerator(); } XYURLGenerator urlGenerator = null; if (urls) { urlGenerator = new StandardXYURLGenerator(); } XYItemRenderer renderer = new XYLineAndShapeRenderer(false, true); renderer.setDefaultToolTipGenerator(toolTipGenerator); renderer.setURLGenerator(urlGenerator); plot.setRenderer(renderer); plot.setOrientation(orientation); JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); currentTheme.apply(chart); return chart; } /** * Creates and returns a default instance of an XY bar chart. *

* The chart object returned by this method uses an {@link XYPlot} instance * as the plot, with a {@link DateAxis} for the domain axis, a * {@link NumberAxis} as the range axis, and a {@link XYBarRenderer} as the * renderer. * * @param title the chart title ({@code null} permitted). * @param xAxisLabel a label for the X-axis ({@code null} permitted). * @param dateAxis make the domain axis display dates? * @param yAxisLabel a label for the Y-axis ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * * @return An XY bar chart. */ public static JFreeChart createXYBarChart(String title, String xAxisLabel, boolean dateAxis, String yAxisLabel, IntervalXYDataset dataset) { return createXYBarChart(title, xAxisLabel, dateAxis, yAxisLabel, dataset, PlotOrientation.VERTICAL, true, true, false); } /** * Creates and returns a default instance of an XY bar chart. *

* The chart object returned by this method uses an {@link XYPlot} instance * as the plot, with a {@link DateAxis} for the domain axis, a * {@link NumberAxis} as the range axis, and a {@link XYBarRenderer} as the * renderer. * * @param title the chart title ({@code null} permitted). * @param xAxisLabel a label for the X-axis ({@code null} permitted). * @param dateAxis make the domain axis display dates? * @param yAxisLabel a label for the Y-axis ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * @param orientation the orientation (horizontal or vertical) * ({@code null} NOT permitted). * @param legend a flag specifying whether or not a legend is required. * @param tooltips configure chart to generate tool tips? * @param urls configure chart to generate URLs? * * @return An XY bar chart. */ public static JFreeChart createXYBarChart(String title, String xAxisLabel, boolean dateAxis, String yAxisLabel, IntervalXYDataset dataset, PlotOrientation orientation, boolean legend, boolean tooltips, boolean urls) { Args.nullNotPermitted(orientation, "orientation"); ValueAxis domainAxis; if (dateAxis) { domainAxis = new DateAxis(xAxisLabel); } else { NumberAxis axis = new NumberAxis(xAxisLabel); axis.setAutoRangeIncludesZero(false); domainAxis = axis; } ValueAxis valueAxis = new NumberAxis(yAxisLabel); XYBarRenderer renderer = new XYBarRenderer(); if (tooltips) { XYToolTipGenerator tt; if (dateAxis) { tt = StandardXYToolTipGenerator.getTimeSeriesInstance(); } else { tt = new StandardXYToolTipGenerator(); } renderer.setDefaultToolTipGenerator(tt); } if (urls) { renderer.setURLGenerator(new StandardXYURLGenerator()); } XYPlot plot = new XYPlot(dataset, domainAxis, valueAxis, renderer); plot.setOrientation(orientation); JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); currentTheme.apply(chart); return chart; } /** * Creates an area chart using an {@link XYDataset}. *

* The chart object returned by this method uses an {@link XYPlot} instance * as the plot, with a {@link NumberAxis} for the domain axis, a * {@link NumberAxis} as the range axis, and a {@link XYAreaRenderer} as * the renderer. * * @param title the chart title ({@code null} permitted). * @param xAxisLabel a label for the X-axis ({@code null} permitted). * @param yAxisLabel a label for the Y-axis ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * * @return An XY area chart. */ public static JFreeChart createXYAreaChart(String title,String xAxisLabel, String yAxisLabel, XYDataset dataset) { return createXYAreaChart(title, xAxisLabel, yAxisLabel, dataset, PlotOrientation.VERTICAL, true, true, false); } /** * Creates an area chart using an {@link XYDataset}. *

* The chart object returned by this method uses an {@link XYPlot} instance * as the plot, with a {@link NumberAxis} for the domain axis, a * {@link NumberAxis} as the range axis, and a {@link XYAreaRenderer} as * the renderer. * * @param title the chart title ({@code null} permitted). * @param xAxisLabel a label for the X-axis ({@code null} permitted). * @param yAxisLabel a label for the Y-axis ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * @param orientation the plot orientation (horizontal or vertical) * ({@code null} NOT permitted). * @param legend a flag specifying whether or not a legend is required. * @param tooltips configure chart to generate tool tips? * @param urls configure chart to generate URLs? * * @return An XY area chart. */ public static JFreeChart createXYAreaChart(String title, String xAxisLabel, String yAxisLabel, XYDataset dataset, PlotOrientation orientation, boolean legend, boolean tooltips, boolean urls) { Args.nullNotPermitted(orientation, "orientation"); NumberAxis xAxis = new NumberAxis(xAxisLabel); xAxis.setAutoRangeIncludesZero(false); NumberAxis yAxis = new NumberAxis(yAxisLabel); XYPlot plot = new XYPlot(dataset, xAxis, yAxis, null); plot.setOrientation(orientation); plot.setForegroundAlpha(0.5f); XYToolTipGenerator tipGenerator = null; if (tooltips) { tipGenerator = new StandardXYToolTipGenerator(); } XYURLGenerator urlGenerator = null; if (urls) { urlGenerator = new StandardXYURLGenerator(); } plot.setRenderer(new XYAreaRenderer(XYAreaRenderer.AREA, tipGenerator, urlGenerator)); JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); currentTheme.apply(chart); return chart; } /** * Creates a stacked XY area plot. The chart object returned by this * method uses an {@link XYPlot} instance as the plot, with a * {@link NumberAxis} for the domain axis, a {@link NumberAxis} as the * range axis, and a {@link StackedXYAreaRenderer2} as the renderer. * * @param title the chart title ({@code null} permitted). * @param xAxisLabel a label for the X-axis ({@code null} permitted). * @param yAxisLabel a label for the Y-axis ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * * @return A stacked XY area chart. */ public static JFreeChart createStackedXYAreaChart(String title, String xAxisLabel, String yAxisLabel, TableXYDataset dataset) { return createStackedXYAreaChart(title, xAxisLabel, yAxisLabel, dataset, PlotOrientation.VERTICAL, true, true, false); } /** * Creates a stacked XY area plot. The chart object returned by this * method uses an {@link XYPlot} instance as the plot, with a * {@link NumberAxis} for the domain axis, a {@link NumberAxis} as the * range axis, and a {@link StackedXYAreaRenderer2} as the renderer. * * @param title the chart title ({@code null} permitted). * @param xAxisLabel a label for the X-axis ({@code null} permitted). * @param yAxisLabel a label for the Y-axis ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * @param orientation the plot orientation (horizontal or vertical) * ({@code null} NOT permitted). * @param legend a flag specifying whether or not a legend is required. * @param tooltips configure chart to generate tool tips? * @param urls configure chart to generate URLs? * * @return A stacked XY area chart. */ public static JFreeChart createStackedXYAreaChart(String title, String xAxisLabel, String yAxisLabel, TableXYDataset dataset, PlotOrientation orientation, boolean legend, boolean tooltips, boolean urls) { Args.nullNotPermitted(orientation, "orientation"); NumberAxis xAxis = new NumberAxis(xAxisLabel); xAxis.setAutoRangeIncludesZero(false); xAxis.setLowerMargin(0.0); xAxis.setUpperMargin(0.0); NumberAxis yAxis = new NumberAxis(yAxisLabel); XYToolTipGenerator toolTipGenerator = null; if (tooltips) { toolTipGenerator = new StandardXYToolTipGenerator(); } XYURLGenerator urlGenerator = null; if (urls) { urlGenerator = new StandardXYURLGenerator(); } StackedXYAreaRenderer2 renderer = new StackedXYAreaRenderer2( toolTipGenerator, urlGenerator); renderer.setOutline(true); XYPlot plot = new XYPlot(dataset, xAxis, yAxis, renderer); plot.setOrientation(orientation); plot.setRangeAxis(yAxis); // forces recalculation of the axis range JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); currentTheme.apply(chart); return chart; } /** * Creates a line chart (based on an {@link XYDataset}) with default * settings. * * @param title the chart title ({@code null} permitted). * @param xAxisLabel a label for the X-axis ({@code null} permitted). * @param yAxisLabel a label for the Y-axis ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * * @return The chart. */ public static JFreeChart createXYLineChart(String title, String xAxisLabel, String yAxisLabel, XYDataset dataset) { return createXYLineChart(title, xAxisLabel, yAxisLabel, dataset, PlotOrientation.VERTICAL, true, true, false); } /** * Creates a line chart (based on an {@link XYDataset}) with default * settings. * * @param title the chart title ({@code null} permitted). * @param xAxisLabel a label for the X-axis ({@code null} permitted). * @param yAxisLabel a label for the Y-axis ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * @param orientation the plot orientation (horizontal or vertical) * ({@code null} NOT permitted). * @param legend a flag specifying whether or not a legend is required. * @param tooltips configure chart to generate tool tips? * @param urls configure chart to generate URLs? * * @return The chart. */ public static JFreeChart createXYLineChart(String title, String xAxisLabel, String yAxisLabel, XYDataset dataset, PlotOrientation orientation, boolean legend, boolean tooltips, boolean urls) { Args.nullNotPermitted(orientation, "orientation"); NumberAxis xAxis = new NumberAxis(xAxisLabel); xAxis.setAutoRangeIncludesZero(false); NumberAxis yAxis = new NumberAxis(yAxisLabel); XYItemRenderer renderer = new XYLineAndShapeRenderer(true, false); XYPlot plot = new XYPlot(dataset, xAxis, yAxis, renderer); plot.setOrientation(orientation); if (tooltips) { renderer.setDefaultToolTipGenerator(new StandardXYToolTipGenerator()); } if (urls) { renderer.setURLGenerator(new StandardXYURLGenerator()); } JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); currentTheme.apply(chart); return chart; } /** * Creates a stepped XY plot with default settings. * * @param title the chart title ({@code null} permitted). * @param xAxisLabel a label for the X-axis ({@code null} permitted). * @param yAxisLabel a label for the Y-axis ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * * @return A chart. */ public static JFreeChart createXYStepChart(String title, String xAxisLabel, String yAxisLabel, XYDataset dataset) { return createXYStepChart(title, xAxisLabel, yAxisLabel, dataset, PlotOrientation.VERTICAL, true, true, false); } /** * Creates a stepped XY plot with default settings. * * @param title the chart title ({@code null} permitted). * @param xAxisLabel a label for the X-axis ({@code null} permitted). * @param yAxisLabel a label for the Y-axis ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * @param orientation the plot orientation (horizontal or vertical) * ({@code null} NOT permitted). * @param legend a flag specifying whether or not a legend is required. * @param tooltips configure chart to generate tool tips? * @param urls configure chart to generate URLs? * * @return A chart. */ public static JFreeChart createXYStepChart(String title, String xAxisLabel, String yAxisLabel, XYDataset dataset, PlotOrientation orientation, boolean legend, boolean tooltips, boolean urls) { Args.nullNotPermitted(orientation, "orientation"); DateAxis xAxis = new DateAxis(xAxisLabel); NumberAxis yAxis = new NumberAxis(yAxisLabel); yAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits()); XYToolTipGenerator toolTipGenerator = null; if (tooltips) { toolTipGenerator = new StandardXYToolTipGenerator(); } XYURLGenerator urlGenerator = null; if (urls) { urlGenerator = new StandardXYURLGenerator(); } XYItemRenderer renderer = new XYStepRenderer(toolTipGenerator, urlGenerator); XYPlot plot = new XYPlot(dataset, xAxis, yAxis, null); plot.setRenderer(renderer); plot.setOrientation(orientation); plot.setDomainCrosshairVisible(false); plot.setRangeCrosshairVisible(false); JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); currentTheme.apply(chart); return chart; } /** * Creates a filled stepped XY plot with default settings. * * @param title the chart title ({@code null} permitted). * @param xAxisLabel a label for the X-axis ({@code null} permitted). * @param yAxisLabel a label for the Y-axis ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * * @return A chart. */ public static JFreeChart createXYStepAreaChart(String title, String xAxisLabel, String yAxisLabel, XYDataset dataset) { return createXYStepAreaChart(title, xAxisLabel, yAxisLabel, dataset, PlotOrientation.VERTICAL, true, true, false); } /** * Creates a filled stepped XY plot with default settings. * * @param title the chart title ({@code null} permitted). * @param xAxisLabel a label for the X-axis ({@code null} permitted). * @param yAxisLabel a label for the Y-axis ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * @param orientation the plot orientation (horizontal or vertical) * ({@code null} NOT permitted). * @param legend a flag specifying whether or not a legend is required. * @param tooltips configure chart to generate tool tips? * @param urls configure chart to generate URLs? * * @return A chart. */ public static JFreeChart createXYStepAreaChart(String title, String xAxisLabel, String yAxisLabel, XYDataset dataset, PlotOrientation orientation, boolean legend, boolean tooltips, boolean urls) { Args.nullNotPermitted(orientation, "orientation"); NumberAxis xAxis = new NumberAxis(xAxisLabel); xAxis.setAutoRangeIncludesZero(false); NumberAxis yAxis = new NumberAxis(yAxisLabel); XYToolTipGenerator toolTipGenerator = null; if (tooltips) { toolTipGenerator = new StandardXYToolTipGenerator(); } XYURLGenerator urlGenerator = null; if (urls) { urlGenerator = new StandardXYURLGenerator(); } XYItemRenderer renderer = new XYStepAreaRenderer( XYStepAreaRenderer.AREA_AND_SHAPES, toolTipGenerator, urlGenerator); XYPlot plot = new XYPlot(dataset, xAxis, yAxis, null); plot.setRenderer(renderer); plot.setOrientation(orientation); plot.setDomainCrosshairVisible(false); plot.setRangeCrosshairVisible(false); JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); currentTheme.apply(chart); return chart; } /** * Creates and returns a time series chart. A time series chart is an * {@link XYPlot} with a {@link DateAxis} for the x-axis and a * {@link NumberAxis} for the y-axis. The default renderer is an * {@link XYLineAndShapeRenderer}. *

* A convenient dataset to use with this chart is a * {@link org.jfree.data.time.TimeSeriesCollection}. * * @param title the chart title ({@code null} permitted). * @param timeAxisLabel a label for the time axis ({@code null} * permitted). * @param valueAxisLabel a label for the value axis ({@code null} * permitted). * @param dataset the dataset for the chart ({@code null} permitted). * * @return A time series chart. */ public static JFreeChart createTimeSeriesChart(String title, String timeAxisLabel, String valueAxisLabel, XYDataset dataset) { return createTimeSeriesChart(title, timeAxisLabel, valueAxisLabel, dataset, true, true, false); } /** * Creates and returns a time series chart. A time series chart is an * {@link XYPlot} with a {@link DateAxis} for the x-axis and a * {@link NumberAxis} for the y-axis. The default renderer is an * {@link XYLineAndShapeRenderer}. *

* A convenient dataset to use with this chart is a * {@link org.jfree.data.time.TimeSeriesCollection}. * * @param title the chart title ({@code null} permitted). * @param timeAxisLabel a label for the time axis ({@code null} * permitted). * @param valueAxisLabel a label for the value axis ({@code null} * permitted). * @param dataset the dataset for the chart ({@code null} permitted). * @param legend a flag specifying whether or not a legend is required. * @param tooltips configure chart to generate tool tips? * @param urls configure chart to generate URLs? * * @return A time series chart. */ public static JFreeChart createTimeSeriesChart(String title, String timeAxisLabel, String valueAxisLabel, XYDataset dataset, boolean legend, boolean tooltips, boolean urls) { ValueAxis timeAxis = new DateAxis(timeAxisLabel); timeAxis.setLowerMargin(0.02); // reduce the default margins timeAxis.setUpperMargin(0.02); NumberAxis valueAxis = new NumberAxis(valueAxisLabel); valueAxis.setAutoRangeIncludesZero(false); // override default XYPlot plot = new XYPlot(dataset, timeAxis, valueAxis, null); XYToolTipGenerator toolTipGenerator = null; if (tooltips) { toolTipGenerator = StandardXYToolTipGenerator.getTimeSeriesInstance(); } XYURLGenerator urlGenerator = null; if (urls) { urlGenerator = new StandardXYURLGenerator(); } XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer(true, false); renderer.setDefaultToolTipGenerator(toolTipGenerator); renderer.setURLGenerator(urlGenerator); plot.setRenderer(renderer); JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); currentTheme.apply(chart); return chart; } /** * Creates and returns a default instance of a candlesticks chart. * * @param title the chart title ({@code null} permitted). * @param timeAxisLabel a label for the time axis ({@code null} * permitted). * @param valueAxisLabel a label for the value axis ({@code null} * permitted). * @param dataset the dataset for the chart ({@code null} permitted). * @param legend a flag specifying whether or not a legend is required. * * @return A candlestick chart. */ public static JFreeChart createCandlestickChart(String title, String timeAxisLabel, String valueAxisLabel, OHLCDataset dataset, boolean legend) { ValueAxis timeAxis = new DateAxis(timeAxisLabel); NumberAxis valueAxis = new NumberAxis(valueAxisLabel); XYPlot plot = new XYPlot(dataset, timeAxis, valueAxis, null); plot.setRenderer(new CandlestickRenderer()); JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); currentTheme.apply(chart); return chart; } /** * Creates and returns a default instance of a high-low-open-close chart. * * @param title the chart title ({@code null} permitted). * @param timeAxisLabel a label for the time axis ({@code null} * permitted). * @param valueAxisLabel a label for the value axis ({@code null} * permitted). * @param dataset the dataset for the chart ({@code null} permitted). * @param legend a flag specifying whether or not a legend is required. * * @return A high-low-open-close chart. */ public static JFreeChart createHighLowChart(String title, String timeAxisLabel, String valueAxisLabel, OHLCDataset dataset, boolean legend) { ValueAxis timeAxis = new DateAxis(timeAxisLabel); NumberAxis valueAxis = new NumberAxis(valueAxisLabel); HighLowRenderer renderer = new HighLowRenderer(); renderer.setDefaultToolTipGenerator(new HighLowItemLabelGenerator()); XYPlot plot = new XYPlot(dataset, timeAxis, valueAxis, renderer); JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); currentTheme.apply(chart); return chart; } /** * Creates a bubble chart with default settings. The chart is composed of * an {@link XYPlot}, with a {@link NumberAxis} for the domain axis, * a {@link NumberAxis} for the range axis, and an {@link XYBubbleRenderer} * to draw the data items. * * @param title the chart title ({@code null} permitted). * @param xAxisLabel a label for the X-axis ({@code null} permitted). * @param yAxisLabel a label for the Y-axis ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * * @return A bubble chart. */ public static JFreeChart createBubbleChart(String title, String xAxisLabel, String yAxisLabel, XYZDataset dataset) { return createBubbleChart(title, xAxisLabel, yAxisLabel, dataset, PlotOrientation.VERTICAL, true, true, false); } /** * Creates a bubble chart with default settings. The chart is composed of * an {@link XYPlot}, with a {@link NumberAxis} for the domain axis, * a {@link NumberAxis} for the range axis, and an {@link XYBubbleRenderer} * to draw the data items. * * @param title the chart title ({@code null} permitted). * @param xAxisLabel a label for the X-axis ({@code null} permitted). * @param yAxisLabel a label for the Y-axis ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * @param orientation the orientation (horizontal or vertical) * ({@code null} NOT permitted). * @param legend a flag specifying whether or not a legend is required. * @param tooltips configure chart to generate tool tips? * @param urls configure chart to generate URLs? * * @return A bubble chart. */ public static JFreeChart createBubbleChart(String title, String xAxisLabel, String yAxisLabel, XYZDataset dataset, PlotOrientation orientation, boolean legend, boolean tooltips, boolean urls) { Args.nullNotPermitted(orientation, "orientation"); NumberAxis xAxis = new NumberAxis(xAxisLabel); xAxis.setAutoRangeIncludesZero(false); NumberAxis yAxis = new NumberAxis(yAxisLabel); yAxis.setAutoRangeIncludesZero(false); XYPlot plot = new XYPlot(dataset, xAxis, yAxis, null); XYItemRenderer renderer = new XYBubbleRenderer( XYBubbleRenderer.SCALE_ON_RANGE_AXIS); if (tooltips) { renderer.setDefaultToolTipGenerator(new StandardXYZToolTipGenerator()); } if (urls) { renderer.setURLGenerator(new StandardXYZURLGenerator()); } plot.setRenderer(renderer); plot.setOrientation(orientation); JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); currentTheme.apply(chart); return chart; } /** * Creates a histogram chart. This chart is constructed with an * {@link XYPlot} using an {@link XYBarRenderer}. The domain and range * axes are {@link NumberAxis} instances. * * @param title the chart title ({@code null} permitted). * @param xAxisLabel the x axis label ({@code null} permitted). * @param yAxisLabel the y axis label ({@code null} permitted). * @param dataset the dataset ({@code null} permitted). * * @return A chart. */ public static JFreeChart createHistogram(String title, String xAxisLabel, String yAxisLabel, IntervalXYDataset dataset) { return createHistogram(title, xAxisLabel, yAxisLabel, dataset, PlotOrientation.VERTICAL, true, true, false); } /** * Creates a histogram chart. This chart is constructed with an * {@link XYPlot} using an {@link XYBarRenderer}. The domain and range * axes are {@link NumberAxis} instances. * * @param title the chart title ({@code null} permitted). * @param xAxisLabel the x axis label ({@code null} permitted). * @param yAxisLabel the y axis label ({@code null} permitted). * @param dataset the dataset ({@code null} permitted). * @param orientation the orientation (horizontal or vertical) * ({@code null} NOT permitted). * @param legend create a legend? * @param tooltips display tooltips? * @param urls generate URLs? * * @return The chart. */ public static JFreeChart createHistogram(String title, String xAxisLabel, String yAxisLabel, IntervalXYDataset dataset, PlotOrientation orientation, boolean legend, boolean tooltips, boolean urls) { Args.nullNotPermitted(orientation, "orientation"); NumberAxis xAxis = new NumberAxis(xAxisLabel); xAxis.setAutoRangeIncludesZero(false); ValueAxis yAxis = new NumberAxis(yAxisLabel); XYItemRenderer renderer = new XYBarRenderer(); if (tooltips) { renderer.setDefaultToolTipGenerator(new StandardXYToolTipGenerator()); } if (urls) { renderer.setURLGenerator(new StandardXYURLGenerator()); } XYPlot plot = new XYPlot(dataset, xAxis, yAxis, renderer); plot.setOrientation(orientation); plot.setDomainZeroBaselineVisible(true); plot.setRangeZeroBaselineVisible(true); JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); currentTheme.apply(chart); return chart; } /** * Creates and returns a default instance of a box and whisker chart * based on data from a {@link BoxAndWhiskerCategoryDataset}. * * @param title the chart title ({@code null} permitted). * @param categoryAxisLabel a label for the category axis * ({@code null} permitted). * @param valueAxisLabel a label for the value axis ({@code null} * permitted). * @param dataset the dataset for the chart ({@code null} permitted). * @param legend a flag specifying whether or not a legend is required. * * @return A box and whisker chart. */ public static JFreeChart createBoxAndWhiskerChart(String title, String categoryAxisLabel, String valueAxisLabel, BoxAndWhiskerCategoryDataset dataset, boolean legend) { CategoryAxis categoryAxis = new CategoryAxis(categoryAxisLabel); NumberAxis valueAxis = new NumberAxis(valueAxisLabel); valueAxis.setAutoRangeIncludesZero(false); BoxAndWhiskerRenderer renderer = new BoxAndWhiskerRenderer(); renderer.setDefaultToolTipGenerator(new BoxAndWhiskerToolTipGenerator()); CategoryPlot plot = new CategoryPlot(dataset, categoryAxis, valueAxis, renderer); JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); currentTheme.apply(chart); return chart; } /** * Creates and returns a default instance of a box and whisker chart. * * @param title the chart title ({@code null} permitted). * @param timeAxisLabel a label for the time axis ({@code null} * permitted). * @param valueAxisLabel a label for the value axis ({@code null} * permitted). * @param dataset the dataset for the chart ({@code null} permitted). * @param legend a flag specifying whether or not a legend is required. * * @return A box and whisker chart. */ public static JFreeChart createBoxAndWhiskerChart(String title, String timeAxisLabel, String valueAxisLabel, BoxAndWhiskerXYDataset dataset, boolean legend) { ValueAxis timeAxis = new DateAxis(timeAxisLabel); NumberAxis valueAxis = new NumberAxis(valueAxisLabel); valueAxis.setAutoRangeIncludesZero(false); XYBoxAndWhiskerRenderer renderer = new XYBoxAndWhiskerRenderer(10.0); XYPlot plot = new XYPlot(dataset, timeAxis, valueAxis, renderer); JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); currentTheme.apply(chart); return chart; } /** * Creates a wind plot with default settings. * * @param title the chart title ({@code null} permitted). * @param xAxisLabel a label for the x-axis ({@code null} permitted). * @param yAxisLabel a label for the y-axis ({@code null} permitted). * @param dataset the dataset for the chart ({@code null} permitted). * @param legend a flag that controls whether or not a legend is created. * @param tooltips configure chart to generate tool tips? * @param urls configure chart to generate URLs? * * @return A wind plot. * */ public static JFreeChart createWindPlot(String title, String xAxisLabel, String yAxisLabel, WindDataset dataset, boolean legend, boolean tooltips, boolean urls) { ValueAxis xAxis = new DateAxis(xAxisLabel); ValueAxis yAxis = new NumberAxis(yAxisLabel); yAxis.setRange(-12.0, 12.0); WindItemRenderer renderer = new WindItemRenderer(); if (tooltips) { renderer.setDefaultToolTipGenerator(new StandardXYToolTipGenerator()); } if (urls) { renderer.setURLGenerator(new StandardXYURLGenerator()); } XYPlot plot = new XYPlot(dataset, xAxis, yAxis, renderer); JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); currentTheme.apply(chart); return chart; } /** * Creates a wafer map chart. * * @param title the chart title ({@code null} permitted). * @param dataset the dataset ({@code null} permitted). * @param orientation the plot orientation (horizontal or vertical) * ({@code null} NOT permitted. * @param legend display a legend? * @param tooltips generate tooltips? * @param urls generate URLs? * * @return A wafer map chart. */ public static JFreeChart createWaferMapChart(String title, WaferMapDataset dataset, PlotOrientation orientation, boolean legend, boolean tooltips, boolean urls) { Args.nullNotPermitted(orientation, "orientation"); WaferMapPlot plot = new WaferMapPlot(dataset); WaferMapRenderer renderer = new WaferMapRenderer(); plot.setRenderer(renderer); JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT, plot, legend); currentTheme.apply(chart); return chart; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ChartFrame.java000066400000000000000000000054061463604235500257420ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * ChartFrame.java * --------------- * (C) Copyright 2001-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.WindowConstants; /** * A frame for displaying a chart. */ public class ChartFrame extends JFrame { /** The chart panel. */ private final ChartPanel chartPanel; /** * Constructs a frame for a chart. * * @param title the frame title. * @param chart the chart. */ public ChartFrame(String title, JFreeChart chart) { this(title, chart, false); } /** * Constructs a frame for a chart. * * @param title the frame title. * @param chart the chart. * @param scrollPane if {@code true}, put the Chart(Panel) into a * JScrollPane. */ public ChartFrame(String title, JFreeChart chart, boolean scrollPane) { super(title); setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); this.chartPanel = new ChartPanel(chart); if (scrollPane) { setContentPane(new JScrollPane(this.chartPanel)); } else { setContentPane(this.chartPanel); } } /** * Returns the chart panel for the frame. * * @return The chart panel. */ public ChartPanel getChartPanel() { return this.chartPanel; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ChartHints.java000066400000000000000000000066021463604235500257740ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * ChartHints.java * --------------- * (C) Copyright 2014-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart; import java.util.Map; /** * Special rendering hints that can be used internally by JFreeChart or by * specialised implementations of the {@code Graphics2D} API. For example, * JFreeSVG's {@code SVGGraphics2D} class, will use the * {@code KEY_BEGIN_ELEMENT} and {@code KEY_END_ELEMENT} hints to drive the * output content. */ public final class ChartHints { private ChartHints() { // no need to instantiate this } /** * The key for a hint to signal the beginning of an element. The value * should be a string containing the element id or, alternatively, a Map * containing the 'id' (String) and 'ref' (String in JSON format). */ public static final Key KEY_BEGIN_ELEMENT = new ChartHints.Key(0); /** * The key for a hint that ends an element. */ public static final Key KEY_END_ELEMENT = new ChartHints.Key(1); /** * A key for rendering hints that can be used with JFreeChart (in * addition to the regular Java2D rendering hints). */ public static class Key extends java.awt.RenderingHints.Key { /** * Creates a new key. * * @param privateKey the private key. */ public Key(int privateKey) { super(privateKey); } /** * Returns {@code true} if {@code val} is a value that is * compatible with this key, and {@code false} otherwise. * * @param val the value. * * @return A boolean. */ @Override public boolean isCompatibleValue(Object val) { switch (intKey()) { case 0: return val == null || val instanceof String || val instanceof Map; case 1: return val == null || val instanceof Object; default: throw new RuntimeException("Not possible!"); } } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ChartMouseEvent.java000066400000000000000000000066141463604235500270040ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * ChartMouseEvent.java * -------------------- * (C) Copyright 2002-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Alex Weber; * */ package org.jfree.chart; import java.awt.event.MouseEvent; import java.io.Serializable; import java.util.EventObject; import org.jfree.chart.entity.ChartEntity; /** * A mouse event for a chart that is displayed in a {@link ChartPanel}. * * @see ChartMouseListener */ public class ChartMouseEvent extends EventObject implements Serializable { /** For serialization. */ private static final long serialVersionUID = -682393837314562149L; /** The chart that the mouse event relates to. */ private final JFreeChart chart; /** The Java mouse event that triggered this event. */ private final MouseEvent trigger; /** The chart entity (if any). */ private final ChartEntity entity; /** * Constructs a new event. * * @param chart the source chart ({@code null} not permitted). * @param trigger the mouse event that triggered this event * ({@code null} not permitted). * @param entity the chart entity (if any) under the mouse point * ({@code null} permitted). */ public ChartMouseEvent(JFreeChart chart, MouseEvent trigger, ChartEntity entity) { super(chart); this.chart = chart; this.trigger = trigger; this.entity = entity; } /** * Returns the chart that the mouse event relates to. * * @return The chart (never {@code null}). */ public JFreeChart getChart() { return this.chart; } /** * Returns the mouse event that triggered this event. * * @return The event (never {@code null}). */ public MouseEvent getTrigger() { return this.trigger; } /** * Returns the chart entity (if any) under the mouse point. * * @return The chart entity (possibly {@code null}). */ public ChartEntity getEntity() { return this.entity; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ChartMouseListener.java000066400000000000000000000043241463604235500275040ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * ChartMouseListener.java * ----------------------- * (C) Copyright 2002-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Alex Weber; * */ package org.jfree.chart; import java.util.EventListener; /** * The interface that must be implemented by classes that wish to receive * {@link ChartMouseEvent} notifications from a {@link ChartPanel}. * * @see ChartPanel#addChartMouseListener(ChartMouseListener) */ public interface ChartMouseListener extends EventListener { /** * Callback method for receiving notification of a mouse click on a chart. * * @param event information about the event. */ void chartMouseClicked(ChartMouseEvent event); /** * Callback method for receiving notification of a mouse movement on a * chart. * * @param event information about the event. */ void chartMouseMoved(ChartMouseEvent event); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ChartPanel.java000066400000000000000000003366761463604235500257670ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * ChartPanel.java * --------------- * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Andrzej Porebski; * Soren Caspersen; * Jonathan Nash; * Hans-Jurgen Greiner; * Andreas Schneider; * Daniel van Enckevort; * David M O'Donnell; * Arnaud Lelievre; * Matthias Rose; * Onno vd Akker; * Sergei Ivanov; * Ulrich Voigt - patch 2686040; * Alessandro Borges - patch 1460845; * Martin Hoeller; * Simon Legner - patch from bug 1129; * Yuri Blankenstein; */ package org.jfree.chart; import java.awt.AWTEvent; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Composite; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.Insets; import java.awt.Paint; import java.awt.Point; import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.Transparency; import java.awt.datatransfer.Clipboard; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.InputEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.geom.AffineTransform; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.print.PageFormat; import java.awt.print.Printable; import java.awt.print.PrinterException; import java.awt.print.PrinterJob; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.EventListener; import java.util.List; import java.util.ResourceBundle; import javax.swing.JFileChooser; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.SwingUtilities; import javax.swing.ToolTipManager; import javax.swing.event.EventListenerList; import javax.swing.filechooser.FileNameExtensionFilter; import org.jfree.chart.editor.ChartEditor; import org.jfree.chart.editor.ChartEditorManager; import org.jfree.chart.entity.ChartEntity; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.ChartChangeEvent; import org.jfree.chart.event.ChartChangeListener; import org.jfree.chart.event.ChartProgressEvent; import org.jfree.chart.event.ChartProgressListener; import org.jfree.chart.event.OverlayChangeEvent; import org.jfree.chart.event.OverlayChangeListener; import org.jfree.chart.panel.Overlay; import org.jfree.chart.plot.Pannable; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.Zoomable; import org.jfree.chart.util.Args; import org.jfree.chart.util.ResourceBundleWrapper; import org.jfree.chart.util.SerialUtils; /** * A Swing GUI component for displaying a {@link JFreeChart} object. *

* The panel registers with the chart to receive notification of changes to any * component of the chart. The chart is redrawn automatically whenever this * notification is received. */ public class ChartPanel extends JPanel implements ChartChangeListener, ChartProgressListener, ActionListener, MouseListener, MouseMotionListener, OverlayChangeListener, Printable, Serializable { /** For serialization. */ private static final long serialVersionUID = 6046366297214274674L; /** * Default setting for buffer usage. The default has been changed to * {@code true} from version 1.0.13 onwards, because of a severe * performance problem with drawing the zoom rectangle using XOR (which * now happens only when the buffer is NOT used). */ public static final boolean DEFAULT_BUFFER_USED = true; /** The default panel width. */ public static final int DEFAULT_WIDTH = 1024; /** The default panel height. */ public static final int DEFAULT_HEIGHT = 768; /** The default limit below which chart scaling kicks in. */ public static final int DEFAULT_MINIMUM_DRAW_WIDTH = 300; /** The default limit below which chart scaling kicks in. */ public static final int DEFAULT_MINIMUM_DRAW_HEIGHT = 200; /** The default limit above which chart scaling kicks in. */ public static final int DEFAULT_MAXIMUM_DRAW_WIDTH = 2048; /** The default limit above which chart scaling kicks in. */ public static final int DEFAULT_MAXIMUM_DRAW_HEIGHT = 1536; /** The minimum size required to perform a zoom on a rectangle */ public static final int DEFAULT_ZOOM_TRIGGER_DISTANCE = 10; /** Properties action command. */ public static final String PROPERTIES_COMMAND = "PROPERTIES"; /** * Copy action command. */ public static final String COPY_COMMAND = "COPY"; /** Save action command. */ public static final String SAVE_COMMAND = "SAVE"; /** Action command to save as PNG. */ private static final String SAVE_AS_PNG_COMMAND = "SAVE_AS_PNG"; /** Action command to save as SVG. */ private static final String SAVE_AS_SVG_COMMAND = "SAVE_AS_SVG"; /** Action command to save as PDF. */ private static final String SAVE_AS_PDF_COMMAND = "SAVE_AS_PDF"; /** Print action command. */ public static final String PRINT_COMMAND = "PRINT"; /** Zoom in (both axes) action command. */ public static final String ZOOM_IN_BOTH_COMMAND = "ZOOM_IN_BOTH"; /** Zoom in (domain axis only) action command. */ public static final String ZOOM_IN_DOMAIN_COMMAND = "ZOOM_IN_DOMAIN"; /** Zoom in (range axis only) action command. */ public static final String ZOOM_IN_RANGE_COMMAND = "ZOOM_IN_RANGE"; /** Zoom out (both axes) action command. */ public static final String ZOOM_OUT_BOTH_COMMAND = "ZOOM_OUT_BOTH"; /** Zoom out (domain axis only) action command. */ public static final String ZOOM_OUT_DOMAIN_COMMAND = "ZOOM_DOMAIN_BOTH"; /** Zoom out (range axis only) action command. */ public static final String ZOOM_OUT_RANGE_COMMAND = "ZOOM_RANGE_BOTH"; /** Zoom reset (both axes) action command. */ public static final String ZOOM_RESET_BOTH_COMMAND = "ZOOM_RESET_BOTH"; /** Zoom reset (domain axis only) action command. */ public static final String ZOOM_RESET_DOMAIN_COMMAND = "ZOOM_RESET_DOMAIN"; /** Zoom reset (range axis only) action command. */ public static final String ZOOM_RESET_RANGE_COMMAND = "ZOOM_RESET_RANGE"; /** The chart that is displayed in the panel. */ private JFreeChart chart; /** Storage for registered (chart) mouse listeners. */ private transient EventListenerList chartMouseListeners; /** A flag that controls whether the off-screen buffer is used. */ private final boolean useBuffer; /** A flag that indicates that the buffer should be refreshed. */ private boolean refreshBuffer; /** A buffer for the rendered chart. */ private transient BufferedImage chartBuffer; /** * The minimum width for drawing a chart (uses scaling for smaller widths). */ private int minimumDrawWidth; /** * The minimum height for drawing a chart (uses scaling for smaller * heights). */ private int minimumDrawHeight; /** * The maximum width for drawing a chart (uses scaling for bigger * widths). */ private int maximumDrawWidth; /** * The maximum height for drawing a chart (uses scaling for bigger * heights). */ private int maximumDrawHeight; /** The popup menu for the frame. */ private JPopupMenu popup; /** The drawing info collected the last time the chart was drawn. */ private final ChartRenderingInfo info; /** The chart anchor point. */ private Point2D anchor; /** The scale factor used to draw the chart. */ private double scaleX; /** The scale factor used to draw the chart. */ private double scaleY; /** The plot orientation. */ private PlotOrientation orientation = PlotOrientation.VERTICAL; /** A flag that controls whether or not domain zooming is enabled. */ private boolean domainZoomable = false; /** A flag that controls whether or not range zooming is enabled. */ private boolean rangeZoomable = false; /** * The zoom rectangle starting point (selected by the user with a mouse * click). This is a point on the screen, not the chart (which may have * been scaled up or down to fit the panel). */ private Point2D zoomPoint = null; /** The zoom rectangle (selected by the user with the mouse). */ private transient Rectangle2D zoomRectangle = null; /** Controls if the zoom rectangle is drawn as an outline or filled. */ private boolean fillZoomRectangle = true; /** The minimum distance required to drag the mouse to trigger a zoom. */ private int zoomTriggerDistance; /** A flag that controls whether or not horizontal tracing is enabled. */ private boolean horizontalAxisTrace = false; /** A flag that controls whether or not vertical tracing is enabled. */ private boolean verticalAxisTrace = false; /** A vertical trace line. */ private transient Line2D verticalTraceLine; /** A horizontal trace line. */ private transient Line2D horizontalTraceLine; /** Menu item for zooming in on a chart (both axes). */ private JMenuItem zoomInBothMenuItem; /** Menu item for zooming in on a chart (domain axis). */ private JMenuItem zoomInDomainMenuItem; /** Menu item for zooming in on a chart (range axis). */ private JMenuItem zoomInRangeMenuItem; /** Menu item for zooming out on a chart. */ private JMenuItem zoomOutBothMenuItem; /** Menu item for zooming out on a chart (domain axis). */ private JMenuItem zoomOutDomainMenuItem; /** Menu item for zooming out on a chart (range axis). */ private JMenuItem zoomOutRangeMenuItem; /** Menu item for resetting the zoom (both axes). */ private JMenuItem zoomResetBothMenuItem; /** Menu item for resetting the zoom (domain axis only). */ private JMenuItem zoomResetDomainMenuItem; /** Menu item for resetting the zoom (range axis only). */ private JMenuItem zoomResetRangeMenuItem; /** * The default directory for saving charts to file. */ private File defaultDirectoryForSaveAs; /** A flag that controls whether or not file extensions are enforced. */ private boolean enforceFileExtensions; /** A flag that indicates if original tooltip delays are changed. */ private boolean ownToolTipDelaysActive; /** Original initial tooltip delay of ToolTipManager.sharedInstance(). */ private int originalToolTipInitialDelay; /** Original reshow tooltip delay of ToolTipManager.sharedInstance(). */ private int originalToolTipReshowDelay; /** Original dismiss tooltip delay of ToolTipManager.sharedInstance(). */ private int originalToolTipDismissDelay; /** Own initial tooltip delay to be used in this chart panel. */ private int ownToolTipInitialDelay; /** Own reshow tooltip delay to be used in this chart panel. */ private int ownToolTipReshowDelay; /** Own dismiss tooltip delay to be used in this chart panel. */ private int ownToolTipDismissDelay; /** The factor used to zoom in on an axis range. */ private double zoomInFactor = 0.5; /** The factor used to zoom out on an axis range. */ private double zoomOutFactor = 2.0; /** * A flag that controls whether zoom operations are centred on the * current anchor point, or the centre point of the relevant axis. */ private boolean zoomAroundAnchor; /** * The paint used to draw the zoom rectangle outline. */ private transient Paint zoomOutlinePaint; /** * The zoom fill paint (should use transparency). */ private transient Paint zoomFillPaint; /** The resourceBundle for the localization. */ protected static ResourceBundle localizationResources = ResourceBundleWrapper.getBundle( "org.jfree.chart.LocalizationBundle"); /** * Temporary storage for the width and height of the chart * drawing area during panning. */ private double panW, panH; /** The last mouse position during panning. */ private Point panLast; /** * The mask for mouse events to trigger panning. */ private int panMask = InputEvent.CTRL_MASK; /** * A list of overlays for the panel. */ private final List overlays; /** * Constructs a panel that displays the specified chart. * * @param chart the chart. */ public ChartPanel(JFreeChart chart) { this(chart, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_MINIMUM_DRAW_WIDTH, DEFAULT_MINIMUM_DRAW_HEIGHT, DEFAULT_MAXIMUM_DRAW_WIDTH, DEFAULT_MAXIMUM_DRAW_HEIGHT, DEFAULT_BUFFER_USED, true, // properties true, // save true, // print true, // zoom true // tooltips ); } /** * Constructs a panel containing a chart. The {@code useBuffer} flag * controls whether or not an offscreen {@code BufferedImage} is * maintained for the chart. If the buffer is used, more memory is * consumed, but panel repaints will be a lot quicker in cases where the * chart itself hasn't changed (for example, when another frame is moved * to reveal the panel). WARNING: If you set the {@code useBuffer} * flag to false, note that the mouse zooming rectangle will (in that case) * be drawn using XOR, and there is a SEVERE performance problem with that * on JRE6 on Windows. * * @param chart the chart. * @param useBuffer a flag controlling whether or not an off-screen buffer * is used (read the warning above before setting this * to {@code false}). */ public ChartPanel(JFreeChart chart, boolean useBuffer) { this(chart, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_MINIMUM_DRAW_WIDTH, DEFAULT_MINIMUM_DRAW_HEIGHT, DEFAULT_MAXIMUM_DRAW_WIDTH, DEFAULT_MAXIMUM_DRAW_HEIGHT, useBuffer, true, // properties true, // save true, // print true, // zoom true // tooltips ); } /** * Constructs a JFreeChart panel. * * @param chart the chart. * @param properties a flag indicating whether or not the chart property * editor should be available via the popup menu. * @param save a flag indicating whether or not save options should be * available via the popup menu. * @param print a flag indicating whether or not the print option * should be available via the popup menu. * @param zoom a flag indicating whether or not zoom options should * be added to the popup menu. * @param tooltips a flag indicating whether or not tooltips should be * enabled for the chart. */ public ChartPanel(JFreeChart chart, boolean properties, boolean save, boolean print, boolean zoom, boolean tooltips) { this(chart, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_MINIMUM_DRAW_WIDTH, DEFAULT_MINIMUM_DRAW_HEIGHT, DEFAULT_MAXIMUM_DRAW_WIDTH, DEFAULT_MAXIMUM_DRAW_HEIGHT, DEFAULT_BUFFER_USED, properties, save, print, zoom, tooltips); } /** * Constructs a JFreeChart panel. * * @param chart the chart. * @param width the preferred width of the panel. * @param height the preferred height of the panel. * @param minimumDrawWidth the minimum drawing width. * @param minimumDrawHeight the minimum drawing height. * @param maximumDrawWidth the maximum drawing width. * @param maximumDrawHeight the maximum drawing height. * @param useBuffer a flag that indicates whether to use the off-screen * buffer to improve performance (at the expense of * memory). * @param properties a flag indicating whether or not the chart property * editor should be available via the popup menu. * @param save a flag indicating whether or not save options should be * available via the popup menu. * @param print a flag indicating whether or not the print option * should be available via the popup menu. * @param zoom a flag indicating whether or not zoom options should be * added to the popup menu. * @param tooltips a flag indicating whether or not tooltips should be * enabled for the chart. */ public ChartPanel(JFreeChart chart, int width, int height, int minimumDrawWidth, int minimumDrawHeight, int maximumDrawWidth, int maximumDrawHeight, boolean useBuffer, boolean properties, boolean save, boolean print, boolean zoom, boolean tooltips) { this(chart, width, height, minimumDrawWidth, minimumDrawHeight, maximumDrawWidth, maximumDrawHeight, useBuffer, properties, true, save, print, zoom, tooltips); } /** * Constructs a JFreeChart panel. * * @param chart the chart. * @param width the preferred width of the panel. * @param height the preferred height of the panel. * @param minimumDrawWidth the minimum drawing width. * @param minimumDrawHeight the minimum drawing height. * @param maximumDrawWidth the maximum drawing width. * @param maximumDrawHeight the maximum drawing height. * @param useBuffer a flag that indicates whether to use the off-screen * buffer to improve performance (at the expense of * memory). * @param properties a flag indicating whether or not the chart property * editor should be available via the popup menu. * @param copy a flag indicating whether or not a copy option should be * available via the popup menu. * @param save a flag indicating whether or not save options should be * available via the popup menu. * @param print a flag indicating whether or not the print option * should be available via the popup menu. * @param zoom a flag indicating whether or not zoom options should be * added to the popup menu. * @param tooltips a flag indicating whether or not tooltips should be * enabled for the chart. */ public ChartPanel(JFreeChart chart, int width, int height, int minimumDrawWidth, int minimumDrawHeight, int maximumDrawWidth, int maximumDrawHeight, boolean useBuffer, boolean properties, boolean copy, boolean save, boolean print, boolean zoom, boolean tooltips) { setChart(chart); this.chartMouseListeners = new EventListenerList(); this.info = new ChartRenderingInfo(); setPreferredSize(new Dimension(width, height)); this.useBuffer = useBuffer; this.refreshBuffer = false; this.minimumDrawWidth = minimumDrawWidth; this.minimumDrawHeight = minimumDrawHeight; this.maximumDrawWidth = maximumDrawWidth; this.maximumDrawHeight = maximumDrawHeight; this.zoomTriggerDistance = DEFAULT_ZOOM_TRIGGER_DISTANCE; // set up popup menu... this.popup = null; if (properties || copy || save || print || zoom) { this.popup = createPopupMenu(properties, copy, save, print, zoom); } enableEvents(AWTEvent.MOUSE_EVENT_MASK); enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK); setDisplayToolTips(tooltips); addMouseListener(this); addMouseMotionListener(this); this.defaultDirectoryForSaveAs = null; this.enforceFileExtensions = true; // initialize ChartPanel-specific tool tip delays with // values the from ToolTipManager.sharedInstance() ToolTipManager ttm = ToolTipManager.sharedInstance(); this.ownToolTipInitialDelay = ttm.getInitialDelay(); this.ownToolTipDismissDelay = ttm.getDismissDelay(); this.ownToolTipReshowDelay = ttm.getReshowDelay(); this.zoomAroundAnchor = false; this.zoomOutlinePaint = Color.BLUE; this.zoomFillPaint = new Color(0, 0, 255, 63); this.panMask = InputEvent.CTRL_MASK; // for MacOSX we can't use the CTRL key for mouse drags, see: // http://developer.apple.com/qa/qa2004/qa1362.html String osName = System.getProperty("os.name").toLowerCase(); if (osName.startsWith("mac os x")) { this.panMask = InputEvent.ALT_MASK; } this.overlays = new ArrayList<>(); } /** * Returns the chart contained in the panel. * * @return The chart (possibly {@code null}). */ public JFreeChart getChart() { return this.chart; } /** * Sets the chart that is displayed in the panel. * * @param chart the chart ({@code null} permitted). */ public void setChart(JFreeChart chart) { // stop listening for changes to the existing chart if (this.chart != null) { this.chart.removeChangeListener(this); this.chart.removeProgressListener(this); } // add the new chart this.chart = chart; if (chart != null) { this.chart.addChangeListener(this); this.chart.addProgressListener(this); Plot plot = chart.getPlot(); this.domainZoomable = false; this.rangeZoomable = false; if (plot instanceof Zoomable) { Zoomable z = (Zoomable) plot; this.domainZoomable = z.isDomainZoomable(); this.rangeZoomable = z.isRangeZoomable(); this.orientation = z.getOrientation(); } } else { this.domainZoomable = false; this.rangeZoomable = false; } if (this.useBuffer) { this.refreshBuffer = true; } repaint(); } /** * Returns the minimum drawing width for charts. *

* If the width available on the panel is less than this, then the chart is * drawn at the minimum width then scaled down to fit. * * @return The minimum drawing width. */ public int getMinimumDrawWidth() { return this.minimumDrawWidth; } /** * Sets the minimum drawing width for the chart on this panel. *

* At the time the chart is drawn on the panel, if the available width is * less than this amount, the chart will be drawn using the minimum width * then scaled down to fit the available space. * * @param width The width. */ public void setMinimumDrawWidth(int width) { this.minimumDrawWidth = width; } /** * Returns the maximum drawing width for charts. *

* If the width available on the panel is greater than this, then the chart * is drawn at the maximum width then scaled up to fit. * * @return The maximum drawing width. */ public int getMaximumDrawWidth() { return this.maximumDrawWidth; } /** * Sets the maximum drawing width for the chart on this panel. *

* At the time the chart is drawn on the panel, if the available width is * greater than this amount, the chart will be drawn using the maximum * width then scaled up to fit the available space. * * @param width The width. */ public void setMaximumDrawWidth(int width) { this.maximumDrawWidth = width; } /** * Returns the minimum drawing height for charts. *

* If the height available on the panel is less than this, then the chart * is drawn at the minimum height then scaled down to fit. * * @return The minimum drawing height. */ public int getMinimumDrawHeight() { return this.minimumDrawHeight; } /** * Sets the minimum drawing height for the chart on this panel. *

* At the time the chart is drawn on the panel, if the available height is * less than this amount, the chart will be drawn using the minimum height * then scaled down to fit the available space. * * @param height The height. */ public void setMinimumDrawHeight(int height) { this.minimumDrawHeight = height; } /** * Returns the maximum drawing height for charts. *

* If the height available on the panel is greater than this, then the * chart is drawn at the maximum height then scaled up to fit. * * @return The maximum drawing height. */ public int getMaximumDrawHeight() { return this.maximumDrawHeight; } /** * Sets the maximum drawing height for the chart on this panel. *

* At the time the chart is drawn on the panel, if the available height is * greater than this amount, the chart will be drawn using the maximum * height then scaled up to fit the available space. * * @param height The height. */ public void setMaximumDrawHeight(int height) { this.maximumDrawHeight = height; } /** * Returns the X scale factor for the chart. This will be 1.0 if no * scaling has been used. * * @return The scale factor. */ public double getScaleX() { return this.scaleX; } /** * Returns the Y scale factory for the chart. This will be 1.0 if no * scaling has been used. * * @return The scale factor. */ public double getScaleY() { return this.scaleY; } /** * Returns the anchor point. * * @return The anchor point (possibly {@code null}). */ public Point2D getAnchor() { return this.anchor; } /** * Sets the anchor point. This method is provided for the use of * subclasses, not end users. * * @param anchor the anchor point ({@code null} permitted). */ protected void setAnchor(Point2D anchor) { this.anchor = anchor; } /** * Returns the popup menu. * * @return The popup menu. */ public JPopupMenu getPopupMenu() { return this.popup; } /** * Sets the popup menu for the panel. * * @param popup the popup menu ({@code null} permitted). */ public void setPopupMenu(JPopupMenu popup) { this.popup = popup; } /** * Returns the chart rendering info from the most recent chart redraw. * * @return The chart rendering info. */ public ChartRenderingInfo getChartRenderingInfo() { return this.info; } /** * A convenience method that switches on mouse-based zooming. * * @param flag {@code true} enables zooming and rectangle fill on * zoom. */ public void setMouseZoomable(boolean flag) { setMouseZoomable(flag, true); } /** * A convenience method that switches on mouse-based zooming. * * @param flag {@code true} if zooming enabled * @param fillRectangle {@code true} if zoom rectangle is filled, * false if rectangle is shown as outline only. */ public void setMouseZoomable(boolean flag, boolean fillRectangle) { setDomainZoomable(flag); setRangeZoomable(flag); setFillZoomRectangle(fillRectangle); } /** * Returns the flag that determines whether or not zooming is enabled for * the domain axis. * * @return A boolean. */ public boolean isDomainZoomable() { return this.domainZoomable; } /** * Sets the flag that controls whether zooming is enabled for the * domain axis. A check is made to ensure that the current plot supports * zooming for the domain values. * * @param flag {@code true} enables zooming if possible. */ public void setDomainZoomable(boolean flag) { if (flag) { Plot plot = this.chart.getPlot(); if (plot instanceof Zoomable) { Zoomable z = (Zoomable) plot; this.domainZoomable = z.isDomainZoomable(); } } else { this.domainZoomable = false; } } /** * Returns the flag that determines whether or not zooming is enabled for * the range axis. * * @return A boolean. */ public boolean isRangeZoomable() { return this.rangeZoomable; } /** * A flag that controls mouse-based zooming on the vertical axis. * * @param flag {@code true} enables zooming. */ public void setRangeZoomable(boolean flag) { if (flag) { Plot plot = this.chart.getPlot(); if (plot instanceof Zoomable) { Zoomable z = (Zoomable) plot; this.rangeZoomable = z.isRangeZoomable(); } } else { this.rangeZoomable = false; } } /** * Returns the flag that controls whether or not the zoom rectangle is * filled when drawn. * * @return A boolean. */ public boolean getFillZoomRectangle() { return this.fillZoomRectangle; } /** * A flag that controls how the zoom rectangle is drawn. * * @param flag {@code true} instructs to fill the rectangle on * zoom, otherwise it will be outlined. */ public void setFillZoomRectangle(boolean flag) { this.fillZoomRectangle = flag; } /** * Returns the zoom trigger distance. This controls how far the mouse must * move before a zoom action is triggered. * * @return The distance (in Java2D units). */ public int getZoomTriggerDistance() { return this.zoomTriggerDistance; } /** * Sets the zoom trigger distance. This controls how far the mouse must * move before a zoom action is triggered. * * @param distance the distance (in Java2D units). */ public void setZoomTriggerDistance(int distance) { this.zoomTriggerDistance = distance; } /** * Returns the flag that controls whether or not a horizontal axis trace * line is drawn over the plot area at the current mouse location. * * @return A boolean. */ public boolean getHorizontalAxisTrace() { return this.horizontalAxisTrace; } /** * A flag that controls trace lines on the horizontal axis. * * @param flag {@code true} enables trace lines for the mouse * pointer on the horizontal axis. */ public void setHorizontalAxisTrace(boolean flag) { this.horizontalAxisTrace = flag; } /** * Returns the horizontal trace line. * * @return The horizontal trace line (possibly {@code null}). */ protected Line2D getHorizontalTraceLine() { return this.horizontalTraceLine; } /** * Sets the horizontal trace line. * * @param line the line ({@code null} permitted). */ protected void setHorizontalTraceLine(Line2D line) { this.horizontalTraceLine = line; } /** * Returns the flag that controls whether or not a vertical axis trace * line is drawn over the plot area at the current mouse location. * * @return A boolean. */ public boolean getVerticalAxisTrace() { return this.verticalAxisTrace; } /** * A flag that controls trace lines on the vertical axis. * * @param flag {@code true} enables trace lines for the mouse * pointer on the vertical axis. */ public void setVerticalAxisTrace(boolean flag) { this.verticalAxisTrace = flag; } /** * Returns the vertical trace line. * * @return The vertical trace line (possibly {@code null}). */ protected Line2D getVerticalTraceLine() { return this.verticalTraceLine; } /** * Sets the vertical trace line. * * @param line the line ({@code null} permitted). */ protected void setVerticalTraceLine(Line2D line) { this.verticalTraceLine = line; } /** * Returns the default directory for the "save as" option. * * @return The default directory (possibly {@code null}). */ public File getDefaultDirectoryForSaveAs() { return this.defaultDirectoryForSaveAs; } /** * Sets the default directory for the "save as" option. If you set this * to {@code null}, the user's default directory will be used. * * @param directory the directory ({@code null} permitted). */ public void setDefaultDirectoryForSaveAs(File directory) { if (directory != null) { if (!directory.isDirectory()) { throw new IllegalArgumentException( "The 'directory' argument is not a directory."); } } this.defaultDirectoryForSaveAs = directory; } /** * Returns {@code true} if file extensions should be enforced, and * {@code false} otherwise. * * @return The flag. * * @see #setEnforceFileExtensions(boolean) */ public boolean isEnforceFileExtensions() { return this.enforceFileExtensions; } /** * Sets a flag that controls whether or not file extensions are enforced. * * @param enforce the new flag value. * * @see #isEnforceFileExtensions() */ public void setEnforceFileExtensions(boolean enforce) { this.enforceFileExtensions = enforce; } /** * Returns the flag that controls whether or not zoom operations are * centered around the current anchor point. * * @return A boolean. * * @see #setZoomAroundAnchor(boolean) */ public boolean getZoomAroundAnchor() { return this.zoomAroundAnchor; } /** * Sets the flag that controls whether or not zoom operations are * centered around the current anchor point. * * @param zoomAroundAnchor the new flag value. * * @see #getZoomAroundAnchor() */ public void setZoomAroundAnchor(boolean zoomAroundAnchor) { this.zoomAroundAnchor = zoomAroundAnchor; } /** * Returns the zoom rectangle fill paint. * * @return The zoom rectangle fill paint (never {@code null}). * * @see #setZoomFillPaint(java.awt.Paint) * @see #setFillZoomRectangle(boolean) */ public Paint getZoomFillPaint() { return this.zoomFillPaint; } /** * Sets the zoom rectangle fill paint. * * @param paint the paint ({@code null} not permitted). * * @see #getZoomFillPaint() * @see #getFillZoomRectangle() */ public void setZoomFillPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.zoomFillPaint = paint; } /** * Returns the zoom rectangle outline paint. * * @return The zoom rectangle outline paint (never {@code null}). * * @see #setZoomOutlinePaint(java.awt.Paint) * @see #setFillZoomRectangle(boolean) */ public Paint getZoomOutlinePaint() { return this.zoomOutlinePaint; } /** * Sets the zoom rectangle outline paint. * * @param paint the paint ({@code null} not permitted). * * @see #getZoomOutlinePaint() * @see #getFillZoomRectangle() */ public void setZoomOutlinePaint(Paint paint) { this.zoomOutlinePaint = paint; } /** * The mouse wheel handler. */ private MouseWheelHandler mouseWheelHandler; /** * Returns {@code true} if the mouse wheel handler is enabled, and * {@code false} otherwise. * * @return A boolean. */ public boolean isMouseWheelEnabled() { return this.mouseWheelHandler != null; } /** * Enables or disables mouse wheel support for the panel. * * @param flag a boolean. */ public void setMouseWheelEnabled(boolean flag) { if (flag && this.mouseWheelHandler == null) { this.mouseWheelHandler = new MouseWheelHandler(this); } else if (!flag && this.mouseWheelHandler != null) { this.removeMouseWheelListener(this.mouseWheelHandler); this.mouseWheelHandler = null; } } /** * Add an overlay to the panel. * * @param overlay the overlay ({@code null} not permitted). */ public void addOverlay(Overlay overlay) { Args.nullNotPermitted(overlay, "overlay"); this.overlays.add(overlay); overlay.addChangeListener(this); repaint(); } /** * Removes an overlay from the panel. * * @param overlay the overlay to remove ({@code null} not permitted). */ public void removeOverlay(Overlay overlay) { Args.nullNotPermitted(overlay, "overlay"); boolean removed = this.overlays.remove(overlay); if (removed) { overlay.removeChangeListener(this); repaint(); } } /** * Handles a change to an overlay by repainting the panel. * * @param event the event. */ @Override public void overlayChanged(OverlayChangeEvent event) { repaint(); } /** * Switches the display of tooltips for the panel on or off. Note that * tooltips can only be displayed if the chart has been configured to * generate tooltip items. * * @param flag {@code true} to enable tooltips, {@code false} to * disable tooltips. */ public void setDisplayToolTips(boolean flag) { if (flag) { ToolTipManager.sharedInstance().registerComponent(this); } else { ToolTipManager.sharedInstance().unregisterComponent(this); } } /** * Returns a string for the tooltip. * * @param e the mouse event. * * @return A tool tip or {@code null} if no tooltip is available. */ @Override public String getToolTipText(MouseEvent e) { String result = null; if (this.info != null) { EntityCollection entities = this.info.getEntityCollection(); if (entities != null) { Insets insets = getInsets(); ChartEntity entity = entities.getEntity( (int) ((e.getX() - insets.left) / this.scaleX), (int) ((e.getY() - insets.top) / this.scaleY)); if (entity != null) { result = entity.getToolTipText(); } } } return result; } /** * Translates a Java2D point on the chart to a screen location. * * @param java2DPoint the Java2D point. * * @return The screen location. */ public Point translateJava2DToScreen(Point2D java2DPoint) { Insets insets = getInsets(); int x = (int) (java2DPoint.getX() * this.scaleX + insets.left); int y = (int) (java2DPoint.getY() * this.scaleY + insets.top); return new Point(x, y); } /** * Translates a panel (component) location to a Java2D point. * * @param screenPoint the screen location ({@code null} not * permitted). * * @return The Java2D coordinates. */ public Point2D translateScreenToJava2D(Point screenPoint) { Insets insets = getInsets(); double x = (screenPoint.getX() - insets.left) / this.scaleX; double y = (screenPoint.getY() - insets.top) / this.scaleY; return new Point2D.Double(x, y); } /** * Applies any scaling that is in effect for the chart drawing to the * given rectangle. * * @param rect the rectangle ({@code null} not permitted). * * @return A new scaled rectangle. */ public Rectangle2D scale(Rectangle2D rect) { Insets insets = getInsets(); double x = rect.getX() * getScaleX() + insets.left; double y = rect.getY() * getScaleY() + insets.top; double w = rect.getWidth() * getScaleX(); double h = rect.getHeight() * getScaleY(); return new Rectangle2D.Double(x, y, w, h); } /** * Returns the chart entity at a given point. *

* This method will return null if there is (a) no entity at the given * point, or (b) no entity collection has been generated. * * @param viewX the x-coordinate. * @param viewY the y-coordinate. * * @return The chart entity (possibly {@code null}). */ public ChartEntity getEntityForPoint(int viewX, int viewY) { ChartEntity result = null; if (this.info != null) { Insets insets = getInsets(); double x = (viewX - insets.left) / this.scaleX; double y = (viewY - insets.top) / this.scaleY; EntityCollection entities = this.info.getEntityCollection(); result = entities != null ? entities.getEntity(x, y) : null; } return result; } /** * Returns the flag that controls whether or not the offscreen buffer * needs to be refreshed. * * @return A boolean. */ public boolean getRefreshBuffer() { return this.refreshBuffer; } /** * Sets the refresh buffer flag. This flag is used to avoid unnecessary * redrawing of the chart when the offscreen image buffer is used. * * @param flag {@code true} indicates that the buffer should be * refreshed. */ public void setRefreshBuffer(boolean flag) { this.refreshBuffer = flag; } /** * Paints the component by drawing the chart to fill the entire component, * but allowing for the insets (which will be non-zero if a border has been * set for this component). To increase performance (at the expense of * memory), an off-screen buffer image can be used. * * @param g the graphics device for drawing on. */ @Override public void paintComponent(Graphics g) { super.paintComponent(g); if (this.chart == null) { return; } Graphics2D g2 = (Graphics2D) g.create(); // first determine the size of the chart rendering area... Dimension size = getSize(); Insets insets = getInsets(); final int availableWidth = size.width - insets.left - insets.right; final int availableHeight = size.height - insets.top - insets.bottom; // work out if scaling is required... boolean scale = false; int drawWidth = availableWidth; int drawHeight = availableHeight; this.scaleX = 1.0; this.scaleY = 1.0; if (drawWidth < this.minimumDrawWidth) { this.scaleX = (double) drawWidth / (double) this.minimumDrawWidth; drawWidth = this.minimumDrawWidth; scale = true; } else if (drawWidth > this.maximumDrawWidth) { this.scaleX = (double) drawWidth / (double) this.maximumDrawWidth; drawWidth = this.maximumDrawWidth; scale = true; } if (drawHeight < this.minimumDrawHeight) { this.scaleY = (double) drawHeight / (double) this.minimumDrawHeight; drawHeight = this.minimumDrawHeight; scale = true; } else if (drawHeight > this.maximumDrawHeight) { this.scaleY = (double) drawHeight / (double) this.maximumDrawHeight; drawHeight = this.maximumDrawHeight; scale = true; } Dimension chartSize = new Dimension(drawWidth, drawHeight); // are we using the chart buffer? if (this.useBuffer) { // for better rendering on the HiDPI monitors upscaling the buffer to the "native" resolution // instead of using logical one provided by Swing final AffineTransform globalTransform = ((Graphics2D) g).getTransform(); final double globalScaleX = globalTransform.getScaleX(); final double globalScaleY = globalTransform.getScaleY(); final Dimension bufferSize = new Dimension( (int) Math.ceil(availableWidth * globalScaleX), (int) Math.ceil(availableHeight * globalScaleY)); this.chartBuffer = paintChartToBuffer(g2, bufferSize, chartSize, anchor, info); // zap the buffer onto the panel... g2.drawImage(this.chartBuffer, insets.left, insets.top, availableWidth, availableHeight, this); g2.addRenderingHints(this.chart.getRenderingHints()); // bug#187 } else { // redrawing the chart every time... AffineTransform saved = g2.getTransform(); g2.translate(insets.left, insets.top); if (scale) { AffineTransform st = AffineTransform.getScaleInstance( this.scaleX, this.scaleY); g2.transform(st); } this.chart.draw(g2, new Rectangle(chartSize), this.anchor, this.info); g2.setTransform(saved); } for (Overlay overlay : this.overlays) { overlay.paintOverlay(g2, this); } // redraw the zoom rectangle (if present) - if useBuffer is false, // we use XOR so we can XOR the rectangle away again without redrawing // the chart drawZoomRectangle(g2, !this.useBuffer); g2.dispose(); this.anchor = null; this.verticalTraceLine = null; this.horizontalTraceLine = null; } /** * Paints the chart to fill the entire off-screen buffer image. * * @param g2 the graphics context to create an off-screen buffer * image. * @param bufferSize the required off-screen buffer image size. * @param chartSize the size with which the chart should be drawn (apply * scaling if not equal to {@code bufferSize}). * @param anchor the anchor point (in Java2D space) for the chart * ({@code null} permitted). * @param info records info about the drawing ({@code null} means * collect no info). * @return the off-screen buffer image to draw onto the panel. */ protected BufferedImage paintChartToBuffer(Graphics2D g2, Dimension bufferSize, Dimension chartSize, Point2D anchor, ChartRenderingInfo info) { final BufferedImage buffer; if ((this.chartBuffer == null) || (this.chartBuffer.getWidth() != bufferSize.width) || (this.chartBuffer.getHeight() != bufferSize.height)) { GraphicsConfiguration gc = g2.getDeviceConfiguration(); buffer = gc.createCompatibleImage(bufferSize.width, bufferSize.height, Transparency.TRANSLUCENT); this.refreshBuffer = true; } else { buffer = this.chartBuffer; } // do we need to redraw the buffer? if (this.refreshBuffer) { this.refreshBuffer = false; // clear the flag Graphics2D bufferG2 = buffer.createGraphics(); if (!bufferSize.equals(chartSize)) { // Scale the chart to fit the buffer bufferG2.scale( bufferSize.getWidth() / chartSize.getWidth(), bufferSize.getHeight() / chartSize.getHeight()); } Rectangle chartArea = new Rectangle(chartSize); // make the background of the buffer clear and transparent Composite savedComposite = bufferG2.getComposite(); bufferG2.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f)); bufferG2.fill(chartArea); bufferG2.setComposite(savedComposite); this.chart.draw(bufferG2, chartArea, this.anchor, this.info); bufferG2.dispose(); } return buffer; } /** * Receives notification of changes to the chart, and redraws the chart. * * @param event details of the chart change event. */ @Override public void chartChanged(ChartChangeEvent event) { this.refreshBuffer = true; Plot plot = this.chart.getPlot(); if (plot instanceof Zoomable) { Zoomable z = (Zoomable) plot; this.orientation = z.getOrientation(); } repaint(); } /** * Receives notification of a chart progress event. * * @param event the event. */ @Override public void chartProgress(ChartProgressEvent event) { // does nothing - override if necessary } /** * Handles action events generated by the popup menu. * * @param event the event. */ @Override public void actionPerformed(ActionEvent event) { String command = event.getActionCommand(); // many of the zoom methods need a screen location - all we have is // the zoomPoint, but it might be null. Here we grab the x and y // coordinates, or use defaults... double screenX = -1.0; double screenY = -1.0; if (this.zoomPoint != null) { screenX = this.zoomPoint.getX(); screenY = this.zoomPoint.getY(); } if (command.equals(PROPERTIES_COMMAND)) { doEditChartProperties(); } else if (command.equals(COPY_COMMAND)) { doCopy(); } else if (command.equals(SAVE_AS_PNG_COMMAND)) { try { doSaveAs(); } catch (IOException e) { JOptionPane.showMessageDialog(this, "I/O error occurred.", localizationResources.getString("Save_as_PNG"), JOptionPane.WARNING_MESSAGE); } } else if (command.equals(SAVE_AS_SVG_COMMAND)) { try { saveAsSVG(null); } catch (IOException e) { JOptionPane.showMessageDialog(this, "I/O error occurred.", localizationResources.getString("Save_as_SVG"), JOptionPane.WARNING_MESSAGE); } } else if (command.equals(SAVE_AS_PDF_COMMAND)) { saveAsPDF(null); } else if (command.equals(PRINT_COMMAND)) { createChartPrintJob(); } else if (command.equals(ZOOM_IN_BOTH_COMMAND)) { zoomInBoth(screenX, screenY); } else if (command.equals(ZOOM_IN_DOMAIN_COMMAND)) { zoomInDomain(screenX, screenY); } else if (command.equals(ZOOM_IN_RANGE_COMMAND)) { zoomInRange(screenX, screenY); } else if (command.equals(ZOOM_OUT_BOTH_COMMAND)) { zoomOutBoth(screenX, screenY); } else if (command.equals(ZOOM_OUT_DOMAIN_COMMAND)) { zoomOutDomain(screenX, screenY); } else if (command.equals(ZOOM_OUT_RANGE_COMMAND)) { zoomOutRange(screenX, screenY); } else if (command.equals(ZOOM_RESET_BOTH_COMMAND)) { restoreAutoBounds(); } else if (command.equals(ZOOM_RESET_DOMAIN_COMMAND)) { restoreAutoDomainBounds(); } else if (command.equals(ZOOM_RESET_RANGE_COMMAND)) { restoreAutoRangeBounds(); } } /** * Handles a 'mouse entered' event. This method changes the tooltip delays * of ToolTipManager.sharedInstance() to the possibly different values set * for this chart panel. * * @param e the mouse event. */ @Override public void mouseEntered(MouseEvent e) { if (!this.ownToolTipDelaysActive) { ToolTipManager ttm = ToolTipManager.sharedInstance(); this.originalToolTipInitialDelay = ttm.getInitialDelay(); ttm.setInitialDelay(this.ownToolTipInitialDelay); this.originalToolTipReshowDelay = ttm.getReshowDelay(); ttm.setReshowDelay(this.ownToolTipReshowDelay); this.originalToolTipDismissDelay = ttm.getDismissDelay(); ttm.setDismissDelay(this.ownToolTipDismissDelay); this.ownToolTipDelaysActive = true; } } /** * Handles a 'mouse exited' event. This method resets the tooltip delays of * ToolTipManager.sharedInstance() to their * original values in effect before mouseEntered() * * @param e the mouse event. */ @Override public void mouseExited(MouseEvent e) { if (this.ownToolTipDelaysActive) { // restore original tooltip dealys ToolTipManager ttm = ToolTipManager.sharedInstance(); ttm.setInitialDelay(this.originalToolTipInitialDelay); ttm.setReshowDelay(this.originalToolTipReshowDelay); ttm.setDismissDelay(this.originalToolTipDismissDelay); this.ownToolTipDelaysActive = false; } } /** * Handles a 'mouse pressed' event. *

* This event is the popup trigger on Unix/Linux. For Windows, the popup * trigger is the 'mouse released' event. * * @param e The mouse event. */ @Override public void mousePressed(MouseEvent e) { if (this.chart == null) { return; } Plot plot = this.chart.getPlot(); int mods = e.getModifiers(); if ((mods & this.panMask) == this.panMask) { // can we pan this plot? if (plot instanceof Pannable) { Pannable pannable = (Pannable) plot; if (pannable.isDomainPannable() || pannable.isRangePannable()) { Rectangle2D screenDataArea = getScreenDataArea(e.getX(), e.getY()); if (screenDataArea != null && screenDataArea.contains( e.getPoint())) { this.panW = screenDataArea.getWidth(); this.panH = screenDataArea.getHeight(); this.panLast = e.getPoint(); setCursor(Cursor.getPredefinedCursor( Cursor.MOVE_CURSOR)); } } // the actual panning occurs later in the mouseDragged() // method } } else if (this.zoomRectangle == null) { Rectangle2D screenDataArea = getScreenDataArea(e.getX(), e.getY()); if (screenDataArea != null) { this.zoomPoint = getPointInRectangle(e.getX(), e.getY(), screenDataArea); } else { this.zoomPoint = null; } if (e.isPopupTrigger()) { if (this.popup != null) { displayPopupMenu(e.getX(), e.getY()); } } } } /** * Returns a point based on (x, y) but constrained to be within the bounds * of the given rectangle. This method could be moved to JCommon. * * @param x the x-coordinate. * @param y the y-coordinate. * @param area the rectangle ({@code null} not permitted). * * @return A point within the rectangle. */ private Point2D getPointInRectangle(int x, int y, Rectangle2D area) { double xx = Math.max(area.getMinX(), Math.min(x, area.getMaxX())); double yy = Math.max(area.getMinY(), Math.min(y, area.getMaxY())); return new Point2D.Double(xx, yy); } /** * Handles a 'mouse dragged' event. * * @param e the mouse event. */ @Override public void mouseDragged(MouseEvent e) { // if the popup menu has already been triggered, then ignore dragging... if (this.popup != null && this.popup.isShowing()) { return; } // handle panning if we have a start point if (this.panLast != null) { double dx = e.getX() - this.panLast.getX(); double dy = e.getY() - this.panLast.getY(); if (dx == 0.0 && dy == 0.0) { return; } double wPercent = -dx / this.panW; double hPercent = dy / this.panH; boolean old = this.chart.getPlot().isNotify(); this.chart.getPlot().setNotify(false); Pannable p = (Pannable) this.chart.getPlot(); if (p.getOrientation() == PlotOrientation.VERTICAL) { p.panDomainAxes(wPercent, this.info.getPlotInfo(), this.panLast); p.panRangeAxes(hPercent, this.info.getPlotInfo(), this.panLast); } else { p.panDomainAxes(hPercent, this.info.getPlotInfo(), this.panLast); p.panRangeAxes(wPercent, this.info.getPlotInfo(), this.panLast); } this.panLast = e.getPoint(); this.chart.getPlot().setNotify(old); return; } // if no initial zoom point was set, ignore dragging... if (this.zoomPoint == null) { return; } Graphics2D g2 = (Graphics2D) getGraphics(); // erase the previous zoom rectangle (if any). We only need to do // this is we are using XOR mode, which we do when we're not using // the buffer (if there is a buffer, then at the end of this method we // just trigger a repaint) if (!this.useBuffer) { drawZoomRectangle(g2, true); } boolean hZoom, vZoom; if (this.orientation == PlotOrientation.HORIZONTAL) { hZoom = this.rangeZoomable; vZoom = this.domainZoomable; } else { hZoom = this.domainZoomable; vZoom = this.rangeZoomable; } Rectangle2D scaledDataArea = getScreenDataArea( (int) this.zoomPoint.getX(), (int) this.zoomPoint.getY()); if (hZoom && vZoom) { // selected rectangle shouldn't extend outside the data area... double xmax = Math.min(e.getX(), scaledDataArea.getMaxX()); double ymax = Math.min(e.getY(), scaledDataArea.getMaxY()); this.zoomRectangle = new Rectangle2D.Double( this.zoomPoint.getX(), this.zoomPoint.getY(), xmax - this.zoomPoint.getX(), ymax - this.zoomPoint.getY()); } else if (hZoom) { double xmax = Math.min(e.getX(), scaledDataArea.getMaxX()); this.zoomRectangle = new Rectangle2D.Double( this.zoomPoint.getX(), scaledDataArea.getMinY(), xmax - this.zoomPoint.getX(), scaledDataArea.getHeight()); } else if (vZoom) { double ymax = Math.min(e.getY(), scaledDataArea.getMaxY()); this.zoomRectangle = new Rectangle2D.Double( scaledDataArea.getMinX(), this.zoomPoint.getY(), scaledDataArea.getWidth(), ymax - this.zoomPoint.getY()); } // Draw the new zoom rectangle... if (this.useBuffer) { repaint(); } else { // with no buffer, we use XOR to draw the rectangle "over" the // chart... drawZoomRectangle(g2, true); } g2.dispose(); } /** * Handles a 'mouse released' event. On Windows, we need to check if this * is a popup trigger, but only if we haven't already been tracking a zoom * rectangle. * * @param e information about the event. */ @Override public void mouseReleased(MouseEvent e) { // if we've been panning, we need to reset now that the mouse is // released... if (this.panLast != null) { this.panLast = null; setCursor(Cursor.getDefaultCursor()); } else if (this.zoomRectangle != null) { boolean hZoom, vZoom; if (this.orientation == PlotOrientation.HORIZONTAL) { hZoom = this.rangeZoomable; vZoom = this.domainZoomable; } else { hZoom = this.domainZoomable; vZoom = this.rangeZoomable; } boolean zoomTrigger1 = hZoom && Math.abs(e.getX() - this.zoomPoint.getX()) >= this.zoomTriggerDistance; boolean zoomTrigger2 = vZoom && Math.abs(e.getY() - this.zoomPoint.getY()) >= this.zoomTriggerDistance; if (zoomTrigger1 || zoomTrigger2) { if ((hZoom && (e.getX() < this.zoomPoint.getX())) || (vZoom && (e.getY() < this.zoomPoint.getY()))) { restoreAutoBounds(); } else { double x, y, w, h; Rectangle2D screenDataArea = getScreenDataArea( (int) this.zoomPoint.getX(), (int) this.zoomPoint.getY()); double maxX = screenDataArea.getMaxX(); double maxY = screenDataArea.getMaxY(); // for mouseReleased event, (horizontalZoom || verticalZoom) // will be true, so we can just test for either being false; // otherwise both are true if (!vZoom) { x = this.zoomPoint.getX(); y = screenDataArea.getMinY(); w = Math.min(this.zoomRectangle.getWidth(), maxX - this.zoomPoint.getX()); h = screenDataArea.getHeight(); } else if (!hZoom) { x = screenDataArea.getMinX(); y = this.zoomPoint.getY(); w = screenDataArea.getWidth(); h = Math.min(this.zoomRectangle.getHeight(), maxY - this.zoomPoint.getY()); } else { x = this.zoomPoint.getX(); y = this.zoomPoint.getY(); w = Math.min(this.zoomRectangle.getWidth(), maxX - this.zoomPoint.getX()); h = Math.min(this.zoomRectangle.getHeight(), maxY - this.zoomPoint.getY()); } Rectangle2D zoomArea = new Rectangle2D.Double(x, y, w, h); zoom(zoomArea); } this.zoomPoint = null; this.zoomRectangle = null; } else { // erase the zoom rectangle Graphics2D g2 = (Graphics2D) getGraphics(); if (this.useBuffer) { repaint(); } else { drawZoomRectangle(g2, true); } g2.dispose(); this.zoomPoint = null; this.zoomRectangle = null; } } else if (e.isPopupTrigger()) { if (this.popup != null) { displayPopupMenu(e.getX(), e.getY()); } } } /** * Receives notification of mouse clicks on the panel. These are * translated and passed on to any registered {@link ChartMouseListener}s. * * @param event Information about the mouse event. */ @Override public void mouseClicked(MouseEvent event) { Insets insets = getInsets(); int x = (int) ((event.getX() - insets.left) / this.scaleX); int y = (int) ((event.getY() - insets.top) / this.scaleY); this.anchor = new Point2D.Double(x, y); if (this.chart == null) { return; } this.chart.setNotify(true); // new entity code... Object[] listeners = this.chartMouseListeners.getListeners( ChartMouseListener.class); if (listeners.length == 0) { return; } ChartEntity entity = null; if (this.info != null) { EntityCollection entities = this.info.getEntityCollection(); if (entities != null) { entity = entities.getEntity(x, y); } } ChartMouseEvent chartEvent = new ChartMouseEvent(getChart(), event, entity); for (int i = listeners.length - 1; i >= 0; i -= 1) { ((ChartMouseListener) listeners[i]).chartMouseClicked(chartEvent); } } /** * Implementation of the MouseMotionListener's method. * * @param e the event. */ @Override public void mouseMoved(MouseEvent e) { Graphics2D g2 = (Graphics2D) getGraphics(); if (this.horizontalAxisTrace) { drawHorizontalAxisTrace(g2, e.getX()); } if (this.verticalAxisTrace) { drawVerticalAxisTrace(g2, e.getY()); } g2.dispose(); Object[] listeners = this.chartMouseListeners.getListeners( ChartMouseListener.class); if (listeners.length == 0) { return; } Insets insets = getInsets(); int x = (int) ((e.getX() - insets.left) / this.scaleX); int y = (int) ((e.getY() - insets.top) / this.scaleY); ChartEntity entity = null; if (this.info != null) { EntityCollection entities = this.info.getEntityCollection(); if (entities != null) { entity = entities.getEntity(x, y); } } // we can only generate events if the panel's chart is not null // (see bug report 1556951) if (this.chart != null) { ChartMouseEvent event = new ChartMouseEvent(getChart(), e, entity); for (int i = listeners.length - 1; i >= 0; i -= 1) { ((ChartMouseListener) listeners[i]).chartMouseMoved(event); } } } /** * Zooms in on an anchor point (specified in screen coordinate space). * * @param x the x value (in screen coordinates). * @param y the y value (in screen coordinates). */ public void zoomInBoth(double x, double y) { Plot plot = this.chart.getPlot(); if (plot == null) { return; } // here we tweak the notify flag on the plot so that only // one notification happens even though we update multiple // axes... boolean savedNotify = plot.isNotify(); plot.setNotify(false); zoomInDomain(x, y); zoomInRange(x, y); plot.setNotify(savedNotify); } /** * Decreases the length of the domain axis, centered about the given * coordinate on the screen. The length of the domain axis is reduced * by the value of {@link #getZoomInFactor()}. * * @param x the x coordinate (in screen coordinates). * @param y the y-coordinate (in screen coordinates). */ public void zoomInDomain(double x, double y) { Plot plot = this.chart.getPlot(); if (plot instanceof Zoomable) { // here we tweak the notify flag on the plot so that only // one notification happens even though we update multiple // axes... boolean savedNotify = plot.isNotify(); plot.setNotify(false); Zoomable z = (Zoomable) plot; z.zoomDomainAxes(this.zoomInFactor, this.info.getPlotInfo(), translateScreenToJava2D(new Point((int) x, (int) y)), this.zoomAroundAnchor); plot.setNotify(savedNotify); } } /** * Decreases the length of the range axis, centered about the given * coordinate on the screen. The length of the range axis is reduced by * the value of {@link #getZoomInFactor()}. * * @param x the x-coordinate (in screen coordinates). * @param y the y coordinate (in screen coordinates). */ public void zoomInRange(double x, double y) { Plot plot = this.chart.getPlot(); if (plot instanceof Zoomable) { // here we tweak the notify flag on the plot so that only // one notification happens even though we update multiple // axes... boolean savedNotify = plot.isNotify(); plot.setNotify(false); Zoomable z = (Zoomable) plot; z.zoomRangeAxes(this.zoomInFactor, this.info.getPlotInfo(), translateScreenToJava2D(new Point((int) x, (int) y)), this.zoomAroundAnchor); plot.setNotify(savedNotify); } } /** * Zooms out on an anchor point (specified in screen coordinate space). * * @param x the x value (in screen coordinates). * @param y the y value (in screen coordinates). */ public void zoomOutBoth(double x, double y) { Plot plot = this.chart.getPlot(); if (plot == null) { return; } // here we tweak the notify flag on the plot so that only // one notification happens even though we update multiple // axes... boolean savedNotify = plot.isNotify(); plot.setNotify(false); zoomOutDomain(x, y); zoomOutRange(x, y); plot.setNotify(savedNotify); } /** * Increases the length of the domain axis, centered about the given * coordinate on the screen. The length of the domain axis is increased * by the value of {@link #getZoomOutFactor()}. * * @param x the x coordinate (in screen coordinates). * @param y the y-coordinate (in screen coordinates). */ public void zoomOutDomain(double x, double y) { Plot plot = this.chart.getPlot(); if (plot instanceof Zoomable) { // here we tweak the notify flag on the plot so that only // one notification happens even though we update multiple // axes... boolean savedNotify = plot.isNotify(); plot.setNotify(false); Zoomable z = (Zoomable) plot; z.zoomDomainAxes(this.zoomOutFactor, this.info.getPlotInfo(), translateScreenToJava2D(new Point((int) x, (int) y)), this.zoomAroundAnchor); plot.setNotify(savedNotify); } } /** * Increases the length the range axis, centered about the given * coordinate on the screen. The length of the range axis is increased * by the value of {@link #getZoomOutFactor()}. * * @param x the x coordinate (in screen coordinates). * @param y the y-coordinate (in screen coordinates). */ public void zoomOutRange(double x, double y) { Plot plot = this.chart.getPlot(); if (plot instanceof Zoomable) { // here we tweak the notify flag on the plot so that only // one notification happens even though we update multiple // axes... boolean savedNotify = plot.isNotify(); plot.setNotify(false); Zoomable z = (Zoomable) plot; z.zoomRangeAxes(this.zoomOutFactor, this.info.getPlotInfo(), translateScreenToJava2D(new Point((int) x, (int) y)), this.zoomAroundAnchor); plot.setNotify(savedNotify); } } /** * Zooms in on a selected region. * * @param selection the selected region. */ public void zoom(Rectangle2D selection) { // get the origin of the zoom selection in the Java2D space used for // drawing the chart (that is, before any scaling to fit the panel) Point2D selectOrigin = translateScreenToJava2D(new Point( (int) Math.ceil(selection.getX()), (int) Math.ceil(selection.getY()))); PlotRenderingInfo plotInfo = this.info.getPlotInfo(); Rectangle2D scaledDataArea = getScreenDataArea( (int) selection.getCenterX(), (int) selection.getCenterY()); if ((selection.getHeight() > 0) && (selection.getWidth() > 0)) { double hLower = (selection.getMinX() - scaledDataArea.getMinX()) / scaledDataArea.getWidth(); double hUpper = (selection.getMaxX() - scaledDataArea.getMinX()) / scaledDataArea.getWidth(); double vLower = (scaledDataArea.getMaxY() - selection.getMaxY()) / scaledDataArea.getHeight(); double vUpper = (scaledDataArea.getMaxY() - selection.getMinY()) / scaledDataArea.getHeight(); Plot p = this.chart.getPlot(); if (p instanceof Zoomable) { // here we tweak the notify flag on the plot so that only // one notification happens even though we update multiple // axes... boolean savedNotify = p.isNotify(); p.setNotify(false); Zoomable z = (Zoomable) p; if (z.getOrientation() == PlotOrientation.HORIZONTAL) { z.zoomDomainAxes(vLower, vUpper, plotInfo, selectOrigin); z.zoomRangeAxes(hLower, hUpper, plotInfo, selectOrigin); } else { z.zoomDomainAxes(hLower, hUpper, plotInfo, selectOrigin); z.zoomRangeAxes(vLower, vUpper, plotInfo, selectOrigin); } p.setNotify(savedNotify); } } } /** * Restores the auto-range calculation on both axes. */ public void restoreAutoBounds() { Plot plot = this.chart.getPlot(); if (plot == null) { return; } // here we tweak the notify flag on the plot so that only // one notification happens even though we update multiple // axes... boolean savedNotify = plot.isNotify(); plot.setNotify(false); restoreAutoDomainBounds(); restoreAutoRangeBounds(); plot.setNotify(savedNotify); } /** * Restores the auto-range calculation on the domain axis. */ public void restoreAutoDomainBounds() { Plot plot = this.chart.getPlot(); if (plot instanceof Zoomable) { Zoomable z = (Zoomable) plot; // here we tweak the notify flag on the plot so that only // one notification happens even though we update multiple // axes... boolean savedNotify = plot.isNotify(); plot.setNotify(false); // we need to guard against this.zoomPoint being null Point2D zp = (this.zoomPoint != null ? this.zoomPoint : new Point()); z.zoomDomainAxes(0.0, this.info.getPlotInfo(), zp); plot.setNotify(savedNotify); } } /** * Restores the auto-range calculation on the range axis. */ public void restoreAutoRangeBounds() { Plot plot = this.chart.getPlot(); if (plot instanceof Zoomable) { Zoomable z = (Zoomable) plot; // here we tweak the notify flag on the plot so that only // one notification happens even though we update multiple // axes... boolean savedNotify = plot.isNotify(); plot.setNotify(false); // we need to guard against this.zoomPoint being null Point2D zp = (this.zoomPoint != null ? this.zoomPoint : new Point()); z.zoomRangeAxes(0.0, this.info.getPlotInfo(), zp); plot.setNotify(savedNotify); } } /** * Returns the data area for the chart (the area inside the axes) with the * current scaling applied (that is, the area as it appears on screen). * * @return The scaled data area. */ public Rectangle2D getScreenDataArea() { Rectangle2D dataArea = this.info.getPlotInfo().getDataArea(); Insets insets = getInsets(); double x = dataArea.getX() * this.scaleX + insets.left; double y = dataArea.getY() * this.scaleY + insets.top; double w = dataArea.getWidth() * this.scaleX; double h = dataArea.getHeight() * this.scaleY; return new Rectangle2D.Double(x, y, w, h); } /** * Returns the data area (the area inside the axes) for the plot or subplot, * with the current scaling applied. * * @param x the x-coordinate (for subplot selection). * @param y the y-coordinate (for subplot selection). * * @return The scaled data area. */ public Rectangle2D getScreenDataArea(int x, int y) { PlotRenderingInfo plotInfo = this.info.getPlotInfo(); Rectangle2D result; if (plotInfo.getSubplotCount() == 0) { result = getScreenDataArea(); } else { // get the origin of the zoom selection in the Java2D space used for // drawing the chart (that is, before any scaling to fit the panel) Point2D selectOrigin = translateScreenToJava2D(new Point(x, y)); int subplotIndex = plotInfo.getSubplotIndex(selectOrigin); if (subplotIndex == -1) { return null; } result = scale(plotInfo.getSubplotInfo(subplotIndex).getDataArea()); } return result; } /** * Returns the initial tooltip delay value used inside this chart panel. * * @return An integer representing the initial delay value, in milliseconds. * * @see javax.swing.ToolTipManager#getInitialDelay() */ public int getInitialDelay() { return this.ownToolTipInitialDelay; } /** * Returns the reshow tooltip delay value used inside this chart panel. * * @return An integer representing the reshow delay value, in milliseconds. * * @see javax.swing.ToolTipManager#getReshowDelay() */ public int getReshowDelay() { return this.ownToolTipReshowDelay; } /** * Returns the dismissal tooltip delay value used inside this chart panel. * * @return An integer representing the dismissal delay value, in * milliseconds. * * @see javax.swing.ToolTipManager#getDismissDelay() */ public int getDismissDelay() { return this.ownToolTipDismissDelay; } /** * Specifies the initial delay value for this chart panel. * * @param delay the number of milliseconds to delay (after the cursor has * paused) before displaying. * * @see javax.swing.ToolTipManager#setInitialDelay(int) */ public void setInitialDelay(int delay) { this.ownToolTipInitialDelay = delay; } /** * Specifies the amount of time before the user has to wait initialDelay * milliseconds before a tooltip will be shown. * * @param delay time in milliseconds * * @see javax.swing.ToolTipManager#setReshowDelay(int) */ public void setReshowDelay(int delay) { this.ownToolTipReshowDelay = delay; } /** * Specifies the dismissal delay value for this chart panel. * * @param delay the number of milliseconds to delay before taking away the * tooltip * * @see javax.swing.ToolTipManager#setDismissDelay(int) */ public void setDismissDelay(int delay) { this.ownToolTipDismissDelay = delay; } /** * Returns the zoom in factor. * * @return The zoom in factor. * * @see #setZoomInFactor(double) */ public double getZoomInFactor() { return this.zoomInFactor; } /** * Sets the zoom in factor. * * @param factor the factor. * * @see #getZoomInFactor() */ public void setZoomInFactor(double factor) { this.zoomInFactor = factor; } /** * Returns the zoom out factor. * * @return The zoom out factor. * * @see #setZoomOutFactor(double) */ public double getZoomOutFactor() { return this.zoomOutFactor; } /** * Sets the zoom out factor. * * @param factor the factor. * * @see #getZoomOutFactor() */ public void setZoomOutFactor(double factor) { this.zoomOutFactor = factor; } /** * Draws zoom rectangle (if present). * The drawing is performed in XOR mode, therefore * when this method is called twice in a row, * the second call will completely restore the state * of the canvas. * * @param g2 the graphics device. * @param xor use XOR for drawing? */ private void drawZoomRectangle(Graphics2D g2, boolean xor) { if (this.zoomRectangle != null) { if (xor) { // Set XOR mode to draw the zoom rectangle g2.setXORMode(Color.GRAY); } if (this.fillZoomRectangle) { g2.setPaint(this.zoomFillPaint); g2.fill(this.zoomRectangle); } else { g2.setPaint(this.zoomOutlinePaint); g2.draw(this.zoomRectangle); } if (xor) { // Reset to the default 'overwrite' mode g2.setPaintMode(); } } } /** * Draws a vertical line used to trace the mouse position to the horizontal * axis. * * @param g2 the graphics device. * @param x the x-coordinate of the trace line. */ private void drawHorizontalAxisTrace(Graphics2D g2, int x) { Rectangle2D dataArea = getScreenDataArea(); g2.setXORMode(Color.ORANGE); if (((int) dataArea.getMinX() < x) && (x < (int) dataArea.getMaxX())) { if (this.verticalTraceLine != null) { g2.draw(this.verticalTraceLine); this.verticalTraceLine.setLine(x, (int) dataArea.getMinY(), x, (int) dataArea.getMaxY()); } else { this.verticalTraceLine = new Line2D.Float(x, (int) dataArea.getMinY(), x, (int) dataArea.getMaxY()); } g2.draw(this.verticalTraceLine); } // Reset to the default 'overwrite' mode g2.setPaintMode(); } /** * Draws a horizontal line used to trace the mouse position to the vertical * axis. * * @param g2 the graphics device. * @param y the y-coordinate of the trace line. */ private void drawVerticalAxisTrace(Graphics2D g2, int y) { Rectangle2D dataArea = getScreenDataArea(); g2.setXORMode(Color.ORANGE); if (((int) dataArea.getMinY() < y) && (y < (int) dataArea.getMaxY())) { if (this.horizontalTraceLine != null) { g2.draw(this.horizontalTraceLine); this.horizontalTraceLine.setLine((int) dataArea.getMinX(), y, (int) dataArea.getMaxX(), y); } else { this.horizontalTraceLine = new Line2D.Float( (int) dataArea.getMinX(), y, (int) dataArea.getMaxX(), y); } g2.draw(this.horizontalTraceLine); } // Reset to the default 'overwrite' mode g2.setPaintMode(); } /** * Displays a dialog that allows the user to edit the properties for the * current chart. */ public void doEditChartProperties() { ChartEditor editor = ChartEditorManager.getChartEditor(this.chart); int result = JOptionPane.showConfirmDialog(this, editor, localizationResources.getString("Chart_Properties"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE); if (result == JOptionPane.OK_OPTION) { editor.updateChart(this.chart); } } /** * Copies the current chart to the system clipboard. */ public void doCopy() { Clipboard systemClipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); Insets insets = getInsets(); int w = getWidth() - insets.left - insets.right; int h = getHeight() - insets.top - insets.bottom; ChartTransferable selection = new ChartTransferable(this.chart, w, h, getMinimumDrawWidth(), getMinimumDrawHeight(), getMaximumDrawWidth(), getMaximumDrawHeight(), true); systemClipboard.setContents(selection, null); } /** * Opens a file chooser and gives the user an opportunity to save the chart * in PNG format. * * @throws IOException if there is an I/O error. */ public void doSaveAs() throws IOException { JFileChooser fileChooser = new JFileChooser(); fileChooser.setCurrentDirectory(this.defaultDirectoryForSaveAs); FileNameExtensionFilter filter = new FileNameExtensionFilter( localizationResources.getString("PNG_Image_Files"), "png"); fileChooser.addChoosableFileFilter(filter); fileChooser.setFileFilter(filter); int option = fileChooser.showSaveDialog(this); if (option == JFileChooser.APPROVE_OPTION) { String filename = fileChooser.getSelectedFile().getPath(); if (isEnforceFileExtensions()) { if (!filename.endsWith(".png")) { filename = filename + ".png"; } } ChartUtils.saveChartAsPNG(new File(filename), this.chart, getWidth(), getHeight()); } } /** * Saves the chart in SVG format (a filechooser will be displayed so that * the user can specify the filename). Note that this method only works * if the JFreeSVG library is on the classpath...if this library is not * present, the method will fail. */ private void saveAsSVG(File f) throws IOException { File file = f; if (file == null) { JFileChooser fileChooser = new JFileChooser(); fileChooser.setCurrentDirectory(this.defaultDirectoryForSaveAs); FileNameExtensionFilter filter = new FileNameExtensionFilter( localizationResources.getString("SVG_Files"), "svg"); fileChooser.addChoosableFileFilter(filter); fileChooser.setFileFilter(filter); int option = fileChooser.showSaveDialog(this); if (option == JFileChooser.APPROVE_OPTION) { String filename = fileChooser.getSelectedFile().getPath(); if (isEnforceFileExtensions()) { if (!filename.endsWith(".svg")) { filename = filename + ".svg"; } } file = new File(filename); if (file.exists()) { String fileExists = localizationResources.getString( "FILE_EXISTS_CONFIRM_OVERWRITE"); int response = JOptionPane.showConfirmDialog(this, fileExists, localizationResources.getString("Save_as_SVG"), JOptionPane.OK_CANCEL_OPTION); if (response == JOptionPane.CANCEL_OPTION) { file = null; } } } } if (file != null) { // use reflection to get the SVG string String svg = generateSVG(getWidth(), getHeight()); BufferedWriter writer = null; try { writer = new BufferedWriter(new FileWriter(file)); writer.write("\n"); writer.write(svg + "\n"); writer.flush(); } finally { try { if (writer != null) { writer.close(); } } catch (IOException ex) { throw new RuntimeException(ex); } } } } /** * Generates a string containing a rendering of the chart in SVG format. * This feature is only supported if the JFreeSVG library is included on * the classpath. * * @return A string containing an SVG element for the current chart, or * {@code null} if there is a problem with the method invocation * by reflection. */ private String generateSVG(int width, int height) { Graphics2D g2 = createSVGGraphics2D(width, height); if (g2 == null) { throw new IllegalStateException("JFreeSVG library is not present."); } // we suppress shadow generation, because SVG is a vector format and // the shadow effect is applied via bitmap effects... g2.setRenderingHint(JFreeChart.KEY_SUPPRESS_SHADOW_GENERATION, true); String svg = null; Rectangle2D drawArea = new Rectangle2D.Double(0, 0, width, height); this.chart.draw(g2, drawArea); try { Method m = g2.getClass().getMethod("getSVGElement"); svg = (String) m.invoke(g2); } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { // null will be returned } return svg; } private Graphics2D createSVGGraphics2D(int w, int h) { try { Class svgGraphics2d = Class.forName("org.jfree.graphics2d.svg.SVGGraphics2D"); Constructor ctor = svgGraphics2d.getConstructor(int.class, int.class); return (Graphics2D) ctor.newInstance(w, h); } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { return null; } } /** * Saves the chart in PDF format (a filechooser will be displayed so that * the user can specify the filename). Note that this method only works * if the OrsonPDF library is on the classpath...if this library is not * present, the method will fail. */ private void saveAsPDF(File f) { File file = f; if (file == null) { JFileChooser fileChooser = new JFileChooser(); fileChooser.setCurrentDirectory(this.defaultDirectoryForSaveAs); FileNameExtensionFilter filter = new FileNameExtensionFilter( localizationResources.getString("PDF_Files"), "pdf"); fileChooser.addChoosableFileFilter(filter); fileChooser.setFileFilter(filter); int option = fileChooser.showSaveDialog(this); if (option == JFileChooser.APPROVE_OPTION) { String filename = fileChooser.getSelectedFile().getPath(); if (isEnforceFileExtensions()) { if (!filename.endsWith(".pdf")) { filename = filename + ".pdf"; } } file = new File(filename); if (file.exists()) { String fileExists = localizationResources.getString( "FILE_EXISTS_CONFIRM_OVERWRITE"); int response = JOptionPane.showConfirmDialog(this, fileExists, localizationResources.getString("Save_as_PDF"), JOptionPane.OK_CANCEL_OPTION); if (response == JOptionPane.CANCEL_OPTION) { file = null; } } } } if (file != null) { writeAsPDF(file, getWidth(), getHeight()); } } /** * Returns {@code true} if OrsonPDF is on the classpath, and * {@code false} otherwise. The OrsonPDF library can be found at * http://www.object-refinery.com/pdf/ * * @return A boolean. */ private boolean isOrsonPDFAvailable() { Class pdfDocumentClass = null; try { pdfDocumentClass = Class.forName("com.orsonpdf.PDFDocument"); } catch (ClassNotFoundException e) { // pdfDocument class will be null so the function will return false } return (pdfDocumentClass != null); } /** * Writes the current chart to the specified file in PDF format. This * will only work when the OrsonPDF library is found on the classpath. * Reflection is used to ensure there is no compile-time dependency on * OrsonPDF (which is non-free software). * * @param file the output file ({@code null} not permitted). * @param w the chart width. * @param h the chart height. */ private void writeAsPDF(File file, int w, int h) { if (!isOrsonPDFAvailable()) { throw new IllegalStateException( "OrsonPDF is not present on the classpath."); } Args.nullNotPermitted(file, "file"); try { Class pdfDocClass = Class.forName("com.orsonpdf.PDFDocument"); Object pdfDoc = pdfDocClass.newInstance(); Method m = pdfDocClass.getMethod("createPage", Rectangle2D.class); Rectangle2D rect = new Rectangle(w, h); Object page = m.invoke(pdfDoc, rect); Method m2 = page.getClass().getMethod("getGraphics2D"); Graphics2D g2 = (Graphics2D) m2.invoke(page); // we suppress shadow generation, because PDF is a vector format and // the shadow effect is applied via bitmap effects... g2.setRenderingHint(JFreeChart.KEY_SUPPRESS_SHADOW_GENERATION, true); Rectangle2D drawArea = new Rectangle2D.Double(0, 0, w, h); this.chart.draw(g2, drawArea); Method m3 = pdfDocClass.getMethod("writeToFile", File.class); m3.invoke(pdfDoc, file); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException ex) { throw new RuntimeException(ex); } } /** * Creates a print job for the chart. */ public void createChartPrintJob() { PrinterJob job = PrinterJob.getPrinterJob(); PageFormat pf = job.defaultPage(); PageFormat pf2 = job.pageDialog(pf); if (pf2 != pf) { job.setPrintable(this, pf2); if (job.printDialog()) { try { job.print(); } catch (PrinterException e) { JOptionPane.showMessageDialog(this, e); } } } } /** * Prints the chart on a single page. * * @param g the graphics context. * @param pf the page format to use. * @param pageIndex the index of the page. If not {@code 0}, nothing * gets printed. * * @return The result of printing. */ @Override public int print(Graphics g, PageFormat pf, int pageIndex) { if (pageIndex != 0) { return NO_SUCH_PAGE; } Graphics2D g2 = (Graphics2D) g; double x = pf.getImageableX(); double y = pf.getImageableY(); double w = pf.getImageableWidth(); double h = pf.getImageableHeight(); this.chart.draw(g2, new Rectangle2D.Double(x, y, w, h), this.anchor, null); return PAGE_EXISTS; } /** * Adds a listener to the list of objects listening for chart mouse events. * * @param listener the listener ({@code null} not permitted). */ public void addChartMouseListener(ChartMouseListener listener) { Args.nullNotPermitted(listener, "listener"); this.chartMouseListeners.add(ChartMouseListener.class, listener); } /** * Removes a listener from the list of objects listening for chart mouse * events. * * @param listener the listener. */ public void removeChartMouseListener(ChartMouseListener listener) { this.chartMouseListeners.remove(ChartMouseListener.class, listener); } /** * Returns an array of the listeners of the given type registered with the * panel. * * @param listenerType the listener type. * * @return An array of listeners. */ @Override public EventListener[] getListeners(Class listenerType) { if (listenerType == ChartMouseListener.class) { // fetch listeners from local storage return this.chartMouseListeners.getListeners(listenerType); } else { return super.getListeners(listenerType); } } /** * Creates a popup menu for the panel. * * @param properties include a menu item for the chart property editor. * @param save include a menu item for saving the chart. * @param print include a menu item for printing the chart. * @param zoom include menu items for zooming. * * @return The popup menu. */ protected JPopupMenu createPopupMenu(boolean properties, boolean save, boolean print, boolean zoom) { return createPopupMenu(properties, false, save, print, zoom); } /** * Creates a popup menu for the panel. * * @param properties include a menu item for the chart property editor. * @param copy include a menu item for copying to the clipboard. * @param save include a menu item for saving the chart. * @param print include a menu item for printing the chart. * @param zoom include menu items for zooming. * * @return The popup menu. */ protected JPopupMenu createPopupMenu(boolean properties, boolean copy, boolean save, boolean print, boolean zoom) { JPopupMenu result = new JPopupMenu(localizationResources.getString("Chart") + ":"); boolean separator = false; if (properties) { JMenuItem propertiesItem = new JMenuItem( localizationResources.getString("Properties...")); propertiesItem.setActionCommand(PROPERTIES_COMMAND); propertiesItem.addActionListener(this); result.add(propertiesItem); separator = true; } if (copy) { if (separator) { result.addSeparator(); } JMenuItem copyItem = new JMenuItem( localizationResources.getString("Copy")); copyItem.setActionCommand(COPY_COMMAND); copyItem.addActionListener(this); result.add(copyItem); separator = !save; } if (save) { if (separator) { result.addSeparator(); } JMenu saveSubMenu = new JMenu(localizationResources.getString( "Save_as")); JMenuItem pngItem = new JMenuItem(localizationResources.getString( "PNG...")); pngItem.setActionCommand("SAVE_AS_PNG"); pngItem.addActionListener(this); saveSubMenu.add(pngItem); if (createSVGGraphics2D(10, 10) != null) { JMenuItem svgItem = new JMenuItem(localizationResources.getString( "SVG...")); svgItem.setActionCommand("SAVE_AS_SVG"); svgItem.addActionListener(this); saveSubMenu.add(svgItem); } if (isOrsonPDFAvailable()) { JMenuItem pdfItem = new JMenuItem( localizationResources.getString("PDF...")); pdfItem.setActionCommand("SAVE_AS_PDF"); pdfItem.addActionListener(this); saveSubMenu.add(pdfItem); } result.add(saveSubMenu); separator = true; } if (print) { if (separator) { result.addSeparator(); } JMenuItem printItem = new JMenuItem( localizationResources.getString("Print...")); printItem.setActionCommand(PRINT_COMMAND); printItem.addActionListener(this); result.add(printItem); separator = true; } if (zoom) { if (separator) { result.addSeparator(); } JMenu zoomInMenu = new JMenu( localizationResources.getString("Zoom_In")); this.zoomInBothMenuItem = new JMenuItem( localizationResources.getString("All_Axes")); this.zoomInBothMenuItem.setActionCommand(ZOOM_IN_BOTH_COMMAND); this.zoomInBothMenuItem.addActionListener(this); zoomInMenu.add(this.zoomInBothMenuItem); zoomInMenu.addSeparator(); this.zoomInDomainMenuItem = new JMenuItem( localizationResources.getString("Domain_Axis")); this.zoomInDomainMenuItem.setActionCommand(ZOOM_IN_DOMAIN_COMMAND); this.zoomInDomainMenuItem.addActionListener(this); zoomInMenu.add(this.zoomInDomainMenuItem); this.zoomInRangeMenuItem = new JMenuItem( localizationResources.getString("Range_Axis")); this.zoomInRangeMenuItem.setActionCommand(ZOOM_IN_RANGE_COMMAND); this.zoomInRangeMenuItem.addActionListener(this); zoomInMenu.add(this.zoomInRangeMenuItem); result.add(zoomInMenu); JMenu zoomOutMenu = new JMenu( localizationResources.getString("Zoom_Out")); this.zoomOutBothMenuItem = new JMenuItem( localizationResources.getString("All_Axes")); this.zoomOutBothMenuItem.setActionCommand(ZOOM_OUT_BOTH_COMMAND); this.zoomOutBothMenuItem.addActionListener(this); zoomOutMenu.add(this.zoomOutBothMenuItem); zoomOutMenu.addSeparator(); this.zoomOutDomainMenuItem = new JMenuItem( localizationResources.getString("Domain_Axis")); this.zoomOutDomainMenuItem.setActionCommand( ZOOM_OUT_DOMAIN_COMMAND); this.zoomOutDomainMenuItem.addActionListener(this); zoomOutMenu.add(this.zoomOutDomainMenuItem); this.zoomOutRangeMenuItem = new JMenuItem( localizationResources.getString("Range_Axis")); this.zoomOutRangeMenuItem.setActionCommand(ZOOM_OUT_RANGE_COMMAND); this.zoomOutRangeMenuItem.addActionListener(this); zoomOutMenu.add(this.zoomOutRangeMenuItem); result.add(zoomOutMenu); JMenu autoRangeMenu = new JMenu( localizationResources.getString("Auto_Range")); this.zoomResetBothMenuItem = new JMenuItem( localizationResources.getString("All_Axes")); this.zoomResetBothMenuItem.setActionCommand( ZOOM_RESET_BOTH_COMMAND); this.zoomResetBothMenuItem.addActionListener(this); autoRangeMenu.add(this.zoomResetBothMenuItem); autoRangeMenu.addSeparator(); this.zoomResetDomainMenuItem = new JMenuItem( localizationResources.getString("Domain_Axis")); this.zoomResetDomainMenuItem.setActionCommand( ZOOM_RESET_DOMAIN_COMMAND); this.zoomResetDomainMenuItem.addActionListener(this); autoRangeMenu.add(this.zoomResetDomainMenuItem); this.zoomResetRangeMenuItem = new JMenuItem( localizationResources.getString("Range_Axis")); this.zoomResetRangeMenuItem.setActionCommand( ZOOM_RESET_RANGE_COMMAND); this.zoomResetRangeMenuItem.addActionListener(this); autoRangeMenu.add(this.zoomResetRangeMenuItem); result.addSeparator(); result.add(autoRangeMenu); } return result; } /** * The idea is to modify the zooming options depending on the type of chart * being displayed by the panel. * * @param x horizontal position of the popup. * @param y vertical position of the popup. */ protected void displayPopupMenu(int x, int y) { if (this.popup == null) { return; } // go through each zoom menu item and decide whether or not to // enable it... boolean isDomainZoomable = false; boolean isRangeZoomable = false; Plot plot = (this.chart != null ? this.chart.getPlot() : null); if (plot instanceof Zoomable) { Zoomable z = (Zoomable) plot; isDomainZoomable = z.isDomainZoomable(); isRangeZoomable = z.isRangeZoomable(); } if (this.zoomInDomainMenuItem != null) { this.zoomInDomainMenuItem.setEnabled(isDomainZoomable); } if (this.zoomOutDomainMenuItem != null) { this.zoomOutDomainMenuItem.setEnabled(isDomainZoomable); } if (this.zoomResetDomainMenuItem != null) { this.zoomResetDomainMenuItem.setEnabled(isDomainZoomable); } if (this.zoomInRangeMenuItem != null) { this.zoomInRangeMenuItem.setEnabled(isRangeZoomable); } if (this.zoomOutRangeMenuItem != null) { this.zoomOutRangeMenuItem.setEnabled(isRangeZoomable); } if (this.zoomResetRangeMenuItem != null) { this.zoomResetRangeMenuItem.setEnabled(isRangeZoomable); } if (this.zoomInBothMenuItem != null) { this.zoomInBothMenuItem.setEnabled(isDomainZoomable && isRangeZoomable); } if (this.zoomOutBothMenuItem != null) { this.zoomOutBothMenuItem.setEnabled(isDomainZoomable && isRangeZoomable); } if (this.zoomResetBothMenuItem != null) { this.zoomResetBothMenuItem.setEnabled(isDomainZoomable && isRangeZoomable); } this.popup.show(this, x, y); } /** * Updates the UI for a LookAndFeel change. */ @Override public void updateUI() { // here we need to update the UI for the popup menu, if the panel // has one... if (this.popup != null) { SwingUtilities.updateComponentTreeUI(this.popup); } super.updateUI(); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.zoomFillPaint, stream); SerialUtils.writePaint(this.zoomOutlinePaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.zoomFillPaint = SerialUtils.readPaint(stream); this.zoomOutlinePaint = SerialUtils.readPaint(stream); // we create a new but empty chartMouseListeners list this.chartMouseListeners = new EventListenerList(); // register as a listener with sub-components... if (this.chart != null) { this.chart.addChangeListener(this); } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ChartRenderingInfo.java000066400000000000000000000203241463604235500274350ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * ChartRenderingInfo.java * ----------------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.entity.StandardEntityCollection; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; /** * A structure for storing rendering information from one call to the * JFreeChart.draw() method. *

* An instance of the {@link JFreeChart} class can draw itself within an * arbitrary rectangle on any {@code Graphics2D}. It is assumed that * client code will sometimes render the same chart in more than one view, so * the {@link JFreeChart} instance does not retain any information about its * rendered dimensions. This information can be useful sometimes, so you have * the option to collect the information at each call to * {@code JFreeChart.draw()}, by passing an instance of this * {@code ChartRenderingInfo} class. */ public class ChartRenderingInfo implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 2751952018173406822L; /** The area in which the chart is drawn. */ private transient Rectangle2D chartArea; /** Rendering info for the chart's plot (and subplots, if any). */ private PlotRenderingInfo plotInfo; /** * Storage for the chart entities. Since retaining entity information for * charts with a large number of data points consumes a lot of memory, it * is intended that you can set this to {@code null} to prevent the * information being collected. */ private EntityCollection entities; /** * Constructs a new ChartRenderingInfo structure that can be used to * collect information about the dimensions of a rendered chart. */ public ChartRenderingInfo() { this(new StandardEntityCollection()); } /** * Constructs a new instance. If an entity collection is supplied, it will * be populated with information about the entities in a chart. If it is * {@code null}, no entity information (including tool tips) will * be collected. * * @param entities an entity collection ({@code null} permitted). */ public ChartRenderingInfo(EntityCollection entities) { this.chartArea = new Rectangle2D.Double(); this.plotInfo = new PlotRenderingInfo(this); this.entities = entities; } /** * Returns the area in which the chart was drawn. * * @return The area in which the chart was drawn. * * @see #setChartArea(Rectangle2D) */ public Rectangle2D getChartArea() { return this.chartArea; } /** * Sets the area in which the chart was drawn. * * @param area the chart area. * * @see #getChartArea() */ public void setChartArea(Rectangle2D area) { this.chartArea.setRect(area); } /** * Returns the collection of entities maintained by this instance. * * @return The entity collection (possibly {@code null}). * * @see #setEntityCollection(EntityCollection) */ public EntityCollection getEntityCollection() { return this.entities; } /** * Sets the entity collection. * * @param entities the entity collection ({@code null} permitted). * * @see #getEntityCollection() */ public void setEntityCollection(EntityCollection entities) { this.entities = entities; } /** * Clears the information recorded by this object. */ public void clear() { this.chartArea.setRect(0.0, 0.0, 0.0, 0.0); this.plotInfo = new PlotRenderingInfo(this); if (this.entities != null) { this.entities.clear(); } } /** * Returns the rendering info for the chart's plot. * * @return The rendering info for the plot. */ public PlotRenderingInfo getPlotInfo() { return this.plotInfo; } /** * Tests this object for equality with an arbitrary object. * * @param obj the object to test against ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof ChartRenderingInfo)) { return false; } ChartRenderingInfo that = (ChartRenderingInfo) obj; if (!Objects.equals(this.chartArea, that.chartArea)) { return false; } if (!Objects.equals(this.plotInfo, that.plotInfo)) { return false; } if (!Objects.equals(this.entities, that.entities)) { return false; } return true; } @Override public int hashCode() { int hash = 7; hash = 37 * hash + Objects.hashCode(this.chartArea); hash = 37 * hash + Objects.hashCode(this.plotInfo); hash = 37 * hash + Objects.hashCode(this.entities); return hash; } /** * Returns a clone of this object. * * @return A clone. * * @throws CloneNotSupportedException if the object cannot be cloned. */ @Override public ChartRenderingInfo clone() throws CloneNotSupportedException { ChartRenderingInfo clone = (ChartRenderingInfo) super.clone(); if (this.chartArea != null) { clone.chartArea = (Rectangle2D) this.chartArea.clone(); } if (this.entities instanceof PublicCloneable) { PublicCloneable pc = (PublicCloneable) this.entities; clone.entities = (EntityCollection) pc.clone(); } return clone; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeShape(this.chartArea, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.chartArea = (Rectangle2D) SerialUtils.readShape(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ChartTheme.java000066400000000000000000000037771463604235500257630ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * ChartTheme.java * --------------- * (C) Copyright 2008-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart; /** * A {@link ChartTheme} a class that can apply a style or 'theme' to a chart. * It can be implemented in an arbitrary manner, with the styling applied to * the chart via the {@code apply(JFreeChart)} method. We provide one * implementation ({@link StandardChartTheme}) that just mimics the manual * process of calling methods to set various chart parameters. */ public interface ChartTheme { /** * Applies this theme to the supplied chart. * * @param chart the chart ({@code null} not permitted). */ void apply(JFreeChart chart); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ChartTransferable.java000066400000000000000000000204341463604235500273160ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * ChartTransferable.java * ---------------------- * (C) Copyright 2009-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart; import java.awt.Graphics2D; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.IOException; /** * A class used to represent a chart on the clipboard. */ public class ChartTransferable implements Transferable { /** The data flavor. */ final DataFlavor imageFlavor = new DataFlavor( "image/x-java-image; class=java.awt.Image", "Image"); /** The chart. */ private JFreeChart chart; /** The width of the chart on the clipboard. */ private final int width; /** The height of the chart on the clipboard. */ private final int height; /** * The smallest width at which the chart will be drawn (if necessary, the * chart will then be scaled down to fit the requested width). */ private final int minDrawWidth; /** * The smallest height at which the chart will be drawn (if necessary, the * chart will then be scaled down to fit the requested height). */ private final int minDrawHeight; /** * The largest width at which the chart will be drawn (if necessary, the * chart will then be scaled up to fit the requested width). */ private final int maxDrawWidth; /** * The largest height at which the chart will be drawn (if necessary, the * chart will then be scaled up to fit the requested height). */ private final int maxDrawHeight; /** * Creates a new chart selection. * * @param chart the chart. * @param width the chart width. * @param height the chart height. */ public ChartTransferable(JFreeChart chart, int width, int height) { this(chart, width, height, true); } /** * Creates a new chart selection. * * @param chart the chart. * @param width the chart width. * @param height the chart height. * @param cloneData clone the dataset(s)? */ public ChartTransferable(JFreeChart chart, int width, int height, boolean cloneData) { this(chart, width, height, 0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE, true); } /** * Creates a new chart selection. The minimum and maximum drawing * dimensions are used to match the scaling behaviour in the * {@link ChartPanel} class. * * @param chart the chart. * @param width the chart width. * @param height the chart height. * @param minDrawW the minimum drawing width. * @param minDrawH the minimum drawing height. * @param maxDrawW the maximum drawing width. * @param maxDrawH the maximum drawing height. * @param cloneData clone the dataset(s)? */ public ChartTransferable(JFreeChart chart, int width, int height, int minDrawW, int minDrawH, int maxDrawW, int maxDrawH, boolean cloneData) { // we clone the chart because presumably there can be some delay // between putting this instance on the system clipboard and // actually having the getTransferData() method called... try { this.chart = (JFreeChart) chart.clone(); } catch (CloneNotSupportedException e) { this.chart = chart; } // FIXME: we've cloned the chart, but the dataset(s) aren't cloned // and we should do that this.width = width; this.height = height; this.minDrawWidth = minDrawW; this.minDrawHeight = minDrawH; this.maxDrawWidth = maxDrawW; this.maxDrawHeight = maxDrawH; } /** * Returns the data flavors supported. * * @return The data flavors supported. */ @Override public DataFlavor[] getTransferDataFlavors() { return new DataFlavor[] {this.imageFlavor}; } /** * Returns {@code true} if the specified flavor is supported. * * @param flavor the flavor. * * @return A boolean. */ @Override public boolean isDataFlavorSupported(DataFlavor flavor) { return this.imageFlavor.equals(flavor); } /** * Returns the content for the requested flavor, if it is supported. * * @param flavor the requested flavor. * * @return The content. * * @throws java.awt.datatransfer.UnsupportedFlavorException if the flavor * is not supported. * @throws java.io.IOException if there is an IO problem. */ @Override public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { if (this.imageFlavor.equals(flavor)) { return createBufferedImage(this.chart, this.width, this.height, this.minDrawWidth, this.minDrawHeight, this.maxDrawWidth, this.maxDrawHeight); } else { throw new UnsupportedFlavorException(flavor); } } /** * A utility method that creates an image of a chart, with scaling. * * @param chart the chart. * @param w the image width. * @param h the image height. * @param minDrawW the minimum width for chart drawing. * @param minDrawH the minimum height for chart drawing. * @param maxDrawW the maximum width for chart drawing. * @param maxDrawH the maximum height for chart drawing. * * @return A chart image. */ private BufferedImage createBufferedImage(JFreeChart chart, int w, int h, int minDrawW, int minDrawH, int maxDrawW, int maxDrawH) { BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); // bug #182 Graphics2D g2 = image.createGraphics(); // work out if scaling is required... boolean scale = false; double drawWidth = w; double drawHeight = h; double scaleX = 1.0; double scaleY = 1.0; if (drawWidth < minDrawW) { scaleX = drawWidth / minDrawW; drawWidth = minDrawW; scale = true; } else if (drawWidth > maxDrawW) { scaleX = drawWidth / maxDrawW; drawWidth = maxDrawW; scale = true; } if (drawHeight < minDrawH) { scaleY = drawHeight / minDrawH; drawHeight = minDrawH; scale = true; } else if (drawHeight > maxDrawH) { scaleY = drawHeight / maxDrawH; drawHeight = maxDrawH; scale = true; } Rectangle2D chartArea = new Rectangle2D.Double(0.0, 0.0, drawWidth, drawHeight); if (scale) { AffineTransform st = AffineTransform.getScaleInstance(scaleX, scaleY); g2.transform(st); } chart.draw(g2, chartArea, null, null); g2.dispose(); return image; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ChartUtils.java000066400000000000000000000643161463604235500260150ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * ChartUtils.java * --------------- * (C) Copyright 2001-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Wolfgang Irler; * Richard Atkinson; * Xavier Poinsard; * */ package org.jfree.chart; import java.awt.Graphics2D; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import org.jfree.chart.encoders.EncoderUtil; import org.jfree.chart.encoders.ImageFormat; import org.jfree.chart.imagemap.ImageMapUtils; import org.jfree.chart.imagemap.OverLIBToolTipTagFragmentGenerator; import org.jfree.chart.imagemap.StandardToolTipTagFragmentGenerator; import org.jfree.chart.imagemap.StandardURLTagFragmentGenerator; import org.jfree.chart.imagemap.ToolTipTagFragmentGenerator; import org.jfree.chart.imagemap.URLTagFragmentGenerator; import org.jfree.chart.util.Args; /** * A collection of utility methods for JFreeChart. Includes methods for * converting charts to image formats (PNG and JPEG) plus creating simple HTML * image maps. * * @see ImageMapUtils */ public abstract class ChartUtils { /** * Applies the current theme to the specified chart. This method is * provided for convenience, the theme itself is stored in the * {@link ChartFactory} class. * * @param chart the chart ({@code null} not permitted). */ public static void applyCurrentTheme(JFreeChart chart) { ChartFactory.getChartTheme().apply(chart); } /** * Writes a chart to an output stream in PNG format. * * @param out the output stream ({@code null} not permitted). * @param chart the chart ({@code null} not permitted). * @param width the image width. * @param height the image height. * * @throws IOException if there are any I/O errors. */ public static void writeChartAsPNG(OutputStream out, JFreeChart chart, int width, int height) throws IOException { // defer argument checking... writeChartAsPNG(out, chart, width, height, null); } /** * Writes a chart to an output stream in PNG format. * * @param out the output stream ({@code null} not permitted). * @param chart the chart ({@code null} not permitted). * @param width the image width. * @param height the image height. * @param encodeAlpha encode alpha? * @param compression the compression level (0-9). * * @throws IOException if there are any I/O errors. */ public static void writeChartAsPNG(OutputStream out, JFreeChart chart, int width, int height, boolean encodeAlpha, int compression) throws IOException { // defer argument checking... ChartUtils.writeChartAsPNG(out, chart, width, height, null, encodeAlpha, compression); } /** * Writes a chart to an output stream in PNG format. This method allows * you to pass in a {@link ChartRenderingInfo} object, to collect * information about the chart dimensions/entities. You will need this * info if you want to create an HTML image map. * * @param out the output stream ({@code null} not permitted). * @param chart the chart ({@code null} not permitted). * @param width the image width. * @param height the image height. * @param info the chart rendering info ({@code null} permitted). * * @throws IOException if there are any I/O errors. */ public static void writeChartAsPNG(OutputStream out, JFreeChart chart, int width, int height, ChartRenderingInfo info) throws IOException { Args.nullNotPermitted(chart, "chart"); BufferedImage bufferedImage = chart.createBufferedImage(width, height, info); EncoderUtil.writeBufferedImage(bufferedImage, ImageFormat.PNG, out); } /** * Writes a chart to an output stream in PNG format. This method allows * you to pass in a {@link ChartRenderingInfo} object, to collect * information about the chart dimensions/entities. You will need this * info if you want to create an HTML image map. * * @param out the output stream ({@code null} not permitted). * @param chart the chart ({@code null} not permitted). * @param width the image width. * @param height the image height. * @param info carries back chart rendering info ({@code null} * permitted). * @param encodeAlpha encode alpha? * @param compression the PNG compression level (0-9). * * @throws IOException if there are any I/O errors. */ public static void writeChartAsPNG(OutputStream out, JFreeChart chart, int width, int height, ChartRenderingInfo info, boolean encodeAlpha, int compression) throws IOException { Args.nullNotPermitted(out, "out"); Args.nullNotPermitted(chart, "chart"); BufferedImage chartImage = chart.createBufferedImage(width, height, BufferedImage.TYPE_INT_ARGB, info); ChartUtils.writeBufferedImageAsPNG(out, chartImage, encodeAlpha, compression); } /** * Writes a scaled version of a chart to an output stream in PNG format. * * @param out the output stream ({@code null} not permitted). * @param chart the chart ({@code null} not permitted). * @param width the unscaled chart width. * @param height the unscaled chart height. * @param widthScaleFactor the horizontal scale factor. * @param heightScaleFactor the vertical scale factor. * * @throws IOException if there are any I/O problems. */ public static void writeScaledChartAsPNG(OutputStream out, JFreeChart chart, int width, int height, int widthScaleFactor, int heightScaleFactor) throws IOException { Args.nullNotPermitted(out, "out"); Args.nullNotPermitted(chart, "chart"); double desiredWidth = width * widthScaleFactor; double desiredHeight = height * heightScaleFactor; double defaultWidth = width; double defaultHeight = height; boolean scale = false; // get desired width and height from somewhere then... if ((widthScaleFactor != 1) || (heightScaleFactor != 1)) { scale = true; } double scaleX = desiredWidth / defaultWidth; double scaleY = desiredHeight / defaultHeight; BufferedImage image = new BufferedImage((int) desiredWidth, (int) desiredHeight, BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = image.createGraphics(); if (scale) { AffineTransform saved = g2.getTransform(); g2.transform(AffineTransform.getScaleInstance(scaleX, scaleY)); chart.draw(g2, new Rectangle2D.Double(0, 0, defaultWidth, defaultHeight), null, null); g2.setTransform(saved); g2.dispose(); } else { chart.draw(g2, new Rectangle2D.Double(0, 0, defaultWidth, defaultHeight), null, null); } out.write(encodeAsPNG(image)); } /** * Saves a chart to the specified file in PNG format. * * @param file the file name ({@code null} not permitted). * @param chart the chart ({@code null} not permitted). * @param width the image width. * @param height the image height. * * @throws IOException if there are any I/O errors. */ public static void saveChartAsPNG(File file, JFreeChart chart, int width, int height) throws IOException { // defer argument checking... saveChartAsPNG(file, chart, width, height, null); } /** * Saves a chart to a file in PNG format. This method allows you to pass * in a {@link ChartRenderingInfo} object, to collect information about the * chart dimensions/entities. You will need this info if you want to * create an HTML image map. * * @param file the file ({@code null} not permitted). * @param chart the chart ({@code null} not permitted). * @param width the image width. * @param height the image height. * @param info the chart rendering info ({@code null} permitted). * * @throws IOException if there are any I/O errors. */ public static void saveChartAsPNG(File file, JFreeChart chart, int width, int height, ChartRenderingInfo info) throws IOException { Args.nullNotPermitted(file, "file"); OutputStream out = new BufferedOutputStream(new FileOutputStream(file)); try { ChartUtils.writeChartAsPNG(out, chart, width, height, info); } finally { out.close(); } } /** * Saves a chart to a file in PNG format. This method allows you to pass * in a {@link ChartRenderingInfo} object, to collect information about the * chart dimensions/entities. You will need this info if you want to * create an HTML image map. * * @param file the file ({@code null} not permitted). * @param chart the chart ({@code null} not permitted). * @param width the image width. * @param height the image height. * @param info the chart rendering info ({@code null} permitted). * @param encodeAlpha encode alpha? * @param compression the PNG compression level (0-9). * * @throws IOException if there are any I/O errors. */ public static void saveChartAsPNG(File file, JFreeChart chart, int width, int height, ChartRenderingInfo info, boolean encodeAlpha, int compression) throws IOException { Args.nullNotPermitted(file, "file"); Args.nullNotPermitted(chart, "chart"); OutputStream out = new BufferedOutputStream(new FileOutputStream(file)); try { writeChartAsPNG(out, chart, width, height, info, encodeAlpha, compression); } finally { out.close(); } } /** * Writes a chart to an output stream in JPEG format. Please note that * JPEG is a poor format for chart images, use PNG if possible. * * @param out the output stream ({@code null} not permitted). * @param chart the chart ({@code null} not permitted). * @param width the image width. * @param height the image height. * * @throws IOException if there are any I/O errors. */ public static void writeChartAsJPEG(OutputStream out, JFreeChart chart, int width, int height) throws IOException { // defer argument checking... writeChartAsJPEG(out, chart, width, height, null); } /** * Writes a chart to an output stream in JPEG format. Please note that * JPEG is a poor format for chart images, use PNG if possible. * * @param out the output stream ({@code null} not permitted). * @param quality the quality setting. * @param chart the chart ({@code null} not permitted). * @param width the image width. * @param height the image height. * * @throws IOException if there are any I/O errors. */ public static void writeChartAsJPEG(OutputStream out, float quality, JFreeChart chart, int width, int height) throws IOException { // defer argument checking... ChartUtils.writeChartAsJPEG(out, quality, chart, width, height, null); } /** * Writes a chart to an output stream in JPEG format. This method allows * you to pass in a {@link ChartRenderingInfo} object, to collect * information about the chart dimensions/entities. You will need this * info if you want to create an HTML image map. * * @param out the output stream ({@code null} not permitted). * @param chart the chart ({@code null} not permitted). * @param width the image width. * @param height the image height. * @param info the chart rendering info ({@code null} permitted). * * @throws IOException if there are any I/O errors. */ public static void writeChartAsJPEG(OutputStream out, JFreeChart chart, int width, int height, ChartRenderingInfo info) throws IOException { Args.nullNotPermitted(out, "out"); Args.nullNotPermitted(chart, "chart"); BufferedImage image = chart.createBufferedImage(width, height, BufferedImage.TYPE_INT_RGB, info); EncoderUtil.writeBufferedImage(image, ImageFormat.JPEG, out); } /** * Writes a chart to an output stream in JPEG format. This method allows * you to pass in a {@link ChartRenderingInfo} object, to collect * information about the chart dimensions/entities. You will need this * info if you want to create an HTML image map. * * @param out the output stream ({@code null} not permitted). * @param quality the output quality (0.0f to 1.0f). * @param chart the chart ({@code null} not permitted). * @param width the image width. * @param height the image height. * @param info the chart rendering info ({@code null} permitted). * * @throws IOException if there are any I/O errors. */ public static void writeChartAsJPEG(OutputStream out, float quality, JFreeChart chart, int width, int height, ChartRenderingInfo info) throws IOException { Args.nullNotPermitted(out, "out"); Args.nullNotPermitted(chart, "chart"); BufferedImage image = chart.createBufferedImage(width, height, BufferedImage.TYPE_INT_RGB, info); EncoderUtil.writeBufferedImage(image, ImageFormat.JPEG, out, quality); } /** * Saves a chart to a file in JPEG format. * * @param file the file ({@code null} not permitted). * @param chart the chart ({@code null} not permitted). * @param width the image width. * @param height the image height. * * @throws IOException if there are any I/O errors. */ public static void saveChartAsJPEG(File file, JFreeChart chart, int width, int height) throws IOException { // defer argument checking... saveChartAsJPEG(file, chart, width, height, null); } /** * Saves a chart to a file in JPEG format. * * @param file the file ({@code null} not permitted). * @param quality the JPEG quality setting. * @param chart the chart ({@code null} not permitted). * @param width the image width. * @param height the image height. * * @throws IOException if there are any I/O errors. */ public static void saveChartAsJPEG(File file, float quality, JFreeChart chart, int width, int height) throws IOException { // defer argument checking... saveChartAsJPEG(file, quality, chart, width, height, null); } /** * Saves a chart to a file in JPEG format. This method allows you to pass * in a {@link ChartRenderingInfo} object, to collect information about the * chart dimensions/entities. You will need this info if you want to * create an HTML image map. * * @param file the file name ({@code null} not permitted). * @param chart the chart ({@code null} not permitted). * @param width the image width. * @param height the image height. * @param info the chart rendering info ({@code null} permitted). * * @throws IOException if there are any I/O errors. */ public static void saveChartAsJPEG(File file, JFreeChart chart, int width, int height, ChartRenderingInfo info) throws IOException { Args.nullNotPermitted(file, "file"); Args.nullNotPermitted(chart, "chart"); OutputStream out = new BufferedOutputStream(new FileOutputStream(file)); try { writeChartAsJPEG(out, chart, width, height, info); } finally { out.close(); } } /** * Saves a chart to a file in JPEG format. This method allows you to pass * in a {@link ChartRenderingInfo} object, to collect information about the * chart dimensions/entities. You will need this info if you want to * create an HTML image map. * * @param file the file name ({@code null} not permitted). * @param quality the quality setting. * @param chart the chart ({@code null} not permitted). * @param width the image width. * @param height the image height. * @param info the chart rendering info ({@code null} permitted). * * @throws IOException if there are any I/O errors. */ public static void saveChartAsJPEG(File file, float quality, JFreeChart chart, int width, int height, ChartRenderingInfo info) throws IOException { Args.nullNotPermitted(file, "file"); Args.nullNotPermitted(chart, "chart"); OutputStream out = new BufferedOutputStream(new FileOutputStream( file)); try { writeChartAsJPEG(out, quality, chart, width, height, info); } finally { out.close(); } } /** * Writes a {@link BufferedImage} to an output stream in JPEG format. * * @param out the output stream ({@code null} not permitted). * @param image the image ({@code null} not permitted). * * @throws IOException if there are any I/O errors. */ public static void writeBufferedImageAsJPEG(OutputStream out, BufferedImage image) throws IOException { // defer argument checking... writeBufferedImageAsJPEG(out, 0.75f, image); } /** * Writes a {@link BufferedImage} to an output stream in JPEG format. * * @param out the output stream ({@code null} not permitted). * @param quality the image quality (0.0f to 1.0f). * @param image the image ({@code null} not permitted). * * @throws IOException if there are any I/O errors. */ public static void writeBufferedImageAsJPEG(OutputStream out, float quality, BufferedImage image) throws IOException { EncoderUtil.writeBufferedImage(image, ImageFormat.JPEG, out, quality); } /** * Writes a {@link BufferedImage} to an output stream in PNG format. * * @param out the output stream ({@code null} not permitted). * @param image the image ({@code null} not permitted). * * @throws IOException if there are any I/O errors. */ public static void writeBufferedImageAsPNG(OutputStream out, BufferedImage image) throws IOException { EncoderUtil.writeBufferedImage(image, ImageFormat.PNG, out); } /** * Writes a {@link BufferedImage} to an output stream in PNG format. * * @param out the output stream ({@code null} not permitted). * @param image the image ({@code null} not permitted). * @param encodeAlpha encode alpha? * @param compression the compression level (0-9). * * @throws IOException if there are any I/O errors. */ public static void writeBufferedImageAsPNG(OutputStream out, BufferedImage image, boolean encodeAlpha, int compression) throws IOException { EncoderUtil.writeBufferedImage(image, ImageFormat.PNG, out, compression, encodeAlpha); } /** * Encodes a {@link BufferedImage} to PNG format. * * @param image the image ({@code null} not permitted). * * @return A byte array in PNG format. * * @throws IOException if there is an I/O problem. */ public static byte[] encodeAsPNG(BufferedImage image) throws IOException { return EncoderUtil.encode(image, ImageFormat.PNG); } /** * Encodes a {@link BufferedImage} to PNG format. * * @param image the image ({@code null} not permitted). * @param encodeAlpha encode alpha? * @param compression the PNG compression level (0-9). * * @return The byte array in PNG format. * * @throws IOException if there is an I/O problem. */ public static byte[] encodeAsPNG(BufferedImage image, boolean encodeAlpha, int compression) throws IOException { return EncoderUtil.encode(image, ImageFormat.PNG, compression, encodeAlpha); } /** * Writes an image map to an output stream. * * @param writer the writer ({@code null} not permitted). * @param name the map name ({@code null} not permitted). * @param info the chart rendering info ({@code null} not permitted). * @param useOverLibForToolTips whether to use OverLIB for tooltips * (http://www.bosrup.com/web/overlib/). * * @throws IOException if there are any I/O errors. */ public static void writeImageMap(PrintWriter writer, String name, ChartRenderingInfo info, boolean useOverLibForToolTips) throws IOException { ToolTipTagFragmentGenerator toolTipTagFragmentGenerator; if (useOverLibForToolTips) { toolTipTagFragmentGenerator = new OverLIBToolTipTagFragmentGenerator(); } else { toolTipTagFragmentGenerator = new StandardToolTipTagFragmentGenerator(); } ImageMapUtils.writeImageMap(writer, name, info, toolTipTagFragmentGenerator, new StandardURLTagFragmentGenerator()); } /** * Writes an image map to the specified writer. * * @param writer the writer ({@code null} not permitted). * @param name the map name ({@code null} not permitted). * @param info the chart rendering info ({@code null} not permitted). * @param toolTipTagFragmentGenerator a generator for the HTML fragment * that will contain the tooltip text ({@code null} not permitted * if {@code info} contains tooltip information). * @param urlTagFragmentGenerator a generator for the HTML fragment that * will contain the URL reference ({@code null} not permitted if * {@code info} contains URLs). * * @throws IOException if there are any I/O errors. */ public static void writeImageMap(PrintWriter writer, String name, ChartRenderingInfo info, ToolTipTagFragmentGenerator toolTipTagFragmentGenerator, URLTagFragmentGenerator urlTagFragmentGenerator) throws IOException { writer.println(ImageMapUtils.getImageMap(name, info, toolTipTagFragmentGenerator, urlTagFragmentGenerator)); } /** * Creates an HTML image map. This method maps to * {@link ImageMapUtils#getImageMap(String, ChartRenderingInfo, * ToolTipTagFragmentGenerator, URLTagFragmentGenerator)}, using default * generators. * * @param name the map name ({@code null} not permitted). * @param info the chart rendering info ({@code null} not permitted). * * @return The map tag. */ public static String getImageMap(String name, ChartRenderingInfo info) { return ImageMapUtils.getImageMap(name, info, new StandardToolTipTagFragmentGenerator(), new StandardURLTagFragmentGenerator()); } /** * Creates an HTML image map. This method maps directly to * {@link ImageMapUtils#getImageMap(String, ChartRenderingInfo, * ToolTipTagFragmentGenerator, URLTagFragmentGenerator)}. * * @param name the map name ({@code null} not permitted). * @param info the chart rendering info ({@code null} not permitted). * @param toolTipTagFragmentGenerator a generator for the HTML fragment * that will contain the tooltip text ({@code null} not permitted * if {@code info} contains tooltip information). * @param urlTagFragmentGenerator a generator for the HTML fragment that * will contain the URL reference ({@code null} not permitted if * {@code info} contains URLs). * * @return The map tag. */ public static String getImageMap(String name, ChartRenderingInfo info, ToolTipTagFragmentGenerator toolTipTagFragmentGenerator, URLTagFragmentGenerator urlTagFragmentGenerator) { return ImageMapUtils.getImageMap(name, info, toolTipTagFragmentGenerator, urlTagFragmentGenerator); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/HashUtils.java000066400000000000000000000240141463604235500256260ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * HashUtils.java * -------------- * (C) Copyright 2006-present, by David Gilbert; * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart; import java.awt.GradientPaint; import java.awt.Paint; import java.awt.Stroke; import org.jfree.chart.util.BooleanList; import org.jfree.chart.util.PaintList; import org.jfree.chart.util.StrokeList; /** * Some utility methods for calculating hash codes. */ public class HashUtils { /** * Returns a hash code for a {@code Paint} instance. If * {@code p} is {@code null}, this method returns zero. * * @param p the paint ({@code null} permitted). * * @return The hash code. */ public static int hashCodeForPaint(Paint p) { if (p == null) { return 0; } int result; // handle GradientPaint as a special case if (p instanceof GradientPaint) { GradientPaint gp = (GradientPaint) p; result = 193; result = 37 * result + gp.getColor1().hashCode(); result = 37 * result + gp.getPoint1().hashCode(); result = 37 * result + gp.getColor2().hashCode(); result = 37 * result + gp.getPoint2().hashCode(); } else { // we assume that all other Paint instances implement equals() and // hashCode()...of course that might not be true, but what can we // do about it? result = p.hashCode(); } return result; } /** * Returns a hash code for a {@code double[]} instance. If the array * is {@code null}, this method returns zero. * * @param a the array ({@code null} permitted). * * @return The hash code. */ public static int hashCodeForDoubleArray(double[] a) { if (a == null) { return 0; } int result = 193; long temp; for (int i = 0; i < a.length; i++) { temp = Double.doubleToLongBits(a[i]); result = 29 * result + (int) (temp ^ (temp >>> 32)); } return result; } /** * Returns a hash value based on a seed value and the value of a boolean * primitive. * * @param pre the seed value. * @param b the boolean value. * * @return A hash value. */ public static int hashCode(int pre, boolean b) { return 37 * pre + (b ? 0 : 1); } /** * Returns a hash value based on a seed value and the value of an int * primitive. * * @param pre the seed value. * @param i the int value. * * @return A hash value. */ public static int hashCode(int pre, int i) { return 37 * pre + i; } /** * Returns a hash value based on a seed value and the value of a double * primitive. * * @param pre the seed value. * @param d the double value. * * @return A hash value. */ public static int hashCode(int pre, double d) { long l = Double.doubleToLongBits(d); return 37 * pre + (int) (l ^ (l >>> 32)); } /** * Returns a hash value based on a seed value and a paint instance. * * @param pre the seed value. * @param p the paint ({@code null} permitted). * * @return A hash value. */ public static int hashCode(int pre, Paint p) { return 37 * pre + hashCodeForPaint(p); } /** * Returns a hash value based on a seed value and a stroke instance. * * @param pre the seed value. * @param s the stroke ({@code null} permitted). * * @return A hash value. */ public static int hashCode(int pre, Stroke s) { int h = (s != null ? s.hashCode() : 0); return 37 * pre + h; } /** * Returns a hash value based on a seed value and a string instance. * * @param pre the seed value. * @param s the string ({@code null} permitted). * * @return A hash value. */ public static int hashCode(int pre, String s) { int h = (s != null ? s.hashCode() : 0); return 37 * pre + h; } /** * Returns a hash value based on a seed value and a {@code Comparable} * instance. * * @param pre the seed value. * @param c the comparable ({@code null} permitted). * * @return A hash value. */ public static int hashCode(int pre, Comparable c) { int h = (c != null ? c.hashCode() : 0); return 37 * pre + h; } /** * Returns a hash value based on a seed value and an {@code Object} * instance. * * @param pre the seed value. * @param obj the object ({@code null} permitted). * * @return A hash value. */ public static int hashCode(int pre, Object obj) { int h = (obj != null ? obj.hashCode() : 0); return 37 * pre + h; } /** * Computes a hash code for a {@link BooleanList}. In the latest version * of JCommon, the {@link BooleanList} class should implement the hashCode() * method correctly, but we compute it here anyway so that we can work with * older versions of JCommon (back to 1.0.0). * * @param pre the seed value. * @param list the list ({@code null} permitted). * * @return The hash code. */ public static int hashCode(int pre, BooleanList list) { if (list == null) { return pre; } int result = 127; int size = list.size(); result = HashUtils.hashCode(result, size); // for efficiency, we just use the first, last and middle items to // compute a hashCode... if (size > 0) { result = HashUtils.hashCode(result, list.getBoolean(0)); if (size > 1) { result = HashUtils.hashCode(result, list.getBoolean(size - 1)); if (size > 2) { result = HashUtils.hashCode(result, list.getBoolean(size / 2)); } } } return 37 * pre + result; } /** * Computes a hash code for a {@link PaintList}. In the latest version * of JCommon, the {@link PaintList} class should implement the hashCode() * method correctly, but we compute it here anyway so that we can work with * older versions of JCommon (back to 1.0.0). * * @param pre the seed value. * @param list the list ({@code null} permitted). * * @return The hash code. */ public static int hashCode(int pre, PaintList list) { if (list == null) { return pre; } int result = 127; int size = list.size(); result = HashUtils.hashCode(result, size); // for efficiency, we just use the first, last and middle items to // compute a hashCode... if (size > 0) { result = HashUtils.hashCode(result, list.getPaint(0)); if (size > 1) { result = HashUtils.hashCode(result, list.getPaint(size - 1)); if (size > 2) { result = HashUtils.hashCode(result, list.getPaint(size / 2)); } } } return 37 * pre + result; } /** * Computes a hash code for a {@link StrokeList}. In the latest version * of JCommon, the {@link StrokeList} class should implement the hashCode() * method correctly, but we compute it here anyway so that we can work with * older versions of JCommon (back to 1.0.0). * * @param pre the seed value. * @param list the list ({@code null} permitted). * * @return The hash code. */ public static int hashCode(int pre, StrokeList list) { if (list == null) { return pre; } int result = 127; int size = list.size(); result = HashUtils.hashCode(result, size); // for efficiency, we just use the first, last and middle items to // compute a hashCode... if (size > 0) { result = HashUtils.hashCode(result, list.getStroke(0)); if (size > 1) { result = HashUtils.hashCode(result, list.getStroke(size - 1)); if (size > 2) { result = HashUtils.hashCode(result, list.getStroke(size / 2)); } } } return 37 * pre + result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/JFreeChart.java000066400000000000000000001610551463604235500257060ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * JFreeChart.java * --------------- * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Andrzej Porebski; * David Li; * Wolfgang Irler; * Christian W. Zuckschwerdt; * Klaus Rheinwald; * Nicolas Brodu; * Peter Kolb (patch 2603321); * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * * NOTE: The above list of contributors lists only the people that have * contributed to this source file (JFreeChart.java) - for a list of ALL * contributors to the project, please see the README.md file. * */ package org.jfree.chart; import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Composite; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Paint; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; import javax.swing.UIManager; import javax.swing.event.EventListenerList; import org.jfree.chart.block.BlockParams; import org.jfree.chart.block.EntityBlockResult; import org.jfree.chart.block.LengthConstraintType; import org.jfree.chart.block.RectangleConstraint; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.entity.JFreeChartEntity; import org.jfree.chart.event.ChartChangeEvent; import org.jfree.chart.event.ChartChangeListener; import org.jfree.chart.event.ChartProgressEvent; import org.jfree.chart.event.ChartProgressListener; import org.jfree.chart.event.PlotChangeEvent; import org.jfree.chart.event.PlotChangeListener; import org.jfree.chart.event.TitleChangeEvent; import org.jfree.chart.event.TitleChangeListener; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.title.LegendTitle; import org.jfree.chart.title.TextTitle; import org.jfree.chart.title.Title; import org.jfree.chart.ui.Align; import org.jfree.chart.ui.Drawable; import org.jfree.chart.ui.HorizontalAlignment; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.ui.Size2D; import org.jfree.chart.ui.VerticalAlignment; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.SerialUtils; import org.jfree.data.Range; /** * A chart class implemented using the Java 2D APIs. The current version * supports bar charts, line charts, pie charts and xy plots (including time * series data). *

* JFreeChart coordinates several objects to achieve its aim of being able to * draw a chart on a Java 2D graphics device: a list of {@link Title} objects * (which often includes the chart's legend), a {@link Plot} and a * {@link org.jfree.data.general.Dataset} (the plot in turn manages a * domain axis and a range axis). *

* You should use a {@link ChartPanel} to display a chart in a GUI. *

* The {@link ChartFactory} class contains static methods for creating * 'ready-made' charts. * * @see ChartPanel * @see ChartFactory * @see Title * @see Plot */ public class JFreeChart implements Drawable, TitleChangeListener, PlotChangeListener, Serializable, Cloneable { /** For serialization. */ private static final long serialVersionUID = -3470703747817429120L; /** The default font for titles. */ public static final Font DEFAULT_TITLE_FONT = new Font("SansSerif", Font.BOLD, 18); /** The default background color. */ public static final Paint DEFAULT_BACKGROUND_PAINT = UIManager.getColor("Panel.background"); /** The default background image. */ public static final Image DEFAULT_BACKGROUND_IMAGE = null; /** The default background image alignment. */ public static final int DEFAULT_BACKGROUND_IMAGE_ALIGNMENT = Align.FIT; /** The default background image alpha. */ public static final float DEFAULT_BACKGROUND_IMAGE_ALPHA = 0.5f; /** * The key for a rendering hint that can suppress the generation of a * shadow effect when drawing the chart. The hint value must be a * Boolean. */ public static final RenderingHints.Key KEY_SUPPRESS_SHADOW_GENERATION = new RenderingHints.Key(0) { @Override public boolean isCompatibleValue(Object val) { return val instanceof Boolean; } }; /** * Rendering hints that will be used for chart drawing. This should never * be {@code null}. */ private transient RenderingHints renderingHints; /** The chart id (optional, will be used by JFreeSVG export). */ private String id; /** A flag that controls whether or not the chart border is drawn. */ private boolean borderVisible; /** The stroke used to draw the chart border (if visible). */ private transient Stroke borderStroke; /** The paint used to draw the chart border (if visible). */ private transient Paint borderPaint; /** The padding between the chart border and the chart drawing area. */ private RectangleInsets padding; /** The chart title (optional). */ private TextTitle title; /** * The chart subtitles (zero, one or many). This field should never be * {@code null}. */ private List subtitles; /** Draws the visual representation of the data. */ private Plot plot; /** Paint used to draw the background of the chart. */ private transient Paint backgroundPaint; /** An optional background image for the chart. */ private transient Image backgroundImage; // todo: not serialized yet /** The alignment for the background image. */ private int backgroundImageAlignment = Align.FIT; /** The alpha transparency for the background image. */ private float backgroundImageAlpha = 0.5f; /** Storage for registered change listeners. */ private transient EventListenerList changeListeners; /** Storage for registered progress listeners. */ private transient EventListenerList progressListeners; /** * A flag that can be used to enable/disable notification of chart change * events. */ private boolean notify; /** * A flag that controls whether or not rendering hints that identify * chart element should be added during rendering. This defaults to false * and it should only be enabled if the output target will use the hints. * JFreeSVG is one output target that supports these hints. */ private boolean elementHinting; /** * Creates a new chart based on the supplied plot. The chart will have * a legend added automatically, but no title (although you can easily add * one later). *

* Note that the {@link ChartFactory} class contains a range * of static methods that will return ready-made charts, and often this * is a more convenient way to create charts than using this constructor. * * @param plot the plot ({@code null} not permitted). */ public JFreeChart(Plot plot) { this(null, null, plot, true); } /** * Creates a new chart with the given title and plot. A default font * ({@link #DEFAULT_TITLE_FONT}) is used for the title, and the chart will * have a legend added automatically. *

* Note that the {@link ChartFactory} class contains a range * of static methods that will return ready-made charts, and often this * is a more convenient way to create charts than using this constructor. * * @param title the chart title ({@code null} permitted). * @param plot the plot ({@code null} not permitted). */ public JFreeChart(String title, Plot plot) { this(title, JFreeChart.DEFAULT_TITLE_FONT, plot, true); } /** * Creates a new chart with the given title and plot. The * {@code createLegend} argument specifies whether or not a legend * should be added to the chart. *

* Note that the {@link ChartFactory} class contains a range * of static methods that will return ready-made charts, and often this * is a more convenient way to create charts than using this constructor. * * @param title the chart title ({@code null} permitted). * @param titleFont the font for displaying the chart title * ({@code null} permitted). * @param plot controller of the visual representation of the data * ({@code null} not permitted). * @param createLegend a flag indicating whether or not a legend should * be created for the chart. */ public JFreeChart(String title, Font titleFont, Plot plot, boolean createLegend) { Args.nullNotPermitted(plot, "plot"); this.id = null; plot.setChart(this); // create storage for listeners... this.progressListeners = new EventListenerList(); this.changeListeners = new EventListenerList(); this.notify = true; // default is to notify listeners when the // chart changes this.renderingHints = new RenderingHints( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // added the following hint because of // http://stackoverflow.com/questions/7785082/ this.renderingHints.put(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); this.borderVisible = false; this.borderStroke = new BasicStroke(1.0f); this.borderPaint = Color.BLACK; this.padding = RectangleInsets.ZERO_INSETS; this.plot = plot; plot.addChangeListener(this); this.subtitles = new ArrayList(); // create a legend, if requested... if (createLegend) { LegendTitle legend = new LegendTitle(this.plot); legend.setMargin(new RectangleInsets(1.0, 1.0, 1.0, 1.0)); legend.setBackgroundPaint(Color.WHITE); legend.setPosition(RectangleEdge.BOTTOM); this.subtitles.add(legend); legend.addChangeListener(this); } // add the chart title, if one has been specified... if (title != null) { if (titleFont == null) { titleFont = DEFAULT_TITLE_FONT; } this.title = new TextTitle(title, titleFont); this.title.addChangeListener(this); } this.backgroundPaint = DEFAULT_BACKGROUND_PAINT; this.backgroundImage = DEFAULT_BACKGROUND_IMAGE; this.backgroundImageAlignment = DEFAULT_BACKGROUND_IMAGE_ALIGNMENT; this.backgroundImageAlpha = DEFAULT_BACKGROUND_IMAGE_ALPHA; } /** * Returns the ID for the chart. * * @return The ID for the chart (possibly {@code null}). */ public String getID() { return this.id; } /** * Sets the ID for the chart. * * @param id the id ({@code null} permitted). */ public void setID(String id) { this.id = id; } /** * Returns the flag that controls whether or not rendering hints * ({@link ChartHints#KEY_BEGIN_ELEMENT} and * {@link ChartHints#KEY_END_ELEMENT}) that identify chart elements are * added during rendering. The default value is {@code false}. * * @return A boolean. * * @see #setElementHinting(boolean) */ public boolean getElementHinting() { return this.elementHinting; } /** * Sets the flag that controls whether or not rendering hints * ({@link ChartHints#KEY_BEGIN_ELEMENT} and * {@link ChartHints#KEY_END_ELEMENT}) that identify chart elements are * added during rendering. * * @param hinting the new flag value. * * @see #getElementHinting() */ public void setElementHinting(boolean hinting) { this.elementHinting = hinting; } /** * Returns the collection of rendering hints for the chart. * * @return The rendering hints for the chart (never {@code null}). * * @see #setRenderingHints(RenderingHints) */ public RenderingHints getRenderingHints() { return this.renderingHints; } /** * Sets the rendering hints for the chart. These will be added (using the * {@code Graphics2D.addRenderingHints()} method) near the start of the * {@code JFreeChart.draw()} method. * * @param renderingHints the rendering hints ({@code null} not permitted). * * @see #getRenderingHints() */ public void setRenderingHints(RenderingHints renderingHints) { Args.nullNotPermitted(renderingHints, "renderingHints"); this.renderingHints = renderingHints; fireChartChanged(); } /** * Returns a flag that controls whether or not a border is drawn around the * outside of the chart. * * @return A boolean. * * @see #setBorderVisible(boolean) */ public boolean isBorderVisible() { return this.borderVisible; } /** * Sets a flag that controls whether or not a border is drawn around the * outside of the chart. * * @param visible the flag. * * @see #isBorderVisible() */ public void setBorderVisible(boolean visible) { this.borderVisible = visible; fireChartChanged(); } /** * Returns the stroke used to draw the chart border (if visible). * * @return The border stroke. * * @see #setBorderStroke(Stroke) */ public Stroke getBorderStroke() { return this.borderStroke; } /** * Sets the stroke used to draw the chart border (if visible). * * @param stroke the stroke. * * @see #getBorderStroke() */ public void setBorderStroke(Stroke stroke) { this.borderStroke = stroke; fireChartChanged(); } /** * Returns the paint used to draw the chart border (if visible). * * @return The border paint. * * @see #setBorderPaint(Paint) */ public Paint getBorderPaint() { return this.borderPaint; } /** * Sets the paint used to draw the chart border (if visible). * * @param paint the paint. * * @see #getBorderPaint() */ public void setBorderPaint(Paint paint) { this.borderPaint = paint; fireChartChanged(); } /** * Returns the padding between the chart border and the chart drawing area. * * @return The padding (never {@code null}). * * @see #setPadding(RectangleInsets) */ public RectangleInsets getPadding() { return this.padding; } /** * Sets the padding between the chart border and the chart drawing area, * and sends a {@link ChartChangeEvent} to all registered listeners. * * @param padding the padding ({@code null} not permitted). * * @see #getPadding() */ public void setPadding(RectangleInsets padding) { Args.nullNotPermitted(padding, "padding"); this.padding = padding; notifyListeners(new ChartChangeEvent(this)); } /** * Returns the main chart title. Very often a chart will have just one * title, so we make this case simple by providing accessor methods for * the main title. However, multiple titles are supported - see the * {@link #addSubtitle(Title)} method. * * @return The chart title (possibly {@code null}). * * @see #setTitle(TextTitle) */ public TextTitle getTitle() { return this.title; } /** * Sets the main title for the chart and sends a {@link ChartChangeEvent} * to all registered listeners. If you do not want a title for the * chart, set it to {@code null}. If you want more than one title on * a chart, use the {@link #addSubtitle(Title)} method. * * @param title the title ({@code null} permitted). * * @see #getTitle() */ public void setTitle(TextTitle title) { if (this.title != null) { this.title.removeChangeListener(this); } this.title = title; if (title != null) { title.addChangeListener(this); } fireChartChanged(); } /** * Sets the chart title and sends a {@link ChartChangeEvent} to all * registered listeners. This is a convenience method that ends up calling * the {@link #setTitle(TextTitle)} method. If there is an existing title, * its text is updated, otherwise a new title using the default font is * added to the chart. If {@code text} is {@code null} the chart * title is set to {@code null}. * * @param text the title text ({@code null} permitted). * * @see #getTitle() */ public void setTitle(String text) { if (text != null) { if (this.title == null) { setTitle(new TextTitle(text, JFreeChart.DEFAULT_TITLE_FONT)); } else { this.title.setText(text); } } else { setTitle((TextTitle) null); } } /** * Adds a legend to the plot and sends a {@link ChartChangeEvent} to all * registered listeners. * * @param legend the legend ({@code null} not permitted). * * @see #removeLegend() */ public void addLegend(LegendTitle legend) { addSubtitle(legend); } /** * Returns the legend for the chart, if there is one. Note that a chart * can have more than one legend - this method returns the first. * * @return The legend (possibly {@code null}). * * @see #getLegend(int) */ public LegendTitle getLegend() { return getLegend(0); } /** * Returns the nth legend for a chart, or {@code null}. * * @param index the legend index (zero-based). * * @return The legend (possibly {@code null}). * * @see #addLegend(LegendTitle) */ public LegendTitle getLegend(int index) { int seen = 0; Iterator iterator = this.subtitles.iterator(); while (iterator.hasNext()) { Title subtitle = (Title) iterator.next(); if (subtitle instanceof LegendTitle) { if (seen == index) { return (LegendTitle) subtitle; } else { seen++; } } } return null; } /** * Removes the first legend in the chart and sends a * {@link ChartChangeEvent} to all registered listeners. * * @see #getLegend() */ public void removeLegend() { removeSubtitle(getLegend()); } /** * Returns the list of subtitles for the chart. * * @return The subtitle list (possibly empty, but never {@code null}). * * @see #setSubtitles(List) */ public List getSubtitles() { return new ArrayList(this.subtitles); } /** * Sets the title list for the chart (completely replaces any existing * titles) and sends a {@link ChartChangeEvent} to all registered * listeners. * * @param subtitles the new list of subtitles ({@code null} not * permitted). * * @see #getSubtitles() */ public void setSubtitles(List subtitles) { if (subtitles == null) { throw new NullPointerException("Null 'subtitles' argument."); } setNotify(false); clearSubtitles(); Iterator iterator = subtitles.iterator(); while (iterator.hasNext()) { Title t = (Title) iterator.next(); if (t != null) { addSubtitle(t); } } setNotify(true); // this fires a ChartChangeEvent } /** * Returns the number of titles for the chart. * * @return The number of titles for the chart. * * @see #getSubtitles() */ public int getSubtitleCount() { return this.subtitles.size(); } /** * Returns a chart subtitle. * * @param index the index of the chart subtitle (zero based). * * @return A chart subtitle. * * @see #addSubtitle(Title) */ public Title getSubtitle(int index) { if ((index < 0) || (index >= getSubtitleCount())) { throw new IllegalArgumentException("Index out of range."); } return (Title) this.subtitles.get(index); } /** * Adds a chart subtitle, and notifies registered listeners that the chart * has been modified. * * @param subtitle the subtitle ({@code null} not permitted). * * @see #getSubtitle(int) */ public void addSubtitle(Title subtitle) { Args.nullNotPermitted(subtitle, "subtitle"); this.subtitles.add(subtitle); subtitle.addChangeListener(this); fireChartChanged(); } /** * Adds a subtitle at a particular position in the subtitle list, and sends * a {@link ChartChangeEvent} to all registered listeners. * * @param index the index (in the range 0 to {@link #getSubtitleCount()}). * @param subtitle the subtitle to add ({@code null} not permitted). */ public void addSubtitle(int index, Title subtitle) { if (index < 0 || index > getSubtitleCount()) { throw new IllegalArgumentException( "The 'index' argument is out of range."); } Args.nullNotPermitted(subtitle, "subtitle"); this.subtitles.add(index, subtitle); subtitle.addChangeListener(this); fireChartChanged(); } /** * Clears all subtitles from the chart and sends a {@link ChartChangeEvent} * to all registered listeners. * * @see #addSubtitle(Title) */ public void clearSubtitles() { Iterator iterator = this.subtitles.iterator(); while (iterator.hasNext()) { Title t = (Title) iterator.next(); t.removeChangeListener(this); } this.subtitles.clear(); fireChartChanged(); } /** * Removes the specified subtitle and sends a {@link ChartChangeEvent} to * all registered listeners. * * @param title the title. * * @see #addSubtitle(Title) */ public void removeSubtitle(Title title) { this.subtitles.remove(title); fireChartChanged(); } /** * Returns the plot for the chart. The plot is a class responsible for * coordinating the visual representation of the data, including the axes * (if any). * * @return The plot. */ public Plot getPlot() { return this.plot; } /** * Returns the plot cast as a {@link CategoryPlot}. *

* NOTE: if the plot is not an instance of {@link CategoryPlot}, then a * {@code ClassCastException} is thrown. * * @return The plot. * * @see #getPlot() */ public CategoryPlot getCategoryPlot() { return (CategoryPlot) this.plot; } /** * Returns the plot cast as an {@link XYPlot}. *

* NOTE: if the plot is not an instance of {@link XYPlot}, then a * {@code ClassCastException} is thrown. * * @return The plot. * * @see #getPlot() */ public XYPlot getXYPlot() { return (XYPlot) this.plot; } /** * Returns a flag that indicates whether or not anti-aliasing is used when * the chart is drawn. * * @return The flag. * * @see #setAntiAlias(boolean) */ public boolean getAntiAlias() { Object val = this.renderingHints.get(RenderingHints.KEY_ANTIALIASING); return RenderingHints.VALUE_ANTIALIAS_ON.equals(val); } /** * Sets a flag that indicates whether or not anti-aliasing is used when the * chart is drawn. *

* Anti-aliasing usually improves the appearance of charts, but is slower. * * @param flag the new value of the flag. * * @see #getAntiAlias() */ public void setAntiAlias(boolean flag) { Object hint = flag ? RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF; this.renderingHints.put(RenderingHints.KEY_ANTIALIASING, hint); fireChartChanged(); } /** * Returns the current value stored in the rendering hints table for * {@link RenderingHints#KEY_TEXT_ANTIALIASING}. * * @return The hint value (possibly {@code null}). * * @see #setTextAntiAlias(Object) */ public Object getTextAntiAlias() { return this.renderingHints.get(RenderingHints.KEY_TEXT_ANTIALIASING); } /** * Sets the value in the rendering hints table for * {@link RenderingHints#KEY_TEXT_ANTIALIASING} to either * {@link RenderingHints#VALUE_TEXT_ANTIALIAS_ON} or * {@link RenderingHints#VALUE_TEXT_ANTIALIAS_OFF}, then sends a * {@link ChartChangeEvent} to all registered listeners. * * @param flag the new value of the flag. * * @see #getTextAntiAlias() * @see #setTextAntiAlias(Object) */ public void setTextAntiAlias(boolean flag) { if (flag) { setTextAntiAlias(RenderingHints.VALUE_TEXT_ANTIALIAS_ON); } else { setTextAntiAlias(RenderingHints.VALUE_TEXT_ANTIALIAS_OFF); } } /** * Sets the value in the rendering hints table for * {@link RenderingHints#KEY_TEXT_ANTIALIASING} and sends a * {@link ChartChangeEvent} to all registered listeners. * * @param val the new value ({@code null} permitted). * * @see #getTextAntiAlias() * @see #setTextAntiAlias(boolean) */ public void setTextAntiAlias(Object val) { this.renderingHints.put(RenderingHints.KEY_TEXT_ANTIALIASING, val); notifyListeners(new ChartChangeEvent(this)); } /** * Returns the paint used for the chart background. * * @return The paint (possibly {@code null}). * * @see #setBackgroundPaint(Paint) */ public Paint getBackgroundPaint() { return this.backgroundPaint; } /** * Sets the paint used to fill the chart background and sends a * {@link ChartChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} permitted). * * @see #getBackgroundPaint() */ public void setBackgroundPaint(Paint paint) { if (this.backgroundPaint != null) { if (!this.backgroundPaint.equals(paint)) { this.backgroundPaint = paint; fireChartChanged(); } } else { if (paint != null) { this.backgroundPaint = paint; fireChartChanged(); } } } /** * Returns the background image for the chart, or {@code null} if * there is no image. * * @return The image (possibly {@code null}). * * @see #setBackgroundImage(Image) */ public Image getBackgroundImage() { return this.backgroundImage; } /** * Sets the background image for the chart and sends a * {@link ChartChangeEvent} to all registered listeners. * * @param image the image ({@code null} permitted). * * @see #getBackgroundImage() */ public void setBackgroundImage(Image image) { if (this.backgroundImage != null) { if (!this.backgroundImage.equals(image)) { this.backgroundImage = image; fireChartChanged(); } } else { if (image != null) { this.backgroundImage = image; fireChartChanged(); } } } /** * Returns the background image alignment. Alignment constants are defined * in the {@link Align} class. * * @return The alignment. * * @see #setBackgroundImageAlignment(int) */ public int getBackgroundImageAlignment() { return this.backgroundImageAlignment; } /** * Sets the background alignment. Alignment options are defined by the * {@link org.jfree.chart.ui.Align} class. * * @param alignment the alignment. * * @see #getBackgroundImageAlignment() */ public void setBackgroundImageAlignment(int alignment) { if (this.backgroundImageAlignment != alignment) { this.backgroundImageAlignment = alignment; fireChartChanged(); } } /** * Returns the alpha-transparency for the chart's background image. * * @return The alpha-transparency. * * @see #setBackgroundImageAlpha(float) */ public float getBackgroundImageAlpha() { return this.backgroundImageAlpha; } /** * Sets the alpha-transparency for the chart's background image. * Registered listeners are notified that the chart has been changed. * * @param alpha the alpha value. * * @see #getBackgroundImageAlpha() */ public void setBackgroundImageAlpha(float alpha) { if (this.backgroundImageAlpha != alpha) { this.backgroundImageAlpha = alpha; fireChartChanged(); } } /** * Returns a flag that controls whether or not change events are sent to * registered listeners. * * @return A boolean. * * @see #setNotify(boolean) */ public boolean isNotify() { return this.notify; } /** * Sets a flag that controls whether or not listeners receive * {@link ChartChangeEvent} notifications. * * @param notify a boolean. * * @see #isNotify() */ public void setNotify(boolean notify) { this.notify = notify; // if the flag is being set to true, there may be queued up changes... if (notify) { notifyListeners(new ChartChangeEvent(this)); } } /** * Draws the chart on a Java 2D graphics device (such as the screen or a * printer). *

* This method is the focus of the entire JFreeChart library. * * @param g2 the graphics device. * @param area the area within which the chart should be drawn. */ @Override public void draw(Graphics2D g2, Rectangle2D area) { draw(g2, area, null, null); } /** * Draws the chart on a Java 2D graphics device (such as the screen or a * printer). This method is the focus of the entire JFreeChart library. * * @param g2 the graphics device. * @param area the area within which the chart should be drawn. * @param info records info about the drawing (null means collect no info). */ public void draw(Graphics2D g2, Rectangle2D area, ChartRenderingInfo info) { draw(g2, area, null, info); } /** * Draws the chart on a Java 2D graphics device (such as the screen or a * printer). *

* This method is the focus of the entire JFreeChart library. * * @param g2 the graphics device. * @param chartArea the area within which the chart should be drawn. * @param anchor the anchor point (in Java2D space) for the chart * ({@code null} permitted). * @param info records info about the drawing (null means collect no info). */ public void draw(Graphics2D g2, Rectangle2D chartArea, Point2D anchor, ChartRenderingInfo info) { notifyListeners(new ChartProgressEvent(this, this, ChartProgressEvent.DRAWING_STARTED, 0)); if (this.elementHinting) { Map m = new HashMap(); if (this.id != null) { m.put("id", this.id); } m.put("ref", "JFREECHART_TOP_LEVEL"); g2.setRenderingHint(ChartHints.KEY_BEGIN_ELEMENT, m); } EntityCollection entities = null; // record the chart area, if info is requested... if (info != null) { info.clear(); info.setChartArea(chartArea); entities = info.getEntityCollection(); } if (entities != null) { entities.add(new JFreeChartEntity((Rectangle2D) chartArea.clone(), this)); } // ensure no drawing occurs outside chart area... Shape savedClip = g2.getClip(); g2.clip(chartArea); g2.addRenderingHints(this.renderingHints); // draw the chart background... if (this.backgroundPaint != null) { g2.setPaint(this.backgroundPaint); g2.fill(chartArea); } if (this.backgroundImage != null) { Composite originalComposite = g2.getComposite(); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, this.backgroundImageAlpha)); Rectangle2D dest = new Rectangle2D.Double(0.0, 0.0, this.backgroundImage.getWidth(null), this.backgroundImage.getHeight(null)); Align.align(dest, chartArea, this.backgroundImageAlignment); g2.drawImage(this.backgroundImage, (int) dest.getX(), (int) dest.getY(), (int) dest.getWidth(), (int) dest.getHeight(), null); g2.setComposite(originalComposite); } if (isBorderVisible()) { Paint paint = getBorderPaint(); Stroke stroke = getBorderStroke(); if (paint != null && stroke != null) { Rectangle2D borderArea = new Rectangle2D.Double( chartArea.getX(), chartArea.getY(), chartArea.getWidth() - 1.0, chartArea.getHeight() - 1.0); g2.setPaint(paint); g2.setStroke(stroke); g2.draw(borderArea); } } // draw the title and subtitles... Rectangle2D nonTitleArea = new Rectangle2D.Double(); nonTitleArea.setRect(chartArea); this.padding.trim(nonTitleArea); if (this.title != null && this.title.isVisible()) { EntityCollection e = drawTitle(this.title, g2, nonTitleArea, (entities != null)); if (e != null && entities != null) { entities.addAll(e); } } Iterator iterator = this.subtitles.iterator(); while (iterator.hasNext()) { Title currentTitle = (Title) iterator.next(); if (currentTitle.isVisible()) { EntityCollection e = drawTitle(currentTitle, g2, nonTitleArea, (entities != null)); if (e != null && entities != null) { entities.addAll(e); } } } Rectangle2D plotArea = nonTitleArea; // draw the plot (axes and data visualisation) PlotRenderingInfo plotInfo = null; if (info != null) { plotInfo = info.getPlotInfo(); } this.plot.draw(g2, plotArea, anchor, null, plotInfo); g2.setClip(savedClip); if (this.elementHinting) { g2.setRenderingHint(ChartHints.KEY_END_ELEMENT, Boolean.TRUE); } notifyListeners(new ChartProgressEvent(this, this, ChartProgressEvent.DRAWING_FINISHED, 100)); } /** * Creates a rectangle that is aligned to the frame. * * @param dimensions the dimensions for the rectangle. * @param frame the frame to align to. * @param hAlign the horizontal alignment. * @param vAlign the vertical alignment. * * @return A rectangle. */ private Rectangle2D createAlignedRectangle2D(Size2D dimensions, Rectangle2D frame, HorizontalAlignment hAlign, VerticalAlignment vAlign) { double x = Double.NaN; double y = Double.NaN; if (hAlign == HorizontalAlignment.LEFT) { x = frame.getX(); } else if (hAlign == HorizontalAlignment.CENTER) { x = frame.getCenterX() - (dimensions.width / 2.0); } else if (hAlign == HorizontalAlignment.RIGHT) { x = frame.getMaxX() - dimensions.width; } if (vAlign == VerticalAlignment.TOP) { y = frame.getY(); } else if (vAlign == VerticalAlignment.CENTER) { y = frame.getCenterY() - (dimensions.height / 2.0); } else if (vAlign == VerticalAlignment.BOTTOM) { y = frame.getMaxY() - dimensions.height; } return new Rectangle2D.Double(x, y, dimensions.width, dimensions.height); } /** * Draws a title. The title should be drawn at the top, bottom, left or * right of the specified area, and the area should be updated to reflect * the amount of space used by the title. * * @param t the title ({@code null} not permitted). * @param g2 the graphics device ({@code null} not permitted). * @param area the chart area, excluding any existing titles * ({@code null} not permitted). * @param entities a flag that controls whether or not an entity * collection is returned for the title. * * @return An entity collection for the title (possibly {@code null}). */ protected EntityCollection drawTitle(Title t, Graphics2D g2, Rectangle2D area, boolean entities) { Args.nullNotPermitted(t, "t"); Args.nullNotPermitted(area, "area"); Rectangle2D titleArea; RectangleEdge position = t.getPosition(); double ww = area.getWidth(); if (ww <= 0.0) { return null; } double hh = area.getHeight(); if (hh <= 0.0) { return null; } RectangleConstraint constraint = new RectangleConstraint(ww, new Range(0.0, ww), LengthConstraintType.RANGE, hh, new Range(0.0, hh), LengthConstraintType.RANGE); Object retValue = null; BlockParams p = new BlockParams(); p.setGenerateEntities(entities); if (position == RectangleEdge.TOP) { Size2D size = t.arrange(g2, constraint); titleArea = createAlignedRectangle2D(size, area, t.getHorizontalAlignment(), VerticalAlignment.TOP); retValue = t.draw(g2, titleArea, p); area.setRect(area.getX(), Math.min(area.getY() + size.height, area.getMaxY()), area.getWidth(), Math.max(area.getHeight() - size.height, 0)); } else if (position == RectangleEdge.BOTTOM) { Size2D size = t.arrange(g2, constraint); titleArea = createAlignedRectangle2D(size, area, t.getHorizontalAlignment(), VerticalAlignment.BOTTOM); retValue = t.draw(g2, titleArea, p); area.setRect(area.getX(), area.getY(), area.getWidth(), area.getHeight() - size.height); } else if (position == RectangleEdge.RIGHT) { Size2D size = t.arrange(g2, constraint); titleArea = createAlignedRectangle2D(size, area, HorizontalAlignment.RIGHT, t.getVerticalAlignment()); retValue = t.draw(g2, titleArea, p); area.setRect(area.getX(), area.getY(), area.getWidth() - size.width, area.getHeight()); } else if (position == RectangleEdge.LEFT) { Size2D size = t.arrange(g2, constraint); titleArea = createAlignedRectangle2D(size, area, HorizontalAlignment.LEFT, t.getVerticalAlignment()); retValue = t.draw(g2, titleArea, p); area.setRect(area.getX() + size.width, area.getY(), area.getWidth() - size.width, area.getHeight()); } else { throw new RuntimeException("Unrecognised title position."); } EntityCollection result = null; if (retValue instanceof EntityBlockResult) { EntityBlockResult ebr = (EntityBlockResult) retValue; result = ebr.getEntityCollection(); } return result; } /** * Creates and returns a buffered image into which the chart has been drawn. * * @param width the width. * @param height the height. * * @return A buffered image. */ public BufferedImage createBufferedImage(int width, int height) { return createBufferedImage(width, height, null); } /** * Creates and returns a buffered image into which the chart has been drawn. * * @param width the width. * @param height the height. * @param info carries back chart state information ({@code null} * permitted). * * @return A buffered image. */ public BufferedImage createBufferedImage(int width, int height, ChartRenderingInfo info) { return createBufferedImage(width, height, BufferedImage.TYPE_INT_ARGB, info); } /** * Creates and returns a buffered image into which the chart has been drawn. * * @param width the width. * @param height the height. * @param imageType the image type. * @param info carries back chart state information ({@code null} * permitted). * * @return A buffered image. */ public BufferedImage createBufferedImage(int width, int height, int imageType, ChartRenderingInfo info) { BufferedImage image = new BufferedImage(width, height, imageType); Graphics2D g2 = image.createGraphics(); draw(g2, new Rectangle2D.Double(0, 0, width, height), null, info); g2.dispose(); return image; } /** * Creates and returns a buffered image into which the chart has been drawn. * * @param imageWidth the image width. * @param imageHeight the image height. * @param drawWidth the width for drawing the chart (will be scaled to * fit image). * @param drawHeight the height for drawing the chart (will be scaled to * fit image). * @param info optional object for collection chart dimension and entity * information. * * @return A buffered image. */ public BufferedImage createBufferedImage(int imageWidth, int imageHeight, double drawWidth, double drawHeight, ChartRenderingInfo info) { BufferedImage image = new BufferedImage(imageWidth, imageHeight, BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = image.createGraphics(); double scaleX = imageWidth / drawWidth; double scaleY = imageHeight / drawHeight; AffineTransform st = AffineTransform.getScaleInstance(scaleX, scaleY); g2.transform(st); draw(g2, new Rectangle2D.Double(0, 0, drawWidth, drawHeight), null, info); g2.dispose(); return image; } /** * Handles a 'click' on the chart. JFreeChart is not a UI component, so * some other object (for example, {@link ChartPanel}) needs to capture * the click event and pass it onto the JFreeChart object. * If you are not using JFreeChart in a client application, then this * method is not required. * * @param x x-coordinate of the click (in Java2D space). * @param y y-coordinate of the click (in Java2D space). * @param info contains chart dimension and entity information * ({@code null} not permitted). */ public void handleClick(int x, int y, ChartRenderingInfo info) { // pass the click on to the plot... // rely on the plot to post a plot change event and redraw the chart... this.plot.handleClick(x, y, info.getPlotInfo()); } /** * Registers an object for notification of changes to the chart. * * @param listener the listener ({@code null} not permitted). * * @see #removeChangeListener(ChartChangeListener) */ public void addChangeListener(ChartChangeListener listener) { Args.nullNotPermitted(listener, "listener"); this.changeListeners.add(ChartChangeListener.class, listener); } /** * Deregisters an object for notification of changes to the chart. * * @param listener the listener ({@code null} not permitted) * * @see #addChangeListener(ChartChangeListener) */ public void removeChangeListener(ChartChangeListener listener) { Args.nullNotPermitted(listener, "listener"); this.changeListeners.remove(ChartChangeListener.class, listener); } /** * Sends a default {@link ChartChangeEvent} to all registered listeners. *

* This method is for convenience only. */ public void fireChartChanged() { ChartChangeEvent event = new ChartChangeEvent(this); notifyListeners(event); } /** * Sends a {@link ChartChangeEvent} to all registered listeners. * * @param event information about the event that triggered the * notification. */ protected void notifyListeners(ChartChangeEvent event) { if (this.notify) { Object[] listeners = this.changeListeners.getListenerList(); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ChartChangeListener.class) { ((ChartChangeListener) listeners[i + 1]).chartChanged( event); } } } } /** * Registers an object for notification of progress events relating to the * chart. * * @param listener the object being registered. * * @see #removeProgressListener(ChartProgressListener) */ public void addProgressListener(ChartProgressListener listener) { this.progressListeners.add(ChartProgressListener.class, listener); } /** * Deregisters an object for notification of changes to the chart. * * @param listener the object being deregistered. * * @see #addProgressListener(ChartProgressListener) */ public void removeProgressListener(ChartProgressListener listener) { this.progressListeners.remove(ChartProgressListener.class, listener); } /** * Sends a {@link ChartProgressEvent} to all registered listeners. * * @param event information about the event that triggered the * notification. */ protected void notifyListeners(ChartProgressEvent event) { Object[] listeners = this.progressListeners.getListenerList(); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ChartProgressListener.class) { ((ChartProgressListener) listeners[i + 1]).chartProgress(event); } } } /** * Receives notification that a chart title has changed, and passes this * on to registered listeners. * * @param event information about the chart title change. */ @Override public void titleChanged(TitleChangeEvent event) { event.setChart(this); notifyListeners(event); } /** * Receives notification that the plot has changed, and passes this on to * registered listeners. * * @param event information about the plot change. */ @Override public void plotChanged(PlotChangeEvent event) { event.setChart(this); notifyListeners(event); } /** * Tests this chart for equality with another object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof JFreeChart)) { return false; } JFreeChart that = (JFreeChart) obj; if (!Objects.equals(this.renderingHints, that.renderingHints)) { return false; } if (this.borderVisible != that.borderVisible) { return false; } if (this.elementHinting != that.elementHinting) { return false; } if (!Objects.equals(this.borderStroke, that.borderStroke)) { return false; } if (!PaintUtils.equal(this.borderPaint, that.borderPaint)) { return false; } if (!Objects.equals(this.padding, that.padding)) { return false; } if (!Objects.equals(this.title, that.title)) { return false; } if (!Objects.equals(this.subtitles, that.subtitles)) { return false; } if (!Objects.equals(this.plot, that.plot)) { return false; } if (!PaintUtils.equal(this.backgroundPaint, that.backgroundPaint)) { return false; } if (!Objects.equals(this.backgroundImage, that.backgroundImage)) { return false; } if (this.backgroundImageAlignment != that.backgroundImageAlignment) { return false; } if (Float.floatToIntBits(this.backgroundImageAlpha) != Float.floatToIntBits(that.backgroundImageAlpha)) { return false; } if (this.notify != that.notify) { return false; } if (!Objects.equals(this.id, that.id)) { return false; } return true; } @Override public int hashCode() { int hash = 7; hash = 43 * hash + Objects.hashCode(this.renderingHints); hash = 43 * hash + Objects.hashCode(this.id); hash = 43 * hash + (this.borderVisible ? 1 : 0); hash = 43 * hash + Objects.hashCode(this.borderStroke); hash = 43 * hash + HashUtils.hashCodeForPaint(this.borderPaint); hash = 43 * hash + Objects.hashCode(this.padding); hash = 43 * hash + Objects.hashCode(this.title); hash = 43 * hash + Objects.hashCode(this.subtitles); hash = 43 * hash + Objects.hashCode(this.plot); hash = 43 * hash + HashUtils.hashCodeForPaint(this.backgroundPaint); hash = 43 * hash + Objects.hashCode(this.backgroundImage); hash = 43 * hash + this.backgroundImageAlignment; hash = 43 * hash + Float.floatToIntBits(this.backgroundImageAlpha); hash = 43 * hash + (this.notify ? 1 : 0); hash = 43 * hash + (this.elementHinting ? 1 : 0); return hash; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeStroke(this.borderStroke, stream); SerialUtils.writePaint(this.borderPaint, stream); SerialUtils.writePaint(this.backgroundPaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.borderStroke = SerialUtils.readStroke(stream); this.borderPaint = SerialUtils.readPaint(stream); this.backgroundPaint = SerialUtils.readPaint(stream); this.progressListeners = new EventListenerList(); this.changeListeners = new EventListenerList(); this.renderingHints = new RenderingHints( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); this.renderingHints.put(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); // register as a listener with sub-components... if (this.title != null) { this.title.addChangeListener(this); } for (int i = 0; i < getSubtitleCount(); i++) { getSubtitle(i).addChangeListener(this); } this.plot.addChangeListener(this); } /** * Clones the object, and takes care of listeners. * Note: caller shall register its own listeners on cloned graph. * * @return A clone. * * @throws CloneNotSupportedException if the chart is not cloneable. */ @Override public Object clone() throws CloneNotSupportedException { JFreeChart chart = (JFreeChart) super.clone(); chart.renderingHints = (RenderingHints) this.renderingHints.clone(); // private boolean borderVisible; // private transient Stroke borderStroke; // private transient Paint borderPaint; if (this.title != null) { chart.title = (TextTitle) this.title.clone(); chart.title.addChangeListener(chart); } chart.subtitles = new ArrayList<>(); for (int i = 0; i < getSubtitleCount(); i++) { Title subtitle = (Title) getSubtitle(i).clone(); chart.subtitles.add(subtitle); subtitle.addChangeListener(chart); } if (this.plot != null) { chart.plot = (Plot) this.plot.clone(); chart.plot.addChangeListener(chart); } chart.progressListeners = new EventListenerList(); chart.changeListeners = new EventListenerList(); return chart; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/LegendItem.java000066400000000000000000001103621463604235500257410ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * LegendItem.java * --------------- * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Andrzej Porebski; * David Li; * Wolfgang Irler; * Luke Quinane; * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.text.AttributedString; import java.text.CharacterIterator; import java.util.Objects; import org.jfree.chart.text.AttributedStringUtils; import org.jfree.chart.ui.GradientPaintTransformer; import org.jfree.chart.ui.StandardGradientPaintTransformer; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; import org.jfree.chart.util.ShapeUtils; import org.jfree.data.general.Dataset; /** * A temporary storage object for recording the properties of a legend item, * without any consideration for layout issues. */ public class LegendItem implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -797214582948827144L; /** * The dataset. */ private Dataset dataset; /** * The series key. */ private Comparable seriesKey; /** The dataset index. */ private int datasetIndex; /** The series index. */ private int series; /** The label. */ private String label; /** * The label font ({@code null} is permitted). */ private Font labelFont; /** * The label paint ({@code null} is permitted). */ private transient Paint labelPaint; /** The attributed label (if null, fall back to the regular label). */ private transient AttributedString attributedLabel; /** * The description (not currently used - could be displayed as a tool tip). */ private String description; /** The tool tip text. */ private String toolTipText; /** The url text. */ private String urlText; /** A flag that controls whether or not the shape is visible. */ private boolean shapeVisible; /** The shape. */ private transient Shape shape; /** A flag that controls whether or not the shape is filled. */ private boolean shapeFilled; /** The paint. */ private transient Paint fillPaint; /** * A gradient paint transformer. */ private GradientPaintTransformer fillPaintTransformer; /** A flag that controls whether or not the shape outline is visible. */ private boolean shapeOutlineVisible; /** The outline paint. */ private transient Paint outlinePaint; /** The outline stroke. */ private transient Stroke outlineStroke; /** A flag that controls whether or not the line is visible. */ private boolean lineVisible; /** The line. */ private transient Shape line; /** The stroke. */ private transient Stroke lineStroke; /** The line paint. */ private transient Paint linePaint; /** * The shape must be non-null for a LegendItem - if no shape is required, * use this. */ private static final Shape UNUSED_SHAPE = new Line2D.Float(); /** * The stroke must be non-null for a LegendItem - if no stroke is required, * use this. */ private static final Stroke UNUSED_STROKE = new BasicStroke(0.0f); /** * Creates a legend item with the specified label. The remaining * attributes take default values. * * @param label the label ({@code null} not permitted). */ public LegendItem(String label) { this(label, Color.BLACK); } /** * Creates a legend item with the specified label and fill paint. The * remaining attributes take default values. * * @param label the label ({@code null} not permitted). * @param paint the paint ({@code null} not permitted). */ public LegendItem(String label, Paint paint) { this(label, null, null, null, new Rectangle2D.Double(-4.0, -4.0, 8.0, 8.0), paint); } /** * Creates a legend item with a filled shape. The shape is not outlined, * and no line is visible. * * @param label the label ({@code null} not permitted). * @param description the description ({@code null} permitted). * @param toolTipText the tool tip text ({@code null} permitted). * @param urlText the URL text ({@code null} permitted). * @param shape the shape ({@code null} not permitted). * @param fillPaint the paint used to fill the shape ({@code null} * not permitted). */ public LegendItem(String label, String description, String toolTipText, String urlText, Shape shape, Paint fillPaint) { this(label, description, toolTipText, urlText, /* shape visible = */ true, shape, /* shape filled = */ true, fillPaint, /* shape outlined */ false, Color.BLACK, UNUSED_STROKE, /* line visible */ false, UNUSED_SHAPE, UNUSED_STROKE, Color.BLACK); } /** * Creates a legend item with a filled and outlined shape. * * @param label the label ({@code null} not permitted). * @param description the description ({@code null} permitted). * @param toolTipText the tool tip text ({@code null} permitted). * @param urlText the URL text ({@code null} permitted). * @param shape the shape ({@code null} not permitted). * @param fillPaint the paint used to fill the shape ({@code null} * not permitted). * @param outlineStroke the outline stroke ({@code null} not * permitted). * @param outlinePaint the outline paint ({@code null} not * permitted). */ public LegendItem(String label, String description, String toolTipText, String urlText, Shape shape, Paint fillPaint, Stroke outlineStroke, Paint outlinePaint) { this(label, description, toolTipText, urlText, /* shape visible = */ true, shape, /* shape filled = */ true, fillPaint, /* shape outlined = */ true, outlinePaint, outlineStroke, /* line visible */ false, UNUSED_SHAPE, UNUSED_STROKE, Color.BLACK); } /** * Creates a legend item using a line. * * @param label the label ({@code null} not permitted). * @param description the description ({@code null} permitted). * @param toolTipText the tool tip text ({@code null} permitted). * @param urlText the URL text ({@code null} permitted). * @param line the line ({@code null} not permitted). * @param lineStroke the line stroke ({@code null} not permitted). * @param linePaint the line paint ({@code null} not permitted). */ public LegendItem(String label, String description, String toolTipText, String urlText, Shape line, Stroke lineStroke, Paint linePaint) { this(label, description, toolTipText, urlText, /* shape visible = */ false, UNUSED_SHAPE, /* shape filled = */ false, Color.BLACK, /* shape outlined = */ false, Color.BLACK, UNUSED_STROKE, /* line visible = */ true, line, lineStroke, linePaint); } /** * Creates a new legend item. * * @param label the label ({@code null} not permitted). * @param description the description (not currently used, * {@code null} permitted). * @param toolTipText the tool tip text ({@code null} permitted). * @param urlText the URL text ({@code null} permitted). * @param shapeVisible a flag that controls whether or not the shape is * displayed. * @param shape the shape ({@code null} permitted). * @param shapeFilled a flag that controls whether or not the shape is * filled. * @param fillPaint the fill paint ({@code null} not permitted). * @param shapeOutlineVisible a flag that controls whether or not the * shape is outlined. * @param outlinePaint the outline paint ({@code null} not permitted). * @param outlineStroke the outline stroke ({@code null} not * permitted). * @param lineVisible a flag that controls whether or not the line is * visible. * @param line the line. * @param lineStroke the stroke ({@code null} not permitted). * @param linePaint the line paint ({@code null} not permitted). */ public LegendItem(String label, String description, String toolTipText, String urlText, boolean shapeVisible, Shape shape, boolean shapeFilled, Paint fillPaint, boolean shapeOutlineVisible, Paint outlinePaint, Stroke outlineStroke, boolean lineVisible, Shape line, Stroke lineStroke, Paint linePaint) { Args.nullNotPermitted(label, "label"); Args.nullNotPermitted(fillPaint, "fillPaint"); Args.nullNotPermitted(lineStroke, "lineStroke"); Args.nullNotPermitted(outlinePaint, "outlinePaint"); Args.nullNotPermitted(outlineStroke, "outlineStroke"); this.label = label; this.labelPaint = null; this.attributedLabel = null; this.description = description; this.shapeVisible = shapeVisible; this.shape = shape; this.shapeFilled = shapeFilled; this.fillPaint = fillPaint; this.fillPaintTransformer = new StandardGradientPaintTransformer(); this.shapeOutlineVisible = shapeOutlineVisible; this.outlinePaint = outlinePaint; this.outlineStroke = outlineStroke; this.lineVisible = lineVisible; this.line = line; this.lineStroke = lineStroke; this.linePaint = linePaint; this.toolTipText = toolTipText; this.urlText = urlText; } /** * Creates a legend item with a filled shape. The shape is not outlined, * and no line is visible. * * @param label the label ({@code null} not permitted). * @param description the description ({@code null} permitted). * @param toolTipText the tool tip text ({@code null} permitted). * @param urlText the URL text ({@code null} permitted). * @param shape the shape ({@code null} not permitted). * @param fillPaint the paint used to fill the shape ({@code null} * not permitted). */ public LegendItem(AttributedString label, String description, String toolTipText, String urlText, Shape shape, Paint fillPaint) { this(label, description, toolTipText, urlText, /* shape visible = */ true, shape, /* shape filled = */ true, fillPaint, /* shape outlined = */ false, Color.BLACK, UNUSED_STROKE, /* line visible = */ false, UNUSED_SHAPE, UNUSED_STROKE, Color.BLACK); } /** * Creates a legend item with a filled and outlined shape. * * @param label the label ({@code null} not permitted). * @param description the description ({@code null} permitted). * @param toolTipText the tool tip text ({@code null} permitted). * @param urlText the URL text ({@code null} permitted). * @param shape the shape ({@code null} not permitted). * @param fillPaint the paint used to fill the shape ({@code null} * not permitted). * @param outlineStroke the outline stroke ({@code null} not * permitted). * @param outlinePaint the outline paint ({@code null} not * permitted). */ public LegendItem(AttributedString label, String description, String toolTipText, String urlText, Shape shape, Paint fillPaint, Stroke outlineStroke, Paint outlinePaint) { this(label, description, toolTipText, urlText, /* shape visible = */ true, shape, /* shape filled = */ true, fillPaint, /* shape outlined = */ true, outlinePaint, outlineStroke, /* line visible = */ false, UNUSED_SHAPE, UNUSED_STROKE, Color.BLACK); } /** * Creates a legend item using a line. * * @param label the label ({@code null} not permitted). * @param description the description ({@code null} permitted). * @param toolTipText the tool tip text ({@code null} permitted). * @param urlText the URL text ({@code null} permitted). * @param line the line ({@code null} not permitted). * @param lineStroke the line stroke ({@code null} not permitted). * @param linePaint the line paint ({@code null} not permitted). */ public LegendItem(AttributedString label, String description, String toolTipText, String urlText, Shape line, Stroke lineStroke, Paint linePaint) { this(label, description, toolTipText, urlText, /* shape visible = */ false, UNUSED_SHAPE, /* shape filled = */ false, Color.BLACK, /* shape outlined = */ false, Color.BLACK, UNUSED_STROKE, /* line visible = */ true, line, lineStroke, linePaint); } /** * Creates a new legend item. * * @param label the label ({@code null} not permitted). * @param description the description (not currently used, * {@code null} permitted). * @param toolTipText the tool tip text ({@code null} permitted). * @param urlText the URL text ({@code null} permitted). * @param shapeVisible a flag that controls whether or not the shape is * displayed. * @param shape the shape ({@code null} permitted). * @param shapeFilled a flag that controls whether or not the shape is * filled. * @param fillPaint the fill paint ({@code null} not permitted). * @param shapeOutlineVisible a flag that controls whether or not the * shape is outlined. * @param outlinePaint the outline paint ({@code null} not permitted). * @param outlineStroke the outline stroke ({@code null} not * permitted). * @param lineVisible a flag that controls whether or not the line is * visible. * @param line the line ({@code null} not permitted). * @param lineStroke the stroke ({@code null} not permitted). * @param linePaint the line paint ({@code null} not permitted). */ public LegendItem(AttributedString label, String description, String toolTipText, String urlText, boolean shapeVisible, Shape shape, boolean shapeFilled, Paint fillPaint, boolean shapeOutlineVisible, Paint outlinePaint, Stroke outlineStroke, boolean lineVisible, Shape line, Stroke lineStroke, Paint linePaint) { Args.nullNotPermitted(label, "label"); Args.nullNotPermitted(fillPaint, "fillPaint"); Args.nullNotPermitted(lineStroke, "lineStroke"); Args.nullNotPermitted(line, "line"); Args.nullNotPermitted(linePaint, "linePaint"); Args.nullNotPermitted(outlinePaint, "outlinePaint"); Args.nullNotPermitted(outlineStroke, "outlineStroke"); this.label = characterIteratorToString(label.getIterator()); this.attributedLabel = label; this.description = description; this.shapeVisible = shapeVisible; this.shape = shape; this.shapeFilled = shapeFilled; this.fillPaint = fillPaint; this.fillPaintTransformer = new StandardGradientPaintTransformer(); this.shapeOutlineVisible = shapeOutlineVisible; this.outlinePaint = outlinePaint; this.outlineStroke = outlineStroke; this.lineVisible = lineVisible; this.line = line; this.lineStroke = lineStroke; this.linePaint = linePaint; this.toolTipText = toolTipText; this.urlText = urlText; } /** * Returns a string containing the characters from the given iterator. * * @param iterator the iterator ({@code null} not permitted). * * @return A string. */ private String characterIteratorToString(CharacterIterator iterator) { int endIndex = iterator.getEndIndex(); int beginIndex = iterator.getBeginIndex(); int count = endIndex - beginIndex; if (count <= 0) { return ""; } char[] chars = new char[count]; int i = 0; char c = iterator.first(); while (c != CharacterIterator.DONE) { chars[i] = c; i++; c = iterator.next(); } return new String(chars); } /** * Returns the dataset. * * @return The dataset. * * @see #setDatasetIndex(int) */ public Dataset getDataset() { return this.dataset; } /** * Sets the dataset. * * @param dataset the dataset. */ public void setDataset(Dataset dataset) { this.dataset = dataset; } /** * Returns the dataset index for this legend item. * * @return The dataset index. * * @see #setDatasetIndex(int) * @see #getDataset() */ public int getDatasetIndex() { return this.datasetIndex; } /** * Sets the dataset index for this legend item. * * @param index the index. * * @see #getDatasetIndex() */ public void setDatasetIndex(int index) { this.datasetIndex = index; } /** * Returns the series key. * * @return The series key. * * @see #setSeriesKey(Comparable) */ public Comparable getSeriesKey() { return this.seriesKey; } /** * Sets the series key. * * @param key the series key. */ public void setSeriesKey(Comparable key) { this.seriesKey = key; } /** * Returns the series index for this legend item. * * @return The series index. */ public int getSeriesIndex() { return this.series; } /** * Sets the series index for this legend item. * * @param index the index. */ public void setSeriesIndex(int index) { this.series = index; } /** * Returns the label. * * @return The label (never {@code null}). */ public String getLabel() { return this.label; } /** * Returns the label font. * * @return The label font (possibly {@code null}). */ public Font getLabelFont() { return this.labelFont; } /** * Sets the label font. * * @param font the font ({@code null} permitted). */ public void setLabelFont(Font font) { this.labelFont = font; } /** * Returns the paint used to draw the label. * * @return The paint (possibly {@code null}). */ public Paint getLabelPaint() { return this.labelPaint; } /** * Sets the paint used to draw the label. * * @param paint the paint ({@code null} permitted). */ public void setLabelPaint(Paint paint) { this.labelPaint = paint; } /** * Returns the attributed label. * * @return The attributed label (possibly {@code null}). */ public AttributedString getAttributedLabel() { return this.attributedLabel; } /** * Returns the description for the legend item. * * @return The description (possibly {@code null}). * * @see #setDescription(java.lang.String) */ public String getDescription() { return this.description; } /** * Sets the description for this legend item. * * @param text the description ({@code null} permitted). * * @see #getDescription() */ public void setDescription(String text) { this.description = text; } /** * Returns the tool tip text. * * @return The tool tip text (possibly {@code null}). * * @see #setToolTipText(java.lang.String) */ public String getToolTipText() { return this.toolTipText; } /** * Sets the tool tip text for this legend item. * * @param text the text ({@code null} permitted). * * @see #getToolTipText() */ public void setToolTipText(String text) { this.toolTipText = text; } /** * Returns the URL text. * * @return The URL text (possibly {@code null}). * * @see #setURLText(java.lang.String) */ public String getURLText() { return this.urlText; } /** * Sets the URL text. * * @param text the text ({@code null} permitted). * * @see #getURLText() */ public void setURLText(String text) { this.urlText = text; } /** * Returns a flag that indicates whether or not the shape is visible. * * @return A boolean. * * @see #setShapeVisible(boolean) */ public boolean isShapeVisible() { return this.shapeVisible; } /** * Sets the flag that controls whether or not the shape is visible. * * @param visible the new flag value. * * @see #isShapeVisible() * @see #isLineVisible() */ public void setShapeVisible(boolean visible) { this.shapeVisible = visible; } /** * Returns the shape used to label the series represented by this legend * item. * * @return The shape (never {@code null}). * * @see #setShape(java.awt.Shape) */ public Shape getShape() { return this.shape; } /** * Sets the shape for the legend item. * * @param shape the shape ({@code null} not permitted). * * @see #getShape() */ public void setShape(Shape shape) { Args.nullNotPermitted(shape, "shape"); this.shape = shape; } /** * Returns a flag that controls whether or not the shape is filled. * * @return A boolean. */ public boolean isShapeFilled() { return this.shapeFilled; } /** * Returns the fill paint. * * @return The fill paint (never {@code null}). */ public Paint getFillPaint() { return this.fillPaint; } /** * Sets the fill paint. * * @param paint the paint ({@code null} not permitted). */ public void setFillPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.fillPaint = paint; } /** * Returns the flag that controls whether or not the shape outline * is visible. * * @return A boolean. */ public boolean isShapeOutlineVisible() { return this.shapeOutlineVisible; } /** * Returns the line stroke for the series. * * @return The stroke (never {@code null}). */ public Stroke getLineStroke() { return this.lineStroke; } /** * Sets the line stroke. * * @param stroke the stroke ({@code null} not permitted). */ public void setLineStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.lineStroke = stroke; } /** * Returns the paint used for lines. * * @return The paint (never {@code null}). */ public Paint getLinePaint() { return this.linePaint; } /** * Sets the line paint. * * @param paint the paint ({@code null} not permitted). */ public void setLinePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.linePaint = paint; } /** * Returns the outline paint. * * @return The outline paint (never {@code null}). */ public Paint getOutlinePaint() { return this.outlinePaint; } /** * Sets the outline paint. * * @param paint the paint ({@code null} not permitted). */ public void setOutlinePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.outlinePaint = paint; } /** * Returns the outline stroke. * * @return The outline stroke (never {@code null}). * * @see #setOutlineStroke(java.awt.Stroke) */ public Stroke getOutlineStroke() { return this.outlineStroke; } /** * Sets the outline stroke. * * @param stroke the stroke ({@code null} not permitted). * * @see #getOutlineStroke() */ public void setOutlineStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.outlineStroke = stroke; } /** * Returns a flag that indicates whether or not the line is visible. * * @return A boolean. * * @see #setLineVisible(boolean) */ public boolean isLineVisible() { return this.lineVisible; } /** * Sets the flag that controls whether or not the line shape is visible for * this legend item. * * @param visible the new flag value. * * @see #isLineVisible() */ public void setLineVisible(boolean visible) { this.lineVisible = visible; } /** * Returns the line. * * @return The line (never {@code null}). * * @see #setLine(java.awt.Shape) * @see #isLineVisible() */ public Shape getLine() { return this.line; } /** * Sets the line. * * @param line the line ({@code null} not permitted). * * @see #getLine() */ public void setLine(Shape line) { Args.nullNotPermitted(line, "line"); this.line = line; } /** * Returns the transformer used when the fill paint is an instance of * {@code GradientPaint}. * * @return The transformer (never {@code null}). * * @see #setFillPaintTransformer(GradientPaintTransformer) */ public GradientPaintTransformer getFillPaintTransformer() { return this.fillPaintTransformer; } /** * Sets the transformer used when the fill paint is an instance of * {@code GradientPaint}. * * @param transformer the transformer ({@code null} not permitted). * * @see #getFillPaintTransformer() */ public void setFillPaintTransformer(GradientPaintTransformer transformer) { Args.nullNotPermitted(transformer, "transformer"); this.fillPaintTransformer = transformer; } /** * Tests this item for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof LegendItem)) { return false; } LegendItem that = (LegendItem) obj; if (!Objects.equals(this.dataset, that.dataset)) { return false; } if (!Objects.equals(this.seriesKey, that.seriesKey)) { return false; } if (this.datasetIndex != that.datasetIndex) { return false; } if (this.series != that.series) { return false; } if (!Objects.equals(this.label, that.label)) { return false; } if (!Objects.equals(this.labelFont, that.labelFont)) { return false; } if (!Objects.equals(this.description, that.description)) { return false; } if (!Objects.equals(this.toolTipText, that.toolTipText)) { return false; } if (!Objects.equals(this.urlText, that.urlText)) { return false; } if (this.shapeVisible != that.shapeVisible) { return false; } if (this.shapeFilled != that.shapeFilled) { return false; } if (!Objects.equals(this.fillPaintTransformer, that.fillPaintTransformer)) { return false; } if (!ShapeUtils.equal(this.shape, that.shape)) { return false; } if (!PaintUtils.equal(this.fillPaint, that.fillPaint)) { return false; } if (!AttributedStringUtils.equal(this.attributedLabel, that.attributedLabel)) { return false; } if (this.shapeOutlineVisible != that.shapeOutlineVisible) { return false; } if (!Objects.equals(this.outlineStroke, that.outlineStroke)) { return false; } if (!PaintUtils.equal(this.outlinePaint, that.outlinePaint)) { return false; } if (!this.lineVisible == that.lineVisible) { return false; } if (!ShapeUtils.equal(this.line, that.line)) { return false; } if (!Objects.equals(this.lineStroke, that.lineStroke)) { return false; } if (!PaintUtils.equal(this.linePaint, that.linePaint)) { return false; } if (!Objects.equals(this.labelFont, that.labelFont)) { return false; } if (!PaintUtils.equal(this.labelPaint, that.labelPaint)) { return false; } return true; } @Override public int hashCode() { int hash = 7; hash = 83 * hash + Objects.hashCode(this.dataset); hash = 83 * hash + Objects.hashCode(this.seriesKey); hash = 83 * hash + this.datasetIndex; hash = 83 * hash + this.series; hash = 83 * hash + Objects.hashCode(this.label); hash = 83 * hash + Objects.hashCode(this.labelFont); hash = 83 * hash + HashUtils.hashCodeForPaint(this.labelPaint); hash = 83 * hash + Objects.hashCode(this.attributedLabel); hash = 83 * hash + Objects.hashCode(this.description); hash = 83 * hash + Objects.hashCode(this.toolTipText); hash = 83 * hash + Objects.hashCode(this.urlText); hash = 83 * hash + (this.shapeVisible ? 1 : 0); hash = 83 * hash + Objects.hashCode(this.shape); hash = 83 * hash + (this.shapeFilled ? 1 : 0); hash = 83 * hash + HashUtils.hashCodeForPaint(this.fillPaint); hash = 83 * hash + Objects.hashCode(this.fillPaintTransformer); hash = 83 * hash + (this.shapeOutlineVisible ? 1 : 0); hash = 83 * hash + HashUtils.hashCodeForPaint(this.outlinePaint); hash = 83 * hash + Objects.hashCode(this.outlineStroke); hash = 83 * hash + (this.lineVisible ? 1 : 0); hash = 83 * hash + Objects.hashCode(this.line); hash = 83 * hash + Objects.hashCode(this.lineStroke); hash = 83 * hash + HashUtils.hashCodeForPaint(this.linePaint); return hash; } /** * Returns an independent copy of this object (except that the clone will * still reference the same dataset as the original {@code LegendItem}). * * @return A clone. * * @throws CloneNotSupportedException if the legend item cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { LegendItem clone = (LegendItem) super.clone(); if (this.seriesKey instanceof PublicCloneable) { PublicCloneable pc = (PublicCloneable) this.seriesKey; clone.seriesKey = (Comparable) pc.clone(); } // FIXME: Clone the attributed string if it is not null clone.shape = ShapeUtils.clone(this.shape); if (this.fillPaintTransformer instanceof PublicCloneable) { PublicCloneable pc = (PublicCloneable) this.fillPaintTransformer; clone.fillPaintTransformer = (GradientPaintTransformer) pc.clone(); } clone.line = ShapeUtils.clone(this.line); return clone; } /** * Provides serialization support. * * @param stream the output stream ({@code null} not permitted). * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeAttributedString(this.attributedLabel, stream); SerialUtils.writeShape(this.shape, stream); SerialUtils.writePaint(this.fillPaint, stream); SerialUtils.writeStroke(this.outlineStroke, stream); SerialUtils.writePaint(this.outlinePaint, stream); SerialUtils.writeShape(this.line, stream); SerialUtils.writeStroke(this.lineStroke, stream); SerialUtils.writePaint(this.linePaint, stream); SerialUtils.writePaint(this.labelPaint, stream); } /** * Provides serialization support. * * @param stream the input stream ({@code null} not permitted). * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.attributedLabel = SerialUtils.readAttributedString(stream); this.shape = SerialUtils.readShape(stream); this.fillPaint = SerialUtils.readPaint(stream); this.outlineStroke = SerialUtils.readStroke(stream); this.outlinePaint = SerialUtils.readPaint(stream); this.line = SerialUtils.readShape(stream); this.lineStroke = SerialUtils.readStroke(stream); this.linePaint = SerialUtils.readPaint(stream); this.labelPaint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/LegendItemCollection.java000066400000000000000000000107171463604235500277600ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * LegendItemCollection.java * ------------------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart; import java.io.Serializable; import java.util.Iterator; import java.util.List; import java.util.Objects; import org.jfree.chart.util.ObjectUtils; /** * A collection of legend items. */ public class LegendItemCollection implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 1365215565589815953L; /** Storage for the legend items. */ private List items; /** * Constructs a new legend item collection, initially empty. */ public LegendItemCollection() { this.items = new java.util.ArrayList(); } /** * Adds a legend item to the collection. * * @param item the item to add. */ public void add(LegendItem item) { this.items.add(item); } /** * Adds the legend items from another collection to this collection. * * @param collection the other collection ({@code null} not * permitted). */ public void addAll(LegendItemCollection collection) { this.items.addAll(collection.items); } /** * Returns a legend item from the collection. * * @param index the legend item index (zero-based). * * @return The legend item. */ public LegendItem get(int index) { return (LegendItem) this.items.get(index); } /** * Returns the number of legend items in the collection. * * @return The item count. */ public int getItemCount() { return this.items.size(); } /** * Returns an iterator that provides access to all the legend items. * * @return An iterator. */ public Iterator iterator() { return this.items.iterator(); } /** * Tests this collection for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof LegendItemCollection)) { return false; } LegendItemCollection that = (LegendItemCollection) obj; if (!Objects.equals(this.items, that.items)) { return false; } return true; } @Override public int hashCode() { int hash = 7; hash = 13 * hash + Objects.hashCode(this.items); return hash; } /** * Returns a clone of the collection. * * @return A clone. * * @throws CloneNotSupportedException if an item in the collection is not * cloneable. */ @Override public Object clone() throws CloneNotSupportedException { LegendItemCollection clone = (LegendItemCollection) super.clone(); clone.items = (List) ObjectUtils.deepClone(this.items); return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/LegendItemSource.java000066400000000000000000000035711463604235500271250ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * LegendItemSource.java * --------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart; /** * A source of legend items. A {@link org.jfree.chart.title.LegendTitle} will * maintain a list of sources (often just one) from which it obtains legend * items. */ public interface LegendItemSource { /** * Returns a (possibly empty) collection of legend items. * * @return The legend item collection (never {@code null}). */ LegendItemCollection getLegendItems(); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/LegendRenderingOrder.java000066400000000000000000000071451463604235500277600ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * LegendRenderingOrder.java * ------------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: Angel; * Contributor(s): David Gilbert; * */ package org.jfree.chart; import java.io.ObjectStreamException; import java.io.Serializable; /** * Represents the order for rendering legend items. */ public final class LegendRenderingOrder implements Serializable { /** For serialization. */ private static final long serialVersionUID = -3832486612685808616L; /** In order. */ public static final LegendRenderingOrder STANDARD = new LegendRenderingOrder("LegendRenderingOrder.STANDARD"); /** In reverse order. */ public static final LegendRenderingOrder REVERSE = new LegendRenderingOrder("LegendRenderingOrder.REVERSE"); /** The name. */ private String name; /** * Private constructor. * * @param name the name. */ private LegendRenderingOrder(String name) { this.name = name; } /** * Returns a string representing the object. * * @return The string. */ @Override public String toString() { return this.name; } /** * Returns {@code true} if this object is equal to the specified * object, and {@code false} otherwise. * * @param obj the other object. * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof LegendRenderingOrder)) { return false; } LegendRenderingOrder order = (LegendRenderingOrder) obj; if (!this.name.equals(order.toString())) { return false; } return true; } /** * Ensures that serialization returns the unique instances. * * @return The object. * * @throws ObjectStreamException if there is a problem. */ private Object readResolve() throws ObjectStreamException { if (this.equals(LegendRenderingOrder.STANDARD)) { return LegendRenderingOrder.STANDARD; } else if (this.equals(LegendRenderingOrder.REVERSE)) { return LegendRenderingOrder.REVERSE; } return null; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/MouseWheelHandler.java000066400000000000000000000115411463604235500272760ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * MouseWheelHandler.java * ---------------------- * (C) Copyright 2009-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Ulrich Voigt - patch 2686040; * Jim Goodwin - bug fix; * */ package org.jfree.chart; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; import java.awt.geom.Point2D; import java.io.Serializable; import org.jfree.chart.plot.PiePlot; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.Zoomable; /** * A class that handles mouse wheel events for the {@link ChartPanel} class. */ public class MouseWheelHandler implements MouseWheelListener, Serializable { /** The chart panel. */ private ChartPanel chartPanel; /** The zoom factor. */ double zoomFactor; /** * Creates a new instance for the specified chart panel. * * @param chartPanel the chart panel ({@code null} not permitted). */ public MouseWheelHandler(ChartPanel chartPanel) { this.chartPanel = chartPanel; this.zoomFactor = 0.10; this.chartPanel.addMouseWheelListener(this); } /** * Returns the current zoom factor. The default value is 0.10 (ten * percent). * * @return The zoom factor. * * @see #setZoomFactor(double) */ public double getZoomFactor() { return this.zoomFactor; } /** * Sets the zoom factor. * * @param zoomFactor the zoom factor. * * @see #getZoomFactor() */ public void setZoomFactor(double zoomFactor) { this.zoomFactor = zoomFactor; } /** * Handles a mouse wheel event from the underlying chart panel. * * @param e the event. */ @Override public void mouseWheelMoved(MouseWheelEvent e) { JFreeChart chart = this.chartPanel.getChart(); if (chart == null) { return; } Plot plot = chart.getPlot(); if (plot instanceof Zoomable) { Zoomable zoomable = (Zoomable) plot; handleZoomable(zoomable, e); } else if (plot instanceof PiePlot) { PiePlot pp = (PiePlot) plot; pp.handleMouseWheelRotation(e.getWheelRotation()); } } /** * Handle the case where a plot implements the {@link Zoomable} interface. * * @param zoomable the zoomable plot. * @param e the mouse wheel event. */ private void handleZoomable(Zoomable zoomable, MouseWheelEvent e) { // don't zoom unless the mouse pointer is in the plot's data area ChartRenderingInfo info = this.chartPanel.getChartRenderingInfo(); PlotRenderingInfo pinfo = info.getPlotInfo(); Point2D p = this.chartPanel.translateScreenToJava2D(e.getPoint()); if (!pinfo.getDataArea().contains(p)) { return; } Plot plot = (Plot) zoomable; // do not notify while zooming each axis boolean notifyState = plot.isNotify(); plot.setNotify(false); int clicks = e.getWheelRotation(); double zf = 1.0 + this.zoomFactor; if (clicks < 0) { zf = 1.0 / zf; } if (chartPanel.isDomainZoomable()) { zoomable.zoomDomainAxes(zf, pinfo, p, true); } if (chartPanel.isRangeZoomable()) { zoomable.zoomRangeAxes(zf, pinfo, p, true); } plot.setNotify(notifyState); // this generates the change event too } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/OfflineRenderingChartPanel.java000066400000000000000000000442371463604235500311150ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------- * OfflineRenderingChartPanel.java * ------------------------------- * (C) Copyright 2000-present, by Yuri Blankenstein and Contributors. * * Original Author: Yuri Blankenstein; */ package org.jfree.chart; import java.awt.AlphaComposite; import java.awt.Container; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; import java.awt.Rectangle; import java.awt.Transparency; import java.awt.geom.Point2D; import java.awt.image.BufferedImage; import javax.swing.SwingWorker; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.plot.PlotRenderingInfo; /** * A {@link ChartPanel} that applies offline rendering, for better performance * when navigating (i.e. panning / zooming) {@link JFreeChart charts} with lots * of data. *

* This chart panel uses a {@link SwingWorker} to perform the actual * {@link JFreeChart} rendering. While rendering, a {@link Cursor#WAIT_CURSOR * wait cursor} is visible and the current buffered image of the chart will be * scaled and drawn to the screen. When - while rendering - another * {@link #setRefreshBuffer(boolean) refresh} is requested, this will be either * postponed until the current rendering is done or ignored when another refresh * is requested. */ public class OfflineRenderingChartPanel extends ChartPanel { private static final long serialVersionUID = -724633596883320084L; /** * Using enum state pattern to control the 'offline' rendering */ protected enum State { IDLE { @Override protected State renderOffline(OfflineRenderingChartPanel panel, OfflineChartRenderer renderer) { // Start rendering offline renderer.execute(); panel.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); return RENDERING; } @Override protected State offlineRenderingDone( OfflineRenderingChartPanel panel, OfflineChartRenderer renderer) { throw new IllegalStateException( "offlineRenderingDone not expected in IDLE state"); } }, RENDERING { @Override protected State renderOffline(OfflineRenderingChartPanel panel, OfflineChartRenderer renderer) { // We're already rendering, we'll start this renderer when we're // finished. If another rendering is requested, this one will be // ignored, see RE_RENDERING_PENDING. This gains a lot of speed // as not all requested (intermediate) renderings are executed // for large plots. panel.pendingOfflineRenderer = renderer; return RE_RENDERING_PENDING; } @Override protected State offlineRenderingDone( OfflineRenderingChartPanel panel, OfflineChartRenderer renderer) { // Offline rendering done, prepare the buffer and info for the // next repaint and request it. panel.currentChartBuffer = renderer.buffer; panel.currentChartRenderingInfo = renderer.info; panel.repaint(); panel.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); return IDLE; } }, RE_RENDERING_PENDING { @Override protected State renderOffline(OfflineRenderingChartPanel panel, OfflineChartRenderer renderer) { // We're already rendering, we'll start this renderer when we're // finished. panel.pendingOfflineRenderer = renderer; return RE_RENDERING_PENDING; } @Override protected State offlineRenderingDone( OfflineRenderingChartPanel panel, OfflineChartRenderer renderer) { // Store the intermediate result, but do not actively repaint // as this could trigger another RE_RENDERING_PENDING if i.e. // the buffer-image-size of the pending renderer differs from // the current buffer-image-size. panel.currentChartBuffer = renderer.buffer; panel.currentChartRenderingInfo = renderer.info; // Immediately start rendering again to update the chart to the // latest requested state. panel.pendingOfflineRenderer.execute(); panel.pendingOfflineRenderer = null; return RENDERING; } }; protected abstract State renderOffline( final OfflineRenderingChartPanel panel, final OfflineChartRenderer renderer); protected abstract State offlineRenderingDone( final OfflineRenderingChartPanel panel, final OfflineChartRenderer renderer); } /** A buffer for the rendered chart. */ private transient BufferedImage currentChartBuffer = null; private transient ChartRenderingInfo currentChartRenderingInfo = null; /** A pending rendering for the chart. */ private transient OfflineChartRenderer pendingOfflineRenderer = null; private State state = State.IDLE; /** * Constructs a double buffered JFreeChart panel that displays the specified * chart. * * @param chart the chart. */ public OfflineRenderingChartPanel(JFreeChart chart) { this(chart, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_MINIMUM_DRAW_WIDTH, DEFAULT_MINIMUM_DRAW_HEIGHT, DEFAULT_MAXIMUM_DRAW_WIDTH, DEFAULT_MAXIMUM_DRAW_HEIGHT, true, // properties true, // save true, // print true, // zoom true // tooltips ); } /** * Constructs a double buffered JFreeChart panel. * * @param chart the chart. * @param properties a flag indicating whether or not the chart property * editor should be available via the popup menu. * @param save a flag indicating whether or not save options should be * available via the popup menu. * @param print a flag indicating whether or not the print option * should be available via the popup menu. * @param zoom a flag indicating whether or not zoom options should be * added to the popup menu. * @param tooltips a flag indicating whether or not tooltips should be * enabled for the chart. */ public OfflineRenderingChartPanel(JFreeChart chart, boolean properties, boolean save, boolean print, boolean zoom, boolean tooltips) { this(chart, DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_MINIMUM_DRAW_WIDTH, DEFAULT_MINIMUM_DRAW_HEIGHT, DEFAULT_MAXIMUM_DRAW_WIDTH, DEFAULT_MAXIMUM_DRAW_HEIGHT, properties, save, print, zoom, tooltips); } /** * Constructs a double buffered JFreeChart panel. * * @param chart the chart. * @param width the preferred width of the panel. * @param height the preferred height of the panel. * @param minimumDrawWidth the minimum drawing width. * @param minimumDrawHeight the minimum drawing height. * @param maximumDrawWidth the maximum drawing width. * @param maximumDrawHeight the maximum drawing height. * @param properties a flag indicating whether or not the chart * property editor should be available via the * popup menu. * @param save a flag indicating whether or not save options * should be available via the popup menu. * @param print a flag indicating whether or not the print * option should be available via the popup menu. * @param zoom a flag indicating whether or not zoom options * should be added to the popup menu. * @param tooltips a flag indicating whether or not tooltips should * be enabled for the chart. */ public OfflineRenderingChartPanel(JFreeChart chart, int width, int height, int minimumDrawWidth, int minimumDrawHeight, int maximumDrawWidth, int maximumDrawHeight, boolean properties, boolean save, boolean print, boolean zoom, boolean tooltips) { this(chart, width, height, minimumDrawWidth, minimumDrawHeight, maximumDrawWidth, maximumDrawHeight, properties, true, save, print, zoom, tooltips); } /** * Constructs a double buffered JFreeChart panel. * * @param chart the chart. * @param width the preferred width of the panel. * @param height the preferred height of the panel. * @param minimumDrawWidth the minimum drawing width. * @param minimumDrawHeight the minimum drawing height. * @param maximumDrawWidth the maximum drawing width. * @param maximumDrawHeight the maximum drawing height. * @param properties a flag indicating whether or not the chart * property editor should be available via the * popup menu. * @param copy a flag indicating whether or not a copy option * should be available via the popup menu. * @param save a flag indicating whether or not save options * should be available via the popup menu. * @param print a flag indicating whether or not the print * option should be available via the popup menu. * @param zoom a flag indicating whether or not zoom options * should be added to the popup menu. * @param tooltips a flag indicating whether or not tooltips should * be enabled for the chart. */ public OfflineRenderingChartPanel(JFreeChart chart, int width, int height, int minimumDrawWidth, int minimumDrawHeight, int maximumDrawWidth, int maximumDrawHeight, boolean properties, boolean copy, boolean save, boolean print, boolean zoom, boolean tooltips) { super(chart, width, height, minimumDrawWidth, minimumDrawHeight, maximumDrawWidth, maximumDrawHeight, true, properties, copy, save, print, zoom, tooltips); } @Override protected BufferedImage paintChartToBuffer(Graphics2D g2, Dimension bufferSize, Dimension chartSize, Point2D anchor, ChartRenderingInfo info) { synchronized (state) { if (this.currentChartBuffer == null) { // Rendering the first time, prepare an empty buffer and // start rendering, no need for an additional state this.currentChartBuffer = createChartBuffer(g2, bufferSize); clearChartBuffer(currentChartBuffer); setRefreshBuffer(true); } else if ((this.currentChartBuffer.getWidth() != bufferSize.width) || (this.currentChartBuffer .getHeight() != bufferSize.height)) { setRefreshBuffer(true); } // do we need to redraw the buffer? if (getRefreshBuffer()) { setRefreshBuffer(false); // clear the flag // Rendering is done offline, hence it requires a fresh buffer // and rendering info BufferedImage rendererBuffer = createChartBuffer(g2, bufferSize); ChartRenderingInfo rendererInfo = info; if (rendererInfo != null) { // As the chart will be re-rendered, the current chart // entities cannot be trusted final EntityCollection entityCollection = rendererInfo.getEntityCollection(); if (entityCollection != null) { entityCollection.clear(); } // Offline rendering requires its own instance of // ChartRenderingInfo, using clone if possible try { rendererInfo = rendererInfo.clone(); } catch (CloneNotSupportedException e) { // Not expected e.printStackTrace(); rendererInfo = new ChartRenderingInfo(); } } OfflineChartRenderer offlineRenderer = new OfflineChartRenderer( getChart(), rendererBuffer, chartSize, anchor, rendererInfo); state = state.renderOffline(this, offlineRenderer); } // Copy the rendered ChartRenderingInfo into the passed info // argument and mark that we have done so. copyChartRenderingInfo(this.currentChartRenderingInfo, info); this.currentChartRenderingInfo = info; return this.currentChartBuffer; } } private class OfflineChartRenderer extends SwingWorker { private final JFreeChart chart; private final BufferedImage buffer; private final Dimension chartSize; private final Point2D anchor; private final ChartRenderingInfo info; public OfflineChartRenderer(JFreeChart chart, BufferedImage image, Dimension chartSize, Point2D anchor, ChartRenderingInfo info) { this.chart = chart; this.buffer = image; this.chartSize = chartSize; this.anchor = anchor; this.info = info; } @Override protected Object doInBackground() throws Exception { clearChartBuffer(buffer); Graphics2D bufferG2 = buffer.createGraphics(); if ((this.buffer.getWidth() != this.chartSize.width) || (this.buffer.getHeight() != this.chartSize.height)) { // Scale the chart to fit the buffer bufferG2.scale( this.buffer.getWidth() / this.chartSize.getWidth(), this.buffer.getHeight() / this.chartSize.getHeight()); } Rectangle chartArea = new Rectangle(this.chartSize); this.chart.draw(bufferG2, chartArea, this.anchor, this.info); bufferG2.dispose(); // Return type is not used return null; } @Override protected void done() { synchronized (state) { state = state.offlineRenderingDone( OfflineRenderingChartPanel.this, this); } } } @Override public void setCursor(Cursor cursor) { super.setCursor(cursor); // Buggy mouse cursor: setting both behaves as expected Container root = getTopLevelAncestor(); if (null != root) { root.setCursor(cursor); } } private static void copyChartRenderingInfo(ChartRenderingInfo source, ChartRenderingInfo target) { if (source == null || target == null || source == target) { // Nothing to do return; } target.clear(); target.setChartArea(source.getChartArea()); target.setEntityCollection(source.getEntityCollection()); copyPlotRenderingInfo(source.getPlotInfo(), target.getPlotInfo()); } private static void copyPlotRenderingInfo(PlotRenderingInfo source, PlotRenderingInfo target) { target.setDataArea(source.getDataArea()); target.setPlotArea(source.getPlotArea()); for (int i = 0; i < target.getSubplotCount(); i++) { PlotRenderingInfo subSource = source.getSubplotInfo(i); PlotRenderingInfo subTarget = new PlotRenderingInfo( target.getOwner()); copyPlotRenderingInfo(subSource, subTarget); target.addSubplotInfo(subTarget); } } private static BufferedImage createChartBuffer(Graphics2D g2, Dimension bufferSize) { GraphicsConfiguration gc = g2.getDeviceConfiguration(); return gc.createCompatibleImage(bufferSize.width, bufferSize.height, Transparency.TRANSLUCENT); } private static void clearChartBuffer(BufferedImage buffer) { Graphics2D bufferG2 = buffer.createGraphics(); // make the background of the buffer clear and transparent bufferG2.setComposite(AlphaComposite.getInstance(AlphaComposite.CLEAR, 0.0f)); bufferG2.fill(new Rectangle(buffer.getWidth(), buffer.getHeight())); bufferG2.dispose(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/PaintMap.java000066400000000000000000000151711463604235500254370ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------- * PaintMap.java * ------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart; import java.awt.Paint; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.SerialUtils; /** * A storage structure that maps {@code Comparable} instances with * {@code Paint} instances. *

* To support cloning and serialization, you should only use keys that are * cloneable and serializable. Special handling for the {@code Paint} * instances is included in this class. */ public class PaintMap implements Cloneable, Serializable { /** For serialization. */ static final long serialVersionUID = -4639833772123069274L; /** Storage for the keys and values. */ private transient Map store; /** * Creates a new (empty) map. */ public PaintMap() { this.store = new HashMap(); } /** * Returns the paint associated with the specified key, or * {@code null}. * * @param key the key ({@code null} not permitted). * * @return The paint, or {@code null}. * * @throws IllegalArgumentException if {@code key} is * {@code null}. */ public Paint getPaint(Comparable key) { Args.nullNotPermitted(key, "key"); return (Paint) this.store.get(key); } /** * Returns {@code true} if the map contains the specified key, and * {@code false} otherwise. * * @param key the key. * * @return {@code true} if the map contains the specified key, and * {@code false} otherwise. */ public boolean containsKey(Comparable key) { return this.store.containsKey(key); } /** * Adds a mapping between the specified {@code key} and * {@code Paint} values. * * @param key the key ({@code null} not permitted). * @param paint the paint. * * @throws IllegalArgumentException if {@code key} is * {@code null}. */ public void put(Comparable key, Paint paint) { Args.nullNotPermitted(key, "key"); this.store.put(key, paint); } /** * Resets the map to empty. */ public void clear() { this.store.clear(); } /** * Tests this map for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof PaintMap)) { return false; } PaintMap that = (PaintMap) obj; if (this.store.size() != that.store.size()) { return false; } Set keys = this.store.keySet(); Iterator iterator = keys.iterator(); while (iterator.hasNext()) { Comparable key = (Comparable) iterator.next(); Paint p1 = getPaint(key); Paint p2 = that.getPaint(key); if (!PaintUtils.equal(p1, p2)) { return false; } } return true; } /** * Returns a clone of this {@code PaintMap}. * * @return A clone of this instance. * * @throws CloneNotSupportedException if any key is not cloneable. */ @Override public Object clone() throws CloneNotSupportedException { PaintMap clone = (PaintMap) super.clone(); clone.store = new HashMap(); clone.store.putAll(this.store); // TODO: I think we need to make sure the keys are actually cloned, // whereas the paint instances are always immutable so they're OK return clone; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); stream.writeInt(this.store.size()); Set keys = this.store.keySet(); Iterator iterator = keys.iterator(); while (iterator.hasNext()) { Comparable key = (Comparable) iterator.next(); stream.writeObject(key); Paint paint = getPaint(key); SerialUtils.writePaint(paint, stream); } } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.store = new HashMap(); int keyCount = stream.readInt(); for (int i = 0; i < keyCount; i++) { Comparable key = (Comparable) stream.readObject(); Paint paint = SerialUtils.readPaint(stream); this.store.put(key, paint); } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/PolarChartPanel.java000066400000000000000000000203221463604235500267370ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * PolarChartPanel.java * -------------------- * (C) Copyright 2004-present, by Solution Engineering, Inc. and Contributors. * * Original Author: Daniel Bridenbecker, Solution Engineering, Inc.; * Contributor(s): David Gilbert; * Martin Hoeller; * */ package org.jfree.chart; import java.awt.Component; import java.awt.event.ActionEvent; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PolarPlot; /** * {@code PolarChartPanel} is the top level object for using the * {@link PolarPlot}. Since this class has a {@code JPanel} in the * inheritance hierarchy, one uses this class to integrate the Polar plot into * their application. *

* The main modification to {@code ChartPanel} is the popup menu. It * removes {@code ChartPanel}'s versions of: *

    *
  • {@code Zoom In}
  • *
  • {@code Zoom Out}
  • *
  • {@code Auto Range}
  • *
* and replaces them with versions more appropriate for {@link PolarPlot}. */ public class PolarChartPanel extends ChartPanel { // ----------------- // --- Constants --- // ----------------- /** Zoom in command string. */ private static final String POLAR_ZOOM_IN_ACTION_COMMAND = "Polar Zoom In"; /** Zoom out command string. */ private static final String POLAR_ZOOM_OUT_ACTION_COMMAND = "Polar Zoom Out"; /** Auto range command string. */ private static final String POLAR_AUTO_RANGE_ACTION_COMMAND = "Polar Auto Range"; // ------------------------ // --- Member Variables --- // ------------------------ // -------------------- // --- Constructors --- // -------------------- /** * Constructs a JFreeChart panel. * * @param chart the chart. */ public PolarChartPanel(JFreeChart chart) { this(chart, true); } /** * Creates a new panel. * * @param chart the chart. * @param useBuffer buffered? */ public PolarChartPanel(JFreeChart chart, boolean useBuffer) { super(chart, useBuffer); checkChart(chart); setMinimumDrawWidth(200); setMinimumDrawHeight(200); setMaximumDrawWidth(2000); setMaximumDrawHeight(2000); } // -------------------------- // --- ChartPanel Methods --- // -------------------------- /** * Sets the chart that is displayed in the panel. * * @param chart The chart. */ @Override public void setChart(JFreeChart chart) { checkChart(chart); super.setChart(chart); } /** * Creates a popup menu for the panel. * * @param properties include a menu item for the chart property editor. * @param save include a menu item for saving the chart. * @param print include a menu item for printing the chart. * @param zoom include menu items for zooming. * * @return The popup menu. */ @Override protected JPopupMenu createPopupMenu(boolean properties, boolean save, boolean print, boolean zoom) { JPopupMenu result = super.createPopupMenu(properties, save, print, zoom); int zoomInIndex = getPopupMenuItem(result, localizationResources.getString("Zoom_In")); int zoomOutIndex = getPopupMenuItem(result, localizationResources.getString("Zoom_Out")); int autoIndex = getPopupMenuItem(result, localizationResources.getString("Auto_Range")); if (zoom) { JMenuItem zoomIn = new JMenuItem( localizationResources.getString("Zoom_In")); zoomIn.setActionCommand(POLAR_ZOOM_IN_ACTION_COMMAND); zoomIn.addActionListener(this); JMenuItem zoomOut = new JMenuItem( localizationResources.getString("Zoom_Out")); zoomOut.setActionCommand(POLAR_ZOOM_OUT_ACTION_COMMAND); zoomOut.addActionListener(this); JMenuItem auto = new JMenuItem( localizationResources.getString("Auto_Range")); auto.setActionCommand(POLAR_AUTO_RANGE_ACTION_COMMAND); auto.addActionListener(this); if (zoomInIndex != -1) { result.remove(zoomInIndex); } else { zoomInIndex = result.getComponentCount() - 1; } result.add(zoomIn, zoomInIndex); if (zoomOutIndex != -1) { result.remove(zoomOutIndex); } else { zoomOutIndex = zoomInIndex + 1; } result.add(zoomOut, zoomOutIndex); if (autoIndex != -1) { result.remove(autoIndex); } else { autoIndex = zoomOutIndex + 1; } result.add(auto, autoIndex); } return result; } /** * Handles action events generated by the popup menu. * * @param event the event. */ @Override public void actionPerformed(ActionEvent event) { String command = event.getActionCommand(); if (command.equals(POLAR_ZOOM_IN_ACTION_COMMAND)) { PolarPlot plot = (PolarPlot) getChart().getPlot(); plot.zoom(0.5); } else if (command.equals(POLAR_ZOOM_OUT_ACTION_COMMAND)) { PolarPlot plot = (PolarPlot) getChart().getPlot(); plot.zoom(2.0); } else if (command.equals(POLAR_AUTO_RANGE_ACTION_COMMAND)) { PolarPlot plot = (PolarPlot) getChart().getPlot(); plot.getAxis().setAutoRange(true); } else { super.actionPerformed(event); } } // ---------------------- // --- Public Methods --- // ---------------------- // ----------------------- // --- Private Methods --- // ----------------------- /** * Test that the chart is using an xy plot with time as the domain axis. * * @param chart the chart. */ private void checkChart(JFreeChart chart) { Plot plot = chart.getPlot(); if (!(plot instanceof PolarPlot)) { throw new IllegalArgumentException("plot is not a PolarPlot"); } } /** * Returns the index of an item in a popup menu. * * @param menu the menu. * @param text the label. * * @return The item index. */ private int getPopupMenuItem(JPopupMenu menu, String text) { int index = -1; for (int i = 0; (index == -1) && (i < menu.getComponentCount()); i++) { Component comp = menu.getComponent(i); if (comp instanceof JMenuItem) { JMenuItem item = (JMenuItem) comp; if (text.equals(item.getText())) { index = i; } } } return index; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/StandardChartTheme.java000066400000000000000000001613331463604235500274350ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * StandardChartTheme.java * ----------------------- * (C) Copyright 2008-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.Paint; import java.awt.Stroke; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Iterator; import java.util.List; import java.util.Objects; import org.jfree.chart.annotations.XYAnnotation; import org.jfree.chart.annotations.XYTextAnnotation; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.PeriodAxis; import org.jfree.chart.axis.PeriodAxisLabelInfo; import org.jfree.chart.axis.SubCategoryAxis; import org.jfree.chart.axis.SymbolAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.block.Block; import org.jfree.chart.block.BlockContainer; import org.jfree.chart.block.LabelBlock; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.CombinedDomainCategoryPlot; import org.jfree.chart.plot.CombinedDomainXYPlot; import org.jfree.chart.plot.CombinedRangeCategoryPlot; import org.jfree.chart.plot.CombinedRangeXYPlot; import org.jfree.chart.plot.DefaultDrawingSupplier; import org.jfree.chart.plot.DrawingSupplier; import org.jfree.chart.plot.FastScatterPlot; import org.jfree.chart.plot.MeterPlot; import org.jfree.chart.plot.MultiplePiePlot; import org.jfree.chart.plot.PieLabelLinkStyle; import org.jfree.chart.plot.PiePlot; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PolarPlot; import org.jfree.chart.plot.SpiderWebPlot; import org.jfree.chart.plot.ThermometerPlot; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.AbstractRenderer; import org.jfree.chart.renderer.category.BarPainter; import org.jfree.chart.renderer.category.BarRenderer; import org.jfree.chart.renderer.category.CategoryItemRenderer; import org.jfree.chart.renderer.category.GradientBarPainter; import org.jfree.chart.renderer.category.MinMaxCategoryRenderer; import org.jfree.chart.renderer.category.StatisticalBarRenderer; import org.jfree.chart.renderer.xy.GradientXYBarPainter; import org.jfree.chart.renderer.xy.XYBarPainter; import org.jfree.chart.renderer.xy.XYBarRenderer; import org.jfree.chart.renderer.xy.XYItemRenderer; import org.jfree.chart.title.CompositeTitle; import org.jfree.chart.title.LegendTitle; import org.jfree.chart.title.PaintScaleLegend; import org.jfree.chart.title.TextTitle; import org.jfree.chart.title.Title; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.util.DefaultShadowGenerator; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; import org.jfree.chart.util.ShadowGenerator; /** * A default implementation of the {@link ChartTheme} interface. This * implementation just collects a whole bunch of chart attributes and mimics * the manual process of applying each attribute to the right sub-object * within the JFreeChart instance. It's not elegant code, but it works. */ public class StandardChartTheme implements ChartTheme, Cloneable, PublicCloneable, Serializable { /** The name of this theme. */ private String name; /** * The largest font size. Use for the main chart title. */ private Font extraLargeFont; /** * A large font. Used for subtitles. */ private Font largeFont; /** * The regular font size. Used for axis tick labels, legend items etc. */ private Font regularFont; /** * The small font size. */ private Font smallFont; /** The paint used to display the main chart title. */ private transient Paint titlePaint; /** The paint used to display subtitles. */ private transient Paint subtitlePaint; /** The background paint for the chart. */ private transient Paint chartBackgroundPaint; /** The legend background paint. */ private transient Paint legendBackgroundPaint; /** The legend item paint. */ private transient Paint legendItemPaint; /** The drawing supplier. */ private DrawingSupplier drawingSupplier; /** The background paint for the plot. */ private transient Paint plotBackgroundPaint; /** The plot outline paint. */ private transient Paint plotOutlinePaint; /** The label link style for pie charts. */ private PieLabelLinkStyle labelLinkStyle; /** The label link paint for pie charts. */ private transient Paint labelLinkPaint; /** The domain grid line paint. */ private transient Paint domainGridlinePaint; /** The range grid line paint. */ private transient Paint rangeGridlinePaint; /* The baseline paint (used for domain and range zero baselines). */ private transient Paint baselinePaint; /** The crosshair paint. */ private transient Paint crosshairPaint; /** The axis offsets. */ private RectangleInsets axisOffset; /** The axis label paint. */ private transient Paint axisLabelPaint; /** The tick label paint. */ private transient Paint tickLabelPaint; /** The item label paint. */ private transient Paint itemLabelPaint; /** * A flag that controls whether or not shadows are visible (for example, * in a bar renderer). */ private boolean shadowVisible; /** The shadow paint. */ private transient Paint shadowPaint; /** The bar painter. */ private BarPainter barPainter; /** The XY bar painter. */ private XYBarPainter xyBarPainter; /** The thermometer paint. */ private transient Paint thermometerPaint; /** The error indicator paint for the {@link StatisticalBarRenderer}. */ private transient Paint errorIndicatorPaint; /** The grid band paint for a {@link SymbolAxis}. */ private transient Paint gridBandPaint = SymbolAxis.DEFAULT_GRID_BAND_PAINT; /** The grid band alternate paint for a {@link SymbolAxis}. */ private transient Paint gridBandAlternatePaint = SymbolAxis.DEFAULT_GRID_BAND_ALTERNATE_PAINT; /* The shadow generator (can be null). */ private ShadowGenerator shadowGenerator; /** * Creates and returns the default 'JFree' chart theme. * * @return A chart theme. */ public static ChartTheme createJFreeTheme() { return new StandardChartTheme("JFree"); } /** * Creates and returns a theme called "Darkness". In this theme, the * charts have a black background. * * @return The "Darkness" theme. */ public static ChartTheme createDarknessTheme() { StandardChartTheme theme = new StandardChartTheme("Darkness"); theme.titlePaint = Color.WHITE; theme.subtitlePaint = Color.WHITE; theme.legendBackgroundPaint = Color.BLACK; theme.legendItemPaint = Color.WHITE; theme.chartBackgroundPaint = Color.BLACK; theme.plotBackgroundPaint = Color.BLACK; theme.plotOutlinePaint = Color.YELLOW; theme.baselinePaint = Color.WHITE; theme.crosshairPaint = Color.RED; theme.labelLinkPaint = Color.LIGHT_GRAY; theme.tickLabelPaint = Color.WHITE; theme.axisLabelPaint = Color.WHITE; theme.shadowPaint = Color.DARK_GRAY; theme.itemLabelPaint = Color.WHITE; theme.drawingSupplier = new DefaultDrawingSupplier( new Paint[] {Color.decode("0xFFFF00"), Color.decode("0x0036CC"), Color.decode("0xFF0000"), Color.decode("0xFFFF7F"), Color.decode("0x6681CC"), Color.decode("0xFF7F7F"), Color.decode("0xFFFFBF"), Color.decode("0x99A6CC"), Color.decode("0xFFBFBF"), Color.decode("0xA9A938"), Color.decode("0x2D4587")}, new Paint[] {Color.decode("0xFFFF00"), Color.decode("0x0036CC")}, new Stroke[] {new BasicStroke(2.0f)}, new Stroke[] {new BasicStroke(0.5f)}, DefaultDrawingSupplier.DEFAULT_SHAPE_SEQUENCE); theme.errorIndicatorPaint = Color.LIGHT_GRAY; theme.gridBandPaint = new Color(255, 255, 255, 20); theme.gridBandAlternatePaint = new Color(255, 255, 255, 40); theme.shadowGenerator = null; return theme; } /** * Creates and returns a {@link ChartTheme} that doesn't apply any changes * to the JFreeChart defaults. This produces the "legacy" look for * JFreeChart. * * @return A legacy theme. */ public static ChartTheme createLegacyTheme() { StandardChartTheme theme = new StandardChartTheme("Legacy") { @Override public void apply(JFreeChart chart) { // do nothing at all } }; return theme; } /** * Creates a new default instance. * * @param name the name of the theme ({@code null} not permitted). */ public StandardChartTheme(String name) { this(name, false); } /** * Creates a new default instance. * * @param name the name of the theme ({@code null} not permitted). * @param shadow a flag that controls whether a shadow generator is * included. */ public StandardChartTheme(String name, boolean shadow) { Args.nullNotPermitted(name, "name"); this.name = name; this.extraLargeFont = new Font("Tahoma", Font.BOLD, 20); this.largeFont = new Font("Tahoma", Font.BOLD, 14); this.regularFont = new Font("Tahoma", Font.PLAIN, 12); this.smallFont = new Font("Tahoma", Font.PLAIN, 10); this.titlePaint = Color.BLACK; this.subtitlePaint = Color.BLACK; this.legendBackgroundPaint = Color.WHITE; this.legendItemPaint = Color.DARK_GRAY; this.chartBackgroundPaint = Color.WHITE; this.drawingSupplier = new DefaultDrawingSupplier(); this.plotBackgroundPaint = Color.LIGHT_GRAY; this.plotOutlinePaint = Color.BLACK; this.labelLinkPaint = Color.BLACK; this.labelLinkStyle = PieLabelLinkStyle.CUBIC_CURVE; this.axisOffset = new RectangleInsets(4, 4, 4, 4); this.domainGridlinePaint = Color.WHITE; this.rangeGridlinePaint = Color.WHITE; this.baselinePaint = Color.BLACK; this.crosshairPaint = Color.BLUE; this.axisLabelPaint = Color.DARK_GRAY; this.tickLabelPaint = Color.DARK_GRAY; this.barPainter = new GradientBarPainter(); this.xyBarPainter = new GradientXYBarPainter(); this.shadowVisible = false; this.shadowPaint = Color.GRAY; this.itemLabelPaint = Color.BLACK; this.thermometerPaint = Color.WHITE; this.errorIndicatorPaint = Color.BLACK; this.shadowGenerator = shadow ? new DefaultShadowGenerator() : null; } /** * Returns the largest font for this theme. * * @return The largest font for this theme. * * @see #setExtraLargeFont(Font) */ public Font getExtraLargeFont() { return this.extraLargeFont; } /** * Sets the largest font for this theme. * * @param font the font ({@code null} not permitted). * * @see #getExtraLargeFont() */ public void setExtraLargeFont(Font font) { Args.nullNotPermitted(font, "font"); this.extraLargeFont = font; } /** * Returns the large font for this theme. * * @return The large font (never {@code null}). * * @see #setLargeFont(Font) */ public Font getLargeFont() { return this.largeFont; } /** * Sets the large font for this theme. * * @param font the font ({@code null} not permitted). * * @see #getLargeFont() */ public void setLargeFont(Font font) { Args.nullNotPermitted(font, "font"); this.largeFont = font; } /** * Returns the regular font. * * @return The regular font (never {@code null}). * * @see #setRegularFont(Font) */ public Font getRegularFont() { return this.regularFont; } /** * Sets the regular font for this theme. * * @param font the font ({@code null} not permitted). * * @see #getRegularFont() */ public void setRegularFont(Font font) { Args.nullNotPermitted(font, "font"); this.regularFont = font; } /** * Returns the small font. * * @return The small font (never {@code null}). * * @see #setSmallFont(Font) */ public Font getSmallFont() { return this.smallFont; } /** * Sets the small font for this theme. * * @param font the font ({@code null} not permitted). * * @see #getSmallFont() */ public void setSmallFont(Font font) { Args.nullNotPermitted(font, "font"); this.smallFont = font; } /** * Returns the title paint. * * @return The title paint (never {@code null}). * * @see #setTitlePaint(Paint) */ public Paint getTitlePaint() { return this.titlePaint; } /** * Sets the title paint. * * @param paint the paint ({@code null} not permitted). * * @see #getTitlePaint() */ public void setTitlePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.titlePaint = paint; } /** * Returns the subtitle paint. * * @return The subtitle paint (never {@code null}). * * @see #setSubtitlePaint(Paint) */ public Paint getSubtitlePaint() { return this.subtitlePaint; } /** * Sets the subtitle paint. * * @param paint the paint ({@code null} not permitted). * * @see #getSubtitlePaint() */ public void setSubtitlePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.subtitlePaint = paint; } /** * Returns the chart background paint. * * @return The chart background paint (never {@code null}). * * @see #setChartBackgroundPaint(Paint) */ public Paint getChartBackgroundPaint() { return this.chartBackgroundPaint; } /** * Sets the chart background paint. * * @param paint the paint ({@code null} not permitted). * * @see #getChartBackgroundPaint() */ public void setChartBackgroundPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.chartBackgroundPaint = paint; } /** * Returns the legend background paint. * * @return The legend background paint (never {@code null}). * * @see #setLegendBackgroundPaint(Paint) */ public Paint getLegendBackgroundPaint() { return this.legendBackgroundPaint; } /** * Sets the legend background paint. * * @param paint the paint ({@code null} not permitted). * * @see #getLegendBackgroundPaint() */ public void setLegendBackgroundPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.legendBackgroundPaint = paint; } /** * Returns the legend item paint. * * @return The legend item paint (never {@code null}). * * @see #setLegendItemPaint(Paint) */ public Paint getLegendItemPaint() { return this.legendItemPaint; } /** * Sets the legend item paint. * * @param paint the paint ({@code null} not permitted). * * @see #getLegendItemPaint() */ public void setLegendItemPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.legendItemPaint = paint; } /** * Returns the plot background paint. * * @return The plot background paint (never {@code null}). * * @see #setPlotBackgroundPaint(Paint) */ public Paint getPlotBackgroundPaint() { return this.plotBackgroundPaint; } /** * Sets the plot background paint. * * @param paint the paint ({@code null} not permitted). * * @see #getPlotBackgroundPaint() */ public void setPlotBackgroundPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.plotBackgroundPaint = paint; } /** * Returns the plot outline paint. * * @return The plot outline paint (never {@code null}). * * @see #setPlotOutlinePaint(Paint) */ public Paint getPlotOutlinePaint() { return this.plotOutlinePaint; } /** * Sets the plot outline paint. * * @param paint the paint ({@code null} not permitted). * * @see #getPlotOutlinePaint() */ public void setPlotOutlinePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.plotOutlinePaint = paint; } /** * Returns the label link style for pie charts. * * @return The label link style (never {@code null}). * * @see #setLabelLinkStyle(PieLabelLinkStyle) */ public PieLabelLinkStyle getLabelLinkStyle() { return this.labelLinkStyle; } /** * Sets the label link style for pie charts. * * @param style the style ({@code null} not permitted). * * @see #getLabelLinkStyle() */ public void setLabelLinkStyle(PieLabelLinkStyle style) { Args.nullNotPermitted(style, "style"); this.labelLinkStyle = style; } /** * Returns the label link paint for pie charts. * * @return The label link paint (never {@code null}). * * @see #setLabelLinkPaint(Paint) */ public Paint getLabelLinkPaint() { return this.labelLinkPaint; } /** * Sets the label link paint for pie charts. * * @param paint the paint ({@code null} not permitted). * * @see #getLabelLinkPaint() */ public void setLabelLinkPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.labelLinkPaint = paint; } /** * Returns the domain grid line paint. * * @return The domain grid line paint (never {@code null}). * * @see #setDomainGridlinePaint(Paint) */ public Paint getDomainGridlinePaint() { return this.domainGridlinePaint; } /** * Sets the domain grid line paint. * * @param paint the paint ({@code null} not permitted). * * @see #getDomainGridlinePaint() */ public void setDomainGridlinePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.domainGridlinePaint = paint; } /** * Returns the range grid line paint. * * @return The range grid line paint (never {@code null}). * * @see #setRangeGridlinePaint(Paint) */ public Paint getRangeGridlinePaint() { return this.rangeGridlinePaint; } /** * Sets the range grid line paint. * * @param paint the paint ({@code null} not permitted). * * @see #getRangeGridlinePaint() */ public void setRangeGridlinePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.rangeGridlinePaint = paint; } /** * Returns the baseline paint. * * @return The baseline paint. */ public Paint getBaselinePaint() { return this.baselinePaint; } /** * Sets the baseline paint. * * @param paint the paint ({@code null} not permitted). */ public void setBaselinePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.baselinePaint = paint; } /** * Returns the crosshair paint. * * @return The crosshair paint. */ public Paint getCrosshairPaint() { return this.crosshairPaint; } /** * Sets the crosshair paint. * * @param paint the paint ({@code null} not permitted). */ public void setCrosshairPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.crosshairPaint = paint; } /** * Returns the axis offsets. * * @return The axis offsets (never {@code null}). * * @see #setAxisOffset(RectangleInsets) */ public RectangleInsets getAxisOffset() { return this.axisOffset; } /** * Sets the axis offset. * * @param offset the offset ({@code null} not permitted). * * @see #getAxisOffset() */ public void setAxisOffset(RectangleInsets offset) { Args.nullNotPermitted(offset, "offset"); this.axisOffset = offset; } /** * Returns the axis label paint. * * @return The axis label paint (never {@code null}). * * @see #setAxisLabelPaint(Paint) */ public Paint getAxisLabelPaint() { return this.axisLabelPaint; } /** * Sets the axis label paint. * * @param paint the paint ({@code null} not permitted). * * @see #getAxisLabelPaint() */ public void setAxisLabelPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.axisLabelPaint = paint; } /** * Returns the tick label paint. * * @return The tick label paint (never {@code null}). * * @see #setTickLabelPaint(Paint) */ public Paint getTickLabelPaint() { return this.tickLabelPaint; } /** * Sets the tick label paint. * * @param paint the paint ({@code null} not permitted). * * @see #getTickLabelPaint() */ public void setTickLabelPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.tickLabelPaint = paint; } /** * Returns the item label paint. * * @return The item label paint (never {@code null}). * * @see #setItemLabelPaint(Paint) */ public Paint getItemLabelPaint() { return this.itemLabelPaint; } /** * Sets the item label paint. * * @param paint the paint ({@code null} not permitted). * * @see #getItemLabelPaint() */ public void setItemLabelPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.itemLabelPaint = paint; } /** * Returns the shadow visibility flag. * * @return The shadow visibility flag. * * @see #setShadowVisible(boolean) */ public boolean isShadowVisible() { return this.shadowVisible; } /** * Sets the shadow visibility flag. * * @param visible the flag. * * @see #isShadowVisible() */ public void setShadowVisible(boolean visible) { this.shadowVisible = visible; } /** * Returns the shadow paint. * * @return The shadow paint (never {@code null}). * * @see #setShadowPaint(Paint) */ public Paint getShadowPaint() { return this.shadowPaint; } /** * Sets the shadow paint. * * @param paint the paint ({@code null} not permitted). * * @see #getShadowPaint() */ public void setShadowPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.shadowPaint = paint; } /** * Returns the bar painter. * * @return The bar painter (never {@code null}). * * @see #setBarPainter(BarPainter) */ public BarPainter getBarPainter() { return this.barPainter; } /** * Sets the bar painter. * * @param painter the painter ({@code null} not permitted). * * @see #getBarPainter() */ public void setBarPainter(BarPainter painter) { Args.nullNotPermitted(painter, "painter"); this.barPainter = painter; } /** * Returns the XY bar painter. * * @return The XY bar painter (never {@code null}). * * @see #setXYBarPainter(XYBarPainter) */ public XYBarPainter getXYBarPainter() { return this.xyBarPainter; } /** * Sets the XY bar painter. * * @param painter the painter ({@code null} not permitted). * * @see #getXYBarPainter() */ public void setXYBarPainter(XYBarPainter painter) { Args.nullNotPermitted(painter, "painter"); this.xyBarPainter = painter; } /** * Returns the thermometer paint. * * @return The thermometer paint (never {@code null}). * * @see #setThermometerPaint(Paint) */ public Paint getThermometerPaint() { return this.thermometerPaint; } /** * Sets the thermometer paint. * * @param paint the paint ({@code null} not permitted). * * @see #getThermometerPaint() */ public void setThermometerPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.thermometerPaint = paint; } /** * Returns the error indicator paint. * * @return The error indicator paint (never {@code null}). * * @see #setErrorIndicatorPaint(Paint) */ public Paint getErrorIndicatorPaint() { return this.errorIndicatorPaint; } /** * Sets the error indicator paint. * * @param paint the paint ({@code null} not permitted). * * @see #getErrorIndicatorPaint() */ public void setErrorIndicatorPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.errorIndicatorPaint = paint; } /** * Returns the grid band paint. * * @return The grid band paint (never {@code null}). * * @see #setGridBandPaint(Paint) */ public Paint getGridBandPaint() { return this.gridBandPaint; } /** * Sets the grid band paint. * * @param paint the paint ({@code null} not permitted). * * @see #getGridBandPaint() */ public void setGridBandPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.gridBandPaint = paint; } /** * Returns the grid band alternate paint (used for a {@link SymbolAxis}). * * @return The paint (never {@code null}). * * @see #setGridBandAlternatePaint(Paint) */ public Paint getGridBandAlternatePaint() { return this.gridBandAlternatePaint; } /** * Sets the grid band alternate paint (used for a {@link SymbolAxis}). * * @param paint the paint ({@code null} not permitted). * * @see #getGridBandAlternatePaint() */ public void setGridBandAlternatePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.gridBandAlternatePaint = paint; } /** * Returns the name of this theme. * * @return The name of this theme. */ public String getName() { return this.name; } /** * Returns a clone of the drawing supplier for this theme. * * @return A clone of the drawing supplier. */ public DrawingSupplier getDrawingSupplier() { DrawingSupplier result = null; if (this.drawingSupplier instanceof PublicCloneable) { PublicCloneable pc = (PublicCloneable) this.drawingSupplier; try { result = (DrawingSupplier) pc.clone(); } catch (CloneNotSupportedException e) { throw new RuntimeException(e); } } return result; } /** * Sets the drawing supplier for this theme. * * @param supplier the supplier ({@code null} not permitted). * * @see #getDrawingSupplier() */ public void setDrawingSupplier(DrawingSupplier supplier) { Args.nullNotPermitted(supplier, "supplier"); this.drawingSupplier = supplier; } /** * Applies this theme to the supplied chart. * * @param chart the chart ({@code null} not permitted). */ @Override public void apply(JFreeChart chart) { Args.nullNotPermitted(chart, "chart"); TextTitle title = chart.getTitle(); if (title != null) { title.setFont(this.extraLargeFont); title.setPaint(this.titlePaint); } int subtitleCount = chart.getSubtitleCount(); for (int i = 0; i < subtitleCount; i++) { applyToTitle(chart.getSubtitle(i)); } chart.setBackgroundPaint(this.chartBackgroundPaint); // now process the plot if there is one Plot plot = chart.getPlot(); if (plot != null) { applyToPlot(plot); } } /** * Applies the attributes of this theme to the specified title. * * @param title the title. */ protected void applyToTitle(Title title) { if (title instanceof TextTitle) { TextTitle tt = (TextTitle) title; tt.setFont(this.largeFont); tt.setPaint(this.subtitlePaint); } else if (title instanceof LegendTitle) { LegendTitle lt = (LegendTitle) title; if (lt.getBackgroundPaint() != null) { lt.setBackgroundPaint(this.legendBackgroundPaint); } lt.setItemFont(this.regularFont); lt.setItemPaint(this.legendItemPaint); if (lt.getWrapper() != null) { applyToBlockContainer(lt.getWrapper()); } } else if (title instanceof PaintScaleLegend) { PaintScaleLegend psl = (PaintScaleLegend) title; psl.setBackgroundPaint(this.legendBackgroundPaint); ValueAxis axis = psl.getAxis(); if (axis != null) { applyToValueAxis(axis); } } else if (title instanceof CompositeTitle) { CompositeTitle ct = (CompositeTitle) title; BlockContainer bc = ct.getContainer(); List blocks = bc.getBlocks(); Iterator iterator = blocks.iterator(); while (iterator.hasNext()) { Block b = (Block) iterator.next(); if (b instanceof Title) { applyToTitle((Title) b); } } } } /** * Applies the attributes of this theme to the specified container. * * @param bc a block container ({@code null} not permitted). */ protected void applyToBlockContainer(BlockContainer bc) { Iterator iterator = bc.getBlocks().iterator(); while (iterator.hasNext()) { Block b = (Block) iterator.next(); applyToBlock(b); } } /** * Applies the attributes of this theme to the specified block. * * @param b the block. */ protected void applyToBlock(Block b) { if (b instanceof Title) { applyToTitle((Title) b); } else if (b instanceof LabelBlock) { LabelBlock lb = (LabelBlock) b; lb.setFont(this.regularFont); lb.setPaint(this.legendItemPaint); } } /** * Applies the attributes of this theme to a plot. * * @param plot the plot ({@code null}). */ protected void applyToPlot(Plot plot) { Args.nullNotPermitted(plot, "plot"); if (plot.getDrawingSupplier() != null) { plot.setDrawingSupplier(getDrawingSupplier()); } if (plot.getBackgroundPaint() != null) { plot.setBackgroundPaint(this.plotBackgroundPaint); } plot.setOutlinePaint(this.plotOutlinePaint); // now handle specific plot types (and yes, I know this is some // really ugly code that has to be manually updated any time a new // plot type is added - I should have written something much cooler, // but I didn't and neither did anyone else). if (plot instanceof PiePlot) { applyToPiePlot((PiePlot) plot); } else if (plot instanceof MultiplePiePlot) { applyToMultiplePiePlot((MultiplePiePlot) plot); } else if (plot instanceof CategoryPlot) { applyToCategoryPlot((CategoryPlot) plot); } else if (plot instanceof XYPlot) { applyToXYPlot((XYPlot) plot); } else if (plot instanceof FastScatterPlot) { applyToFastScatterPlot((FastScatterPlot) plot); } else if (plot instanceof MeterPlot) { applyToMeterPlot((MeterPlot) plot); } else if (plot instanceof ThermometerPlot) { applyToThermometerPlot((ThermometerPlot) plot); } else if (plot instanceof SpiderWebPlot) { applyToSpiderWebPlot((SpiderWebPlot) plot); } else if (plot instanceof PolarPlot) { applyToPolarPlot((PolarPlot) plot); } } /** * Applies the attributes of this theme to a {@link PiePlot} instance. * This method also clears any set values for the section paint, outline * etc, so that the theme's {@link DrawingSupplier} will be used. * * @param plot the plot ({@code null} not permitted). */ protected void applyToPiePlot(PiePlot plot) { plot.setLabelLinkPaint(this.labelLinkPaint); plot.setLabelLinkStyle(this.labelLinkStyle); plot.setLabelFont(this.regularFont); plot.setShadowGenerator(this.shadowGenerator); // clear the section attributes so that the theme's DrawingSupplier // will be used if (plot.getAutoPopulateSectionPaint()) { plot.clearSectionPaints(false); } if (plot.getAutoPopulateSectionOutlinePaint()) { plot.clearSectionOutlinePaints(false); } if (plot.getAutoPopulateSectionOutlineStroke()) { plot.clearSectionOutlineStrokes(false); } } /** * Applies the attributes of this theme to a {@link MultiplePiePlot}. * * @param plot the plot ({@code null} not permitted). */ protected void applyToMultiplePiePlot(MultiplePiePlot plot) { apply(plot.getPieChart()); } /** * Applies the attributes of this theme to a {@link CategoryPlot}. * * @param plot the plot ({@code null} not permitted). */ protected void applyToCategoryPlot(CategoryPlot plot) { plot.setAxisOffset(this.axisOffset); plot.setDomainGridlinePaint(this.domainGridlinePaint); plot.setRangeGridlinePaint(this.rangeGridlinePaint); plot.setRangeZeroBaselinePaint(this.baselinePaint); plot.setShadowGenerator(this.shadowGenerator); // process all domain axes int domainAxisCount = plot.getDomainAxisCount(); for (int i = 0; i < domainAxisCount; i++) { CategoryAxis axis = plot.getDomainAxis(i); if (axis != null) { applyToCategoryAxis(axis); } } // process all range axes int rangeAxisCount = plot.getRangeAxisCount(); for (int i = 0; i < rangeAxisCount; i++) { ValueAxis axis = plot.getRangeAxis(i); if (axis != null) { applyToValueAxis(axis); } } // process all renderers int rendererCount = plot.getRendererCount(); for (int i = 0; i < rendererCount; i++) { CategoryItemRenderer r = plot.getRenderer(i); if (r != null) { applyToCategoryItemRenderer(r); } } if (plot instanceof CombinedDomainCategoryPlot) { CombinedDomainCategoryPlot cp = (CombinedDomainCategoryPlot) plot; Iterator iterator = cp.getSubplots().iterator(); while (iterator.hasNext()) { CategoryPlot subplot = (CategoryPlot) iterator.next(); if (subplot != null) { applyToPlot(subplot); } } } if (plot instanceof CombinedRangeCategoryPlot) { CombinedRangeCategoryPlot cp = (CombinedRangeCategoryPlot) plot; Iterator iterator = cp.getSubplots().iterator(); while (iterator.hasNext()) { CategoryPlot subplot = (CategoryPlot) iterator.next(); if (subplot != null) { applyToPlot(subplot); } } } } /** * Applies the attributes of this theme to a {@link XYPlot}. * * @param plot the plot ({@code null} not permitted). */ protected void applyToXYPlot(XYPlot plot) { plot.setAxisOffset(this.axisOffset); plot.setDomainZeroBaselinePaint(this.baselinePaint); plot.setRangeZeroBaselinePaint(this.baselinePaint); plot.setDomainGridlinePaint(this.domainGridlinePaint); plot.setRangeGridlinePaint(this.rangeGridlinePaint); plot.setDomainCrosshairPaint(this.crosshairPaint); plot.setRangeCrosshairPaint(this.crosshairPaint); plot.setShadowGenerator(this.shadowGenerator); // process all domain axes for (ValueAxis xAxis : plot.getDomainAxes().values()) { if (xAxis != null) { applyToValueAxis(xAxis); } } // process all range axes for (ValueAxis yAxis : plot.getRangeAxes().values()) { if (yAxis != null) { applyToValueAxis(yAxis); } } // process all renderers for (XYItemRenderer r : plot.getRenderers().values()) { if (r != null) { applyToXYItemRenderer(r); } } // process all annotations for (XYAnnotation a : plot.getAnnotations()) { applyToXYAnnotation(a); } if (plot instanceof CombinedDomainXYPlot) { CombinedDomainXYPlot cp = (CombinedDomainXYPlot) plot; for (XYPlot subplot : cp.getSubplots()) { if (subplot != null) { applyToPlot(subplot); } } } if (plot instanceof CombinedRangeXYPlot) { CombinedRangeXYPlot cp = (CombinedRangeXYPlot) plot; for (XYPlot subplot : cp.getSubplots()) { if (subplot != null) { applyToPlot(subplot); } } } } /** * Applies the attributes of this theme to a {@link FastScatterPlot}. * * @param plot the plot ({@code null} not permitted). */ protected void applyToFastScatterPlot(FastScatterPlot plot) { plot.setDomainGridlinePaint(this.domainGridlinePaint); plot.setRangeGridlinePaint(this.rangeGridlinePaint); ValueAxis xAxis = plot.getDomainAxis(); if (xAxis != null) { applyToValueAxis(xAxis); } ValueAxis yAxis = plot.getRangeAxis(); if (yAxis != null) { applyToValueAxis(yAxis); } } /** * Applies the attributes of this theme to a {@link PolarPlot}. This * method is called from the {@link #applyToPlot(Plot)} method. * * @param plot the plot ({@code null} not permitted). */ protected void applyToPolarPlot(PolarPlot plot) { plot.setAngleLabelFont(this.regularFont); plot.setAngleLabelPaint(this.tickLabelPaint); plot.setAngleGridlinePaint(this.domainGridlinePaint); plot.setRadiusGridlinePaint(this.rangeGridlinePaint); ValueAxis axis = plot.getAxis(); if (axis != null) { applyToValueAxis(axis); } } /** * Applies the attributes of this theme to a {@link SpiderWebPlot}. * * @param plot the plot ({@code null} not permitted). */ protected void applyToSpiderWebPlot(SpiderWebPlot plot) { plot.setLabelFont(this.regularFont); plot.setLabelPaint(this.axisLabelPaint); plot.setAxisLinePaint(this.axisLabelPaint); } /** * Applies the attributes of this theme to a {@link MeterPlot}. * * @param plot the plot ({@code null} not permitted). */ protected void applyToMeterPlot(MeterPlot plot) { plot.setDialBackgroundPaint(this.plotBackgroundPaint); plot.setValueFont(this.largeFont); plot.setValuePaint(this.axisLabelPaint); plot.setDialOutlinePaint(this.plotOutlinePaint); plot.setNeedlePaint(this.thermometerPaint); plot.setTickLabelFont(this.regularFont); plot.setTickLabelPaint(this.tickLabelPaint); } /** * Applies the attributes for this theme to a {@link ThermometerPlot}. * This method is called from the {@link #applyToPlot(Plot)} method. * * @param plot the plot. */ protected void applyToThermometerPlot(ThermometerPlot plot) { plot.setValueFont(this.largeFont); plot.setThermometerPaint(this.thermometerPaint); ValueAxis axis = plot.getRangeAxis(); if (axis != null) { applyToValueAxis(axis); } } /** * Applies the attributes for this theme to a {@link CategoryAxis}. * * @param axis the axis ({@code null} not permitted). */ protected void applyToCategoryAxis(CategoryAxis axis) { axis.setLabelFont(this.largeFont); axis.setLabelPaint(this.axisLabelPaint); axis.setTickLabelFont(this.regularFont); axis.setTickLabelPaint(this.tickLabelPaint); if (axis instanceof SubCategoryAxis) { SubCategoryAxis sca = (SubCategoryAxis) axis; sca.setSubLabelFont(this.regularFont); sca.setSubLabelPaint(this.tickLabelPaint); } } /** * Applies the attributes for this theme to a {@link ValueAxis}. * * @param axis the axis ({@code null} not permitted). */ protected void applyToValueAxis(ValueAxis axis) { axis.setLabelFont(this.largeFont); axis.setLabelPaint(this.axisLabelPaint); axis.setTickLabelFont(this.regularFont); axis.setTickLabelPaint(this.tickLabelPaint); if (axis instanceof SymbolAxis) { applyToSymbolAxis((SymbolAxis) axis); } if (axis instanceof PeriodAxis) { applyToPeriodAxis((PeriodAxis) axis); } } /** * Applies the attributes for this theme to a {@link SymbolAxis}. * * @param axis the axis ({@code null} not permitted). */ protected void applyToSymbolAxis(SymbolAxis axis) { axis.setGridBandPaint(this.gridBandPaint); axis.setGridBandAlternatePaint(this.gridBandAlternatePaint); } /** * Applies the attributes for this theme to a {@link PeriodAxis}. * * @param axis the axis ({@code null} not permitted). */ protected void applyToPeriodAxis(PeriodAxis axis) { PeriodAxisLabelInfo[] info = axis.getLabelInfo(); for (int i = 0; i < info.length; i++) { PeriodAxisLabelInfo e = info[i]; PeriodAxisLabelInfo n = new PeriodAxisLabelInfo(e.getPeriodClass(), e.getDateFormat(), e.getPadding(), this.regularFont, this.tickLabelPaint, e.getDrawDividers(), e.getDividerStroke(), e.getDividerPaint()); info[i] = n; } axis.setLabelInfo(info); } /** * Applies the attributes for this theme to an {@link AbstractRenderer}. * * @param renderer the renderer ({@code null} not permitted). */ protected void applyToAbstractRenderer(AbstractRenderer renderer) { if (renderer.getAutoPopulateSeriesPaint()) { renderer.clearSeriesPaints(false); } if (renderer.getAutoPopulateSeriesStroke()) { renderer.clearSeriesStrokes(false); } } /** * Applies the settings of this theme to the specified renderer. * * @param renderer the renderer ({@code null} not permitted). */ protected void applyToCategoryItemRenderer(CategoryItemRenderer renderer) { Args.nullNotPermitted(renderer, "renderer"); if (renderer instanceof AbstractRenderer) { applyToAbstractRenderer((AbstractRenderer) renderer); } renderer.setDefaultItemLabelFont(this.regularFont); renderer.setDefaultItemLabelPaint(this.itemLabelPaint); // now we handle some special cases - yes, UGLY code alert! // BarRenderer if (renderer instanceof BarRenderer) { BarRenderer br = (BarRenderer) renderer; br.setBarPainter(this.barPainter); br.setShadowVisible(this.shadowVisible); br.setShadowPaint(this.shadowPaint); } // StatisticalBarRenderer if (renderer instanceof StatisticalBarRenderer) { StatisticalBarRenderer sbr = (StatisticalBarRenderer) renderer; sbr.setErrorIndicatorPaint(this.errorIndicatorPaint); } // MinMaxCategoryRenderer if (renderer instanceof MinMaxCategoryRenderer) { MinMaxCategoryRenderer mmcr = (MinMaxCategoryRenderer) renderer; mmcr.setGroupPaint(this.errorIndicatorPaint); } } /** * Applies the settings of this theme to the specified renderer. * * @param renderer the renderer ({@code null} not permitted). */ protected void applyToXYItemRenderer(XYItemRenderer renderer) { Args.nullNotPermitted(renderer, "renderer"); if (renderer instanceof AbstractRenderer) { applyToAbstractRenderer((AbstractRenderer) renderer); } renderer.setDefaultItemLabelFont(this.regularFont); renderer.setDefaultItemLabelPaint(this.itemLabelPaint); if (renderer instanceof XYBarRenderer) { XYBarRenderer br = (XYBarRenderer) renderer; br.setBarPainter(this.xyBarPainter); br.setShadowVisible(this.shadowVisible); } } /** * Applies the settings of this theme to the specified annotation. * * @param annotation the annotation. */ protected void applyToXYAnnotation(XYAnnotation annotation) { Args.nullNotPermitted(annotation, "annotation"); if (annotation instanceof XYTextAnnotation) { XYTextAnnotation xyta = (XYTextAnnotation) annotation; xyta.setFont(this.smallFont); xyta.setPaint(this.itemLabelPaint); } } /** * Tests this theme for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof StandardChartTheme)) { return false; } StandardChartTheme that = (StandardChartTheme) obj; if (!Objects.equals(this.name, that.name)) { return false; } if (!Objects.equals(this.extraLargeFont, that.extraLargeFont)) { return false; } if (!Objects.equals(this.largeFont, that.largeFont)) { return false; } if (!Objects.equals(this.regularFont, that.regularFont)) { return false; } if (!Objects.equals(this.smallFont, that.smallFont)) { return false; } if (!Objects.equals(this.drawingSupplier, that.drawingSupplier)) { return false; } if (!PaintUtils.equal(this.titlePaint, that.titlePaint)) { return false; } if (!PaintUtils.equal(this.subtitlePaint, that.subtitlePaint)) { return false; } if (!PaintUtils.equal(this.chartBackgroundPaint, that.chartBackgroundPaint)) { return false; } if (!PaintUtils.equal(this.legendBackgroundPaint, that.legendBackgroundPaint)) { return false; } if (!PaintUtils.equal(this.legendItemPaint, that.legendItemPaint)) { return false; } if (!PaintUtils.equal(this.plotBackgroundPaint, that.plotBackgroundPaint)) { return false; } if (!PaintUtils.equal(this.plotOutlinePaint, that.plotOutlinePaint)) { return false; } if (!Objects.equals(this.labelLinkStyle, that.labelLinkStyle)) { return false; } if (!PaintUtils.equal(this.labelLinkPaint, that.labelLinkPaint)) { return false; } if (!PaintUtils.equal(this.domainGridlinePaint, that.domainGridlinePaint)) { return false; } if (!PaintUtils.equal(this.rangeGridlinePaint, that.rangeGridlinePaint)) { return false; } if (!PaintUtils.equal(this.baselinePaint, that.baselinePaint)) { return false; } if (!PaintUtils.equal(this.crosshairPaint, that.crosshairPaint)) { return false; } if (!Objects.equals(this.axisOffset, that.axisOffset)) { return false; } if (!PaintUtils.equal(this.axisLabelPaint, that.axisLabelPaint)) { return false; } if (!PaintUtils.equal(this.tickLabelPaint, that.tickLabelPaint)) { return false; } if (!PaintUtils.equal(this.itemLabelPaint, that.itemLabelPaint)) { return false; } if (this.shadowVisible != that.shadowVisible) { return false; } if (!PaintUtils.equal(this.shadowPaint, that.shadowPaint)) { return false; } if (!Objects.equals(this.barPainter, that.barPainter)) { return false; } if (!Objects.equals(this.xyBarPainter, that.xyBarPainter)) { return false; } if (!Objects.equals(this.shadowGenerator, that.shadowGenerator)) { return false; } if (!PaintUtils.equal(this.thermometerPaint, that.thermometerPaint)) { return false; } if (!PaintUtils.equal(this.errorIndicatorPaint, that.errorIndicatorPaint)) { return false; } if (!PaintUtils.equal(this.gridBandPaint, that.gridBandPaint)) { return false; } if (!PaintUtils.equal(this.gridBandAlternatePaint, that.gridBandAlternatePaint)) { return false; } return true; } @Override public int hashCode() { int hash = 7; hash = 83 * hash + Objects.hashCode(this.name); hash = 83 * hash + Objects.hashCode(this.extraLargeFont); hash = 83 * hash + Objects.hashCode(this.largeFont); hash = 83 * hash + Objects.hashCode(this.regularFont); hash = 83 * hash + Objects.hashCode(this.smallFont); hash = 83 * hash + HashUtils.hashCodeForPaint(this.titlePaint); hash = 83 * hash + HashUtils.hashCodeForPaint(this.subtitlePaint); hash = 83 * hash + HashUtils.hashCodeForPaint(this.chartBackgroundPaint); hash = 83 * hash + HashUtils.hashCodeForPaint(this.legendBackgroundPaint); hash = 83 * hash + HashUtils.hashCodeForPaint(this.legendItemPaint); hash = 83 * hash + Objects.hashCode(this.drawingSupplier); hash = 83 * hash + HashUtils.hashCodeForPaint(this.plotBackgroundPaint); hash = 83 * hash + HashUtils.hashCodeForPaint(this.plotOutlinePaint); hash = 83 * hash + Objects.hashCode(this.labelLinkStyle); hash = 83 * hash + HashUtils.hashCodeForPaint(this.labelLinkPaint); hash = 83 * hash + HashUtils.hashCodeForPaint(this.domainGridlinePaint); hash = 83 * hash + HashUtils.hashCodeForPaint(this.rangeGridlinePaint); hash = 83 * hash + HashUtils.hashCodeForPaint(this.baselinePaint); hash = 83 * hash + HashUtils.hashCodeForPaint(this.crosshairPaint); hash = 83 * hash + Objects.hashCode(this.axisOffset); hash = 83 * hash + HashUtils.hashCodeForPaint(this.axisLabelPaint); hash = 83 * hash + HashUtils.hashCodeForPaint(this.tickLabelPaint); hash = 83 * hash + HashUtils.hashCodeForPaint(this.itemLabelPaint); hash = 83 * hash + (this.shadowVisible ? 1 : 0); hash = 83 * hash + HashUtils.hashCodeForPaint(this.shadowPaint); hash = 83 * hash + Objects.hashCode(this.barPainter); hash = 83 * hash + Objects.hashCode(this.xyBarPainter); hash = 83 * hash + HashUtils.hashCodeForPaint(this.thermometerPaint); hash = 83 * hash + HashUtils.hashCodeForPaint(this.errorIndicatorPaint); hash = 83 * hash + HashUtils.hashCodeForPaint(this.gridBandPaint); hash = 83 * hash + HashUtils.hashCodeForPaint(this.gridBandAlternatePaint); hash = 83 * hash + Objects.hashCode(this.shadowGenerator); return hash; } /** * Returns a clone of this theme. * * @return A clone. * * @throws CloneNotSupportedException if the theme cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Provides serialization support. * * @param stream the output stream ({@code null} not permitted). * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.titlePaint, stream); SerialUtils.writePaint(this.subtitlePaint, stream); SerialUtils.writePaint(this.chartBackgroundPaint, stream); SerialUtils.writePaint(this.legendBackgroundPaint, stream); SerialUtils.writePaint(this.legendItemPaint, stream); SerialUtils.writePaint(this.plotBackgroundPaint, stream); SerialUtils.writePaint(this.plotOutlinePaint, stream); SerialUtils.writePaint(this.labelLinkPaint, stream); SerialUtils.writePaint(this.baselinePaint, stream); SerialUtils.writePaint(this.domainGridlinePaint, stream); SerialUtils.writePaint(this.rangeGridlinePaint, stream); SerialUtils.writePaint(this.crosshairPaint, stream); SerialUtils.writePaint(this.axisLabelPaint, stream); SerialUtils.writePaint(this.tickLabelPaint, stream); SerialUtils.writePaint(this.itemLabelPaint, stream); SerialUtils.writePaint(this.shadowPaint, stream); SerialUtils.writePaint(this.thermometerPaint, stream); SerialUtils.writePaint(this.errorIndicatorPaint, stream); SerialUtils.writePaint(this.gridBandPaint, stream); SerialUtils.writePaint(this.gridBandAlternatePaint, stream); } /** * Provides serialization support. * * @param stream the input stream ({@code null} not permitted). * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.titlePaint = SerialUtils.readPaint(stream); this.subtitlePaint = SerialUtils.readPaint(stream); this.chartBackgroundPaint = SerialUtils.readPaint(stream); this.legendBackgroundPaint = SerialUtils.readPaint(stream); this.legendItemPaint = SerialUtils.readPaint(stream); this.plotBackgroundPaint = SerialUtils.readPaint(stream); this.plotOutlinePaint = SerialUtils.readPaint(stream); this.labelLinkPaint = SerialUtils.readPaint(stream); this.baselinePaint = SerialUtils.readPaint(stream); this.domainGridlinePaint = SerialUtils.readPaint(stream); this.rangeGridlinePaint = SerialUtils.readPaint(stream); this.crosshairPaint = SerialUtils.readPaint(stream); this.axisLabelPaint = SerialUtils.readPaint(stream); this.tickLabelPaint = SerialUtils.readPaint(stream); this.itemLabelPaint = SerialUtils.readPaint(stream); this.shadowPaint = SerialUtils.readPaint(stream); this.thermometerPaint = SerialUtils.readPaint(stream); this.errorIndicatorPaint = SerialUtils.readPaint(stream); this.gridBandPaint = SerialUtils.readPaint(stream); this.gridBandAlternatePaint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/StrokeMap.java000066400000000000000000000150631463604235500256330ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * StrokeMap.java * -------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart; import java.awt.Stroke; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Iterator; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.TreeMap; import org.jfree.chart.util.Args; import org.jfree.chart.util.SerialUtils; /** * A storage structure that maps {@code Comparable} instances with * {@code Stroke} instances. *

* To support cloning and serialization, you should only use keys that are * cloneable and serializable. Special handling for the {@code Stroke} * instances is included in this class. */ public class StrokeMap implements Cloneable, Serializable { /** For serialization. */ static final long serialVersionUID = -8148916785963525169L; /** Storage for the keys and values. */ private transient Map store; /** * Creates a new (empty) map. */ public StrokeMap() { this.store = new TreeMap(); } /** * Returns the stroke associated with the specified key, or * {@code null}. * * @param key the key ({@code null} not permitted). * * @return The stroke, or {@code null}. * * @throws IllegalArgumentException if {@code key} is * {@code null}. */ public Stroke getStroke(Comparable key) { Args.nullNotPermitted(key, "key"); return (Stroke) this.store.get(key); } /** * Returns {@code true} if the map contains the specified key, and * {@code false} otherwise. * * @param key the key. * * @return {@code true} if the map contains the specified key, and * {@code false} otherwise. */ public boolean containsKey(Comparable key) { return this.store.containsKey(key); } /** * Adds a mapping between the specified {@code key} and * {@code stroke} values. * * @param key the key ({@code null} not permitted). * @param stroke the stroke. */ public void put(Comparable key, Stroke stroke) { Args.nullNotPermitted(key, "key"); this.store.put(key, stroke); } /** * Resets the map to empty. */ public void clear() { this.store.clear(); } /** * Tests this map for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof StrokeMap)) { return false; } StrokeMap that = (StrokeMap) obj; if (this.store.size() != that.store.size()) { return false; } Set keys = this.store.keySet(); Iterator iterator = keys.iterator(); while (iterator.hasNext()) { Comparable key = (Comparable) iterator.next(); Stroke s1 = getStroke(key); Stroke s2 = that.getStroke(key); if (!Objects.equals(s1, s2)) { return false; } } return true; } /** * Returns a clone of this {@code StrokeMap}. * * @return A clone of this instance. * * @throws CloneNotSupportedException if any key is not cloneable. */ @Override public Object clone() throws CloneNotSupportedException { StrokeMap clone = (StrokeMap) super.clone(); clone.store = new TreeMap(); clone.store.putAll(this.store); // TODO: I think we need to make sure the keys are actually cloned, // whereas the stroke instances are always immutable so they're OK return clone; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); stream.writeInt(this.store.size()); Set keys = this.store.keySet(); Iterator iterator = keys.iterator(); while (iterator.hasNext()) { Comparable key = (Comparable) iterator.next(); stream.writeObject(key); Stroke stroke = getStroke(key); SerialUtils.writeStroke(stroke, stream); } } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.store = new TreeMap(); int keyCount = stream.readInt(); for (int i = 0; i < keyCount; i++) { Comparable key = (Comparable) stream.readObject(); Stroke stroke = SerialUtils.readStroke(stream); this.store.put(key, stroke); } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/annotations/000077500000000000000000000000001463604235500254135ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/annotations/AbstractAnnotation.java000066400000000000000000000174751463604235500320720ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * AbstractAnnotation.java * ----------------------- * (C) Copyright 2009-present, by David Gilbert and Contributors. * * Original Author: Peter Kolb (see patch 2809117); * Contributor(s): Tracy Hiltbrand (added equals/canEqual/hashCode); * */ package org.jfree.chart.annotations; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Arrays; import java.util.EventListener; import java.util.List; import javax.swing.event.EventListenerList; import org.jfree.chart.event.AnnotationChangeEvent; import org.jfree.chart.event.AnnotationChangeListener; /** * An abstract implementation of the {@link Annotation} interface, containing a * mechanism for registering change listeners. */ public abstract class AbstractAnnotation implements Annotation, Cloneable, Serializable { /** Storage for registered change listeners. */ private transient EventListenerList listenerList; /** * A flag that indicates whether listeners should be notified * about changes of the annotation. */ private boolean notify = true; /** * Constructs an annotation. */ protected AbstractAnnotation() { this.listenerList = new EventListenerList(); } /** * Registers an object to receive notification of changes to the * annotation. * * @param listener the object to register. * * @see #removeChangeListener(AnnotationChangeListener) */ @Override public void addChangeListener(AnnotationChangeListener listener) { this.listenerList.add(AnnotationChangeListener.class, listener); } /** * Deregisters an object so that it no longer receives notification of * changes to the annotation. * * @param listener the object to deregister. * * @see #addChangeListener(AnnotationChangeListener) */ @Override public void removeChangeListener(AnnotationChangeListener listener) { this.listenerList.remove(AnnotationChangeListener.class, listener); } /** * Returns {@code true} if the specified object is registered with * the annotation as a listener. Most applications won't need to call this * method, it exists mainly for use by unit testing code. * * @param listener the listener. * * @return A boolean. * * @see #addChangeListener(AnnotationChangeListener) * @see #removeChangeListener(AnnotationChangeListener) */ public boolean hasListener(EventListener listener) { List list = Arrays.asList(this.listenerList.getListenerList()); return list.contains(listener); } /** * Notifies all registered listeners that the annotation has changed. * * @see #addChangeListener(AnnotationChangeListener) */ protected void fireAnnotationChanged() { if (notify) { notifyListeners(new AnnotationChangeEvent(this, this)); } } /** * Notifies all registered listeners that the annotation has changed. * * @param event contains information about the event that triggered the * notification. * * @see #addChangeListener(AnnotationChangeListener) * @see #removeChangeListener(AnnotationChangeListener) */ protected void notifyListeners(AnnotationChangeEvent event) { Object[] listeners = this.listenerList.getListenerList(); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == AnnotationChangeListener.class) { ((AnnotationChangeListener) listeners[i + 1]).annotationChanged( event); } } } /** * Returns a flag that indicates whether listeners should be * notified about changes to the annotation. * * @return the flag. * * @see #setNotify(boolean) */ public boolean getNotify(){ return this.notify; } /** * Sets a flag that indicates whether listeners should be notified about * changes of an annotation. * * @param flag the flag * * @see #getNotify() */ public void setNotify(boolean flag){ this.notify = flag; if (notify) { fireAnnotationChanged(); } } /** * Returns a clone of the annotation. The cloned annotation will NOT * include the {@link AnnotationChangeListener} references that have been * registered with this annotation. * * @return A clone. * * @throws CloneNotSupportedException if the annotation does not support * cloning. */ @Override public Object clone() throws CloneNotSupportedException { AbstractAnnotation clone = (AbstractAnnotation) super.clone(); clone.listenerList = new EventListenerList(); return clone; } /** * Handles serialization. * * @param stream the output stream. * * @throws IOException if there is an I/O problem. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); } /** * Restores a serialized object. * * @param stream the input stream. * * @throws IOException if there is an I/O problem. * @throws ClassNotFoundException if there is a problem loading a class. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.listenerList = new EventListenerList(); } @Override public int hashCode() { int hash = 7; hash = 71 * hash + (this.notify ? 1 : 0); return hash; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof AbstractAnnotation)) { return false; } AbstractAnnotation that = (AbstractAnnotation) obj; if (this.notify != that.notify) { return false; } // fix the "equals not symmetric" problem if (!that.canEqual(this)) { return false; } return true; } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof AbstractAnnotation); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/annotations/AbstractXYAnnotation.java000066400000000000000000000152461463604235500323450ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * AbstractXYAnnotation.java * ------------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Peter Kolb (patch 2809117); * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.annotations; import java.awt.Graphics2D; import java.awt.Shape; import java.awt.geom.Rectangle2D; import java.util.Objects; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.entity.XYAnnotationEntity; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; /** * The interface that must be supported by annotations that are to be added to * an {@link XYPlot}. */ public abstract class AbstractXYAnnotation extends AbstractAnnotation implements XYAnnotation { /** The tool tip text. */ private String toolTipText; /** The URL. */ private String url; /** * Creates a new instance that has no tool tip or URL specified. */ protected AbstractXYAnnotation() { super(); this.toolTipText = null; this.url = null; } /** * Returns the tool tip text for the annotation. This will be displayed in * a {@link org.jfree.chart.ChartPanel} when the mouse pointer hovers over * the annotation. * * @return The tool tip text (possibly {@code null}). * * @see #setToolTipText(String) */ public String getToolTipText() { return this.toolTipText; } /** * Sets the tool tip text for the annotation. * * @param text the tool tip text ({@code null} permitted). * * @see #getToolTipText() */ public void setToolTipText(String text) { this.toolTipText = text; } /** * Returns the URL for the annotation. This URL will be used to provide * hyperlinks when an HTML image map is created for the chart. * * @return The URL (possibly {@code null}). * * @see #setURL(String) */ public String getURL() { return this.url; } /** * Sets the URL for the annotation. * * @param url the URL ({@code null} permitted). * * @see #getURL() */ public void setURL(String url) { this.url = url; } /** * Draws the annotation. * * @param g2 the graphics device. * @param plot the plot. * @param dataArea the data area. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param rendererIndex the renderer index. * @param info if supplied, this info object will be populated with * entity information. */ @Override public abstract void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, ValueAxis domainAxis, ValueAxis rangeAxis, int rendererIndex, PlotRenderingInfo info); /** * A utility method for adding an {@link XYAnnotationEntity} to * a {@link PlotRenderingInfo} instance. * * @param info the plot rendering info ({@code null} permitted). * @param hotspot the hotspot area. * @param rendererIndex the renderer index. * @param toolTipText the tool tip text. * @param urlText the URL text. */ protected void addEntity(PlotRenderingInfo info, Shape hotspot, int rendererIndex, String toolTipText, String urlText) { if (info == null) { return; } EntityCollection entities = info.getOwner().getEntityCollection(); if (entities == null) { return; } XYAnnotationEntity entity = new XYAnnotationEntity(hotspot, rendererIndex, toolTipText, urlText); entities.add(entity); } /** * Tests this annotation for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof AbstractXYAnnotation)) { return false; } AbstractXYAnnotation that = (AbstractXYAnnotation) obj; if (!Objects.equals(this.toolTipText, that.toolTipText)) { return false; } if (!Objects.equals(this.url, that.url)) { return false; } // fix the "equals not symmetric" problem if (!that.canEqual(this)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof AbstractXYAnnotation); } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = super.hashCode(); // equals calls superclass, hashCode must also result = 37 * result + Objects.hashCode(this.toolTipText); result = 37 * result + Objects.hashCode(this.url); return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/annotations/Annotation.java000066400000000000000000000043121463604235500303700ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * Annotation.java * --------------- * (C) Copyright 2009-present, by Peter Kolb and Contributors. * * Original Author: Peter Kolb (see patch 2809117); * Contributor(s): ; * */ package org.jfree.chart.annotations; import org.jfree.chart.event.AnnotationChangeEvent; import org.jfree.chart.event.AnnotationChangeListener; /** * The base interface for annotations. All annotations should support the * {@link AnnotationChangeEvent} mechanism by allowing listeners to register * and receive notification of any changes to the annotation. */ public interface Annotation { /** * Registers an object for notification of changes to the annotation. * * @param listener the object to register. */ void addChangeListener(AnnotationChangeListener listener); /** * Deregisters an object for notification of changes to the annotation. * * @param listener the object to deregister. */ void removeChangeListener(AnnotationChangeListener listener); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/annotations/CategoryAnnotation.java000066400000000000000000000044741463604235500320770ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * CategoryAnnotation.java * ----------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.annotations; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.plot.CategoryPlot; /** * The interface that must be supported by annotations that are to be added to * a {@link CategoryPlot}. Note that, in JFreeChart 1.0.14, a non-compatible * change has been made to this interface (it now extends the Annotation * interface to support change notifications). */ public interface CategoryAnnotation extends Annotation { /** * Draws the annotation. * * @param g2 the graphics device. * @param plot the plot. * @param dataArea the data area. * @param domainAxis the domain axis. * @param rangeAxis the range axis. */ void draw(Graphics2D g2, CategoryPlot plot, Rectangle2D dataArea, CategoryAxis domainAxis, ValueAxis rangeAxis); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/annotations/CategoryLineAnnotation.java000066400000000000000000000344131463604235500327030ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * CategoryLineAnnotation.java * --------------------------- * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Peter Kolb (patch 2809117); * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.annotations; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Stroke; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.HashUtils; import org.jfree.chart.axis.CategoryAnchor; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.AnnotationChangeEvent; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; import org.jfree.data.category.CategoryDataset; /** * A line annotation that can be placed on a {@link CategoryPlot}. */ public class CategoryLineAnnotation extends AbstractAnnotation implements CategoryAnnotation, Cloneable, PublicCloneable, Serializable { /** For serialization. */ static final long serialVersionUID = 3477740483341587984L; /** The category for the start of the line. */ private Comparable category1; /** The value for the start of the line. */ private double value1; /** The category for the end of the line. */ private Comparable category2; /** The value for the end of the line. */ private double value2; /** The line color. */ private transient Paint paint = Color.BLACK; /** The line stroke. */ private transient Stroke stroke = new BasicStroke(1.0f); /** * Creates a new annotation that draws a line between (category1, value1) * and (category2, value2). * * @param category1 the category ({@code null} not permitted). * @param value1 the value (must be finite). * @param category2 the category ({@code null} not permitted). * @param value2 the value (must be finite). * @param paint the line color ({@code null} not permitted). * @param stroke the line stroke ({@code null} not permitted). */ public CategoryLineAnnotation(Comparable category1, double value1, Comparable category2, double value2, Paint paint, Stroke stroke) { super(); Args.nullNotPermitted(category1, "category1"); Args.requireFinite(value1, "value1"); Args.nullNotPermitted(category2, "category2"); Args.requireFinite(value2, "value2"); Args.nullNotPermitted(paint, "paint"); Args.nullNotPermitted(stroke, "stroke"); this.category1 = category1; this.value1 = value1; this.category2 = category2; this.value2 = value2; this.paint = paint; this.stroke = stroke; } /** * Returns the category for the start of the line. * * @return The category for the start of the line (never {@code null}). * * @see #setCategory1(Comparable) */ public Comparable getCategory1() { return this.category1; } /** * Sets the category for the start of the line and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param category the category ({@code null} not permitted). * * @see #getCategory1() */ public void setCategory1(Comparable category) { Args.nullNotPermitted(category, "category"); this.category1 = category; fireAnnotationChanged(); } /** * Returns the y-value for the start of the line. * * @return The y-value for the start of the line. * * @see #setValue1(double) */ public double getValue1() { return this.value1; } /** * Sets the y-value for the start of the line and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param value the value (must be finite). * * @see #getValue1() */ public void setValue1(double value) { Args.requireFinite(value, "value"); this.value1 = value; fireAnnotationChanged(); } /** * Returns the category for the end of the line. * * @return The category for the end of the line (never {@code null}). * * @see #setCategory2(Comparable) */ public Comparable getCategory2() { return this.category2; } /** * Sets the category for the end of the line and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param category the category ({@code null} not permitted). * * @see #getCategory2() */ public void setCategory2(Comparable category) { Args.nullNotPermitted(category, "category"); this.category2 = category; fireAnnotationChanged(); } /** * Returns the y-value for the end of the line. * * @return The y-value for the end of the line. * * @see #setValue2(double) */ public double getValue2() { return this.value2; } /** * Sets the y-value for the end of the line and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param value the value (must be finite). * * @see #getValue2() */ public void setValue2(double value) { Args.requireFinite(value, "value"); this.value2 = value; fireAnnotationChanged(); } /** * Returns the paint used to draw the connecting line. * * @return The paint (never {@code null}). * * @see #setPaint(Paint) */ public Paint getPaint() { return this.paint; } /** * Sets the paint used to draw the connecting line and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getPaint() */ public void setPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.paint = paint; fireAnnotationChanged(); } /** * Returns the stroke used to draw the connecting line. * * @return The stroke (never {@code null}). * * @see #setStroke(Stroke) */ public Stroke getStroke() { return this.stroke; } /** * Sets the stroke used to draw the connecting line and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getStroke() */ public void setStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.stroke = stroke; fireAnnotationChanged(); } /** * Draws the annotation. * * @param g2 the graphics device ({@code null} not permitted). * @param plot the plot ({@code null} not permitted). * @param dataArea the data area ({@code null} not permitted). * @param domainAxis the domain axis ({@code null} not permitted). * @param rangeAxis the range axis ({@code null} not permitted). */ @Override public void draw(Graphics2D g2, CategoryPlot plot, Rectangle2D dataArea, CategoryAxis domainAxis, ValueAxis rangeAxis) { CategoryDataset dataset = plot.getDataset(); int catIndex1 = dataset.getColumnIndex(this.category1); int catIndex2 = dataset.getColumnIndex(this.category2); int catCount = dataset.getColumnCount(); double lineX1 = 0.0f; double lineY1 = 0.0f; double lineX2 = 0.0f; double lineY2 = 0.0f; PlotOrientation orientation = plot.getOrientation(); RectangleEdge domainEdge = Plot.resolveDomainAxisLocation( plot.getDomainAxisLocation(), orientation); RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation( plot.getRangeAxisLocation(), orientation); if (orientation == PlotOrientation.HORIZONTAL) { lineY1 = domainAxis.getCategoryJava2DCoordinate( CategoryAnchor.MIDDLE, catIndex1, catCount, dataArea, domainEdge); lineX1 = rangeAxis.valueToJava2D(this.value1, dataArea, rangeEdge); lineY2 = domainAxis.getCategoryJava2DCoordinate( CategoryAnchor.MIDDLE, catIndex2, catCount, dataArea, domainEdge); lineX2 = rangeAxis.valueToJava2D(this.value2, dataArea, rangeEdge); } else if (orientation == PlotOrientation.VERTICAL) { lineX1 = domainAxis.getCategoryJava2DCoordinate( CategoryAnchor.MIDDLE, catIndex1, catCount, dataArea, domainEdge); lineY1 = rangeAxis.valueToJava2D(this.value1, dataArea, rangeEdge); lineX2 = domainAxis.getCategoryJava2DCoordinate( CategoryAnchor.MIDDLE, catIndex2, catCount, dataArea, domainEdge); lineY2 = rangeAxis.valueToJava2D(this.value2, dataArea, rangeEdge); } g2.setPaint(this.paint); g2.setStroke(this.stroke); g2.drawLine((int) lineX1, (int) lineY1, (int) lineX2, (int) lineY2); } /** * Tests this object for equality with another. * * @param obj the object ({@code null} permitted). * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof CategoryLineAnnotation)) { return false; } CategoryLineAnnotation that = (CategoryLineAnnotation) obj; if (!Objects.equals(this.category1, that.category1)) { return false; } if (Double.doubleToLongBits(this.value1) != Double.doubleToLongBits(that.value1)) { return false; } if (!Objects.equals(this.category2, that.category2)) { return false; } if (Double.doubleToLongBits(this.value2) != Double.doubleToLongBits(that.value2)) { return false; } if (!PaintUtils.equal(this.paint, that.paint)) { return false; } if (!Objects.equals(this.stroke, that.stroke)) { return false; } // fix the "equals not symmetric" problem if (!that.canEqual(this)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof CategoryLineAnnotation); } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = super.hashCode(); // equals calls superclass, hashCode must also result = 37 * result + Objects.hashCode(this.category1); long temp = Double.doubleToLongBits(this.value1); result = 37 * result + (int) (temp ^ (temp >>> 32)); result = 37 * result + Objects.hashCode(this.category2); temp = Double.doubleToLongBits(this.value2); result = 37 * result + (int) (temp ^ (temp >>> 32)); result = 37 * result + HashUtils.hashCodeForPaint(this.paint); result = 37 * result + Objects.hashCode(this.stroke); return result; } /** * Returns a clone of the annotation. * * @return A clone. * * @throws CloneNotSupportedException this class will not throw this * exception, but subclasses (if any) might. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.paint, stream); SerialUtils.writeStroke(this.stroke, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.paint = SerialUtils.readPaint(stream); this.stroke = SerialUtils.readStroke(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/annotations/CategoryPointerAnnotation.java000066400000000000000000000427421463604235500334400ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------ * CategoryPointerAnnotation.java * ------------------------------ * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Peter Kolb (patch 2809117); * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.annotations; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Stroke; import java.awt.geom.GeneralPath; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.HashUtils; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.AnnotationChangeEvent; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.text.TextUtils; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.Args; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; import org.jfree.data.category.CategoryDataset; /** * An arrow and label that can be placed on a {@link CategoryPlot}. The arrow * is drawn at a user-definable angle so that it points towards the (category, * value) location for the annotation. *

* The arrow length (and its offset from the (category, value) location) is * controlled by the tip radius and the base radius attributes. Imagine two * circles around the (category, value) coordinate: the inner circle defined by * the tip radius, and the outer circle defined by the base radius. Now, draw * the arrow starting at some point on the outer circle (the point is * determined by the angle), with the arrow tip being drawn at a corresponding * point on the inner circle. */ public class CategoryPointerAnnotation extends CategoryTextAnnotation implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -4031161445009858551L; /** The default tip radius (in Java2D units). */ public static final double DEFAULT_TIP_RADIUS = 10.0; /** The default base radius (in Java2D units). */ public static final double DEFAULT_BASE_RADIUS = 30.0; /** The default label offset (in Java2D units). */ public static final double DEFAULT_LABEL_OFFSET = 3.0; /** The default arrow length (in Java2D units). */ public static final double DEFAULT_ARROW_LENGTH = 5.0; /** The default arrow width (in Java2D units). */ public static final double DEFAULT_ARROW_WIDTH = 3.0; /** The angle of the arrow's line (in radians). */ private double angle; /** * The radius from the (x, y) point to the tip of the arrow (in Java2D * units). */ private double tipRadius; /** * The radius from the (x, y) point to the start of the arrow line (in * Java2D units). */ private double baseRadius; /** The length of the arrow head (in Java2D units). */ private double arrowLength; /** The arrow width (in Java2D units, per side). */ private double arrowWidth; /** The arrow stroke. */ private transient Stroke arrowStroke; /** The arrow paint. */ private transient Paint arrowPaint; /** The radius from the base point to the anchor point for the label. */ private double labelOffset; /** * Creates a new label and arrow annotation. * * @param label the label ({@code null} permitted). * @param key the category key. * @param value the y-value (measured against the chart's range axis). * @param angle the angle of the arrow's line (in radians). */ public CategoryPointerAnnotation(String label, Comparable key, double value, double angle) { super(label, key, value); this.angle = angle; this.tipRadius = DEFAULT_TIP_RADIUS; this.baseRadius = DEFAULT_BASE_RADIUS; this.arrowLength = DEFAULT_ARROW_LENGTH; this.arrowWidth = DEFAULT_ARROW_WIDTH; this.labelOffset = DEFAULT_LABEL_OFFSET; this.arrowStroke = new BasicStroke(1.0f); this.arrowPaint = Color.BLACK; } /** * Returns the angle of the arrow. * * @return The angle (in radians). * * @see #setAngle(double) */ public double getAngle() { return this.angle; } /** * Sets the angle of the arrow and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param angle the angle (in radians). * * @see #getAngle() */ public void setAngle(double angle) { this.angle = angle; fireAnnotationChanged(); } /** * Returns the tip radius. * * @return The tip radius (in Java2D units). * * @see #setTipRadius(double) */ public double getTipRadius() { return this.tipRadius; } /** * Sets the tip radius and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param radius the radius (in Java2D units). * * @see #getTipRadius() */ public void setTipRadius(double radius) { this.tipRadius = radius; fireAnnotationChanged(); } /** * Returns the base radius. * * @return The base radius (in Java2D units). * * @see #setBaseRadius(double) */ public double getBaseRadius() { return this.baseRadius; } /** * Sets the base radius and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param radius the radius (in Java2D units). * * @see #getBaseRadius() */ public void setBaseRadius(double radius) { this.baseRadius = radius; fireAnnotationChanged(); } /** * Returns the label offset. * * @return The label offset (in Java2D units). * * @see #setLabelOffset(double) */ public double getLabelOffset() { return this.labelOffset; } /** * Sets the label offset (from the arrow base, continuing in a straight * line, in Java2D units) and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param offset the offset (in Java2D units). * * @see #getLabelOffset() */ public void setLabelOffset(double offset) { this.labelOffset = offset; fireAnnotationChanged(); } /** * Returns the arrow length. * * @return The arrow length. * * @see #setArrowLength(double) */ public double getArrowLength() { return this.arrowLength; } /** * Sets the arrow length and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param length the length. * * @see #getArrowLength() */ public void setArrowLength(double length) { this.arrowLength = length; fireAnnotationChanged(); } /** * Returns the arrow width. * * @return The arrow width (in Java2D units). * * @see #setArrowWidth(double) */ public double getArrowWidth() { return this.arrowWidth; } /** * Sets the arrow width and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param width the width (in Java2D units). * * @see #getArrowWidth() */ public void setArrowWidth(double width) { this.arrowWidth = width; fireAnnotationChanged(); } /** * Returns the stroke used to draw the arrow line. * * @return The arrow stroke (never {@code null}). * * @see #setArrowStroke(Stroke) */ public Stroke getArrowStroke() { return this.arrowStroke; } /** * Sets the stroke used to draw the arrow line and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getArrowStroke() */ public void setArrowStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.arrowStroke = stroke; fireAnnotationChanged(); } /** * Returns the paint used for the arrow. * * @return The arrow paint (never {@code null}). * * @see #setArrowPaint(Paint) */ public Paint getArrowPaint() { return this.arrowPaint; } /** * Sets the paint used for the arrow and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param paint the arrow paint ({@code null} not permitted). * * @see #getArrowPaint() */ public void setArrowPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.arrowPaint = paint; fireAnnotationChanged(); } /** * Draws the annotation. * * @param g2 the graphics device. * @param plot the plot. * @param dataArea the data area. * @param domainAxis the domain axis. * @param rangeAxis the range axis. */ @Override public void draw(Graphics2D g2, CategoryPlot plot, Rectangle2D dataArea, CategoryAxis domainAxis, ValueAxis rangeAxis) { PlotOrientation orientation = plot.getOrientation(); RectangleEdge domainEdge = Plot.resolveDomainAxisLocation( plot.getDomainAxisLocation(), orientation); RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation( plot.getRangeAxisLocation(), orientation); CategoryDataset dataset = plot.getDataset(); int catIndex = dataset.getColumnIndex(getCategory()); int catCount = dataset.getColumnCount(); double j2DX = domainAxis.getCategoryMiddle(catIndex, catCount, dataArea, domainEdge); double j2DY = rangeAxis.valueToJava2D(getValue(), dataArea, rangeEdge); if (orientation == PlotOrientation.HORIZONTAL) { double temp = j2DX; j2DX = j2DY; j2DY = temp; } double startX = j2DX + Math.cos(this.angle) * this.baseRadius; double startY = j2DY + Math.sin(this.angle) * this.baseRadius; double endX = j2DX + Math.cos(this.angle) * this.tipRadius; double endY = j2DY + Math.sin(this.angle) * this.tipRadius; double arrowBaseX = endX + Math.cos(this.angle) * this.arrowLength; double arrowBaseY = endY + Math.sin(this.angle) * this.arrowLength; double arrowLeftX = arrowBaseX + Math.cos(this.angle + Math.PI / 2.0) * this.arrowWidth; double arrowLeftY = arrowBaseY + Math.sin(this.angle + Math.PI / 2.0) * this.arrowWidth; double arrowRightX = arrowBaseX - Math.cos(this.angle + Math.PI / 2.0) * this.arrowWidth; double arrowRightY = arrowBaseY - Math.sin(this.angle + Math.PI / 2.0) * this.arrowWidth; GeneralPath arrow = new GeneralPath(); arrow.moveTo((float) endX, (float) endY); arrow.lineTo((float) arrowLeftX, (float) arrowLeftY); arrow.lineTo((float) arrowRightX, (float) arrowRightY); arrow.closePath(); g2.setStroke(this.arrowStroke); g2.setPaint(this.arrowPaint); Line2D line = new Line2D.Double(startX, startY, arrowBaseX, arrowBaseY); g2.draw(line); g2.fill(arrow); // draw the label g2.setFont(getFont()); g2.setPaint(getPaint()); double labelX = j2DX + Math.cos(this.angle) * (this.baseRadius + this.labelOffset); double labelY = j2DY + Math.sin(this.angle) * (this.baseRadius + this.labelOffset); /* Rectangle2D hotspot = */ TextUtils.drawAlignedString(getText(), g2, (float) labelX, (float) labelY, getTextAnchor()); // TODO: implement the entity for the annotation } /** * Tests this annotation for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof CategoryPointerAnnotation)) { return false; } CategoryPointerAnnotation that = (CategoryPointerAnnotation) obj; if (Double.doubleToLongBits(this.angle) != Double.doubleToLongBits(that.angle)) { return false; } if (Double.doubleToLongBits(this.tipRadius) != Double.doubleToLongBits(that.tipRadius)) { return false; } if (Double.doubleToLongBits(this.baseRadius) != Double.doubleToLongBits(that.baseRadius)) { return false; } if (Double.doubleToLongBits(this.arrowLength) != Double.doubleToLongBits(that.arrowLength)) { return false; } if (Double.doubleToLongBits(this.arrowWidth) != Double.doubleToLongBits(that.arrowWidth)) { return false; } if (!PaintUtils.equal(this.arrowPaint, that.arrowPaint)) { return false; } if (!Objects.equals(this.arrowStroke, that.arrowStroke)) { return false; } if (Double.doubleToLongBits(this.labelOffset) != Double.doubleToLongBits(that.labelOffset)) { return false; } // fix the "equals not symmetric" problem if (!that.canEqual(this)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof CategoryPointerAnnotation); } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = super.hashCode(); // equals calls superclass, hashCode must also long temp = Double.doubleToLongBits(this.angle); result = 37 * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(this.tipRadius); result = 37 * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(this.baseRadius); result = 37 * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(this.arrowLength); result = 37 * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(this.arrowWidth); result = 37 * result + (int) (temp ^ (temp >>> 32)); result = 37 * result + HashUtils.hashCodeForPaint(this.arrowPaint); result = 37 * result + Objects.hashCode(this.arrowStroke); temp = Double.doubleToLongBits(this.labelOffset); result = 37 * result + (int) (temp ^ (temp >>> 32)); return result; } /** * Returns a clone of the annotation. * * @return A clone. * * @throws CloneNotSupportedException if the annotation can't be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.arrowPaint, stream); SerialUtils.writeStroke(this.arrowStroke, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.arrowPaint = SerialUtils.readPaint(stream); this.arrowStroke = SerialUtils.readStroke(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/annotations/CategoryTextAnnotation.java000066400000000000000000000221141463604235500327330ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * CategoryTextAnnotation.java * --------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Peter Kolb (patch 2809117); * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.annotations; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.axis.CategoryAnchor; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.AnnotationChangeEvent; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.text.TextUtils; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.category.CategoryDataset; /** * A text annotation that can be placed on a {@link CategoryPlot}. */ public class CategoryTextAnnotation extends TextAnnotation implements CategoryAnnotation, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 3333360090781320147L; /** The category. */ private Comparable category; /** The category anchor (START, MIDDLE, or END). */ private CategoryAnchor categoryAnchor; /** The value. */ private double value; /** * Creates a new annotation to be displayed at the given location. * * @param text the text ({@code null} not permitted). * @param category the category ({@code null} not permitted). * @param value the value. */ public CategoryTextAnnotation(String text, Comparable category, double value) { super(text); Args.nullNotPermitted(category, "category"); this.category = category; this.value = value; this.categoryAnchor = CategoryAnchor.MIDDLE; } /** * Returns the category. * * @return The category (never {@code null}). * * @see #setCategory(Comparable) */ public Comparable getCategory() { return this.category; } /** * Sets the category that the annotation attaches to and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param category the category ({@code null} not permitted). * * @see #getCategory() */ public void setCategory(Comparable category) { Args.nullNotPermitted(category, "category"); this.category = category; fireAnnotationChanged(); } /** * Returns the category anchor point. * * @return The category anchor point. * * @see #setCategoryAnchor(CategoryAnchor) */ public CategoryAnchor getCategoryAnchor() { return this.categoryAnchor; } /** * Sets the category anchor point and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param anchor the anchor point ({@code null} not permitted). * * @see #getCategoryAnchor() */ public void setCategoryAnchor(CategoryAnchor anchor) { Args.nullNotPermitted(anchor, "anchor"); this.categoryAnchor = anchor; fireAnnotationChanged(); } /** * Returns the value that the annotation attaches to. * * @return The value. * * @see #setValue(double) */ public double getValue() { return this.value; } /** * Sets the value and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param value the value. * * @see #getValue() */ public void setValue(double value) { this.value = value; fireAnnotationChanged(); } /** * Draws the annotation. * * @param g2 the graphics device. * @param plot the plot. * @param dataArea the data area. * @param domainAxis the domain axis. * @param rangeAxis the range axis. */ @Override public void draw(Graphics2D g2, CategoryPlot plot, Rectangle2D dataArea, CategoryAxis domainAxis, ValueAxis rangeAxis) { CategoryDataset dataset = plot.getDataset(); int catIndex = dataset.getColumnIndex(this.category); int catCount = dataset.getColumnCount(); float anchorX = 0.0f; float anchorY = 0.0f; PlotOrientation orientation = plot.getOrientation(); RectangleEdge domainEdge = Plot.resolveDomainAxisLocation( plot.getDomainAxisLocation(), orientation); RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation( plot.getRangeAxisLocation(), orientation); if (orientation == PlotOrientation.HORIZONTAL) { anchorY = (float) domainAxis.getCategoryJava2DCoordinate( this.categoryAnchor, catIndex, catCount, dataArea, domainEdge); anchorX = (float) rangeAxis.valueToJava2D(this.value, dataArea, rangeEdge); } else if (orientation == PlotOrientation.VERTICAL) { anchorX = (float) domainAxis.getCategoryJava2DCoordinate( this.categoryAnchor, catIndex, catCount, dataArea, domainEdge); anchorY = (float) rangeAxis.valueToJava2D(this.value, dataArea, rangeEdge); } g2.setFont(getFont()); g2.setPaint(getPaint()); TextUtils.drawRotatedString(getText(), g2, anchorX, anchorY, getTextAnchor(), getRotationAngle(), getRotationAnchor()); } /** * Tests this object for equality with another. * * @param obj the object ({@code null} permitted). * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof CategoryTextAnnotation)) { return false; } CategoryTextAnnotation that = (CategoryTextAnnotation) obj; if (!Objects.equals(this.category, that.category)) { return false; } if (!Objects.equals(this.categoryAnchor, that.categoryAnchor)) { return false; } if (Double.doubleToLongBits(this.value) != Double.doubleToLongBits(that.value)) { return false; } // fix the "equals not symmetric" problem if (!that.canEqual(this)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof CategoryTextAnnotation); } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = super.hashCode(); // equals calls superclass, hashCode must also result = 37 * result + Objects.hashCode(this.category); result = 37 * result + Objects.hashCode(this.categoryAnchor); long temp = Double.doubleToLongBits(this.value); result = 37 * result + (int) (temp ^ (temp >>> 32)); return result; } /** * Returns a clone of the annotation. * * @return A clone. * * @throws CloneNotSupportedException this class will not throw this * exception, but subclasses (if any) might. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/annotations/TextAnnotation.java000066400000000000000000000251131463604235500312370ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * TextAnnotation.java * ------------------- * (C) Copyright 2002-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Peter Kolb (patch 2809117); * Martin Hoeller; * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.annotations; import java.awt.Color; import java.awt.Font; import java.awt.Paint; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.HashUtils; import org.jfree.chart.event.AnnotationChangeEvent; import org.jfree.chart.ui.TextAnchor; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.SerialUtils; /** * A base class for text annotations. This class records the content but not * the location of the annotation. */ public class TextAnnotation extends AbstractAnnotation implements Serializable { /** For serialization. */ private static final long serialVersionUID = 7008912287533127432L; /** The default font. */ public static final Font DEFAULT_FONT = new Font("SansSerif", Font.PLAIN, 10); /** The default paint. */ public static final Paint DEFAULT_PAINT = Color.BLACK; /** The default text anchor. */ public static final TextAnchor DEFAULT_TEXT_ANCHOR = TextAnchor.CENTER; /** The default rotation anchor. */ public static final TextAnchor DEFAULT_ROTATION_ANCHOR = TextAnchor.CENTER; /** The default rotation angle. */ public static final double DEFAULT_ROTATION_ANGLE = 0.0; /** The text. */ private String text; /** The font. */ private Font font; /** The paint. */ private transient Paint paint; /** The text anchor. */ private TextAnchor textAnchor; /** The rotation anchor. */ private TextAnchor rotationAnchor; /** The rotation angle. */ private double rotationAngle; /** * Creates a text annotation with default settings. * * @param text the text ({@code null} not permitted). */ protected TextAnnotation(String text) { super(); Args.nullNotPermitted(text, "text"); this.text = text; this.font = DEFAULT_FONT; this.paint = DEFAULT_PAINT; this.textAnchor = DEFAULT_TEXT_ANCHOR; this.rotationAnchor = DEFAULT_ROTATION_ANCHOR; this.rotationAngle = DEFAULT_ROTATION_ANGLE; } /** * Returns the text for the annotation. * * @return The text (never {@code null}). * * @see #setText(String) */ public String getText() { return this.text; } /** * Sets the text for the annotation and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param text the text ({@code null} not permitted). * * @see #getText() */ public void setText(String text) { Args.nullNotPermitted(text, "text"); this.text = text; fireAnnotationChanged(); } /** * Returns the font for the annotation. * * @return The font (never {@code null}). * * @see #setFont(Font) */ public Font getFont() { return this.font; } /** * Sets the font for the annotation and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param font the font ({@code null} not permitted). * * @see #getFont() */ public void setFont(Font font) { Args.nullNotPermitted(font, "font"); this.font = font; fireAnnotationChanged(); } /** * Returns the paint for the annotation. * * @return The paint (never {@code null}). * * @see #setPaint(Paint) */ public Paint getPaint() { return this.paint; } /** * Sets the paint for the annotation and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getPaint() */ public void setPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.paint = paint; fireAnnotationChanged(); } /** * Returns the text anchor. * * @return The text anchor. * * @see #setTextAnchor(TextAnchor) */ public TextAnchor getTextAnchor() { return this.textAnchor; } /** * Sets the text anchor (the point on the text bounding rectangle that is * aligned to the (x, y) coordinate of the annotation) and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param anchor the anchor point ({@code null} not permitted). * * @see #getTextAnchor() */ public void setTextAnchor(TextAnchor anchor) { Args.nullNotPermitted(anchor, "anchor"); this.textAnchor = anchor; fireAnnotationChanged(); } /** * Returns the rotation anchor. * * @return The rotation anchor point (never {@code null}). * * @see #setRotationAnchor(TextAnchor) */ public TextAnchor getRotationAnchor() { return this.rotationAnchor; } /** * Sets the rotation anchor point and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param anchor the anchor ({@code null} not permitted). * * @see #getRotationAnchor() */ public void setRotationAnchor(TextAnchor anchor) { Args.nullNotPermitted(anchor, "anchor"); this.rotationAnchor = anchor; fireAnnotationChanged(); } /** * Returns the rotation angle in radians. * * @return The rotation angle. * * @see #setRotationAngle(double) */ public double getRotationAngle() { return this.rotationAngle; } /** * Sets the rotation angle and sends an {@link AnnotationChangeEvent} to * all registered listeners. The angle is measured clockwise in radians. * * @param angle the angle (in radians). * * @see #getRotationAngle() */ public void setRotationAngle(double angle) { this.rotationAngle = angle; fireAnnotationChanged(); } /** * Tests this object for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } // now try to reject equality... if (!(obj instanceof TextAnnotation)) { return false; } TextAnnotation that = (TextAnnotation) obj; if (!Objects.equals(this.text, that.getText())) { return false; } if (!Objects.equals(this.font, that.getFont())) { return false; } if (!PaintUtils.equal(this.paint, that.getPaint())) { return false; } if (!Objects.equals(this.textAnchor, that.getTextAnchor())) { return false; } if (!Objects.equals(this.rotationAnchor, that.getRotationAnchor())) { return false; } if (Double.doubleToLongBits(this.rotationAngle) != Double.doubleToLongBits(that.rotationAngle)) { return false; } // fix the "equals not symmetric" problem if (!that.canEqual(this)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof TextAnnotation); } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = super.hashCode(); // equals calls superclass, hashCode must also result = 37 * result + Objects.hashCode(this.font); result = 37 * result + HashUtils.hashCodeForPaint(this.paint); result = 37 * result + Objects.hashCode(this.rotationAnchor); long temp = Double.doubleToLongBits(this.rotationAngle); result = 37 * result + (int) (temp ^ (temp >>> 32)); result = 37 * result + Objects.hashCode(this.text); result = 37 * result + Objects.hashCode(this.textAnchor); return result; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.paint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.paint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/annotations/XYAnnotation.java000066400000000000000000000050321463604235500306510ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * XYAnnotation.java * ----------------- * (C) Copyright 2002-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Peter Kolb (patch 2809117); * */ package org.jfree.chart.annotations; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; /** * The interface that must be supported by annotations that are to be added to * an {@link XYPlot}. Note that, in JFreeChart 1.0.14, a non-compatible * change has been made to this interface (it now extends the Annotation * interface to support change notifications). */ public interface XYAnnotation extends Annotation { /** * Draws the annotation. * * @param g2 the graphics device. * @param plot the plot. * @param dataArea the data area. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param rendererIndex the renderer index. * @param info an optional info object that will be populated with * entity information. */ void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, ValueAxis domainAxis, ValueAxis rangeAxis, int rendererIndex, PlotRenderingInfo info); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/annotations/XYAnnotationBoundsInfo.java000066400000000000000000000043201463604235500326370ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * XYAnnotationBoundsInfo.java * --------------------------- * (C) Copyright 2009-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.annotations; import org.jfree.data.Range; /** * An interface that supplies information about the bounds of the annotation. */ public interface XYAnnotationBoundsInfo { /** * Returns a flag that determines whether or not the annotation's * bounds should be taken into account for auto-range calculations on * the axes that the annotation is plotted against. * * @return A boolean. */ boolean getIncludeInDataBounds(); /** * Returns the range of x-values (in data space) that the annotation * uses. * * @return The x-range. */ Range getXRange(); /** * Returns the range of y-values (in data space) that the annotation * uses. * * @return The y-range. */ Range getYRange(); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/annotations/XYBoxAnnotation.java000066400000000000000000000324701463604235500313300ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * XYBoxAnnotation.java * -------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Peter Kolb (see patch 2809117); Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.annotations; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Stroke; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.HashUtils; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.Args; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; /** * A box annotation that can be placed on an {@link XYPlot}. The * box coordinates are specified in data space. */ public class XYBoxAnnotation extends AbstractXYAnnotation implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 6764703772526757457L; /** The lower x-coordinate. */ private double x0; /** The lower y-coordinate. */ private double y0; /** The upper x-coordinate. */ private double x1; /** The upper y-coordinate. */ private double y1; /** The stroke used to draw the box outline. */ private transient Stroke stroke; /** The paint used to draw the box outline. */ private transient Paint outlinePaint; /** The paint used to fill the box. */ private transient Paint fillPaint; /** * Creates a new annotation (where, by default, the box is drawn * with a black outline). * * @param x0 the lower x-coordinate of the box (in data space). * @param y0 the lower y-coordinate of the box (in data space). * @param x1 the upper x-coordinate of the box (in data space). * @param y1 the upper y-coordinate of the box (in data space). */ public XYBoxAnnotation(double x0, double y0, double x1, double y1) { this(x0, y0, x1, y1, new BasicStroke(1.0f), Color.BLACK); } /** * Creates a new annotation where the box is drawn as an outline using * the specified {@code stroke} and {@code outlinePaint}. * * @param x0 the lower x-coordinate of the box (in data space). * @param y0 the lower y-coordinate of the box (in data space). * @param x1 the upper x-coordinate of the box (in data space). * @param y1 the upper y-coordinate of the box (in data space). * @param stroke the shape stroke ({@code null} permitted). * @param outlinePaint the shape color ({@code null} permitted). */ public XYBoxAnnotation(double x0, double y0, double x1, double y1, Stroke stroke, Paint outlinePaint) { this(x0, y0, x1, y1, stroke, outlinePaint, null); } /** * Creates a new annotation. * * @param x0 the lower x-coordinate of the box (in data space, must be finite). * @param y0 the lower y-coordinate of the box (in data space, must be finite). * @param x1 the upper x-coordinate of the box (in data space, must be finite). * @param y1 the upper y-coordinate of the box (in data space, must be finite). * @param stroke the shape stroke ({@code null} permitted). * @param outlinePaint the shape color ({@code null} permitted). * @param fillPaint the paint used to fill the shape ({@code null} * permitted). */ public XYBoxAnnotation(double x0, double y0, double x1, double y1, Stroke stroke, Paint outlinePaint, Paint fillPaint) { super(); Args.requireFinite(x0, "x0"); Args.requireFinite(y0, "y0"); Args.requireFinite(x1, "x1"); Args.requireFinite(y1, "y1"); this.x0 = x0; this.y0 = y0; this.x1 = x1; this.y1 = y1; this.stroke = stroke; this.outlinePaint = outlinePaint; this.fillPaint = fillPaint; } /** * Returns the x-coordinate for the bottom left corner of the box (set in the * constructor). * * @return The x-coordinate for the bottom left corner of the box. */ public double getX0() { return x0; } /** * Returns the y-coordinate for the bottom left corner of the box (set in the * constructor). * * @return The y-coordinate for the bottom left corner of the box. */ public double getY0() { return y0; } /** * Returns the x-coordinate for the top right corner of the box (set in the * constructor). * * @return The x-coordinate for the top right corner of the box. */ public double getX1() { return x1; } /** * Returns the y-coordinate for the top right corner of the box (set in the * constructor). * * @return The y-coordinate for the top right corner of the box. */ public double getY1() { return y1; } /** * Returns the stroke used for the box outline. * * @return The stroke (possibly {@code null}). */ public Stroke getStroke() { return stroke; } /** * Returns the paint used for the box outline. * * @return The paint (possibly {@code null}). */ public Paint getOutlinePaint() { return outlinePaint; } /** * Returns the paint used for the box fill. * * @return The paint (possibly {@code null}). */ public Paint getFillPaint() { return fillPaint; } /** * Draws the annotation. This method is usually called by the * {@link XYPlot} class, you shouldn't need to call it directly. * * @param g2 the graphics device. * @param plot the plot. * @param dataArea the data area. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param rendererIndex the renderer index. * @param info the plot rendering info. */ @Override public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, ValueAxis domainAxis, ValueAxis rangeAxis, int rendererIndex, PlotRenderingInfo info) { PlotOrientation orientation = plot.getOrientation(); RectangleEdge domainEdge = Plot.resolveDomainAxisLocation( plot.getDomainAxisLocation(), orientation); RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation( plot.getRangeAxisLocation(), orientation); double transX0 = domainAxis.valueToJava2D(this.x0, dataArea, domainEdge); double transY0 = rangeAxis.valueToJava2D(this.y0, dataArea, rangeEdge); double transX1 = domainAxis.valueToJava2D(this.x1, dataArea, domainEdge); double transY1 = rangeAxis.valueToJava2D(this.y1, dataArea, rangeEdge); Rectangle2D box = null; if (orientation == PlotOrientation.HORIZONTAL) { box = new Rectangle2D.Double(transY0, transX1, transY1 - transY0, transX0 - transX1); } else if (orientation == PlotOrientation.VERTICAL) { box = new Rectangle2D.Double(transX0, transY1, transX1 - transX0, transY0 - transY1); } if (this.fillPaint != null) { g2.setPaint(this.fillPaint); g2.fill(box); } if (this.stroke != null && this.outlinePaint != null) { g2.setPaint(this.outlinePaint); g2.setStroke(this.stroke); g2.draw(box); } addEntity(info, box, rendererIndex, getToolTipText(), getURL()); } /** * Tests this annotation for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYBoxAnnotation)) { return false; } XYBoxAnnotation that = (XYBoxAnnotation) obj; if (Double.doubleToLongBits(this.x0) != Double.doubleToLongBits(that.x0)) { return false; } if (Double.doubleToLongBits(this.y0) != Double.doubleToLongBits(that.y0)) { return false; } if (Double.doubleToLongBits(this.x1) != Double.doubleToLongBits(that.x1)) { return false; } if (Double.doubleToLongBits(this.y1) != Double.doubleToLongBits(that.y1)) { return false; } if (!Objects.equals(this.stroke, that.stroke)) { return false; } if (!PaintUtils.equal(this.outlinePaint, that.outlinePaint)) { return false; } if (!PaintUtils.equal(this.fillPaint, that.fillPaint)) { return false; } // fix the "equals not symmetric" problem if (!that.canEqual(this)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof XYBoxAnnotation); } /** * Returns a hash code. * * @return A hash code. */ @Override public int hashCode() { int hash = super.hashCode(); // equals calls superclass, hashCode must also hash = 67 * hash + (int) (Double.doubleToLongBits(this.x0) ^ (Double.doubleToLongBits(this.x0) >>> 32)); hash = 67 * hash + (int) (Double.doubleToLongBits(this.y0) ^ (Double.doubleToLongBits(this.y0) >>> 32)); hash = 67 * hash + (int) (Double.doubleToLongBits(this.x1) ^ (Double.doubleToLongBits(this.x1) >>> 32)); hash = 67 * hash + (int) (Double.doubleToLongBits(this.y1) ^ (Double.doubleToLongBits(this.y1) >>> 32)); hash = 67 * hash + Objects.hashCode(this.stroke); hash = 67 * hash + HashUtils.hashCodeForPaint(this.outlinePaint); hash = 67 * hash + HashUtils.hashCodeForPaint(this.fillPaint); return hash; } /** * Returns a clone. * * @return A clone. * * @throws CloneNotSupportedException not thrown by this class, but may be * by subclasses. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Provides serialization support. * * @param stream the output stream ({@code null} not permitted). * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeStroke(this.stroke, stream); SerialUtils.writePaint(this.outlinePaint, stream); SerialUtils.writePaint(this.fillPaint, stream); } /** * Provides serialization support. * * @param stream the input stream ({@code null} not permitted). * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.stroke = SerialUtils.readStroke(stream); this.outlinePaint = SerialUtils.readPaint(stream); this.fillPaint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/annotations/XYDataImageAnnotation.java000066400000000000000000000302741463604235500324140ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * XYDataImageAnnotation.java * -------------------------- * (C) Copyright 2008-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Peter Kolb (patch 2809117); Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.annotations; import java.awt.Graphics2D; import java.awt.Image; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Objects; import org.jfree.chart.axis.AxisLocation; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.Range; /** * An annotation that allows an image to be placed within a rectangle specified * in data coordinates on an {@link XYPlot}. Note that this annotation * is not currently serializable, so don't use it if you plan on serializing * your chart(s). */ public class XYDataImageAnnotation extends AbstractXYAnnotation implements Cloneable, PublicCloneable, XYAnnotationBoundsInfo { /** The image. */ private transient Image image; /** * The x-coordinate (in data space). */ private double x; /** * The y-coordinate (in data space). */ private double y; /** * The image display area width in data coordinates. */ private double w; /** * The image display area height in data coordinates. */ private double h; /** * A flag indicating whether or not the annotation should contribute to * the data range for a plot/renderer. */ private boolean includeInDataBounds; /** * Creates a new annotation to be displayed within the specified rectangle. * * @param image the image ({@code null} not permitted). * @param x the x-coordinate (in data space). * @param y the y-coordinate (in data space). * @param w the image display area width. * @param h the image display area height. */ public XYDataImageAnnotation(Image image, double x, double y, double w, double h) { this(image, x, y, w, h, false); } /** * Creates a new annotation to be displayed within the specified rectangle. * * @param image the image ({@code null} not permitted). * @param x the x-coordinate (in data space). * @param y the y-coordinate (in data space). * @param w the image display area width. * @param h the image display area height. * @param includeInDataBounds a flag that controls whether or not the * annotation is included in the data bounds for the axis autoRange. */ public XYDataImageAnnotation(Image image, double x, double y, double w, double h, boolean includeInDataBounds) { super(); Args.nullNotPermitted(image, "image"); this.image = image; this.x = x; this.y = y; this.w = w; this.h = h; this.includeInDataBounds = includeInDataBounds; } /** * Returns the image for the annotation. * * @return The image. */ public Image getImage() { return this.image; } /** * Returns the x-coordinate (in data space) for the annotation. * * @return The x-coordinate. */ public double getX() { return this.x; } /** * Returns the y-coordinate (in data space) for the annotation. * * @return The y-coordinate. */ public double getY() { return this.y; } /** * Returns the width (in data space) of the data rectangle into which the * image will be drawn. * * @return The width. */ public double getWidth() { return this.w; } /** * Returns the height (in data space) of the data rectangle into which the * image will be drawn. * * @return The height. */ public double getHeight() { return this.h; } /** * Returns the flag that controls whether or not the annotation should * contribute to the autoRange for the axis it is plotted against. * * @return A boolean. */ @Override public boolean getIncludeInDataBounds() { return this.includeInDataBounds; } /** * Returns the x-range for the annotation. * * @return The range. */ @Override public Range getXRange() { return new Range(this.x, this.x + this.w); } /** * Returns the y-range for the annotation. * * @return The range. */ @Override public Range getYRange() { return new Range(this.y, this.y + this.h); } /** * Draws the annotation. This method is called by the drawing code in the * {@link XYPlot} class, you don't normally need to call this method * directly. * * @param g2 the graphics device. * @param plot the plot. * @param dataArea the data area. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param rendererIndex the renderer index. * @param info if supplied, this info object will be populated with * entity information. */ @Override public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, ValueAxis domainAxis, ValueAxis rangeAxis, int rendererIndex, PlotRenderingInfo info) { PlotOrientation orientation = plot.getOrientation(); AxisLocation xAxisLocation = plot.getDomainAxisLocation(); AxisLocation yAxisLocation = plot.getRangeAxisLocation(); RectangleEdge xEdge = Plot.resolveDomainAxisLocation(xAxisLocation, orientation); RectangleEdge yEdge = Plot.resolveRangeAxisLocation(yAxisLocation, orientation); float j2DX0 = (float) domainAxis.valueToJava2D(this.x, dataArea, xEdge); float j2DY0 = (float) rangeAxis.valueToJava2D(this.y, dataArea, yEdge); float j2DX1 = (float) domainAxis.valueToJava2D(this.x + this.w, dataArea, xEdge); float j2DY1 = (float) rangeAxis.valueToJava2D(this.y + this.h, dataArea, yEdge); float xx0 = 0.0f; float yy0 = 0.0f; float xx1 = 0.0f; float yy1 = 0.0f; if (orientation == PlotOrientation.HORIZONTAL) { xx0 = j2DY0; xx1 = j2DY1; yy0 = j2DX0; yy1 = j2DX1; } else if (orientation == PlotOrientation.VERTICAL) { xx0 = j2DX0; xx1 = j2DX1; yy0 = j2DY0; yy1 = j2DY1; } // TODO: rotate the image when drawn with horizontal orientation? g2.drawImage(this.image, (int) xx0, (int) Math.min(yy0, yy1), (int) (xx1 - xx0), (int) Math.abs(yy1 - yy0), null); String toolTip = getToolTipText(); String url = getURL(); if (toolTip != null || url != null) { addEntity(info, new Rectangle2D.Float(xx0, yy0, (xx1 - xx0), (yy1 - yy0)), rendererIndex, toolTip, url); } } /** * Tests this object for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYDataImageAnnotation)) { return false; } XYDataImageAnnotation that = (XYDataImageAnnotation) obj; if (Double.doubleToLongBits(this.x) != Double.doubleToLongBits(that.x)) { return false; } if (Double.doubleToLongBits(this.y) != Double.doubleToLongBits(that.y)) { return false; } if (Double.doubleToLongBits(this.w) != Double.doubleToLongBits(that.w)) { return false; } if (Double.doubleToLongBits(this.h) != Double.doubleToLongBits(that.h)) { return false; } if (this.includeInDataBounds != that.includeInDataBounds) { return false; } if (!Objects.equals(this.image, that.image)) { return false; } // fix the "equals not symmetric" problem if (!that.canEqual(this)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof XYDataImageAnnotation); } /** * Returns a hash code for this object. * * @return A hash code. */ @Override public int hashCode() { int hash = super.hashCode(); // equals calls superclass, hashCode must also hash = 89 * hash + Objects.hashCode(this.image); hash = 89 * hash + (int) (Double.doubleToLongBits(this.x) ^ (Double.doubleToLongBits(this.x) >>> 32)); hash = 89 * hash + (int) (Double.doubleToLongBits(this.y) ^ (Double.doubleToLongBits(this.y) >>> 32)); hash = 89 * hash + (int) (Double.doubleToLongBits(this.w) ^ (Double.doubleToLongBits(this.w) >>> 32)); hash = 89 * hash + (int) (Double.doubleToLongBits(this.h) ^ (Double.doubleToLongBits(this.h) >>> 32)); hash = 89 * hash + (this.includeInDataBounds ? 1 : 0); return hash; } /** * Returns a clone of the annotation. * * @return A clone. * * @throws CloneNotSupportedException if the annotation can't be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); // FIXME //SerialUtils.writeImage(this.image, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); // FIXME //this.image = SerialUtils.readImage(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/annotations/XYDataRangeAnnotation.java000066400000000000000000000077401463604235500324300ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * XYDataRangeAnnotation.java * -------------------------- * (C) Copyright 2021-present, by Yuri Blankenstein and Contributors. * * Original Author: Yuri Blankenstein (for ESI TNO); * */ package org.jfree.chart.annotations; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.data.Range; /** * This annotation can be put on an {@link XYPlot} to ensure a visible data * range. The range values should be specified w.r.t. to the first (index 0) * domain or range axis. * * @see XYPlot#getDataRange(ValueAxis) * @see XYPlot#getDomainAxis() * @see XYPlot#getRangeAxis() */ public class XYDataRangeAnnotation extends AbstractXYAnnotation implements XYAnnotationBoundsInfo { private static final long serialVersionUID = 2058170262687146829L; private final Range minimumDomainRange; private final Range minimumRangeRange; /** * Creates a new instance. * * @param minimumDomainRange the range to ensure on the domain axis * ({@code null} permitted). * @param minimumRangeRange the range to ensure on the range axis * ({@code null} permitted). */ public XYDataRangeAnnotation(Range minimumDomainRange, Range minimumRangeRange) { this.minimumDomainRange = minimumDomainRange; this.minimumRangeRange = minimumRangeRange; } @Override public boolean getIncludeInDataBounds() { return true; } @Override public Range getXRange() { return minimumDomainRange; } @Override public Range getYRange() { return minimumRangeRange; } @Override public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, ValueAxis domainAxis, ValueAxis rangeAxis, int rendererIndex, PlotRenderingInfo info) { // Nothing to do here } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + ((minimumDomainRange == null) ? 0 : minimumDomainRange.hashCode()); result = prime * result + ((minimumRangeRange == null) ? 0 : minimumRangeRange.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!super.equals(obj)) return false; if (getClass() != obj.getClass()) return false; XYDataRangeAnnotation other = (XYDataRangeAnnotation) obj; if (minimumDomainRange == null) { if (other.minimumDomainRange != null) return false; } else if (!minimumDomainRange.equals(other.minimumDomainRange)) return false; if (minimumRangeRange == null) { if (other.minimumRangeRange != null) return false; } else if (!minimumRangeRange.equals(other.minimumRangeRange)) return false; return true; } }jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/annotations/XYDrawableAnnotation.java000066400000000000000000000256721463604235500323270ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * XYDrawableAnnotation.java * ------------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.annotations; import java.awt.Graphics2D; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.Drawable; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; /** * A general annotation that can be placed on an {@link XYPlot}. */ public class XYDrawableAnnotation extends AbstractXYAnnotation implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -6540812859722691020L; /** The scaling factor. */ private double drawScaleFactor; /** The x-coordinate. */ private double x; /** The y-coordinate. */ private double y; /** The width. */ private double displayWidth; /** The height. */ private double displayHeight; /** The drawable object. */ private Drawable drawable; /** * Creates a new annotation to be displayed within the given area. * * @param x the x-coordinate for the area (must be finite). * @param y the y-coordinate for the area (must be finite). * @param width the width of the area (must be finite). * @param height the height of the area (must be finite). * @param drawable the drawable object ({@code null} not permitted). */ public XYDrawableAnnotation(double x, double y, double width, double height, Drawable drawable) { this(x, y, width, height, 1.0, drawable); } /** * Creates a new annotation to be displayed within the given area. If you * specify a {@code drawScaleFactor} of 2.0, the {@code drawable} * will be drawn at twice the requested display size then scaled down to * fit the space. * * @param x the x-coordinate for the area (must be finite). * @param y the y-coordinate for the area (must be finite). * @param displayWidth the width of the area (must be finite). * @param displayHeight the height of the area (must be finite). * @param drawScaleFactor the scaling factor for drawing (must be finite). * @param drawable the drawable object ({@code null} not permitted). */ public XYDrawableAnnotation(double x, double y, double displayWidth, double displayHeight, double drawScaleFactor, Drawable drawable) { super(); Args.nullNotPermitted(drawable, "drawable"); Args.requireFinite(x, "x"); Args.requireFinite(y, "y"); Args.requireFinite(displayWidth, "displayWidth"); Args.requireFinite(displayHeight, "displayHeight"); Args.requireFinite(drawScaleFactor, "drawScaleFactor"); this.x = x; this.y = y; this.displayWidth = displayWidth; this.displayHeight = displayHeight; this.drawScaleFactor = drawScaleFactor; this.drawable = drawable; } /** * Returns the x-coordinate (set in the constructor). * * @return The x-coordinate. */ public double getX() { return x; } /** * Returns the y-coordinate (set in the constructor). * * @return The y-coordinate. */ public double getY() { return y; } /** * Returns the display width (set in the constructor). * * @return The display width. */ public double getDisplayWidth() { return displayWidth; } /** * Returns the display height (set in the constructor). * * @return The display height. */ public double getDisplayHeight() { return displayHeight; } /** * Returns the scale factor (set in the constructor). * * @return The scale factor. */ public double getDrawScaleFactor() { return drawScaleFactor; } /** * Draws the annotation. * * @param g2 the graphics device. * @param plot the plot. * @param dataArea the data area. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param rendererIndex the renderer index. * @param info if supplied, this info object will be populated with * entity information. */ @Override public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, ValueAxis domainAxis, ValueAxis rangeAxis, int rendererIndex, PlotRenderingInfo info) { PlotOrientation orientation = plot.getOrientation(); RectangleEdge domainEdge = Plot.resolveDomainAxisLocation( plot.getDomainAxisLocation(), orientation); RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation( plot.getRangeAxisLocation(), orientation); float j2DX = (float) domainAxis.valueToJava2D(this.x, dataArea, domainEdge); float j2DY = (float) rangeAxis.valueToJava2D(this.y, dataArea, rangeEdge); Rectangle2D displayArea = new Rectangle2D.Double( j2DX - this.displayWidth / 2.0, j2DY - this.displayHeight / 2.0, this.displayWidth, this.displayHeight); // here we change the AffineTransform so we can draw the annotation // to a larger area and scale it down into the display area // afterwards, the original transform is restored AffineTransform savedTransform = g2.getTransform(); Rectangle2D drawArea = new Rectangle2D.Double(0.0, 0.0, this.displayWidth * this.drawScaleFactor, this.displayHeight * this.drawScaleFactor); g2.scale(1 / this.drawScaleFactor, 1 / this.drawScaleFactor); g2.translate((j2DX - this.displayWidth / 2.0) * this.drawScaleFactor, (j2DY - this.displayHeight / 2.0) * this.drawScaleFactor); this.drawable.draw(g2, drawArea); g2.setTransform(savedTransform); String toolTip = getToolTipText(); String url = getURL(); if (toolTip != null || url != null) { addEntity(info, displayArea, rendererIndex, toolTip, url); } } /** * Tests this annotation for equality with an arbitrary object. * * @param obj the object to test against. * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { // simple case return true; } if (!(obj instanceof XYDrawableAnnotation)) { return false; } XYDrawableAnnotation that = (XYDrawableAnnotation) obj; if (Double.doubleToLongBits(this.x) != Double.doubleToLongBits(that.x)) { return false; } if (Double.doubleToLongBits(this.y) != Double.doubleToLongBits(that.y)) { return false; } if (Double.doubleToLongBits(this.displayWidth) != Double.doubleToLongBits(that.displayWidth)) { return false; } if (Double.doubleToLongBits(this.displayHeight) != Double.doubleToLongBits(that.displayHeight)) { return false; } if (Double.doubleToLongBits(this.drawScaleFactor) != Double.doubleToLongBits(that.drawScaleFactor)) { return false; } if (!Objects.equals(this.drawable, that.drawable)) { return false; } // fix the "equals not symmetric" problem if (!that.canEqual(this)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof XYDrawableAnnotation); } /** * Returns a hash code. * * @return A hash code. */ @Override public int hashCode() { int hash = super.hashCode(); // equals calls superclass, hashCode must also hash = 41 * hash + (int) (Double.doubleToLongBits(this.x) ^ (Double.doubleToLongBits(this.x) >>> 32)); hash = 41 * hash + (int) (Double.doubleToLongBits(this.y) ^ (Double.doubleToLongBits(this.y) >>> 32)); hash = 41 * hash + (int) (Double.doubleToLongBits(this.drawScaleFactor) ^ (Double.doubleToLongBits(this.drawScaleFactor) >>> 32)); hash = 41 * hash + (int) (Double.doubleToLongBits(this.displayWidth) ^ (Double.doubleToLongBits(this.displayWidth) >>> 32)); hash = 41 * hash + (int) (Double.doubleToLongBits(this.displayHeight) ^ (Double.doubleToLongBits(this.displayHeight) >>> 32)); hash = 41 * hash + Objects.hashCode(this.drawable); return hash; } /** * Returns a clone of the annotation. * * @return A clone. * * @throws CloneNotSupportedException if the annotation can't be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/annotations/XYImageAnnotation.java000066400000000000000000000240671463604235500316250ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * XYImageAnnotation.java * ---------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Mike Harris; * Peter Kolb (patch 2809117); * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.annotations; import java.awt.Graphics2D; import java.awt.Image; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.axis.AxisLocation; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.RectangleAnchor; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; /** * An annotation that allows an image to be placed at some location on * an {@link XYPlot}. * * TODO: implement serialization properly (image is not serializable). */ public class XYImageAnnotation extends AbstractXYAnnotation implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -4364694501921559958L; /** The x-coordinate (in data space). */ private double x; /** The y-coordinate (in data space). */ private double y; /** The image. */ private transient Image image; /** The image anchor point. */ private RectangleAnchor anchor; /** * Creates a new annotation to be displayed at the specified (x, y) * location. * * @param x the x-coordinate (in data space, must be finite). * @param y the y-coordinate (in data space, must be finite). * @param image the image ({@code null} not permitted). */ public XYImageAnnotation(double x, double y, Image image) { this(x, y, image, RectangleAnchor.CENTER); } /** * Creates a new annotation to be displayed at the specified (x, y) * location. * * @param x the x-coordinate (in data space). * @param y the y-coordinate (in data space). * @param image the image ({@code null} not permitted). * @param anchor the image anchor ({@code null} not permitted). */ public XYImageAnnotation(double x, double y, Image image, RectangleAnchor anchor) { super(); Args.nullNotPermitted(image, "image"); Args.nullNotPermitted(anchor, "anchor"); Args.requireFinite(x, "x"); Args.requireFinite(y, "y"); this.x = x; this.y = y; this.image = image; this.anchor = anchor; } /** * Returns the x-coordinate (in data space) for the annotation. * * @return The x-coordinate. */ public double getX() { return this.x; } /** * Returns the y-coordinate (in data space) for the annotation. * * @return The y-coordinate. */ public double getY() { return this.y; } /** * Returns the image for the annotation. * * @return The image. */ public Image getImage() { return this.image; } /** * Returns the image anchor for the annotation. * * @return The image anchor. */ public RectangleAnchor getImageAnchor() { return this.anchor; } /** * Draws the annotation. This method is called by the drawing code in the * {@link XYPlot} class, you don't normally need to call this method * directly. * * @param g2 the graphics device. * @param plot the plot. * @param dataArea the data area. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param rendererIndex the renderer index. * @param info if supplied, this info object will be populated with * entity information. */ @Override public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, ValueAxis domainAxis, ValueAxis rangeAxis, int rendererIndex, PlotRenderingInfo info) { PlotOrientation orientation = plot.getOrientation(); AxisLocation domainAxisLocation = plot.getDomainAxisLocation(); AxisLocation rangeAxisLocation = plot.getRangeAxisLocation(); RectangleEdge domainEdge = Plot.resolveDomainAxisLocation(domainAxisLocation, orientation); RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation(rangeAxisLocation, orientation); float j2DX = (float) domainAxis.valueToJava2D(this.x, dataArea, domainEdge); float j2DY = (float) rangeAxis.valueToJava2D(this.y, dataArea, rangeEdge); float xx = 0.0f; float yy = 0.0f; if (orientation == PlotOrientation.HORIZONTAL) { xx = j2DY; yy = j2DX; } else if (orientation == PlotOrientation.VERTICAL) { xx = j2DX; yy = j2DY; } int w = this.image.getWidth(null); int h = this.image.getHeight(null); Rectangle2D imageRect = new Rectangle2D.Double(0, 0, w, h); Point2D anchorPoint = this.anchor.getAnchorPoint(imageRect); xx = xx - (float) anchorPoint.getX(); yy = yy - (float) anchorPoint.getY(); g2.drawImage(this.image, (int) xx, (int) yy, null); String toolTip = getToolTipText(); String url = getURL(); if (toolTip != null || url != null) { addEntity(info, new Rectangle2D.Float(xx, yy, w, h), rendererIndex, toolTip, url); } } /** * Tests this object for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYImageAnnotation)) { return false; } XYImageAnnotation that = (XYImageAnnotation) obj; if (Double.doubleToLongBits(this.x) != Double.doubleToLongBits(that.x)) { return false; } if (Double.doubleToLongBits(this.y) != Double.doubleToLongBits(that.y)) { return false; } if (!Objects.equals(this.image, that.image)) { return false; } if (this.anchor != that.anchor) { return false; } // fix the "equals not symmetric" problem if (!that.canEqual(this)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof XYImageAnnotation); } /** * Returns a hash code for this object. * * @return A hash code. */ @Override public int hashCode() { int hash = super.hashCode(); // equals calls superclass, hashCode must also hash = 41 * hash + (int) (Double.doubleToLongBits(this.x) ^ (Double.doubleToLongBits(this.x) >>> 32)); hash = 41 * hash + (int) (Double.doubleToLongBits(this.y) ^ (Double.doubleToLongBits(this.y) >>> 32)); hash = 41 * hash + Objects.hashCode(this.image); hash = 41 * hash + Objects.hashCode(this.anchor); return hash; } /** * Returns a clone of the annotation. * * @return A clone. * * @throws CloneNotSupportedException if the annotation can't be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); //SerialUtils.writeImage(this.image, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); //this.image = SerialUtils.readImage(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/annotations/XYInversePointerAnnotation.java000066400000000000000000000230421463604235500335470ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * XYInversePointerAnnotation.java * ------------------------ * (C) Copyright 2021-present, by Yuri Blankenstein and Contributors. * * Original Author: Yuri Blankenstein (for ESI TNO); * */ package org.jfree.chart.annotations; import java.awt.Graphics2D; import java.awt.Shape; import java.awt.geom.Ellipse2D; import java.awt.geom.GeneralPath; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.util.GeomUtil; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.text.TextUtils; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.PublicCloneable; /** * An arrow and label that can be placed on an {@link XYPlot}. The arrow is * drawn at a user-definable angle but points towards the label of the * annotation. *

* The arrow length (and its offset from the (x, y) location) is controlled by * the tip radius and the base radius attributes. Imagine two circles around the * (x, y) coordinate: the inner circle defined by the tip radius, and the outer * circle defined by the base radius. Now, draw the arrow starting at some point * on the outer circle (the point is determined by the angle), with the arrow * tip being drawn at a corresponding point on the inner circle. */ public class XYInversePointerAnnotation extends XYPointerAnnotation implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -4031161445009858551L; /** The default dot radius (in Java2D units). */ public static final double DEFAULT_DOT_RADIUS = 0; /** The radius of the dot at the start of the arrow. */ private double dotRadius; /** * Creates a new label and arrow annotation. * * @param label the label ({@code null} permitted). * @param x the x-coordinate (measured against the chart's domain axis). * @param y the y-coordinate (measured against the chart's range axis). * @param angle the angle of the arrow's line (in radians). */ public XYInversePointerAnnotation(String label, double x, double y, double angle) { super(label, x, y, angle); this.dotRadius = DEFAULT_DOT_RADIUS; } /** * Returns the radius of the dot at the start of the arrow. * * @return the radius of the dot at the start of the arrow * @see #setDotRadius(double) */ public double getDotRadius() { return dotRadius; } /** * Sets the radius of the dot at the start of the arrow, ≤ 0 will omit * the dot. * * @param dotRadius the radius of the dot at the start of the arrow * @see #getDotRadius() */ public void setDotRadius(double dotRadius) { this.dotRadius = dotRadius; fireAnnotationChanged(); } /** * Draws the annotation. * * @param g2 the graphics device. * @param plot the plot. * @param dataArea the data area. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param rendererIndex the renderer index. * @param info the plot rendering info. */ @Override public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, ValueAxis domainAxis, ValueAxis rangeAxis, int rendererIndex, PlotRenderingInfo info) { PlotOrientation orientation = plot.getOrientation(); RectangleEdge domainEdge = Plot.resolveDomainAxisLocation( plot.getDomainAxisLocation(), orientation); RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation( plot.getRangeAxisLocation(), orientation); double j2DX = domainAxis.valueToJava2D(getX(), dataArea, domainEdge); double j2DY = rangeAxis.valueToJava2D(getY(), dataArea, rangeEdge); if (orientation == PlotOrientation.HORIZONTAL) { double temp = j2DX; j2DX = j2DY; j2DY = temp; } double startX = j2DX + Math.cos(getAngle()) * getBaseRadius(); double startY = j2DY + Math.sin(getAngle()) * getBaseRadius(); double endX = j2DX + Math.cos(getAngle()) * getTipRadius(); double endY = j2DY + Math.sin(getAngle()) * getTipRadius(); // Already calculate the label bounds to adjust the start point double labelX = j2DX + Math.cos(getAngle()) * (getBaseRadius() + getLabelOffset()); double labelY = j2DY + Math.sin(getAngle()) * (getBaseRadius() + getLabelOffset()); Shape hotspot = TextUtils.calculateRotatedStringBounds(getText(), g2, (float) labelX, (float) labelY, getTextAnchor(), getRotationAngle(), getRotationAnchor()); Point2D[] pointOnLabelBounds = GeomUtil.calculateIntersectionPoints( new Line2D.Double(startX, startY, endX, endY), GeomUtil.getLines(hotspot, null)); if (pointOnLabelBounds.length > 0) { // Adjust the start point to the intersection with the label bounds startX = pointOnLabelBounds[0].getX(); startY = pointOnLabelBounds[0].getY(); } double arrowBaseX = startX - Math.cos(getAngle()) * getArrowLength(); double arrowBaseY = startY - Math.sin(getAngle()) * getArrowLength(); double arrowLeftX = arrowBaseX + Math.cos(getAngle() + Math.PI / 2.0) * getArrowWidth(); double arrowLeftY = arrowBaseY + Math.sin(getAngle() + Math.PI / 2.0) * getArrowWidth(); double arrowRightX = arrowBaseX - Math.cos(getAngle() + Math.PI / 2.0) * getArrowWidth(); double arrowRightY = arrowBaseY - Math.sin(getAngle() + Math.PI / 2.0) * getArrowWidth(); GeneralPath arrow = new GeneralPath(); arrow.moveTo((float) startX, (float) startY); arrow.lineTo((float) arrowLeftX, (float) arrowLeftY); arrow.lineTo((float) arrowRightX, (float) arrowRightY); arrow.closePath(); g2.setStroke(getArrowStroke()); g2.setPaint(getArrowPaint()); Line2D line = new Line2D.Double(arrowBaseX, arrowBaseY, endX, endY); g2.draw(line); g2.fill(arrow); if (dotRadius > 0) { Ellipse2D.Double dot = new Ellipse2D.Double(endX - dotRadius, endY - dotRadius, 2 * dotRadius, 2 * dotRadius); g2.fill(dot); } // draw the label g2.setFont(getFont()); if (getBackgroundPaint() != null) { g2.setPaint(getBackgroundPaint()); g2.fill(hotspot); } g2.setPaint(getPaint()); TextUtils.drawRotatedString(getText(), g2, (float) labelX, (float) labelY, getTextAnchor(), getRotationAngle(), getRotationAnchor()); if (isOutlineVisible()) { g2.setStroke(getOutlineStroke()); g2.setPaint(getOutlinePaint()); g2.draw(hotspot); } String toolTip = getToolTipText(); String url = getURL(); if (toolTip != null || url != null) { addEntity(info, hotspot, rendererIndex, toolTip, url); } } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); long temp; temp = Double.doubleToLongBits(dotRadius); result = prime * result + (int) (temp ^ (temp >>> 32)); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!super.equals(obj)) return false; if (getClass() != obj.getClass()) return false; XYInversePointerAnnotation other = (XYInversePointerAnnotation) obj; if (Double.doubleToLongBits(dotRadius) != Double .doubleToLongBits(other.dotRadius)) return false; return true; } /** * Returns a clone of the annotation. * * @return A clone. * * @throws CloneNotSupportedException if the annotation can't be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/annotations/XYLineAnnotation.java000066400000000000000000000312171463604235500314650ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * XYLineAnnotation.java * --------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Peter Kolb (see patch 2809117); Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.annotations; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Stroke; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.HashUtils; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.LineUtils; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; import org.jfree.chart.util.ShapeUtils; /** * A simple line annotation that can be placed on an {@link XYPlot}. * Instances of this class are immutable. */ public class XYLineAnnotation extends AbstractXYAnnotation implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -80535465244091334L; /** The x-coordinate. */ private double x1; /** The y-coordinate. */ private double y1; /** The x-coordinate. */ private double x2; /** The y-coordinate. */ private double y2; /** The line stroke. */ private transient Stroke stroke; /** The line color. */ private transient Paint paint; /** * Creates a new annotation that draws a line from (x1, y1) to (x2, y2) * where the coordinates are measured in data space (that is, against the * plot's axes). All the line coordinates are required to be finite values. * * @param x1 the x-coordinate for the start of the line. * @param y1 the y-coordinate for the start of the line. * @param x2 the x-coordinate for the end of the line. * @param y2 the y-coordinate for the end of the line. */ public XYLineAnnotation(double x1, double y1, double x2, double y2) { this(x1, y1, x2, y2, new BasicStroke(1.0f), Color.BLACK); } /** * Creates a new annotation that draws a line from (x1, y1) to (x2, y2) * where the coordinates are measured in data space (that is, against the * plot's axes). * * @param x1 the x-coordinate for the start of the line (must be finite). * @param y1 the y-coordinate for the start of the line (must be finite). * @param x2 the x-coordinate for the end of the line (must be finite). * @param y2 the y-coordinate for the end of the line (must be finite). * @param stroke the line stroke ({@code null} not permitted). * @param paint the line color ({@code null} not permitted). */ public XYLineAnnotation(double x1, double y1, double x2, double y2, Stroke stroke, Paint paint) { super(); Args.nullNotPermitted(stroke, "stroke"); Args.nullNotPermitted(paint, "paint"); Args.requireFinite(x1, "x1"); Args.requireFinite(y1, "y1"); Args.requireFinite(x2, "x2"); Args.requireFinite(y2, "y2"); this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; this.stroke = stroke; this.paint = paint; } /** * Returns the x-coordinate for the starting point of the line (set in the * constructor). * * @return The x-coordinate for the starting point of the line. */ public double getX1() { return x1; } /** * Returns the y-coordinate for the starting point of the line (set in the * constructor). * * @return The y-coordinate for the starting point of the line. */ public double getY1() { return y1; } /** * Returns the x-coordinate for the ending point of the line (set in the * constructor). * * @return The x-coordinate for the ending point of the line. */ public double getX2() { return x2; } /** * Returns the y-coordinate for the ending point of the line (set in the * constructor). * * @return The y-coordinate for the ending point of the line. */ public double getY2() { return y2; } /** * Returns the stroke used for drawing the line (set in the constructor). * * @return The stroke (never {@code null}). */ public Stroke getStroke() { return stroke; } /** * Returns the paint used for drawing the line (set in the constructor). * * @return The paint (never {@code null}). */ public Paint getPaint() { return paint; } /** * Draws the annotation. This method is called by the {@link XYPlot} * class, you won't normally need to call it yourself. * * @param g2 the graphics device. * @param plot the plot. * @param dataArea the data area. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param rendererIndex the renderer index. * @param info if supplied, this info object will be populated with * entity information. */ @Override public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, ValueAxis domainAxis, ValueAxis rangeAxis, int rendererIndex, PlotRenderingInfo info) { PlotOrientation orientation = plot.getOrientation(); RectangleEdge domainEdge = Plot.resolveDomainAxisLocation( plot.getDomainAxisLocation(), orientation); RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation( plot.getRangeAxisLocation(), orientation); float j2DX1 = 0.0f; float j2DX2 = 0.0f; float j2DY1 = 0.0f; float j2DY2 = 0.0f; if (orientation == PlotOrientation.VERTICAL) { j2DX1 = (float) domainAxis.valueToJava2D(this.x1, dataArea, domainEdge); j2DY1 = (float) rangeAxis.valueToJava2D(this.y1, dataArea, rangeEdge); j2DX2 = (float) domainAxis.valueToJava2D(this.x2, dataArea, domainEdge); j2DY2 = (float) rangeAxis.valueToJava2D(this.y2, dataArea, rangeEdge); } else if (orientation == PlotOrientation.HORIZONTAL) { j2DY1 = (float) domainAxis.valueToJava2D(this.x1, dataArea, domainEdge); j2DX1 = (float) rangeAxis.valueToJava2D(this.y1, dataArea, rangeEdge); j2DY2 = (float) domainAxis.valueToJava2D(this.x2, dataArea, domainEdge); j2DX2 = (float) rangeAxis.valueToJava2D(this.y2, dataArea, rangeEdge); } g2.setPaint(this.paint); g2.setStroke(this.stroke); Line2D line = new Line2D.Float(j2DX1, j2DY1, j2DX2, j2DY2); // line is clipped to avoid JRE bug 6574155, for more info // see JFreeChart bug 2221495 boolean visible = LineUtils.clipLine(line, dataArea); if (visible) { g2.draw(line); } String toolTip = getToolTipText(); String url = getURL(); if (toolTip != null || url != null) { addEntity(info, ShapeUtils.createLineRegion(line, 1.0f), rendererIndex, toolTip, url); } } /** * Tests this object for equality with an arbitrary object. * * @param obj the object to test against ({@code null} permitted). * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYLineAnnotation)) { return false; } XYLineAnnotation that = (XYLineAnnotation) obj; if (Double.doubleToLongBits(this.x1) != Double.doubleToLongBits(that.x1)) { return false; } if (Double.doubleToLongBits(this.y1) != Double.doubleToLongBits(that.y1)) { return false; } if (Double.doubleToLongBits(this.x2) != Double.doubleToLongBits(that.x2)) { return false; } if (Double.doubleToLongBits(this.y2) != Double.doubleToLongBits(that.y2)) { return false; } if (!PaintUtils.equal(this.paint, that.paint)) { return false; } if (!Objects.equals(this.stroke, that.stroke)) { return false; } // fix the "equals not symmetric" problem if (!that.canEqual(this)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof XYLineAnnotation); } /** * Returns a hash code. * * @return A hash code. */ @Override public int hashCode() { int hash = super.hashCode(); // equals calls superclass, hashCode must also hash = 89 * hash + (int) (Double.doubleToLongBits(this.x1) ^ (Double.doubleToLongBits(this.x1) >>> 32)); hash = 89 * hash + (int) (Double.doubleToLongBits(this.y1) ^ (Double.doubleToLongBits(this.y1) >>> 32)); hash = 89 * hash + (int) (Double.doubleToLongBits(this.x2) ^ (Double.doubleToLongBits(this.x2) >>> 32)); hash = 89 * hash + (int) (Double.doubleToLongBits(this.y2) ^ (Double.doubleToLongBits(this.y2) >>> 32)); hash = 89 * hash + Objects.hashCode(this.stroke); hash = 89 * hash + HashUtils.hashCodeForPaint(this.paint); return hash; } /** * Returns a clone of the annotation. * * @return A clone. * * @throws CloneNotSupportedException if the annotation can't be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.paint, stream); SerialUtils.writeStroke(this.stroke, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.paint = SerialUtils.readPaint(stream); this.stroke = SerialUtils.readStroke(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/annotations/XYNoteAnnotation.java000066400000000000000000000351621463604235500315060ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * XYNoteAnnotation.java * --------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert (as XYPointerAnnotation.java); * Contributor(s): Yuri Blankenstein (copy to XYNoteAnnotation.java; for ESI TNO); * */ package org.jfree.chart.annotations; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.HashUtils; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.AnnotationChangeEvent; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.text.TextUtils; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; /** * An line and label that can be placed on an {@link XYPlot}. The line is * drawn at a user-definable angle so that it points towards the (x, y) * location for the annotation. *

* The line length (and its offset from the (x, y) location) is controlled by * the tip radius and the base radius attributes. Imagine two circles around * the (x, y) coordinate: the inner circle defined by the tip radius, and the * outer circle defined by the base radius. Now, draw the line starting at * some point on the outer circle (the point is determined by the angle), with * the line tip being drawn at a corresponding point on the inner circle. */ public class XYNoteAnnotation extends XYTextAnnotation implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -4031161445009858551L; /** The default tip radius (in Java2D units). */ public static final double DEFAULT_TIP_RADIUS = 0.0; /** The default base radius (in Java2D units). */ public static final double DEFAULT_BASE_RADIUS = 30.0; /** The default label offset (in Java2D units). */ public static final double DEFAULT_LABEL_OFFSET = 3.0; /** The default line stroke. */ public static final Stroke DEFAULT_LINE_STROKE = new BasicStroke(0.5f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 2.0f, new float[] {2.0f}, 0.0f); /** The default line stroke. */ public static final Paint DEFAULT_BACKGROUND_PAINT = new Color(255, 255, 203); /** The default line stroke. */ public static final Paint DEFAULT_OUTLINE_PAINT = new Color(255, 204, 102); /** The angle of the line's line (in radians). */ private double angle; /** * The radius from the (x, y) point to the tip of the line (in Java2D * units). */ private double tipRadius; /** * The radius from the (x, y) point to the start of the line line (in * Java2D units). */ private double baseRadius; /** The line stroke. */ private transient Stroke lineStroke; /** The line paint. */ private transient Paint linePaint; /** The radius from the base point to the anchor point for the label. */ private double labelOffset; /** * Creates a new label and line annotation. * * @param label the label ({@code null} permitted). * @param x the x-coordinate (measured against the chart's domain axis). * @param y the y-coordinate (measured against the chart's range axis). * @param angle the angle of the line's line (in radians). */ public XYNoteAnnotation(String label, double x, double y, double angle) { super(label, x, y); this.angle = angle; this.tipRadius = DEFAULT_TIP_RADIUS; this.baseRadius = DEFAULT_BASE_RADIUS; this.labelOffset = DEFAULT_LABEL_OFFSET; this.lineStroke = DEFAULT_LINE_STROKE; this.linePaint = Color.BLACK; setBackgroundPaint(DEFAULT_BACKGROUND_PAINT); setOutlineVisible(true); setOutlinePaint(DEFAULT_OUTLINE_PAINT); setOutlineStroke(new BasicStroke(1.0f)); } /** * Returns the angle of the line. * * @return The angle (in radians). * * @see #setAngle(double) */ public double getAngle() { return this.angle; } /** * Sets the angle of the line and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param angle the angle (in radians). * * @see #getAngle() */ public void setAngle(double angle) { this.angle = angle; fireAnnotationChanged(); } /** * Returns the tip radius. * * @return The tip radius (in Java2D units). * * @see #setTipRadius(double) */ public double getTipRadius() { return this.tipRadius; } /** * Sets the tip radius and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param radius the radius (in Java2D units). * * @see #getTipRadius() */ public void setTipRadius(double radius) { this.tipRadius = radius; fireAnnotationChanged(); } /** * Returns the base radius. * * @return The base radius (in Java2D units). * * @see #setBaseRadius(double) */ public double getBaseRadius() { return this.baseRadius; } /** * Sets the base radius and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param radius the radius (in Java2D units). * * @see #getBaseRadius() */ public void setBaseRadius(double radius) { this.baseRadius = radius; fireAnnotationChanged(); } /** * Returns the label offset. * * @return The label offset (in Java2D units). * * @see #setLabelOffset(double) */ public double getLabelOffset() { return this.labelOffset; } /** * Sets the label offset (from the line base, continuing in a straight * line, in Java2D units) and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param offset the offset (in Java2D units). * * @see #getLabelOffset() */ public void setLabelOffset(double offset) { this.labelOffset = offset; fireAnnotationChanged(); } /** * Returns the stroke used to draw the line line. * * @return The line stroke (never {@code null}). * * @see #setLineStroke(Stroke) */ public Stroke getLineStroke() { return this.lineStroke; } /** * Sets the stroke used to draw the line line and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getLineStroke() */ public void setLineStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.lineStroke = stroke; fireAnnotationChanged(); } /** * Returns the paint used for the line. * * @return The line paint (never {@code null}). * * @see #setLinePaint(Paint) */ public Paint getLinePaint() { return this.linePaint; } /** * Sets the paint used for the line and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param paint the line paint ({@code null} not permitted). * * @see #getLinePaint() */ public void setLinePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.linePaint = paint; fireAnnotationChanged(); } /** * Draws the annotation. * * @param g2 the graphics device. * @param plot the plot. * @param dataArea the data area. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param rendererIndex the renderer index. * @param info the plot rendering info. */ @Override public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, ValueAxis domainAxis, ValueAxis rangeAxis, int rendererIndex, PlotRenderingInfo info) { PlotOrientation orientation = plot.getOrientation(); RectangleEdge domainEdge = Plot.resolveDomainAxisLocation( plot.getDomainAxisLocation(), orientation); RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation( plot.getRangeAxisLocation(), orientation); double j2DX = domainAxis.valueToJava2D(getX(), dataArea, domainEdge); double j2DY = rangeAxis.valueToJava2D(getY(), dataArea, rangeEdge); if (orientation == PlotOrientation.HORIZONTAL) { double temp = j2DX; j2DX = j2DY; j2DY = temp; } double startX = j2DX + Math.cos(this.angle) * this.baseRadius; double startY = j2DY + Math.sin(this.angle) * this.baseRadius; double endX = j2DX + Math.cos(this.angle) * this.tipRadius; double endY = j2DY + Math.sin(this.angle) * this.tipRadius; g2.setStroke(this.lineStroke); g2.setPaint(this.linePaint); Line2D line = new Line2D.Double(startX, startY, endX, endY); g2.draw(line); // draw the label double labelX = j2DX + Math.cos(this.angle) * (this.baseRadius + this.labelOffset); double labelY = j2DY + Math.sin(this.angle) * (this.baseRadius + this.labelOffset); g2.setFont(getFont()); Shape hotspot = TextUtils.calculateRotatedStringBounds( getText(), g2, (float) labelX, (float) labelY, getTextAnchor(), getRotationAngle(), getRotationAnchor()); if (getBackgroundPaint() != null) { g2.setPaint(getBackgroundPaint()); g2.fill(hotspot); } g2.setPaint(getPaint()); TextUtils.drawRotatedString(getText(), g2, (float) labelX, (float) labelY, getTextAnchor(), getRotationAngle(), getRotationAnchor()); if (isOutlineVisible()) { g2.setStroke(getOutlineStroke()); g2.setPaint(getOutlinePaint()); g2.draw(hotspot); } String toolTip = getToolTipText(); String url = getURL(); if (toolTip != null || url != null) { addEntity(info, hotspot, rendererIndex, toolTip, url); } } /** * Tests this annotation for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYNoteAnnotation)) { return false; } XYNoteAnnotation that = (XYNoteAnnotation) obj; if (this.angle != that.angle) { return false; } if (this.tipRadius != that.tipRadius) { return false; } if (this.baseRadius != that.baseRadius) { return false; } if (!this.linePaint.equals(that.linePaint)) { return false; } if (!Objects.equals(this.lineStroke, that.lineStroke)) { return false; } if (this.labelOffset != that.labelOffset) { return false; } return super.equals(obj); } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = super.hashCode(); long temp = Double.doubleToLongBits(this.angle); result = 37 * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(this.tipRadius); result = 37 * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(this.baseRadius); result = 37 * result + (int) (temp ^ (temp >>> 32)); result = result * 37 + HashUtils.hashCodeForPaint(this.linePaint); result = result * 37 + this.lineStroke.hashCode(); temp = Double.doubleToLongBits(this.labelOffset); result = 37 * result + (int) (temp ^ (temp >>> 32)); return result; } /** * Returns a clone of the annotation. * * @return A clone. * * @throws CloneNotSupportedException if the annotation can't be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.linePaint, stream); SerialUtils.writeStroke(this.lineStroke, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.linePaint = SerialUtils.readPaint(stream); this.lineStroke = SerialUtils.readStroke(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/annotations/XYPointerAnnotation.java000066400000000000000000000445261463604235500322250ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * XYPointerAnnotation.java * ------------------------ * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Peter Kolb (patch 2809117); Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.annotations; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.GeneralPath; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.HashUtils; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.AnnotationChangeEvent; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.text.TextUtils; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.Args; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; /** * An arrow and label that can be placed on an {@link XYPlot}. The arrow is * drawn at a user-definable angle so that it points towards the (x, y) * location for the annotation. *

* The arrow length (and its offset from the (x, y) location) is controlled by * the tip radius and the base radius attributes. Imagine two circles around * the (x, y) coordinate: the inner circle defined by the tip radius, and the * outer circle defined by the base radius. Now, draw the arrow starting at * some point on the outer circle (the point is determined by the angle), with * the arrow tip being drawn at a corresponding point on the inner circle. */ public class XYPointerAnnotation extends XYTextAnnotation implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -4031161445009858551L; /** The default tip radius (in Java2D units). */ public static final double DEFAULT_TIP_RADIUS = 10.0; /** The default base radius (in Java2D units). */ public static final double DEFAULT_BASE_RADIUS = 30.0; /** The default label offset (in Java2D units). */ public static final double DEFAULT_LABEL_OFFSET = 3.0; /** The default arrow length (in Java2D units). */ public static final double DEFAULT_ARROW_LENGTH = 5.0; /** The default arrow width (in Java2D units). */ public static final double DEFAULT_ARROW_WIDTH = 3.0; /** The angle of the arrow's line (in radians). */ private double angle; /** * The radius from the (x, y) point to the tip of the arrow (in Java2D * units). */ private double tipRadius; /** * The radius from the (x, y) point to the start of the arrow line (in * Java2D units). */ private double baseRadius; /** The length of the arrow head (in Java2D units). */ private double arrowLength; /** The arrow width (in Java2D units, per side). */ private double arrowWidth; /** The arrow stroke. */ private transient Stroke arrowStroke; /** The arrow paint. */ private transient Paint arrowPaint; /** The radius from the base point to the anchor point for the label. */ private double labelOffset; /** * Creates a new label and arrow annotation. * * @param label the label ({@code null} permitted). * @param x the x-coordinate (measured against the chart's domain axis). * @param y the y-coordinate (measured against the chart's range axis). * @param angle the angle of the arrow's line (in radians). */ public XYPointerAnnotation(String label, double x, double y, double angle) { super(label, x, y); Args.requireFinite(x, "x"); Args.requireFinite(y, "y"); Args.requireFinite(angle, "angle"); this.angle = angle; this.tipRadius = DEFAULT_TIP_RADIUS; this.baseRadius = DEFAULT_BASE_RADIUS; this.arrowLength = DEFAULT_ARROW_LENGTH; this.arrowWidth = DEFAULT_ARROW_WIDTH; this.labelOffset = DEFAULT_LABEL_OFFSET; this.arrowStroke = new BasicStroke(1.0f); this.arrowPaint = Color.BLACK; } /** * Returns the angle of the arrow. * * @return The angle (in radians). * * @see #setAngle(double) */ public double getAngle() { return this.angle; } /** * Sets the angle of the arrow and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param angle the angle (in radians). * * @see #getAngle() */ public void setAngle(double angle) { this.angle = angle; fireAnnotationChanged(); } /** * Returns the tip radius. * * @return The tip radius (in Java2D units). * * @see #setTipRadius(double) */ public double getTipRadius() { return this.tipRadius; } /** * Sets the tip radius and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param radius the radius (in Java2D units). * * @see #getTipRadius() */ public void setTipRadius(double radius) { this.tipRadius = radius; fireAnnotationChanged(); } /** * Returns the base radius. * * @return The base radius (in Java2D units). * * @see #setBaseRadius(double) */ public double getBaseRadius() { return this.baseRadius; } /** * Sets the base radius and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param radius the radius (in Java2D units). * * @see #getBaseRadius() */ public void setBaseRadius(double radius) { this.baseRadius = radius; fireAnnotationChanged(); } /** * Returns the label offset. * * @return The label offset (in Java2D units). * * @see #setLabelOffset(double) */ public double getLabelOffset() { return this.labelOffset; } /** * Sets the label offset (from the arrow base, continuing in a straight * line, in Java2D units) and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param offset the offset (in Java2D units). * * @see #getLabelOffset() */ public void setLabelOffset(double offset) { this.labelOffset = offset; fireAnnotationChanged(); } /** * Returns the arrow length. * * @return The arrow length. * * @see #setArrowLength(double) */ public double getArrowLength() { return this.arrowLength; } /** * Sets the arrow length and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param length the length. * * @see #getArrowLength() */ public void setArrowLength(double length) { this.arrowLength = length; fireAnnotationChanged(); } /** * Returns the arrow width. * * @return The arrow width (in Java2D units). * * @see #setArrowWidth(double) */ public double getArrowWidth() { return this.arrowWidth; } /** * Sets the arrow width and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param width the width (in Java2D units). * * @see #getArrowWidth() */ public void setArrowWidth(double width) { this.arrowWidth = width; fireAnnotationChanged(); } /** * Returns the stroke used to draw the arrow line. * * @return The arrow stroke (never {@code null}). * * @see #setArrowStroke(Stroke) */ public Stroke getArrowStroke() { return this.arrowStroke; } /** * Sets the stroke used to draw the arrow line and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getArrowStroke() */ public void setArrowStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.arrowStroke = stroke; fireAnnotationChanged(); } /** * Returns the paint used for the arrow. * * @return The arrow paint (never {@code null}). * * @see #setArrowPaint(Paint) */ public Paint getArrowPaint() { return this.arrowPaint; } /** * Sets the paint used for the arrow and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param paint the arrow paint ({@code null} not permitted). * * @see #getArrowPaint() */ public void setArrowPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.arrowPaint = paint; fireAnnotationChanged(); } /** * Draws the annotation. * * @param g2 the graphics device. * @param plot the plot. * @param dataArea the data area. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param rendererIndex the renderer index. * @param info the plot rendering info. */ @Override public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, ValueAxis domainAxis, ValueAxis rangeAxis, int rendererIndex, PlotRenderingInfo info) { PlotOrientation orientation = plot.getOrientation(); RectangleEdge domainEdge = Plot.resolveDomainAxisLocation( plot.getDomainAxisLocation(), orientation); RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation( plot.getRangeAxisLocation(), orientation); double j2DX = domainAxis.valueToJava2D(getX(), dataArea, domainEdge); double j2DY = rangeAxis.valueToJava2D(getY(), dataArea, rangeEdge); if (orientation == PlotOrientation.HORIZONTAL) { double temp = j2DX; j2DX = j2DY; j2DY = temp; } double startX = j2DX + Math.cos(this.angle) * this.baseRadius; double startY = j2DY + Math.sin(this.angle) * this.baseRadius; double endX = j2DX + Math.cos(this.angle) * this.tipRadius; double endY = j2DY + Math.sin(this.angle) * this.tipRadius; double arrowBaseX = endX + Math.cos(this.angle) * this.arrowLength; double arrowBaseY = endY + Math.sin(this.angle) * this.arrowLength; double arrowLeftX = arrowBaseX + Math.cos(this.angle + Math.PI / 2.0) * this.arrowWidth; double arrowLeftY = arrowBaseY + Math.sin(this.angle + Math.PI / 2.0) * this.arrowWidth; double arrowRightX = arrowBaseX - Math.cos(this.angle + Math.PI / 2.0) * this.arrowWidth; double arrowRightY = arrowBaseY - Math.sin(this.angle + Math.PI / 2.0) * this.arrowWidth; GeneralPath arrow = new GeneralPath(); arrow.moveTo((float) endX, (float) endY); arrow.lineTo((float) arrowLeftX, (float) arrowLeftY); arrow.lineTo((float) arrowRightX, (float) arrowRightY); arrow.closePath(); g2.setStroke(this.arrowStroke); g2.setPaint(this.arrowPaint); Line2D line = new Line2D.Double(startX, startY, arrowBaseX, arrowBaseY); g2.draw(line); g2.fill(arrow); // draw the label double labelX = j2DX + Math.cos(this.angle) * (this.baseRadius + this.labelOffset); double labelY = j2DY + Math.sin(this.angle) * (this.baseRadius + this.labelOffset); g2.setFont(getFont()); Shape hotspot = TextUtils.calculateRotatedStringBounds( getText(), g2, (float) labelX, (float) labelY, getTextAnchor(), getRotationAngle(), getRotationAnchor()); if (getBackgroundPaint() != null) { g2.setPaint(getBackgroundPaint()); g2.fill(hotspot); } g2.setPaint(getPaint()); TextUtils.drawRotatedString(getText(), g2, (float) labelX, (float) labelY, getTextAnchor(), getRotationAngle(), getRotationAnchor()); if (isOutlineVisible()) { g2.setStroke(getOutlineStroke()); g2.setPaint(getOutlinePaint()); g2.draw(hotspot); } String toolTip = getToolTipText(); String url = getURL(); if (toolTip != null || url != null) { addEntity(info, hotspot, rendererIndex, toolTip, url); } } /** * Tests this annotation for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYPointerAnnotation)) { return false; } XYPointerAnnotation that = (XYPointerAnnotation) obj; if (Double.doubleToLongBits(this.angle) != Double.doubleToLongBits(that.angle)) { return false; } if (Double.doubleToLongBits(this.tipRadius) != Double.doubleToLongBits(that.tipRadius)) { return false; } if (Double.doubleToLongBits(this.baseRadius) != Double.doubleToLongBits(that.baseRadius)) { return false; } if (Double.doubleToLongBits(this.arrowLength) != Double.doubleToLongBits(that.arrowLength)) { return false; } if (Double.doubleToLongBits(this.arrowWidth) != Double.doubleToLongBits(that.arrowWidth)) { return false; } if (!PaintUtils.equal(this.arrowPaint, that.arrowPaint)) { return false; } if (!Objects.equals(this.arrowStroke, that.arrowStroke)) { return false; } if (Double.doubleToLongBits(this.labelOffset) != Double.doubleToLongBits(that.labelOffset)) { return false; } // fix the "equals not symmetric" problem if (!that.canEqual(this)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof XYPointerAnnotation); } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int hash = super.hashCode(); // equals calls superclass, hashCode must also hash = 41 * hash + (int) (Double.doubleToLongBits(this.angle) ^ (Double.doubleToLongBits(this.angle) >>> 32)); hash = 41 * hash + (int) (Double.doubleToLongBits(this.tipRadius) ^ (Double.doubleToLongBits(this.tipRadius) >>> 32)); hash = 41 * hash + (int) (Double.doubleToLongBits(this.baseRadius) ^ (Double.doubleToLongBits(this.baseRadius) >>> 32)); hash = 41 * hash + (int) (Double.doubleToLongBits(this.arrowLength) ^ (Double.doubleToLongBits(this.arrowLength) >>> 32)); hash = 41 * hash + (int) (Double.doubleToLongBits(this.arrowWidth) ^ (Double.doubleToLongBits(this.arrowWidth) >>> 32)); hash = 41 * hash + HashUtils.hashCodeForPaint(this.arrowPaint); hash = 41 * hash + Objects.hashCode(this.arrowStroke); hash = 41 * hash + (int) (Double.doubleToLongBits(this.labelOffset) ^ (Double.doubleToLongBits(this.labelOffset) >>> 32)); return hash; } /** * Returns a clone of the annotation. * * @return A clone. * * @throws CloneNotSupportedException if the annotation can't be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.arrowPaint, stream); SerialUtils.writeStroke(this.arrowStroke, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.arrowPaint = SerialUtils.readPaint(stream); this.arrowStroke = SerialUtils.readStroke(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/annotations/XYPolygonAnnotation.java000066400000000000000000000304061463604235500322240ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * XYPolygonAnnotation.java * ------------------------ * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Peter Kolb (patch 2809117); Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.annotations; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Stroke; import java.awt.geom.GeneralPath; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Arrays; import java.util.Objects; import org.jfree.chart.HashUtils; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; /** * A polygon annotation that can be placed on an {@link XYPlot}. The * polygon coordinates are specified in data space. */ public class XYPolygonAnnotation extends AbstractXYAnnotation implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -6984203651995900036L; /** The polygon. */ private double[] polygon; /** The stroke used to draw the box outline. */ private transient Stroke stroke; /** The paint used to draw the box outline. */ private transient Paint outlinePaint; /** The paint used to fill the box. */ private transient Paint fillPaint; /** * Creates a new annotation (where, by default, the polygon is drawn * with a black outline). The array of polygon coordinates must contain * an even number of coordinates (each pair is an (x, y) location on the * plot) and the last point is automatically joined back to the first point. * * @param polygon the coordinates of the polygon's vertices * ({@code null} not permitted). */ public XYPolygonAnnotation(double[] polygon) { this(polygon, new BasicStroke(1.0f), Color.BLACK); } /** * Creates a new annotation where the box is drawn as an outline using * the specified {@code stroke} and {@code outlinePaint}. * The array of polygon coordinates must contain an even number of * coordinates (each pair is an (x, y) location on the plot) and the last * point is automatically joined back to the first point. * * @param polygon the coordinates of the polygon's vertices * ({@code null} not permitted). * @param stroke the shape stroke ({@code null} permitted). * @param outlinePaint the shape color ({@code null} permitted). */ public XYPolygonAnnotation(double[] polygon, Stroke stroke, Paint outlinePaint) { this(polygon, stroke, outlinePaint, null); } /** * Creates a new annotation. The array of polygon coordinates must * contain an even number of coordinates (each pair is an (x, y) location * on the plot) and the last point is automatically joined back to the * first point. * * @param polygon the coordinates of the polygon's vertices * ({@code null} not permitted). * @param stroke the shape stroke ({@code null} permitted). * @param outlinePaint the shape color ({@code null} permitted). * @param fillPaint the paint used to fill the shape ({@code null} * permitted). */ public XYPolygonAnnotation(double[] polygon, Stroke stroke, Paint outlinePaint, Paint fillPaint) { super(); Args.nullNotPermitted(polygon, "polygon"); if (polygon.length % 2 != 0) { throw new IllegalArgumentException("The 'polygon' array must " + "contain an even number of items."); } this.polygon = polygon.clone(); this.stroke = stroke; this.outlinePaint = outlinePaint; this.fillPaint = fillPaint; } /** * Returns the coordinates of the polygon's vertices. The returned array * is a copy, so it is safe to modify without altering the annotation's * state. * * @return The coordinates of the polygon's vertices. */ public double[] getPolygonCoordinates() { return this.polygon.clone(); } /** * Returns the fill paint. * * @return The fill paint (possibly {@code null}). */ public Paint getFillPaint() { return this.fillPaint; } /** * Returns the outline stroke. * * @return The outline stroke (possibly {@code null}). */ public Stroke getOutlineStroke() { return this.stroke; } /** * Returns the outline paint. * * @return The outline paint (possibly {@code null}). */ public Paint getOutlinePaint() { return this.outlinePaint; } /** * Draws the annotation. This method is usually called by the * {@link XYPlot} class, you shouldn't need to call it directly. * * @param g2 the graphics device. * @param plot the plot. * @param dataArea the data area. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param rendererIndex the renderer index. * @param info the plot rendering info. */ @Override public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, ValueAxis domainAxis, ValueAxis rangeAxis, int rendererIndex, PlotRenderingInfo info) { // if we don't have at least 2 (x, y) coordinates, just return if (this.polygon.length < 4) { return; } PlotOrientation orientation = plot.getOrientation(); RectangleEdge domainEdge = Plot.resolveDomainAxisLocation( plot.getDomainAxisLocation(), orientation); RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation( plot.getRangeAxisLocation(), orientation); GeneralPath area = new GeneralPath(); double x = domainAxis.valueToJava2D(this.polygon[0], dataArea, domainEdge); double y = rangeAxis.valueToJava2D(this.polygon[1], dataArea, rangeEdge); if (orientation == PlotOrientation.HORIZONTAL) { area.moveTo((float) y, (float) x); for (int i = 2; i < this.polygon.length; i += 2) { x = domainAxis.valueToJava2D(this.polygon[i], dataArea, domainEdge); y = rangeAxis.valueToJava2D(this.polygon[i + 1], dataArea, rangeEdge); area.lineTo((float) y, (float) x); } area.closePath(); } else if (orientation == PlotOrientation.VERTICAL) { area.moveTo((float) x, (float) y); for (int i = 2; i < this.polygon.length; i += 2) { x = domainAxis.valueToJava2D(this.polygon[i], dataArea, domainEdge); y = rangeAxis.valueToJava2D(this.polygon[i + 1], dataArea, rangeEdge); area.lineTo((float) x, (float) y); } area.closePath(); } if (this.fillPaint != null) { g2.setPaint(this.fillPaint); g2.fill(area); } if (this.stroke != null && this.outlinePaint != null) { g2.setPaint(this.outlinePaint); g2.setStroke(this.stroke); g2.draw(area); } addEntity(info, area, rendererIndex, getToolTipText(), getURL()); } /** * Tests this annotation for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYPolygonAnnotation)) { return false; } XYPolygonAnnotation that = (XYPolygonAnnotation) obj; if (!Arrays.equals(this.polygon, that.polygon)) { return false; } if (!Objects.equals(this.stroke, that.stroke)) { return false; } if (!PaintUtils.equal(this.outlinePaint, that.outlinePaint)) { return false; } if (!PaintUtils.equal(this.fillPaint, that.fillPaint)) { return false; } // fix the "equals not symmetric" problem if (!that.canEqual(this)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof XYPolygonAnnotation); } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int hash = super.hashCode(); // equals calls superclass, hashCode must also hash = 13 * hash + HashUtils.hashCodeForPaint(this.fillPaint); hash = 13 * hash + HashUtils.hashCodeForPaint(this.outlinePaint); hash = 13 * hash + Objects.hashCode(this.stroke); hash = 13 * hash + Arrays.hashCode(this.polygon); return hash; } /** * Returns a clone. * * @return A clone. * * @throws CloneNotSupportedException not thrown by this class, but may be * by subclasses. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Provides serialization support. * * @param stream the output stream ({@code null} not permitted). * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeStroke(this.stroke, stream); SerialUtils.writePaint(this.outlinePaint, stream); SerialUtils.writePaint(this.fillPaint, stream); } /** * Provides serialization support. * * @param stream the input stream ({@code null} not permitted). * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.stroke = SerialUtils.readStroke(stream); this.outlinePaint = SerialUtils.readPaint(stream); this.fillPaint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/annotations/XYShapeAnnotation.java000066400000000000000000000237751463604235500316500ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * XYShapeAnnotation.java * ---------------------- * (C) Copyright 2003-present, by Ondax, Inc. and Contributors. * * Original Author: Greg Steckman (for Ondax, Inc.); * Contributor(s): David Gilbert; * Peter Kolb (patch 2809117); * */ package org.jfree.chart.annotations; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.HashUtils; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; /** * A simple {@code Shape} annotation that can be placed on an * {@link XYPlot}. The shape coordinates are specified in data space. */ public class XYShapeAnnotation extends AbstractXYAnnotation implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -8553218317600684041L; /** The shape. */ private transient Shape shape; /** The stroke used to draw the shape's outline. */ private transient Stroke stroke; /** The paint used to draw the shape's outline. */ private transient Paint outlinePaint; /** The paint used to fill the shape. */ private transient Paint fillPaint; /** * Creates a new annotation (where, by default, the shape is drawn * with a black outline). * * @param shape the shape (coordinates in data space, {@code null} * not permitted). */ public XYShapeAnnotation(Shape shape) { this(shape, new BasicStroke(1.0f), Color.BLACK); } /** * Creates a new annotation where the shape is drawn as an outline using * the specified {@code stroke} and {@code outlinePaint}. * * @param shape the shape ({@code null} not permitted). * @param stroke the shape stroke ({@code null} permitted). * @param outlinePaint the shape color ({@code null} permitted). */ public XYShapeAnnotation(Shape shape, Stroke stroke, Paint outlinePaint) { this(shape, stroke, outlinePaint, null); } /** * Creates a new annotation. * * @param shape the shape ({@code null} not permitted). * @param stroke the shape stroke ({@code null} permitted). * @param outlinePaint the shape color ({@code null} permitted). * @param fillPaint the paint used to fill the shape ({@code null} * permitted. */ public XYShapeAnnotation(Shape shape, Stroke stroke, Paint outlinePaint, Paint fillPaint) { super(); Args.nullNotPermitted(shape, "shape"); this.shape = shape; this.stroke = stroke; this.outlinePaint = outlinePaint; this.fillPaint = fillPaint; } /** * Draws the annotation. This method is usually called by the * {@link XYPlot} class, you shouldn't need to call it directly. * * @param g2 the graphics device. * @param plot the plot. * @param dataArea the data area. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param rendererIndex the renderer index. * @param info the plot rendering info. */ @Override public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, ValueAxis domainAxis, ValueAxis rangeAxis, int rendererIndex, PlotRenderingInfo info) { PlotOrientation orientation = plot.getOrientation(); RectangleEdge domainEdge = Plot.resolveDomainAxisLocation( plot.getDomainAxisLocation(), orientation); RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation( plot.getRangeAxisLocation(), orientation); // compute transform matrix elements via sample points. Assume no // rotation or shear. Rectangle2D bounds = this.shape.getBounds2D(); double x0 = bounds.getMinX(); double x1 = bounds.getMaxX(); double xx0 = domainAxis.valueToJava2D(x0, dataArea, domainEdge); double xx1 = domainAxis.valueToJava2D(x1, dataArea, domainEdge); double m00 = (xx1 - xx0) / (x1 - x0); double m02 = xx0 - x0 * m00; double y0 = bounds.getMaxY(); double y1 = bounds.getMinY(); double yy0 = rangeAxis.valueToJava2D(y0, dataArea, rangeEdge); double yy1 = rangeAxis.valueToJava2D(y1, dataArea, rangeEdge); double m11 = (yy1 - yy0) / (y1 - y0); double m12 = yy0 - m11 * y0; // create transform & transform shape Shape s = null; if (orientation == PlotOrientation.HORIZONTAL) { AffineTransform t1 = new AffineTransform(0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f); AffineTransform t2 = new AffineTransform(m11, 0.0f, 0.0f, m00, m12, m02); s = t1.createTransformedShape(this.shape); s = t2.createTransformedShape(s); } else if (orientation == PlotOrientation.VERTICAL) { AffineTransform t = new AffineTransform(m00, 0, 0, m11, m02, m12); s = t.createTransformedShape(this.shape); } if (this.fillPaint != null) { g2.setPaint(this.fillPaint); g2.fill(s); } if (this.stroke != null && this.outlinePaint != null) { g2.setPaint(this.outlinePaint); g2.setStroke(this.stroke); g2.draw(s); } addEntity(info, s, rendererIndex, getToolTipText(), getURL()); } /** * Tests this annotation for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } // now try to reject equality if (!super.equals(obj)) { return false; } if (!(obj instanceof XYShapeAnnotation)) { return false; } XYShapeAnnotation that = (XYShapeAnnotation) obj; if (!this.shape.equals(that.shape)) { return false; } if (!Objects.equals(this.stroke, that.stroke)) { return false; } if (!PaintUtils.equal(this.outlinePaint, that.outlinePaint)) { return false; } if (!PaintUtils.equal(this.fillPaint, that.fillPaint)) { return false; } // seem to be the same return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = 193; result = 37 * result + this.shape.hashCode(); if (this.stroke != null) { result = 37 * result + this.stroke.hashCode(); } result = 37 * result + HashUtils.hashCodeForPaint( this.outlinePaint); result = 37 * result + HashUtils.hashCodeForPaint(this.fillPaint); return result; } /** * Returns a clone. * * @return A clone. * * @throws CloneNotSupportedException ???. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeShape(this.shape, stream); SerialUtils.writeStroke(this.stroke, stream); SerialUtils.writePaint(this.outlinePaint, stream); SerialUtils.writePaint(this.fillPaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.shape = SerialUtils.readShape(stream); this.stroke = SerialUtils.readStroke(stream); this.outlinePaint = SerialUtils.readPaint(stream); this.fillPaint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/annotations/XYTextAnnotation.java000066400000000000000000000474471463604235500315360ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * XYTextAnnotation.java * --------------------- * (C) Copyright 2002-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Peter Kolb (patch 2809117); * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.annotations; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.HashUtils; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.AnnotationChangeEvent; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.text.TextUtils; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.TextAnchor; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; /** * A text annotation that can be placed at a particular (x, y) location on an * {@link XYPlot}. */ public class XYTextAnnotation extends AbstractXYAnnotation implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -2946063342782506328L; /** The default font. */ public static final Font DEFAULT_FONT = new Font("SansSerif", Font.PLAIN, 10); /** The default paint. */ public static final Paint DEFAULT_PAINT = Color.BLACK; /** The default text anchor. */ public static final TextAnchor DEFAULT_TEXT_ANCHOR = TextAnchor.CENTER; /** The default rotation anchor. */ public static final TextAnchor DEFAULT_ROTATION_ANCHOR = TextAnchor.CENTER; /** The default rotation angle. */ public static final double DEFAULT_ROTATION_ANGLE = 0.0; /** The text. */ private String text; /** The font. */ private Font font; /** The paint. */ private transient Paint paint; /** The x-coordinate. */ private double x; /** The y-coordinate. */ private double y; /** The text anchor (to be aligned with (x, y)). */ private TextAnchor textAnchor; /** The rotation anchor. */ private TextAnchor rotationAnchor; /** The rotation angle. */ private double rotationAngle; /** The background paint (possibly null). */ private transient Paint backgroundPaint; /** The flag that controls the visibility of the outline. */ private boolean outlineVisible; /** The outline paint (never null). */ private transient Paint outlinePaint; /** The outline stroke (never null). */ private transient Stroke outlineStroke; /** * Creates a new annotation to be displayed at the given coordinates. The * coordinates are specified in data space (they will be converted to * Java2D space for display). * * @param text the text ({@code null} not permitted). * @param x the x-coordinate (in data space, must be finite). * @param y the y-coordinate (in data space, must be finite). */ public XYTextAnnotation(String text, double x, double y) { super(); Args.nullNotPermitted(text, "text"); Args.requireFinite(x, "x"); Args.requireFinite(y, "y"); this.text = text; this.font = DEFAULT_FONT; this.paint = DEFAULT_PAINT; this.x = x; this.y = y; this.textAnchor = DEFAULT_TEXT_ANCHOR; this.rotationAnchor = DEFAULT_ROTATION_ANCHOR; this.rotationAngle = DEFAULT_ROTATION_ANGLE; // by default the outline and background won't be visible this.backgroundPaint = null; this.outlineVisible = false; this.outlinePaint = Color.BLACK; this.outlineStroke = new BasicStroke(0.5f); } /** * Returns the text for the annotation. * * @return The text (never {@code null}). * * @see #setText(String) */ public String getText() { return this.text; } /** * Sets the text for the annotation. * * @param text the text ({@code null} not permitted). * * @see #getText() */ public void setText(String text) { Args.nullNotPermitted(text, "text"); this.text = text; fireAnnotationChanged(); } /** * Returns the font for the annotation. * * @return The font (never {@code null}). * * @see #setFont(Font) */ public Font getFont() { return this.font; } /** * Sets the font for the annotation and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param font the font ({@code null} not permitted). * * @see #getFont() */ public void setFont(Font font) { Args.nullNotPermitted(font, "font"); this.font = font; fireAnnotationChanged(); } /** * Returns the paint for the annotation. * * @return The paint (never {@code null}). * * @see #setPaint(Paint) */ public Paint getPaint() { return this.paint; } /** * Sets the paint for the annotation and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getPaint() */ public void setPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.paint = paint; fireAnnotationChanged(); } /** * Returns the text anchor. * * @return The text anchor (never {@code null}). * * @see #setTextAnchor(TextAnchor) */ public TextAnchor getTextAnchor() { return this.textAnchor; } /** * Sets the text anchor (the point on the text bounding rectangle that is * aligned to the (x, y) coordinate of the annotation) and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param anchor the anchor point ({@code null} not permitted). * * @see #getTextAnchor() */ public void setTextAnchor(TextAnchor anchor) { Args.nullNotPermitted(anchor, "anchor"); this.textAnchor = anchor; fireAnnotationChanged(); } /** * Returns the rotation anchor. * * @return The rotation anchor point (never {@code null}). * * @see #setRotationAnchor(TextAnchor) */ public TextAnchor getRotationAnchor() { return this.rotationAnchor; } /** * Sets the rotation anchor point and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param anchor the anchor ({@code null} not permitted). * * @see #getRotationAnchor() */ public void setRotationAnchor(TextAnchor anchor) { Args.nullNotPermitted(anchor, "anchor"); this.rotationAnchor = anchor; fireAnnotationChanged(); } /** * Returns the rotation angle. * * @return The rotation angle. * * @see #setRotationAngle(double) */ public double getRotationAngle() { return this.rotationAngle; } /** * Sets the rotation angle and sends an {@link AnnotationChangeEvent} to * all registered listeners. The angle is measured clockwise in radians. * * @param angle the angle (in radians). * * @see #getRotationAngle() */ public void setRotationAngle(double angle) { this.rotationAngle = angle; fireAnnotationChanged(); } /** * Returns the x coordinate for the text anchor point (measured against the * domain axis). * * @return The x coordinate (in data space). * * @see #setX(double) */ public double getX() { return this.x; } /** * Sets the x coordinate for the text anchor point (measured against the * domain axis) and sends an {@link AnnotationChangeEvent} to all * registered listeners. * * @param x the x coordinate (in data space). * * @see #getX() */ public void setX(double x) { Args.requireFinite(x, "x"); this.x = x; fireAnnotationChanged(); } /** * Returns the y coordinate for the text anchor point (measured against the * range axis). * * @return The y coordinate (in data space). * * @see #setY(double) */ public double getY() { return this.y; } /** * Sets the y coordinate for the text anchor point (measured against the * range axis) and sends an {@link AnnotationChangeEvent} to all registered * listeners. * * @param y the y coordinate. * * @see #getY() */ public void setY(double y) { Args.requireFinite(y, "y"); this.y = y; fireAnnotationChanged(); } /** * Returns the background paint for the annotation. * * @return The background paint (possibly {@code null}). * * @see #setBackgroundPaint(Paint) */ public Paint getBackgroundPaint() { return this.backgroundPaint; } /** * Sets the background paint for the annotation and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} permitted). * * @see #getBackgroundPaint() */ public void setBackgroundPaint(Paint paint) { this.backgroundPaint = paint; fireAnnotationChanged(); } /** * Returns the outline paint for the annotation. * * @return The outline paint (never {@code null}). * * @see #setOutlinePaint(Paint) */ public Paint getOutlinePaint() { return this.outlinePaint; } /** * Sets the outline paint for the annotation and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getOutlinePaint() */ public void setOutlinePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.outlinePaint = paint; fireAnnotationChanged(); } /** * Returns the outline stroke for the annotation. * * @return The outline stroke (never {@code null}). * * @see #setOutlineStroke(Stroke) */ public Stroke getOutlineStroke() { return this.outlineStroke; } /** * Sets the outline stroke for the annotation and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getOutlineStroke() */ public void setOutlineStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.outlineStroke = stroke; fireAnnotationChanged(); } /** * Returns the flag that controls whether or not the outline is drawn. * * @return A boolean. */ public boolean isOutlineVisible() { return this.outlineVisible; } /** * Sets the flag that controls whether or not the outline is drawn and * sends an {@link AnnotationChangeEvent} to all registered listeners. * * @param visible the new flag value. */ public void setOutlineVisible(boolean visible) { this.outlineVisible = visible; fireAnnotationChanged(); } /** * Draws the annotation. * * @param g2 the graphics device. * @param plot the plot. * @param dataArea the data area. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param rendererIndex the renderer index. * @param info an optional info object that will be populated with * entity information. */ @Override public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, ValueAxis domainAxis, ValueAxis rangeAxis, int rendererIndex, PlotRenderingInfo info) { PlotOrientation orientation = plot.getOrientation(); RectangleEdge domainEdge = Plot.resolveDomainAxisLocation( plot.getDomainAxisLocation(), orientation); RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation( plot.getRangeAxisLocation(), orientation); float anchorX = (float) domainAxis.valueToJava2D( this.x, dataArea, domainEdge); float anchorY = (float) rangeAxis.valueToJava2D( this.y, dataArea, rangeEdge); if (orientation == PlotOrientation.HORIZONTAL) { float tempAnchor = anchorX; anchorX = anchorY; anchorY = tempAnchor; } g2.setFont(getFont()); Shape hotspot = TextUtils.calculateRotatedStringBounds( getText(), g2, anchorX, anchorY, getTextAnchor(), getRotationAngle(), getRotationAnchor()); if (this.backgroundPaint != null) { g2.setPaint(this.backgroundPaint); g2.fill(hotspot); } g2.setPaint(getPaint()); TextUtils.drawRotatedString(getText(), g2, anchorX, anchorY, getTextAnchor(), getRotationAngle(), getRotationAnchor()); if (this.outlineVisible) { g2.setStroke(this.outlineStroke); g2.setPaint(this.outlinePaint); g2.draw(hotspot); } String toolTip = getToolTipText(); String url = getURL(); if (toolTip != null || url != null) { addEntity(info, hotspot, rendererIndex, toolTip, url); } } /** * Tests this annotation for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYTextAnnotation)) { return false; } XYTextAnnotation that = (XYTextAnnotation) obj; if (Double.doubleToLongBits(this.x) != Double.doubleToLongBits(that.x)) { return false; } if (Double.doubleToLongBits(this.y) != Double.doubleToLongBits(that.y)) { return false; } if (Double.doubleToLongBits(this.rotationAngle) != Double.doubleToLongBits(that.rotationAngle)) { return false; } if (!PaintUtils.equal(this.paint, that.paint)) { return false; } if (this.outlineVisible != that.outlineVisible) { return false; } if (!Objects.equals(this.text, that.text)) { return false; } if (!Objects.equals(this.font, that.font)) { return false; } if (!Objects.equals(this.textAnchor, that.textAnchor)) { return false; } if (!Objects.equals(this.rotationAnchor, that.rotationAnchor)) { return false; } if (!PaintUtils.equal(this.backgroundPaint, that.backgroundPaint)) { return false; } if (!PaintUtils.equal(this.outlinePaint, that.outlinePaint)) { return false; } if (!Objects.equals(this.outlineStroke, that.outlineStroke)) { return false; } // fix the "equals not symmetric" problem if (!that.canEqual(this)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof XYTextAnnotation); } /** * Returns a hash code for the object. * * @return A hash code. */ @Override public int hashCode() { int hash = super.hashCode(); // equals calls superclass, hashCode must also hash = 23 * hash + Objects.hashCode(this.text); hash = 23 * hash + Objects.hashCode(this.font); hash = 23 * hash + HashUtils.hashCodeForPaint(this.paint); hash = 23 * hash + (int) (Double.doubleToLongBits(this.x) ^ (Double.doubleToLongBits(this.x) >>> 32)); hash = 23 * hash + (int) (Double.doubleToLongBits(this.y) ^ (Double.doubleToLongBits(this.y) >>> 32)); hash = 23 * hash + Objects.hashCode(this.textAnchor); hash = 23 * hash + Objects.hashCode(this.rotationAnchor); hash = 23 * hash + (int) (Double.doubleToLongBits(this.rotationAngle) ^ (Double.doubleToLongBits(this.rotationAngle) >>> 32)); hash = 23 * hash + HashUtils.hashCodeForPaint(this.backgroundPaint); hash = 23 * hash + (this.outlineVisible ? 1 : 0); hash = 23 * hash + HashUtils.hashCodeForPaint(this.outlinePaint); hash = 23 * hash + Objects.hashCode(this.outlineStroke); return hash; } /** * Returns a clone of the annotation. * * @return A clone. * * @throws CloneNotSupportedException if the annotation can't be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.paint, stream); SerialUtils.writePaint(this.backgroundPaint, stream); SerialUtils.writePaint(this.outlinePaint, stream); SerialUtils.writeStroke(this.outlineStroke, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.paint = SerialUtils.readPaint(stream); this.backgroundPaint = SerialUtils.readPaint(stream); this.outlinePaint = SerialUtils.readPaint(stream); this.outlineStroke = SerialUtils.readStroke(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/annotations/XYTitleAnnotation.java000066400000000000000000000325061463604235500316610ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * XYTitleAnnotation.java * ---------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Andrew Mickish; * Peter Kolb (patch 2809117); * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.annotations; import java.awt.Graphics2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.axis.AxisLocation; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.block.BlockParams; import org.jfree.chart.block.EntityBlockResult; import org.jfree.chart.block.RectangleConstraint; import org.jfree.chart.event.AnnotationChangeEvent; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.title.Title; import org.jfree.chart.ui.RectangleAnchor; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.Size2D; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.XYCoordinateType; import org.jfree.data.Range; /** * An annotation that allows any {@link Title} to be placed at a location on * an {@link XYPlot}. */ public class XYTitleAnnotation extends AbstractXYAnnotation implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -4364694501921559958L; /** The coordinate type. */ private XYCoordinateType coordinateType; /** The x-coordinate (in data space). */ private double x; /** The y-coordinate (in data space). */ private double y; /** The maximum width. */ private double maxWidth; /** The maximum height. */ private double maxHeight; /** The title. */ private Title title; /** * The title anchor point. */ private RectangleAnchor anchor; /** * Creates a new annotation to be displayed at the specified (x, y) * location. * * @param x the x-coordinate (in data space). * @param y the y-coordinate (in data space). * @param title the title ({@code null} not permitted). */ public XYTitleAnnotation(double x, double y, Title title) { this(x, y, title, RectangleAnchor.CENTER); } /** * Creates a new annotation to be displayed at the specified (x, y) * location. * * @param x the x-coordinate (in data space). * @param y the y-coordinate (in data space). * @param title the title ({@code null} not permitted). * @param anchor the title anchor ({@code null} not permitted). */ public XYTitleAnnotation(double x, double y, Title title, RectangleAnchor anchor) { super(); Args.nullNotPermitted(title, "title"); Args.nullNotPermitted(anchor, "anchor"); this.coordinateType = XYCoordinateType.RELATIVE; this.x = x; this.y = y; this.maxWidth = 0.0; this.maxHeight = 0.0; this.title = title; this.anchor = anchor; } /** * Returns the coordinate type (set in the constructor). * * @return The coordinate type (never {@code null}). */ public XYCoordinateType getCoordinateType() { return this.coordinateType; } /** * Returns the x-coordinate for the annotation. * * @return The x-coordinate. */ public double getX() { return this.x; } /** * Returns the y-coordinate for the annotation. * * @return The y-coordinate. */ public double getY() { return this.y; } /** * Returns the title for the annotation. * * @return The title. */ public Title getTitle() { return this.title; } /** * Returns the title anchor for the annotation. * * @return The title anchor. */ public RectangleAnchor getTitleAnchor() { return this.anchor; } /** * Returns the maximum width. * * @return The maximum width. */ public double getMaxWidth() { return this.maxWidth; } /** * Sets the maximum width and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param max the maximum width (0.0 or less means no maximum). */ public void setMaxWidth(double max) { this.maxWidth = max; fireAnnotationChanged(); } /** * Returns the maximum height. * * @return The maximum height. */ public double getMaxHeight() { return this.maxHeight; } /** * Sets the maximum height and sends an * {@link AnnotationChangeEvent} to all registered listeners. * * @param max the maximum height. */ public void setMaxHeight(double max) { this.maxHeight = max; fireAnnotationChanged(); } /** * Draws the annotation. This method is called by the drawing code in the * {@link XYPlot} class, you don't normally need to call this method * directly. * * @param g2 the graphics device. * @param plot the plot. * @param dataArea the data area. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param rendererIndex the renderer index. * @param info if supplied, this info object will be populated with * entity information. */ @Override public void draw(Graphics2D g2, XYPlot plot, Rectangle2D dataArea, ValueAxis domainAxis, ValueAxis rangeAxis, int rendererIndex, PlotRenderingInfo info) { PlotOrientation orientation = plot.getOrientation(); AxisLocation domainAxisLocation = plot.getDomainAxisLocation(); AxisLocation rangeAxisLocation = plot.getRangeAxisLocation(); RectangleEdge domainEdge = Plot.resolveDomainAxisLocation( domainAxisLocation, orientation); RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation( rangeAxisLocation, orientation); Range xRange = domainAxis.getRange(); Range yRange = rangeAxis.getRange(); double anchorX, anchorY; if (this.coordinateType == XYCoordinateType.RELATIVE) { anchorX = xRange.getLowerBound() + (this.x * xRange.getLength()); anchorY = yRange.getLowerBound() + (this.y * yRange.getLength()); } else { anchorX = domainAxis.valueToJava2D(this.x, dataArea, domainEdge); anchorY = rangeAxis.valueToJava2D(this.y, dataArea, rangeEdge); } float j2DX = (float) domainAxis.valueToJava2D(anchorX, dataArea, domainEdge); float j2DY = (float) rangeAxis.valueToJava2D(anchorY, dataArea, rangeEdge); float xx = 0.0f; float yy = 0.0f; if (orientation == PlotOrientation.HORIZONTAL) { xx = j2DY; yy = j2DX; } else if (orientation == PlotOrientation.VERTICAL) { xx = j2DX; yy = j2DY; } double maxW = dataArea.getWidth(); double maxH = dataArea.getHeight(); if (this.coordinateType == XYCoordinateType.RELATIVE) { if (this.maxWidth > 0.0) { maxW = maxW * this.maxWidth; } if (this.maxHeight > 0.0) { maxH = maxH * this.maxHeight; } } if (this.coordinateType == XYCoordinateType.DATA) { maxW = this.maxWidth; maxH = this.maxHeight; } RectangleConstraint rc = new RectangleConstraint( new Range(0, maxW), new Range(0, maxH)); Size2D size = this.title.arrange(g2, rc); Rectangle2D titleRect = new Rectangle2D.Double(0, 0, size.width, size.height); Point2D anchorPoint = this.anchor.getAnchorPoint(titleRect); xx = xx - (float) anchorPoint.getX(); yy = yy - (float) anchorPoint.getY(); titleRect.setRect(xx, yy, titleRect.getWidth(), titleRect.getHeight()); BlockParams p = new BlockParams(); if (info != null) { if (info.getOwner().getEntityCollection() != null) { p.setGenerateEntities(true); } } Object result = this.title.draw(g2, titleRect, p); if (info != null) { if (result instanceof EntityBlockResult) { EntityBlockResult ebr = (EntityBlockResult) result; info.getOwner().getEntityCollection().addAll( ebr.getEntityCollection()); } String toolTip = getToolTipText(); String url = getURL(); if (toolTip != null || url != null) { addEntity(info, new Rectangle2D.Float(xx, yy, (float) size.width, (float) size.height), rendererIndex, toolTip, url); } } } /** * Tests this object for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYTitleAnnotation)) { return false; } XYTitleAnnotation that = (XYTitleAnnotation) obj; if (Double.doubleToLongBits(this.x) != Double.doubleToLongBits(that.x)) { return false; } if (Double.doubleToLongBits(this.y) != Double.doubleToLongBits(that.y)) { return false; } if (Double.doubleToLongBits(this.maxWidth) != Double.doubleToLongBits(that.maxWidth)) { return false; } if (Double.doubleToLongBits(this.maxHeight) != Double.doubleToLongBits(that.maxHeight)) { return false; } if (!Objects.equals(this.coordinateType, that.coordinateType)) { return false; } if (!Objects.equals(this.title, that.title)) { return false; } if (!Objects.equals(this.anchor, that.anchor)){ return false; } // fix the "equals not symmetric" problem if (!that.canEqual(this)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof XYTitleAnnotation); } /** * Returns a hash code for this object. * * @return A hash code. */ @Override public int hashCode() { int result = super.hashCode(); // equals calls superclass, hashCode must also result = 67 * result + Objects.hashCode(this.coordinateType); result = 67 * result + (int) (Double.doubleToLongBits(this.x) ^ (Double.doubleToLongBits(this.x) >>> 32)); result = 67 * result + (int) (Double.doubleToLongBits(this.y) ^ (Double.doubleToLongBits(this.y) >>> 32)); result = 67 * result + (int) (Double.doubleToLongBits(this.maxWidth) ^ (Double.doubleToLongBits(this.maxWidth) >>> 32)); result = 67 * result + (int) (Double.doubleToLongBits(this.maxHeight) ^ (Double.doubleToLongBits(this.maxHeight) >>> 32)); result = 67 * result + Objects.hashCode(this.title); result = 67 * result + Objects.hashCode(this.anchor); return result; } /** * Returns a clone of the annotation. * * @return A clone. * * @throws CloneNotSupportedException if the annotation can't be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/annotations/package.html000066400000000000000000000002321463604235500276710ustar00rootroot00000000000000 A framework for adding annotations to charts. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/000077500000000000000000000000001463604235500240225ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/Axis.java000066400000000000000000001622161463604235500256010ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------- * Axis.java * --------- * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Bill Kelemen; * Nicolas Brodu; * Peter Kolb (patches 1934255 and 2603321); * Andrew Mickish (patch 1870189); * */ package org.jfree.chart.axis; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.Stroke; import java.awt.font.TextLayout; import java.awt.geom.AffineTransform; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.text.AttributedString; import java.util.Arrays; import java.util.EventListener; import java.util.List; import java.util.Objects; import javax.swing.event.EventListenerList; import org.jfree.chart.entity.AxisEntity; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.AxisChangeEvent; import org.jfree.chart.event.AxisChangeListener; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.text.AttributedStringUtils; import org.jfree.chart.text.TextUtils; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.ui.TextAnchor; import org.jfree.chart.util.AttrStringUtils; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.SerialUtils; /** * The base class for all axes in JFreeChart. Subclasses are divided into * those that display values ({@link ValueAxis}) and those that display * categories ({@link CategoryAxis}). */ public abstract class Axis implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 7719289504573298271L; /** The default axis visibility ({@code true}). */ public static final boolean DEFAULT_AXIS_VISIBLE = true; /** The default axis label font ({@code Font("SansSerif", Font.PLAIN, 12)}). */ public static final Font DEFAULT_AXIS_LABEL_FONT = new Font( "SansSerif", Font.PLAIN, 12); /** The default axis label paint ({@code Color.BLACK}). */ public static final Paint DEFAULT_AXIS_LABEL_PAINT = Color.BLACK; /** The default axis label insets ({@code RectangleInsets(3.0, 3.0, 3.0, 3.0)}). */ public static final RectangleInsets DEFAULT_AXIS_LABEL_INSETS = new RectangleInsets(3.0, 3.0, 3.0, 3.0); /** The default axis line paint ({@code Color.GRAY}). */ public static final Paint DEFAULT_AXIS_LINE_PAINT = Color.GRAY; /** The default axis line stroke ({@code BasicStroke(0.5f)}). */ public static final Stroke DEFAULT_AXIS_LINE_STROKE = new BasicStroke(0.5f); /** The default tick labels visibility ({@code true}). */ public static final boolean DEFAULT_TICK_LABELS_VISIBLE = true; /** The default tick label font ({@code Font("SansSerif", Font.PLAIN, 10)}). */ public static final Font DEFAULT_TICK_LABEL_FONT = new Font("SansSerif", Font.PLAIN, 10); /** The default tick label paint ({@code Color.BLACK}). */ public static final Paint DEFAULT_TICK_LABEL_PAINT = Color.BLACK; /** The default tick label insets ({@code RectangleInsets(2.0, 4.0, 2.0, 4.0)}). */ public static final RectangleInsets DEFAULT_TICK_LABEL_INSETS = new RectangleInsets(2.0, 4.0, 2.0, 4.0); /** The default tick marks visible ({@code true}). */ public static final boolean DEFAULT_TICK_MARKS_VISIBLE = true; /** The default tick stroke ({@code BasicStroke(0.5f)}). */ public static final Stroke DEFAULT_TICK_MARK_STROKE = new BasicStroke(0.5f); /** The default tick paint ({@code Color.GRAY}). */ public static final Paint DEFAULT_TICK_MARK_PAINT = Color.GRAY; /** The default tick mark inside length ({@code 0.0f}). */ public static final float DEFAULT_TICK_MARK_INSIDE_LENGTH = 0.0f; /** The default tick mark outside length ({@code 2.0f}). */ public static final float DEFAULT_TICK_MARK_OUTSIDE_LENGTH = 2.0f; /** A flag indicating whether or not the axis is visible. */ private boolean visible; /** The label for the axis. */ private String label; /** * An attributed label for the axis (overrides label if non-null). * We have to use this override method to preserve the API compatibility. */ private transient AttributedString attributedLabel; /** The font for displaying the axis label. */ private Font labelFont; /** The paint for drawing the axis label. */ private transient Paint labelPaint; /** The insets for the axis label. */ private RectangleInsets labelInsets; /** The label angle. */ private double labelAngle; /** The axis label location (new in 1.0.16). */ private AxisLabelLocation labelLocation; /** A flag that controls whether or not the axis line is visible. */ private boolean axisLineVisible; /** The stroke used for the axis line. */ private transient Stroke axisLineStroke; /** The paint used for the axis line. */ private transient Paint axisLinePaint; /** * A flag that indicates whether or not tick labels are visible for the * axis. */ private boolean tickLabelsVisible; /** The font used to display the tick labels. */ private Font tickLabelFont; /** The color used to display the tick labels. */ private transient Paint tickLabelPaint; /** The blank space around each tick label. */ private RectangleInsets tickLabelInsets; /** * A flag that indicates whether or not major tick marks are visible for * the axis. */ private boolean tickMarksVisible; /** * The length of the major tick mark inside the data area (zero * permitted). */ private float tickMarkInsideLength; /** * The length of the major tick mark outside the data area (zero * permitted). */ private float tickMarkOutsideLength; /** * A flag that indicates whether or not minor tick marks are visible for the * axis. */ private boolean minorTickMarksVisible; /** * The length of the minor tick mark inside the data area (zero permitted). */ private float minorTickMarkInsideLength; /** * The length of the minor tick mark outside the data area (zero permitted). */ private float minorTickMarkOutsideLength; /** The stroke used to draw tick marks. */ private transient Stroke tickMarkStroke; /** The paint used to draw tick marks. */ private transient Paint tickMarkPaint; /** The fixed (horizontal or vertical) dimension for the axis. */ private double fixedDimension; /** * A reference back to the plot that the axis is assigned to (can be * {@code null}). */ private transient Plot plot; /** Storage for registered listeners. */ private transient EventListenerList listenerList; /** * Constructs an axis with the specific label and default values for other * attributes. * * @param label the axis label ({@code null} permitted). */ protected Axis(String label) { this.label = label; this.visible = DEFAULT_AXIS_VISIBLE; this.labelFont = DEFAULT_AXIS_LABEL_FONT; this.labelPaint = DEFAULT_AXIS_LABEL_PAINT; this.labelInsets = DEFAULT_AXIS_LABEL_INSETS; this.labelAngle = 0.0; this.labelLocation = AxisLabelLocation.MIDDLE; this.axisLineVisible = true; this.axisLinePaint = DEFAULT_AXIS_LINE_PAINT; this.axisLineStroke = DEFAULT_AXIS_LINE_STROKE; this.tickLabelsVisible = DEFAULT_TICK_LABELS_VISIBLE; this.tickLabelFont = DEFAULT_TICK_LABEL_FONT; this.tickLabelPaint = DEFAULT_TICK_LABEL_PAINT; this.tickLabelInsets = DEFAULT_TICK_LABEL_INSETS; this.tickMarksVisible = DEFAULT_TICK_MARKS_VISIBLE; this.tickMarkStroke = DEFAULT_TICK_MARK_STROKE; this.tickMarkPaint = DEFAULT_TICK_MARK_PAINT; this.tickMarkInsideLength = DEFAULT_TICK_MARK_INSIDE_LENGTH; this.tickMarkOutsideLength = DEFAULT_TICK_MARK_OUTSIDE_LENGTH; this.minorTickMarksVisible = false; this.minorTickMarkInsideLength = 0.0f; this.minorTickMarkOutsideLength = 2.0f; this.plot = null; this.listenerList = new EventListenerList(); } /** * Returns {@code true} if the axis is visible, and * {@code false} otherwise. * * @return A boolean. * * @see #setVisible(boolean) */ public boolean isVisible() { return this.visible; } /** * Sets a flag that controls whether or not the axis is visible and sends * an {@link AxisChangeEvent} to all registered listeners. * * @param flag the flag. * * @see #isVisible() */ public void setVisible(boolean flag) { if (flag != this.visible) { this.visible = flag; fireChangeEvent(); } } /** * Returns the label for the axis. * * @return The label for the axis ({@code null} possible). * * @see #getLabelFont() * @see #getLabelPaint() * @see #setLabel(String) */ public String getLabel() { return this.label; } /** * Sets the label for the axis and sends an {@link AxisChangeEvent} to all * registered listeners. * * @param label the new label ({@code null} permitted). * * @see #getLabel() * @see #setLabelFont(Font) * @see #setLabelPaint(Paint) */ public void setLabel(String label) { this.label = label; fireChangeEvent(); } /** * Returns the attributed label (the returned value is a copy, so * modifying it will not impact the state of the axis). The default value * is {@code null}. * * @return The attributed label (possibly {@code null}). */ public AttributedString getAttributedLabel() { if (this.attributedLabel != null) { return new AttributedString(this.attributedLabel.getIterator()); } else { return null; } } /** * Sets the attributed label for the axis and sends an * {@link AxisChangeEvent} to all registered listeners. This is a * convenience method that converts the string into an * {@code AttributedString} using the current font attributes. * * @param label the label ({@code null} permitted). */ public void setAttributedLabel(String label) { setAttributedLabel(createAttributedLabel(label)); } /** * Sets the attributed label for the axis and sends an * {@link AxisChangeEvent} to all registered listeners. * * @param label the label ({@code null} permitted). */ public void setAttributedLabel(AttributedString label) { if (label != null) { this.attributedLabel = new AttributedString(label.getIterator()); } else { this.attributedLabel = null; } fireChangeEvent(); } /** * Creates and returns an {@code AttributedString} with the specified * text and the labelFont and labelPaint applied as attributes. * * @param label the label ({@code null} permitted). * * @return An attributed string or {@code null}. */ public AttributedString createAttributedLabel(String label) { if (label == null) { return null; } AttributedString s = new AttributedString(label); s.addAttributes(this.labelFont.getAttributes(), 0, label.length()); return s; } /** * Returns the font for the axis label. * * @return The font (never {@code null}). * * @see #setLabelFont(Font) */ public Font getLabelFont() { return this.labelFont; } /** * Sets the font for the axis label and sends an {@link AxisChangeEvent} * to all registered listeners. * * @param font the font ({@code null} not permitted). * * @see #getLabelFont() */ public void setLabelFont(Font font) { Args.nullNotPermitted(font, "font"); if (!this.labelFont.equals(font)) { this.labelFont = font; fireChangeEvent(); } } /** * Returns the color/shade used to draw the axis label. * * @return The paint (never {@code null}). * * @see #setLabelPaint(Paint) */ public Paint getLabelPaint() { return this.labelPaint; } /** * Sets the paint used to draw the axis label and sends an * {@link AxisChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getLabelPaint() */ public void setLabelPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.labelPaint = paint; fireChangeEvent(); } /** * Returns the insets for the label (that is, the amount of blank space * that should be left around the label). * * @return The label insets (never {@code null}). * * @see #setLabelInsets(RectangleInsets) */ public RectangleInsets getLabelInsets() { return this.labelInsets; } /** * Sets the insets for the axis label, and sends an {@link AxisChangeEvent} * to all registered listeners. * * @param insets the insets ({@code null} not permitted). * * @see #getLabelInsets() */ public void setLabelInsets(RectangleInsets insets) { setLabelInsets(insets, true); } /** * Sets the insets for the axis label, and sends an {@link AxisChangeEvent} * to all registered listeners. * * @param insets the insets ({@code null} not permitted). * @param notify notify listeners? */ public void setLabelInsets(RectangleInsets insets, boolean notify) { Args.nullNotPermitted(insets, "insets"); if (!insets.equals(this.labelInsets)) { this.labelInsets = insets; if (notify) { fireChangeEvent(); } } } /** * Returns the angle of the axis label. * * @return The angle (in radians). * * @see #setLabelAngle(double) */ public double getLabelAngle() { return this.labelAngle; } /** * Sets the angle for the label and sends an {@link AxisChangeEvent} to all * registered listeners. * * @param angle the angle (in radians). * * @see #getLabelAngle() */ public void setLabelAngle(double angle) { this.labelAngle = angle; fireChangeEvent(); } /** * Returns the location of the axis label. The default is * {@link AxisLabelLocation#MIDDLE}. * * @return The location of the axis label (never {@code null}). */ public AxisLabelLocation getLabelLocation() { return this.labelLocation; } /** * Sets the axis label location and sends an {@link AxisChangeEvent} to * all registered listeners. * * @param location the new location ({@code null} not permitted). */ public void setLabelLocation(AxisLabelLocation location) { Args.nullNotPermitted(location, "location"); this.labelLocation = location; fireChangeEvent(); } /** * A flag that controls whether or not the axis line is drawn. * * @return A boolean. * * @see #getAxisLinePaint() * @see #getAxisLineStroke() * @see #setAxisLineVisible(boolean) */ public boolean isAxisLineVisible() { return this.axisLineVisible; } /** * Sets a flag that controls whether or not the axis line is visible and * sends an {@link AxisChangeEvent} to all registered listeners. * * @param visible the flag. * * @see #isAxisLineVisible() * @see #setAxisLinePaint(Paint) * @see #setAxisLineStroke(Stroke) */ public void setAxisLineVisible(boolean visible) { this.axisLineVisible = visible; fireChangeEvent(); } /** * Returns the paint used to draw the axis line. * * @return The paint (never {@code null}). * * @see #setAxisLinePaint(Paint) */ public Paint getAxisLinePaint() { return this.axisLinePaint; } /** * Sets the paint used to draw the axis line and sends an * {@link AxisChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getAxisLinePaint() */ public void setAxisLinePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.axisLinePaint = paint; fireChangeEvent(); } /** * Returns the stroke used to draw the axis line. * * @return The stroke (never {@code null}). * * @see #setAxisLineStroke(Stroke) */ public Stroke getAxisLineStroke() { return this.axisLineStroke; } /** * Sets the stroke used to draw the axis line and sends an * {@link AxisChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getAxisLineStroke() */ public void setAxisLineStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.axisLineStroke = stroke; fireChangeEvent(); } /** * Returns a flag indicating whether or not the tick labels are visible. * * @return The flag. * * @see #getTickLabelFont() * @see #getTickLabelPaint() * @see #setTickLabelsVisible(boolean) */ public boolean isTickLabelsVisible() { return this.tickLabelsVisible; } /** * Sets the flag that determines whether or not the tick labels are * visible and sends an {@link AxisChangeEvent} to all registered * listeners. * * @param flag the flag. * * @see #isTickLabelsVisible() * @see #setTickLabelFont(Font) * @see #setTickLabelPaint(Paint) */ public void setTickLabelsVisible(boolean flag) { if (flag != this.tickLabelsVisible) { this.tickLabelsVisible = flag; fireChangeEvent(); } } /** * Returns the flag that indicates whether or not the minor tick marks are * showing. * * @return The flag that indicates whether or not the minor tick marks are * showing. * * @see #setMinorTickMarksVisible(boolean) */ public boolean isMinorTickMarksVisible() { return this.minorTickMarksVisible; } /** * Sets the flag that indicates whether or not the minor tick marks are * showing and sends an {@link AxisChangeEvent} to all registered * listeners. * * @param flag the flag. * * @see #isMinorTickMarksVisible() */ public void setMinorTickMarksVisible(boolean flag) { if (flag != this.minorTickMarksVisible) { this.minorTickMarksVisible = flag; fireChangeEvent(); } } /** * Returns the font used for the tick labels (if showing). * * @return The font (never {@code null}). * * @see #setTickLabelFont(Font) */ public Font getTickLabelFont() { return this.tickLabelFont; } /** * Sets the font for the tick labels and sends an {@link AxisChangeEvent} * to all registered listeners. * * @param font the font ({@code null} not allowed). * * @see #getTickLabelFont() */ public void setTickLabelFont(Font font) { Args.nullNotPermitted(font, "font"); if (!this.tickLabelFont.equals(font)) { this.tickLabelFont = font; fireChangeEvent(); } } /** * Returns the color/shade used for the tick labels. * * @return The paint used for the tick labels. * * @see #setTickLabelPaint(Paint) */ public Paint getTickLabelPaint() { return this.tickLabelPaint; } /** * Sets the paint used to draw tick labels (if they are showing) and * sends an {@link AxisChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getTickLabelPaint() */ public void setTickLabelPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.tickLabelPaint = paint; fireChangeEvent(); } /** * Returns the insets for the tick labels. * * @return The insets (never {@code null}). * * @see #setTickLabelInsets(RectangleInsets) */ public RectangleInsets getTickLabelInsets() { return this.tickLabelInsets; } /** * Sets the insets for the tick labels and sends an {@link AxisChangeEvent} * to all registered listeners. * * @param insets the insets ({@code null} not permitted). * * @see #getTickLabelInsets() */ public void setTickLabelInsets(RectangleInsets insets) { Args.nullNotPermitted(insets, "insets"); if (!this.tickLabelInsets.equals(insets)) { this.tickLabelInsets = insets; fireChangeEvent(); } } /** * Returns the flag that indicates whether or not the tick marks are * showing. * * @return The flag that indicates whether or not the tick marks are * showing. * * @see #setTickMarksVisible(boolean) */ public boolean isTickMarksVisible() { return this.tickMarksVisible; } /** * Sets the flag that indicates whether or not the tick marks are showing * and sends an {@link AxisChangeEvent} to all registered listeners. * * @param flag the flag. * * @see #isTickMarksVisible() */ public void setTickMarksVisible(boolean flag) { if (flag != this.tickMarksVisible) { this.tickMarksVisible = flag; fireChangeEvent(); } } /** * Returns the inside length of the tick marks. * * @return The length. * * @see #getTickMarkOutsideLength() * @see #setTickMarkInsideLength(float) */ public float getTickMarkInsideLength() { return this.tickMarkInsideLength; } /** * Sets the inside length of the tick marks and sends * an {@link AxisChangeEvent} to all registered listeners. * * @param length the new length. * * @see #getTickMarkInsideLength() */ public void setTickMarkInsideLength(float length) { this.tickMarkInsideLength = length; fireChangeEvent(); } /** * Returns the outside length of the tick marks. * * @return The length. * * @see #getTickMarkInsideLength() * @see #setTickMarkOutsideLength(float) */ public float getTickMarkOutsideLength() { return this.tickMarkOutsideLength; } /** * Sets the outside length of the tick marks and sends * an {@link AxisChangeEvent} to all registered listeners. * * @param length the new length. * * @see #getTickMarkInsideLength() */ public void setTickMarkOutsideLength(float length) { this.tickMarkOutsideLength = length; fireChangeEvent(); } /** * Returns the stroke used to draw tick marks. * * @return The stroke (never {@code null}). * * @see #setTickMarkStroke(Stroke) */ public Stroke getTickMarkStroke() { return this.tickMarkStroke; } /** * Sets the stroke used to draw tick marks and sends * an {@link AxisChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getTickMarkStroke() */ public void setTickMarkStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); if (!this.tickMarkStroke.equals(stroke)) { this.tickMarkStroke = stroke; fireChangeEvent(); } } /** * Returns the paint used to draw tick marks (if they are showing). * * @return The paint (never {@code null}). * * @see #setTickMarkPaint(Paint) */ public Paint getTickMarkPaint() { return this.tickMarkPaint; } /** * Sets the paint used to draw tick marks and sends an * {@link AxisChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getTickMarkPaint() */ public void setTickMarkPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.tickMarkPaint = paint; fireChangeEvent(); } /** * Returns the inside length of the minor tick marks. * * @return The length. * * @see #getMinorTickMarkOutsideLength() * @see #setMinorTickMarkInsideLength(float) */ public float getMinorTickMarkInsideLength() { return this.minorTickMarkInsideLength; } /** * Sets the inside length of the minor tick marks and sends * an {@link AxisChangeEvent} to all registered listeners. * * @param length the new length. * * @see #getMinorTickMarkInsideLength() */ public void setMinorTickMarkInsideLength(float length) { this.minorTickMarkInsideLength = length; fireChangeEvent(); } /** * Returns the outside length of the minor tick marks. * * @return The length. * * @see #getMinorTickMarkInsideLength() * @see #setMinorTickMarkOutsideLength(float) */ public float getMinorTickMarkOutsideLength() { return this.minorTickMarkOutsideLength; } /** * Sets the outside length of the minor tick marks and sends * an {@link AxisChangeEvent} to all registered listeners. * * @param length the new length. * * @see #getMinorTickMarkInsideLength() */ public void setMinorTickMarkOutsideLength(float length) { this.minorTickMarkOutsideLength = length; fireChangeEvent(); } /** * Returns the plot that the axis is assigned to. This method will return * {@code null} if the axis is not currently assigned to a plot. * * @return The plot that the axis is assigned to (possibly {@code null}). * * @see #setPlot(Plot) */ public Plot getPlot() { return this.plot; } /** * Sets a reference to the plot that the axis is assigned to. *

* This method is used internally, you shouldn't need to call it yourself. * * @param plot the plot. * * @see #getPlot() */ public void setPlot(Plot plot) { this.plot = plot; configure(); } /** * Returns the fixed dimension for the axis. * * @return The fixed dimension. * * @see #setFixedDimension(double) */ public double getFixedDimension() { return this.fixedDimension; } /** * Sets the fixed dimension for the axis. *

* This is used when combining more than one plot on a chart. In this case, * there may be several axes that need to have the same height or width so * that they are aligned. This method is used to fix a dimension for the * axis (the context determines whether the dimension is horizontal or * vertical). * * @param dimension the fixed dimension. * * @see #getFixedDimension() */ public void setFixedDimension(double dimension) { this.fixedDimension = dimension; } /** * Configures the axis to work with the current plot. Override this method * to perform any special processing (such as auto-rescaling). */ public abstract void configure(); /** * Estimates the space (height or width) required to draw the axis. * * @param g2 the graphics device. * @param plot the plot that the axis belongs to. * @param plotArea the area within which the plot (including axes) should * be drawn. * @param edge the axis location. * @param space space already reserved. * * @return The space required to draw the axis (including pre-reserved * space). */ public abstract AxisSpace reserveSpace(Graphics2D g2, Plot plot, Rectangle2D plotArea, RectangleEdge edge, AxisSpace space); /** * Draws the axis on a Java 2D graphics device (such as the screen or a * printer). * * @param g2 the graphics device ({@code null} not permitted). * @param cursor the cursor location (determines where to draw the axis). * @param plotArea the area within which the axes and plot should be drawn. * @param dataArea the area within which the data should be drawn. * @param edge the axis location ({@code null} not permitted). * @param plotState collects information about the plot * ({@code null} permitted). * * @return The axis state (never {@code null}). */ public abstract AxisState draw(Graphics2D g2, double cursor, Rectangle2D plotArea, Rectangle2D dataArea, RectangleEdge edge, PlotRenderingInfo plotState); /** * Calculates the positions of the ticks for the axis, storing the results * in the tick list (ready for drawing). * * @param g2 the graphics device. * @param state the axis state. * @param dataArea the area inside the axes. * @param edge the edge on which the axis is located. * * @return The list of ticks. */ public abstract List refreshTicks(Graphics2D g2, AxisState state, Rectangle2D dataArea, RectangleEdge edge); /** * Creates an entity for the axis and adds it to the rendering info. * If {@code plotState} is {@code null}, this means that rendering info * is not being collected so this method simply returns without doing * anything. * * @param cursor the initial cursor value. * @param state the axis state after completion of the drawing with a * possibly updated cursor position. * @param dataArea the data area. * @param edge the edge ({@code null} not permitted). * @param plotState the PlotRenderingInfo from which a reference to the * entity collection can be obtained ({@code null} permitted). */ protected void createAndAddEntity(double cursor, AxisState state, Rectangle2D dataArea, RectangleEdge edge, PlotRenderingInfo plotState) { Args.nullNotPermitted(edge, "edge"); if (plotState == null || plotState.getOwner() == null) { return; // no need to create entity if we can't save it anyways... } Rectangle2D hotspot = null; if (edge.equals(RectangleEdge.TOP)) { hotspot = new Rectangle2D.Double(dataArea.getX(), state.getCursor(), dataArea.getWidth(), cursor - state.getCursor()); } else if (edge.equals(RectangleEdge.BOTTOM)) { hotspot = new Rectangle2D.Double(dataArea.getX(), cursor, dataArea.getWidth(), state.getCursor() - cursor); } else if (edge.equals(RectangleEdge.LEFT)) { hotspot = new Rectangle2D.Double(state.getCursor(), dataArea.getY(), cursor - state.getCursor(), dataArea.getHeight()); } else if (edge.equals(RectangleEdge.RIGHT)) { hotspot = new Rectangle2D.Double(cursor, dataArea.getY(), state.getCursor() - cursor, dataArea.getHeight()); } EntityCollection e = plotState.getOwner().getEntityCollection(); if (e != null) { e.add(new AxisEntity(hotspot, this)); } } /** * Registers an object for notification of changes to the axis. * * @param listener the object that is being registered. * * @see #removeChangeListener(AxisChangeListener) */ public void addChangeListener(AxisChangeListener listener) { this.listenerList.add(AxisChangeListener.class, listener); } /** * Deregisters an object for notification of changes to the axis. * * @param listener the object to deregister. * * @see #addChangeListener(AxisChangeListener) */ public void removeChangeListener(AxisChangeListener listener) { this.listenerList.remove(AxisChangeListener.class, listener); } /** * Returns {@code true} if the specified object is registered with * the dataset as a listener. Most applications won't need to call this * method, it exists mainly for use by unit testing code. * * @param listener the listener. * * @return A boolean. */ public boolean hasListener(EventListener listener) { List list = Arrays.asList(this.listenerList.getListenerList()); return list.contains(listener); } /** * Notifies all registered listeners that the axis has changed. * The AxisChangeEvent provides information about the change. * * @param event information about the change to the axis. */ protected void notifyListeners(AxisChangeEvent event) { Object[] listeners = this.listenerList.getListenerList(); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == AxisChangeListener.class) { ((AxisChangeListener) listeners[i + 1]).axisChanged(event); } } } /** * Sends an {@link AxisChangeEvent} to all registered listeners. */ protected void fireChangeEvent() { notifyListeners(new AxisChangeEvent(this)); } /** * Returns a rectangle that encloses the axis label. This is typically * used for layout purposes (it gives the maximum dimensions of the label). * * @param g2 the graphics device. * @param edge the edge of the plot area along which the axis is measuring. * * @return The enclosing rectangle. */ protected Rectangle2D getLabelEnclosure(Graphics2D g2, RectangleEdge edge) { Rectangle2D result = new Rectangle2D.Double(); Rectangle2D bounds = null; if (this.attributedLabel != null) { TextLayout layout = new TextLayout( this.attributedLabel.getIterator(), g2.getFontRenderContext()); bounds = layout.getBounds(); } else { String axisLabel = getLabel(); if (axisLabel != null && !axisLabel.equals("")) { FontMetrics fm = g2.getFontMetrics(getLabelFont()); bounds = TextUtils.getTextBounds(axisLabel, g2, fm); } } if (bounds != null) { RectangleInsets insets = getLabelInsets(); bounds = insets.createOutsetRectangle(bounds); double angle = getLabelAngle(); if (edge == RectangleEdge.LEFT || edge == RectangleEdge.RIGHT) { angle = angle - Math.PI / 2.0; } double x = bounds.getCenterX(); double y = bounds.getCenterY(); AffineTransform transformer = AffineTransform.getRotateInstance(angle, x, y); Shape labelBounds = transformer.createTransformedShape(bounds); result = labelBounds.getBounds2D(); } return result; } /** * Returns the x-coordinate for the point to which the axis label should * be aligned. * * @param location the axis label location ({@code null} not permitted). * @param dataArea the display area in which the data will be rendered ({@code null} not permitted). * * @return The x-coordinate. */ protected double labelLocationX(AxisLabelLocation location, Rectangle2D dataArea) { if (location.equals(AxisLabelLocation.HIGH_END)) { return dataArea.getMaxX(); } if (location.equals(AxisLabelLocation.MIDDLE)) { return dataArea.getCenterX(); } if (location.equals(AxisLabelLocation.LOW_END)) { return dataArea.getMinX(); } throw new RuntimeException("Unexpected AxisLabelLocation: " + location); } /** * Returns the y-coordinate for the point to which the axis label should * be aligned. * * @param location the location ({@code null} not permitted). * @param dataArea the data area ({@code null} not permitted). * * @return The y-coordinate. */ protected double labelLocationY(AxisLabelLocation location, Rectangle2D dataArea) { if (location.equals(AxisLabelLocation.HIGH_END)) { return dataArea.getMinY(); } if (location.equals(AxisLabelLocation.MIDDLE)) { return dataArea.getCenterY(); } if (location.equals(AxisLabelLocation.LOW_END)) { return dataArea.getMaxY(); } throw new RuntimeException("Unexpected AxisLabelLocation: " + location); } /** * Returns the appropriate horizontal text anchor for the specified axis * location. * * @param location the location ({@code null} not permitted). * * @return The text anchor (never {@code null}). */ protected TextAnchor labelAnchorH(AxisLabelLocation location) { if (location.equals(AxisLabelLocation.HIGH_END)) { return TextAnchor.CENTER_RIGHT; } if (location.equals(AxisLabelLocation.MIDDLE)) { return TextAnchor.CENTER; } if (location.equals(AxisLabelLocation.LOW_END)) { return TextAnchor.CENTER_LEFT; } throw new RuntimeException("Unexpected AxisLabelLocation: " + location); } /** * Returns the appropriate vertical text anchor for the specified axis * location. * * @param location the location ({@code null} not permitted). * * @return The text anchor (never {@code null}). */ protected TextAnchor labelAnchorV(AxisLabelLocation location) { if (location.equals(AxisLabelLocation.HIGH_END)) { return TextAnchor.CENTER_RIGHT; } if (location.equals(AxisLabelLocation.MIDDLE)) { return TextAnchor.CENTER; } if (location.equals(AxisLabelLocation.LOW_END)) { return TextAnchor.CENTER_LEFT; } throw new RuntimeException("Unexpected AxisLabelLocation: " + location); } /** * Draws the axis label. * * @param label the label text. * @param g2 the graphics device. * @param plotArea the plot area. * @param dataArea the area inside the axes. * @param edge the location of the axis. * @param state the axis state ({@code null} not permitted). * * @return Information about the axis. */ protected AxisState drawLabel(String label, Graphics2D g2, Rectangle2D plotArea, Rectangle2D dataArea, RectangleEdge edge, AxisState state) { // it is unlikely that 'state' will be null, but check anyway... Args.nullNotPermitted(state, "state"); if ((label == null) || (label.equals(""))) { return state; } Font font = getLabelFont(); RectangleInsets insets = getLabelInsets(); g2.setFont(font); g2.setPaint(getLabelPaint()); FontMetrics fm = g2.getFontMetrics(); Rectangle2D labelBounds = TextUtils.getTextBounds(label, g2, fm); if (edge == RectangleEdge.TOP) { AffineTransform t = AffineTransform.getRotateInstance( getLabelAngle(), labelBounds.getCenterX(), labelBounds.getCenterY()); Shape rotatedLabelBounds = t.createTransformedShape(labelBounds); labelBounds = rotatedLabelBounds.getBounds2D(); double labelx = labelLocationX(this.labelLocation, dataArea); double labely = state.getCursor() - insets.getBottom() - labelBounds.getHeight() / 2.0; TextAnchor anchor = labelAnchorH(this.labelLocation); TextUtils.drawRotatedString(label, g2, (float) labelx, (float) labely, anchor, getLabelAngle(), TextAnchor.CENTER); state.cursorUp(insets.getTop() + labelBounds.getHeight() + insets.getBottom()); } else if (edge == RectangleEdge.BOTTOM) { AffineTransform t = AffineTransform.getRotateInstance( getLabelAngle(), labelBounds.getCenterX(), labelBounds.getCenterY()); Shape rotatedLabelBounds = t.createTransformedShape(labelBounds); labelBounds = rotatedLabelBounds.getBounds2D(); double labelx = labelLocationX(this.labelLocation, dataArea); double labely = state.getCursor() + insets.getTop() + labelBounds.getHeight() / 2.0; TextAnchor anchor = labelAnchorH(this.labelLocation); TextUtils.drawRotatedString(label, g2, (float) labelx, (float) labely, anchor, getLabelAngle(), TextAnchor.CENTER); state.cursorDown(insets.getTop() + labelBounds.getHeight() + insets.getBottom()); } else if (edge == RectangleEdge.LEFT) { AffineTransform t = AffineTransform.getRotateInstance( getLabelAngle() - Math.PI / 2.0, labelBounds.getCenterX(), labelBounds.getCenterY()); Shape rotatedLabelBounds = t.createTransformedShape(labelBounds); labelBounds = rotatedLabelBounds.getBounds2D(); double labelx = state.getCursor() - insets.getRight() - labelBounds.getWidth() / 2.0; double labely = labelLocationY(this.labelLocation, dataArea); TextAnchor anchor = labelAnchorV(this.labelLocation); TextUtils.drawRotatedString(label, g2, (float) labelx, (float) labely, anchor, getLabelAngle() - Math.PI / 2.0, anchor); state.cursorLeft(insets.getLeft() + labelBounds.getWidth() + insets.getRight()); } else if (edge == RectangleEdge.RIGHT) { AffineTransform t = AffineTransform.getRotateInstance( getLabelAngle() + Math.PI / 2.0, labelBounds.getCenterX(), labelBounds.getCenterY()); Shape rotatedLabelBounds = t.createTransformedShape(labelBounds); labelBounds = rotatedLabelBounds.getBounds2D(); double labelx = state.getCursor() + insets.getLeft() + labelBounds.getWidth() / 2.0; double labely = labelLocationY(this.labelLocation, dataArea); TextAnchor anchor = labelAnchorV(this.labelLocation); TextUtils.drawRotatedString(label, g2, (float) labelx, (float) labely, anchor, getLabelAngle() + Math.PI / 2.0, anchor); state.cursorRight(insets.getLeft() + labelBounds.getWidth() + insets.getRight()); } return state; } /** * Draws the axis label. * * @param label the label text. * @param g2 the graphics device. * @param plotArea the plot area. * @param dataArea the area inside the axes. * @param edge the location of the axis. * @param state the axis state ({@code null} not permitted). * * @return Information about the axis. */ protected AxisState drawAttributedLabel(AttributedString label, Graphics2D g2, Rectangle2D plotArea, Rectangle2D dataArea, RectangleEdge edge, AxisState state) { // it is unlikely that 'state' will be null, but check anyway... Args.nullNotPermitted(state, "state"); if (label == null) { return state; } RectangleInsets insets = getLabelInsets(); g2.setFont(getLabelFont()); g2.setPaint(getLabelPaint()); TextLayout layout = new TextLayout(this.attributedLabel.getIterator(), g2.getFontRenderContext()); Rectangle2D labelBounds = layout.getBounds(); if (edge == RectangleEdge.TOP) { AffineTransform t = AffineTransform.getRotateInstance( getLabelAngle(), labelBounds.getCenterX(), labelBounds.getCenterY()); Shape rotatedLabelBounds = t.createTransformedShape(labelBounds); labelBounds = rotatedLabelBounds.getBounds2D(); double labelx = labelLocationX(this.labelLocation, dataArea); double labely = state.getCursor() - insets.getBottom() - labelBounds.getHeight() / 2.0; TextAnchor anchor = labelAnchorH(this.labelLocation); AttrStringUtils.drawRotatedString(label, g2, (float) labelx, (float) labely, anchor, getLabelAngle(), TextAnchor.CENTER); state.cursorUp(insets.getTop() + labelBounds.getHeight() + insets.getBottom()); } else if (edge == RectangleEdge.BOTTOM) { AffineTransform t = AffineTransform.getRotateInstance( getLabelAngle(), labelBounds.getCenterX(), labelBounds.getCenterY()); Shape rotatedLabelBounds = t.createTransformedShape(labelBounds); labelBounds = rotatedLabelBounds.getBounds2D(); double labelx = labelLocationX(this.labelLocation, dataArea); double labely = state.getCursor() + insets.getTop() + labelBounds.getHeight() / 2.0; TextAnchor anchor = labelAnchorH(this.labelLocation); AttrStringUtils.drawRotatedString(label, g2, (float) labelx, (float) labely, anchor, getLabelAngle(), TextAnchor.CENTER); state.cursorDown(insets.getTop() + labelBounds.getHeight() + insets.getBottom()); } else if (edge == RectangleEdge.LEFT) { AffineTransform t = AffineTransform.getRotateInstance( getLabelAngle() - Math.PI / 2.0, labelBounds.getCenterX(), labelBounds.getCenterY()); Shape rotatedLabelBounds = t.createTransformedShape(labelBounds); labelBounds = rotatedLabelBounds.getBounds2D(); double labelx = state.getCursor() - insets.getRight() - labelBounds.getWidth() / 2.0; double labely = labelLocationY(this.labelLocation, dataArea); TextAnchor anchor = labelAnchorV(this.labelLocation); AttrStringUtils.drawRotatedString(label, g2, (float) labelx, (float) labely, anchor, getLabelAngle() - Math.PI / 2.0, anchor); state.cursorLeft(insets.getLeft() + labelBounds.getWidth() + insets.getRight()); } else if (edge == RectangleEdge.RIGHT) { AffineTransform t = AffineTransform.getRotateInstance( getLabelAngle() + Math.PI / 2.0, labelBounds.getCenterX(), labelBounds.getCenterY()); Shape rotatedLabelBounds = t.createTransformedShape(labelBounds); labelBounds = rotatedLabelBounds.getBounds2D(); double labelx = state.getCursor() + insets.getLeft() + labelBounds.getWidth() / 2.0; double labely = labelLocationY(this.labelLocation, dataArea); TextAnchor anchor = labelAnchorV(this.labelLocation); AttrStringUtils.drawRotatedString(label, g2, (float) labelx, (float) labely, anchor, getLabelAngle() + Math.PI / 2.0, anchor); state.cursorRight(insets.getLeft() + labelBounds.getWidth() + insets.getRight()); } return state; } /** * Draws an axis line at the current cursor position and edge. * * @param g2 the graphics device. * @param cursor the cursor position. * @param dataArea the data area. * @param edge the edge. */ protected void drawAxisLine(Graphics2D g2, double cursor, Rectangle2D dataArea, RectangleEdge edge) { Line2D axisLine = null; double x = dataArea.getX(); double y = dataArea.getY(); if (edge == RectangleEdge.TOP) { axisLine = new Line2D.Double(x, cursor, dataArea.getMaxX(), cursor); } else if (edge == RectangleEdge.BOTTOM) { axisLine = new Line2D.Double(x, cursor, dataArea.getMaxX(), cursor); } else if (edge == RectangleEdge.LEFT) { axisLine = new Line2D.Double(cursor, y, cursor, dataArea.getMaxY()); } else if (edge == RectangleEdge.RIGHT) { axisLine = new Line2D.Double(cursor, y, cursor, dataArea.getMaxY()); } g2.setPaint(this.axisLinePaint); g2.setStroke(this.axisLineStroke); Object saved = g2.getRenderingHint(RenderingHints.KEY_STROKE_CONTROL); g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE); g2.draw(axisLine); g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, saved); } /** * Returns a clone of the axis. * * @return A clone. * * @throws CloneNotSupportedException if some component of the axis does * not support cloning. */ @Override public Object clone() throws CloneNotSupportedException { Axis clone = (Axis) super.clone(); // It's up to the plot which clones up to restore the correct references clone.plot = null; clone.listenerList = new EventListenerList(); return clone; } /** * Tests this axis for equality with another object. * * @param obj the object ({@code null} permitted). * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof Axis)) { return false; } Axis that = (Axis) obj; if (this.visible != that.visible) { return false; } if (!Objects.equals(this.label, that.label)) { return false; } if (!AttributedStringUtils.equal(this.attributedLabel, that.attributedLabel)) { return false; } if (!Objects.equals(this.labelFont, that.labelFont)) { return false; } if (!PaintUtils.equal(this.labelPaint, that.labelPaint)) { return false; } if (!Objects.equals(this.labelInsets, that.labelInsets)) { return false; } if (this.labelAngle != that.labelAngle) { return false; } if (!this.labelLocation.equals(that.labelLocation)) { return false; } if (this.axisLineVisible != that.axisLineVisible) { return false; } if (!Objects.equals(this.axisLineStroke, that.axisLineStroke)) { return false; } if (!PaintUtils.equal(this.axisLinePaint, that.axisLinePaint)) { return false; } if (this.tickLabelsVisible != that.tickLabelsVisible) { return false; } if (!Objects.equals(this.tickLabelFont, that.tickLabelFont)) { return false; } if (!PaintUtils.equal(this.tickLabelPaint, that.tickLabelPaint)) { return false; } if (!Objects.equals(this.tickLabelInsets, that.tickLabelInsets)) { return false; } if (this.tickMarksVisible != that.tickMarksVisible) { return false; } if (this.tickMarkInsideLength != that.tickMarkInsideLength) { return false; } if (this.tickMarkOutsideLength != that.tickMarkOutsideLength) { return false; } if (!PaintUtils.equal(this.tickMarkPaint, that.tickMarkPaint)) { return false; } if (!Objects.equals(this.tickMarkStroke, that.tickMarkStroke)) { return false; } if (this.minorTickMarksVisible != that.minorTickMarksVisible) { return false; } if (this.minorTickMarkInsideLength != that.minorTickMarkInsideLength) { return false; } if (this.minorTickMarkOutsideLength != that.minorTickMarkOutsideLength) { return false; } if (this.fixedDimension != that.fixedDimension) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int hash = 3; if (this.label != null) { hash = 83 * hash + this.label.hashCode(); } return hash; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeAttributedString(this.attributedLabel, stream); SerialUtils.writePaint(this.labelPaint, stream); SerialUtils.writePaint(this.tickLabelPaint, stream); SerialUtils.writeStroke(this.axisLineStroke, stream); SerialUtils.writePaint(this.axisLinePaint, stream); SerialUtils.writeStroke(this.tickMarkStroke, stream); SerialUtils.writePaint(this.tickMarkPaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.attributedLabel = SerialUtils.readAttributedString(stream); this.labelPaint = SerialUtils.readPaint(stream); this.tickLabelPaint = SerialUtils.readPaint(stream); this.axisLineStroke = SerialUtils.readStroke(stream); this.axisLinePaint = SerialUtils.readPaint(stream); this.tickMarkStroke = SerialUtils.readStroke(stream); this.tickMarkPaint = SerialUtils.readPaint(stream); this.listenerList = new EventListenerList(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/AxisCollection.java000066400000000000000000000103551463604235500276110ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * AxisCollection.java * ------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.util.List; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.Args; /** * A collection of axes that have been assigned to the TOP, BOTTOM, LEFT or * RIGHT of a chart. This class is used internally by JFreeChart, you won't * normally need to use it yourself. */ public class AxisCollection { /** The axes that need to be drawn at the top of the plot area. */ private final List axesAtTop; /** The axes that need to be drawn at the bottom of the plot area. */ private final List axesAtBottom; /** The axes that need to be drawn at the left of the plot area. */ private final List axesAtLeft; /** The axes that need to be drawn at the right of the plot area. */ private final List axesAtRight; /** * Creates a new empty collection. */ public AxisCollection() { this.axesAtTop = new java.util.ArrayList(); this.axesAtBottom = new java.util.ArrayList(); this.axesAtLeft = new java.util.ArrayList(); this.axesAtRight = new java.util.ArrayList(); } /** * Returns a list of the axes (if any) that need to be drawn at the top of * the plot area. * * @return A list of axes. */ public List getAxesAtTop() { return this.axesAtTop; } /** * Returns a list of the axes (if any) that need to be drawn at the bottom * of the plot area. * * @return A list of axes. */ public List getAxesAtBottom() { return this.axesAtBottom; } /** * Returns a list of the axes (if any) that need to be drawn at the left * of the plot area. * * @return A list of axes. */ public List getAxesAtLeft() { return this.axesAtLeft; } /** * Returns a list of the axes (if any) that need to be drawn at the right * of the plot area. * * @return A list of axes. */ public List getAxesAtRight() { return this.axesAtRight; } /** * Adds an axis to the collection. * * @param axis the axis ({@code null} not permitted). * @param edge the edge of the plot that the axis should be drawn on * ({@code null} not permitted). */ public void add(Axis axis, RectangleEdge edge) { Args.nullNotPermitted(axis, "axis"); Args.nullNotPermitted(edge, "edge"); if (edge == RectangleEdge.TOP) { this.axesAtTop.add(axis); } else if (edge == RectangleEdge.BOTTOM) { this.axesAtBottom.add(axis); } else if (edge == RectangleEdge.LEFT) { this.axesAtLeft.add(axis); } else if (edge == RectangleEdge.RIGHT) { this.axesAtRight.add(axis); } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/AxisLabelLocation.java000066400000000000000000000076431463604235500302340ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * AxisLabelLocation.java * ---------------------- * (C) Copyright 2013-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.io.ObjectStreamException; import java.io.Serializable; /** * Used to indicate the location of an axis label. */ public final class AxisLabelLocation implements Serializable { /** For serialization. */ private static final long serialVersionUID = 1L; /** Axis label at the top. */ public static final AxisLabelLocation HIGH_END = new AxisLabelLocation( "HIGH_END"); /** Axis label at the middle. */ public static final AxisLabelLocation MIDDLE = new AxisLabelLocation( "MIDDLE"); /** Axis label at the bottom. */ public static final AxisLabelLocation LOW_END = new AxisLabelLocation( "LOW_END"); /** The name. */ private final String name; /** * Private constructor. * * @param name the name. */ private AxisLabelLocation(String name) { this.name = name; } /** * Returns a string representing the object. * * @return The string. */ @Override public String toString() { return this.name; } /** * Returns {@code true} if this object is equal to the specified * object, and {@code false} otherwise. * * @param obj the other object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof AxisLabelLocation)) { return false; } AxisLabelLocation location = (AxisLabelLocation) obj; if (!this.name.equals(location.toString())) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int hash = 5; hash = 83 * hash + this.name.hashCode(); return hash; } /** * Ensures that serialization returns the unique instances. * * @return The object. * * @throws ObjectStreamException if there is a problem. */ private Object readResolve() throws ObjectStreamException { if (this.equals(AxisLabelLocation.HIGH_END)) { return AxisLabelLocation.HIGH_END; } if (this.equals(AxisLabelLocation.MIDDLE)) { return AxisLabelLocation.MIDDLE; } if (this.equals(AxisLabelLocation.LOW_END)) { return AxisLabelLocation.LOW_END; } return null; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/AxisLocation.java000066400000000000000000000132031463604235500272610ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * AxisLocation.java * ----------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Nick Guenther; * */ package org.jfree.chart.axis; import java.io.ObjectStreamException; import java.io.Serializable; import org.jfree.chart.util.Args; /** * Used to indicate the location of an axis on a 2D plot, prior to knowing the * orientation of the plot. */ public final class AxisLocation implements Serializable { /** For serialization. */ private static final long serialVersionUID = -3276922179323563410L; /** Axis at the top or left. */ public static final AxisLocation TOP_OR_LEFT = new AxisLocation( "AxisLocation.TOP_OR_LEFT"); /** Axis at the top or right. */ public static final AxisLocation TOP_OR_RIGHT = new AxisLocation( "AxisLocation.TOP_OR_RIGHT"); /** Axis at the bottom or left. */ public static final AxisLocation BOTTOM_OR_LEFT = new AxisLocation( "AxisLocation.BOTTOM_OR_LEFT"); /** Axis at the bottom or right. */ public static final AxisLocation BOTTOM_OR_RIGHT = new AxisLocation( "AxisLocation.BOTTOM_OR_RIGHT"); /** The name. */ private final String name; /** * Private constructor. * * @param name the name. */ private AxisLocation(String name) { this.name = name; } /** * Returns the location that is opposite to this location. * * @return The opposite location. */ public AxisLocation getOpposite() { return getOpposite(this); } /** * Returns a string representing the object. * * @return The string. */ @Override public String toString() { return this.name; } /** * Returns {@code true} if this object is equal to the specified * object, and {@code false} otherwise. * * @param obj the other object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof AxisLocation)) { return false; } AxisLocation location = (AxisLocation) obj; if (!this.name.equals(location.toString())) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int hash = 5; hash = 83 * hash + this.name.hashCode(); return hash; } /** * Returns the location that is opposite to the supplied location. * * @param location the location ({@code null} not permitted). * * @return The opposite location. */ public static AxisLocation getOpposite(AxisLocation location) { Args.nullNotPermitted(location, "location"); AxisLocation result = null; if (location == AxisLocation.TOP_OR_LEFT) { result = AxisLocation.BOTTOM_OR_RIGHT; } else if (location == AxisLocation.TOP_OR_RIGHT) { result = AxisLocation.BOTTOM_OR_LEFT; } else if (location == AxisLocation.BOTTOM_OR_LEFT) { result = AxisLocation.TOP_OR_RIGHT; } else if (location == AxisLocation.BOTTOM_OR_RIGHT) { result = AxisLocation.TOP_OR_LEFT; } else { throw new IllegalStateException("AxisLocation not recognised."); } return result; } /** * Ensures that serialization returns the unique instances. * * @return The object. * * @throws ObjectStreamException if there is a problem. */ private Object readResolve() throws ObjectStreamException { if (this.equals(AxisLocation.TOP_OR_RIGHT)) { return AxisLocation.TOP_OR_RIGHT; } else if (this.equals(AxisLocation.BOTTOM_OR_RIGHT)) { return AxisLocation.BOTTOM_OR_RIGHT; } else if (this.equals(AxisLocation.TOP_OR_LEFT)) { return AxisLocation.TOP_OR_LEFT; } else if (this.equals(AxisLocation.BOTTOM_OR_LEFT)) { return AxisLocation.BOTTOM_OR_LEFT; } return null; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/AxisSpace.java000066400000000000000000000257261463604235500265610ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * AxisSpace.java * -------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (equals method complies with EqualsVerifier); * */ package org.jfree.chart.axis; import java.awt.geom.Rectangle2D; import java.io.Serializable; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; /** * A record that contains the space required at each edge of a plot. */ public class AxisSpace implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -2490732595134766305L; /** The top space. */ private double top; /** The bottom space. */ private double bottom; /** The left space. */ private double left; /** The right space. */ private double right; /** * Creates a new axis space record. */ public AxisSpace() { this.top = 0.0; this.bottom = 0.0; this.left = 0.0; this.right = 0.0; } /** * Returns the space reserved for axes at the top of the plot area. * * @return The space (in Java2D units). */ public double getTop() { return this.top; } /** * Sets the space reserved for axes at the top of the plot area. * * @param space the space (in Java2D units). */ public void setTop(double space) { this.top = space; } /** * Returns the space reserved for axes at the bottom of the plot area. * * @return The space (in Java2D units). */ public double getBottom() { return this.bottom; } /** * Sets the space reserved for axes at the bottom of the plot area. * * @param space the space (in Java2D units). */ public void setBottom(double space) { this.bottom = space; } /** * Returns the space reserved for axes at the left of the plot area. * * @return The space (in Java2D units). */ public double getLeft() { return this.left; } /** * Sets the space reserved for axes at the left of the plot area. * * @param space the space (in Java2D units). */ public void setLeft(double space) { this.left = space; } /** * Returns the space reserved for axes at the right of the plot area. * * @return The space (in Java2D units). */ public double getRight() { return this.right; } /** * Sets the space reserved for axes at the right of the plot area. * * @param space the space (in Java2D units). */ public void setRight(double space) { this.right = space; } /** * Adds space to the top, bottom, left or right edge of the plot area. * * @param space the space (in Java2D units). * @param edge the edge ({@code null} not permitted). */ public void add(double space, RectangleEdge edge) { Args.nullNotPermitted(edge, "edge"); if (edge == RectangleEdge.TOP) { this.top += space; } else if (edge == RectangleEdge.BOTTOM) { this.bottom += space; } else if (edge == RectangleEdge.LEFT) { this.left += space; } else if (edge == RectangleEdge.RIGHT) { this.right += space; } else { throw new IllegalStateException("Unrecognised 'edge' argument."); } } /** * Ensures that this object reserves at least as much space as another. * * @param space the other space. */ public void ensureAtLeast(AxisSpace space) { this.top = Math.max(this.top, space.top); this.bottom = Math.max(this.bottom, space.bottom); this.left = Math.max(this.left, space.left); this.right = Math.max(this.right, space.right); } /** * Ensures there is a minimum amount of space at the edge corresponding to * the specified axis location. * * @param space the space. * @param edge the location. */ public void ensureAtLeast(double space, RectangleEdge edge) { if (edge == RectangleEdge.TOP) { if (this.top < space) { this.top = space; } } else if (edge == RectangleEdge.BOTTOM) { if (this.bottom < space) { this.bottom = space; } } else if (edge == RectangleEdge.LEFT) { if (this.left < space) { this.left = space; } } else if (edge == RectangleEdge.RIGHT) { if (this.right < space) { this.right = space; } } else { throw new IllegalStateException( "AxisSpace.ensureAtLeast(): unrecognised AxisLocation." ); } } /** * Shrinks an area by the space attributes. * * @param area the area to shrink. * @param result an optional carrier for the result. * * @return The result. */ public Rectangle2D shrink(Rectangle2D area, Rectangle2D result) { if (result == null) { result = new Rectangle2D.Double(); } result.setRect( area.getX() + this.left, area.getY() + this.top, area.getWidth() - this.left - this.right, area.getHeight() - this.top - this.bottom ); return result; } /** * Expands an area by the amount of space represented by this object. * * @param area the area to expand. * @param result an optional carrier for the result. * * @return The result. */ public Rectangle2D expand(Rectangle2D area, Rectangle2D result) { if (result == null) { result = new Rectangle2D.Double(); } result.setRect( area.getX() - this.left, area.getY() - this.top, area.getWidth() + this.left + this.right, area.getHeight() + this.top + this.bottom ); return result; } /** * Calculates the reserved area. * * @param area the area. * @param edge the edge. * * @return The reserved area. */ public Rectangle2D reserved(Rectangle2D area, RectangleEdge edge) { Rectangle2D result = null; if (edge == RectangleEdge.TOP) { result = new Rectangle2D.Double( area.getX(), area.getY(), area.getWidth(), this.top ); } else if (edge == RectangleEdge.BOTTOM) { result = new Rectangle2D.Double( area.getX(), area.getMaxY() - this.top, area.getWidth(), this.bottom ); } else if (edge == RectangleEdge.LEFT) { result = new Rectangle2D.Double( area.getX(), area.getY(), this.left, area.getHeight() ); } else if (edge == RectangleEdge.RIGHT) { result = new Rectangle2D.Double( area.getMaxX() - this.right, area.getY(), this.right, area.getHeight() ); } return result; } /** * Returns a clone of the object. * * @return A clone. * * @throws CloneNotSupportedException This class won't throw this exception, * but subclasses (if any) might. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Tests this object for equality with another object. * * @param obj the object to compare against. * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof AxisSpace)) { return false; } AxisSpace that = (AxisSpace) obj; if (Double.doubleToLongBits(this.top) != Double.doubleToLongBits(that.top)) { return false; } if (Double.doubleToLongBits(this.bottom) != Double.doubleToLongBits(that.bottom)) { return false; } if (Double.doubleToLongBits(this.left) != Double.doubleToLongBits(that.left)) { return false; } if (Double.doubleToLongBits(this.right) != Double.doubleToLongBits(that.right)) { return false; } return true; } /** * Returns a hash code for this object. * * @return A hash code. */ @Override public int hashCode() { int result = 23; long l = Double.doubleToLongBits(this.top); result = 37 * result + (int) (l ^ (l >>> 32)); l = Double.doubleToLongBits(this.bottom); result = 37 * result + (int) (l ^ (l >>> 32)); l = Double.doubleToLongBits(this.left); result = 37 * result + (int) (l ^ (l >>> 32)); l = Double.doubleToLongBits(this.right); result = 37 * result + (int) (l ^ (l >>> 32)); return result; } /** * Returns a string representing the object (for debugging purposes). * * @return A string. */ @Override public String toString() { return super.toString() + "[left=" + this.left + ",right=" + this.right + ",top=" + this.top + ",bottom=" + this.bottom + "]"; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/AxisState.java000066400000000000000000000115421463604235500265750ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * AxisState.java * -------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.util.List; import org.jfree.chart.ui.RectangleEdge; /** * Instances of this class are used to carry state information for an axis * during the drawing process. By retaining this information in a separate * object, it is possible for multiple threads to draw the same axis to * different output targets (each drawing will maintain separate state * information). */ public class AxisState { /** The cursor position. */ private double cursor; /** The axis ticks. */ private List ticks; /** The maximum width/height. */ private double max; /** * Creates a new axis state. */ public AxisState() { this(0.0); } /** * Creates a new axis state. * * @param cursor the cursor. */ public AxisState(double cursor) { this.cursor = cursor; this.ticks = new java.util.ArrayList(); } /** * Returns the cursor position. * * @return The cursor position. */ public double getCursor() { return this.cursor; } /** * Sets the cursor position. * * @param cursor the cursor position. */ public void setCursor(double cursor) { this.cursor = cursor; } /** * Moves the cursor outwards by the specified number of units. * * @param units the units. * @param edge the edge. */ public void moveCursor(double units, RectangleEdge edge) { if (edge == RectangleEdge.TOP) { cursorUp(units); } else if (edge == RectangleEdge.BOTTOM) { cursorDown(units); } else if (edge == RectangleEdge.LEFT) { cursorLeft(units); } else if (edge == RectangleEdge.RIGHT) { cursorRight(units); } } /** * Moves the cursor up by the specified number of Java 2D units. * * @param units the units. */ public void cursorUp(double units) { this.cursor = this.cursor - units; } /** * Moves the cursor down by the specified number of Java 2D units. * * @param units the units. */ public void cursorDown(double units) { this.cursor = this.cursor + units; } /** * Moves the cursor left by the specified number of Java 2D units. * * @param units the units. */ public void cursorLeft(double units) { this.cursor = this.cursor - units; } /** * Moves the cursor right by the specified number of Java 2D units. * * @param units the units. */ public void cursorRight(double units) { this.cursor = this.cursor + units; } /** * Returns the list of ticks. * * @return The list of ticks. */ public List getTicks() { return this.ticks; } /** * Sets the list of ticks. * * @param ticks the ticks. */ public void setTicks(List ticks) { this.ticks = ticks; } /** * Returns the maximum width/height. * * @return The maximum width/height. */ public double getMax() { return this.max; } /** * Sets the maximum width/height. * * @param max the maximum width/height. */ public void setMax(double max) { this.max = max; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/CategoryAnchor.java000066400000000000000000000074071463604235500276050ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * CategoryAnchor.java * ------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.io.ObjectStreamException; import java.io.Serializable; /** * Used to indicate one of three positions within a category: * {@code START}, {@code MIDDLE} and {@code END}. */ public final class CategoryAnchor implements Serializable { /** For serialization. */ private static final long serialVersionUID = -2604142742210173810L; /** Start of period. */ public static final CategoryAnchor START = new CategoryAnchor("CategoryAnchor.START"); /** Middle of period. */ public static final CategoryAnchor MIDDLE = new CategoryAnchor("CategoryAnchor.MIDDLE"); /** End of period. */ public static final CategoryAnchor END = new CategoryAnchor("CategoryAnchor.END"); /** The name. */ private final String name; /** * Private constructor. * * @param name the name. */ private CategoryAnchor(String name) { this.name = name; } /** * Returns a string representing the object. * * @return The string. */ @Override public String toString() { return this.name; } /** * Returns {@code true} if this object is equal to the specified * object, and {@code false} otherwise. * * @param obj the other object. * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof CategoryAnchor)) { return false; } CategoryAnchor position = (CategoryAnchor) obj; if (!this.name.equals(position.toString())) { return false; } return true; } /** * Ensures that serialization returns the unique instances. * * @return The object. * * @throws ObjectStreamException if there is a problem. */ private Object readResolve() throws ObjectStreamException { if (this.equals(CategoryAnchor.START)) { return CategoryAnchor.START; } else if (this.equals(CategoryAnchor.MIDDLE)) { return CategoryAnchor.MIDDLE; } else if (this.equals(CategoryAnchor.END)) { return CategoryAnchor.END; } return null; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/CategoryAxis.java000066400000000000000000001455251463604235500273030ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * CategoryAxis.java * ----------------- * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Pady Srinivasan (patch 1217634); * Peter Kolb (patches 2497611 and 2603321); * */ package org.jfree.chart.axis; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import org.jfree.chart.entity.CategoryLabelEntity; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.AxisChangeEvent; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.text.G2TextMeasurer; import org.jfree.chart.text.TextBlock; import org.jfree.chart.text.TextUtils; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.ui.Size2D; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.SerialUtils; import org.jfree.chart.util.ShapeUtils; import org.jfree.data.category.CategoryDataset; /** * An axis that displays categories. */ public class CategoryAxis extends Axis implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 5886554608114265863L; /** * The default margin for the axis (used for both lower and upper margins). */ public static final double DEFAULT_AXIS_MARGIN = 0.05; /** * The default margin between categories (a percentage of the overall axis * length). */ public static final double DEFAULT_CATEGORY_MARGIN = 0.20; /** The amount of space reserved at the start of the axis. */ private double lowerMargin; /** The amount of space reserved at the end of the axis. */ private double upperMargin; /** The amount of space reserved between categories. */ private double categoryMargin; /** The maximum number of lines for category labels. */ private int maximumCategoryLabelLines; /** * A ratio that is multiplied by the width of one category to determine the * maximum label width. */ private float maximumCategoryLabelWidthRatio; /** The category label offset. */ private int categoryLabelPositionOffset; /** * A structure defining the category label positions for each axis * location. */ private CategoryLabelPositions categoryLabelPositions; /** Storage for tick label font overrides (if any). */ private Map tickLabelFontMap; /** Storage for tick label paint overrides (if any). */ private transient Map tickLabelPaintMap; /** Storage for the category label tooltips (if any). */ private Map categoryLabelToolTips; /** Storage for the category label URLs (if any). */ private Map categoryLabelURLs; /** * Creates a new category axis with no label. */ public CategoryAxis() { this(null); } /** * Constructs a category axis, using default values where necessary. * * @param label the axis label ({@code null} permitted). */ public CategoryAxis(String label) { super(label); this.lowerMargin = DEFAULT_AXIS_MARGIN; this.upperMargin = DEFAULT_AXIS_MARGIN; this.categoryMargin = DEFAULT_CATEGORY_MARGIN; this.maximumCategoryLabelLines = 1; this.maximumCategoryLabelWidthRatio = 0.0f; this.categoryLabelPositionOffset = 4; this.categoryLabelPositions = CategoryLabelPositions.STANDARD; this.tickLabelFontMap = new HashMap(); this.tickLabelPaintMap = new HashMap(); this.categoryLabelToolTips = new HashMap(); this.categoryLabelURLs = new HashMap(); } /** * Returns the lower margin for the axis. * * @return The margin. * * @see #getUpperMargin() * @see #setLowerMargin(double) */ public double getLowerMargin() { return this.lowerMargin; } /** * Sets the lower margin for the axis and sends an {@link AxisChangeEvent} * to all registered listeners. * * @param margin the margin as a percentage of the axis length (for * example, 0.05 is five percent). * * @see #getLowerMargin() */ public void setLowerMargin(double margin) { this.lowerMargin = margin; fireChangeEvent(); } /** * Returns the upper margin for the axis. * * @return The margin. * * @see #getLowerMargin() * @see #setUpperMargin(double) */ public double getUpperMargin() { return this.upperMargin; } /** * Sets the upper margin for the axis and sends an {@link AxisChangeEvent} * to all registered listeners. * * @param margin the margin as a percentage of the axis length (for * example, 0.05 is five percent). * * @see #getUpperMargin() */ public void setUpperMargin(double margin) { this.upperMargin = margin; fireChangeEvent(); } /** * Returns the category margin. * * @return The margin. * * @see #setCategoryMargin(double) */ public double getCategoryMargin() { return this.categoryMargin; } /** * Sets the category margin and sends an {@link AxisChangeEvent} to all * registered listeners. The overall category margin is distributed over * N-1 gaps, where N is the number of categories on the axis. * * @param margin the margin as a percentage of the axis length (for * example, 0.05 is five percent). * * @see #getCategoryMargin() */ public void setCategoryMargin(double margin) { this.categoryMargin = margin; fireChangeEvent(); } /** * Returns the maximum number of lines to use for each category label. * * @return The maximum number of lines. * * @see #setMaximumCategoryLabelLines(int) */ public int getMaximumCategoryLabelLines() { return this.maximumCategoryLabelLines; } /** * Sets the maximum number of lines to use for each category label and * sends an {@link AxisChangeEvent} to all registered listeners. * * @param lines the maximum number of lines. * * @see #getMaximumCategoryLabelLines() */ public void setMaximumCategoryLabelLines(int lines) { this.maximumCategoryLabelLines = lines; fireChangeEvent(); } /** * Returns the category label width ratio. * * @return The ratio. * * @see #setMaximumCategoryLabelWidthRatio(float) */ public float getMaximumCategoryLabelWidthRatio() { return this.maximumCategoryLabelWidthRatio; } /** * Sets the maximum category label width ratio and sends an * {@link AxisChangeEvent} to all registered listeners. * * @param ratio the ratio. * * @see #getMaximumCategoryLabelWidthRatio() */ public void setMaximumCategoryLabelWidthRatio(float ratio) { this.maximumCategoryLabelWidthRatio = ratio; fireChangeEvent(); } /** * Returns the offset between the axis and the category labels (before * label positioning is taken into account). * * @return The offset (in Java2D units). * * @see #setCategoryLabelPositionOffset(int) */ public int getCategoryLabelPositionOffset() { return this.categoryLabelPositionOffset; } /** * Sets the offset between the axis and the category labels (before label * positioning is taken into account) and sends a change event to all * registered listeners. * * @param offset the offset (in Java2D units). * * @see #getCategoryLabelPositionOffset() */ public void setCategoryLabelPositionOffset(int offset) { this.categoryLabelPositionOffset = offset; fireChangeEvent(); } /** * Returns the category label position specification (this contains label * positioning info for all four possible axis locations). * * @return The positions (never {@code null}). * * @see #setCategoryLabelPositions(CategoryLabelPositions) */ public CategoryLabelPositions getCategoryLabelPositions() { return this.categoryLabelPositions; } /** * Sets the category label position specification for the axis and sends an * {@link AxisChangeEvent} to all registered listeners. * * @param positions the positions ({@code null} not permitted). * * @see #getCategoryLabelPositions() */ public void setCategoryLabelPositions(CategoryLabelPositions positions) { Args.nullNotPermitted(positions, "positions"); this.categoryLabelPositions = positions; fireChangeEvent(); } /** * Returns the font for the tick label for the given category. * * @param category the category ({@code null} not permitted). * * @return The font (never {@code null}). * * @see #setTickLabelFont(Comparable, Font) */ public Font getTickLabelFont(Comparable category) { Args.nullNotPermitted(category, "category"); Font result = (Font) this.tickLabelFontMap.get(category); // if there is no specific font, use the general one... if (result == null) { result = getTickLabelFont(); } return result; } /** * Sets the font for the tick label for the specified category and sends * an {@link AxisChangeEvent} to all registered listeners. * * @param category the category ({@code null} not permitted). * @param font the font ({@code null} permitted). * * @see #getTickLabelFont(Comparable) */ public void setTickLabelFont(Comparable category, Font font) { Args.nullNotPermitted(category, "category"); if (font == null) { this.tickLabelFontMap.remove(category); } else { this.tickLabelFontMap.put(category, font); } fireChangeEvent(); } /** * Returns the paint for the tick label for the given category. * * @param category the category ({@code null} not permitted). * * @return The paint (never {@code null}). * * @see #setTickLabelPaint(Paint) */ public Paint getTickLabelPaint(Comparable category) { Args.nullNotPermitted(category, "category"); Paint result = (Paint) this.tickLabelPaintMap.get(category); // if there is no specific paint, use the general one... if (result == null) { result = getTickLabelPaint(); } return result; } /** * Sets the paint for the tick label for the specified category and sends * an {@link AxisChangeEvent} to all registered listeners. * * @param category the category ({@code null} not permitted). * @param paint the paint ({@code null} permitted). * * @see #getTickLabelPaint(Comparable) */ public void setTickLabelPaint(Comparable category, Paint paint) { Args.nullNotPermitted(category, "category"); if (paint == null) { this.tickLabelPaintMap.remove(category); } else { this.tickLabelPaintMap.put(category, paint); } fireChangeEvent(); } /** * Adds a tooltip to the specified category and sends an * {@link AxisChangeEvent} to all registered listeners. * * @param category the category ({@code null} not permitted). * @param tooltip the tooltip text ({@code null} permitted). * * @see #removeCategoryLabelToolTip(Comparable) */ public void addCategoryLabelToolTip(Comparable category, String tooltip) { Args.nullNotPermitted(category, "category"); this.categoryLabelToolTips.put(category, tooltip); fireChangeEvent(); } /** * Returns the tool tip text for the label belonging to the specified * category. * * @param category the category ({@code null} not permitted). * * @return The tool tip text (possibly {@code null}). * * @see #addCategoryLabelToolTip(Comparable, String) * @see #removeCategoryLabelToolTip(Comparable) */ public String getCategoryLabelToolTip(Comparable category) { Args.nullNotPermitted(category, "category"); return (String) this.categoryLabelToolTips.get(category); } /** * Removes the tooltip for the specified category and, if there was a value * associated with that category, sends an {@link AxisChangeEvent} to all * registered listeners. * * @param category the category ({@code null} not permitted). * * @see #addCategoryLabelToolTip(Comparable, String) * @see #clearCategoryLabelToolTips() */ public void removeCategoryLabelToolTip(Comparable category) { Args.nullNotPermitted(category, "category"); if (this.categoryLabelToolTips.remove(category) != null) { fireChangeEvent(); } } /** * Clears the category label tooltips and sends an {@link AxisChangeEvent} * to all registered listeners. * * @see #addCategoryLabelToolTip(Comparable, String) * @see #removeCategoryLabelToolTip(Comparable) */ public void clearCategoryLabelToolTips() { this.categoryLabelToolTips.clear(); fireChangeEvent(); } /** * Adds a URL (to be used in image maps) to the specified category and * sends an {@link AxisChangeEvent} to all registered listeners. * * @param category the category ({@code null} not permitted). * @param url the URL text ({@code null} permitted). * * @see #removeCategoryLabelURL(Comparable) */ public void addCategoryLabelURL(Comparable category, String url) { Args.nullNotPermitted(category, "category"); this.categoryLabelURLs.put(category, url); fireChangeEvent(); } /** * Returns the URL for the label belonging to the specified category. * * @param category the category ({@code null} not permitted). * * @return The URL text (possibly {@code null}). * * @see #addCategoryLabelURL(Comparable, String) * @see #removeCategoryLabelURL(Comparable) */ public String getCategoryLabelURL(Comparable category) { Args.nullNotPermitted(category, "category"); return (String) this.categoryLabelURLs.get(category); } /** * Removes the URL for the specified category and, if there was a URL * associated with that category, sends an {@link AxisChangeEvent} to all * registered listeners. * * @param category the category ({@code null} not permitted). * * @see #addCategoryLabelURL(Comparable, String) * @see #clearCategoryLabelURLs() */ public void removeCategoryLabelURL(Comparable category) { Args.nullNotPermitted(category, "category"); if (this.categoryLabelURLs.remove(category) != null) { fireChangeEvent(); } } /** * Clears the category label URLs and sends an {@link AxisChangeEvent} * to all registered listeners. * * @see #addCategoryLabelURL(Comparable, String) * @see #removeCategoryLabelURL(Comparable) */ public void clearCategoryLabelURLs() { this.categoryLabelURLs.clear(); fireChangeEvent(); } /** * Returns the Java 2D coordinate for a category. * * @param anchor the anchor point. * @param category the category index. * @param categoryCount the category count. * @param area the data area. * @param edge the location of the axis. * * @return The coordinate. */ public double getCategoryJava2DCoordinate(CategoryAnchor anchor, int category, int categoryCount, Rectangle2D area, RectangleEdge edge) { double result = 0.0; if (anchor == CategoryAnchor.START) { result = getCategoryStart(category, categoryCount, area, edge); } else if (anchor == CategoryAnchor.MIDDLE) { result = getCategoryMiddle(category, categoryCount, area, edge); } else if (anchor == CategoryAnchor.END) { result = getCategoryEnd(category, categoryCount, area, edge); } return result; } /** * Returns the starting coordinate for the specified category. * * @param category the category. * @param categoryCount the number of categories. * @param area the data area. * @param edge the axis location. * * @return The coordinate. * * @see #getCategoryMiddle(int, int, Rectangle2D, RectangleEdge) * @see #getCategoryEnd(int, int, Rectangle2D, RectangleEdge) */ public double getCategoryStart(int category, int categoryCount, Rectangle2D area, RectangleEdge edge) { double result = 0.0; if ((edge == RectangleEdge.TOP) || (edge == RectangleEdge.BOTTOM)) { result = area.getX() + area.getWidth() * getLowerMargin(); } else if ((edge == RectangleEdge.LEFT) || (edge == RectangleEdge.RIGHT)) { result = area.getMinY() + area.getHeight() * getLowerMargin(); } double categorySize = calculateCategorySize(categoryCount, area, edge); double categoryGapWidth = calculateCategoryGapSize(categoryCount, area, edge); result = result + category * (categorySize + categoryGapWidth); return result; } /** * Returns the middle coordinate for the specified category. * * @param category the category. * @param categoryCount the number of categories. * @param area the data area. * @param edge the axis location. * * @return The coordinate. * * @see #getCategoryStart(int, int, Rectangle2D, RectangleEdge) * @see #getCategoryEnd(int, int, Rectangle2D, RectangleEdge) */ public double getCategoryMiddle(int category, int categoryCount, Rectangle2D area, RectangleEdge edge) { if (category < 0 || category >= categoryCount) { throw new IllegalArgumentException("Invalid category index: " + category); } return getCategoryStart(category, categoryCount, area, edge) + calculateCategorySize(categoryCount, area, edge) / 2; } /** * Returns the end coordinate for the specified category. * * @param category the category. * @param categoryCount the number of categories. * @param area the data area. * @param edge the axis location. * * @return The coordinate. * * @see #getCategoryStart(int, int, Rectangle2D, RectangleEdge) * @see #getCategoryMiddle(int, int, Rectangle2D, RectangleEdge) */ public double getCategoryEnd(int category, int categoryCount, Rectangle2D area, RectangleEdge edge) { return getCategoryStart(category, categoryCount, area, edge) + calculateCategorySize(categoryCount, area, edge); } /** * A convenience method that returns the axis coordinate for the centre of * a category. * * @param category the category key ({@code null} not permitted). * @param categories the categories ({@code null} not permitted). * @param area the data area ({@code null} not permitted). * @param edge the edge along which the axis lies ({@code null} not * permitted). * * @return The centre coordinate. * * @see #getCategorySeriesMiddle(Comparable, Comparable, CategoryDataset, * double, Rectangle2D, RectangleEdge) */ public double getCategoryMiddle(Comparable category, List categories, Rectangle2D area, RectangleEdge edge) { Args.nullNotPermitted(categories, "categories"); int categoryIndex = categories.indexOf(category); int categoryCount = categories.size(); return getCategoryMiddle(categoryIndex, categoryCount, area, edge); } /** * Returns the middle coordinate (in Java2D space) for a series within a * category. * * @param category the category ({@code null} not permitted). * @param seriesKey the series key ({@code null} not permitted). * @param dataset the dataset ({@code null} not permitted). * @param itemMargin the item margin (0.0 <= itemMargin < 1.0); * @param area the area ({@code null} not permitted). * @param edge the edge ({@code null} not permitted). * * @return The coordinate in Java2D space. */ public double getCategorySeriesMiddle(Comparable category, Comparable seriesKey, CategoryDataset dataset, double itemMargin, Rectangle2D area, RectangleEdge edge) { int categoryIndex = dataset.getColumnIndex(category); int categoryCount = dataset.getColumnCount(); int seriesIndex = dataset.getRowIndex(seriesKey); int seriesCount = dataset.getRowCount(); double start = getCategoryStart(categoryIndex, categoryCount, area, edge); double end = getCategoryEnd(categoryIndex, categoryCount, area, edge); double width = end - start; if (seriesCount == 1) { return start + width / 2.0; } else { double gap = (width * itemMargin) / (seriesCount - 1); double ww = (width * (1 - itemMargin)) / seriesCount; return start + (seriesIndex * (ww + gap)) + ww / 2.0; } } /** * Returns the middle coordinate (in Java2D space) for a series within a * category. * * @param categoryIndex the category index. * @param categoryCount the category count. * @param seriesIndex the series index. * @param seriesCount the series count. * @param itemMargin the item margin (0.0 <= itemMargin < 1.0); * @param area the area ({@code null} not permitted). * @param edge the edge ({@code null} not permitted). * * @return The coordinate in Java2D space. */ public double getCategorySeriesMiddle(int categoryIndex, int categoryCount, int seriesIndex, int seriesCount, double itemMargin, Rectangle2D area, RectangleEdge edge) { double start = getCategoryStart(categoryIndex, categoryCount, area, edge); double end = getCategoryEnd(categoryIndex, categoryCount, area, edge); double width = end - start; if (seriesCount == 1) { return start + width / 2.0; } else { double gap = (width * itemMargin) / (seriesCount - 1); double ww = (width * (1 - itemMargin)) / seriesCount; return start + (seriesIndex * (ww + gap)) + ww / 2.0; } } /** * Calculates the size (width or height, depending on the location of the * axis) of a category. * * @param categoryCount the number of categories. * @param area the area within which the categories will be drawn. * @param edge the axis location. * * @return The category size. */ protected double calculateCategorySize(int categoryCount, Rectangle2D area, RectangleEdge edge) { double result; double available = 0.0; if ((edge == RectangleEdge.TOP) || (edge == RectangleEdge.BOTTOM)) { available = area.getWidth(); } else if ((edge == RectangleEdge.LEFT) || (edge == RectangleEdge.RIGHT)) { available = area.getHeight(); } if (categoryCount > 1) { result = available * (1 - getLowerMargin() - getUpperMargin() - getCategoryMargin()); result = result / categoryCount; } else { result = available * (1 - getLowerMargin() - getUpperMargin()); } return result; } /** * Calculates the size (width or height, depending on the location of the * axis) of a category gap. * * @param categoryCount the number of categories. * @param area the area within which the categories will be drawn. * @param edge the axis location. * * @return The category gap width. */ protected double calculateCategoryGapSize(int categoryCount, Rectangle2D area, RectangleEdge edge) { double result = 0.0; double available = 0.0; if ((edge == RectangleEdge.TOP) || (edge == RectangleEdge.BOTTOM)) { available = area.getWidth(); } else if ((edge == RectangleEdge.LEFT) || (edge == RectangleEdge.RIGHT)) { available = area.getHeight(); } if (categoryCount > 1) { result = available * getCategoryMargin() / (categoryCount - 1); } return result; } /** * Estimates the space required for the axis, given a specific drawing area. * * @param g2 the graphics device (used to obtain font information). * @param plot the plot that the axis belongs to. * @param plotArea the area within which the axis should be drawn. * @param edge the axis location (top or bottom). * @param space the space already reserved. * * @return The space required to draw the axis. */ @Override public AxisSpace reserveSpace(Graphics2D g2, Plot plot, Rectangle2D plotArea, RectangleEdge edge, AxisSpace space) { // create a new space object if one wasn't supplied... if (space == null) { space = new AxisSpace(); } // if the axis is not visible, no additional space is required... if (!isVisible()) { return space; } // calculate the max size of the tick labels (if visible)... double tickLabelHeight = 0.0; double tickLabelWidth = 0.0; if (isTickLabelsVisible()) { g2.setFont(getTickLabelFont()); AxisState state = new AxisState(); // we call refresh ticks just to get the maximum width or height refreshTicks(g2, state, plotArea, edge); if (edge == RectangleEdge.TOP) { tickLabelHeight = state.getMax(); } else if (edge == RectangleEdge.BOTTOM) { tickLabelHeight = state.getMax(); } else if (edge == RectangleEdge.LEFT) { tickLabelWidth = state.getMax(); } else if (edge == RectangleEdge.RIGHT) { tickLabelWidth = state.getMax(); } } // get the axis label size and update the space object... Rectangle2D labelEnclosure = getLabelEnclosure(g2, edge); double labelHeight, labelWidth; if (RectangleEdge.isTopOrBottom(edge)) { labelHeight = labelEnclosure.getHeight(); space.add(labelHeight + tickLabelHeight + this.categoryLabelPositionOffset, edge); } else if (RectangleEdge.isLeftOrRight(edge)) { labelWidth = labelEnclosure.getWidth(); space.add(labelWidth + tickLabelWidth + this.categoryLabelPositionOffset, edge); } return space; } /** * Configures the axis against the current plot. */ @Override public void configure() { // nothing required } /** * Draws the axis on a Java 2D graphics device (such as the screen or a * printer). * * @param g2 the graphics device ({@code null} not permitted). * @param cursor the cursor location. * @param plotArea the area within which the axis should be drawn * ({@code null} not permitted). * @param dataArea the area within which the plot is being drawn * ({@code null} not permitted). * @param edge the location of the axis ({@code null} not permitted). * @param plotState collects information about the plot * ({@code null} permitted). * * @return The axis state (never {@code null}). */ @Override public AxisState draw(Graphics2D g2, double cursor, Rectangle2D plotArea, Rectangle2D dataArea, RectangleEdge edge, PlotRenderingInfo plotState) { // if the axis is not visible, don't draw it... if (!isVisible()) { return new AxisState(cursor); } if (isAxisLineVisible()) { drawAxisLine(g2, cursor, dataArea, edge); } AxisState state = new AxisState(cursor); if (isTickMarksVisible()) { drawTickMarks(g2, cursor, dataArea, edge, state); } createAndAddEntity(cursor, state, dataArea, edge, plotState); // draw the category labels and axis label state = drawCategoryLabels(g2, plotArea, dataArea, edge, state, plotState); if (getAttributedLabel() != null) { state = drawAttributedLabel(getAttributedLabel(), g2, plotArea, dataArea, edge, state); } else { state = drawLabel(getLabel(), g2, plotArea, dataArea, edge, state); } return state; } /** * Draws the category labels and returns the updated axis state. * * @param g2 the graphics device ({@code null} not permitted). * @param plotArea the plot area ({@code null} not permitted). * @param dataArea the area inside the axes ({@code null} not * permitted). * @param edge the axis location ({@code null} not permitted). * @param state the axis state ({@code null} not permitted). * @param plotState collects information about the plot ({@code null} * permitted). * * @return The updated axis state (never {@code null}). */ protected AxisState drawCategoryLabels(Graphics2D g2, Rectangle2D plotArea, Rectangle2D dataArea, RectangleEdge edge, AxisState state, PlotRenderingInfo plotState) { Args.nullNotPermitted(state, "state"); if (!isTickLabelsVisible()) { return state; } List ticks = refreshTicks(g2, state, plotArea, edge); state.setTicks(ticks); int categoryIndex = 0; Iterator iterator = ticks.iterator(); while (iterator.hasNext()) { CategoryTick tick = (CategoryTick) iterator.next(); g2.setFont(getTickLabelFont(tick.getCategory())); g2.setPaint(getTickLabelPaint(tick.getCategory())); CategoryLabelPosition position = this.categoryLabelPositions.getLabelPosition(edge); double x0 = 0.0; double x1 = 0.0; double y0 = 0.0; double y1 = 0.0; if (edge == RectangleEdge.TOP) { x0 = getCategoryStart(categoryIndex, ticks.size(), dataArea, edge); x1 = getCategoryEnd(categoryIndex, ticks.size(), dataArea, edge); y1 = state.getCursor() - this.categoryLabelPositionOffset; y0 = y1 - state.getMax(); } else if (edge == RectangleEdge.BOTTOM) { x0 = getCategoryStart(categoryIndex, ticks.size(), dataArea, edge); x1 = getCategoryEnd(categoryIndex, ticks.size(), dataArea, edge); y0 = state.getCursor() + this.categoryLabelPositionOffset; y1 = y0 + state.getMax(); } else if (edge == RectangleEdge.LEFT) { y0 = getCategoryStart(categoryIndex, ticks.size(), dataArea, edge); y1 = getCategoryEnd(categoryIndex, ticks.size(), dataArea, edge); x1 = state.getCursor() - this.categoryLabelPositionOffset; x0 = x1 - state.getMax(); } else if (edge == RectangleEdge.RIGHT) { y0 = getCategoryStart(categoryIndex, ticks.size(), dataArea, edge); y1 = getCategoryEnd(categoryIndex, ticks.size(), dataArea, edge); x0 = state.getCursor() + this.categoryLabelPositionOffset; x1 = x0 - state.getMax(); } Rectangle2D area = new Rectangle2D.Double(x0, y0, (x1 - x0), (y1 - y0)); Point2D anchorPoint = position.getCategoryAnchor().getAnchorPoint(area); TextBlock block = tick.getLabel(); block.draw(g2, (float) anchorPoint.getX(), (float) anchorPoint.getY(), position.getLabelAnchor(), (float) anchorPoint.getX(), (float) anchorPoint.getY(), position.getAngle()); Shape bounds = block.calculateBounds(g2, (float) anchorPoint.getX(), (float) anchorPoint.getY(), position.getLabelAnchor(), (float) anchorPoint.getX(), (float) anchorPoint.getY(), position.getAngle()); if (plotState != null && plotState.getOwner() != null) { EntityCollection entities = plotState.getOwner() .getEntityCollection(); if (entities != null) { String tooltip = getCategoryLabelToolTip( tick.getCategory()); String url = getCategoryLabelURL(tick.getCategory()); entities.add(new CategoryLabelEntity(tick.getCategory(), bounds, tooltip, url)); } } categoryIndex++; } if (edge.equals(RectangleEdge.TOP)) { double h = state.getMax() + this.categoryLabelPositionOffset; state.cursorUp(h); } else if (edge.equals(RectangleEdge.BOTTOM)) { double h = state.getMax() + this.categoryLabelPositionOffset; state.cursorDown(h); } else if (edge == RectangleEdge.LEFT) { double w = state.getMax() + this.categoryLabelPositionOffset; state.cursorLeft(w); } else if (edge == RectangleEdge.RIGHT) { double w = state.getMax() + this.categoryLabelPositionOffset; state.cursorRight(w); } return state; } /** * Creates a temporary list of ticks that can be used when drawing the axis. * * @param g2 the graphics device (used to get font measurements). * @param state the axis state. * @param dataArea the area inside the axes. * @param edge the location of the axis. * * @return A list of ticks. */ @Override public List refreshTicks(Graphics2D g2, AxisState state, Rectangle2D dataArea, RectangleEdge edge) { List ticks = new java.util.ArrayList(); // sanity check for data area... if (dataArea.getHeight() <= 0.0 || dataArea.getWidth() < 0.0) { return ticks; } CategoryPlot plot = (CategoryPlot) getPlot(); List categories = plot.getCategoriesForAxis(this); double max = 0.0; if (categories != null) { CategoryLabelPosition position = this.categoryLabelPositions.getLabelPosition(edge); float r = this.maximumCategoryLabelWidthRatio; if (r <= 0.0) { r = position.getWidthRatio(); } float l; if (position.getWidthType() == CategoryLabelWidthType.CATEGORY) { l = (float) calculateCategorySize(categories.size(), dataArea, edge); } else { if (RectangleEdge.isLeftOrRight(edge)) { l = (float) dataArea.getWidth(); } else { l = (float) dataArea.getHeight(); } } int categoryIndex = 0; Iterator iterator = categories.iterator(); while (iterator.hasNext()) { Comparable category = (Comparable) iterator.next(); g2.setFont(getTickLabelFont(category)); TextBlock label = createLabel(category, l * r, edge, g2); if (edge == RectangleEdge.TOP || edge == RectangleEdge.BOTTOM) { max = Math.max(max, calculateTextBlockHeight(label, position, g2)); } else if (edge == RectangleEdge.LEFT || edge == RectangleEdge.RIGHT) { max = Math.max(max, calculateTextBlockWidth(label, position, g2)); } Tick tick = new CategoryTick(category, label, position.getLabelAnchor(), position.getRotationAnchor(), position.getAngle()); ticks.add(tick); categoryIndex = categoryIndex + 1; } } state.setMax(max); return ticks; } /** * Draws the tick marks. * * @param g2 the graphics target. * @param cursor the cursor position (an offset when drawing multiple axes) * @param dataArea the area for plotting the data. * @param edge the location of the axis. * @param state the axis state. */ public void drawTickMarks(Graphics2D g2, double cursor, Rectangle2D dataArea, RectangleEdge edge, AxisState state) { Plot p = getPlot(); if (p == null) { return; } CategoryPlot plot = (CategoryPlot) p; double il = getTickMarkInsideLength(); double ol = getTickMarkOutsideLength(); Line2D line = new Line2D.Double(); List categories = plot.getCategoriesForAxis(this); g2.setPaint(getTickMarkPaint()); g2.setStroke(getTickMarkStroke()); Object saved = g2.getRenderingHint(RenderingHints.KEY_STROKE_CONTROL); g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE); if (edge.equals(RectangleEdge.TOP)) { Iterator iterator = categories.iterator(); while (iterator.hasNext()) { Comparable key = (Comparable) iterator.next(); double x = getCategoryMiddle(key, categories, dataArea, edge); line.setLine(x, cursor, x, cursor + il); g2.draw(line); line.setLine(x, cursor, x, cursor - ol); g2.draw(line); } state.cursorUp(ol); } else if (edge.equals(RectangleEdge.BOTTOM)) { Iterator iterator = categories.iterator(); while (iterator.hasNext()) { Comparable key = (Comparable) iterator.next(); double x = getCategoryMiddle(key, categories, dataArea, edge); line.setLine(x, cursor, x, cursor - il); g2.draw(line); line.setLine(x, cursor, x, cursor + ol); g2.draw(line); } state.cursorDown(ol); } else if (edge.equals(RectangleEdge.LEFT)) { Iterator iterator = categories.iterator(); while (iterator.hasNext()) { Comparable key = (Comparable) iterator.next(); double y = getCategoryMiddle(key, categories, dataArea, edge); line.setLine(cursor, y, cursor + il, y); g2.draw(line); line.setLine(cursor, y, cursor - ol, y); g2.draw(line); } state.cursorLeft(ol); } else if (edge.equals(RectangleEdge.RIGHT)) { Iterator iterator = categories.iterator(); while (iterator.hasNext()) { Comparable key = (Comparable) iterator.next(); double y = getCategoryMiddle(key, categories, dataArea, edge); line.setLine(cursor, y, cursor - il, y); g2.draw(line); line.setLine(cursor, y, cursor + ol, y); g2.draw(line); } state.cursorRight(ol); } g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, saved); } /** * Creates a label. * * @param category the category. * @param width the available width. * @param edge the edge on which the axis appears. * @param g2 the graphics device. * * @return A label. */ protected TextBlock createLabel(Comparable category, float width, RectangleEdge edge, Graphics2D g2) { TextBlock label = TextUtils.createTextBlock(category.toString(), getTickLabelFont(category), getTickLabelPaint(category), width, this.maximumCategoryLabelLines, new G2TextMeasurer(g2)); return label; } /** * A utility method for determining the width of a text block. * * @param block the text block. * @param position the position. * @param g2 the graphics device. * * @return The width. */ protected double calculateTextBlockWidth(TextBlock block, CategoryLabelPosition position, Graphics2D g2) { RectangleInsets insets = getTickLabelInsets(); Size2D size = block.calculateDimensions(g2); Rectangle2D box = new Rectangle2D.Double(0.0, 0.0, size.getWidth(), size.getHeight()); Shape rotatedBox = ShapeUtils.rotateShape(box, position.getAngle(), 0.0f, 0.0f); double w = rotatedBox.getBounds2D().getWidth() + insets.getLeft() + insets.getRight(); return w; } /** * A utility method for determining the height of a text block. * * @param block the text block. * @param position the label position. * @param g2 the graphics device. * * @return The height. */ protected double calculateTextBlockHeight(TextBlock block, CategoryLabelPosition position, Graphics2D g2) { RectangleInsets insets = getTickLabelInsets(); Size2D size = block.calculateDimensions(g2); Rectangle2D box = new Rectangle2D.Double(0.0, 0.0, size.getWidth(), size.getHeight()); Shape rotatedBox = ShapeUtils.rotateShape(box, position.getAngle(), 0.0f, 0.0f); double h = rotatedBox.getBounds2D().getHeight() + insets.getTop() + insets.getBottom(); return h; } /** * Creates a clone of the axis. * * @return A clone. * * @throws CloneNotSupportedException if some component of the axis does * not support cloning. */ @Override public Object clone() throws CloneNotSupportedException { CategoryAxis clone = (CategoryAxis) super.clone(); clone.tickLabelFontMap = new HashMap(this.tickLabelFontMap); clone.tickLabelPaintMap = new HashMap(this.tickLabelPaintMap); clone.categoryLabelToolTips = new HashMap(this.categoryLabelToolTips); clone.categoryLabelURLs = new HashMap(this.categoryLabelToolTips); return clone; } /** * Tests this axis for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof CategoryAxis)) { return false; } if (!super.equals(obj)) { return false; } CategoryAxis that = (CategoryAxis) obj; if (that.lowerMargin != this.lowerMargin) { return false; } if (that.upperMargin != this.upperMargin) { return false; } if (that.categoryMargin != this.categoryMargin) { return false; } if (that.maximumCategoryLabelWidthRatio != this.maximumCategoryLabelWidthRatio) { return false; } if (that.categoryLabelPositionOffset != this.categoryLabelPositionOffset) { return false; } if (!Objects.equals(that.categoryLabelPositions, this.categoryLabelPositions)) { return false; } if (!Objects.equals(that.categoryLabelToolTips, this.categoryLabelToolTips)) { return false; } if (!Objects.equals(this.categoryLabelURLs, that.categoryLabelURLs)) { return false; } if (!Objects.equals(this.tickLabelFontMap, that.tickLabelFontMap)) { return false; } if (!equalPaintMaps(this.tickLabelPaintMap, that.tickLabelPaintMap)) { return false; } return true; } /** * Returns a hash code for this object. * * @return A hash code. */ @Override public int hashCode() { return super.hashCode(); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); writePaintMap(this.tickLabelPaintMap, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.tickLabelPaintMap = readPaintMap(stream); } /** * Reads a {@code Map} of ({@code Comparable}, {@code Paint}) * elements from a stream. * * @param in the input stream. * * @return The map. * * @throws IOException * @throws ClassNotFoundException * * @see #writePaintMap(Map, ObjectOutputStream) */ private Map readPaintMap(ObjectInputStream in) throws IOException, ClassNotFoundException { boolean isNull = in.readBoolean(); if (isNull) { return null; } Map result = new HashMap(); int count = in.readInt(); for (int i = 0; i < count; i++) { Comparable category = (Comparable) in.readObject(); Paint paint = SerialUtils.readPaint(in); result.put(category, paint); } return result; } /** * Writes a map of ({@code Comparable}, {@code Paint}) * elements to a stream. * * @param map the map ({@code null} permitted). * * @param out * @throws IOException * * @see #readPaintMap(ObjectInputStream) */ private void writePaintMap(Map map, ObjectOutputStream out) throws IOException { if (map == null) { out.writeBoolean(true); } else { out.writeBoolean(false); Set keys = map.keySet(); int count = keys.size(); out.writeInt(count); Iterator iterator = keys.iterator(); while (iterator.hasNext()) { Comparable key = (Comparable) iterator.next(); out.writeObject(key); SerialUtils.writePaint((Paint) map.get(key), out); } } } /** * Tests two maps containing ({@code Comparable}, {@code Paint}) * elements for equality. * * @param map1 the first map ({@code null} not permitted). * @param map2 the second map ({@code null} not permitted). * * @return A boolean. */ private boolean equalPaintMaps(Map map1, Map map2) { if (map1.size() != map2.size()) { return false; } Set entries = map1.entrySet(); Iterator iterator = entries.iterator(); while (iterator.hasNext()) { Map.Entry entry = (Map.Entry) iterator.next(); Paint p1 = (Paint) entry.getValue(); Paint p2 = (Paint) map2.get(entry.getKey()); if (!PaintUtils.equal(p1, p2)) { return false; } } return true; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/CategoryLabelPosition.java000066400000000000000000000217741463604235500311420ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * CategoryLabelPosition.java * -------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.axis; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.text.TextBlockAnchor; import org.jfree.chart.ui.RectangleAnchor; import org.jfree.chart.ui.TextAnchor; import org.jfree.chart.util.Args; /** * The attributes that control the position of the labels for the categories on * a {@link CategoryAxis}. Instances of this class are immutable and other * JFreeChart classes rely upon this. */ public class CategoryLabelPosition implements Serializable { /** For serialization. */ private static final long serialVersionUID = 5168681143844183864L; /** The category anchor point. */ private final RectangleAnchor categoryAnchor; /** The text block anchor. */ private final TextBlockAnchor labelAnchor; /** The rotation anchor. */ private final TextAnchor rotationAnchor; /** The rotation angle (in radians). */ private final double angle; /** The width calculation type. */ private final CategoryLabelWidthType widthType; /** * The maximum label width as a percentage of the category space or the * range space. */ private final float widthRatio; /** * Creates a new position record with default settings. */ public CategoryLabelPosition() { this(RectangleAnchor.CENTER, TextBlockAnchor.BOTTOM_CENTER, TextAnchor.CENTER, 0.0, CategoryLabelWidthType.CATEGORY, 0.95f); } /** * Creates a new category label position record. * * @param categoryAnchor the category anchor ({@code null} not * permitted). * @param labelAnchor the label anchor ({@code null} not permitted). */ public CategoryLabelPosition(RectangleAnchor categoryAnchor, TextBlockAnchor labelAnchor) { // argument checking delegated... this(categoryAnchor, labelAnchor, TextAnchor.CENTER, 0.0, CategoryLabelWidthType.CATEGORY, 0.95f); } /** * Creates a new category label position record. * * @param categoryAnchor the category anchor ({@code null} not * permitted). * @param labelAnchor the label anchor ({@code null} not permitted). * @param widthType the width type ({@code null} not permitted). * @param widthRatio the maximum label width as a percentage (of the * category space or the range space). */ public CategoryLabelPosition(RectangleAnchor categoryAnchor, TextBlockAnchor labelAnchor, CategoryLabelWidthType widthType, float widthRatio) { // argument checking delegated... this(categoryAnchor, labelAnchor, TextAnchor.CENTER, 0.0, widthType, widthRatio); } /** * Creates a new position record. The item label anchor is a point * relative to the data item (dot, bar or other visual item) on a chart. * The item label is aligned by aligning the text anchor with the item * label anchor. * * @param categoryAnchor the category anchor ({@code null} not * permitted). * @param labelAnchor the label anchor ({@code null} not permitted). * @param rotationAnchor the rotation anchor ({@code null} not * permitted). * @param angle the rotation angle ({@code null} not permitted). * @param widthType the width type ({@code null} not permitted). * @param widthRatio the maximum label width as a percentage (of the * category space or the range space). */ public CategoryLabelPosition(RectangleAnchor categoryAnchor, TextBlockAnchor labelAnchor, TextAnchor rotationAnchor, double angle, CategoryLabelWidthType widthType, float widthRatio) { Args.nullNotPermitted(categoryAnchor, "categoryAnchor"); Args.nullNotPermitted(labelAnchor, "labelAnchor"); Args.nullNotPermitted(rotationAnchor, "rotationAnchor"); Args.nullNotPermitted(widthType, "widthType"); this.categoryAnchor = categoryAnchor; this.labelAnchor = labelAnchor; this.rotationAnchor = rotationAnchor; this.angle = angle; this.widthType = widthType; this.widthRatio = widthRatio; } /** * Returns the item label anchor. * * @return The item label anchor (never {@code null}). */ public RectangleAnchor getCategoryAnchor() { return this.categoryAnchor; } /** * Returns the text block anchor. * * @return The text block anchor (never {@code null}). */ public TextBlockAnchor getLabelAnchor() { return this.labelAnchor; } /** * Returns the rotation anchor point. * * @return The rotation anchor point (never {@code null}). */ public TextAnchor getRotationAnchor() { return this.rotationAnchor; } /** * Returns the angle of rotation for the label. * * @return The angle (in radians). */ public double getAngle() { return this.angle; } /** * Returns the width calculation type. * * @return The width calculation type (never {@code null}). */ public CategoryLabelWidthType getWidthType() { return this.widthType; } /** * Returns the ratio used to calculate the maximum category label width. * * @return The ratio. */ public float getWidthRatio() { return this.widthRatio; } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof CategoryLabelPosition)) { return false; } CategoryLabelPosition that = (CategoryLabelPosition) obj; if (Double.doubleToLongBits(this.angle) != Double.doubleToLongBits(that.angle)) { return false; } if (Float.floatToIntBits(this.widthRatio) != Float.floatToIntBits(that.widthRatio)) { return false; } if (!Objects.equals(this.categoryAnchor,that.categoryAnchor)) { return false; } if (!Objects.equals(this.labelAnchor, that.labelAnchor)) { return false; } if (!Objects.equals(this.rotationAnchor, that.rotationAnchor)) { return false; } if (!Objects.equals(this.widthType, that.widthType)) { return false; } return true; } /** * Returns a hash code for this object. * * @return A hash code. */ @Override public int hashCode() { int result = 19; result = 61 * result + Objects.hashCode(this.categoryAnchor); result = 61 * result + Objects.hashCode(this.labelAnchor); result = 61 * result + Objects.hashCode(this.rotationAnchor); result = 61 * result + (int) (Double.doubleToLongBits(this.angle) ^ (Double.doubleToLongBits(this.angle) >>> 32)); result = 61 * result + Objects.hashCode(this.widthType); result = 61 * result + Float.floatToIntBits(this.widthRatio); return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/CategoryLabelPositions.java000066400000000000000000000367531463604235500313300ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * CategoryLabelPositions.java * --------------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.axis; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.text.TextBlockAnchor; import org.jfree.chart.ui.RectangleAnchor; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.TextAnchor; import org.jfree.chart.util.Args; /** * Records the label positions for a category axis. Instances of this class * are immutable. */ public class CategoryLabelPositions implements Serializable { /** For serialization. */ private static final long serialVersionUID = -8999557901920364580L; /** STANDARD category label positions. */ public static final CategoryLabelPositions STANDARD = new CategoryLabelPositions( new CategoryLabelPosition( RectangleAnchor.BOTTOM, TextBlockAnchor.BOTTOM_CENTER), // TOP new CategoryLabelPosition( RectangleAnchor.TOP, TextBlockAnchor.TOP_CENTER), // BOTTOM new CategoryLabelPosition( RectangleAnchor.RIGHT, TextBlockAnchor.CENTER_RIGHT, CategoryLabelWidthType.RANGE, 0.30f), // LEFT new CategoryLabelPosition( RectangleAnchor.LEFT, TextBlockAnchor.CENTER_LEFT, CategoryLabelWidthType.RANGE, 0.30f) // RIGHT ); /** UP_90 category label positions. */ public static final CategoryLabelPositions UP_90 = new CategoryLabelPositions( new CategoryLabelPosition( RectangleAnchor.BOTTOM, TextBlockAnchor.CENTER_LEFT, TextAnchor.CENTER_LEFT, -Math.PI / 2.0, CategoryLabelWidthType.RANGE, 0.30f), // TOP new CategoryLabelPosition( RectangleAnchor.TOP, TextBlockAnchor.CENTER_RIGHT, TextAnchor.CENTER_RIGHT, -Math.PI / 2.0, CategoryLabelWidthType.RANGE, 0.30f), // BOTTOM new CategoryLabelPosition( RectangleAnchor.RIGHT, TextBlockAnchor.BOTTOM_CENTER, TextAnchor.BOTTOM_CENTER, -Math.PI / 2.0, CategoryLabelWidthType.CATEGORY, 0.9f), // LEFT new CategoryLabelPosition( RectangleAnchor.LEFT, TextBlockAnchor.TOP_CENTER, TextAnchor.TOP_CENTER, -Math.PI / 2.0, CategoryLabelWidthType.CATEGORY, 0.90f) // RIGHT ); /** DOWN_90 category label positions. */ public static final CategoryLabelPositions DOWN_90 = new CategoryLabelPositions( new CategoryLabelPosition( RectangleAnchor.BOTTOM, TextBlockAnchor.CENTER_RIGHT, TextAnchor.CENTER_RIGHT, Math.PI / 2.0, CategoryLabelWidthType.RANGE, 0.30f), // TOP new CategoryLabelPosition( RectangleAnchor.TOP, TextBlockAnchor.CENTER_LEFT, TextAnchor.CENTER_LEFT, Math.PI / 2.0, CategoryLabelWidthType.RANGE, 0.30f), // BOTTOM new CategoryLabelPosition( RectangleAnchor.RIGHT, TextBlockAnchor.TOP_CENTER, TextAnchor.TOP_CENTER, Math.PI / 2.0, CategoryLabelWidthType.CATEGORY, 0.90f), // LEFT new CategoryLabelPosition( RectangleAnchor.LEFT, TextBlockAnchor.BOTTOM_CENTER, TextAnchor.BOTTOM_CENTER, Math.PI / 2.0, CategoryLabelWidthType.CATEGORY, 0.90f) // RIGHT ); /** UP_45 category label positions. */ public static final CategoryLabelPositions UP_45 = createUpRotationLabelPositions(Math.PI / 4.0); /** DOWN_45 category label positions. */ public static final CategoryLabelPositions DOWN_45 = createDownRotationLabelPositions(Math.PI / 4.0); /** * Creates a new instance where the category labels angled upwards by the * specified amount. * * @param angle the rotation angle (should be < Math.PI / 2.0). * * @return A category label position specification. */ public static CategoryLabelPositions createUpRotationLabelPositions( double angle) { return new CategoryLabelPositions( new CategoryLabelPosition( RectangleAnchor.BOTTOM, TextBlockAnchor.BOTTOM_LEFT, TextAnchor.BOTTOM_LEFT, -angle, CategoryLabelWidthType.RANGE, 0.50f), // TOP new CategoryLabelPosition( RectangleAnchor.TOP, TextBlockAnchor.TOP_RIGHT, TextAnchor.TOP_RIGHT, -angle, CategoryLabelWidthType.RANGE, 0.50f), // BOTTOM new CategoryLabelPosition( RectangleAnchor.RIGHT, TextBlockAnchor.BOTTOM_RIGHT, TextAnchor.BOTTOM_RIGHT, -angle, CategoryLabelWidthType.RANGE, 0.50f), // LEFT new CategoryLabelPosition( RectangleAnchor.LEFT, TextBlockAnchor.TOP_LEFT, TextAnchor.TOP_LEFT, -angle, CategoryLabelWidthType.RANGE, 0.50f) // RIGHT ); } /** * Creates a new instance where the category labels angled downwards by the * specified amount. * * @param angle the rotation angle (should be < Math.PI / 2.0). * * @return A category label position specification. */ public static CategoryLabelPositions createDownRotationLabelPositions( double angle) { return new CategoryLabelPositions( new CategoryLabelPosition( RectangleAnchor.BOTTOM, TextBlockAnchor.BOTTOM_RIGHT, TextAnchor.BOTTOM_RIGHT, angle, CategoryLabelWidthType.RANGE, 0.50f), // TOP new CategoryLabelPosition( RectangleAnchor.TOP, TextBlockAnchor.TOP_LEFT, TextAnchor.TOP_LEFT, angle, CategoryLabelWidthType.RANGE, 0.50f), // BOTTOM new CategoryLabelPosition( RectangleAnchor.RIGHT, TextBlockAnchor.TOP_RIGHT, TextAnchor.TOP_RIGHT, angle, CategoryLabelWidthType.RANGE, 0.50f), // LEFT new CategoryLabelPosition( RectangleAnchor.LEFT, TextBlockAnchor.BOTTOM_LEFT, TextAnchor.BOTTOM_LEFT, angle, CategoryLabelWidthType.RANGE, 0.50f) // RIGHT ); } /** * The label positioning details used when an axis is at the top of a * chart. */ private final CategoryLabelPosition positionForAxisAtTop; /** * The label positioning details used when an axis is at the bottom of a * chart. */ private final CategoryLabelPosition positionForAxisAtBottom; /** * The label positioning details used when an axis is at the left of a * chart. */ private final CategoryLabelPosition positionForAxisAtLeft; /** * The label positioning details used when an axis is at the right of a * chart. */ private final CategoryLabelPosition positionForAxisAtRight; /** * Default constructor. */ public CategoryLabelPositions() { this.positionForAxisAtTop = new CategoryLabelPosition(); this.positionForAxisAtBottom = new CategoryLabelPosition(); this.positionForAxisAtLeft = new CategoryLabelPosition(); this.positionForAxisAtRight = new CategoryLabelPosition(); } /** * Creates a new position specification. * * @param top the label position info used when an axis is at the top * ({@code null} not permitted). * @param bottom the label position info used when an axis is at the * bottom ({@code null} not permitted). * @param left the label position info used when an axis is at the left * ({@code null} not permitted). * @param right the label position info used when an axis is at the right * ({@code null} not permitted). */ public CategoryLabelPositions(CategoryLabelPosition top, CategoryLabelPosition bottom, CategoryLabelPosition left, CategoryLabelPosition right) { Args.nullNotPermitted(top, "top"); Args.nullNotPermitted(bottom, "bottom"); Args.nullNotPermitted(left, "left"); Args.nullNotPermitted(right, "right"); this.positionForAxisAtTop = top; this.positionForAxisAtBottom = bottom; this.positionForAxisAtLeft = left; this.positionForAxisAtRight = right; } /** * Returns the category label position specification for an axis at the * given location. * * @param edge the axis location. * * @return The category label position specification. */ public CategoryLabelPosition getLabelPosition(RectangleEdge edge) { CategoryLabelPosition result = null; if (edge == RectangleEdge.TOP) { result = this.positionForAxisAtTop; } else if (edge == RectangleEdge.BOTTOM) { result = this.positionForAxisAtBottom; } else if (edge == RectangleEdge.LEFT) { result = this.positionForAxisAtLeft; } else if (edge == RectangleEdge.RIGHT) { result = this.positionForAxisAtRight; } return result; } /** * Returns a new instance based on an existing instance but with the top * position changed. * * @param base the base ({@code null} not permitted). * @param top the top position ({@code null} not permitted). * * @return A new instance (never {@code null}). */ public static CategoryLabelPositions replaceTopPosition( CategoryLabelPositions base, CategoryLabelPosition top) { Args.nullNotPermitted(base, "base"); Args.nullNotPermitted(top, "top"); return new CategoryLabelPositions(top, base.getLabelPosition(RectangleEdge.BOTTOM), base.getLabelPosition(RectangleEdge.LEFT), base.getLabelPosition(RectangleEdge.RIGHT)); } /** * Returns a new instance based on an existing instance but with the bottom * position changed. * * @param base the base ({@code null} not permitted). * @param bottom the bottom position ({@code null} not permitted). * * @return A new instance (never {@code null}). */ public static CategoryLabelPositions replaceBottomPosition( CategoryLabelPositions base, CategoryLabelPosition bottom) { Args.nullNotPermitted(base, "base"); Args.nullNotPermitted(bottom, "bottom"); return new CategoryLabelPositions( base.getLabelPosition(RectangleEdge.TOP), bottom, base.getLabelPosition(RectangleEdge.LEFT), base.getLabelPosition(RectangleEdge.RIGHT)); } /** * Returns a new instance based on an existing instance but with the left * position changed. * * @param base the base ({@code null} not permitted). * @param left the left position ({@code null} not permitted). * * @return A new instance (never {@code null}). */ public static CategoryLabelPositions replaceLeftPosition( CategoryLabelPositions base, CategoryLabelPosition left) { Args.nullNotPermitted(base, "base"); Args.nullNotPermitted(left, "left"); return new CategoryLabelPositions( base.getLabelPosition(RectangleEdge.TOP), base.getLabelPosition(RectangleEdge.BOTTOM), left, base.getLabelPosition(RectangleEdge.RIGHT)); } /** * Returns a new instance based on an existing instance but with the right * position changed. * * @param base the base ({@code null} not permitted). * @param right the right position ({@code null} not permitted). * * @return A new instance (never {@code null}). */ public static CategoryLabelPositions replaceRightPosition( CategoryLabelPositions base, CategoryLabelPosition right) { Args.nullNotPermitted(base, "base"); Args.nullNotPermitted(right, "right"); return new CategoryLabelPositions( base.getLabelPosition(RectangleEdge.TOP), base.getLabelPosition(RectangleEdge.BOTTOM), base.getLabelPosition(RectangleEdge.LEFT), right); } /** * Returns {@code true} if this object is equal to the specified * object, and {@code false} otherwise. * * @param obj the other object. * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof CategoryLabelPositions)) { return false; } CategoryLabelPositions that = (CategoryLabelPositions) obj; if (!Objects.equals(this.positionForAxisAtTop, that.positionForAxisAtTop)) { return false; } if (!Objects.equals(this.positionForAxisAtBottom, that.positionForAxisAtBottom)) { return false; } if (!Objects.equals(this.positionForAxisAtLeft, that.positionForAxisAtLeft)) { return false; } if (!Objects.equals(this.positionForAxisAtRight, that.positionForAxisAtRight)) { return false; } return true; } /** * Returns a hash code for this object. * * @return A hash code. */ @Override public int hashCode() { int result = 19; result = 19 * result + Objects.hashCode(this.positionForAxisAtTop); result = 19 * result + Objects.hashCode(this.positionForAxisAtBottom); result = 19 * result + Objects.hashCode(this.positionForAxisAtLeft); result = 19 * result + Objects.hashCode(this.positionForAxisAtRight); return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/CategoryLabelWidthType.java000066400000000000000000000074051463604235500312520ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * CategoryLabelWidthType.java * --------------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.io.ObjectStreamException; import java.io.Serializable; import org.jfree.chart.util.Args; /** * Represents the width types for a category label. */ public final class CategoryLabelWidthType implements Serializable { /** For serialization. */ private static final long serialVersionUID = -6976024792582949656L; /** Percentage of category. */ public static final CategoryLabelWidthType CATEGORY = new CategoryLabelWidthType("CategoryLabelWidthType.CATEGORY"); /** Percentage of range. */ public static final CategoryLabelWidthType RANGE = new CategoryLabelWidthType("CategoryLabelWidthType.RANGE"); /** The name. */ private final String name; /** * Private constructor. * * @param name the name ({@code null} not permitted). */ private CategoryLabelWidthType(String name) { Args.nullNotPermitted(name, "name"); this.name = name; } /** * Returns a string representing the object. * * @return The string (never {@code null}). */ @Override public String toString() { return this.name; } /** * Returns {@code true} if this object is equal to the specified * object, and {@code false} otherwise. * * @param obj the other object. * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof CategoryLabelWidthType)) { return false; } CategoryLabelWidthType t = (CategoryLabelWidthType) obj; if (!this.name.equals(t.toString())) { return false; } return true; } /** * Ensures that serialization returns the unique instances. * * @return The object. * * @throws ObjectStreamException if there is a problem. */ private Object readResolve() throws ObjectStreamException { if (this.equals(CategoryLabelWidthType.CATEGORY)) { return CategoryLabelWidthType.CATEGORY; } else if (this.equals(CategoryLabelWidthType.RANGE)) { return CategoryLabelWidthType.RANGE; } return null; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/CategoryTick.java000066400000000000000000000115071463604235500272610ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * CategoryTick.java * ----------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.axis; import java.util.Objects; import org.jfree.chart.text.TextBlock; import org.jfree.chart.text.TextBlockAnchor; import org.jfree.chart.ui.TextAnchor; /** * A tick for a {@link CategoryAxis}. */ public class CategoryTick extends Tick { /** The category. */ private final Comparable category; /** The label. */ private final TextBlock label; /** The label anchor. */ private final TextBlockAnchor labelAnchor; /** * Creates a new tick. * * @param category the category. * @param label the label. * @param labelAnchor the label anchor. * @param rotationAnchor the rotation anchor. * @param angle the rotation angle (in radians). */ public CategoryTick(Comparable category, TextBlock label, TextBlockAnchor labelAnchor, TextAnchor rotationAnchor, double angle) { super("", TextAnchor.CENTER, rotationAnchor, angle); this.category = category; this.label = label; this.labelAnchor = labelAnchor; } /** * Returns the category. * * @return The category. */ public Comparable getCategory() { return this.category; } /** * Returns the label. * * @return The label. */ public TextBlock getLabel() { return this.label; } /** * Returns the label anchor. * * @return The label anchor. */ public TextBlockAnchor getLabelAnchor() { return this.labelAnchor; } /** * Tests this category tick for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof CategoryTick)) { return false; } CategoryTick that = (CategoryTick) obj; if (!Objects.equals(this.category, that.category)) { return false; } if (!Objects.equals(this.label, that.label)) { return false; } if (!Objects.equals(this.labelAnchor, that.labelAnchor)) { return false; } if (!that.canEqual(this)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof CategoryTick); } /** * Returns a hash code for this object. * * @return A hash code. */ @Override public int hashCode() { int hash = super.hashCode(); // equals calls superclass, hashCode must also hash = 89 * hash + Objects.hashCode(this.category); hash = 89 * hash + Objects.hashCode(this.label); hash = 89 * hash + Objects.hashCode(this.labelAnchor); return hash; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/CompassFormat.java000066400000000000000000000114401463604235500274430ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * CompassFormat.java * ------------------ * (C) Copyright 2003-present, by Sylvain Vieujot and Contributors. * * Original Author: Sylvain Vieujot; * Contributor(s): David Gilbert; * Simon Legner (GitHub #298); * */ package org.jfree.chart.axis; import java.text.FieldPosition; import java.text.NumberFormat; import java.text.ParsePosition; import org.jfree.chart.util.Args; /** * A formatter that displays numbers as directions. */ public class CompassFormat extends NumberFormat { /** The directions. */ public final String[] directions; /** * Creates a new formatter using English identifiers. */ public CompassFormat() { this("N", "E", "S", "W"); } /** * Creates a new formatter using the specified identifiers for * the base wind directions. * * @param n the code for NORTH. * @param e the code for EAST. * @param s the code for SOUTH. * @param w the code for WEST. */ public CompassFormat(String n, String e, String s, String w) { this(new String[] { n, n + n + e, n + e, e + n + e, e, e + s + e, s + e, s + s + e, s, s + s + w, s + w, w + s + w, w, w + n + w, n + w, n + n + w }); } /** * Creates a new formatter using the specified identifiers. * * @param directions an array containing 16 strings representing * the directions of a compass. */ public CompassFormat(String[] directions) { super(); Args.nullNotPermitted(directions, "directions"); if (directions.length != 16) { throw new IllegalArgumentException("The 'directions' array must " + "contain exactly 16 elements"); } this.directions = directions; } /** * Returns a string representing the direction. * * @param direction the direction. * * @return A string. */ public String getDirectionCode(double direction) { direction = direction % 360; if (direction < 0.0) { direction = direction + 360.0; } int index = ((int) Math.floor(direction / 11.25) + 1) / 2; return directions[index]; } /** * Formats a number into the specified string buffer. * * @param number the number to format. * @param toAppendTo the string buffer. * @param pos the field position (ignored here). * * @return The string buffer. */ @Override public StringBuffer format(double number, StringBuffer toAppendTo, FieldPosition pos) { return toAppendTo.append(getDirectionCode(number)); } /** * Formats a number into the specified string buffer. * * @param number the number to format. * @param toAppendTo the string buffer. * @param pos the field position (ignored here). * * @return The string buffer. */ @Override public StringBuffer format(long number, StringBuffer toAppendTo, FieldPosition pos) { return toAppendTo.append(getDirectionCode(number)); } /** * This method returns {@code null} for all inputs. This class cannot * be used for parsing. * * @param source the source string. * @param parsePosition the parse position. * * @return {@code null}. */ @Override public Number parse(String source, ParsePosition parsePosition) { return null; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/CyclicNumberAxis.java000066400000000000000000001203231463604235500300720ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * CyclicNumberAxis.java * --------------------- * (C) Copyright 2003-present, by Nicolas Brodu and Contributors. * * Original Author: Nicolas Brodu; * Contributor(s): David Gilbert; * */ package org.jfree.chart.axis; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Stroke; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.text.NumberFormat; import java.util.List; import java.util.Objects; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.text.TextUtils; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.TextAnchor; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.SerialUtils; import org.jfree.data.Range; /** This class extends NumberAxis and handles cycling. Traditional representation of data in the range x0..x1

|-------------------------|
x0                       x1
Here, the range bounds are at the axis extremities. With cyclic axis, however, the time is split in "cycles", or "time frames", or the same duration : the period. A cycle axis cannot by definition handle a larger interval than the period :
x1 - x0 >= period
. Thus, at most a full period can be represented with such an axis. The cycle bound is the number between x0 and x1 which marks the beginning of new time frame:
|---------------------|----------------------------|
x0                   cb                           x1
<---previous cycle---><-------current cycle-------->
It is actually a multiple of the period, plus optionally a start offset:
cb = n * period + offset
Thus, by definition, two consecutive cycle bounds period apart, which is precisely why it is called a period. The visual representation of a cyclic axis is like that:
|----------------------------|---------------------|
cb                         x1|x0                  cb
<-------current cycle--------><---previous cycle--->
The cycle bound is at the axis ends, then current cycle is shown, then the last cycle. When using dynamic data, the visual effect is the current cycle erases the last cycle as x grows. Then, the next cycle bound is reached, and the process starts over, erasing the previous cycle. A Cyclic item renderer is provided to do exactly this. */ public class CyclicNumberAxis extends NumberAxis { /** For serialization. */ static final long serialVersionUID = -7514160997164582554L; /** The default axis line stroke. */ public static Stroke DEFAULT_ADVANCE_LINE_STROKE = new BasicStroke(1.0f); /** The default axis line paint. */ public static final Paint DEFAULT_ADVANCE_LINE_PAINT = Color.GRAY; /** The offset. */ protected double offset; /** The period.*/ protected double period; /** ??. */ protected boolean boundMappedToLastCycle; /** A flag that controls whether or not the advance line is visible. */ protected boolean advanceLineVisible; /** The advance line stroke. */ protected transient Stroke advanceLineStroke = DEFAULT_ADVANCE_LINE_STROKE; /** The advance line paint. */ protected transient Paint advanceLinePaint; private transient boolean internalMarkerWhenTicksOverlap; private transient Tick internalMarkerCycleBoundTick; /** * Creates a CycleNumberAxis with the given period. * * @param period the period. */ public CyclicNumberAxis(double period) { this(period, 0.0); } /** * Creates a CycleNumberAxis with the given period and offset. * * @param period the period. * @param offset the offset. */ public CyclicNumberAxis(double period, double offset) { this(period, offset, null); } /** * Creates a named CycleNumberAxis with the given period. * * @param period the period. * @param label the label. */ public CyclicNumberAxis(double period, String label) { this(0, period, label); } /** * Creates a named CycleNumberAxis with the given period and offset. * * @param period the period. * @param offset the offset. * @param label the label. */ public CyclicNumberAxis(double period, double offset, String label) { super(label); this.period = period; this.offset = offset; setFixedAutoRange(period); this.advanceLineVisible = true; this.advanceLinePaint = DEFAULT_ADVANCE_LINE_PAINT; } /** * The advance line is the line drawn at the limit of the current cycle, * when erasing the previous cycle. * * @return A boolean. */ public boolean isAdvanceLineVisible() { return this.advanceLineVisible; } /** * The advance line is the line drawn at the limit of the current cycle, * when erasing the previous cycle. * * @param visible the flag. */ public void setAdvanceLineVisible(boolean visible) { this.advanceLineVisible = visible; } /** * The advance line is the line drawn at the limit of the current cycle, * when erasing the previous cycle. * * @return The paint (never {@code null}). */ public Paint getAdvanceLinePaint() { return this.advanceLinePaint; } /** * The advance line is the line drawn at the limit of the current cycle, * when erasing the previous cycle. * * @param paint the paint ({@code null} not permitted). */ public void setAdvanceLinePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.advanceLinePaint = paint; } /** * The advance line is the line drawn at the limit of the current cycle, * when erasing the previous cycle. * * @return The stroke (never {@code null}). */ public Stroke getAdvanceLineStroke() { return this.advanceLineStroke; } /** * The advance line is the line drawn at the limit of the current cycle, * when erasing the previous cycle. * * @param stroke the stroke ({@code null} not permitted). */ public void setAdvanceLineStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.advanceLineStroke = stroke; } /** * The cycle bound can be associated either with the current or with the * last cycle. It's up to the user's choice to decide which, as this is * just a convention. By default, the cycle bound is mapped to the current * cycle. *
* Note that this has no effect on visual appearance, as the cycle bound is * mapped successively for both axis ends. Use this function for correct * results in translateValueToJava2D. * * @return {@code true} if the cycle bound is mapped to the last * cycle, {@code false} if it is bound to the current cycle * (default) */ public boolean isBoundMappedToLastCycle() { return this.boundMappedToLastCycle; } /** * The cycle bound can be associated either with the current or with the * last cycle. It's up to the user's choice to decide which, as this is * just a convention. By default, the cycle bound is mapped to the current * cycle. *
* Note that this has no effect on visual appearance, as the cycle bound is * mapped successively for both axis ends. Use this function for correct * results in valueToJava2D. * * @param boundMappedToLastCycle Set it to true to map the cycle bound to * the last cycle. */ public void setBoundMappedToLastCycle(boolean boundMappedToLastCycle) { this.boundMappedToLastCycle = boundMappedToLastCycle; } /** * Selects a tick unit when the axis is displayed horizontally. * * @param g2 the graphics device. * @param drawArea the drawing area. * @param dataArea the data area. * @param edge the side of the rectangle on which the axis is displayed. */ protected void selectHorizontalAutoTickUnit(Graphics2D g2, Rectangle2D drawArea, Rectangle2D dataArea, RectangleEdge edge) { double tickLabelWidth = estimateMaximumTickLabelWidth(g2, getTickUnit()); // Compute number of labels double n = getRange().getLength() * tickLabelWidth / dataArea.getWidth(); setTickUnit( (NumberTickUnit) getStandardTickUnits().getCeilingTickUnit(n), false, false); } /** * Selects a tick unit when the axis is displayed vertically. * * @param g2 the graphics device. * @param drawArea the drawing area. * @param dataArea the data area. * @param edge the side of the rectangle on which the axis is displayed. */ protected void selectVerticalAutoTickUnit(Graphics2D g2, Rectangle2D drawArea, Rectangle2D dataArea, RectangleEdge edge) { double tickLabelWidth = estimateMaximumTickLabelWidth(g2, getTickUnit()); // Compute number of labels double n = getRange().getLength() * tickLabelWidth / dataArea.getHeight(); setTickUnit( (NumberTickUnit) getStandardTickUnits().getCeilingTickUnit(n), false, false); } /** * A special Number tick that also hold information about the cycle bound * mapping for this tick. This is especially useful for having a tick at * each axis end with the cycle bound value. See also * isBoundMappedToLastCycle() */ protected static class CycleBoundTick extends NumberTick { /** Map to last cycle. */ public boolean mapToLastCycle; /** * Creates a new tick. * * @param mapToLastCycle map to last cycle? * @param number the number. * @param label the label. * @param textAnchor the text anchor. * @param rotationAnchor the rotation anchor. * @param angle the rotation angle. */ public CycleBoundTick(boolean mapToLastCycle, Number number, String label, TextAnchor textAnchor, TextAnchor rotationAnchor, double angle) { super(number, label, textAnchor, rotationAnchor, angle); this.mapToLastCycle = mapToLastCycle; } } /** * Calculates the anchor point for a tick. * * @param tick the tick. * @param cursor the cursor. * @param dataArea the data area. * @param edge the side on which the axis is displayed. * * @return The anchor point. */ @Override protected float[] calculateAnchorPoint(ValueTick tick, double cursor, Rectangle2D dataArea, RectangleEdge edge) { if (tick instanceof CycleBoundTick) { boolean mapsav = this.boundMappedToLastCycle; this.boundMappedToLastCycle = ((CycleBoundTick) tick).mapToLastCycle; float[] ret = super.calculateAnchorPoint( tick, cursor, dataArea, edge ); this.boundMappedToLastCycle = mapsav; return ret; } return super.calculateAnchorPoint(tick, cursor, dataArea, edge); } /** * Builds a list of ticks for the axis. This method is called when the * axis is at the top or bottom of the chart (so the axis is "horizontal"). * * @param g2 the graphics device. * @param dataArea the data area. * @param edge the edge. * * @return A list of ticks. */ @Override protected List refreshTicksHorizontal(Graphics2D g2, Rectangle2D dataArea, RectangleEdge edge) { List result = new java.util.ArrayList(); Font tickLabelFont = getTickLabelFont(); g2.setFont(tickLabelFont); if (isAutoTickUnitSelection()) { selectAutoTickUnit(g2, dataArea, edge); } double unit = getTickUnit().getSize(); double cycleBound = getCycleBound(); double currentTickValue = Math.ceil(cycleBound / unit) * unit; double upperValue = getRange().getUpperBound(); boolean cycled = false; boolean boundMapping = this.boundMappedToLastCycle; this.boundMappedToLastCycle = false; CycleBoundTick lastTick = null; float lastX = 0.0f; if (upperValue == cycleBound) { currentTickValue = calculateLowestVisibleTickValue(); cycled = true; this.boundMappedToLastCycle = true; } while (currentTickValue <= upperValue) { // Cycle when necessary boolean cyclenow = false; if ((currentTickValue + unit > upperValue) && !cycled) { cyclenow = true; } double xx = valueToJava2D(currentTickValue, dataArea, edge); String tickLabel; NumberFormat formatter = getNumberFormatOverride(); if (formatter != null) { tickLabel = formatter.format(currentTickValue); } else { tickLabel = getTickUnit().valueToString(currentTickValue); } float x = (float) xx; TextAnchor anchor; TextAnchor rotationAnchor; double angle = 0.0; if (isVerticalTickLabels()) { if (edge == RectangleEdge.TOP) { angle = Math.PI / 2.0; } else { angle = -Math.PI / 2.0; } anchor = TextAnchor.CENTER_RIGHT; // If tick overlap when cycling, update last tick too if ((lastTick != null) && (lastX == x) && (currentTickValue != cycleBound)) { anchor = isInverted() ? TextAnchor.TOP_RIGHT : TextAnchor.BOTTOM_RIGHT; result.remove(result.size() - 1); result.add(new CycleBoundTick( this.boundMappedToLastCycle, lastTick.getNumber(), lastTick.getText(), anchor, anchor, lastTick.getAngle()) ); this.internalMarkerWhenTicksOverlap = true; anchor = isInverted() ? TextAnchor.BOTTOM_RIGHT : TextAnchor.TOP_RIGHT; } rotationAnchor = anchor; } else { if (edge == RectangleEdge.TOP) { anchor = TextAnchor.BOTTOM_CENTER; if ((lastTick != null) && (lastX == x) && (currentTickValue != cycleBound)) { anchor = isInverted() ? TextAnchor.BOTTOM_LEFT : TextAnchor.BOTTOM_RIGHT; result.remove(result.size() - 1); result.add(new CycleBoundTick( this.boundMappedToLastCycle, lastTick.getNumber(), lastTick.getText(), anchor, anchor, lastTick.getAngle()) ); this.internalMarkerWhenTicksOverlap = true; anchor = isInverted() ? TextAnchor.BOTTOM_RIGHT : TextAnchor.BOTTOM_LEFT; } rotationAnchor = anchor; } else { anchor = TextAnchor.TOP_CENTER; if ((lastTick != null) && (lastX == x) && (currentTickValue != cycleBound)) { anchor = isInverted() ? TextAnchor.TOP_LEFT : TextAnchor.TOP_RIGHT; result.remove(result.size() - 1); result.add(new CycleBoundTick( this.boundMappedToLastCycle, lastTick.getNumber(), lastTick.getText(), anchor, anchor, lastTick.getAngle()) ); this.internalMarkerWhenTicksOverlap = true; anchor = isInverted() ? TextAnchor.TOP_RIGHT : TextAnchor.TOP_LEFT; } rotationAnchor = anchor; } } CycleBoundTick tick = new CycleBoundTick( this.boundMappedToLastCycle, currentTickValue, tickLabel, anchor, rotationAnchor, angle ); if (currentTickValue == cycleBound) { this.internalMarkerCycleBoundTick = tick; } result.add(tick); lastTick = tick; lastX = x; currentTickValue += unit; if (cyclenow) { currentTickValue = calculateLowestVisibleTickValue(); upperValue = cycleBound; cycled = true; this.boundMappedToLastCycle = true; } } this.boundMappedToLastCycle = boundMapping; return result; } /** * Builds a list of ticks for the axis. This method is called when the * axis is at the left or right of the chart (so the axis is "vertical"). * * @param g2 the graphics device. * @param dataArea the data area. * @param edge the edge. * * @return A list of ticks. */ protected List refreshVerticalTicks(Graphics2D g2, Rectangle2D dataArea, RectangleEdge edge) { List result = new java.util.ArrayList(); result.clear(); Font tickLabelFont = getTickLabelFont(); g2.setFont(tickLabelFont); if (isAutoTickUnitSelection()) { selectAutoTickUnit(g2, dataArea, edge); } double unit = getTickUnit().getSize(); double cycleBound = getCycleBound(); double currentTickValue = Math.ceil(cycleBound / unit) * unit; double upperValue = getRange().getUpperBound(); boolean cycled = false; boolean boundMapping = this.boundMappedToLastCycle; this.boundMappedToLastCycle = true; NumberTick lastTick = null; float lastY = 0.0f; if (upperValue == cycleBound) { currentTickValue = calculateLowestVisibleTickValue(); cycled = true; this.boundMappedToLastCycle = true; } while (currentTickValue <= upperValue) { // Cycle when necessary boolean cyclenow = false; if ((currentTickValue + unit > upperValue) && !cycled) { cyclenow = true; } double yy = valueToJava2D(currentTickValue, dataArea, edge); String tickLabel; NumberFormat formatter = getNumberFormatOverride(); if (formatter != null) { tickLabel = formatter.format(currentTickValue); } else { tickLabel = getTickUnit().valueToString(currentTickValue); } float y = (float) yy; TextAnchor anchor; TextAnchor rotationAnchor; double angle = 0.0; if (isVerticalTickLabels()) { if (edge == RectangleEdge.LEFT) { anchor = TextAnchor.BOTTOM_CENTER; if ((lastTick != null) && (lastY == y) && (currentTickValue != cycleBound)) { anchor = isInverted() ? TextAnchor.BOTTOM_LEFT : TextAnchor.BOTTOM_RIGHT; result.remove(result.size() - 1); result.add(new CycleBoundTick( this.boundMappedToLastCycle, lastTick.getNumber(), lastTick.getText(), anchor, anchor, lastTick.getAngle()) ); this.internalMarkerWhenTicksOverlap = true; anchor = isInverted() ? TextAnchor.BOTTOM_RIGHT : TextAnchor.BOTTOM_LEFT; } rotationAnchor = anchor; angle = -Math.PI / 2.0; } else { anchor = TextAnchor.BOTTOM_CENTER; if ((lastTick != null) && (lastY == y) && (currentTickValue != cycleBound)) { anchor = isInverted() ? TextAnchor.BOTTOM_RIGHT : TextAnchor.BOTTOM_LEFT; result.remove(result.size() - 1); result.add(new CycleBoundTick( this.boundMappedToLastCycle, lastTick.getNumber(), lastTick.getText(), anchor, anchor, lastTick.getAngle()) ); this.internalMarkerWhenTicksOverlap = true; anchor = isInverted() ? TextAnchor.BOTTOM_LEFT : TextAnchor.BOTTOM_RIGHT; } rotationAnchor = anchor; angle = Math.PI / 2.0; } } else { if (edge == RectangleEdge.LEFT) { anchor = TextAnchor.CENTER_RIGHT; if ((lastTick != null) && (lastY == y) && (currentTickValue != cycleBound)) { anchor = isInverted() ? TextAnchor.BOTTOM_RIGHT : TextAnchor.TOP_RIGHT; result.remove(result.size() - 1); result.add(new CycleBoundTick( this.boundMappedToLastCycle, lastTick.getNumber(), lastTick.getText(), anchor, anchor, lastTick.getAngle()) ); this.internalMarkerWhenTicksOverlap = true; anchor = isInverted() ? TextAnchor.TOP_RIGHT : TextAnchor.BOTTOM_RIGHT; } rotationAnchor = anchor; } else { anchor = TextAnchor.CENTER_LEFT; if ((lastTick != null) && (lastY == y) && (currentTickValue != cycleBound)) { anchor = isInverted() ? TextAnchor.BOTTOM_LEFT : TextAnchor.TOP_LEFT; result.remove(result.size() - 1); result.add(new CycleBoundTick( this.boundMappedToLastCycle, lastTick.getNumber(), lastTick.getText(), anchor, anchor, lastTick.getAngle()) ); this.internalMarkerWhenTicksOverlap = true; anchor = isInverted() ? TextAnchor.TOP_LEFT : TextAnchor.BOTTOM_LEFT; } rotationAnchor = anchor; } } CycleBoundTick tick = new CycleBoundTick( this.boundMappedToLastCycle, currentTickValue, tickLabel, anchor, rotationAnchor, angle); if (currentTickValue == cycleBound) { this.internalMarkerCycleBoundTick = tick; } result.add(tick); lastTick = tick; lastY = y; if (currentTickValue == cycleBound) { this.internalMarkerCycleBoundTick = tick; } currentTickValue += unit; if (cyclenow) { currentTickValue = calculateLowestVisibleTickValue(); upperValue = cycleBound; cycled = true; this.boundMappedToLastCycle = false; } } this.boundMappedToLastCycle = boundMapping; return result; } /** * Converts a coordinate from Java 2D space to data space. * * @param java2DValue the coordinate in Java2D space. * @param dataArea the data area. * @param edge the edge. * * @return The data value. */ @Override public double java2DToValue(double java2DValue, Rectangle2D dataArea, RectangleEdge edge) { Range range = getRange(); double vmax = range.getUpperBound(); double vp = getCycleBound(); double jmin = 0.0; double jmax = 0.0; if (RectangleEdge.isTopOrBottom(edge)) { jmin = dataArea.getMinX(); jmax = dataArea.getMaxX(); } else if (RectangleEdge.isLeftOrRight(edge)) { jmin = dataArea.getMaxY(); jmax = dataArea.getMinY(); } if (isInverted()) { double jbreak = jmax - (vmax - vp) * (jmax - jmin) / this.period; if (java2DValue >= jbreak) { return vp + (jmax - java2DValue) * this.period / (jmax - jmin); } else { return vp - (java2DValue - jmin) * this.period / (jmax - jmin); } } else { double jbreak = (vmax - vp) * (jmax - jmin) / this.period + jmin; if (java2DValue <= jbreak) { return vp + (java2DValue - jmin) * this.period / (jmax - jmin); } else { return vp - (jmax - java2DValue) * this.period / (jmax - jmin); } } } /** * Translates a value from data space to Java 2D space. * * @param value the data value. * @param dataArea the data area. * @param edge the edge. * * @return The Java 2D value. */ @Override public double valueToJava2D(double value, Rectangle2D dataArea, RectangleEdge edge) { Range range = getRange(); double vmin = range.getLowerBound(); double vmax = range.getUpperBound(); double vp = getCycleBound(); if ((value < vmin) || (value > vmax)) { return Double.NaN; } double jmin = 0.0; double jmax = 0.0; if (RectangleEdge.isTopOrBottom(edge)) { jmin = dataArea.getMinX(); jmax = dataArea.getMaxX(); } else if (RectangleEdge.isLeftOrRight(edge)) { jmax = dataArea.getMinY(); jmin = dataArea.getMaxY(); } if (isInverted()) { if (value == vp) { return this.boundMappedToLastCycle ? jmin : jmax; } else if (value > vp) { return jmax - (value - vp) * (jmax - jmin) / this.period; } else { return jmin + (vp - value) * (jmax - jmin) / this.period; } } else { if (value == vp) { return this.boundMappedToLastCycle ? jmax : jmin; } else if (value >= vp) { return jmin + (value - vp) * (jmax - jmin) / this.period; } else { return jmax - (vp - value) * (jmax - jmin) / this.period; } } } /** * Centers the range about the given value. * * @param value the data value. */ @Override public void centerRange(double value) { setRange(value - this.period / 2.0, value + this.period / 2.0); } /** * This function is nearly useless since the auto range is fixed for this * class to the period. The period is extended if necessary to fit the * minimum size. * * @param size the size. * @param notify notify? * * @see org.jfree.chart.axis.ValueAxis#setAutoRangeMinimumSize(double, * boolean) */ @Override public void setAutoRangeMinimumSize(double size, boolean notify) { if (size > this.period) { this.period = size; } super.setAutoRangeMinimumSize(size, notify); } /** * The auto range is fixed for this class to the period by default. * This function will thus set a new period. * * @param length the length. * * @see org.jfree.chart.axis.ValueAxis#setFixedAutoRange(double) */ @Override public void setFixedAutoRange(double length) { this.period = length; super.setFixedAutoRange(length); } /** * Sets a new axis range. The period is extended to fit the range size, if * necessary. * * @param range the range. * @param turnOffAutoRange switch off the auto range. * @param notify notify? * * @see org.jfree.chart.axis.ValueAxis#setRange(Range, boolean, boolean) */ @Override public void setRange(Range range, boolean turnOffAutoRange, boolean notify) { double size = range.getUpperBound() - range.getLowerBound(); if (size > this.period) { this.period = size; } super.setRange(range, turnOffAutoRange, notify); } /** * The cycle bound is defined as the higest value x such that * "offset + period * i = x", with i and integer and x < * range.getUpperBound() This is the value which is at both ends of the * axis : x...up|low...x * The values from x to up are the valued in the current cycle. * The values from low to x are the valued in the previous cycle. * * @return The cycle bound. */ public double getCycleBound() { return Math.floor( (getRange().getUpperBound() - this.offset) / this.period ) * this.period + this.offset; } /** * The cycle bound is a multiple of the period, plus optionally a start * offset. *
cb = n * period + offset
* * @return The current offset. * * @see #getCycleBound() */ public double getOffset() { return this.offset; } /** * The cycle bound is a multiple of the period, plus optionally a start * offset. *
cb = n * period + offset
* * @param offset The offset to set. * * @see #getCycleBound() */ public void setOffset(double offset) { this.offset = offset; } /** * The cycle bound is a multiple of the period, plus optionally a start * offset. *
cb = n * period + offset
* * @return The current period. * * @see #getCycleBound() */ public double getPeriod() { return this.period; } /** * The cycle bound is a multiple of the period, plus optionally a start * offset. *
cb = n * period + offset
* * @param period The period to set. * * @see #getCycleBound() */ public void setPeriod(double period) { this.period = period; } /** * Draws the tick marks and labels. * * @param g2 the graphics device. * @param cursor the cursor. * @param plotArea the plot area. * @param dataArea the area inside the axes. * @param edge the side on which the axis is displayed. * * @return The axis state. */ @Override protected AxisState drawTickMarksAndLabels(Graphics2D g2, double cursor, Rectangle2D plotArea, Rectangle2D dataArea, RectangleEdge edge) { this.internalMarkerWhenTicksOverlap = false; AxisState ret = super.drawTickMarksAndLabels(g2, cursor, plotArea, dataArea, edge); // continue and separate the labels only if necessary if (!this.internalMarkerWhenTicksOverlap) { return ret; } double ol; FontMetrics fm = g2.getFontMetrics(getTickLabelFont()); if (isVerticalTickLabels()) { ol = fm.getMaxAdvance(); } else { ol = fm.getHeight(); } double il = 0; if (isTickMarksVisible()) { float xx = (float) valueToJava2D(getRange().getUpperBound(), dataArea, edge); Line2D mark = null; g2.setStroke(getTickMarkStroke()); g2.setPaint(getTickMarkPaint()); if (edge == RectangleEdge.LEFT) { mark = new Line2D.Double(cursor - ol, xx, cursor + il, xx); } else if (edge == RectangleEdge.RIGHT) { mark = new Line2D.Double(cursor + ol, xx, cursor - il, xx); } else if (edge == RectangleEdge.TOP) { mark = new Line2D.Double(xx, cursor - ol, xx, cursor + il); } else if (edge == RectangleEdge.BOTTOM) { mark = new Line2D.Double(xx, cursor + ol, xx, cursor - il); } g2.draw(mark); } return ret; } /** * Draws the axis. * * @param g2 the graphics device ({@code null} not permitted). * @param cursor the cursor position. * @param plotArea the plot area ({@code null} not permitted). * @param dataArea the data area ({@code null} not permitted). * @param edge the edge ({@code null} not permitted). * @param plotState collects information about the plot * ({@code null} permitted). * * @return The axis state (never {@code null}). */ @Override public AxisState draw(Graphics2D g2, double cursor, Rectangle2D plotArea, Rectangle2D dataArea, RectangleEdge edge, PlotRenderingInfo plotState) { AxisState ret = super.draw(g2, cursor, plotArea, dataArea, edge, plotState); if (isAdvanceLineVisible()) { double xx = valueToJava2D(getRange().getUpperBound(), dataArea, edge); Line2D mark = null; g2.setStroke(getAdvanceLineStroke()); g2.setPaint(getAdvanceLinePaint()); if (edge == RectangleEdge.LEFT) { mark = new Line2D.Double(cursor, xx, cursor + dataArea.getWidth(), xx); } else if (edge == RectangleEdge.RIGHT) { mark = new Line2D.Double(cursor - dataArea.getWidth(), xx, cursor, xx); } else if (edge == RectangleEdge.TOP) { mark = new Line2D.Double(xx, cursor + dataArea.getHeight(), xx, cursor); } else if (edge == RectangleEdge.BOTTOM) { mark = new Line2D.Double(xx, cursor, xx, cursor - dataArea.getHeight()); } g2.draw(mark); } return ret; } /** * Reserve some space on each axis side because we draw a centered label at * each extremity. * * @param g2 the graphics device. * @param plot the plot. * @param plotArea the plot area. * @param edge the edge. * @param space the space already reserved. * * @return The reserved space. */ @Override public AxisSpace reserveSpace(Graphics2D g2, Plot plot, Rectangle2D plotArea, RectangleEdge edge, AxisSpace space) { this.internalMarkerCycleBoundTick = null; AxisSpace ret = super.reserveSpace(g2, plot, plotArea, edge, space); if (this.internalMarkerCycleBoundTick == null) { return ret; } FontMetrics fm = g2.getFontMetrics(getTickLabelFont()); Rectangle2D r = TextUtils.getTextBounds( this.internalMarkerCycleBoundTick.getText(), g2, fm ); if (RectangleEdge.isTopOrBottom(edge)) { if (isVerticalTickLabels()) { space.add(r.getHeight() / 2, RectangleEdge.RIGHT); } else { space.add(r.getWidth() / 2, RectangleEdge.RIGHT); } } else if (RectangleEdge.isLeftOrRight(edge)) { if (isVerticalTickLabels()) { space.add(r.getWidth() / 2, RectangleEdge.TOP); } else { space.add(r.getHeight() / 2, RectangleEdge.TOP); } } return ret; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.advanceLinePaint, stream); SerialUtils.writeStroke(this.advanceLineStroke, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.advanceLinePaint = SerialUtils.readPaint(stream); this.advanceLineStroke = SerialUtils.readStroke(stream); } /** * Tests the axis for equality with another object. * * @param obj the object to test against. * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof CyclicNumberAxis)) { return false; } if (!super.equals(obj)) { return false; } CyclicNumberAxis that = (CyclicNumberAxis) obj; if (this.period != that.period) { return false; } if (this.offset != that.offset) { return false; } if (!PaintUtils.equal(this.advanceLinePaint, that.advanceLinePaint)) { return false; } if (!Objects.equals(this.advanceLineStroke, that.advanceLineStroke)) { return false; } if (this.advanceLineVisible != that.advanceLineVisible) { return false; } if (this.boundMappedToLastCycle != that.boundMappedToLastCycle) { return false; } return true; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/DateAxis.java000066400000000000000000002031311463604235500263670ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------- * DateAxis.java * ------------- * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Jonathan Nash; * David Li; * Michael Rauch; * Bill Kelemen; * Pawel Pabis; * Chris Boek; * Peter Kolb (patches 1934255 and 2603321); * Andrew Mickish (patch 1870189); * Fawad Halim (bug 2201869); * */ package org.jfree.chart.axis; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.font.FontRenderContext; import java.awt.font.LineMetrics; import java.awt.geom.Rectangle2D; import java.io.Serializable; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.Objects; import java.util.TimeZone; import org.jfree.chart.event.AxisChangeEvent; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.ValueAxisPlot; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.ui.TextAnchor; import org.jfree.chart.util.Args; import org.jfree.data.Range; import org.jfree.data.time.DateRange; import org.jfree.data.time.Month; import org.jfree.data.time.RegularTimePeriod; import org.jfree.data.time.Year; /** * The base class for axes that display dates. You will find it easier to * understand how this axis works if you bear in mind that it really * displays/measures integer (or long) data, where the integers are * milliseconds since midnight, 1-Jan-1970. When displaying tick labels, the * millisecond values are converted back to dates using a {@code DateFormat} * instance. *

* You can also create a {@link org.jfree.chart.axis.Timeline} and supply in * the constructor to create an axis that only contains certain domain values. * For example, this allows you to create a date axis that only contains * working days. */ public class DateAxis extends ValueAxis implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -1013460999649007604L; /** The default axis range. */ public static final DateRange DEFAULT_DATE_RANGE = new DateRange(); /** The default minimum auto range size. */ public static final double DEFAULT_AUTO_RANGE_MINIMUM_SIZE_IN_MILLISECONDS = 2.0; /** The default anchor date. */ public static final Date DEFAULT_ANCHOR_DATE = new Date(); /** The current tick unit. */ private DateTickUnit tickUnit; /** The override date format. */ private DateFormat dateFormatOverride; /** * Tick marks can be displayed at the start or the middle of the time * period. */ private DateTickMarkPosition tickMarkPosition = DateTickMarkPosition.START; /** * A timeline that includes all milliseconds (as defined by * {@code java.util.Date}) in the real time line. */ private static class DefaultTimeline implements Timeline, Serializable { /** * Converts a millisecond into a timeline value. * * @param millisecond the millisecond. * * @return The timeline value. */ @Override public long toTimelineValue(long millisecond) { return millisecond; } /** * Converts a date into a timeline value. * * @param date the domain value. * * @return The timeline value. */ @Override public long toTimelineValue(Date date) { return date.getTime(); } /** * Converts a timeline value into a millisecond (as encoded by * {@code java.util.Date}). * * @param value the value. * * @return The millisecond. */ @Override public long toMillisecond(long value) { return value; } /** * Returns {@code true} if the timeline includes the specified * domain value. * * @param millisecond the millisecond. * * @return {@code true}. */ @Override public boolean containsDomainValue(long millisecond) { return true; } /** * Returns {@code true} if the timeline includes the specified * domain value. * * @param date the date. * * @return {@code true}. */ @Override public boolean containsDomainValue(Date date) { return true; } /** * Returns {@code true} if the timeline includes the specified * domain value range. * * @param from the start value. * @param to the end value. * * @return {@code true}. */ @Override public boolean containsDomainRange(long from, long to) { return true; } /** * Returns {@code true} if the timeline includes the specified * domain value range. * * @param from the start date. * @param to the end date. * * @return {@code true}. */ @Override public boolean containsDomainRange(Date from, Date to) { return true; } /** * Tests an object for equality with this instance. * * @param object the object. * * @return A boolean. */ @Override public boolean equals(Object object) { if (object == null) { return false; } if (object == this) { return true; } if (object instanceof DefaultTimeline) { return true; } return false; } } /** A static default timeline shared by all standard DateAxis */ private static final Timeline DEFAULT_TIMELINE = new DefaultTimeline(); /** The time zone for the axis. */ private TimeZone timeZone; /** * The locale for the axis ({@code null} is not permitted). */ private Locale locale; /** Our underlying timeline. */ private Timeline timeline; /** * Creates a date axis with no label. */ public DateAxis() { this(null); } /** * Creates a date axis with the specified label. * * @param label the axis label ({@code null} permitted). */ public DateAxis(String label) { this(label, TimeZone.getDefault(), Locale.getDefault()); } /** * Creates a date axis. * * @param label the axis label ({@code null} permitted). * @param zone the time zone. * @param locale the locale ({@code null} not permitted). */ public DateAxis(String label, TimeZone zone, Locale locale) { super(label, DateAxis.createStandardDateTickUnits(zone, locale)); this.tickUnit = new DateTickUnit(DateTickUnitType.DAY, 1, new SimpleDateFormat()); setAutoRangeMinimumSize( DEFAULT_AUTO_RANGE_MINIMUM_SIZE_IN_MILLISECONDS); setRange(DEFAULT_DATE_RANGE, false, false); this.dateFormatOverride = null; this.timeZone = zone; this.locale = locale; this.timeline = DEFAULT_TIMELINE; } /** * Returns the time zone for the axis. * * @return The time zone (never {@code null}). * * @see #setTimeZone(TimeZone) */ public TimeZone getTimeZone() { return this.timeZone; } /** * Sets the time zone for the axis and sends an {@link AxisChangeEvent} to * all registered listeners. * * @param zone the time zone ({@code null} not permitted). * * @see #getTimeZone() */ public void setTimeZone(TimeZone zone) { Args.nullNotPermitted(zone, "zone"); this.timeZone = zone; setStandardTickUnits(createStandardDateTickUnits(zone, this.locale)); fireChangeEvent(); } /** * Returns the locale for this axis. * * @return The locale (never {@code null}). */ public Locale getLocale() { return this.locale; } /** * Sets the locale for the axis and sends a change event to all registered * listeners. * * @param locale the new locale ({@code null} not permitted). */ public void setLocale(Locale locale) { Args.nullNotPermitted(locale, "locale"); this.locale = locale; setStandardTickUnits(createStandardDateTickUnits(this.timeZone, this.locale)); fireChangeEvent(); } /** * Returns the underlying timeline used by this axis. * * @return The timeline. */ public Timeline getTimeline() { return this.timeline; } /** * Sets the underlying timeline to use for this axis. If the timeline is * changed, an {@link AxisChangeEvent} is sent to all registered listeners. * * @param timeline the timeline. */ public void setTimeline(Timeline timeline) { if (this.timeline != timeline) { this.timeline = timeline; fireChangeEvent(); } } /** * Returns the tick unit for the axis. *

* Note: if the {@code autoTickUnitSelection} flag is * {@code true} the tick unit may be changed while the axis is being * drawn, so in that case the return value from this method may be * irrelevant if the method is called before the axis has been drawn. * * @return The tick unit (possibly {@code null}). * * @see #setTickUnit(DateTickUnit) * @see ValueAxis#isAutoTickUnitSelection() */ public DateTickUnit getTickUnit() { return this.tickUnit; } /** * Sets the tick unit for the axis. The auto-tick-unit-selection flag is * set to {@code false}, and registered listeners are notified that * the axis has been changed. * * @param unit the tick unit. * * @see #getTickUnit() * @see #setTickUnit(DateTickUnit, boolean, boolean) */ public void setTickUnit(DateTickUnit unit) { setTickUnit(unit, true, true); } /** * Sets the tick unit attribute and, if requested, sends an * {@link AxisChangeEvent} to all registered listeners. * * @param unit the new tick unit. * @param notify notify registered listeners? * @param turnOffAutoSelection turn off auto selection? * * @see #getTickUnit() */ public void setTickUnit(DateTickUnit unit, boolean notify, boolean turnOffAutoSelection) { this.tickUnit = unit; if (turnOffAutoSelection) { setAutoTickUnitSelection(false, false); } if (notify) { fireChangeEvent(); } } /** * Returns the date format override. If this is non-null, then it will be * used to format the dates on the axis. * * @return The formatter (possibly {@code null}). */ public DateFormat getDateFormatOverride() { return this.dateFormatOverride; } /** * Sets the date format override and sends an {@link AxisChangeEvent} to * all registered listeners. If this is non-null, then it will be * used to format the dates on the axis. * * @param formatter the date formatter ({@code null} permitted). */ public void setDateFormatOverride(DateFormat formatter) { this.dateFormatOverride = formatter; fireChangeEvent(); } /** * Sets the upper and lower bounds for the axis and sends an * {@link AxisChangeEvent} to all registered listeners. As a side-effect, * the auto-range flag is set to false. * * @param range the new range ({@code null} not permitted). */ @Override public void setRange(Range range) { setRange(range, true, true); } /** * Sets the range for the axis, if requested, sends an * {@link AxisChangeEvent} to all registered listeners. As a side-effect, * the auto-range flag is set to {@code false} (optional). * * @param range the range ({@code null} not permitted). * @param turnOffAutoRange a flag that controls whether or not the auto * range is turned off. * @param notify a flag that controls whether or not listeners are * notified. */ @Override public void setRange(Range range, boolean turnOffAutoRange, boolean notify) { Args.nullNotPermitted(range, "range"); // usually the range will be a DateRange, but if it isn't do a // conversion... if (!(range instanceof DateRange)) { range = new DateRange(range); } super.setRange(range, turnOffAutoRange, notify); } /** * Sets the axis range and sends an {@link AxisChangeEvent} to all * registered listeners. * * @param lower the lower bound for the axis. * @param upper the upper bound for the axis. */ public void setRange(Date lower, Date upper) { if (lower.getTime() >= upper.getTime()) { throw new IllegalArgumentException("Requires 'lower' < 'upper'."); } setRange(new DateRange(lower, upper)); } /** * Sets the axis range and sends an {@link AxisChangeEvent} to all * registered listeners. * * @param lower the lower bound for the axis. * @param upper the upper bound for the axis. */ @Override public void setRange(double lower, double upper) { if (lower >= upper) { throw new IllegalArgumentException("Requires 'lower' < 'upper'."); } setRange(new DateRange(lower, upper)); } /** * Returns the earliest date visible on the axis. * * @return The date. * * @see #setMinimumDate(Date) * @see #getMaximumDate() */ public Date getMinimumDate() { Date result; Range range = getRange(); if (range instanceof DateRange) { DateRange r = (DateRange) range; result = r.getLowerDate(); } else { result = new Date((long) range.getLowerBound()); } return result; } /** * Sets the minimum date visible on the axis and sends an * {@link AxisChangeEvent} to all registered listeners. If * {@code date} is on or after the current maximum date for * the axis, the maximum date will be shifted to preserve the current * length of the axis. * * @param date the date ({@code null} not permitted). * * @see #getMinimumDate() * @see #setMaximumDate(Date) */ public void setMinimumDate(Date date) { Args.nullNotPermitted(date, "date"); // check the new minimum date relative to the current maximum date Date maxDate = getMaximumDate(); long maxMillis = maxDate.getTime(); long newMinMillis = date.getTime(); if (maxMillis <= newMinMillis) { Date oldMin = getMinimumDate(); long length = maxMillis - oldMin.getTime(); maxDate = new Date(newMinMillis + length); } setRange(new DateRange(date, maxDate), true, false); fireChangeEvent(); } /** * Returns the latest date visible on the axis. * * @return The date. * * @see #setMaximumDate(Date) * @see #getMinimumDate() */ public Date getMaximumDate() { Date result; Range range = getRange(); if (range instanceof DateRange) { DateRange r = (DateRange) range; result = r.getUpperDate(); } else { result = new Date((long) range.getUpperBound()); } return result; } /** * Sets the maximum date visible on the axis and sends an * {@link AxisChangeEvent} to all registered listeners. If * {@code maximumDate} is on or before the current minimum date for * the axis, the minimum date will be shifted to preserve the current * length of the axis. * * @param maximumDate the date ({@code null} not permitted). * * @see #getMinimumDate() * @see #setMinimumDate(Date) */ public void setMaximumDate(Date maximumDate) { Args.nullNotPermitted(maximumDate, "maximumDate"); // check the new maximum date relative to the current minimum date Date minDate = getMinimumDate(); long minMillis = minDate.getTime(); long newMaxMillis = maximumDate.getTime(); if (minMillis >= newMaxMillis) { Date oldMax = getMaximumDate(); long length = oldMax.getTime() - minMillis; minDate = new Date(newMaxMillis - length); } setRange(new DateRange(minDate, maximumDate), true, false); fireChangeEvent(); } /** * Returns the tick mark position (start, middle or end of the time period). * * @return The position (never {@code null}). */ public DateTickMarkPosition getTickMarkPosition() { return this.tickMarkPosition; } /** * Sets the tick mark position (start, middle or end of the time period) * and sends an {@link AxisChangeEvent} to all registered listeners. * * @param position the position ({@code null} not permitted). */ public void setTickMarkPosition(DateTickMarkPosition position) { Args.nullNotPermitted(position, "position"); this.tickMarkPosition = position; fireChangeEvent(); } /** * Configures the axis to work with the specified plot. If the axis has * auto-scaling, then sets the maximum and minimum values. */ @Override public void configure() { if (isAutoRange()) { autoAdjustRange(); } } /** * Returns {@code true} if the axis hides this value, and * {@code false} otherwise. * * @param millis the data value. * * @return A value. */ public boolean isHiddenValue(long millis) { return (!this.timeline.containsDomainValue(new Date(millis))); } /** * Translates the data value to the display coordinates (Java 2D User Space) * of the chart. * * @param value the date to be plotted. * @param area the rectangle (in Java2D space) where the data is to be * plotted. * @param edge the axis location. * * @return The coordinate corresponding to the supplied data value. */ @Override public double valueToJava2D(double value, Rectangle2D area, RectangleEdge edge) { value = this.timeline.toTimelineValue((long) value); DateRange range = (DateRange) getRange(); double axisMin = this.timeline.toTimelineValue(range.getLowerMillis()); double axisMax = this.timeline.toTimelineValue(range.getUpperMillis()); double result = 0.0; if (RectangleEdge.isTopOrBottom(edge)) { double minX = area.getX(); double maxX = area.getMaxX(); if (isInverted()) { result = maxX + ((value - axisMin) / (axisMax - axisMin)) * (minX - maxX); } else { result = minX + ((value - axisMin) / (axisMax - axisMin)) * (maxX - minX); } } else if (RectangleEdge.isLeftOrRight(edge)) { double minY = area.getMinY(); double maxY = area.getMaxY(); if (isInverted()) { result = minY + (((value - axisMin) / (axisMax - axisMin)) * (maxY - minY)); } else { result = maxY - (((value - axisMin) / (axisMax - axisMin)) * (maxY - minY)); } } return result; } /** * Translates a date to Java2D coordinates, based on the range displayed by * this axis for the specified data area. * * @param date the date. * @param area the rectangle (in Java2D space) where the data is to be * plotted. * @param edge the axis location. * * @return The coordinate corresponding to the supplied date. */ public double dateToJava2D(Date date, Rectangle2D area, RectangleEdge edge) { double value = date.getTime(); return valueToJava2D(value, area, edge); } /** * Translates a Java2D coordinate into the corresponding data value. To * perform this translation, you need to know the area used for plotting * data, and which edge the axis is located on. * * @param java2DValue the coordinate in Java2D space. * @param area the rectangle (in Java2D space) where the data is to be * plotted. * @param edge the axis location. * * @return A data value. */ @Override public double java2DToValue(double java2DValue, Rectangle2D area, RectangleEdge edge) { DateRange range = (DateRange) getRange(); double axisMin = this.timeline.toTimelineValue(range.getLowerMillis()); double axisMax = this.timeline.toTimelineValue(range.getUpperMillis()); double min = 0.0; double max = 0.0; if (RectangleEdge.isTopOrBottom(edge)) { min = area.getX(); max = area.getMaxX(); } else if (RectangleEdge.isLeftOrRight(edge)) { min = area.getMaxY(); max = area.getY(); } double result; if (isInverted()) { result = axisMax - ((java2DValue - min) / (max - min) * (axisMax - axisMin)); } else { result = axisMin + ((java2DValue - min) / (max - min) * (axisMax - axisMin)); } return this.timeline.toMillisecond((long) result); } /** * Calculates the value of the lowest visible tick on the axis. * * @param unit date unit to use. * * @return The value of the lowest visible tick on the axis. */ public Date calculateLowestVisibleTickValue(DateTickUnit unit) { return nextStandardDate(getMinimumDate(), unit); } /** * Calculates the value of the highest visible tick on the axis. * * @param unit date unit to use. * * @return The value of the highest visible tick on the axis. */ public Date calculateHighestVisibleTickValue(DateTickUnit unit) { return previousStandardDate(getMaximumDate(), unit); } /** * Returns the previous "standard" date, for a given date and tick unit. * * @param date the reference date. * @param unit the tick unit. * * @return The previous "standard" date. */ protected Date previousStandardDate(Date date, DateTickUnit unit) { int milliseconds; int seconds; int minutes; int hours; int days; int months; int years; Calendar calendar = Calendar.getInstance(this.timeZone, this.locale); calendar.setTime(date); int count = unit.getMultiple(); int current = calendar.get(unit.getCalendarField()); int value = count * (current / count); if (DateTickUnitType.MILLISECOND.equals(unit.getUnitType())) { years = calendar.get(Calendar.YEAR); months = calendar.get(Calendar.MONTH); days = calendar.get(Calendar.DATE); hours = calendar.get(Calendar.HOUR_OF_DAY); minutes = calendar.get(Calendar.MINUTE); seconds = calendar.get(Calendar.SECOND); calendar.set(years, months, days, hours, minutes, seconds); calendar.set(Calendar.MILLISECOND, value); Date mm = calendar.getTime(); if (mm.getTime() >= date.getTime()) { calendar.set(Calendar.MILLISECOND, value - count); mm = calendar.getTime(); } return mm; } else if (DateTickUnitType.SECOND.equals(unit.getUnitType())) { years = calendar.get(Calendar.YEAR); months = calendar.get(Calendar.MONTH); days = calendar.get(Calendar.DATE); hours = calendar.get(Calendar.HOUR_OF_DAY); minutes = calendar.get(Calendar.MINUTE); if (this.tickMarkPosition == DateTickMarkPosition.START) { milliseconds = 0; } else if (this.tickMarkPosition == DateTickMarkPosition.MIDDLE) { milliseconds = 500; } else { milliseconds = 999; } calendar.set(Calendar.MILLISECOND, milliseconds); calendar.set(years, months, days, hours, minutes, value); Date dd = calendar.getTime(); if (dd.getTime() >= date.getTime()) { calendar.set(Calendar.SECOND, value - count); dd = calendar.getTime(); } return dd; } else if (DateTickUnitType.MINUTE.equals(unit.getUnitType())) { years = calendar.get(Calendar.YEAR); months = calendar.get(Calendar.MONTH); days = calendar.get(Calendar.DATE); hours = calendar.get(Calendar.HOUR_OF_DAY); if (this.tickMarkPosition == DateTickMarkPosition.START) { seconds = 0; } else if (this.tickMarkPosition == DateTickMarkPosition.MIDDLE) { seconds = 30; } else { seconds = 59; } calendar.clear(Calendar.MILLISECOND); calendar.set(years, months, days, hours, value, seconds); Date d0 = calendar.getTime(); if (d0.getTime() >= date.getTime()) { calendar.set(Calendar.MINUTE, value - count); d0 = calendar.getTime(); } return d0; } else if (DateTickUnitType.HOUR.equals(unit.getUnitType())) { years = calendar.get(Calendar.YEAR); months = calendar.get(Calendar.MONTH); days = calendar.get(Calendar.DATE); if (this.tickMarkPosition == DateTickMarkPosition.START) { minutes = 0; seconds = 0; } else if (this.tickMarkPosition == DateTickMarkPosition.MIDDLE) { minutes = 30; seconds = 0; } else { minutes = 59; seconds = 59; } calendar.clear(Calendar.MILLISECOND); calendar.set(years, months, days, value, minutes, seconds); Date d1 = calendar.getTime(); if (d1.getTime() >= date.getTime()) { calendar.set(Calendar.HOUR_OF_DAY, value - count); d1 = calendar.getTime(); } return d1; } else if (DateTickUnitType.DAY.equals(unit.getUnitType())) { years = calendar.get(Calendar.YEAR); months = calendar.get(Calendar.MONTH); if (this.tickMarkPosition == DateTickMarkPosition.START) { hours = 0; } else if (this.tickMarkPosition == DateTickMarkPosition.MIDDLE) { hours = 12; } else { hours = 23; } calendar.clear(Calendar.MILLISECOND); calendar.set(years, months, value, hours, 0, 0); // long result = calendar.getTimeInMillis(); // won't work with JDK 1.3 Date d2 = calendar.getTime(); if (d2.getTime() >= date.getTime()) { calendar.set(Calendar.DATE, value - count); d2 = calendar.getTime(); } return d2; } else if (DateTickUnitType.MONTH.equals(unit.getUnitType())) { value = count * ((current + 1) / count) - 1; years = calendar.get(Calendar.YEAR); calendar.clear(Calendar.MILLISECOND); calendar.set(years, value, 1, 0, 0, 0); Month month = new Month(calendar.getTime(), this.timeZone, this.locale); Date standardDate = calculateDateForPosition( month, this.tickMarkPosition); long millis = standardDate.getTime(); if (millis >= date.getTime()) { for (int i = 0; i < count; i++) { month = (Month) month.previous(); } // need to peg the month in case the time zone isn't the // default - see bug 2078057 month.peg(Calendar.getInstance(this.timeZone)); standardDate = calculateDateForPosition( month, this.tickMarkPosition); } return standardDate; } else if (DateTickUnitType.YEAR.equals(unit.getUnitType())) { if (this.tickMarkPosition == DateTickMarkPosition.START) { months = 0; days = 1; } else if (this.tickMarkPosition == DateTickMarkPosition.MIDDLE) { months = 6; days = 1; } else { months = 11; days = 31; } calendar.clear(Calendar.MILLISECOND); calendar.set(value, months, days, 0, 0, 0); Date d3 = calendar.getTime(); if (d3.getTime() >= date.getTime()) { calendar.set(Calendar.YEAR, value - count); d3 = calendar.getTime(); } return d3; } return null; } /** * Returns a {@link java.util.Date} corresponding to the specified position * within a {@link RegularTimePeriod}. * * @param period the period. * @param position the position ({@code null} not permitted). * * @return A date. */ private Date calculateDateForPosition(RegularTimePeriod period, DateTickMarkPosition position) { Args.nullNotPermitted(period, "period"); Date result = null; if (position == DateTickMarkPosition.START) { result = new Date(period.getFirstMillisecond()); } else if (position == DateTickMarkPosition.MIDDLE) { result = new Date(period.getMiddleMillisecond()); } else if (position == DateTickMarkPosition.END) { result = new Date(period.getLastMillisecond()); } return result; } /** * Returns the first "standard" date (based on the specified field and * units). * * @param date the reference date. * @param unit the date tick unit. * * @return The next "standard" date. */ protected Date nextStandardDate(Date date, DateTickUnit unit) { Date previous = previousStandardDate(date, unit); Calendar calendar = Calendar.getInstance(this.timeZone, this.locale); calendar.setTime(previous); calendar.add(unit.getCalendarField(), unit.getMultiple()); return calendar.getTime(); } /** * Returns a collection of standard date tick units that uses the default * time zone. This collection will be used by default, but you are free * to create your own collection if you want to (see the * {@link ValueAxis#setStandardTickUnits(TickUnitSource)} method inherited * from the {@link ValueAxis} class). * * @return A collection of standard date tick units. */ public static TickUnitSource createStandardDateTickUnits() { return createStandardDateTickUnits(TimeZone.getDefault(), Locale.getDefault()); } /** * Returns a collection of standard date tick units. This collection will * be used by default, but you are free to create your own collection if * you want to (see the * {@link ValueAxis#setStandardTickUnits(TickUnitSource)} method inherited * from the {@link ValueAxis} class). * * @param zone the time zone ({@code null} not permitted). * @param locale the locale ({@code null} not permitted). * * @return A collection of standard date tick units. */ public static TickUnitSource createStandardDateTickUnits(TimeZone zone, Locale locale) { Args.nullNotPermitted(zone, "zone"); Args.nullNotPermitted(locale, "locale"); TickUnits units = new TickUnits(); // date formatters DateFormat f1 = new SimpleDateFormat("HH:mm:ss.SSS", locale); DateFormat f2 = new SimpleDateFormat("HH:mm:ss", locale); DateFormat f3 = new SimpleDateFormat("HH:mm", locale); DateFormat f4 = new SimpleDateFormat("d-MMM, HH:mm", locale); DateFormat f5 = new SimpleDateFormat("d-MMM", locale); DateFormat f6 = new SimpleDateFormat("MMM-yyyy", locale); DateFormat f7 = new SimpleDateFormat("yyyy", locale); f1.setTimeZone(zone); f2.setTimeZone(zone); f3.setTimeZone(zone); f4.setTimeZone(zone); f5.setTimeZone(zone); f6.setTimeZone(zone); f7.setTimeZone(zone); // milliseconds units.add(new DateTickUnit(DateTickUnitType.MILLISECOND, 1, f1)); units.add(new DateTickUnit(DateTickUnitType.MILLISECOND, 5, DateTickUnitType.MILLISECOND, 1, f1)); units.add(new DateTickUnit(DateTickUnitType.MILLISECOND, 10, DateTickUnitType.MILLISECOND, 1, f1)); units.add(new DateTickUnit(DateTickUnitType.MILLISECOND, 25, DateTickUnitType.MILLISECOND, 5, f1)); units.add(new DateTickUnit(DateTickUnitType.MILLISECOND, 50, DateTickUnitType.MILLISECOND, 10, f1)); units.add(new DateTickUnit(DateTickUnitType.MILLISECOND, 100, DateTickUnitType.MILLISECOND, 10, f1)); units.add(new DateTickUnit(DateTickUnitType.MILLISECOND, 250, DateTickUnitType.MILLISECOND, 10, f1)); units.add(new DateTickUnit(DateTickUnitType.MILLISECOND, 500, DateTickUnitType.MILLISECOND, 50, f1)); // seconds units.add(new DateTickUnit(DateTickUnitType.SECOND, 1, DateTickUnitType.MILLISECOND, 50, f2)); units.add(new DateTickUnit(DateTickUnitType.SECOND, 5, DateTickUnitType.SECOND, 1, f2)); units.add(new DateTickUnit(DateTickUnitType.SECOND, 10, DateTickUnitType.SECOND, 1, f2)); units.add(new DateTickUnit(DateTickUnitType.SECOND, 30, DateTickUnitType.SECOND, 5, f2)); // minutes units.add(new DateTickUnit(DateTickUnitType.MINUTE, 1, DateTickUnitType.SECOND, 5, f3)); units.add(new DateTickUnit(DateTickUnitType.MINUTE, 2, DateTickUnitType.SECOND, 10, f3)); units.add(new DateTickUnit(DateTickUnitType.MINUTE, 5, DateTickUnitType.MINUTE, 1, f3)); units.add(new DateTickUnit(DateTickUnitType.MINUTE, 10, DateTickUnitType.MINUTE, 1, f3)); units.add(new DateTickUnit(DateTickUnitType.MINUTE, 15, DateTickUnitType.MINUTE, 5, f3)); units.add(new DateTickUnit(DateTickUnitType.MINUTE, 20, DateTickUnitType.MINUTE, 5, f3)); units.add(new DateTickUnit(DateTickUnitType.MINUTE, 30, DateTickUnitType.MINUTE, 5, f3)); // hours units.add(new DateTickUnit(DateTickUnitType.HOUR, 1, DateTickUnitType.MINUTE, 5, f3)); units.add(new DateTickUnit(DateTickUnitType.HOUR, 2, DateTickUnitType.MINUTE, 10, f3)); units.add(new DateTickUnit(DateTickUnitType.HOUR, 4, DateTickUnitType.MINUTE, 30, f3)); units.add(new DateTickUnit(DateTickUnitType.HOUR, 6, DateTickUnitType.HOUR, 1, f3)); units.add(new DateTickUnit(DateTickUnitType.HOUR, 12, DateTickUnitType.HOUR, 1, f4)); // days units.add(new DateTickUnit(DateTickUnitType.DAY, 1, DateTickUnitType.HOUR, 1, f5)); units.add(new DateTickUnit(DateTickUnitType.DAY, 2, DateTickUnitType.HOUR, 1, f5)); units.add(new DateTickUnit(DateTickUnitType.DAY, 7, DateTickUnitType.DAY, 1, f5)); units.add(new DateTickUnit(DateTickUnitType.DAY, 15, DateTickUnitType.DAY, 1, f5)); // months units.add(new DateTickUnit(DateTickUnitType.MONTH, 1, DateTickUnitType.DAY, 1, f6)); units.add(new DateTickUnit(DateTickUnitType.MONTH, 2, DateTickUnitType.DAY, 1, f6)); units.add(new DateTickUnit(DateTickUnitType.MONTH, 3, DateTickUnitType.MONTH, 1, f6)); units.add(new DateTickUnit(DateTickUnitType.MONTH, 4, DateTickUnitType.MONTH, 1, f6)); units.add(new DateTickUnit(DateTickUnitType.MONTH, 6, DateTickUnitType.MONTH, 1, f6)); // years units.add(new DateTickUnit(DateTickUnitType.YEAR, 1, DateTickUnitType.MONTH, 1, f7)); units.add(new DateTickUnit(DateTickUnitType.YEAR, 2, DateTickUnitType.MONTH, 3, f7)); units.add(new DateTickUnit(DateTickUnitType.YEAR, 5, DateTickUnitType.YEAR, 1, f7)); units.add(new DateTickUnit(DateTickUnitType.YEAR, 10, DateTickUnitType.YEAR, 1, f7)); units.add(new DateTickUnit(DateTickUnitType.YEAR, 25, DateTickUnitType.YEAR, 5, f7)); units.add(new DateTickUnit(DateTickUnitType.YEAR, 50, DateTickUnitType.YEAR, 10, f7)); units.add(new DateTickUnit(DateTickUnitType.YEAR, 100, DateTickUnitType.YEAR, 20, f7)); return units; } /** * Rescales the axis to ensure that all data is visible. */ @Override protected void autoAdjustRange() { Plot plot = getPlot(); if (plot == null) { return; // no plot, no data } if (plot instanceof ValueAxisPlot) { ValueAxisPlot vap = (ValueAxisPlot) plot; Range r = vap.getDataRange(this); if (r == null) { r = new DateRange(); } long upper = this.timeline.toTimelineValue( (long) r.getUpperBound()); long lower; long fixedAutoRange = (long) getFixedAutoRange(); if (fixedAutoRange > 0.0) { lower = upper - fixedAutoRange; } else { lower = this.timeline.toTimelineValue((long) r.getLowerBound()); double range = upper - lower; long minRange = (long) getAutoRangeMinimumSize(); if (range < minRange) { long expand = (long) (minRange - range) / 2; upper = upper + expand; lower = lower - expand; } upper = upper + (long) (range * getUpperMargin()); lower = lower - (long) (range * getLowerMargin()); } upper = this.timeline.toMillisecond(upper); lower = this.timeline.toMillisecond(lower); DateRange dr = new DateRange(new Date(lower), new Date(upper)); setRange(dr, false, false); } } /** * Selects an appropriate tick value for the axis. The strategy is to * display as many ticks as possible (selected from an array of 'standard' * tick units) without the labels overlapping. * * @param g2 the graphics device. * @param dataArea the area defined by the axes. * @param edge the axis location. */ protected void selectAutoTickUnit(Graphics2D g2, Rectangle2D dataArea, RectangleEdge edge) { if (RectangleEdge.isTopOrBottom(edge)) { selectHorizontalAutoTickUnit(g2, dataArea, edge); } else if (RectangleEdge.isLeftOrRight(edge)) { selectVerticalAutoTickUnit(g2, dataArea, edge); } } /** * Selects an appropriate tick size for the axis. The strategy is to * display as many ticks as possible (selected from a collection of * 'standard' tick units) without the labels overlapping. * * @param g2 the graphics device. * @param dataArea the area defined by the axes. * @param edge the axis location. */ protected void selectHorizontalAutoTickUnit(Graphics2D g2, Rectangle2D dataArea, RectangleEdge edge) { double zero = valueToJava2D(0.0, dataArea, edge); double tickLabelWidth = estimateMaximumTickLabelWidth(g2, getTickUnit()); // start with the current tick unit... TickUnitSource tickUnits = getStandardTickUnits(); TickUnit unit1 = tickUnits.getCeilingTickUnit(getTickUnit()); double x1 = valueToJava2D(unit1.getSize(), dataArea, edge); double unit1Width = Math.abs(x1 - zero); // then extrapolate... double guess = (tickLabelWidth / unit1Width) * unit1.getSize(); DateTickUnit unit2 = (DateTickUnit) tickUnits.getCeilingTickUnit(guess); double x2 = valueToJava2D(unit2.getSize(), dataArea, edge); double unit2Width = Math.abs(x2 - zero); tickLabelWidth = estimateMaximumTickLabelWidth(g2, unit2); if (tickLabelWidth > unit2Width) { unit2 = (DateTickUnit) tickUnits.getLargerTickUnit(unit2); } setTickUnit(unit2, false, false); } /** * Selects an appropriate tick size for the axis. The strategy is to * display as many ticks as possible (selected from a collection of * 'standard' tick units) without the labels overlapping. * * @param g2 the graphics device. * @param dataArea the area in which the plot should be drawn. * @param edge the axis location. */ protected void selectVerticalAutoTickUnit(Graphics2D g2, Rectangle2D dataArea, RectangleEdge edge) { // start with the current tick unit... TickUnitSource tickUnits = getStandardTickUnits(); double zero = valueToJava2D(0.0, dataArea, edge); // start with a unit that is at least 1/10th of the axis length double estimate1 = getRange().getLength() / 10.0; DateTickUnit candidate1 = (DateTickUnit) tickUnits.getCeilingTickUnit(estimate1); double labelHeight1 = estimateMaximumTickLabelHeight(g2, candidate1); double y1 = valueToJava2D(candidate1.getSize(), dataArea, edge); double candidate1UnitHeight = Math.abs(y1 - zero); // now extrapolate based on label height and unit height... double estimate2 = (labelHeight1 / candidate1UnitHeight) * candidate1.getSize(); DateTickUnit candidate2 = (DateTickUnit) tickUnits.getCeilingTickUnit(estimate2); double labelHeight2 = estimateMaximumTickLabelHeight(g2, candidate2); double y2 = valueToJava2D(candidate2.getSize(), dataArea, edge); double unit2Height = Math.abs(y2 - zero); // make final selection... DateTickUnit finalUnit; if (labelHeight2 < unit2Height) { finalUnit = candidate2; } else { finalUnit = (DateTickUnit) tickUnits.getLargerTickUnit(candidate2); } setTickUnit(finalUnit, false, false); } /** * Estimates the maximum width of the tick labels, assuming the specified * tick unit is used. *

* Rather than computing the string bounds of every tick on the axis, we * just look at two values: the lower bound and the upper bound for the * axis. These two values will usually be representative. * * @param g2 the graphics device. * @param unit the tick unit to use for calculation. * * @return The estimated maximum width of the tick labels. */ private double estimateMaximumTickLabelWidth(Graphics2D g2, DateTickUnit unit) { RectangleInsets tickLabelInsets = getTickLabelInsets(); double result = tickLabelInsets.getLeft() + tickLabelInsets.getRight(); Font tickLabelFont = getTickLabelFont(); FontRenderContext frc = g2.getFontRenderContext(); LineMetrics lm = tickLabelFont.getLineMetrics("ABCxyz", frc); if (isVerticalTickLabels()) { // all tick labels have the same width (equal to the height of // the font)... result += lm.getHeight(); } else { // look at lower and upper bounds... DateRange range = (DateRange) getRange(); Date lower = range.getLowerDate(); Date upper = range.getUpperDate(); String lowerStr, upperStr; DateFormat formatter = getDateFormatOverride(); if (formatter != null) { lowerStr = formatter.format(lower); upperStr = formatter.format(upper); } else { lowerStr = unit.dateToString(lower); upperStr = unit.dateToString(upper); } FontMetrics fm = g2.getFontMetrics(tickLabelFont); double w1 = fm.stringWidth(lowerStr); double w2 = fm.stringWidth(upperStr); result += Math.max(w1, w2); } return result; } /** * Estimates the maximum width of the tick labels, assuming the specified * tick unit is used. *

* Rather than computing the string bounds of every tick on the axis, we * just look at two values: the lower bound and the upper bound for the * axis. These two values will usually be representative. * * @param g2 the graphics device. * @param unit the tick unit to use for calculation. * * @return The estimated maximum width of the tick labels. */ private double estimateMaximumTickLabelHeight(Graphics2D g2, DateTickUnit unit) { RectangleInsets tickLabelInsets = getTickLabelInsets(); double result = tickLabelInsets.getTop() + tickLabelInsets.getBottom(); Font tickLabelFont = getTickLabelFont(); FontRenderContext frc = g2.getFontRenderContext(); LineMetrics lm = tickLabelFont.getLineMetrics("ABCxyz", frc); if (!isVerticalTickLabels()) { // all tick labels have the same width (equal to the height of // the font)... result += lm.getHeight(); } else { // look at lower and upper bounds... DateRange range = (DateRange) getRange(); Date lower = range.getLowerDate(); Date upper = range.getUpperDate(); String lowerStr, upperStr; DateFormat formatter = getDateFormatOverride(); if (formatter != null) { lowerStr = formatter.format(lower); upperStr = formatter.format(upper); } else { lowerStr = unit.dateToString(lower); upperStr = unit.dateToString(upper); } FontMetrics fm = g2.getFontMetrics(tickLabelFont); double w1 = fm.stringWidth(lowerStr); double w2 = fm.stringWidth(upperStr); result += Math.max(w1, w2); } return result; } /** * Calculates the positions of the tick labels for the axis, storing the * results in the tick label list (ready for drawing). * * @param g2 the graphics device. * @param state the axis state. * @param dataArea the area in which the plot should be drawn. * @param edge the location of the axis. * * @return A list of ticks. */ @Override public List refreshTicks(Graphics2D g2, AxisState state, Rectangle2D dataArea, RectangleEdge edge) { List result = null; if (RectangleEdge.isTopOrBottom(edge)) { result = refreshTicksHorizontal(g2, dataArea, edge); } else if (RectangleEdge.isLeftOrRight(edge)) { result = refreshTicksVertical(g2, dataArea, edge); } return result; } /** * Corrects the given tick date for the position setting. * * @param time the tick date/time. * @param unit the tick unit. * @param position the tick position. * * @return The adjusted time. */ private Date correctTickDateForPosition(Date time, DateTickUnit unit, DateTickMarkPosition position) { Date result = time; if (unit.getUnitType().equals(DateTickUnitType.MONTH)) { result = calculateDateForPosition(new Month(time, this.timeZone, this.locale), position); } else if (unit.getUnitType().equals(DateTickUnitType.YEAR)) { result = calculateDateForPosition(new Year(time, this.timeZone, this.locale), position); } return result; } /** * Recalculates the ticks for the date axis. * * @param g2 the graphics device. * @param dataArea the area in which the data is to be drawn. * @param edge the location of the axis. * * @return A list of ticks. */ protected List refreshTicksHorizontal(Graphics2D g2, Rectangle2D dataArea, RectangleEdge edge) { List result = new java.util.ArrayList(); Font tickLabelFont = getTickLabelFont(); g2.setFont(tickLabelFont); if (isAutoTickUnitSelection()) { selectAutoTickUnit(g2, dataArea, edge); } DateTickUnit unit = getTickUnit(); Date tickDate = calculateLowestVisibleTickValue(unit); Date upperDate = getMaximumDate(); boolean hasRolled = false; while (tickDate.before(upperDate)) { // could add a flag to make the following correction optional... if (!hasRolled) { tickDate = correctTickDateForPosition(tickDate, unit, this.tickMarkPosition); } long lowestTickTime = tickDate.getTime(); long distance = unit.addToDate(tickDate, this.timeZone).getTime() - lowestTickTime; int minorTickSpaces = getMinorTickCount(); if (minorTickSpaces <= 0) { minorTickSpaces = unit.getMinorTickCount(); } for (int minorTick = 1; minorTick < minorTickSpaces; minorTick++) { long minorTickTime = lowestTickTime - distance * minorTick / minorTickSpaces; if (minorTickTime > 0 && getRange().contains(minorTickTime) && (!isHiddenValue(minorTickTime))) { result.add(new DateTick(TickType.MINOR, new Date(minorTickTime), "", TextAnchor.TOP_CENTER, TextAnchor.CENTER, 0.0)); } } if (!isHiddenValue(tickDate.getTime())) { // work out the value, label and position String tickLabel; DateFormat formatter = getDateFormatOverride(); if (formatter != null) { tickLabel = formatter.format(tickDate); } else { tickLabel = this.tickUnit.dateToString(tickDate); } TextAnchor anchor, rotationAnchor; double angle = 0.0; if (isVerticalTickLabels()) { anchor = TextAnchor.CENTER_RIGHT; rotationAnchor = TextAnchor.CENTER_RIGHT; if (edge == RectangleEdge.TOP) { angle = Math.PI / 2.0; } else { angle = -Math.PI / 2.0; } } else { if (edge == RectangleEdge.TOP) { anchor = TextAnchor.BOTTOM_CENTER; rotationAnchor = TextAnchor.BOTTOM_CENTER; } else { anchor = TextAnchor.TOP_CENTER; rotationAnchor = TextAnchor.TOP_CENTER; } } Tick tick = new DateTick(tickDate, tickLabel, anchor, rotationAnchor, angle); result.add(tick); hasRolled = false; long currentTickTime = tickDate.getTime(); tickDate = unit.addToDate(tickDate, this.timeZone); long nextTickTime = tickDate.getTime(); for (int minorTick = 1; minorTick < minorTickSpaces; minorTick++) { long minorTickTime = currentTickTime + (nextTickTime - currentTickTime) * minorTick / minorTickSpaces; if (getRange().contains(minorTickTime) && (!isHiddenValue(minorTickTime))) { result.add(new DateTick(TickType.MINOR, new Date(minorTickTime), "", TextAnchor.TOP_CENTER, TextAnchor.CENTER, 0.0)); } } } else { tickDate = unit.rollDate(tickDate, this.timeZone); hasRolled = true; } } return result; } /** * Recalculates the ticks for the date axis. * * @param g2 the graphics device. * @param dataArea the area in which the plot should be drawn. * @param edge the location of the axis. * * @return A list of ticks. */ protected List refreshTicksVertical(Graphics2D g2, Rectangle2D dataArea, RectangleEdge edge) { List result = new java.util.ArrayList(); Font tickLabelFont = getTickLabelFont(); g2.setFont(tickLabelFont); if (isAutoTickUnitSelection()) { selectAutoTickUnit(g2, dataArea, edge); } DateTickUnit unit = getTickUnit(); Date tickDate = calculateLowestVisibleTickValue(unit); Date upperDate = getMaximumDate(); boolean hasRolled = false; while (tickDate.before(upperDate)) { // could add a flag to make the following correction optional... if (!hasRolled) { tickDate = correctTickDateForPosition(tickDate, unit, this.tickMarkPosition); } long lowestTickTime = tickDate.getTime(); long distance = unit.addToDate(tickDate, this.timeZone).getTime() - lowestTickTime; int minorTickSpaces = getMinorTickCount(); if (minorTickSpaces <= 0) { minorTickSpaces = unit.getMinorTickCount(); } for (int minorTick = 1; minorTick < minorTickSpaces; minorTick++) { long minorTickTime = lowestTickTime - distance * minorTick / minorTickSpaces; if (minorTickTime > 0 && getRange().contains(minorTickTime) && (!isHiddenValue(minorTickTime))) { result.add(new DateTick(TickType.MINOR, new Date(minorTickTime), "", TextAnchor.TOP_CENTER, TextAnchor.CENTER, 0.0)); } } if (!isHiddenValue(tickDate.getTime())) { // work out the value, label and position String tickLabel; DateFormat formatter = getDateFormatOverride(); if (formatter != null) { tickLabel = formatter.format(tickDate); } else { tickLabel = this.tickUnit.dateToString(tickDate); } TextAnchor anchor, rotationAnchor; double angle = 0.0; if (isVerticalTickLabels()) { anchor = TextAnchor.BOTTOM_CENTER; rotationAnchor = TextAnchor.BOTTOM_CENTER; if (edge == RectangleEdge.LEFT) { angle = -Math.PI / 2.0; } else { angle = Math.PI / 2.0; } } else { if (edge == RectangleEdge.LEFT) { anchor = TextAnchor.CENTER_RIGHT; rotationAnchor = TextAnchor.CENTER_RIGHT; } else { anchor = TextAnchor.CENTER_LEFT; rotationAnchor = TextAnchor.CENTER_LEFT; } } Tick tick = new DateTick(tickDate, tickLabel, anchor, rotationAnchor, angle); result.add(tick); hasRolled = false; long currentTickTime = tickDate.getTime(); tickDate = unit.addToDate(tickDate, this.timeZone); long nextTickTime = tickDate.getTime(); for (int minorTick = 1; minorTick < minorTickSpaces; minorTick++) { long minorTickTime = currentTickTime + (nextTickTime - currentTickTime) * minorTick / minorTickSpaces; if (getRange().contains(minorTickTime) && (!isHiddenValue(minorTickTime))) { result.add(new DateTick(TickType.MINOR, new Date(minorTickTime), "", TextAnchor.TOP_CENTER, TextAnchor.CENTER, 0.0)); } } } else { tickDate = unit.rollDate(tickDate, this.timeZone); hasRolled = true; } } return result; } /** * Draws the axis on a Java 2D graphics device (such as the screen or a * printer). * * @param g2 the graphics device ({@code null} not permitted). * @param cursor the cursor location. * @param plotArea the area within which the axes and data should be * drawn ({@code null} not permitted). * @param dataArea the area within which the data should be drawn * ({@code null} not permitted). * @param edge the location of the axis ({@code null} not permitted). * @param plotState collects information about the plot * ({@code null} permitted). * * @return The axis state (never {@code null}). */ @Override public AxisState draw(Graphics2D g2, double cursor, Rectangle2D plotArea, Rectangle2D dataArea, RectangleEdge edge, PlotRenderingInfo plotState) { // if the axis is not visible, don't draw it... if (!isVisible()) { AxisState state = new AxisState(cursor); // even though the axis is not visible, we need to refresh ticks in // case the grid is being drawn... List ticks = refreshTicks(g2, state, dataArea, edge); state.setTicks(ticks); return state; } // draw the tick marks and labels... AxisState state = drawTickMarksAndLabels(g2, cursor, plotArea, dataArea, edge); // draw the axis label (note that 'state' is passed in *and* // returned)... if (getAttributedLabel() != null) { state = drawAttributedLabel(getAttributedLabel(), g2, plotArea, dataArea, edge, state); } else { state = drawLabel(getLabel(), g2, plotArea, dataArea, edge, state); } createAndAddEntity(cursor, state, dataArea, edge, plotState); return state; } /** * Zooms in on the current range (zoom-in stops once the axis length * reaches the equivalent of one millisecond). * * @param lowerPercent the new lower bound. * @param upperPercent the new upper bound. */ @Override public void zoomRange(double lowerPercent, double upperPercent) { double start = this.timeline.toTimelineValue( (long) getRange().getLowerBound()); double end = this.timeline.toTimelineValue( (long) getRange().getUpperBound()); double length = end - start; Range adjusted; long adjStart, adjEnd; if (isInverted()) { adjStart = (long) (start + (length * (1 - upperPercent))); adjEnd = (long) (start + (length * (1 - lowerPercent))); } else { adjStart = (long) (start + length * lowerPercent); adjEnd = (long) (start + length * upperPercent); } // when zooming to sub-millisecond ranges, it can be the case that // adjEnd == adjStart...and we can't have an axis with zero length // so we apply this instead: if (adjEnd <= adjStart) { adjEnd = adjStart + 1L; } adjusted = new DateRange(this.timeline.toMillisecond(adjStart), this.timeline.toMillisecond(adjEnd)); setRange(adjusted); } /** * Tests this axis for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof DateAxis)) { return false; } DateAxis that = (DateAxis) obj; if (!Objects.equals(this.timeZone, that.timeZone)) { return false; } if (!Objects.equals(this.locale, that.locale)) { return false; } if (!Objects.equals(this.tickUnit, that.tickUnit)) { return false; } if (!Objects.equals(this.dateFormatOverride, that.dateFormatOverride)) { return false; } if (!Objects.equals(this.tickMarkPosition, that.tickMarkPosition)) { return false; } if (!Objects.equals(this.timeline, that.timeline)) { return false; } return super.equals(obj); } /** * Returns a hash code for this object. * * @return A hash code. */ @Override public int hashCode() { return super.hashCode(); } /** * Returns a clone of the object. * * @return A clone. * * @throws CloneNotSupportedException if some component of the axis does * not support cloning. */ @Override public Object clone() throws CloneNotSupportedException { DateAxis clone = (DateAxis) super.clone(); // 'dateTickUnit' is immutable : no need to clone if (this.dateFormatOverride != null) { clone.dateFormatOverride = (DateFormat) this.dateFormatOverride.clone(); } // 'tickMarkPosition' is immutable : no need to clone return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/DateTick.java000066400000000000000000000100561463604235500263570ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------- * DateTick.java * ------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Peter Kolb (patch 1934255); * Andrew Mickish (patch 1870189); * */ package org.jfree.chart.axis; import java.util.Date; import java.util.Objects; import org.jfree.chart.ui.TextAnchor; import org.jfree.chart.util.Args; /** * A tick used by the {@link DateAxis} class. */ public class DateTick extends ValueTick { /** The date. */ private final Date date; /** * Creates a new date tick. * * @param date the date. * @param label the label. * @param textAnchor the part of the label that is aligned to the anchor * point. * @param rotationAnchor defines the rotation point relative to the text. * @param angle the rotation angle (in radians). */ public DateTick(Date date, String label, TextAnchor textAnchor, TextAnchor rotationAnchor, double angle) { this(TickType.MAJOR, date, label, textAnchor, rotationAnchor, angle); } /** * Creates a new date tick. * * @param tickType the tick type ({@code null} not permitted). * @param date the date. * @param label the label. * @param textAnchor the part of the label that is aligned to the anchor * point. * @param rotationAnchor defines the rotation point relative to the text. * @param angle the rotation angle (in radians). */ public DateTick(TickType tickType, Date date, String label, TextAnchor textAnchor, TextAnchor rotationAnchor, double angle) { super(tickType, date.getTime(), label, textAnchor, rotationAnchor, angle); Args.nullNotPermitted(tickType, "tickType"); this.date = date; } /** * Returns the date. * * @return The date. */ public Date getDate() { return this.date; } /** * Tests this tick for equality with an arbitrary object. * * @param obj the object to test ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof DateTick)) { return false; } DateTick that = (DateTick) obj; if (!Objects.equals(this.date, that.date)) { return false; } return super.equals(obj); } /** * Returns a hash code for this object. * * @return A hash code. */ @Override public int hashCode() { return this.date.hashCode(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/DateTickMarkPosition.java000066400000000000000000000076221463604235500307240ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * DateTickMarkPosition.java * ------------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.io.ObjectStreamException; import java.io.Serializable; /** * Used to indicate the required position of tick marks on a date axis relative * to the underlying time period. */ public final class DateTickMarkPosition implements Serializable { /** For serialization. */ private static final long serialVersionUID = 2540750672764537240L; /** Start of period. */ public static final DateTickMarkPosition START = new DateTickMarkPosition("DateTickMarkPosition.START"); /** Middle of period. */ public static final DateTickMarkPosition MIDDLE = new DateTickMarkPosition("DateTickMarkPosition.MIDDLE"); /** End of period. */ public static final DateTickMarkPosition END = new DateTickMarkPosition("DateTickMarkPosition.END"); /** The name. */ private String name; /** * Private constructor. * * @param name the name. */ private DateTickMarkPosition(String name) { this.name = name; } /** * Returns a string representing the object. * * @return The string. */ @Override public String toString() { return this.name; } /** * Returns {@code true} if this object is equal to the specified * object, and {@code false} otherwise. * * @param obj the other object. * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof DateTickMarkPosition)) { return false; } DateTickMarkPosition position = (DateTickMarkPosition) obj; if (!this.name.equals(position.toString())) { return false; } return true; } /** * Ensures that serialization returns the unique instances. * * @return The object. * * @throws ObjectStreamException if there is a problem. */ private Object readResolve() throws ObjectStreamException { if (this.equals(DateTickMarkPosition.START)) { return DateTickMarkPosition.START; } else if (this.equals(DateTickMarkPosition.MIDDLE)) { return DateTickMarkPosition.MIDDLE; } else if (this.equals(DateTickMarkPosition.END)) { return DateTickMarkPosition.END; } return null; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/DateTickUnit.java000066400000000000000000000245361463604235500272270ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * DateTickUnit.java * ----------------- * (C) Copyright 2000-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Chris Boek; * */ package org.jfree.chart.axis; import java.io.Serializable; import java.text.DateFormat; import java.util.Calendar; import java.util.Date; import java.util.Objects; import java.util.TimeZone; import org.jfree.chart.util.Args; /** * A tick unit for use by subclasses of {@link DateAxis}. Instances of this * class are immutable. */ public class DateTickUnit extends TickUnit implements Serializable { /** For serialization. */ private static final long serialVersionUID = -7289292157229621901L; /** * The units. */ private DateTickUnitType unitType; /** The unit count. */ private int count; /** * The roll unit type. */ private DateTickUnitType rollUnitType; /** The roll count. */ private int rollCount; /** The date formatter. */ private DateFormat formatter; /** * Creates a new date tick unit. * * @param unitType the unit type ({@code null} not permitted). * @param multiple the multiple (of the unit type, must be > 0). */ public DateTickUnit(DateTickUnitType unitType, int multiple) { this(unitType, multiple, DateFormat.getDateInstance(DateFormat.SHORT)); } /** * Creates a new date tick unit. * * @param unitType the unit type ({@code null} not permitted). * @param multiple the multiple (of the unit type, must be > 0). * @param formatter the date formatter ({@code null} not permitted). */ public DateTickUnit(DateTickUnitType unitType, int multiple, DateFormat formatter) { this(unitType, multiple, unitType, multiple, formatter); } /** * Creates a new unit. * * @param unitType the unit. * @param multiple the multiple. * @param rollUnitType the roll unit. * @param rollMultiple the roll multiple. * @param formatter the date formatter ({@code null} not permitted). */ public DateTickUnit(DateTickUnitType unitType, int multiple, DateTickUnitType rollUnitType, int rollMultiple, DateFormat formatter) { super(DateTickUnit.getMillisecondCount(unitType, multiple)); Args.nullNotPermitted(formatter, "formatter"); if (multiple <= 0) { throw new IllegalArgumentException("Requires 'multiple' > 0."); } if (rollMultiple <= 0) { throw new IllegalArgumentException("Requires 'rollMultiple' > 0."); } this.unitType = unitType; this.count = multiple; this.rollUnitType = rollUnitType; this.rollCount = rollMultiple; this.formatter = formatter; } /** * Returns the unit type. * * @return The unit type (never {@code null}). */ public DateTickUnitType getUnitType() { return this.unitType; } /** * Returns the unit multiple. * * @return The unit multiple (always > 0). */ public int getMultiple() { return this.count; } /** * Returns the roll unit type. * * @return The roll unit type (never {@code null}). */ public DateTickUnitType getRollUnitType() { return this.rollUnitType; } /** * Returns the roll unit multiple. * * @return The roll unit multiple. */ public int getRollMultiple() { return this.rollCount; } /** * Formats a value. * * @param milliseconds date in milliseconds since 01-01-1970. * * @return The formatted date. */ @Override public String valueToString(double milliseconds) { return this.formatter.format(new Date((long) milliseconds)); } /** * Formats a date using the tick unit's formatter. * * @param date the date. * * @return The formatted date. */ public String dateToString(Date date) { return this.formatter.format(date); } /** * Calculates a new date by adding this unit to the base date. * * @param base the base date. * @param zone the time zone for the date calculation. * * @return A new date one unit after the base date. */ public Date addToDate(Date base, TimeZone zone) { // as far as I know, the Locale for the calendar only affects week // number calculations, and since DateTickUnit doesn't do week // arithmetic, the default locale (whatever it is) should be fine // here... Calendar calendar = Calendar.getInstance(zone); calendar.setTime(base); calendar.add(this.unitType.getCalendarField(), this.count); return calendar.getTime(); } /** * Rolls the date forward by the amount specified by the roll unit and * count. * * @param base the base date. * @return The rolled date. * * @see #rollDate(Date, TimeZone) */ public Date rollDate(Date base) { return rollDate(base, TimeZone.getDefault()); } /** * Rolls the date forward by the amount specified by the roll unit and * count. * * @param base the base date. * @param zone the time zone. * * @return The rolled date. */ public Date rollDate(Date base, TimeZone zone) { // as far as I know, the Locale for the calendar only affects week // number calculations, and since DateTickUnit doesn't do week // arithmetic, the default locale (whatever it is) should be fine // here... Calendar calendar = Calendar.getInstance(zone); calendar.setTime(base); calendar.add(this.rollUnitType.getCalendarField(), this.rollCount); return calendar.getTime(); } /** * Returns a field code that can be used with the {@code Calendar} * class. * * @return The field code. */ public int getCalendarField() { return this.unitType.getCalendarField(); } /** * Returns the (approximate) number of milliseconds for the given unit and * unit count. *

* This value is an approximation some of the time (e.g. months are * assumed to have 31 days) but this shouldn't matter. * * @param unit the unit. * @param count the unit count. * * @return The number of milliseconds. */ private static long getMillisecondCount(DateTickUnitType unit, int count) { if (unit.equals(DateTickUnitType.YEAR)) { return (365L * 24L * 60L * 60L * 1000L) * count; } else if (unit.equals(DateTickUnitType.MONTH)) { return (31L * 24L * 60L * 60L * 1000L) * count; } else if (unit.equals(DateTickUnitType.DAY)) { return (24L * 60L * 60L * 1000L) * count; } else if (unit.equals(DateTickUnitType.HOUR)) { return (60L * 60L * 1000L) * count; } else if (unit.equals(DateTickUnitType.MINUTE)) { return (60L * 1000L) * count; } else if (unit.equals(DateTickUnitType.SECOND)) { return 1000L * count; } else if (unit.equals(DateTickUnitType.MILLISECOND)) { return count; } else { throw new IllegalArgumentException("The 'unit' argument has a " + "value that is not recognised."); } } /** * Tests this unit for equality with another object. * * @param obj the object ({@code null} permitted). * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof DateTickUnit)) { return false; } if (!super.equals(obj)) { return false; } DateTickUnit that = (DateTickUnit) obj; if (!(this.unitType.equals(that.unitType))) { return false; } if (this.count != that.count) { return false; } if (!Objects.equals(this.formatter, that.formatter)) { return false; } return true; } /** * Returns a hash code for this object. * * @return A hash code. */ @Override public int hashCode() { int result = 19; result = 37 * result + this.unitType.hashCode(); result = 37 * result + this.count; result = 37 * result + this.formatter.hashCode(); return result; } /** * Returns a string representation of this instance, primarily used for * debugging purposes. * * @return A string representation of this instance. */ @Override public String toString() { return "DateTickUnit[" + this.unitType.toString() + ", " + this.count + "]"; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/DateTickUnitType.java000066400000000000000000000122601463604235500300600ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * DateTickUnitType.java * --------------------- * (C) Copyright 2009-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.io.ObjectStreamException; import java.io.Serializable; import java.util.Calendar; /** * An enumeration of the unit types for a {@link DateTickUnit} instance. */ public class DateTickUnitType implements Serializable { /** Year. */ public static final DateTickUnitType YEAR = new DateTickUnitType("DateTickUnitType.YEAR", Calendar.YEAR); /** Month. */ public static final DateTickUnitType MONTH = new DateTickUnitType("DateTickUnitType.MONTH", Calendar.MONTH); /** Day. */ public static final DateTickUnitType DAY = new DateTickUnitType("DateTickUnitType.DAY", Calendar.DATE); /** Hour. */ public static final DateTickUnitType HOUR = new DateTickUnitType("DateTickUnitType.HOUR", Calendar.HOUR_OF_DAY); /** Minute. */ public static final DateTickUnitType MINUTE = new DateTickUnitType("DateTickUnitType.MINUTE", Calendar.MINUTE); /** Second. */ public static final DateTickUnitType SECOND = new DateTickUnitType("DateTickUnitType.SECOND", Calendar.SECOND); /** Millisecond. */ public static final DateTickUnitType MILLISECOND = new DateTickUnitType("DateTickUnitType.MILLISECOND", Calendar.MILLISECOND); /** The name. */ private String name; /** The corresponding field value in Java's Calendar class. */ private int calendarField; /** * Private constructor. * * @param name the name. * @param calendarField the calendar field. */ private DateTickUnitType(String name, int calendarField) { this.name = name; this.calendarField = calendarField; } /** * Returns the calendar field. * * @return The calendar field. */ public int getCalendarField() { return this.calendarField; } /** * Returns a string representing the object. * * @return The string. */ @Override public String toString() { return this.name; } /** * Returns {@code true} if this object is equal to the specified * object, and {@code false} otherwise. * * @param obj the other object. * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof DateTickUnitType)) { return false; } DateTickUnitType t = (DateTickUnitType) obj; if (!this.name.equals(t.toString())) { return false; } return true; } /** * Ensures that serialization returns the unique instances. * * @return The object. * * @throws ObjectStreamException if there is a problem. */ private Object readResolve() throws ObjectStreamException { if (this.equals(DateTickUnitType.YEAR)) { return DateTickUnitType.YEAR; } else if (this.equals(DateTickUnitType.MONTH)) { return DateTickUnitType.MONTH; } else if (this.equals(DateTickUnitType.DAY)) { return DateTickUnitType.DAY; } else if (this.equals(DateTickUnitType.HOUR)) { return DateTickUnitType.HOUR; } else if (this.equals(DateTickUnitType.MINUTE)) { return DateTickUnitType.MINUTE; } else if (this.equals(DateTickUnitType.SECOND)) { return DateTickUnitType.SECOND; } else if (this.equals(DateTickUnitType.MILLISECOND)) { return DateTickUnitType.MILLISECOND; } return null; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/ExtendedCategoryAxis.java000066400000000000000000000173151463604235500307570ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * ExtendedCategoryAxis.java * ------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.HashMap; import java.util.Map; import org.jfree.chart.event.AxisChangeEvent; import org.jfree.chart.text.TextBlock; import org.jfree.chart.text.TextFragment; import org.jfree.chart.text.TextLine; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.SerialUtils; /** * An extended version of the {@link CategoryAxis} class that supports * sublabels on the axis. */ public class ExtendedCategoryAxis extends CategoryAxis { /** For serialization. */ static final long serialVersionUID = -3004429093959826567L; /** Storage for the sublabels. */ private Map sublabels; /** The sublabel font. */ private Font sublabelFont; /** The sublabel paint. */ private transient Paint sublabelPaint; /** * Creates a new axis. * * @param label the axis label. */ public ExtendedCategoryAxis(String label) { super(label); this.sublabels = new HashMap(); this.sublabelFont = new Font("SansSerif", Font.PLAIN, 10); this.sublabelPaint = Color.BLACK; } /** * Returns the font for the sublabels. * * @return The font (never {@code null}). * * @see #setSubLabelFont(Font) */ public Font getSubLabelFont() { return this.sublabelFont; } /** * Sets the font for the sublabels and sends an {@link AxisChangeEvent} to * all registered listeners. * * @param font the font ({@code null} not permitted). * * @see #getSubLabelFont() */ public void setSubLabelFont(Font font) { Args.nullNotPermitted(font, "font"); this.sublabelFont = font; notifyListeners(new AxisChangeEvent(this)); } /** * Returns the paint for the sublabels. * * @return The paint (never {@code null}). * * @see #setSubLabelPaint(Paint) */ public Paint getSubLabelPaint() { return this.sublabelPaint; } /** * Sets the paint for the sublabels and sends an {@link AxisChangeEvent} * to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getSubLabelPaint() */ public void setSubLabelPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.sublabelPaint = paint; notifyListeners(new AxisChangeEvent(this)); } /** * Adds a sublabel for a category. * * @param category the category. * @param label the label. */ public void addSubLabel(Comparable category, String label) { this.sublabels.put(category, label); } /** * Overrides the default behaviour by adding the sublabel to the text * block that is used for the category label. * * @param category the category. * @param width the width (not used yet). * @param edge the location of the axis. * @param g2 the graphics device. * * @return A label. */ @Override protected TextBlock createLabel(Comparable category, float width, RectangleEdge edge, Graphics2D g2) { TextBlock label = super.createLabel(category, width, edge, g2); String s = (String) this.sublabels.get(category); if (s != null) { if (edge == RectangleEdge.TOP || edge == RectangleEdge.BOTTOM) { TextLine line = new TextLine(s, this.sublabelFont, this.sublabelPaint); label.addLine(line); } else if (edge == RectangleEdge.LEFT || edge == RectangleEdge.RIGHT) { TextLine line = label.getLastLine(); if (line != null) { line.addFragment(new TextFragment(" " + s, this.sublabelFont, this.sublabelPaint)); } } } return label; } /** * Tests this axis for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof ExtendedCategoryAxis)) { return false; } ExtendedCategoryAxis that = (ExtendedCategoryAxis) obj; if (!this.sublabelFont.equals(that.sublabelFont)) { return false; } if (!PaintUtils.equal(this.sublabelPaint, that.sublabelPaint)) { return false; } if (!this.sublabels.equals(that.sublabels)) { return false; } return super.equals(obj); } /** * Returns a clone of this axis. * * @return A clone. * * @throws CloneNotSupportedException if there is a problem cloning. */ @Override public Object clone() throws CloneNotSupportedException { ExtendedCategoryAxis clone = (ExtendedCategoryAxis) super.clone(); clone.sublabels = new HashMap(this.sublabels); return clone; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.sublabelPaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.sublabelPaint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/LogAxis.java000066400000000000000000001102761463604235500262420ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------ * LogAxis.java * ------------ * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Andrew Mickish (patch 1868745); * Peter Kolb (patches 1934255 and 2603321); */ package org.jfree.chart.axis; import java.awt.Font; import java.awt.Graphics2D; import java.awt.font.FontRenderContext; import java.awt.font.LineMetrics; import java.awt.font.TextAttribute; import java.awt.geom.Rectangle2D; import java.text.AttributedString; import java.text.DecimalFormat; import java.text.Format; import java.text.NumberFormat; import java.util.ArrayList; import java.util.List; import java.util.Objects; import org.jfree.chart.event.AxisChangeEvent; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.ValueAxisPlot; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.ui.TextAnchor; import org.jfree.chart.util.AttrStringUtils; import org.jfree.chart.util.Args; import org.jfree.data.Range; /** * A numerical axis that uses a logarithmic scale. The class is an * alternative to the {@link LogarithmicAxis} class. */ public class LogAxis extends ValueAxis { /** The logarithm base. */ private double base = 10.0; /** The logarithm of the base value - cached for performance. */ private double baseLog = Math.log(10.0); /** * The base symbol to display (if {@code null} then the numerical * value of the base is displayed). */ private String baseSymbol = null; /** * The formatter to use for the base value when the base is displayed * as a numerical value. */ private Format baseFormatter = new DecimalFormat("0"); /** The smallest value permitted on the axis. */ private double smallestValue = 1E-100; /** The current tick unit. */ private NumberTickUnit tickUnit; /** The override number format. */ private NumberFormat numberFormatOverride; /** * Creates a new {@code LogAxis} with no label. */ public LogAxis() { this(null); } /** * Creates a new {@code LogAxis} with the given label. * * @param label the axis label ({@code null} permitted). */ public LogAxis(String label) { super(label, new NumberTickUnitSource()); setDefaultAutoRange(new Range(0.01, 1.0)); this.tickUnit = new NumberTickUnit(1.0, new DecimalFormat("0.#"), 10); } /** * Returns the base for the logarithm calculation. The default value is * {@code 10.0}. * * @return The base for the logarithm calculation. * * @see #setBase(double) */ public double getBase() { return this.base; } /** * Sets the base for the logarithm calculation and sends a change event to * all registered listeners. * * @param base the base value (must be > 1.0). * * @see #getBase() */ public void setBase(double base) { if (base <= 1.0) { throw new IllegalArgumentException("Requires 'base' > 1.0."); } this.base = base; this.baseLog = Math.log(base); fireChangeEvent(); } /** * Returns the symbol used to represent the base of the logarithmic scale * for the axis. If this is {@code null} (the default) then the * numerical value of the base is displayed. * * @return The base symbol (possibly {@code null}). */ public String getBaseSymbol() { return this.baseSymbol; } /** * Sets the symbol used to represent the base value of the logarithmic * scale and sends a change event to all registered listeners. * * @param symbol the symbol ({@code null} permitted). */ public void setBaseSymbol(String symbol) { this.baseSymbol = symbol; fireChangeEvent(); } /** * Returns the formatter used to format the base value of the logarithmic * scale when it is displayed numerically. The default value is * {@code new DecimalFormat("0")}. * * @return The base formatter (never {@code null}). */ public Format getBaseFormatter() { return this.baseFormatter; } /** * Sets the formatter used to format the base value of the logarithmic * scale when it is displayed numerically and sends a change event to all * registered listeners. * * @param formatter the formatter ({@code null} not permitted). */ public void setBaseFormatter(Format formatter) { Args.nullNotPermitted(formatter, "formatter"); this.baseFormatter = formatter; fireChangeEvent(); } /** * Returns the smallest value represented by the axis. * * @return The smallest value represented by the axis. * * @see #setSmallestValue(double) */ public double getSmallestValue() { return this.smallestValue; } /** * Sets the smallest value represented by the axis and sends a change event * to all registered listeners. * * @param value the value. * * @see #getSmallestValue() */ public void setSmallestValue(double value) { if (value <= 0.0) { throw new IllegalArgumentException("Requires 'value' > 0.0."); } this.smallestValue = value; fireChangeEvent(); } /** * Returns the current tick unit. * * @return The current tick unit. * * @see #setTickUnit(NumberTickUnit) */ public NumberTickUnit getTickUnit() { return this.tickUnit; } /** * Sets the tick unit for the axis and sends an {@link AxisChangeEvent} to * all registered listeners. A side effect of calling this method is that * the "auto-select" feature for tick units is switched off (you can * restore it using the {@link ValueAxis#setAutoTickUnitSelection(boolean)} * method). * * @param unit the new tick unit ({@code null} not permitted). * * @see #getTickUnit() */ public void setTickUnit(NumberTickUnit unit) { // defer argument checking... setTickUnit(unit, true, true); } /** * Sets the tick unit for the axis and, if requested, sends an * {@link AxisChangeEvent} to all registered listeners. In addition, an * option is provided to turn off the "auto-select" feature for tick units * (you can restore it using the * {@link ValueAxis#setAutoTickUnitSelection(boolean)} method). * * @param unit the new tick unit ({@code null} not permitted). * @param notify notify listeners? * @param turnOffAutoSelect turn off the auto-tick selection? * * @see #getTickUnit() */ public void setTickUnit(NumberTickUnit unit, boolean notify, boolean turnOffAutoSelect) { Args.nullNotPermitted(unit, "unit"); this.tickUnit = unit; if (turnOffAutoSelect) { setAutoTickUnitSelection(false, false); } if (notify) { fireChangeEvent(); } } /** * Returns the number format override. If this is non-{@code null}, * then it will be used to format the numbers on the axis. * * @return The number formatter (possibly {@code null}). * * @see #setNumberFormatOverride(NumberFormat) */ public NumberFormat getNumberFormatOverride() { return this.numberFormatOverride; } /** * Sets the number format override and sends a change event to all * registered listeners. If this is non-{@code null}, then it will be * used to format the numbers on the axis. * * @param formatter the number formatter ({@code null} permitted). * * @see #getNumberFormatOverride() */ public void setNumberFormatOverride(NumberFormat formatter) { this.numberFormatOverride = formatter; fireChangeEvent(); } /** * Calculates the log of the given value, using the current base. * * @param value the value. * * @return The log of the given value. * * @see #calculateValue(double) * @see #getBase() */ public double calculateLog(double value) { return Math.log(value) / this.baseLog; } /** * Calculates the value from a given log. * * @param log the log value. * * @return The value with the given log. * * @see #calculateLog(double) * @see #getBase() */ public double calculateValue(double log) { return Math.pow(this.base, log); } private double calculateValueNoINF(double log) { double result = calculateValue(log); if (Double.isInfinite(result)) { result = Double.MAX_VALUE; } if (result <= 0.0) { result = Double.MIN_VALUE; } return result; } /** * Converts a Java2D coordinate to an axis value, assuming that the * axis is aligned to the specified {@code edge} of the {@code area}. * * @param java2DValue the Java2D coordinate. * @param area the area for plotting data ({@code null} not * permitted). * @param edge the edge that the axis is aligned to ({@code null} not * permitted). * * @return A value along the axis scale. */ @Override public double java2DToValue(double java2DValue, Rectangle2D area, RectangleEdge edge) { Range range = getRange(); double axisMin = calculateLog(Math.max(this.smallestValue, range.getLowerBound())); double axisMax = calculateLog(range.getUpperBound()); double min = 0.0; double max = 0.0; if (RectangleEdge.isTopOrBottom(edge)) { min = area.getX(); max = area.getMaxX(); } else if (RectangleEdge.isLeftOrRight(edge)) { min = area.getMaxY(); max = area.getY(); } double log; if (isInverted()) { log = axisMax - (java2DValue - min) / (max - min) * (axisMax - axisMin); } else { log = axisMin + (java2DValue - min) / (max - min) * (axisMax - axisMin); } return calculateValue(log); } /** * Converts a value on the axis scale to a Java2D coordinate relative to * the given {@code area}, based on the axis running along the * specified {@code edge}. * * @param value the data value. * @param area the area ({@code null} not permitted). * @param edge the edge ({@code null} not permitted). * * @return The Java2D coordinate corresponding to {@code value}. */ @Override public double valueToJava2D(double value, Rectangle2D area, RectangleEdge edge) { Range range = getRange(); double axisMin = calculateLog(range.getLowerBound()); double axisMax = calculateLog(range.getUpperBound()); value = calculateLog(value); double min = 0.0; double max = 0.0; if (RectangleEdge.isTopOrBottom(edge)) { min = area.getX(); max = area.getMaxX(); } else if (RectangleEdge.isLeftOrRight(edge)) { max = area.getMinY(); min = area.getMaxY(); } if (isInverted()) { return max - ((value - axisMin) / (axisMax - axisMin)) * (max - min); } else { return min + ((value - axisMin) / (axisMax - axisMin)) * (max - min); } } /** * Configures the axis. This method is typically called when an axis * is assigned to a new plot. */ @Override public void configure() { if (isAutoRange()) { autoAdjustRange(); } } /** * Adjusts the axis range to match the data range that the axis is * required to display. */ @Override protected void autoAdjustRange() { Plot plot = getPlot(); if (plot == null) { return; // no plot, no data } if (plot instanceof ValueAxisPlot) { ValueAxisPlot vap = (ValueAxisPlot) plot; Range r = vap.getDataRange(this); if (r == null) { r = getDefaultAutoRange(); } double upper = r.getUpperBound(); double lower = Math.max(r.getLowerBound(), this.smallestValue); double range = upper - lower; // if fixed auto range, then derive lower bound... double fixedAutoRange = getFixedAutoRange(); if (fixedAutoRange > 0.0) { lower = Math.max(upper - fixedAutoRange, this.smallestValue); } else { // ensure the autorange is at least in size... double minRange = getAutoRangeMinimumSize(); if (range < minRange) { double expand = (minRange - range) / 2; upper = upper + expand; lower = lower - expand; } // apply the margins - these should apply to the exponent range double logUpper = calculateLog(upper); double logLower = calculateLog(lower); double logRange = logUpper - logLower; logUpper = logUpper + getUpperMargin() * logRange; logLower = logLower - getLowerMargin() * logRange; upper = calculateValueNoINF(logUpper); lower = calculateValueNoINF(logLower); } setRange(new Range(lower, upper), false, false); } } /** * Draws the axis on a Java 2D graphics device (such as the screen or a * printer). * * @param g2 the graphics device ({@code null} not permitted). * @param cursor the cursor location (determines where to draw the axis). * @param plotArea the area within which the axes and plot should be drawn. * @param dataArea the area within which the data should be drawn. * @param edge the axis location ({@code null} not permitted). * @param plotState collects information about the plot ({@code null} * permitted). * * @return The axis state (never {@code null}). */ @Override public AxisState draw(Graphics2D g2, double cursor, Rectangle2D plotArea, Rectangle2D dataArea, RectangleEdge edge, PlotRenderingInfo plotState) { AxisState state; // if the axis is not visible, don't draw it... if (!isVisible()) { state = new AxisState(cursor); // even though the axis is not visible, we need ticks for the // gridlines... List ticks = refreshTicks(g2, state, dataArea, edge); state.setTicks(ticks); return state; } state = drawTickMarksAndLabels(g2, cursor, plotArea, dataArea, edge); if (getAttributedLabel() != null) { state = drawAttributedLabel(getAttributedLabel(), g2, plotArea, dataArea, edge, state); } else { state = drawLabel(getLabel(), g2, plotArea, dataArea, edge, state); } createAndAddEntity(cursor, state, dataArea, edge, plotState); return state; } /** * Calculates the positions of the tick labels for the axis, storing the * results in the tick label list (ready for drawing). * * @param g2 the graphics device. * @param state the axis state. * @param dataArea the area in which the plot should be drawn. * @param edge the location of the axis. * * @return A list of ticks. */ @Override public List refreshTicks(Graphics2D g2, AxisState state, Rectangle2D dataArea, RectangleEdge edge) { List result = new java.util.ArrayList(); if (RectangleEdge.isTopOrBottom(edge)) { result = refreshTicksHorizontal(g2, dataArea, edge); } else if (RectangleEdge.isLeftOrRight(edge)) { result = refreshTicksVertical(g2, dataArea, edge); } return result; } /** * Returns a list of ticks for an axis at the top or bottom of the chart. * * @param g2 the graphics device ({@code null} not permitted). * @param dataArea the data area ({@code null} not permitted). * @param edge the edge ({@code null} not permitted). * * @return A list of ticks. */ protected List refreshTicksHorizontal(Graphics2D g2, Rectangle2D dataArea, RectangleEdge edge) { Range range = getRange(); List ticks = new ArrayList(); Font tickLabelFont = getTickLabelFont(); g2.setFont(tickLabelFont); TextAnchor textAnchor; if (edge == RectangleEdge.TOP) { textAnchor = TextAnchor.BOTTOM_CENTER; } else { textAnchor = TextAnchor.TOP_CENTER; } if (isAutoTickUnitSelection()) { selectAutoTickUnit(g2, dataArea, edge); } int minorTickCount = this.tickUnit.getMinorTickCount(); double unit = getTickUnit().getSize(); double index = Math.ceil(calculateLog(getRange().getLowerBound()) / unit); double start = index * unit; double end = calculateLog(getUpperBound()); double current = start; boolean hasTicks = (this.tickUnit.getSize() > 0.0) && !Double.isInfinite(start); while (hasTicks && current <= end) { double v = calculateValueNoINF(current); if (range.contains(v)) { ticks.add(new LogTick(TickType.MAJOR, v, createTickLabel(v), textAnchor)); } // add minor ticks (for gridlines) double next = Math.pow(this.base, current + this.tickUnit.getSize()); for (int i = 1; i < minorTickCount; i++) { double minorV = v + i * ((next - v) / minorTickCount); if (range.contains(minorV)) { ticks.add(new LogTick(TickType.MINOR, minorV, null, textAnchor)); } } current = current + this.tickUnit.getSize(); } return ticks; } /** * Returns a list of ticks for an axis at the left or right of the chart. * * @param g2 the graphics device ({@code null} not permitted). * @param dataArea the data area ({@code null} not permitted). * @param edge the edge that the axis is aligned to ({@code null} * not permitted). * * @return A list of ticks. */ protected List refreshTicksVertical(Graphics2D g2, Rectangle2D dataArea, RectangleEdge edge) { Range range = getRange(); List ticks = new ArrayList(); Font tickLabelFont = getTickLabelFont(); g2.setFont(tickLabelFont); TextAnchor textAnchor; if (edge == RectangleEdge.RIGHT) { textAnchor = TextAnchor.CENTER_LEFT; } else { textAnchor = TextAnchor.CENTER_RIGHT; } if (isAutoTickUnitSelection()) { selectAutoTickUnit(g2, dataArea, edge); } int minorTickCount = this.tickUnit.getMinorTickCount(); double unit = getTickUnit().getSize(); double index = Math.ceil(calculateLog(getRange().getLowerBound()) / unit); double start = index * unit; double end = calculateLog(getUpperBound()); double current = start; boolean hasTicks = (this.tickUnit.getSize() > 0.0) && !Double.isInfinite(start); while (hasTicks && current <= end) { double v = calculateValueNoINF(current); if (range.contains(v)) { ticks.add(new LogTick(TickType.MAJOR, v, createTickLabel(v), textAnchor)); } // add minor ticks (for gridlines) double next = Math.pow(this.base, current + this.tickUnit.getSize()); for (int i = 1; i < minorTickCount; i++) { double minorV = v + i * ((next - v) / minorTickCount); if (range.contains(minorV)) { ticks.add(new LogTick(TickType.MINOR, minorV, null, textAnchor)); } } current = current + this.tickUnit.getSize(); } return ticks; } /** * Selects an appropriate tick value for the axis. The strategy is to * display as many ticks as possible (selected from an array of 'standard' * tick units) without the labels overlapping. * * @param g2 the graphics device ({@code null} not permitted). * @param dataArea the area defined by the axes ({@code null} not * permitted). * @param edge the axis location ({@code null} not permitted). */ protected void selectAutoTickUnit(Graphics2D g2, Rectangle2D dataArea, RectangleEdge edge) { if (RectangleEdge.isTopOrBottom(edge)) { selectHorizontalAutoTickUnit(g2, dataArea, edge); } else if (RectangleEdge.isLeftOrRight(edge)) { selectVerticalAutoTickUnit(g2, dataArea, edge); } } /** * Selects an appropriate tick value for the axis. The strategy is to * display as many ticks as possible (selected from an array of 'standard' * tick units) without the labels overlapping. * * @param g2 the graphics device. * @param dataArea the area defined by the axes. * @param edge the axis location. */ protected void selectHorizontalAutoTickUnit(Graphics2D g2, Rectangle2D dataArea, RectangleEdge edge) { // select a tick unit that is the next one bigger than the current // (log) range divided by 50 Range range = getRange(); double logAxisMin = calculateLog(Math.max(this.smallestValue, range.getLowerBound())); double logAxisMax = calculateLog(range.getUpperBound()); double size = (logAxisMax - logAxisMin) / 50; TickUnitSource tickUnits = getStandardTickUnits(); TickUnit candidate = tickUnits.getCeilingTickUnit(size); TickUnit prevCandidate = candidate; boolean found = false; while (!found) { // while the tick labels overlap and there are more tick sizes available, // choose the next bigger label this.tickUnit = (NumberTickUnit) candidate; double tickLabelWidth = estimateMaximumTickLabelWidth(g2, candidate); // what is the available space for one unit? double candidateWidth = exponentLengthToJava2D(candidate.getSize(), dataArea, edge); if (tickLabelWidth < candidateWidth) { found = true; } else if (Double.isNaN(candidateWidth)) { candidate = prevCandidate; found = true; } else { prevCandidate = candidate; candidate = tickUnits.getLargerTickUnit(prevCandidate); if (candidate.equals(prevCandidate)) { found = true; // there are no more candidates } } } setTickUnit((NumberTickUnit) candidate, false, false); } /** * Converts a length in data coordinates into the corresponding length in * Java2D coordinates. * * @param length the length. * @param area the plot area. * @param edge the edge along which the axis lies. * * @return The length in Java2D coordinates. */ public double exponentLengthToJava2D(double length, Rectangle2D area, RectangleEdge edge) { double one = valueToJava2D(calculateValueNoINF(1.0), area, edge); double l = valueToJava2D(calculateValueNoINF(length + 1.0), area, edge); return Math.abs(l - one); } /** * Selects an appropriate tick value for the axis. The strategy is to * display as many ticks as possible (selected from an array of 'standard' * tick units) without the labels overlapping. * * @param g2 the graphics device. * @param dataArea the area in which the plot should be drawn. * @param edge the axis location. */ protected void selectVerticalAutoTickUnit(Graphics2D g2, Rectangle2D dataArea, RectangleEdge edge) { // select a tick unit that is the next one bigger than the current // (log) range divided by 50 Range range = getRange(); double logAxisMin = calculateLog(Math.max(this.smallestValue, range.getLowerBound())); double logAxisMax = calculateLog(range.getUpperBound()); double size = (logAxisMax - logAxisMin) / 50; TickUnitSource tickUnits = getStandardTickUnits(); TickUnit candidate = tickUnits.getCeilingTickUnit(size); TickUnit prevCandidate = candidate; boolean found = false; while (!found) { // while the tick labels overlap and there are more tick sizes available, // choose the next bigger label this.tickUnit = (NumberTickUnit) candidate; double tickLabelHeight = estimateMaximumTickLabelHeight(g2); // what is the available space for one unit? double candidateHeight = exponentLengthToJava2D(candidate.getSize(), dataArea, edge); if (tickLabelHeight < candidateHeight) { found = true; } else if (Double.isNaN(candidateHeight)) { candidate = prevCandidate; found = true; } else { prevCandidate = candidate; candidate = tickUnits.getLargerTickUnit(prevCandidate); if (candidate.equals(prevCandidate)) { found = true; // there are no more candidates } } } setTickUnit((NumberTickUnit) candidate, false, false); } /** * Creates a tick label for the specified value based on the current * tick unit (used for formatting the exponent). * * @param value the value. * * @return The label. */ protected AttributedString createTickLabel(double value) { if (this.numberFormatOverride != null) { String text = this.numberFormatOverride.format(value); AttributedString as = new AttributedString(text); as.addAttribute(TextAttribute.FONT, getTickLabelFont()); return as; } else { String baseStr = this.baseSymbol; if (baseStr == null) { baseStr = this.baseFormatter.format(this.base); } double logy = calculateLog(value); String exponentStr = getTickUnit().valueToString(logy); AttributedString as = new AttributedString(baseStr + exponentStr); as.addAttributes(getTickLabelFont().getAttributes(), 0, (baseStr + exponentStr).length()); as.addAttribute(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER, baseStr.length(), baseStr.length() + exponentStr.length()); return as; } } /** * Estimates the maximum tick label height. * * @param g2 the graphics device. * * @return The maximum height. */ protected double estimateMaximumTickLabelHeight(Graphics2D g2) { RectangleInsets tickLabelInsets = getTickLabelInsets(); double result = tickLabelInsets.getTop() + tickLabelInsets.getBottom(); Font tickLabelFont = getTickLabelFont(); FontRenderContext frc = g2.getFontRenderContext(); result += tickLabelFont.getLineMetrics("123", frc).getHeight(); return result; } /** * Estimates the maximum width of the tick labels, assuming the specified * tick unit is used. *

* Rather than computing the string bounds of every tick on the axis, we * just look at two values: the lower bound and the upper bound for the * axis. These two values will usually be representative. * * @param g2 the graphics device. * @param unit the tick unit to use for calculation. * * @return The estimated maximum width of the tick labels. */ protected double estimateMaximumTickLabelWidth(Graphics2D g2, TickUnit unit) { RectangleInsets tickLabelInsets = getTickLabelInsets(); double result = tickLabelInsets.getLeft() + tickLabelInsets.getRight(); if (isVerticalTickLabels()) { // all tick labels have the same width (equal to the height of the // font)... FontRenderContext frc = g2.getFontRenderContext(); LineMetrics lm = getTickLabelFont().getLineMetrics("0", frc); result += lm.getHeight(); } else { // look at lower and upper bounds... Range range = getRange(); double lower = range.getLowerBound(); double upper = range.getUpperBound(); AttributedString lowerStr = createTickLabel(lower); AttributedString upperStr = createTickLabel(upper); double w1 = AttrStringUtils.getTextBounds(lowerStr, g2).getWidth(); double w2 = AttrStringUtils.getTextBounds(upperStr, g2).getWidth(); result += Math.max(w1, w2); } return result; } /** * Zooms in on the current range. * * @param lowerPercent the new lower bound. * @param upperPercent the new upper bound. */ @Override public void zoomRange(double lowerPercent, double upperPercent) { Range range = getRange(); double start = range.getLowerBound(); double end = range.getUpperBound(); double log1 = calculateLog(start); double log2 = calculateLog(end); double length = log2 - log1; Range adjusted; if (isInverted()) { double logA = log1 + length * (1 - upperPercent); double logB = log1 + length * (1 - lowerPercent); adjusted = new Range(calculateValueNoINF(logA), calculateValueNoINF(logB)); } else { double logA = log1 + length * lowerPercent; double logB = log1 + length * upperPercent; adjusted = new Range(calculateValueNoINF(logA), calculateValueNoINF(logB)); } setRange(adjusted); } /** * Slides the axis range by the specified percentage. * * @param percent the percentage. */ @Override public void pan(double percent) { Range range = getRange(); double lower = range.getLowerBound(); double upper = range.getUpperBound(); double log1 = calculateLog(lower); double log2 = calculateLog(upper); double length = log2 - log1; double adj = length * percent; log1 = log1 + adj; log2 = log2 + adj; setRange(calculateValueNoINF(log1), calculateValueNoINF(log2)); } /** * Increases or decreases the axis range by the specified percentage about * the central value and sends an {@link AxisChangeEvent} to all registered * listeners. *

* To double the length of the axis range, use 200% (2.0). * To halve the length of the axis range, use 50% (0.5). * * @param percent the resize factor. * * @see #resizeRange(double, double) */ @Override public void resizeRange(double percent) { Range range = getRange(); double logMin = calculateLog(range.getLowerBound()); double logMax = calculateLog(range.getUpperBound()); double centralValue = calculateValueNoINF((logMin + logMax) / 2.0); resizeRange(percent, centralValue); } @Override public void resizeRange(double percent, double anchorValue) { resizeRange2(percent, anchorValue); } /** * Resizes the axis length to the specified percentage of the current * range and sends a change event to all registered listeners. If * {@code percent} is greater than 1.0 (100 percent) then the axis * range is increased (which has the effect of zooming out), while if the * {@code percent} is less than 1.0 the axis range is decreased * (which has the effect of zooming in). The resize occurs around an * anchor value (which may not be in the center of the axis). This is used * to support mouse wheel zooming around an arbitrary point on the plot. *

* This method is overridden to perform the percentage calculations on the * log values (which are linear for this axis). * * @param percent the percentage (must be greater than zero). * @param anchorValue the anchor value. */ @Override public void resizeRange2(double percent, double anchorValue) { if (percent > 0.0) { double logAnchorValue = calculateLog(anchorValue); Range range = getRange(); double logAxisMin = calculateLog(range.getLowerBound()); double logAxisMax = calculateLog(range.getUpperBound()); double left = percent * (logAnchorValue - logAxisMin); double right = percent * (logAxisMax - logAnchorValue); double upperBound = calculateValueNoINF(logAnchorValue + right); Range adjusted = new Range(calculateValueNoINF( logAnchorValue - left), upperBound); setRange(adjusted); } else { setAutoRange(true); } } /** * Tests this axis for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof LogAxis)) { return false; } LogAxis that = (LogAxis) obj; if (this.base != that.base) { return false; } if (!Objects.equals(this.baseSymbol, that.baseSymbol)) { return false; } if (!this.baseFormatter.equals(that.baseFormatter)) { return false; } if (this.smallestValue != that.smallestValue) { return false; } if (!Objects.equals(this.numberFormatOverride, that.numberFormatOverride)) { return false; } return super.equals(obj); } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = 193; long temp = Double.doubleToLongBits(this.base); result = 37 * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(this.smallestValue); result = 37 * result + (int) (temp ^ (temp >>> 32)); if (this.numberFormatOverride != null) { result = 37 * result + this.numberFormatOverride.hashCode(); } result = 37 * result + this.tickUnit.hashCode(); return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/LogTick.java000066400000000000000000000046531463604235500262310ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------ * LogTick.java * ------------ * (C) Copyright 2014-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.text.AttributedString; import org.jfree.chart.ui.TextAnchor; /** * A tick from a {@link LogAxis}. */ public class LogTick extends ValueTick { /** The attributed string for the tick label. */ AttributedString attributedLabel; /** * Creates a new instance. * * @param type the type (major or minor tick, {@code null} not * permitted). * @param value the value. * @param label the label ({@code null} permitted). * @param textAnchor the text anchor. */ public LogTick(TickType type, double value, AttributedString label, TextAnchor textAnchor) { super(type, value, null, textAnchor, textAnchor, 0.0); this.attributedLabel = label; } /** * Returns the attributed string for the tick label, or {@code null} * if there is no label. * * @return The attributed string or {@code null}. */ public AttributedString getAttributedLabel() { return this.attributedLabel; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/LogarithmicAxis.java000066400000000000000000001237211463604235500277620ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * LogarithmicAxis.java * -------------------- * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Original Author: Michael Duffy / Eric Thomas; * Contributor(s): David Gilbert; * David M. O'Donnell; * Scott Sams; * Sergei Ivanov; * */ package org.jfree.chart.axis; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.List; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.ValueAxisPlot; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.TextAnchor; import org.jfree.data.Range; /** * A numerical axis that uses a logarithmic scale. */ public class LogarithmicAxis extends NumberAxis { /** For serialization. */ private static final long serialVersionUID = 2502918599004103054L; /** Useful constant for log(10). */ public static final double LOG10_VALUE = Math.log(10.0); /** Smallest arbitrarily-close-to-zero value allowed. */ public static final double SMALL_LOG_VALUE = 1e-100; /** Flag set true to allow negative values in data. */ protected boolean allowNegativesFlag = false; /** * Flag set true make axis throw exception if any values are <= 0 and * 'allowNegativesFlag' is false. */ protected boolean strictValuesFlag = true; /** Number formatter for generating numeric strings. */ protected final NumberFormat numberFormatterObj = NumberFormat.getInstance(); /** Flag set true for "1e#"-style tick labels. */ protected boolean expTickLabelsFlag = false; /** Flag set true for "10^n"-style tick labels. */ protected boolean log10TickLabelsFlag = false; /** True to make 'autoAdjustRange()' select "10^n" values. */ protected boolean autoRangeNextLogFlag = false; /** Helper flag for log axis processing. */ protected boolean smallLogFlag = false; /** * Creates a new axis. * * @param label the axis label. */ public LogarithmicAxis(String label) { super(label); setupNumberFmtObj(); //setup number formatter obj } /** * Sets the 'allowNegativesFlag' flag; true to allow negative values * in data, false to be able to plot positive values arbitrarily close to * zero. * * @param flgVal the new value of the flag. */ public void setAllowNegativesFlag(boolean flgVal) { this.allowNegativesFlag = flgVal; } /** * Returns the 'allowNegativesFlag' flag; true to allow negative values * in data, false to be able to plot positive values arbitrarily close * to zero. * * @return The flag. */ public boolean getAllowNegativesFlag() { return this.allowNegativesFlag; } /** * Sets the 'strictValuesFlag' flag; if true and 'allowNegativesFlag' * is false then this axis will throw a runtime exception if any of its * values are less than or equal to zero; if false then the axis will * adjust for values less than or equal to zero as needed. * * @param flgVal true for strict enforcement. */ public void setStrictValuesFlag(boolean flgVal) { this.strictValuesFlag = flgVal; } /** * Returns the 'strictValuesFlag' flag; if true and 'allowNegativesFlag' * is false then this axis will throw a runtime exception if any of its * values are less than or equal to zero; if false then the axis will * adjust for values less than or equal to zero as needed. * * @return {@code true} if strict enforcement is enabled. */ public boolean getStrictValuesFlag() { return this.strictValuesFlag; } /** * Sets the 'expTickLabelsFlag' flag. If the 'log10TickLabelsFlag' * is false then this will set whether or not "1e#"-style tick labels * are used. The default is to use regular numeric tick labels. * * @param flgVal true for "1e#"-style tick labels, false for * log10 or regular numeric tick labels. */ public void setExpTickLabelsFlag(boolean flgVal) { this.expTickLabelsFlag = flgVal; setupNumberFmtObj(); //setup number formatter obj } /** * Returns the 'expTickLabelsFlag' flag. * * @return {@code true} for "1e#"-style tick labels, * {@code false} for log10 or regular numeric tick labels. */ public boolean getExpTickLabelsFlag() { return this.expTickLabelsFlag; } /** * Sets the 'log10TickLabelsFlag' flag. The default value is false. * * @param flag true for "10^n"-style tick labels, false for "1e#"-style * or regular numeric tick labels. */ public void setLog10TickLabelsFlag(boolean flag) { this.log10TickLabelsFlag = flag; } /** * Returns the 'log10TickLabelsFlag' flag. * * @return {@code true} for "10^n"-style tick labels, * {@code false} for "1e#"-style or regular numeric tick * labels. */ public boolean getLog10TickLabelsFlag() { return this.log10TickLabelsFlag; } /** * Sets the 'autoRangeNextLogFlag' flag. This determines whether or * not the 'autoAdjustRange()' method will select the next "10^n" * values when determining the upper and lower bounds. The default * value is false. * * @param flag {@code true} to make the 'autoAdjustRange()' * method select the next "10^n" values, {@code false} to not. */ public void setAutoRangeNextLogFlag(boolean flag) { this.autoRangeNextLogFlag = flag; } /** * Returns the 'autoRangeNextLogFlag' flag. * * @return {@code true} if the 'autoAdjustRange()' method will * select the next "10^n" values, {@code false} if not. */ public boolean getAutoRangeNextLogFlag() { return this.autoRangeNextLogFlag; } /** * Overridden version that calls original and then sets up flag for * log axis processing. * * @param range the new range. */ @Override public void setRange(Range range) { super.setRange(range); // call parent method setupSmallLogFlag(); // setup flag based on bounds values } /** * Sets up flag for log axis processing. Set true if negative values * not allowed and the lower bound is between 0 and 10. */ protected void setupSmallLogFlag() { // set flag true if negative values not allowed and the // lower bound is between 0 and 10: double lowerVal = getRange().getLowerBound(); this.smallLogFlag = (!this.allowNegativesFlag && lowerVal < 10.0 && lowerVal > 0.0); } /** * Sets up the number formatter object according to the * 'expTickLabelsFlag' flag. */ protected void setupNumberFmtObj() { if (this.numberFormatterObj instanceof DecimalFormat) { //setup for "1e#"-style tick labels or regular // numeric tick labels, depending on flag: ((DecimalFormat) this.numberFormatterObj).applyPattern( this.expTickLabelsFlag ? "0E0" : "0.###"); } } /** * Returns the log10 value, depending on if values between 0 and * 1 are being plotted. If negative values are not allowed and * the lower bound is between 0 and 10 then a normal log is * returned; otherwise the returned value is adjusted if the * given value is less than 10. * * @param val the value. * * @return log10(val). * * @see #switchedPow10(double) */ protected double switchedLog10(double val) { return this.smallLogFlag ? Math.log(val) / LOG10_VALUE : adjustedLog10(val); } /** * Returns a power of 10, depending on if values between 0 and * 1 are being plotted. If negative values are not allowed and * the lower bound is between 0 and 10 then a normal power is * returned; otherwise the returned value is adjusted if the * given value is less than 1. * * @param val the value. * * @return 10val. * * @see #switchedLog10(double) */ public double switchedPow10(double val) { return this.smallLogFlag ? Math.pow(10.0, val) : adjustedPow10(val); } /** * Returns an adjusted log10 value for graphing purposes. The first * adjustment is that negative values are changed to positive during * the calculations, and then the answer is negated at the end. The * second is that, for values less than 10, an increasingly large * (0 to 1) scaling factor is added such that at 0 the value is * adjusted to 1, resulting in a returned result of 0. * * @param val value for which log10 should be calculated. * * @return An adjusted log10(val). * * @see #adjustedPow10(double) */ public double adjustedLog10(double val) { boolean negFlag = (val < 0.0); if (negFlag) { val = -val; // if negative then set flag and make positive } if (val < 10.0) { // if < 10 then val += (10.0 - val) / 10.0; //increase so 0 translates to 0 } //return value; negate if original value was negative: double res = Math.log(val) / LOG10_VALUE; return negFlag ? (-res) : res; } /** * Returns an adjusted power of 10 value for graphing purposes. The first * adjustment is that negative values are changed to positive during * the calculations, and then the answer is negated at the end. The * second is that, for values less than 1, a progressive logarithmic * offset is subtracted such that at 0 the returned result is also 0. * * @param val value for which power of 10 should be calculated. * * @return An adjusted 10val. * * @see #adjustedLog10(double) */ public double adjustedPow10(double val) { boolean negFlag = (val < 0.0); if (negFlag) { val = -val; // if negative then set flag and make positive } double res; if (val < 1.0) { res = (Math.pow(10, val + 1.0) - 10.0) / 9.0; //invert adjustLog10 } else { res = Math.pow(10, val); } return negFlag ? (-res) : res; } /** * Returns the largest (closest to positive infinity) double value that is * not greater than the argument, is equal to a mathematical integer and * satisfying the condition that log base 10 of the value is an integer * (i.e., the value returned will be a power of 10: 1, 10, 100, 1000, etc.). * * @param lower a double value below which a floor will be calcualted. * * @return 10N with N .. { 1 ... } */ protected double computeLogFloor(double lower) { double logFloor; if (this.allowNegativesFlag) { //negative values are allowed if (lower > 10.0) { //parameter value is > 10 // The Math.log() function is based on e not 10. logFloor = Math.log(lower) / LOG10_VALUE; logFloor = Math.floor(logFloor); logFloor = Math.pow(10, logFloor); } else if (lower < -10.0) { //parameter value is < -10 //calculate log using positive value: logFloor = Math.log(-lower) / LOG10_VALUE; //calculate floor using negative value: logFloor = Math.floor(-logFloor); //calculate power using positive value; then negate logFloor = -Math.pow(10, -logFloor); } else { //parameter value is -10 > val < 10 logFloor = Math.floor(lower); //use as-is } } else { //negative values not allowed if (lower > 0.0) { //parameter value is > 0 // The Math.log() function is based on e not 10. logFloor = Math.log(lower) / LOG10_VALUE; logFloor = Math.floor(logFloor); logFloor = Math.pow(10, logFloor); } else { //parameter value is <= 0 logFloor = Math.floor(lower); //use as-is } } return logFloor; } /** * Returns the smallest (closest to negative infinity) double value that is * not less than the argument, is equal to a mathematical integer and * satisfying the condition that log base 10 of the value is an integer * (i.e., the value returned will be a power of 10: 1, 10, 100, 1000, etc.). * * @param upper a double value above which a ceiling will be calcualted. * * @return 10N with N .. { 1 ... } */ protected double computeLogCeil(double upper) { double logCeil; if (this.allowNegativesFlag) { //negative values are allowed if (upper > 10.0) { //parameter value is > 10 // The Math.log() function is based on e not 10. logCeil = Math.log(upper) / LOG10_VALUE; logCeil = Math.ceil(logCeil); logCeil = Math.pow(10, logCeil); } else if (upper < -10.0) { //parameter value is < -10 //calculate log using positive value: logCeil = Math.log(-upper) / LOG10_VALUE; //calculate ceil using negative value: logCeil = Math.ceil(-logCeil); //calculate power using positive value; then negate logCeil = -Math.pow(10, -logCeil); } else { //parameter value is -10 > val < 10 logCeil = Math.ceil(upper); //use as-is } } else { //negative values not allowed if (upper > 0.0) { //parameter value is > 0 // The Math.log() function is based on e not 10. logCeil = Math.log(upper) / LOG10_VALUE; logCeil = Math.ceil(logCeil); logCeil = Math.pow(10, logCeil); } else { //parameter value is <= 0 logCeil = Math.ceil(upper); //use as-is } } return logCeil; } /** * Rescales the axis to ensure that all data is visible. */ @Override public void autoAdjustRange() { Plot plot = getPlot(); if (plot == null) { return; // no plot, no data. } if (plot instanceof ValueAxisPlot) { ValueAxisPlot vap = (ValueAxisPlot) plot; double lower; Range r = vap.getDataRange(this); if (r == null) { //no real data present r = getDefaultAutoRange(); lower = r.getLowerBound(); //get lower bound value } else { //actual data is present lower = r.getLowerBound(); //get lower bound value if (this.strictValuesFlag && !this.allowNegativesFlag && lower <= 0.0) { //strict flag set, allow-negatives not set and values <= 0 throw new RuntimeException("Values less than or equal to " + "zero not allowed with logarithmic axis"); } } //apply lower margin by decreasing lower bound: final double lowerMargin; if (lower > 0.0 && (lowerMargin = getLowerMargin()) > 0.0) { //lower bound and margin OK; get log10 of lower bound final double logLower = (Math.log(lower) / LOG10_VALUE); double logAbs; //get absolute value of log10 value if ((logAbs = Math.abs(logLower)) < 1.0) { logAbs = 1.0; //if less than 1.0 then make it 1.0 } //subtract out margin and get exponential value: lower = Math.pow(10, (logLower - (logAbs * lowerMargin))); } //if flag then change to log version of lowest value // to make range begin at a 10^n value: if (this.autoRangeNextLogFlag) { lower = computeLogFloor(lower); } if (!this.allowNegativesFlag && lower >= 0.0 && lower < SMALL_LOG_VALUE) { //negatives not allowed and lower range bound is zero lower = r.getLowerBound(); //use data range bound instead } double upper = r.getUpperBound(); //apply upper margin by increasing upper bound: final double upperMargin; if (upper > 0.0 && (upperMargin = getUpperMargin()) > 0.0) { //upper bound and margin OK; get log10 of upper bound final double logUpper = (Math.log(upper) / LOG10_VALUE); double logAbs; //get absolute value of log10 value if ((logAbs = Math.abs(logUpper)) < 1.0) { logAbs = 1.0; //if less than 1.0 then make it 1.0 } //add in margin and get exponential value: upper = Math.pow(10, (logUpper + (logAbs * upperMargin))); } if (!this.allowNegativesFlag && upper < 1.0 && upper > 0.0 && lower > 0.0) { //negatives not allowed and upper bound between 0 & 1 //round up to nearest significant digit for bound: //get negative exponent: double expVal = Math.log(upper) / LOG10_VALUE; expVal = Math.ceil(-expVal + 0.001); //get positive exponent expVal = Math.pow(10, expVal); //create multiplier value //multiply, round up, and divide for bound value: upper = (expVal > 0.0) ? Math.ceil(upper * expVal) / expVal : Math.ceil(upper); } else { //negatives allowed or upper bound not between 0 & 1 //if flag then change to log version of highest value to // make range begin at a 10^n value; else use nearest int upper = (this.autoRangeNextLogFlag) ? computeLogCeil(upper) : Math.ceil(upper); } // ensure the autorange is at least in size... double minRange = getAutoRangeMinimumSize(); if (upper - lower < minRange) { upper = (upper + lower + minRange) / 2; lower = (upper + lower - minRange) / 2; //if autorange still below minimum then adjust by 1% // (can be needed when minRange is very small): if (upper - lower < minRange) { double absUpper = Math.abs(upper); //need to account for case where upper==0.0 double adjVal = (absUpper > SMALL_LOG_VALUE) ? absUpper / 100.0 : 0.01; upper = (upper + lower + adjVal) / 2; lower = (upper + lower - adjVal) / 2; } } setRange(new Range(lower, upper), false, false); setupSmallLogFlag(); //setup flag based on bounds values } } /** * Converts a data value to a coordinate in Java2D space, assuming that * the axis runs along one edge of the specified plotArea. * Note that it is possible for the coordinate to fall outside the * plotArea. * * @param value the data value. * @param plotArea the area for plotting the data. * @param edge the axis location. * * @return The Java2D coordinate. */ @Override public double valueToJava2D(double value, Rectangle2D plotArea, RectangleEdge edge) { Range range = getRange(); double axisMin = switchedLog10(range.getLowerBound()); double axisMax = switchedLog10(range.getUpperBound()); double min = 0.0; double max = 0.0; if (RectangleEdge.isTopOrBottom(edge)) { min = plotArea.getMinX(); max = plotArea.getMaxX(); } else if (RectangleEdge.isLeftOrRight(edge)) { min = plotArea.getMaxY(); max = plotArea.getMinY(); } value = switchedLog10(value); if (isInverted()) { return max - (((value - axisMin) / (axisMax - axisMin)) * (max - min)); } else { return min + (((value - axisMin) / (axisMax - axisMin)) * (max - min)); } } /** * Converts a coordinate in Java2D space to the corresponding data * value, assuming that the axis runs along one edge of the specified * plotArea. * * @param java2DValue the coordinate in Java2D space. * @param plotArea the area in which the data is plotted. * @param edge the axis location. * * @return The data value. */ @Override public double java2DToValue(double java2DValue, Rectangle2D plotArea, RectangleEdge edge) { Range range = getRange(); double axisMin = switchedLog10(range.getLowerBound()); double axisMax = switchedLog10(range.getUpperBound()); double plotMin = 0.0; double plotMax = 0.0; if (RectangleEdge.isTopOrBottom(edge)) { plotMin = plotArea.getX(); plotMax = plotArea.getMaxX(); } else if (RectangleEdge.isLeftOrRight(edge)) { plotMin = plotArea.getMaxY(); plotMax = plotArea.getMinY(); } if (isInverted()) { return switchedPow10(axisMax - ((java2DValue - plotMin) / (plotMax - plotMin)) * (axisMax - axisMin)); } else { return switchedPow10(axisMin + ((java2DValue - plotMin) / (plotMax - plotMin)) * (axisMax - axisMin)); } } /** * Zooms in on the current range. * * @param lowerPercent the new lower bound. * @param upperPercent the new upper bound. */ @Override public void zoomRange(double lowerPercent, double upperPercent) { double startLog = switchedLog10(getRange().getLowerBound()); double lengthLog = switchedLog10(getRange().getUpperBound()) - startLog; Range adjusted; if (isInverted()) { adjusted = new Range( switchedPow10(startLog + (lengthLog * (1 - upperPercent))), switchedPow10(startLog + (lengthLog * (1 - lowerPercent)))); } else { adjusted = new Range( switchedPow10(startLog + (lengthLog * lowerPercent)), switchedPow10(startLog + (lengthLog * upperPercent))); } setRange(adjusted); } /** * Calculates the positions of the tick labels for the axis, storing the * results in the tick label list (ready for drawing). * * @param g2 the graphics device. * @param dataArea the area in which the plot should be drawn. * @param edge the location of the axis. * * @return A list of ticks. */ @Override protected List refreshTicksHorizontal(Graphics2D g2, Rectangle2D dataArea, RectangleEdge edge) { List ticks = new java.util.ArrayList(); Range range = getRange(); //get lower bound value: double lowerBoundVal = range.getLowerBound(); //if small log values and lower bound value too small // then set to a small value (don't allow <= 0): if (this.smallLogFlag && lowerBoundVal < SMALL_LOG_VALUE) { lowerBoundVal = SMALL_LOG_VALUE; } //get upper bound value double upperBoundVal = range.getUpperBound(); //get log10 version of lower bound and round to integer: int iBegCount = (int) Math.rint(switchedLog10(lowerBoundVal)); //get log10 version of upper bound and round to integer: int iEndCount = (int) Math.rint(switchedLog10(upperBoundVal)); if (iBegCount == iEndCount && iBegCount > 0 && Math.pow(10, iBegCount) > lowerBoundVal) { //only 1 power of 10 value, it's > 0 and its resulting // tick value will be larger than lower bound of data --iBegCount; //decrement to generate more ticks } double currentTickValue; String tickLabel; boolean zeroTickFlag = false; for (int i = iBegCount; i <= iEndCount; i++) { //for each power of 10 value; create ten ticks for (int j = 0; j < 10; ++j) { //for each tick to be displayed if (this.smallLogFlag) { //small log values in use; create numeric value for tick currentTickValue = Math.pow(10, i) + (Math.pow(10, i) * j); if (this.expTickLabelsFlag || (i < 0 && currentTickValue > 0.0 && currentTickValue < 1.0)) { //showing "1e#"-style ticks or negative exponent // generating tick value between 0 & 1; show fewer if (j == 0 || (i > -4 && j < 2) || currentTickValue >= upperBoundVal) { //first tick of series, or not too small a value and // one of first 3 ticks, or last tick to be displayed // set exact number of fractional digits to be shown // (no effect if showing "1e#"-style ticks): this.numberFormatterObj .setMaximumFractionDigits(-i); //create tick label (force use of fmt obj): tickLabel = makeTickLabel(currentTickValue, true); } else { //no tick label to be shown tickLabel = ""; } } else { //tick value not between 0 & 1 //show tick label if it's the first or last in // the set, or if it's 1-5; beyond that show // fewer as the values get larger: tickLabel = (j < 1 || (i < 1 && j < 5) || (j < 4 - i) || currentTickValue >= upperBoundVal) ? makeTickLabel(currentTickValue) : ""; } } else { //not small log values in use; allow for values <= 0 if (zeroTickFlag) { //if did zero tick last iter then --j; //decrement to do 1.0 tick now } //calculate power-of-ten value for tick: currentTickValue = (i >= 0) ? Math.pow(10, i) + (Math.pow(10, i) * j) : -(Math.pow(10, -i) - (Math.pow(10, -i - 1) * j)); if (!zeroTickFlag) { // did not do zero tick last iteration if (Math.abs(currentTickValue - 1.0) < 0.0001 && lowerBoundVal <= 0.0 && upperBoundVal >= 0.0) { //tick value is 1.0 and 0.0 is within data range currentTickValue = 0.0; //set tick value to zero zeroTickFlag = true; //indicate zero tick } } else { //did zero tick last iteration zeroTickFlag = false; //clear flag } //create tick label string: //show tick label if "1e#"-style and it's one // of the first two, if it's the first or last // in the set, or if it's 1-5; beyond that // show fewer as the values get larger: tickLabel = ((this.expTickLabelsFlag && j < 2) || j < 1 || (i < 1 && j < 5) || (j < 4 - i) || currentTickValue >= upperBoundVal) ? makeTickLabel(currentTickValue) : ""; } if (currentTickValue > upperBoundVal) { return ticks; // if past highest data value then exit // method } if (currentTickValue >= lowerBoundVal - SMALL_LOG_VALUE) { //tick value not below lowest data value TextAnchor anchor; TextAnchor rotationAnchor; double angle = 0.0; if (isVerticalTickLabels()) { anchor = TextAnchor.CENTER_RIGHT; rotationAnchor = TextAnchor.CENTER_RIGHT; if (edge == RectangleEdge.TOP) { angle = Math.PI / 2.0; } else { angle = -Math.PI / 2.0; } } else { if (edge == RectangleEdge.TOP) { anchor = TextAnchor.BOTTOM_CENTER; rotationAnchor = TextAnchor.BOTTOM_CENTER; } else { anchor = TextAnchor.TOP_CENTER; rotationAnchor = TextAnchor.TOP_CENTER; } } Tick tick = new NumberTick(currentTickValue, tickLabel, anchor, rotationAnchor, angle); ticks.add(tick); } } } return ticks; } /** * Calculates the positions of the tick labels for the axis, storing the * results in the tick label list (ready for drawing). * * @param g2 the graphics device. * @param dataArea the area in which the plot should be drawn. * @param edge the location of the axis. * * @return A list of ticks. */ @Override protected List refreshTicksVertical(Graphics2D g2, Rectangle2D dataArea, RectangleEdge edge) { List ticks = new java.util.ArrayList(); //get lower bound value: double lowerBoundVal = getRange().getLowerBound(); //if small log values and lower bound value too small // then set to a small value (don't allow <= 0): if (this.smallLogFlag && lowerBoundVal < SMALL_LOG_VALUE) { lowerBoundVal = SMALL_LOG_VALUE; } //get upper bound value double upperBoundVal = getRange().getUpperBound(); //get log10 version of lower bound and round to integer: int iBegCount = (int) Math.rint(switchedLog10(lowerBoundVal)); //get log10 version of upper bound and round to integer: int iEndCount = (int) Math.rint(switchedLog10(upperBoundVal)); if (iBegCount == iEndCount && iBegCount > 0 && Math.pow(10, iBegCount) > lowerBoundVal) { //only 1 power of 10 value, it's > 0 and its resulting // tick value will be larger than lower bound of data --iBegCount; //decrement to generate more ticks } double tickVal; String tickLabel; boolean zeroTickFlag = false; for (int i = iBegCount; i <= iEndCount; i++) { //for each tick with a label to be displayed int jEndCount = 10; if (i == iEndCount) { jEndCount = 1; } for (int j = 0; j < jEndCount; j++) { //for each tick to be displayed if (this.smallLogFlag) { //small log values in use tickVal = Math.pow(10, i) + (Math.pow(10, i) * j); if (j == 0) { //first tick of group; create label text if (this.log10TickLabelsFlag) { //if flag then tickLabel = "10^" + i; //create "log10"-type label } else { //not "log10"-type label if (this.expTickLabelsFlag) { //if flag then tickLabel = "1e" + i; //create "1e#"-type label } else { //not "1e#"-type label if (i >= 0) { // if positive exponent then // make integer NumberFormat format = getNumberFormatOverride(); if (format != null) { tickLabel = format.format(tickVal); } else { tickLabel = Long.toString((long) Math.rint(tickVal)); } } else { //negative exponent; create fractional value //set exact number of fractional digits to // be shown: this.numberFormatterObj .setMaximumFractionDigits(-i); //create tick label: tickLabel = this.numberFormatterObj.format( tickVal); } } } } else { //not first tick to be displayed tickLabel = ""; //no tick label } } else { //not small log values in use; allow for values <= 0 if (zeroTickFlag) { //if did zero tick last iter then --j; } //decrement to do 1.0 tick now tickVal = (i >= 0) ? Math.pow(10, i) + (Math.pow(10, i) * j) : -(Math.pow(10, -i) - (Math.pow(10, -i - 1) * j)); if (j == 0) { //first tick of group if (!zeroTickFlag) { // did not do zero tick last // iteration if (i > iBegCount && i < iEndCount && Math.abs(tickVal - 1.0) < 0.0001) { // not first or last tick on graph and value // is 1.0 tickVal = 0.0; //change value to 0.0 zeroTickFlag = true; //indicate zero tick tickLabel = "0"; //create label for tick } else { //first or last tick on graph or value is 1.0 //create label for tick: if (this.log10TickLabelsFlag) { //create "log10"-type label tickLabel = (((i < 0) ? "-" : "") + "10^" + Math.abs(i)); } else { if (this.expTickLabelsFlag) { //create "1e#"-type label tickLabel = (((i < 0) ? "-" : "") + "1e" + Math.abs(i)); } else { NumberFormat format = getNumberFormatOverride(); if (format != null) { tickLabel = format.format(tickVal); } else { tickLabel = Long.toString( (long) Math.rint(tickVal)); } } } } } else { // did zero tick last iteration tickLabel = ""; //no label zeroTickFlag = false; //clear flag } } else { // not first tick of group tickLabel = ""; //no label zeroTickFlag = false; //make sure flag cleared } } if (tickVal > upperBoundVal) { return ticks; //if past highest data value then exit method } if (tickVal >= lowerBoundVal - SMALL_LOG_VALUE) { //tick value not below lowest data value TextAnchor anchor; TextAnchor rotationAnchor; double angle = 0.0; if (isVerticalTickLabels()) { if (edge == RectangleEdge.LEFT) { anchor = TextAnchor.BOTTOM_CENTER; rotationAnchor = TextAnchor.BOTTOM_CENTER; angle = -Math.PI / 2.0; } else { anchor = TextAnchor.BOTTOM_CENTER; rotationAnchor = TextAnchor.BOTTOM_CENTER; angle = Math.PI / 2.0; } } else { if (edge == RectangleEdge.LEFT) { anchor = TextAnchor.CENTER_RIGHT; rotationAnchor = TextAnchor.CENTER_RIGHT; } else { anchor = TextAnchor.CENTER_LEFT; rotationAnchor = TextAnchor.CENTER_LEFT; } } //create tick object and add to list: ticks.add(new NumberTick(tickVal, tickLabel, anchor, rotationAnchor, angle)); } } } return ticks; } /** * Converts the given value to a tick label string. * * @param val the value to convert. * @param forceFmtFlag true to force the number-formatter object * to be used. * * @return The tick label string. */ protected String makeTickLabel(double val, boolean forceFmtFlag) { if (this.expTickLabelsFlag || forceFmtFlag) { //using exponents or force-formatter flag is set // (convert 'E' to lower-case 'e'): return this.numberFormatterObj.format(val).toLowerCase(); } return getTickUnit().valueToString(val); } /** * Converts the given value to a tick label string. * @param val the value to convert. * * @return The tick label string. */ protected String makeTickLabel(double val) { return makeTickLabel(val, false); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/MarkerAxisBand.java000066400000000000000000000203631463604235500275240ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * MarkerAxisBand.java * ------------------- * (C) Copyright 2000-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Composite; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.font.LineMetrics; import java.awt.geom.Rectangle2D; import java.io.Serializable; import java.util.Iterator; import java.util.List; import java.util.Objects; import org.jfree.chart.plot.IntervalMarker; import org.jfree.chart.text.TextUtils; import org.jfree.chart.ui.RectangleEdge; /** * A band that can be added to a number axis to display regions. */ public class MarkerAxisBand implements Serializable { /** For serialization. */ private static final long serialVersionUID = -1729482413886398919L; /** The axis that the band belongs to. */ private NumberAxis axis; /** The top outer gap. */ private double topOuterGap; /** The top inner gap. */ private double topInnerGap; /** The bottom outer gap. */ private double bottomOuterGap; /** The bottom inner gap. */ private double bottomInnerGap; /** The font. */ private Font font; /** Storage for the markers. */ private List markers; /** * Constructs a new axis band. * * @param axis the owner. * @param topOuterGap the top outer gap. * @param topInnerGap the top inner gap. * @param bottomOuterGap the bottom outer gap. * @param bottomInnerGap the bottom inner gap. * @param font the font. */ public MarkerAxisBand(NumberAxis axis, double topOuterGap, double topInnerGap, double bottomOuterGap, double bottomInnerGap, Font font) { this.axis = axis; this.topOuterGap = topOuterGap; this.topInnerGap = topInnerGap; this.bottomOuterGap = bottomOuterGap; this.bottomInnerGap = bottomInnerGap; this.font = font; this.markers = new java.util.ArrayList(); } /** * Adds a marker to the band. * * @param marker the marker. */ public void addMarker(IntervalMarker marker) { this.markers.add(marker); } /** * Returns the height of the band. * * @param g2 the graphics device. * * @return The height of the band. */ public double getHeight(Graphics2D g2) { double result = 0.0; if (this.markers.size() > 0) { LineMetrics metrics = this.font.getLineMetrics( "123g", g2.getFontRenderContext() ); result = this.topOuterGap + this.topInnerGap + metrics.getHeight() + this.bottomInnerGap + this.bottomOuterGap; } return result; } /** * A utility method that draws a string inside a rectangle. * * @param g2 the graphics device. * @param bounds the rectangle. * @param font the font. * @param text the text. */ private void drawStringInRect(Graphics2D g2, Rectangle2D bounds, Font font, String text) { g2.setFont(font); FontMetrics fm = g2.getFontMetrics(font); Rectangle2D r = TextUtils.getTextBounds(text, g2, fm); double x = bounds.getX(); if (r.getWidth() < bounds.getWidth()) { x = x + (bounds.getWidth() - r.getWidth()) / 2; } LineMetrics metrics = font.getLineMetrics( text, g2.getFontRenderContext() ); g2.drawString( text, (float) x, (float) (bounds.getMaxY() - this.bottomInnerGap - metrics.getDescent()) ); } /** * Draws the band. * * @param g2 the graphics device. * @param plotArea the plot area. * @param dataArea the data area. * @param x the x-coordinate. * @param y the y-coordinate. */ public void draw(Graphics2D g2, Rectangle2D plotArea, Rectangle2D dataArea, double x, double y) { double h = getHeight(g2); Iterator iterator = this.markers.iterator(); while (iterator.hasNext()) { IntervalMarker marker = (IntervalMarker) iterator.next(); double start = Math.max( marker.getStartValue(), this.axis.getRange().getLowerBound() ); double end = Math.min( marker.getEndValue(), this.axis.getRange().getUpperBound() ); double s = this.axis.valueToJava2D( start, dataArea, RectangleEdge.BOTTOM ); double e = this.axis.valueToJava2D( end, dataArea, RectangleEdge.BOTTOM ); Rectangle2D r = new Rectangle2D.Double( s, y + this.topOuterGap, e - s, h - this.topOuterGap - this.bottomOuterGap ); Composite originalComposite = g2.getComposite(); g2.setComposite(AlphaComposite.getInstance( AlphaComposite.SRC_OVER, marker.getAlpha()) ); g2.setPaint(marker.getPaint()); g2.fill(r); g2.setPaint(marker.getOutlinePaint()); g2.draw(r); g2.setComposite(originalComposite); g2.setPaint(Color.BLACK); drawStringInRect(g2, r, this.font, marker.getLabel()); } } /** * Tests this axis for equality with another object. Note that the axis * that the band belongs to is ignored in the test. * * @param obj the object ({@code null} permitted). * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof MarkerAxisBand)) { return false; } MarkerAxisBand that = (MarkerAxisBand) obj; if (this.topOuterGap != that.topOuterGap) { return false; } if (this.topInnerGap != that.topInnerGap) { return false; } if (this.bottomInnerGap != that.bottomInnerGap) { return false; } if (this.bottomOuterGap != that.bottomOuterGap) { return false; } if (!Objects.equals(this.font, that.font)) { return false; } if (!Objects.equals(this.markers, that.markers)) { return false; } return true; } /** * Returns a hash code for the object. * * @return A hash code. */ @Override public int hashCode() { int result = 37; result = 19 * result + this.font.hashCode(); result = 19 * result + this.markers.hashCode(); return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/ModuloAxis.java000066400000000000000000000334751463604235500267650ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * ModuloAxis.java * --------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.awt.geom.Rectangle2D; import org.jfree.chart.event.AxisChangeEvent; import org.jfree.chart.ui.RectangleEdge; import org.jfree.data.Range; /** * An axis that displays numerical values within a fixed range using a modulo * calculation. */ public class ModuloAxis extends NumberAxis { /** * The fixed range for the axis - all data values will be mapped to this * range using a modulo calculation. */ private Range fixedRange; /** * The display start value (this will sometimes be > displayEnd, in which * case the axis wraps around at some point in the middle of the axis). */ private double displayStart; /** * The display end value. */ private double displayEnd; /** * Creates a new axis. * * @param label the axis label ({@code null} permitted). * @param fixedRange the fixed range ({@code null} not permitted). */ public ModuloAxis(String label, Range fixedRange) { super(label); this.fixedRange = fixedRange; this.displayStart = 270.0; this.displayEnd = 90.0; } /** * Returns the display start value. * * @return The display start value. */ public double getDisplayStart() { return this.displayStart; } /** * Returns the display end value. * * @return The display end value. */ public double getDisplayEnd() { return this.displayEnd; } /** * Sets the display range. The values will be mapped to the fixed range if * necessary. * * @param start the start value. * @param end the end value. */ public void setDisplayRange(double start, double end) { this.displayStart = mapValueToFixedRange(start); this.displayEnd = mapValueToFixedRange(end); if (this.displayStart < this.displayEnd) { setRange(this.displayStart, this.displayEnd); } else { setRange(this.displayStart, this.fixedRange.getUpperBound() + (this.displayEnd - this.fixedRange.getLowerBound())); } notifyListeners(new AxisChangeEvent(this)); } /** * This method should calculate a range that will show all the data values. * For now, it just sets the axis range to the fixedRange. */ @Override protected void autoAdjustRange() { setRange(this.fixedRange, false, false); } /** * Translates a data value to a Java2D coordinate. * * @param value the value. * @param area the area. * @param edge the edge. * * @return A Java2D coordinate. */ @Override public double valueToJava2D(double value, Rectangle2D area, RectangleEdge edge) { double result; double v = mapValueToFixedRange(value); if (this.displayStart < this.displayEnd) { // regular number axis result = trans(v, area, edge); } else { // displayStart > displayEnd, need to handle split double cutoff = (this.displayStart + this.displayEnd) / 2.0; double length1 = this.fixedRange.getUpperBound() - this.displayStart; double length2 = this.displayEnd - this.fixedRange.getLowerBound(); if (v > cutoff) { result = transStart(v, area, edge, length1, length2); } else { result = transEnd(v, area, edge, length1, length2); } } return result; } /** * A regular translation from a data value to a Java2D value. * * @param value the value. * @param area the data area. * @param edge the edge along which the axis lies. * * @return The Java2D coordinate. */ private double trans(double value, Rectangle2D area, RectangleEdge edge) { double min = 0.0; double max = 0.0; if (RectangleEdge.isTopOrBottom(edge)) { min = area.getX(); max = area.getX() + area.getWidth(); } else if (RectangleEdge.isLeftOrRight(edge)) { min = area.getMaxY(); max = area.getMaxY() - area.getHeight(); } if (isInverted()) { return max - ((value - this.displayStart) / (this.displayEnd - this.displayStart)) * (max - min); } else { return min + ((value - this.displayStart) / (this.displayEnd - this.displayStart)) * (max - min); } } /** * Translates a data value to a Java2D value for the first section of the * axis. * * @param value the value. * @param area the data area. * @param edge the edge along which the axis lies. * @param length1 the length of the first section. * @param length2 the length of the second section. * * @return The Java2D coordinate. */ private double transStart(double value, Rectangle2D area, RectangleEdge edge, double length1, double length2) { double min = 0.0; double max = 0.0; if (RectangleEdge.isTopOrBottom(edge)) { min = area.getX(); max = area.getX() + area.getWidth() * length1 / (length1 + length2); } else if (RectangleEdge.isLeftOrRight(edge)) { min = area.getMaxY(); max = area.getMaxY() - area.getHeight() * length1 / (length1 + length2); } if (isInverted()) { return max - ((value - this.displayStart) / (this.fixedRange.getUpperBound() - this.displayStart)) * (max - min); } else { return min + ((value - this.displayStart) / (this.fixedRange.getUpperBound() - this.displayStart)) * (max - min); } } /** * Translates a data value to a Java2D value for the second section of the * axis. * * @param value the value. * @param area the data area. * @param edge the edge along which the axis lies. * @param length1 the length of the first section. * @param length2 the length of the second section. * * @return The Java2D coordinate. */ private double transEnd(double value, Rectangle2D area, RectangleEdge edge, double length1, double length2) { double min = 0.0; double max = 0.0; if (RectangleEdge.isTopOrBottom(edge)) { max = area.getMaxX(); min = area.getMaxX() - area.getWidth() * length2 / (length1 + length2); } else if (RectangleEdge.isLeftOrRight(edge)) { max = area.getMinY(); min = area.getMinY() + area.getHeight() * length2 / (length1 + length2); } if (isInverted()) { return max - ((value - this.fixedRange.getLowerBound()) / (this.displayEnd - this.fixedRange.getLowerBound())) * (max - min); } else { return min + ((value - this.fixedRange.getLowerBound()) / (this.displayEnd - this.fixedRange.getLowerBound())) * (max - min); } } /** * Maps a data value into the fixed range. * * @param value the value. * * @return The mapped value. */ private double mapValueToFixedRange(double value) { double lower = this.fixedRange.getLowerBound(); double length = this.fixedRange.getLength(); if (value < lower) { return lower + length + ((value - lower) % length); } else { return lower + ((value - lower) % length); } } /** * Translates a Java2D coordinate into a data value. * * @param java2DValue the Java2D coordinate. * @param area the area. * @param edge the edge. * * @return The Java2D coordinate. */ @Override public double java2DToValue(double java2DValue, Rectangle2D area, RectangleEdge edge) { double result = 0.0; if (this.displayStart < this.displayEnd) { // regular number axis result = super.java2DToValue(java2DValue, area, edge); } else { // displayStart > displayEnd, need to handle split } return result; } /** * Returns the display length for the axis. * * @return The display length. */ private double getDisplayLength() { if (this.displayStart < this.displayEnd) { return (this.displayEnd - this.displayStart); } else { return (this.fixedRange.getUpperBound() - this.displayStart) + (this.displayEnd - this.fixedRange.getLowerBound()); } } /** * Returns the central value of the current display range. * * @return The central value. */ private double getDisplayCentralValue() { return mapValueToFixedRange(this.displayStart + (getDisplayLength() / 2)); } /** * Increases or decreases the axis range by the specified percentage about * the central value and sends an {@link AxisChangeEvent} to all registered * listeners. *

* To double the length of the axis range, use 200% (2.0). * To halve the length of the axis range, use 50% (0.5). * * @param percent the resize factor. */ @Override public void resizeRange(double percent) { resizeRange(percent, getDisplayCentralValue()); } /** * Increases or decreases the axis range by the specified percentage about * the specified anchor value and sends an {@link AxisChangeEvent} to all * registered listeners. *

* To double the length of the axis range, use 200% (2.0). * To halve the length of the axis range, use 50% (0.5). * * @param percent the resize factor. * @param anchorValue the new central value after the resize. */ @Override public void resizeRange(double percent, double anchorValue) { if (percent > 0.0) { double halfLength = getDisplayLength() * percent / 2; setDisplayRange(anchorValue - halfLength, anchorValue + halfLength); } else { setAutoRange(true); } } /** * Converts a length in data coordinates into the corresponding length in * Java2D coordinates. * * @param length the length. * @param area the plot area. * @param edge the edge along which the axis lies. * * @return The length in Java2D coordinates. */ @Override public double lengthToJava2D(double length, Rectangle2D area, RectangleEdge edge) { double axisLength = 0.0; if (this.displayEnd > this.displayStart) { axisLength = this.displayEnd - this.displayStart; } else { axisLength = (this.fixedRange.getUpperBound() - this.displayStart) + (this.displayEnd - this.fixedRange.getLowerBound()); } double areaLength; if (RectangleEdge.isLeftOrRight(edge)) { areaLength = area.getHeight(); } else { areaLength = area.getWidth(); } return (length / axisLength) * areaLength; } /** * Tests this axis for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof ModuloAxis)) { return false; } ModuloAxis that = (ModuloAxis) obj; if (this.displayStart != that.displayStart) { return false; } if (this.displayEnd != that.displayEnd) { return false; } if (!this.fixedRange.equals(that.fixedRange)) { return false; } return super.equals(obj); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/MonthDateFormat.java000066400000000000000000000217351463604235500277310ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * MonthDateFormat.java * -------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.text.DateFormat; import java.text.DateFormatSymbols; import java.text.FieldPosition; import java.text.NumberFormat; import java.text.ParsePosition; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.Locale; import java.util.TimeZone; import org.jfree.chart.util.Args; /** * A formatter that formats dates to show the initial letter(s) of the month * name and, as an option, the year for the first or last month of each year. */ public class MonthDateFormat extends DateFormat { /** The symbols used for the months. */ private String[] months; /** Flags that control which months will have the year appended. */ private boolean[] showYear; /** The year formatter. */ private DateFormat yearFormatter; /** * Creates a new instance for the default time zone. */ public MonthDateFormat() { this(TimeZone.getDefault()); } /** * Creates a new instance for the specified time zone. * * @param zone the time zone ({@code null} not permitted). */ public MonthDateFormat(TimeZone zone) { this(zone, Locale.getDefault(), 1, true, false); } /** * Creates a new instance for the specified time zone. * * @param locale the locale used to obtain the month * names ({@code null} not permitted). */ public MonthDateFormat(Locale locale) { this(TimeZone.getDefault(), locale, 1, true, false); } /** * Creates a new instance for the specified time zone. * * @param zone the time zone ({@code null} not permitted). * @param chars the maximum number of characters to use from the month * names (that are obtained from the date symbols of the * default locale). If this value is <= 0, the entire * month name is used in each case. */ public MonthDateFormat(TimeZone zone, int chars) { this(zone, Locale.getDefault(), chars, true, false); } /** * Creates a new instance for the specified time zone. * * @param locale the locale ({@code null} not permitted). * @param chars the maximum number of characters to use from the month * names (that are obtained from the date symbols of the * default locale). If this value is <= 0, the entire * month name is used in each case. */ public MonthDateFormat(Locale locale, int chars) { this(TimeZone.getDefault(), locale, chars, true, false); } /** * Creates a new formatter. * * @param zone the time zone used to extract the month and year from dates * passed to this formatter ({@code null} not permitted). * @param locale the locale used to determine the month names * ({@code null} not permitted). * @param chars the maximum number of characters to use from the month * names, or zero to indicate that the entire month name * should be used. * @param showYearForJan a flag that controls whether or not the year is * appended to the symbol for the first month of * each year. * @param showYearForDec a flag that controls whether or not the year is * appended to the symbol for the last month of * each year. */ public MonthDateFormat(TimeZone zone, Locale locale, int chars, boolean showYearForJan, boolean showYearForDec) { this(zone, locale, chars, new boolean[] {showYearForJan, false, false, false, false, false, false, false, false, false, false, false, showYearForDec}, new SimpleDateFormat("yy")); } /** * Creates a new formatter. * * @param zone the time zone used to extract the month and year from dates * passed to this formatter ({@code null} not permitted). * @param locale the locale used to determine the month names * ({@code null} not permitted). * @param chars the maximum number of characters to use from the month * names, or zero to indicate that the entire month name * should be used. * @param showYear an array of flags that control whether or not the * year is displayed for a particular month. * @param yearFormatter the year formatter. */ public MonthDateFormat(TimeZone zone, Locale locale, int chars, boolean[] showYear, DateFormat yearFormatter) { Args.nullNotPermitted(locale, "locale"); DateFormatSymbols dfs = new DateFormatSymbols(locale); String[] monthsFromLocale = dfs.getMonths(); this.months = new String[12]; for (int i = 0; i < 12; i++) { if (chars > 0) { this.months[i] = monthsFromLocale[i].substring(0, Math.min(chars, monthsFromLocale[i].length())); } else { this.months[i] = monthsFromLocale[i]; } } this.calendar = new GregorianCalendar(zone); this.showYear = showYear; this.yearFormatter = yearFormatter; // the following is never used, but it seems that DateFormat requires // it to be non-null. It isn't well covered in the spec, refer to // bug parade 5061189 for more info. this.numberFormat = NumberFormat.getNumberInstance(); } /** * Formats the given date. * * @param date the date. * @param toAppendTo the string buffer. * @param fieldPosition the field position. * * @return The formatted date. */ @Override public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) { this.calendar.setTime(date); int month = this.calendar.get(Calendar.MONTH); toAppendTo.append(this.months[month]); if (this.showYear[month]) { toAppendTo.append(this.yearFormatter.format(date)); } return toAppendTo; } /** * Parses the given string (not implemented). * * @param source the date string. * @param pos the parse position. * * @return {@code null}, as this method has not been implemented. */ @Override public Date parse(String source, ParsePosition pos) { return null; } /** * Tests this formatter for equality with an arbitrary object. * * @param obj the object. * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof MonthDateFormat)) { return false; } if (!super.equals(obj)) { return false; } MonthDateFormat that = (MonthDateFormat) obj; if (!Arrays.equals(this.months, that.months)) { return false; } if (!Arrays.equals(this.showYear, that.showYear)) { return false; } if (!this.yearFormatter.equals(that.yearFormatter)) { return false; } return true; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/NumberAxis.java000066400000000000000000001203111463604235500267400ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * NumberAxis.java * --------------- * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Laurence Vanhelsuwe; * Peter Kolb (patches 1934255 and 2603321); * */ package org.jfree.chart.axis; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.font.FontRenderContext; import java.awt.font.LineMetrics; import java.awt.geom.Rectangle2D; import java.io.Serializable; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Objects; import org.jfree.chart.event.AxisChangeEvent; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.ValueAxisPlot; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.ui.TextAnchor; import org.jfree.chart.util.Args; import org.jfree.data.Range; import org.jfree.data.RangeType; /** * An axis for displaying numerical data. *

* If the axis is set up to automatically determine its range to fit the data, * you can ensure that the range includes zero (statisticians usually prefer * this) by setting the {@code autoRangeIncludesZero} flag to * {@code true}. *

* The {@code NumberAxis} class has a mechanism for automatically * selecting a tick unit that is appropriate for the current axis range. */ public class NumberAxis extends ValueAxis implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 2805933088476185789L; /** The default value for the autoRangeIncludesZero flag. */ public static final boolean DEFAULT_AUTO_RANGE_INCLUDES_ZERO = true; /** The default value for the autoRangeStickyZero flag. */ public static final boolean DEFAULT_AUTO_RANGE_STICKY_ZERO = true; /** The default tick unit. */ public static final NumberTickUnit DEFAULT_TICK_UNIT = new NumberTickUnit( 1.0, new DecimalFormat("0")); /** The default setting for the vertical tick labels flag. */ public static final boolean DEFAULT_VERTICAL_TICK_LABELS = false; /** * The range type (can be used to force the axis to display only positive * values or only negative values). */ private RangeType rangeType; /** * A flag that affects the axis range when the range is determined * automatically. If the auto range does NOT include zero and this flag * is TRUE, then the range is changed to include zero. */ private boolean autoRangeIncludesZero; /** * A flag that affects the size of the margins added to the axis range when * the range is determined automatically. If the value 0 falls within the * margin and this flag is TRUE, then the margin is truncated at zero. */ private boolean autoRangeStickyZero; /** The tick unit for the axis. */ private NumberTickUnit tickUnit; /** The override number format. */ private NumberFormat numberFormatOverride; /** An optional band for marking regions on the axis. */ private MarkerAxisBand markerBand; /** * Default constructor. */ public NumberAxis() { this(null); } /** * Constructs a number axis, using default values where necessary. * * @param label the axis label ({@code null} permitted). */ public NumberAxis(String label) { super(label, NumberAxis.createStandardTickUnits()); this.rangeType = RangeType.FULL; this.autoRangeIncludesZero = DEFAULT_AUTO_RANGE_INCLUDES_ZERO; this.autoRangeStickyZero = DEFAULT_AUTO_RANGE_STICKY_ZERO; this.tickUnit = DEFAULT_TICK_UNIT; this.numberFormatOverride = null; this.markerBand = null; } /** * Returns the axis range type. * * @return The axis range type (never {@code null}). * * @see #setRangeType(RangeType) */ public RangeType getRangeType() { return this.rangeType; } /** * Sets the axis range type. * * @param rangeType the range type ({@code null} not permitted). * * @see #getRangeType() */ public void setRangeType(RangeType rangeType) { Args.nullNotPermitted(rangeType, "rangeType"); this.rangeType = rangeType; notifyListeners(new AxisChangeEvent(this)); } /** * Returns the flag that indicates whether or not the automatic axis range * (if indeed it is determined automatically) is forced to include zero. * * @return The flag. */ public boolean getAutoRangeIncludesZero() { return this.autoRangeIncludesZero; } /** * Sets the flag that indicates whether or not the axis range, if * automatically calculated, is forced to include zero. *

* If the flag is changed to {@code true}, the axis range is * recalculated. *

* Any change to the flag will trigger an {@link AxisChangeEvent}. * * @param flag the new value of the flag. * * @see #getAutoRangeIncludesZero() */ public void setAutoRangeIncludesZero(boolean flag) { if (this.autoRangeIncludesZero != flag) { this.autoRangeIncludesZero = flag; if (isAutoRange()) { autoAdjustRange(); } notifyListeners(new AxisChangeEvent(this)); } } /** * Returns a flag that affects the auto-range when zero falls outside the * data range but inside the margins defined for the axis. * * @return The flag. * * @see #setAutoRangeStickyZero(boolean) */ public boolean getAutoRangeStickyZero() { return this.autoRangeStickyZero; } /** * Sets a flag that affects the auto-range when zero falls outside the data * range but inside the margins defined for the axis. * * @param flag the new flag. * * @see #getAutoRangeStickyZero() */ public void setAutoRangeStickyZero(boolean flag) { if (this.autoRangeStickyZero != flag) { this.autoRangeStickyZero = flag; if (isAutoRange()) { autoAdjustRange(); } notifyListeners(new AxisChangeEvent(this)); } } /** * Returns the tick unit for the axis. *

* Note: if the {@code autoTickUnitSelection} flag is * {@code true} the tick unit may be changed while the axis is being * drawn, so in that case the return value from this method may be * irrelevant if the method is called before the axis has been drawn. * * @return The tick unit for the axis. * * @see #setTickUnit(NumberTickUnit) * @see ValueAxis#isAutoTickUnitSelection() */ public NumberTickUnit getTickUnit() { return this.tickUnit; } /** * Sets the tick unit for the axis and sends an {@link AxisChangeEvent} to * all registered listeners. A side effect of calling this method is that * the "auto-select" feature for tick units is switched off (you can * restore it using the {@link ValueAxis#setAutoTickUnitSelection(boolean)} * method). * * @param unit the new tick unit ({@code null} not permitted). * * @see #getTickUnit() * @see #setTickUnit(NumberTickUnit, boolean, boolean) */ public void setTickUnit(NumberTickUnit unit) { // defer argument checking... setTickUnit(unit, true, true); } /** * Sets the tick unit for the axis and, if requested, sends an * {@link AxisChangeEvent} to all registered listeners. In addition, an * option is provided to turn off the "auto-select" feature for tick units * (you can restore it using the * {@link ValueAxis#setAutoTickUnitSelection(boolean)} method). * * @param unit the new tick unit ({@code null} not permitted). * @param notify notify listeners? * @param turnOffAutoSelect turn off the auto-tick selection? */ public void setTickUnit(NumberTickUnit unit, boolean notify, boolean turnOffAutoSelect) { Args.nullNotPermitted(unit, "unit"); this.tickUnit = unit; if (turnOffAutoSelect) { setAutoTickUnitSelection(false, false); } if (notify) { notifyListeners(new AxisChangeEvent(this)); } } /** * Returns the number format override. If this is non-null, then it will * be used to format the numbers on the axis. * * @return The number formatter (possibly {@code null}). * * @see #setNumberFormatOverride(NumberFormat) */ public NumberFormat getNumberFormatOverride() { return this.numberFormatOverride; } /** * Sets the number format override. If this is non-null, then it will be * used to format the numbers on the axis. * * @param formatter the number formatter ({@code null} permitted). * * @see #getNumberFormatOverride() */ public void setNumberFormatOverride(NumberFormat formatter) { this.numberFormatOverride = formatter; notifyListeners(new AxisChangeEvent(this)); } /** * Returns the (optional) marker band for the axis. * * @return The marker band (possibly {@code null}). * * @see #setMarkerBand(MarkerAxisBand) */ public MarkerAxisBand getMarkerBand() { return this.markerBand; } /** * Sets the marker band for the axis. *

* The marker band is optional, leave it set to {@code null} if you * don't require it. * * @param band the new band ({@code null} permitted). * * @see #getMarkerBand() */ public void setMarkerBand(MarkerAxisBand band) { this.markerBand = band; notifyListeners(new AxisChangeEvent(this)); } /** * Configures the axis to work with the specified plot. If the axis has * auto-scaling, then sets the maximum and minimum values. */ @Override public void configure() { if (isAutoRange()) { autoAdjustRange(); } } /** * Rescales the axis to ensure that all data is visible. */ @Override protected void autoAdjustRange() { Plot plot = getPlot(); if (plot == null) { return; // no plot, no data } if (plot instanceof ValueAxisPlot) { ValueAxisPlot vap = (ValueAxisPlot) plot; Range r = vap.getDataRange(this); if (r == null) { r = getDefaultAutoRange(); } double upper = r.getUpperBound(); double lower = r.getLowerBound(); if (this.rangeType == RangeType.POSITIVE) { lower = Math.max(0.0, lower); upper = Math.max(0.0, upper); } else if (this.rangeType == RangeType.NEGATIVE) { lower = Math.min(0.0, lower); upper = Math.min(0.0, upper); } if (getAutoRangeIncludesZero()) { lower = Math.min(lower, 0.0); upper = Math.max(upper, 0.0); } double range = upper - lower; // if fixed auto range, then derive lower bound... double fixedAutoRange = getFixedAutoRange(); if (fixedAutoRange > 0.0) { lower = upper - fixedAutoRange; } else { // ensure the autorange is at least in size... double minRange = getAutoRangeMinimumSize(); if (range < minRange) { double expand = (minRange - range) / 2; upper = upper + expand; lower = lower - expand; if (lower == upper) { // see bug report 1549218 double adjust = Math.abs(lower) / 10.0; lower = lower - adjust; upper = upper + adjust; } if (this.rangeType == RangeType.POSITIVE) { if (lower < 0.0) { upper = upper - lower; lower = 0.0; } } else if (this.rangeType == RangeType.NEGATIVE) { if (upper > 0.0) { lower = lower - upper; upper = 0.0; } } } if (getAutoRangeStickyZero()) { if (upper <= 0.0) { upper = Math.min(0.0, upper + getUpperMargin() * range); } else { upper = upper + getUpperMargin() * range; } if (lower >= 0.0) { lower = Math.max(0.0, lower - getLowerMargin() * range); } else { lower = lower - getLowerMargin() * range; } } else { upper = upper + getUpperMargin() * range; lower = lower - getLowerMargin() * range; } } setRange(new Range(lower, upper), false, false); } } /** * Converts a data value to a coordinate in Java2D space, assuming that the * axis runs along one edge of the specified dataArea. *

* Note that it is possible for the coordinate to fall outside the plotArea. * * @param value the data value. * @param area the area for plotting the data. * @param edge the axis location. * * @return The Java2D coordinate. * * @see #java2DToValue(double, Rectangle2D, RectangleEdge) */ @Override public double valueToJava2D(double value, Rectangle2D area, RectangleEdge edge) { Range range = getRange(); double axisMin = range.getLowerBound(); double axisMax = range.getUpperBound(); double min = 0.0; double max = 0.0; if (RectangleEdge.isTopOrBottom(edge)) { min = area.getX(); max = area.getMaxX(); } else if (RectangleEdge.isLeftOrRight(edge)) { max = area.getMinY(); min = area.getMaxY(); } if (isInverted()) { return max - ((value - axisMin) / (axisMax - axisMin)) * (max - min); } else { return min + ((value - axisMin) / (axisMax - axisMin)) * (max - min); } } /** * Converts a coordinate in Java2D space to the corresponding data value, * assuming that the axis runs along one edge of the specified dataArea. * * @param java2DValue the coordinate in Java2D space. * @param area the area in which the data is plotted. * @param edge the location. * * @return The data value. * * @see #valueToJava2D(double, Rectangle2D, RectangleEdge) */ @Override public double java2DToValue(double java2DValue, Rectangle2D area, RectangleEdge edge) { Range range = getRange(); double axisMin = range.getLowerBound(); double axisMax = range.getUpperBound(); double min = 0.0; double max = 0.0; if (RectangleEdge.isTopOrBottom(edge)) { min = area.getX(); max = area.getMaxX(); } else if (RectangleEdge.isLeftOrRight(edge)) { min = area.getMaxY(); max = area.getY(); } if (isInverted()) { return axisMax - (java2DValue - min) / (max - min) * (axisMax - axisMin); } else { return axisMin + (java2DValue - min) / (max - min) * (axisMax - axisMin); } } /** * Calculates the value of the lowest visible tick on the axis. * * @return The value of the lowest visible tick on the axis. * * @see #calculateHighestVisibleTickValue() */ protected double calculateLowestVisibleTickValue() { double unit = getTickUnit().getSize(); double index = Math.ceil(getRange().getLowerBound() / unit); return index * unit; } /** * Calculates the value of the highest visible tick on the axis. * * @return The value of the highest visible tick on the axis. * * @see #calculateLowestVisibleTickValue() */ protected double calculateHighestVisibleTickValue() { double unit = getTickUnit().getSize(); double index = Math.floor(getRange().getUpperBound() / unit); return index * unit; } /** * Calculates the number of visible ticks. * * @return The number of visible ticks on the axis. */ protected int calculateVisibleTickCount() { double unit = getTickUnit().getSize(); Range range = getRange(); return (int) (Math.floor(range.getUpperBound() / unit) - Math.ceil(range.getLowerBound() / unit) + 1); } /** * Draws the axis on a Java 2D graphics device (such as the screen or a * printer). * * @param g2 the graphics device ({@code null} not permitted). * @param cursor the cursor location. * @param plotArea the area within which the axes and data should be drawn * ({@code null} not permitted). * @param dataArea the area within which the data should be drawn * ({@code null} not permitted). * @param edge the location of the axis ({@code null} not permitted). * @param plotState collects information about the plot * ({@code null} permitted). * * @return The axis state (never {@code null}). */ @Override public AxisState draw(Graphics2D g2, double cursor, Rectangle2D plotArea, Rectangle2D dataArea, RectangleEdge edge, PlotRenderingInfo plotState) { AxisState state; // if the axis is not visible, don't draw it... if (!isVisible()) { state = new AxisState(cursor); // even though the axis is not visible, we need ticks for the // gridlines... List ticks = refreshTicks(g2, state, dataArea, edge); state.setTicks(ticks); return state; } // draw the tick marks and labels... state = drawTickMarksAndLabels(g2, cursor, plotArea, dataArea, edge); if (getAttributedLabel() != null) { state = drawAttributedLabel(getAttributedLabel(), g2, plotArea, dataArea, edge, state); } else { state = drawLabel(getLabel(), g2, plotArea, dataArea, edge, state); } createAndAddEntity(cursor, state, dataArea, edge, plotState); return state; } /** * Creates the standard tick units. *

* If you don't like these defaults, create your own instance of TickUnits * and then pass it to the setStandardTickUnits() method in the * NumberAxis class. * * @return The standard tick units. * * @see #setStandardTickUnits(TickUnitSource) * @see #createIntegerTickUnits() */ public static TickUnitSource createStandardTickUnits() { return new NumberTickUnitSource(); } /** * Returns a collection of tick units for integer values. * * @return A collection of tick units for integer values. * * @see #setStandardTickUnits(TickUnitSource) * @see #createStandardTickUnits() */ public static TickUnitSource createIntegerTickUnits() { return new NumberTickUnitSource(true); } /** * Creates a collection of standard tick units. The supplied locale is * used to create the number formatter (a localised instance of * {@code NumberFormat}). *

* If you don't like these defaults, create your own instance of * {@link TickUnits} and then pass it to the * {@code setStandardTickUnits()} method. * * @param locale the locale. * * @return A tick unit collection. * * @see #setStandardTickUnits(TickUnitSource) */ public static TickUnitSource createStandardTickUnits(Locale locale) { NumberFormat numberFormat = NumberFormat.getNumberInstance(locale); return new NumberTickUnitSource(false, numberFormat); } /** * Returns a collection of tick units for integer values. * Uses a given Locale to create the DecimalFormats. * * @param locale the locale to use to represent Numbers. * * @return A collection of tick units for integer values. * * @see #setStandardTickUnits(TickUnitSource) */ public static TickUnitSource createIntegerTickUnits(Locale locale) { NumberFormat numberFormat = NumberFormat.getNumberInstance(locale); return new NumberTickUnitSource(true, numberFormat); } /** * Estimates the maximum tick label height. * * @param g2 the graphics device. * * @return The maximum height. */ protected double estimateMaximumTickLabelHeight(Graphics2D g2) { RectangleInsets tickLabelInsets = getTickLabelInsets(); double result = tickLabelInsets.getTop() + tickLabelInsets.getBottom(); Font tickLabelFont = getTickLabelFont(); FontRenderContext frc = g2.getFontRenderContext(); result += tickLabelFont.getLineMetrics("123", frc).getHeight(); return result; } /** * Estimates the maximum width of the tick labels, assuming the specified * tick unit is used. *

* Rather than computing the string bounds of every tick on the axis, we * just look at two values: the lower bound and the upper bound for the * axis. These two values will usually be representative. * * @param g2 the graphics device. * @param unit the tick unit to use for calculation. * * @return The estimated maximum width of the tick labels. */ protected double estimateMaximumTickLabelWidth(Graphics2D g2, TickUnit unit) { RectangleInsets tickLabelInsets = getTickLabelInsets(); double result = tickLabelInsets.getLeft() + tickLabelInsets.getRight(); if (isVerticalTickLabels()) { // all tick labels have the same width (equal to the height of the // font)... FontRenderContext frc = g2.getFontRenderContext(); LineMetrics lm = getTickLabelFont().getLineMetrics("0", frc); result += lm.getHeight(); } else { // look at lower and upper bounds... FontMetrics fm = g2.getFontMetrics(getTickLabelFont()); Range range = getRange(); double lower = range.getLowerBound(); double upper = range.getUpperBound(); String lowerStr, upperStr; NumberFormat formatter = getNumberFormatOverride(); if (formatter != null) { lowerStr = formatter.format(lower); upperStr = formatter.format(upper); } else { lowerStr = unit.valueToString(lower); upperStr = unit.valueToString(upper); } double w1 = fm.stringWidth(lowerStr); double w2 = fm.stringWidth(upperStr); result += Math.max(w1, w2); } return result; } /** * Selects an appropriate tick value for the axis. The strategy is to * display as many ticks as possible (selected from an array of 'standard' * tick units) without the labels overlapping. * * @param g2 the graphics device. * @param dataArea the area defined by the axes. * @param edge the axis location. */ protected void selectAutoTickUnit(Graphics2D g2, Rectangle2D dataArea, RectangleEdge edge) { if (RectangleEdge.isTopOrBottom(edge)) { selectHorizontalAutoTickUnit(g2, dataArea, edge); } else if (RectangleEdge.isLeftOrRight(edge)) { selectVerticalAutoTickUnit(g2, dataArea, edge); } } /** * Selects an appropriate tick value for the axis. The strategy is to * display as many ticks as possible (selected from an array of 'standard' * tick units) without the labels overlapping. * * @param g2 the graphics device. * @param dataArea the area defined by the axes. * @param edge the axis location. */ protected void selectHorizontalAutoTickUnit(Graphics2D g2, Rectangle2D dataArea, RectangleEdge edge) { TickUnit unit = getTickUnit(); TickUnitSource tickUnitSource = getStandardTickUnits(); // we should start with the current tick unit if it gives a count in // the range 3 to 40 otherwise estimate one that will give a count <= 10 double length = getRange().getLength(); int count = (int) (length / unit.getSize()); if (count < 3 || count > 40) { unit = tickUnitSource.getCeilingTickUnit(length / 10); } // now consider the label size relative to the width of the tick unit // and make a guess at the ideal size TickUnit unit1 = tickUnitSource.getCeilingTickUnit(unit); double tickLabelWidth = estimateMaximumTickLabelWidth(g2, unit1); double unit1Width = lengthToJava2D(unit1.getSize(), dataArea, edge); NumberTickUnit unit2 = (NumberTickUnit) unit1; double guess = (tickLabelWidth / unit1Width) * unit1.getSize(); // due to limitations of double precision, when you zoom very far into // a chart, eventually the visible axis range will get reported as // having length 0, and then 'guess' above will be infinite ... in that // case we'll just stick with the tick unit we have, it's better than // throwing an exception // https://github.com/jfree/jfreechart/issues/64 if (Double.isFinite(guess)) { unit2 = (NumberTickUnit) tickUnitSource.getCeilingTickUnit(guess); double unit2Width = lengthToJava2D(unit2.getSize(), dataArea, edge); tickLabelWidth = estimateMaximumTickLabelWidth(g2, unit2); if (tickLabelWidth > unit2Width) { unit2 = (NumberTickUnit) tickUnitSource.getLargerTickUnit(unit2); } } setTickUnit(unit2, false, false); } /** * Selects an appropriate tick value for the axis. The strategy is to * display as many ticks as possible (selected from an array of 'standard' * tick units) without the labels overlapping. * * @param g2 the graphics device. * @param dataArea the area in which the plot should be drawn. * @param edge the axis location. */ protected void selectVerticalAutoTickUnit(Graphics2D g2, Rectangle2D dataArea, RectangleEdge edge) { double tickLabelHeight = estimateMaximumTickLabelHeight(g2); // start with the current tick unit... TickUnitSource tickUnits = getStandardTickUnits(); TickUnit unit1 = tickUnits.getCeilingTickUnit(getTickUnit()); double unitHeight = lengthToJava2D(unit1.getSize(), dataArea, edge); double guess; if (unitHeight > 0) { // then extrapolate... guess = (tickLabelHeight / unitHeight) * unit1.getSize(); } else { guess = getRange().getLength() / 20.0; } NumberTickUnit unit2 = (NumberTickUnit) tickUnits.getCeilingTickUnit( guess); double unit2Height = lengthToJava2D(unit2.getSize(), dataArea, edge); tickLabelHeight = estimateMaximumTickLabelHeight(g2); if (tickLabelHeight > unit2Height) { unit2 = (NumberTickUnit) tickUnits.getLargerTickUnit(unit2); } setTickUnit(unit2, false, false); } /** * Calculates the positions of the tick labels for the axis, storing the * results in the tick label list (ready for drawing). * * @param g2 the graphics device. * @param state the axis state. * @param dataArea the area in which the plot should be drawn. * @param edge the location of the axis. * * @return A list of ticks. */ @Override public List refreshTicks(Graphics2D g2, AxisState state, Rectangle2D dataArea, RectangleEdge edge) { List result = new java.util.ArrayList(); if (RectangleEdge.isTopOrBottom(edge)) { result = refreshTicksHorizontal(g2, dataArea, edge); } else if (RectangleEdge.isLeftOrRight(edge)) { result = refreshTicksVertical(g2, dataArea, edge); } return result; } /** * Calculates the positions of the tick labels for the axis, storing the * results in the tick label list (ready for drawing). * * @param g2 the graphics device. * @param dataArea the area in which the data should be drawn. * @param edge the location of the axis. * * @return A list of ticks. */ protected List refreshTicksHorizontal(Graphics2D g2, Rectangle2D dataArea, RectangleEdge edge) { List result = new java.util.ArrayList(); Font tickLabelFont = getTickLabelFont(); g2.setFont(tickLabelFont); if (isAutoTickUnitSelection()) { selectAutoTickUnit(g2, dataArea, edge); } TickUnit tu = getTickUnit(); double size = tu.getSize(); int count = calculateVisibleTickCount(); double lowestTickValue = calculateLowestVisibleTickValue(); if (count <= ValueAxis.MAXIMUM_TICK_COUNT) { int minorTickSpaces = getMinorTickCount(); if (minorTickSpaces <= 0) { minorTickSpaces = tu.getMinorTickCount(); } for (int minorTick = 1; minorTick < minorTickSpaces; minorTick++) { double minorTickValue = lowestTickValue - size * minorTick / minorTickSpaces; if (getRange().contains(minorTickValue)) { result.add(new NumberTick(TickType.MINOR, minorTickValue, "", TextAnchor.TOP_CENTER, TextAnchor.CENTER, 0.0)); } } for (int i = 0; i < count; i++) { double currentTickValue = lowestTickValue + (i * size); String tickLabel; NumberFormat formatter = getNumberFormatOverride(); if (formatter != null) { tickLabel = formatter.format(currentTickValue); } else { tickLabel = getTickUnit().valueToString(currentTickValue); } TextAnchor anchor, rotationAnchor; double angle = 0.0; if (isVerticalTickLabels()) { anchor = TextAnchor.CENTER_RIGHT; rotationAnchor = TextAnchor.CENTER_RIGHT; if (edge == RectangleEdge.TOP) { angle = Math.PI / 2.0; } else { angle = -Math.PI / 2.0; } } else { if (edge == RectangleEdge.TOP) { anchor = TextAnchor.BOTTOM_CENTER; rotationAnchor = TextAnchor.BOTTOM_CENTER; } else { anchor = TextAnchor.TOP_CENTER; rotationAnchor = TextAnchor.TOP_CENTER; } } Tick tick = new NumberTick(currentTickValue, tickLabel, anchor, rotationAnchor, angle); result.add(tick); double nextTickValue = lowestTickValue + ((i + 1) * size); for (int minorTick = 1; minorTick < minorTickSpaces; minorTick++) { double minorTickValue = currentTickValue + (nextTickValue - currentTickValue) * minorTick / minorTickSpaces; if (getRange().contains(minorTickValue)) { result.add(new NumberTick(TickType.MINOR, minorTickValue, "", TextAnchor.TOP_CENTER, TextAnchor.CENTER, 0.0)); } } } } return result; } /** * Calculates the positions of the tick labels for the axis, storing the * results in the tick label list (ready for drawing). * * @param g2 the graphics device. * @param dataArea the area in which the plot should be drawn. * @param edge the location of the axis. * * @return A list of ticks. */ protected List refreshTicksVertical(Graphics2D g2, Rectangle2D dataArea, RectangleEdge edge) { List result = new ArrayList<>(); Font tickLabelFont = getTickLabelFont(); g2.setFont(tickLabelFont); if (isAutoTickUnitSelection()) { selectAutoTickUnit(g2, dataArea, edge); } TickUnit tu = getTickUnit(); double size = tu.getSize(); int count = calculateVisibleTickCount(); double lowestTickValue = calculateLowestVisibleTickValue(); if (count <= ValueAxis.MAXIMUM_TICK_COUNT) { int minorTickSpaces = getMinorTickCount(); if (minorTickSpaces <= 0) { minorTickSpaces = tu.getMinorTickCount(); } for (int minorTick = 1; minorTick < minorTickSpaces; minorTick++) { double minorTickValue = lowestTickValue - size * minorTick / minorTickSpaces; if (getRange().contains(minorTickValue)) { result.add(new NumberTick(TickType.MINOR, minorTickValue, "", TextAnchor.TOP_CENTER, TextAnchor.CENTER, 0.0)); } } for (int i = 0; i < count; i++) { double currentTickValue = lowestTickValue + (i * size); String tickLabel; NumberFormat formatter = getNumberFormatOverride(); if (formatter != null) { tickLabel = formatter.format(currentTickValue); } else { tickLabel = getTickUnit().valueToString(currentTickValue); } TextAnchor anchor; TextAnchor rotationAnchor; double angle = 0.0; if (isVerticalTickLabels()) { if (edge == RectangleEdge.LEFT) { anchor = TextAnchor.BOTTOM_CENTER; rotationAnchor = TextAnchor.BOTTOM_CENTER; angle = -Math.PI / 2.0; } else { anchor = TextAnchor.BOTTOM_CENTER; rotationAnchor = TextAnchor.BOTTOM_CENTER; angle = Math.PI / 2.0; } } else { if (edge == RectangleEdge.LEFT) { anchor = TextAnchor.CENTER_RIGHT; rotationAnchor = TextAnchor.CENTER_RIGHT; } else { anchor = TextAnchor.CENTER_LEFT; rotationAnchor = TextAnchor.CENTER_LEFT; } } Tick tick = new NumberTick(currentTickValue, tickLabel, anchor, rotationAnchor, angle); result.add(tick); double nextTickValue = lowestTickValue + ((i + 1) * size); for (int minorTick = 1; minorTick < minorTickSpaces; minorTick++) { double minorTickValue = currentTickValue + (nextTickValue - currentTickValue) * minorTick / minorTickSpaces; if (getRange().contains(minorTickValue)) { result.add(new NumberTick(TickType.MINOR, minorTickValue, "", TextAnchor.TOP_CENTER, TextAnchor.CENTER, 0.0)); } } } } return result; } /** * Returns a clone of the axis. * * @return A clone * * @throws CloneNotSupportedException if some component of the axis does * not support cloning. */ @Override public Object clone() throws CloneNotSupportedException { NumberAxis clone = (NumberAxis) super.clone(); if (this.numberFormatOverride != null) { clone.numberFormatOverride = (NumberFormat) this.numberFormatOverride.clone(); } return clone; } /** * Tests the axis for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof NumberAxis)) { return false; } NumberAxis that = (NumberAxis) obj; if (this.autoRangeIncludesZero != that.autoRangeIncludesZero) { return false; } if (this.autoRangeStickyZero != that.autoRangeStickyZero) { return false; } if (!Objects.equals(this.tickUnit, that.tickUnit)) { return false; } if (!Objects.equals(this.numberFormatOverride, that.numberFormatOverride)) { return false; } if (!this.rangeType.equals(that.rangeType)) { return false; } return super.equals(obj); } /** * Returns a hash code for this object. * * @return A hash code. */ @Override public int hashCode() { return super.hashCode(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/NumberTick.java000066400000000000000000000061641463604235500267370ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * NumberTick.java * --------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import org.jfree.chart.ui.TextAnchor; /** * A numerical tick. */ public class NumberTick extends ValueTick { /** The number. */ private Number number; /** * Creates a new tick. * * @param number the number ({@code null} not permitted). * @param label the label. * @param textAnchor the part of the label that is aligned with the anchor * point. * @param rotationAnchor defines the rotation point relative to the text. * @param angle the rotation angle (in radians). */ public NumberTick(Number number, String label, TextAnchor textAnchor, TextAnchor rotationAnchor, double angle) { super(number.doubleValue(), label, textAnchor, rotationAnchor, angle); this.number = number; } /** * Creates a new tick. * * @param tickType the tick type. * @param value the value. * @param label the label. * @param textAnchor the part of the label that is aligned with the anchor * point. * @param rotationAnchor defines the rotation point relative to the text. * @param angle the rotation angle (in radians). */ public NumberTick(TickType tickType, double value, String label, TextAnchor textAnchor, TextAnchor rotationAnchor, double angle) { super(tickType, value, label, textAnchor, rotationAnchor, angle); this.number = value; } /** * Returns the number. * * @return The number. */ public Number getNumber() { return this.number; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/NumberTickUnit.java000066400000000000000000000107361463604235500275770ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * NumberTickUnit.java * ------------------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.io.Serializable; import java.text.NumberFormat; import org.jfree.chart.util.Args; /** * A numerical tick unit. */ public class NumberTickUnit extends TickUnit implements Serializable { /** For serialization. */ private static final long serialVersionUID = 3849459506627654442L; /** A formatter for the tick unit. */ private NumberFormat formatter; /** * Creates a new number tick unit. * * @param size the size of the tick unit. */ public NumberTickUnit(double size) { this(size, NumberFormat.getNumberInstance()); } /** * Creates a new number tick unit. * * @param size the size of the tick unit. * @param formatter a number formatter for the tick unit ({@code null} * not permitted). */ public NumberTickUnit(double size, NumberFormat formatter) { super(size); Args.nullNotPermitted(formatter, "formatter"); this.formatter = formatter; } /** * Creates a new number tick unit. * * @param size the size of the tick unit. * @param formatter a number formatter for the tick unit ({@code null} * not permitted). * @param minorTickCount the number of minor ticks. */ public NumberTickUnit(double size, NumberFormat formatter, int minorTickCount) { super(size, minorTickCount); Args.nullNotPermitted(formatter, "formatter"); this.formatter = formatter; } /** * Converts a value to a string. * * @param value the value. * * @return The formatted string. */ @Override public String valueToString(double value) { return this.formatter.format(value); } /** * Tests this formatter for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof NumberTickUnit)) { return false; } if (!super.equals(obj)) { return false; } NumberTickUnit that = (NumberTickUnit) obj; if (!this.formatter.equals(that.formatter)) { return false; } return true; } /** * Returns a string representing this unit. * * @return A string. */ @Override public String toString() { return "[NumberTickUnit: size=" + this.valueToString(this.getSize()) + ", formatter=" + this.formatter + "]"; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = super.hashCode(); result = 29 * result + (this.formatter != null ? this.formatter.hashCode() : 0); return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/NumberTickUnitSource.java000066400000000000000000000150501463604235500307520ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * NumberTickUnitSource.java * ------------------------- * (C) Copyright 2014-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.io.Serializable; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.Objects; /** * A tick unit source implementation that returns NumberTickUnit instances * that are multiples of 1, 2 or 5 times some power of 10. */ public class NumberTickUnitSource implements TickUnitSource, Serializable { private boolean integers; private int power = 0; private int factor = 1; /** The number formatter to use (an override, it can be null). */ private NumberFormat formatter; /** * Creates a new instance. */ public NumberTickUnitSource() { this(false); } /** * Creates a new instance. * * @param integers show integers only. */ public NumberTickUnitSource(boolean integers) { this(integers, null); } /** * Creates a new instance. * * @param integers show integers only? * @param formatter a formatter for the axis tick labels ({@code null} * permitted). */ public NumberTickUnitSource(boolean integers, NumberFormat formatter) { this.integers = integers; this.formatter = formatter; this.power = 0; this.factor = 1; } @Override public TickUnit getLargerTickUnit(TickUnit unit) { TickUnit t = getCeilingTickUnit(unit); if (t.equals(unit)) { next(); t = new NumberTickUnit(getTickSize(), getTickLabelFormat(), getMinorTickCount()); } return t; } @Override public TickUnit getCeilingTickUnit(TickUnit unit) { return getCeilingTickUnit(unit.getSize()); } @Override public TickUnit getCeilingTickUnit(double size) { if (Double.isInfinite(size)) { throw new IllegalArgumentException("Must be finite."); } this.power = (int) Math.ceil(Math.log10(size)); if (this.integers) { power = Math.max(this.power, 0); } this.factor = 1; boolean done = false; // step down in size until the current size is too small or there are // no more units while (!done) { done = !previous(); if (getTickSize() < size) { next(); done = true; } } return new NumberTickUnit(getTickSize(), getTickLabelFormat(), getMinorTickCount()); } private boolean next() { if (factor == 1) { factor = 2; return true; } if (factor == 2) { factor = 5; return true; } if (factor == 5) { if (power == 300) { return false; } power++; factor = 1; return true; } throw new IllegalStateException("We should never get here."); } private boolean previous() { if (factor == 1) { if (this.integers && power == 0 || power == -300) { return false; } factor = 5; power--; return true; } if (factor == 2) { factor = 1; return true; } if (factor == 5) { factor = 2; return true; } throw new IllegalStateException("We should never get here."); } private double getTickSize() { return this.factor * Math.pow(10.0, this.power); } private DecimalFormat dfNeg4 = new DecimalFormat("0.0000"); private DecimalFormat dfNeg3 = new DecimalFormat("0.000"); private DecimalFormat dfNeg2 = new DecimalFormat("0.00"); private DecimalFormat dfNeg1 = new DecimalFormat("0.0"); private DecimalFormat df0 = new DecimalFormat("#,##0"); private DecimalFormat df = new DecimalFormat("#.######E0"); private NumberFormat getTickLabelFormat() { if (this.formatter != null) { return this.formatter; } if (power == -4) { return dfNeg4; } if (power == -3) { return dfNeg3; } if (power == -2) { return dfNeg2; } if (power == -1) { return dfNeg1; } if (power >= 0 && power <= 6) { return df0; } return df; } private int getMinorTickCount() { if (factor == 1) { return 10; } else if (factor == 5) { return 5; } return 0; } @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof NumberTickUnitSource)) { return false; } NumberTickUnitSource that = (NumberTickUnitSource) obj; if (this.integers != that.integers) { return false; } if (!Objects.equals(this.formatter, that.formatter)) { return false; } if (this.power != that.power) { return false; } if (this.factor != that.factor) { return false; } return true; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/PeriodAxis.java000066400000000000000000001226741463604235500267500ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * PeriodAxis.java * --------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.awt.BasicStroke; import java.awt.Color; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Stroke; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.Constructor; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.TimeZone; import org.jfree.chart.event.AxisChangeEvent; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.ValueAxisPlot; import org.jfree.chart.text.TextUtils; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.TextAnchor; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; import org.jfree.data.Range; import org.jfree.data.time.Day; import org.jfree.data.time.Month; import org.jfree.data.time.RegularTimePeriod; import org.jfree.data.time.Year; /** * An axis that displays a date scale based on a * {@link org.jfree.data.time.RegularTimePeriod}. This axis works when * displayed across the bottom or top of a plot, but is broken for display at * the left or right of charts. */ public class PeriodAxis extends ValueAxis implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 8353295532075872069L; /** The first time period in the overall range. */ private RegularTimePeriod first; /** The last time period in the overall range. */ private RegularTimePeriod last; /** * The time zone used to convert 'first' and 'last' to absolute * milliseconds. */ private TimeZone timeZone; /** The locale (never {@code null}). */ private Locale locale; /** * A calendar used for date manipulations in the current time zone and * locale. */ private Calendar calendar; /** * The {@link RegularTimePeriod} subclass used to automatically determine * the axis range. */ private Class autoRangeTimePeriodClass; /** * Indicates the {@link RegularTimePeriod} subclass that is used to * determine the spacing of the major tick marks. */ private Class majorTickTimePeriodClass; /** * A flag that indicates whether or not tick marks are visible for the * axis. */ private boolean minorTickMarksVisible; /** * Indicates the {@link RegularTimePeriod} subclass that is used to * determine the spacing of the minor tick marks. */ private Class minorTickTimePeriodClass; /** The length of the tick mark inside the data area (zero permitted). */ private float minorTickMarkInsideLength = 0.0f; /** The length of the tick mark outside the data area (zero permitted). */ private float minorTickMarkOutsideLength = 2.0f; /** The stroke used to draw tick marks. */ private transient Stroke minorTickMarkStroke = new BasicStroke(0.5f); /** The paint used to draw tick marks. */ private transient Paint minorTickMarkPaint = Color.BLACK; /** Info for each labeling band. */ private PeriodAxisLabelInfo[] labelInfo; /** * Creates a new axis. * * @param label the axis label. */ public PeriodAxis(String label) { this(label, new Day(), new Day()); } /** * Creates a new axis. * * @param label the axis label ({@code null} permitted). * @param first the first time period in the axis range * ({@code null} not permitted). * @param last the last time period in the axis range * ({@code null} not permitted). */ public PeriodAxis(String label, RegularTimePeriod first, RegularTimePeriod last) { this(label, first, last, TimeZone.getDefault(), Locale.getDefault()); } /** * Creates a new axis. * * @param label the axis label ({@code null} permitted). * @param first the first time period in the axis range * ({@code null} not permitted). * @param last the last time period in the axis range * ({@code null} not permitted). * @param timeZone the time zone ({@code null} not permitted). * @param locale the locale ({@code null} not permitted). */ public PeriodAxis(String label, RegularTimePeriod first, RegularTimePeriod last, TimeZone timeZone, Locale locale) { super(label, null); Args.nullNotPermitted(timeZone, "timeZone"); Args.nullNotPermitted(locale, "locale"); this.first = first; this.last = last; this.timeZone = timeZone; this.locale = locale; this.calendar = Calendar.getInstance(timeZone, locale); this.first.peg(this.calendar); this.last.peg(this.calendar); this.autoRangeTimePeriodClass = first.getClass(); this.majorTickTimePeriodClass = first.getClass(); this.minorTickMarksVisible = false; this.minorTickTimePeriodClass = RegularTimePeriod.downsize( this.majorTickTimePeriodClass); setAutoRange(true); this.labelInfo = new PeriodAxisLabelInfo[2]; SimpleDateFormat df0 = new SimpleDateFormat("MMM", locale); df0.setTimeZone(timeZone); this.labelInfo[0] = new PeriodAxisLabelInfo(Month.class, df0); SimpleDateFormat df1 = new SimpleDateFormat("yyyy", locale); df1.setTimeZone(timeZone); this.labelInfo[1] = new PeriodAxisLabelInfo(Year.class, df1); } /** * Returns the first time period in the axis range. * * @return The first time period (never {@code null}). */ public RegularTimePeriod getFirst() { return this.first; } /** * Sets the first time period in the axis range and sends an * {@link AxisChangeEvent} to all registered listeners. * * @param first the time period ({@code null} not permitted). */ public void setFirst(RegularTimePeriod first) { Args.nullNotPermitted(first, "first"); this.first = first; this.first.peg(this.calendar); fireChangeEvent(); } /** * Returns the last time period in the axis range. * * @return The last time period (never {@code null}). */ public RegularTimePeriod getLast() { return this.last; } /** * Sets the last time period in the axis range and sends an * {@link AxisChangeEvent} to all registered listeners. * * @param last the time period ({@code null} not permitted). */ public void setLast(RegularTimePeriod last) { Args.nullNotPermitted(last, "last"); this.last = last; this.last.peg(this.calendar); fireChangeEvent(); } /** * Returns the time zone used to convert the periods defining the axis * range into absolute milliseconds. * * @return The time zone (never {@code null}). */ public TimeZone getTimeZone() { return this.timeZone; } /** * Sets the time zone that is used to convert the time periods into * absolute milliseconds. * * @param zone the time zone ({@code null} not permitted). */ public void setTimeZone(TimeZone zone) { Args.nullNotPermitted(zone, "zone"); this.timeZone = zone; this.calendar = Calendar.getInstance(zone, this.locale); this.first.peg(this.calendar); this.last.peg(this.calendar); fireChangeEvent(); } /** * Returns the locale for this axis. * * @return The locale (never ({@code null}). */ public Locale getLocale() { return this.locale; } /** * Returns the class used to create the first and last time periods for * the axis range when the auto-range flag is set to {@code true}. * * @return The class (never {@code null}). */ public Class getAutoRangeTimePeriodClass() { return this.autoRangeTimePeriodClass; } /** * Sets the class used to create the first and last time periods for the * axis range when the auto-range flag is set to {@code true} and * sends an {@link AxisChangeEvent} to all registered listeners. * * @param c the class ({@code null} not permitted). */ public void setAutoRangeTimePeriodClass(Class c) { Args.nullNotPermitted(c, "c"); this.autoRangeTimePeriodClass = c; fireChangeEvent(); } /** * Returns the class that controls the spacing of the major tick marks. * * @return The class (never {@code null}). */ public Class getMajorTickTimePeriodClass() { return this.majorTickTimePeriodClass; } /** * Sets the class that controls the spacing of the major tick marks, and * sends an {@link AxisChangeEvent} to all registered listeners. * * @param c the class (a subclass of {@link RegularTimePeriod} is * expected). */ public void setMajorTickTimePeriodClass(Class c) { Args.nullNotPermitted(c, "c"); this.majorTickTimePeriodClass = c; fireChangeEvent(); } /** * Returns the flag that controls whether or not minor tick marks * are displayed for the axis. * * @return A boolean. */ @Override public boolean isMinorTickMarksVisible() { return this.minorTickMarksVisible; } /** * Sets the flag that controls whether or not minor tick marks * are displayed for the axis, and sends a {@link AxisChangeEvent} * to all registered listeners. * * @param visible the flag. */ @Override public void setMinorTickMarksVisible(boolean visible) { this.minorTickMarksVisible = visible; fireChangeEvent(); } /** * Returns the class that controls the spacing of the minor tick marks. * * @return The class (never {@code null}). */ public Class getMinorTickTimePeriodClass() { return this.minorTickTimePeriodClass; } /** * Sets the class that controls the spacing of the minor tick marks, and * sends an {@link AxisChangeEvent} to all registered listeners. * * @param c the class (a subclass of {@link RegularTimePeriod} is * expected). */ public void setMinorTickTimePeriodClass(Class c) { Args.nullNotPermitted(c, "c"); this.minorTickTimePeriodClass = c; fireChangeEvent(); } /** * Returns the stroke used to display minor tick marks, if they are * visible. * * @return A stroke (never {@code null}). */ public Stroke getMinorTickMarkStroke() { return this.minorTickMarkStroke; } /** * Sets the stroke used to display minor tick marks, if they are * visible, and sends a {@link AxisChangeEvent} to all registered * listeners. * * @param stroke the stroke ({@code null} not permitted). */ public void setMinorTickMarkStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.minorTickMarkStroke = stroke; fireChangeEvent(); } /** * Returns the paint used to display minor tick marks, if they are * visible. * * @return A paint (never {@code null}). */ public Paint getMinorTickMarkPaint() { return this.minorTickMarkPaint; } /** * Sets the paint used to display minor tick marks, if they are * visible, and sends a {@link AxisChangeEvent} to all registered * listeners. * * @param paint the paint ({@code null} not permitted). */ public void setMinorTickMarkPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.minorTickMarkPaint = paint; fireChangeEvent(); } /** * Returns the inside length for the minor tick marks. * * @return The length. */ @Override public float getMinorTickMarkInsideLength() { return this.minorTickMarkInsideLength; } /** * Sets the inside length of the minor tick marks and sends an * {@link AxisChangeEvent} to all registered listeners. * * @param length the length. */ @Override public void setMinorTickMarkInsideLength(float length) { this.minorTickMarkInsideLength = length; fireChangeEvent(); } /** * Returns the outside length for the minor tick marks. * * @return The length. */ @Override public float getMinorTickMarkOutsideLength() { return this.minorTickMarkOutsideLength; } /** * Sets the outside length of the minor tick marks and sends an * {@link AxisChangeEvent} to all registered listeners. * * @param length the length. */ @Override public void setMinorTickMarkOutsideLength(float length) { this.minorTickMarkOutsideLength = length; fireChangeEvent(); } /** * Returns an array of label info records. * * @return An array. */ public PeriodAxisLabelInfo[] getLabelInfo() { return this.labelInfo; } /** * Sets the array of label info records and sends an * {@link AxisChangeEvent} to all registered listeners. * * @param info the info. */ public void setLabelInfo(PeriodAxisLabelInfo[] info) { this.labelInfo = info; fireChangeEvent(); } /** * Sets the range for the axis, if requested, sends an * {@link AxisChangeEvent} to all registered listeners. As a side-effect, * the auto-range flag is set to {@code false} (optional). * * @param range the range ({@code null} not permitted). * @param turnOffAutoRange a flag that controls whether or not the auto * range is turned off. * @param notify a flag that controls whether or not listeners are * notified. */ @Override public void setRange(Range range, boolean turnOffAutoRange, boolean notify) { long upper = Math.round(range.getUpperBound()); long lower = Math.round(range.getLowerBound()); this.first = createInstance(this.autoRangeTimePeriodClass, new Date(lower), this.timeZone, this.locale); this.last = createInstance(this.autoRangeTimePeriodClass, new Date(upper), this.timeZone, this.locale); super.setRange(new Range(this.first.getFirstMillisecond(), this.last.getLastMillisecond() + 1.0), turnOffAutoRange, notify); } /** * Configures the axis to work with the current plot. Override this method * to perform any special processing (such as auto-rescaling). */ @Override public void configure() { if (this.isAutoRange()) { autoAdjustRange(); } } /** * Estimates the space (height or width) required to draw the axis. * * @param g2 the graphics device. * @param plot the plot that the axis belongs to. * @param plotArea the area within which the plot (including axes) should * be drawn. * @param edge the axis location. * @param space space already reserved. * * @return The space required to draw the axis (including pre-reserved * space). */ @Override public AxisSpace reserveSpace(Graphics2D g2, Plot plot, Rectangle2D plotArea, RectangleEdge edge, AxisSpace space) { // create a new space object if one wasn't supplied... if (space == null) { space = new AxisSpace(); } // if the axis is not visible, no additional space is required... if (!isVisible()) { return space; } // if the axis has a fixed dimension, return it... double dimension = getFixedDimension(); if (dimension > 0.0) { space.ensureAtLeast(dimension, edge); } // get the axis label size and update the space object... Rectangle2D labelEnclosure = getLabelEnclosure(g2, edge); double labelHeight, labelWidth; double tickLabelBandsDimension = 0.0; for (PeriodAxisLabelInfo info : this.labelInfo) { FontMetrics fm = g2.getFontMetrics(info.getLabelFont()); tickLabelBandsDimension += info.getPadding().extendHeight(fm.getHeight()); } if (RectangleEdge.isTopOrBottom(edge)) { labelHeight = labelEnclosure.getHeight(); space.add(labelHeight + tickLabelBandsDimension, edge); } else if (RectangleEdge.isLeftOrRight(edge)) { labelWidth = labelEnclosure.getWidth(); space.add(labelWidth + tickLabelBandsDimension, edge); } // add space for the outer tick labels, if any... double tickMarkSpace = 0.0; if (isTickMarksVisible()) { tickMarkSpace = getTickMarkOutsideLength(); } if (this.minorTickMarksVisible) { tickMarkSpace = Math.max(tickMarkSpace, this.minorTickMarkOutsideLength); } space.add(tickMarkSpace, edge); return space; } /** * Draws the axis on a Java 2D graphics device (such as the screen or a * printer). * * @param g2 the graphics device ({@code null} not permitted). * @param cursor the cursor location (determines where to draw the axis). * @param plotArea the area within which the axes and plot should be drawn. * @param dataArea the area within which the data should be drawn. * @param edge the axis location ({@code null} not permitted). * @param plotState collects information about the plot * ({@code null} permitted). * * @return The axis state (never {@code null}). */ @Override public AxisState draw(Graphics2D g2, double cursor, Rectangle2D plotArea, Rectangle2D dataArea, RectangleEdge edge, PlotRenderingInfo plotState) { // if the axis is not visible, don't draw it... bug#198 if (!isVisible()) { AxisState state = new AxisState(cursor); // even though the axis is not visible, we need to refresh ticks in // case the grid is being drawn... List ticks = refreshTicks(g2, state, dataArea, edge); state.setTicks(ticks); return state; } AxisState axisState = new AxisState(cursor); if (isAxisLineVisible()) { drawAxisLine(g2, cursor, dataArea, edge); } if (isTickMarksVisible()) { drawTickMarks(g2, axisState, dataArea, edge); } if (isTickLabelsVisible()) { for (int band = 0; band < this.labelInfo.length; band++) { axisState = drawTickLabels(band, g2, axisState, dataArea, edge); } } if (getAttributedLabel() != null) { axisState = drawAttributedLabel(getAttributedLabel(), g2, plotArea, dataArea, edge, axisState); } else { axisState = drawLabel(getLabel(), g2, plotArea, dataArea, edge, axisState); } return axisState; } /** * Draws the tick marks for the axis. * * @param g2 the graphics device. * @param state the axis state. * @param dataArea the data area. * @param edge the edge. */ protected void drawTickMarks(Graphics2D g2, AxisState state, Rectangle2D dataArea, RectangleEdge edge) { if (RectangleEdge.isTopOrBottom(edge)) { drawTickMarksHorizontal(g2, state, dataArea, edge); } else if (RectangleEdge.isLeftOrRight(edge)) { drawTickMarksVertical(g2, state, dataArea, edge); } } /** * Draws the major and minor tick marks for an axis that lies at the top or * bottom of the plot. * * @param g2 the graphics device. * @param state the axis state. * @param dataArea the data area. * @param edge the edge. */ protected void drawTickMarksHorizontal(Graphics2D g2, AxisState state, Rectangle2D dataArea, RectangleEdge edge) { List ticks = new ArrayList(); double x0; double y0 = state.getCursor(); double insideLength = getTickMarkInsideLength(); double outsideLength = getTickMarkOutsideLength(); RegularTimePeriod t = createInstance(this.majorTickTimePeriodClass, this.first.getStart(), getTimeZone(), this.locale); long t0 = t.getFirstMillisecond(); Line2D inside = null; Line2D outside = null; long firstOnAxis = getFirst().getFirstMillisecond(); long lastOnAxis = getLast().getLastMillisecond() + 1; while (t0 <= lastOnAxis) { ticks.add(new NumberTick(Double.valueOf(t0), "", TextAnchor.CENTER, TextAnchor.CENTER, 0.0)); x0 = valueToJava2D(t0, dataArea, edge); if (edge == RectangleEdge.TOP) { inside = new Line2D.Double(x0, y0, x0, y0 + insideLength); outside = new Line2D.Double(x0, y0, x0, y0 - outsideLength); } else if (edge == RectangleEdge.BOTTOM) { inside = new Line2D.Double(x0, y0, x0, y0 - insideLength); outside = new Line2D.Double(x0, y0, x0, y0 + outsideLength); } if (t0 >= firstOnAxis) { g2.setPaint(getTickMarkPaint()); g2.setStroke(getTickMarkStroke()); g2.draw(inside); g2.draw(outside); } // draw minor tick marks if (this.minorTickMarksVisible) { RegularTimePeriod tminor = createInstance( this.minorTickTimePeriodClass, new Date(t0), getTimeZone(), this.locale); long tt0 = tminor.getFirstMillisecond(); while (tt0 < t.getLastMillisecond() && tt0 < lastOnAxis) { double xx0 = valueToJava2D(tt0, dataArea, edge); if (edge == RectangleEdge.TOP) { inside = new Line2D.Double(xx0, y0, xx0, y0 + this.minorTickMarkInsideLength); outside = new Line2D.Double(xx0, y0, xx0, y0 - this.minorTickMarkOutsideLength); } else if (edge == RectangleEdge.BOTTOM) { inside = new Line2D.Double(xx0, y0, xx0, y0 - this.minorTickMarkInsideLength); outside = new Line2D.Double(xx0, y0, xx0, y0 + this.minorTickMarkOutsideLength); } if (tt0 >= firstOnAxis) { g2.setPaint(this.minorTickMarkPaint); g2.setStroke(this.minorTickMarkStroke); g2.draw(inside); g2.draw(outside); } tminor = tminor.next(); tminor.peg(this.calendar); tt0 = tminor.getFirstMillisecond(); } } t = t.next(); t.peg(this.calendar); t0 = t.getFirstMillisecond(); } if (edge == RectangleEdge.TOP) { state.cursorUp(Math.max(outsideLength, this.minorTickMarkOutsideLength)); } else if (edge == RectangleEdge.BOTTOM) { state.cursorDown(Math.max(outsideLength, this.minorTickMarkOutsideLength)); } state.setTicks(ticks); } /** * Draws the tick marks for a vertical axis. * * @param g2 the graphics device. * @param state the axis state. * @param dataArea the data area. * @param edge the edge. */ protected void drawTickMarksVertical(Graphics2D g2, AxisState state, Rectangle2D dataArea, RectangleEdge edge) { // FIXME: implement this... } /** * Draws the tick labels for one "band" of time periods. * * @param band the band index (zero-based). * @param g2 the graphics device. * @param state the axis state. * @param dataArea the data area. * @param edge the edge where the axis is located. * * @return The updated axis state. */ protected AxisState drawTickLabels(int band, Graphics2D g2, AxisState state, Rectangle2D dataArea, RectangleEdge edge) { // work out the initial gap double delta1 = 0.0; FontMetrics fm = g2.getFontMetrics(this.labelInfo[band].getLabelFont()); if (edge == RectangleEdge.BOTTOM) { delta1 = this.labelInfo[band].getPadding().calculateTopOutset( fm.getHeight()); } else if (edge == RectangleEdge.TOP) { delta1 = this.labelInfo[band].getPadding().calculateBottomOutset( fm.getHeight()); } state.moveCursor(delta1, edge); long axisMin = this.first.getFirstMillisecond(); long axisMax = this.last.getLastMillisecond(); g2.setFont(this.labelInfo[band].getLabelFont()); g2.setPaint(this.labelInfo[band].getLabelPaint()); // work out the number of periods to skip for labelling RegularTimePeriod p1 = this.labelInfo[band].createInstance( new Date(axisMin), this.timeZone, this.locale); RegularTimePeriod p2 = this.labelInfo[band].createInstance( new Date(axisMax), this.timeZone, this.locale); DateFormat df = this.labelInfo[band].getDateFormat(); df.setTimeZone(this.timeZone); String label1 = df.format(new Date(p1.getMiddleMillisecond())); String label2 = df.format(new Date(p2.getMiddleMillisecond())); Rectangle2D b1 = TextUtils.getTextBounds(label1, g2, g2.getFontMetrics()); Rectangle2D b2 = TextUtils.getTextBounds(label2, g2, g2.getFontMetrics()); double w = Math.max(b1.getWidth(), b2.getWidth()); long ww = Math.round(java2DToValue(dataArea.getX() + w + 5.0, dataArea, edge)); if (isInverted()) { ww = axisMax - ww; } else { ww = ww - axisMin; } long length = p1.getLastMillisecond() - p1.getFirstMillisecond(); int periods = (int) (ww / length) + 1; RegularTimePeriod p = this.labelInfo[band].createInstance( new Date(axisMin), this.timeZone, this.locale); Rectangle2D b = null; long lastXX = 0L; float y = (float) (state.getCursor()); TextAnchor anchor = TextAnchor.TOP_CENTER; float yDelta = (float) b1.getHeight(); if (edge == RectangleEdge.TOP) { anchor = TextAnchor.BOTTOM_CENTER; yDelta = -yDelta; } while (p.getFirstMillisecond() <= axisMax) { float x = (float) valueToJava2D(p.getMiddleMillisecond(), dataArea, edge); String label = df.format(new Date(p.getMiddleMillisecond())); long first = p.getFirstMillisecond(); long last = p.getLastMillisecond(); if (last > axisMax) { // this is the last period, but it is only partially visible // so check that the label will fit before displaying it... Rectangle2D bb = TextUtils.getTextBounds(label, g2, g2.getFontMetrics()); if ((x + bb.getWidth() / 2) > dataArea.getMaxX()) { float xstart = (float) valueToJava2D(Math.max(first, axisMin), dataArea, edge); if (bb.getWidth() < (dataArea.getMaxX() - xstart)) { x = ((float) dataArea.getMaxX() + xstart) / 2.0f; } else { label = null; } } } if (first < axisMin) { // this is the first period, but it is only partially visible // so check that the label will fit before displaying it... Rectangle2D bb = TextUtils.getTextBounds(label, g2, g2.getFontMetrics()); if ((x - bb.getWidth() / 2) < dataArea.getX()) { float xlast = (float) valueToJava2D(Math.min(last, axisMax), dataArea, edge); if (bb.getWidth() < (xlast - dataArea.getX())) { x = (xlast + (float) dataArea.getX()) / 2.0f; } else { label = null; } } } if (label != null) { g2.setPaint(this.labelInfo[band].getLabelPaint()); b = TextUtils.drawAlignedString(label, g2, x, y, anchor); } if (lastXX > 0L) { if (this.labelInfo[band].getDrawDividers()) { long nextXX = p.getFirstMillisecond(); long mid = (lastXX + nextXX) / 2; float mid2d = (float) valueToJava2D(mid, dataArea, edge); g2.setStroke(this.labelInfo[band].getDividerStroke()); g2.setPaint(this.labelInfo[band].getDividerPaint()); g2.draw(new Line2D.Float(mid2d, y, mid2d, y + yDelta)); } } lastXX = last; for (int i = 0; i < periods; i++) { p = p.next(); } p.peg(this.calendar); } double used = 0.0; if (b != null) { used = b.getHeight(); // work out the trailing gap if (edge == RectangleEdge.BOTTOM) { used += this.labelInfo[band].getPadding().calculateBottomOutset( fm.getHeight()); } else if (edge == RectangleEdge.TOP) { used += this.labelInfo[band].getPadding().calculateTopOutset( fm.getHeight()); } } state.moveCursor(used, edge); return state; } /** * Calculates the positions of the ticks for the axis, storing the results * in the tick list (ready for drawing). * * @param g2 the graphics device. * @param state the axis state. * @param dataArea the area inside the axes. * @param edge the edge on which the axis is located. * * @return The list of ticks. */ @Override public List refreshTicks(Graphics2D g2, AxisState state, Rectangle2D dataArea, RectangleEdge edge) { return Collections.EMPTY_LIST; } /** * Converts a data value to a coordinate in Java2D space, assuming that the * axis runs along one edge of the specified dataArea. *

* Note that it is possible for the coordinate to fall outside the area. * * @param value the data value. * @param area the area for plotting the data. * @param edge the edge along which the axis lies. * * @return The Java2D coordinate. */ @Override public double valueToJava2D(double value, Rectangle2D area, RectangleEdge edge) { double result = Double.NaN; double axisMin = this.first.getFirstMillisecond(); double axisMax = this.last.getLastMillisecond(); if (RectangleEdge.isTopOrBottom(edge)) { double minX = area.getX(); double maxX = area.getMaxX(); if (isInverted()) { result = maxX + ((value - axisMin) / (axisMax - axisMin)) * (minX - maxX); } else { result = minX + ((value - axisMin) / (axisMax - axisMin)) * (maxX - minX); } } else if (RectangleEdge.isLeftOrRight(edge)) { double minY = area.getMinY(); double maxY = area.getMaxY(); if (isInverted()) { result = minY + (((value - axisMin) / (axisMax - axisMin)) * (maxY - minY)); } else { result = maxY - (((value - axisMin) / (axisMax - axisMin)) * (maxY - minY)); } } return result; } /** * Converts a coordinate in Java2D space to the corresponding data value, * assuming that the axis runs along one edge of the specified dataArea. * * @param java2DValue the coordinate in Java2D space. * @param area the area in which the data is plotted. * @param edge the edge along which the axis lies. * * @return The data value. */ @Override public double java2DToValue(double java2DValue, Rectangle2D area, RectangleEdge edge) { double result; double min = 0.0; double max = 0.0; double axisMin = this.first.getFirstMillisecond(); double axisMax = this.last.getLastMillisecond(); if (RectangleEdge.isTopOrBottom(edge)) { min = area.getX(); max = area.getMaxX(); } else if (RectangleEdge.isLeftOrRight(edge)) { min = area.getMaxY(); max = area.getY(); } if (isInverted()) { result = axisMax - ((java2DValue - min) / (max - min) * (axisMax - axisMin)); } else { result = axisMin + ((java2DValue - min) / (max - min) * (axisMax - axisMin)); } return result; } /** * Rescales the axis to ensure that all data is visible. */ @Override protected void autoAdjustRange() { Plot plot = getPlot(); if (plot == null) { return; // no plot, no data } if (plot instanceof ValueAxisPlot) { ValueAxisPlot vap = (ValueAxisPlot) plot; Range r = vap.getDataRange(this); if (r == null) { r = getDefaultAutoRange(); } long upper = Math.round(r.getUpperBound()); long lower = Math.round(r.getLowerBound()); this.first = createInstance(this.autoRangeTimePeriodClass, new Date(lower), this.timeZone, this.locale); this.last = createInstance(this.autoRangeTimePeriodClass, new Date(upper), this.timeZone, this.locale); setRange(r, false, false); } } /** * Tests the axis for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof PeriodAxis)) { return false; } PeriodAxis that = (PeriodAxis) obj; if (!this.first.equals(that.first)) { return false; } if (!this.last.equals(that.last)) { return false; } if (!this.timeZone.equals(that.timeZone)) { return false; } if (!this.locale.equals(that.locale)) { return false; } if (!this.autoRangeTimePeriodClass.equals( that.autoRangeTimePeriodClass)) { return false; } if (!(isMinorTickMarksVisible() == that.isMinorTickMarksVisible())) { return false; } if (!this.majorTickTimePeriodClass.equals( that.majorTickTimePeriodClass)) { return false; } if (!this.minorTickTimePeriodClass.equals( that.minorTickTimePeriodClass)) { return false; } if (!this.minorTickMarkPaint.equals(that.minorTickMarkPaint)) { return false; } if (!this.minorTickMarkStroke.equals(that.minorTickMarkStroke)) { return false; } if (!Arrays.equals(this.labelInfo, that.labelInfo)) { return false; } return super.equals(obj); } /** * Returns a hash code for this object. * * @return A hash code. */ @Override public int hashCode() { return super.hashCode(); } /** * Returns a clone of the axis. * * @return A clone. * * @throws CloneNotSupportedException this class is cloneable, but * subclasses may not be. */ @Override public Object clone() throws CloneNotSupportedException { PeriodAxis clone = (PeriodAxis) super.clone(); clone.timeZone = (TimeZone) this.timeZone.clone(); clone.labelInfo = (PeriodAxisLabelInfo[]) this.labelInfo.clone(); return clone; } /** * A utility method used to create a particular subclass of the * {@link RegularTimePeriod} class that includes the specified millisecond, * assuming the specified time zone. * * @param periodClass the class. * @param millisecond the time. * @param zone the time zone. * @param locale the locale. * * @return The time period. */ private RegularTimePeriod createInstance(Class periodClass, Date millisecond, TimeZone zone, Locale locale) { RegularTimePeriod result = null; try { Constructor c = periodClass.getDeclaredConstructor(new Class[] { Date.class, TimeZone.class, Locale.class}); result = (RegularTimePeriod) c.newInstance(new Object[] { millisecond, zone, locale}); } catch (Exception e) { try { Constructor c = periodClass.getDeclaredConstructor(new Class[] { Date.class}); result = (RegularTimePeriod) c.newInstance(new Object[] { millisecond}); } catch (Exception e2) { // do nothing } } return result; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeStroke(this.minorTickMarkStroke, stream); SerialUtils.writePaint(this.minorTickMarkPaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.minorTickMarkStroke = SerialUtils.readStroke(stream); this.minorTickMarkPaint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/PeriodAxisLabelInfo.java000066400000000000000000000264611463604235500305210ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * PeriodAxisLabelInfo.java * ------------------------ * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.Paint; import java.awt.Stroke; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.Constructor; import java.text.DateFormat; import java.util.Date; import java.util.Locale; import java.util.TimeZone; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.util.Args; import org.jfree.chart.util.SerialUtils; import org.jfree.data.time.RegularTimePeriod; /** * A record that contains information for one "band" of date labels in * a {@link PeriodAxis}. */ public class PeriodAxisLabelInfo implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 5710451740920277357L; /** The default insets. */ public static final RectangleInsets DEFAULT_INSETS = new RectangleInsets(2, 2, 2, 2); /** The default font. */ public static final Font DEFAULT_FONT = new Font("SansSerif", Font.PLAIN, 10); /** The default label paint. */ public static final Paint DEFAULT_LABEL_PAINT = Color.BLACK; /** The default divider stroke. */ public static final Stroke DEFAULT_DIVIDER_STROKE = new BasicStroke(0.5f); /** The default divider paint. */ public static final Paint DEFAULT_DIVIDER_PAINT = Color.GRAY; /** The subclass of {@link RegularTimePeriod} to use for this band. */ private Class periodClass; /** Controls the gaps around the band. */ private RectangleInsets padding; /** The date formatter. */ private DateFormat dateFormat; /** The label font. */ private Font labelFont; /** The label paint. */ private transient Paint labelPaint; /** A flag that controls whether or not dividers are visible. */ private boolean drawDividers; /** The stroke used to draw the dividers. */ private transient Stroke dividerStroke; /** The paint used to draw the dividers. */ private transient Paint dividerPaint; /** * Creates a new instance. * * @param periodClass the subclass of {@link RegularTimePeriod} to use * ({@code null} not permitted). * @param dateFormat the date format ({@code null} not permitted). */ public PeriodAxisLabelInfo(Class periodClass, DateFormat dateFormat) { this(periodClass, dateFormat, DEFAULT_INSETS, DEFAULT_FONT, DEFAULT_LABEL_PAINT, true, DEFAULT_DIVIDER_STROKE, DEFAULT_DIVIDER_PAINT); } /** * Creates a new instance. * * @param periodClass the subclass of {@link RegularTimePeriod} to use * ({@code null} not permitted). * @param dateFormat the date format ({@code null} not permitted). * @param padding controls the space around the band ({@code null} * not permitted). * @param labelFont the label font ({@code null} not permitted). * @param labelPaint the label paint ({@code null} not permitted). * @param drawDividers a flag that controls whether dividers are drawn. * @param dividerStroke the stroke used to draw the dividers * ({@code null} not permitted). * @param dividerPaint the paint used to draw the dividers * ({@code null} not permitted). */ public PeriodAxisLabelInfo(Class periodClass, DateFormat dateFormat, RectangleInsets padding, Font labelFont, Paint labelPaint, boolean drawDividers, Stroke dividerStroke, Paint dividerPaint) { Args.nullNotPermitted(periodClass, "periodClass"); Args.nullNotPermitted(dateFormat, "dateFormat"); Args.nullNotPermitted(padding, "padding"); Args.nullNotPermitted(labelFont, "labelFont"); Args.nullNotPermitted(labelPaint, "labelPaint"); Args.nullNotPermitted(dividerStroke, "dividerStroke"); Args.nullNotPermitted(dividerPaint, "dividerPaint"); this.periodClass = periodClass; this.dateFormat = (DateFormat) dateFormat.clone(); this.padding = padding; this.labelFont = labelFont; this.labelPaint = labelPaint; this.drawDividers = drawDividers; this.dividerStroke = dividerStroke; this.dividerPaint = dividerPaint; } /** * Returns the subclass of {@link RegularTimePeriod} that should be used * to generate the date labels. * * @return The class. */ public Class getPeriodClass() { return this.periodClass; } /** * Returns a copy of the date formatter. * * @return A copy of the date formatter (never {@code null}). */ public DateFormat getDateFormat() { return (DateFormat) this.dateFormat.clone(); } /** * Returns the padding for the band. * * @return The padding. */ public RectangleInsets getPadding() { return this.padding; } /** * Returns the label font. * * @return The label font (never {@code null}). */ public Font getLabelFont() { return this.labelFont; } /** * Returns the label paint. * * @return The label paint. */ public Paint getLabelPaint() { return this.labelPaint; } /** * Returns a flag that controls whether or not dividers are drawn. * * @return A flag. */ public boolean getDrawDividers() { return this.drawDividers; } /** * Returns the stroke used to draw the dividers. * * @return The stroke. */ public Stroke getDividerStroke() { return this.dividerStroke; } /** * Returns the paint used to draw the dividers. * * @return The paint. */ public Paint getDividerPaint() { return this.dividerPaint; } /** * Creates a time period that includes the specified millisecond, assuming * the given time zone. * * @param millisecond the time. * @param zone the time zone. * @param locale the locale. * * @return The time period. */ public RegularTimePeriod createInstance(Date millisecond, TimeZone zone, Locale locale) { RegularTimePeriod result = null; try { Constructor c = this.periodClass.getDeclaredConstructor( new Class[] {Date.class, TimeZone.class, Locale.class}); result = (RegularTimePeriod) c.newInstance(new Object[] { millisecond, zone, locale}); } catch (Exception e) { // do nothing } return result; } /** * Tests this object for equality with an arbitrary object. * * @param obj the object to test against ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj instanceof PeriodAxisLabelInfo) { PeriodAxisLabelInfo info = (PeriodAxisLabelInfo) obj; if (!info.periodClass.equals(this.periodClass)) { return false; } if (!info.dateFormat.equals(this.dateFormat)) { return false; } if (!info.padding.equals(this.padding)) { return false; } if (!info.labelFont.equals(this.labelFont)) { return false; } if (!info.labelPaint.equals(this.labelPaint)) { return false; } if (info.drawDividers != this.drawDividers) { return false; } if (!info.dividerStroke.equals(this.dividerStroke)) { return false; } if (!info.dividerPaint.equals(this.dividerPaint)) { return false; } return true; } return false; } /** * Returns a hash code for this object. * * @return A hash code. */ @Override public int hashCode() { int result = 41; result = result + 37 * this.periodClass.hashCode(); result = result + 37 * this.dateFormat.hashCode(); return result; } /** * Returns a clone of the object. * * @return A clone. * * @throws CloneNotSupportedException if cloning is not supported. */ @Override public Object clone() throws CloneNotSupportedException { PeriodAxisLabelInfo clone = (PeriodAxisLabelInfo) super.clone(); return clone; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.labelPaint, stream); SerialUtils.writeStroke(this.dividerStroke, stream); SerialUtils.writePaint(this.dividerPaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.labelPaint = SerialUtils.readPaint(stream); this.dividerStroke = SerialUtils.readStroke(stream); this.dividerPaint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/QuarterDateFormat.java000066400000000000000000000144261463604235500302660ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * QuarterDateFormat.java * ---------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.io.Serializable; import java.text.DateFormat; import java.text.FieldPosition; import java.text.NumberFormat; import java.text.ParsePosition; import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.TimeZone; import org.jfree.chart.util.Args; /** * A formatter that formats dates to show the year and quarter (for example, * '2004 IV' for the last quarter of 2004. */ public class QuarterDateFormat extends DateFormat implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -6738465248529797176L; /** Symbols for regular quarters. */ public static final String[] REGULAR_QUARTERS = new String[] {"1", "2", "3", "4"}; /** Symbols for roman numbered quarters. */ public static final String[] ROMAN_QUARTERS = new String[] {"I", "II", "III", "IV"}; /** Symbols for greek numbered quarters. */ public static final String[] GREEK_QUARTERS = new String[] {"\u0391", "\u0392", "\u0393", "\u0394"}; /** The strings. */ private String[] quarters = REGULAR_QUARTERS; /** A flag that controls whether the quarter or the year goes first. */ private boolean quarterFirst; /** * Creates a new instance for the default time zone. */ public QuarterDateFormat() { this(TimeZone.getDefault()); } /** * Creates a new instance for the specified time zone. * * @param zone the time zone ({@code null} not permitted). */ public QuarterDateFormat(TimeZone zone) { this(zone, REGULAR_QUARTERS); } /** * Creates a new instance for the specified time zone. * * @param zone the time zone ({@code null} not permitted). * @param quarterSymbols the quarter symbols. */ public QuarterDateFormat(TimeZone zone, String[] quarterSymbols) { this(zone, quarterSymbols, false); } /** * Creates a new instance for the specified time zone. * * @param zone the time zone ({@code null} not permitted). * @param quarterSymbols the quarter symbols. * @param quarterFirst a flag that controls whether the quarter or the * year is displayed first. */ public QuarterDateFormat(TimeZone zone, String[] quarterSymbols, boolean quarterFirst) { Args.nullNotPermitted(zone, "zone"); this.calendar = new GregorianCalendar(zone); this.quarters = quarterSymbols; this.quarterFirst = quarterFirst; // the following is never used, but it seems that DateFormat requires // it to be non-null. It isn't well covered in the spec, refer to // bug parade 5061189 for more info. this.numberFormat = NumberFormat.getNumberInstance(); } /** * Formats the given date. * * @param date the date. * @param toAppendTo the string buffer. * @param fieldPosition the field position. * * @return The formatted date. */ @Override public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) { this.calendar.setTime(date); int year = this.calendar.get(Calendar.YEAR); int month = this.calendar.get(Calendar.MONTH); int quarter = month / 3; if (this.quarterFirst) { toAppendTo.append(this.quarters[quarter]); toAppendTo.append(" "); toAppendTo.append(year); } else { toAppendTo.append(year); toAppendTo.append(" "); toAppendTo.append(this.quarters[quarter]); } return toAppendTo; } /** * Parses the given string (not implemented). * * @param source the date string. * @param pos the parse position. * * @return {@code null}, as this method has not been implemented. */ @Override public Date parse(String source, ParsePosition pos) { return null; } /** * Tests this formatter for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof QuarterDateFormat)) { return false; } QuarterDateFormat that = (QuarterDateFormat) obj; if (!Arrays.equals(this.quarters, that.quarters)) { return false; } if (this.quarterFirst != that.quarterFirst) { return false; } return super.equals(obj); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/StandardTickUnitSource.java000066400000000000000000000077561463604235500313000ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * StandardTickUnitSource.java * --------------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.io.Serializable; import java.text.DecimalFormat; /** * A source that can used by the {@link NumberAxis} class to obtain a * suitable {@link TickUnit}. Instances of this class are {@link Serializable} * from version 1.0.7 onwards. Cloning is not supported, because instances * are immutable. */ public class StandardTickUnitSource implements TickUnitSource, Serializable { /** Constant for log(10.0). */ private static final double LOG_10_VALUE = Math.log(10.0); /** * Default constructor. */ public StandardTickUnitSource() { super(); } /** * Returns a tick unit that is larger than the supplied unit. * * @param unit the unit ({@code null} not permitted). * * @return A tick unit that is larger than the supplied unit. */ @Override public TickUnit getLargerTickUnit(TickUnit unit) { double x = unit.getSize(); double log = Math.log(x) / LOG_10_VALUE; double higher = Math.ceil(log); return new NumberTickUnit(Math.pow(10, higher), new DecimalFormat("0.0E0")); } /** * Returns the tick unit in the collection that is greater than or equal * to (in size) the specified unit. * * @param unit the unit ({@code null} not permitted). * * @return A unit from the collection. */ @Override public TickUnit getCeilingTickUnit(TickUnit unit) { return getLargerTickUnit(unit); } /** * Returns the tick unit in the collection that is greater than or equal * to the specified size. * * @param size the size. * * @return A unit from the collection. */ @Override public TickUnit getCeilingTickUnit(double size) { double log = Math.log(size) / LOG_10_VALUE; double higher = Math.ceil(log); return new NumberTickUnit(Math.pow(10, higher), new DecimalFormat("0.0E0")); } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } return (obj instanceof StandardTickUnitSource); } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { return 0; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/SubCategoryAxis.java000066400000000000000000000361321463604235500277460ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * SubCategoryAxis.java * -------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Adriaan Joubert; * */ package org.jfree.chart.axis; import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Iterator; import java.util.List; import org.jfree.chart.event.AxisChangeEvent; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.text.TextUtils; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.TextAnchor; import org.jfree.chart.util.Args; import org.jfree.chart.util.SerialUtils; import org.jfree.data.category.CategoryDataset; /** * A specialised category axis that can display sub-categories. */ public class SubCategoryAxis extends CategoryAxis implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -1279463299793228344L; /** Storage for the sub-categories (these need to be set manually). */ private List subCategories; /** The font for the sub-category labels. */ private Font subLabelFont = new Font("SansSerif", Font.PLAIN, 10); /** The paint for the sub-category labels. */ private transient Paint subLabelPaint = Color.BLACK; /** * Creates a new axis. * * @param label the axis label. */ public SubCategoryAxis(String label) { super(label); this.subCategories = new java.util.ArrayList(); } /** * Adds a sub-category to the axis and sends an {@link AxisChangeEvent} to * all registered listeners. * * @param subCategory the sub-category ({@code null} not permitted). */ public void addSubCategory(Comparable subCategory) { Args.nullNotPermitted(subCategory, "subCategory"); this.subCategories.add(subCategory); notifyListeners(new AxisChangeEvent(this)); } /** * Returns the font used to display the sub-category labels. * * @return The font (never {@code null}). * * @see #setSubLabelFont(Font) */ public Font getSubLabelFont() { return this.subLabelFont; } /** * Sets the font used to display the sub-category labels and sends an * {@link AxisChangeEvent} to all registered listeners. * * @param font the font ({@code null} not permitted). * * @see #getSubLabelFont() */ public void setSubLabelFont(Font font) { Args.nullNotPermitted(font, "font"); this.subLabelFont = font; notifyListeners(new AxisChangeEvent(this)); } /** * Returns the paint used to display the sub-category labels. * * @return The paint (never {@code null}). * * @see #setSubLabelPaint(Paint) */ public Paint getSubLabelPaint() { return this.subLabelPaint; } /** * Sets the paint used to display the sub-category labels and sends an * {@link AxisChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getSubLabelPaint() */ public void setSubLabelPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.subLabelPaint = paint; notifyListeners(new AxisChangeEvent(this)); } /** * Estimates the space required for the axis, given a specific drawing area. * * @param g2 the graphics device (used to obtain font information). * @param plot the plot that the axis belongs to. * @param plotArea the area within which the axis should be drawn. * @param edge the axis location (top or bottom). * @param space the space already reserved. * * @return The space required to draw the axis. */ @Override public AxisSpace reserveSpace(Graphics2D g2, Plot plot, Rectangle2D plotArea, RectangleEdge edge, AxisSpace space) { // create a new space object if one wasn't supplied... if (space == null) { space = new AxisSpace(); } // if the axis is not visible, no additional space is required... if (!isVisible()) { return space; } space = super.reserveSpace(g2, plot, plotArea, edge, space); double maxdim = getMaxDim(g2, edge); if (RectangleEdge.isTopOrBottom(edge)) { space.add(maxdim, edge); } else if (RectangleEdge.isLeftOrRight(edge)) { space.add(maxdim, edge); } return space; } /** * Returns the maximum of the relevant dimension (height or width) of the * subcategory labels. * * @param g2 the graphics device. * @param edge the edge. * * @return The maximum dimension. */ private double getMaxDim(Graphics2D g2, RectangleEdge edge) { double result = 0.0; g2.setFont(this.subLabelFont); FontMetrics fm = g2.getFontMetrics(); Iterator iterator = this.subCategories.iterator(); while (iterator.hasNext()) { Comparable subcategory = (Comparable) iterator.next(); String label = subcategory.toString(); Rectangle2D bounds = TextUtils.getTextBounds(label, g2, fm); double dim; if (RectangleEdge.isLeftOrRight(edge)) { dim = bounds.getWidth(); } else { // must be top or bottom dim = bounds.getHeight(); } result = Math.max(result, dim); } return result; } /** * Draws the axis on a Java 2D graphics device (such as the screen or a * printer). * * @param g2 the graphics device ({@code null} not permitted). * @param cursor the cursor location. * @param plotArea the area within which the axis should be drawn * ({@code null} not permitted). * @param dataArea the area within which the plot is being drawn * ({@code null} not permitted). * @param edge the location of the axis ({@code null} not permitted). * @param plotState collects information about the plot * ({@code null} permitted). * * @return The axis state (never {@code null}). */ @Override public AxisState draw(Graphics2D g2, double cursor, Rectangle2D plotArea, Rectangle2D dataArea, RectangleEdge edge, PlotRenderingInfo plotState) { // if the axis is not visible, don't draw it... if (!isVisible()) { return new AxisState(cursor); } if (isAxisLineVisible()) { drawAxisLine(g2, cursor, dataArea, edge); } // draw the category labels and axis label AxisState state = new AxisState(cursor); state = drawSubCategoryLabels(g2, plotArea, dataArea, edge, state, plotState); state = drawCategoryLabels(g2, plotArea, dataArea, edge, state, plotState); if (getAttributedLabel() != null) { state = drawAttributedLabel(getAttributedLabel(), g2, plotArea, dataArea, edge, state); } else { state = drawLabel(getLabel(), g2, plotArea, dataArea, edge, state); } return state; } /** * Draws the category labels and returns the updated axis state. * * @param g2 the graphics device ({@code null} not permitted). * @param plotArea the plot area ({@code null} not permitted). * @param dataArea the area inside the axes ({@code null} not * permitted). * @param edge the axis location ({@code null} not permitted). * @param state the axis state ({@code null} not permitted). * @param plotState collects information about the plot ({@code null} * permitted). * * @return The updated axis state (never {@code null}). */ protected AxisState drawSubCategoryLabels(Graphics2D g2, Rectangle2D plotArea, Rectangle2D dataArea, RectangleEdge edge, AxisState state, PlotRenderingInfo plotState) { Args.nullNotPermitted(state, "state"); g2.setFont(this.subLabelFont); g2.setPaint(this.subLabelPaint); CategoryPlot plot = (CategoryPlot) getPlot(); int categoryCount = 0; CategoryDataset dataset = plot.getDataset(); if (dataset != null) { categoryCount = dataset.getColumnCount(); } double maxdim = getMaxDim(g2, edge); for (int categoryIndex = 0; categoryIndex < categoryCount; categoryIndex++) { double x0 = 0.0; double x1 = 0.0; double y0 = 0.0; double y1 = 0.0; if (edge == RectangleEdge.TOP) { x0 = getCategoryStart(categoryIndex, categoryCount, dataArea, edge); x1 = getCategoryEnd(categoryIndex, categoryCount, dataArea, edge); y1 = state.getCursor(); y0 = y1 - maxdim; } else if (edge == RectangleEdge.BOTTOM) { x0 = getCategoryStart(categoryIndex, categoryCount, dataArea, edge); x1 = getCategoryEnd(categoryIndex, categoryCount, dataArea, edge); y0 = state.getCursor(); y1 = y0 + maxdim; } else if (edge == RectangleEdge.LEFT) { y0 = getCategoryStart(categoryIndex, categoryCount, dataArea, edge); y1 = getCategoryEnd(categoryIndex, categoryCount, dataArea, edge); x1 = state.getCursor(); x0 = x1 - maxdim; } else if (edge == RectangleEdge.RIGHT) { y0 = getCategoryStart(categoryIndex, categoryCount, dataArea, edge); y1 = getCategoryEnd(categoryIndex, categoryCount, dataArea, edge); x0 = state.getCursor(); x1 = x0 + maxdim; } Rectangle2D area = new Rectangle2D.Double(x0, y0, (x1 - x0), (y1 - y0)); int subCategoryCount = this.subCategories.size(); float width = (float) ((x1 - x0) / subCategoryCount); float height = (float) ((y1 - y0) / subCategoryCount); float xx, yy; for (int i = 0; i < subCategoryCount; i++) { if (RectangleEdge.isTopOrBottom(edge)) { xx = (float) (x0 + (i + 0.5) * width); yy = (float) area.getCenterY(); } else { xx = (float) area.getCenterX(); yy = (float) (y0 + (i + 0.5) * height); } String label = this.subCategories.get(i).toString(); TextUtils.drawRotatedString(label, g2, xx, yy, TextAnchor.CENTER, 0.0, TextAnchor.CENTER); } } if (edge.equals(RectangleEdge.TOP)) { double h = maxdim; state.cursorUp(h); } else if (edge.equals(RectangleEdge.BOTTOM)) { double h = maxdim; state.cursorDown(h); } else if (edge == RectangleEdge.LEFT) { double w = maxdim; state.cursorLeft(w); } else if (edge == RectangleEdge.RIGHT) { double w = maxdim; state.cursorRight(w); } return state; } /** * Tests the axis for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj instanceof SubCategoryAxis && super.equals(obj)) { SubCategoryAxis axis = (SubCategoryAxis) obj; if (!this.subCategories.equals(axis.subCategories)) { return false; } if (!this.subLabelFont.equals(axis.subLabelFont)) { return false; } if (!this.subLabelPaint.equals(axis.subLabelPaint)) { return false; } return true; } return false; } /** * Returns a hashcode for this instance. * * @return A hashcode for this instance. */ @Override public int hashCode() { return super.hashCode(); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.subLabelPaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.subLabelPaint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/SymbolAxis.java000066400000000000000000000664571463604235500270010ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * SymbolAxis.java * --------------- * (C) Copyright 2002-present, by Anthony Boulestreau and Contributors. * * Original Author: Anthony Boulestreau; * Contributor(s): David Gilbert; * */ package org.jfree.chart.axis; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.text.NumberFormat; import java.util.Arrays; import java.util.Iterator; import java.util.List; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.ValueAxisPlot; import org.jfree.chart.text.TextUtils; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.TextAnchor; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.SerialUtils; import org.jfree.data.Range; /** * A standard linear value axis that replaces integer values with symbols. */ public class SymbolAxis extends NumberAxis implements Serializable { /** For serialization. */ private static final long serialVersionUID = 7216330468770619716L; /** The default grid band paint. */ public static final Paint DEFAULT_GRID_BAND_PAINT = new Color(232, 234, 232, 128); /** * The default paint for alternate grid bands. */ public static final Paint DEFAULT_GRID_BAND_ALTERNATE_PAINT = new Color(0, 0, 0, 0); // transparent /** The list of symbols to display instead of the numeric values. */ private List symbols; /** Flag that indicates whether or not grid bands are visible. */ private boolean gridBandsVisible; /** The paint used to color the grid bands (if the bands are visible). */ private transient Paint gridBandPaint; /** * The paint used to fill the alternate grid bands. */ private transient Paint gridBandAlternatePaint; /** * Constructs a symbol axis, using default attribute values where * necessary. * * @param label the axis label ({@code null} permitted). * @param sv the list of symbols to display instead of the numeric * values. */ public SymbolAxis(String label, String[] sv) { super(label); this.symbols = Arrays.asList(sv); this.gridBandsVisible = true; this.gridBandPaint = DEFAULT_GRID_BAND_PAINT; this.gridBandAlternatePaint = DEFAULT_GRID_BAND_ALTERNATE_PAINT; setAutoTickUnitSelection(false, false); setAutoRangeStickyZero(false); } /** * Returns an array of the symbols for the axis. * * @return The symbols. */ public String[] getSymbols() { String[] result = new String[this.symbols.size()]; result = (String[]) this.symbols.toArray(result); return result; } /** * Returns the flag that controls whether or not grid bands are drawn for * the axis. The default value is {@code true}. * * @return A boolean. * * @see #setGridBandsVisible(boolean) */ public boolean isGridBandsVisible() { return this.gridBandsVisible; } /** * Sets the flag that controls whether or not grid bands are drawn for this * axis and notifies registered listeners that the axis has been modified. * Each band is the area between two adjacent gridlines * running perpendicular to the axis. When the bands are drawn they are * filled with the colors {@link #getGridBandPaint()} and * {@link #getGridBandAlternatePaint()} in an alternating sequence. * * @param flag the new setting. * * @see #isGridBandsVisible() */ public void setGridBandsVisible(boolean flag) { this.gridBandsVisible = flag; fireChangeEvent(); } /** * Returns the paint used to color grid bands (two colors are used * alternately, the other is returned by * {@link #getGridBandAlternatePaint()}). The default value is * {@link #DEFAULT_GRID_BAND_PAINT}. * * @return The paint (never {@code null}). * * @see #setGridBandPaint(Paint) * @see #isGridBandsVisible() */ public Paint getGridBandPaint() { return this.gridBandPaint; } /** * Sets the grid band paint and notifies registered listeners that the * axis has been changed. See the {@link #setGridBandsVisible(boolean)} * method for more information about grid bands. * * @param paint the paint ({@code null} not permitted). * * @see #getGridBandPaint() */ public void setGridBandPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.gridBandPaint = paint; fireChangeEvent(); } /** * Returns the second paint used to color grid bands (two colors are used * alternately, the other is returned by {@link #getGridBandPaint()}). * The default value is {@link #DEFAULT_GRID_BAND_ALTERNATE_PAINT} * (transparent). * * @return The paint (never {@code null}). * * @see #setGridBandAlternatePaint(Paint) */ public Paint getGridBandAlternatePaint() { return this.gridBandAlternatePaint; } /** * Sets the grid band paint and notifies registered listeners that the * axis has been changed. See the {@link #setGridBandsVisible(boolean)} * method for more information about grid bands. * * @param paint the paint ({@code null} not permitted). * * @see #getGridBandAlternatePaint() * @see #setGridBandPaint(Paint) */ public void setGridBandAlternatePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.gridBandAlternatePaint = paint; fireChangeEvent(); } /** * This operation is not supported by this axis. * * @param g2 the graphics device. * @param dataArea the area in which the plot and axes should be drawn. * @param edge the edge along which the axis is drawn. */ @Override protected void selectAutoTickUnit(Graphics2D g2, Rectangle2D dataArea, RectangleEdge edge) { throw new UnsupportedOperationException(); } /** * Draws the axis on a Java 2D graphics device (such as the screen or a * printer). * * @param g2 the graphics device ({@code null} not permitted). * @param cursor the cursor location. * @param plotArea the area within which the plot and axes should be drawn * ({@code null} not permitted). * @param dataArea the area within which the data should be drawn * ({@code null} not permitted). * @param edge the axis location ({@code null} not permitted). * @param plotState collects information about the plot * ({@code null} permitted). * * @return The axis state (never {@code null}). */ @Override public AxisState draw(Graphics2D g2, double cursor, Rectangle2D plotArea, Rectangle2D dataArea, RectangleEdge edge, PlotRenderingInfo plotState) { AxisState info = new AxisState(cursor); if (isVisible()) { info = super.draw(g2, cursor, plotArea, dataArea, edge, plotState); } if (this.gridBandsVisible) { drawGridBands(g2, plotArea, dataArea, edge, info.getTicks()); } return info; } /** * Draws the grid bands (alternate bands are colored using * {@link #getGridBandPaint()} and {@link #getGridBandAlternatePaint()}. * * @param g2 the graphics target ({@code null} not permitted). * @param plotArea the area within which the plot is drawn * ({@code null} not permitted). * @param dataArea the data area to which the axes are aligned * ({@code null} not permitted). * @param edge the edge to which the axis is aligned ({@code null} not * permitted). * @param ticks the ticks ({@code null} not permitted). */ protected void drawGridBands(Graphics2D g2, Rectangle2D plotArea, Rectangle2D dataArea, RectangleEdge edge, List ticks) { Shape savedClip = g2.getClip(); g2.clip(dataArea); if (RectangleEdge.isTopOrBottom(edge)) { drawGridBandsHorizontal(g2, plotArea, dataArea, true, ticks); } else if (RectangleEdge.isLeftOrRight(edge)) { drawGridBandsVertical(g2, plotArea, dataArea, true, ticks); } g2.setClip(savedClip); } /** * Draws the grid bands for the axis when it is at the top or bottom of * the plot. * * @param g2 the graphics target ({@code null} not permitted). * @param plotArea the area within which the plot is drawn (not used here). * @param dataArea the area for the data (to which the axes are aligned, * {@code null} not permitted). * @param firstGridBandIsDark True: the first grid band takes the * color of {@code gridBandPaint}. * False: the second grid band takes the * color of {@code gridBandPaint}. * @param ticks a list of ticks ({@code null} not permitted). */ protected void drawGridBandsHorizontal(Graphics2D g2, Rectangle2D plotArea, Rectangle2D dataArea, boolean firstGridBandIsDark, List ticks) { boolean currentGridBandIsDark = firstGridBandIsDark; double yy = dataArea.getY(); double xx1, xx2; //gets the outline stroke width of the plot double outlineStrokeWidth = 1.0; Stroke outlineStroke = getPlot().getOutlineStroke(); if (outlineStroke != null && outlineStroke instanceof BasicStroke) { outlineStrokeWidth = ((BasicStroke) outlineStroke).getLineWidth(); } Iterator iterator = ticks.iterator(); ValueTick tick; Rectangle2D band; while (iterator.hasNext()) { tick = (ValueTick) iterator.next(); xx1 = valueToJava2D(tick.getValue() - 0.5d, dataArea, RectangleEdge.BOTTOM); xx2 = valueToJava2D(tick.getValue() + 0.5d, dataArea, RectangleEdge.BOTTOM); if (currentGridBandIsDark) { g2.setPaint(this.gridBandPaint); } else { g2.setPaint(this.gridBandAlternatePaint); } band = new Rectangle2D.Double(Math.min(xx1, xx2), yy + outlineStrokeWidth, Math.abs(xx2 - xx1), dataArea.getMaxY() - yy - outlineStrokeWidth); g2.fill(band); currentGridBandIsDark = !currentGridBandIsDark; } } /** * Draws the grid bands for an axis that is aligned to the left or * right of the data area (that is, a vertical axis). * * @param g2 the graphics target ({@code null} not permitted). * @param plotArea the area within which the plot is drawn (not used here). * @param dataArea the area for the data (to which the axes are aligned, * {@code null} not permitted). * @param firstGridBandIsDark True: the first grid band takes the * color of {@code gridBandPaint}. * False: the second grid band takes the * color of {@code gridBandPaint}. * @param ticks a list of ticks ({@code null} not permitted). */ protected void drawGridBandsVertical(Graphics2D g2, Rectangle2D plotArea, Rectangle2D dataArea, boolean firstGridBandIsDark, List ticks) { boolean currentGridBandIsDark = firstGridBandIsDark; double xx = dataArea.getX(); double yy1, yy2; //gets the outline stroke width of the plot double outlineStrokeWidth = 1.0; Stroke outlineStroke = getPlot().getOutlineStroke(); if (outlineStroke != null && outlineStroke instanceof BasicStroke) { outlineStrokeWidth = ((BasicStroke) outlineStroke).getLineWidth(); } Iterator iterator = ticks.iterator(); ValueTick tick; Rectangle2D band; while (iterator.hasNext()) { tick = (ValueTick) iterator.next(); yy1 = valueToJava2D(tick.getValue() + 0.5d, dataArea, RectangleEdge.LEFT); yy2 = valueToJava2D(tick.getValue() - 0.5d, dataArea, RectangleEdge.LEFT); if (currentGridBandIsDark) { g2.setPaint(this.gridBandPaint); } else { g2.setPaint(this.gridBandAlternatePaint); } band = new Rectangle2D.Double(xx + outlineStrokeWidth, Math.min(yy1, yy2), dataArea.getMaxX() - xx - outlineStrokeWidth, Math.abs(yy2 - yy1)); g2.fill(band); currentGridBandIsDark = !currentGridBandIsDark; } } /** * Rescales the axis to ensure that all data is visible. */ @Override protected void autoAdjustRange() { Plot plot = getPlot(); if (plot == null) { return; // no plot, no data } if (plot instanceof ValueAxisPlot) { // ensure that all the symbols are displayed double upper = this.symbols.size() - 1; double lower = 0; double range = upper - lower; // ensure the autorange is at least in size... double minRange = getAutoRangeMinimumSize(); if (range < minRange) { upper = (upper + lower + minRange) / 2; lower = (upper + lower - minRange) / 2; } // this ensure that the grid bands will be displayed correctly. double upperMargin = 0.5; double lowerMargin = 0.5; if (getAutoRangeIncludesZero()) { if (getAutoRangeStickyZero()) { if (upper <= 0.0) { upper = 0.0; } else { upper = upper + upperMargin; } if (lower >= 0.0) { lower = 0.0; } else { lower = lower - lowerMargin; } } else { upper = Math.max(0.0, upper + upperMargin); lower = Math.min(0.0, lower - lowerMargin); } } else { if (getAutoRangeStickyZero()) { if (upper <= 0.0) { upper = Math.min(0.0, upper + upperMargin); } else { upper = upper + upperMargin * range; } if (lower >= 0.0) { lower = Math.max(0.0, lower - lowerMargin); } else { lower = lower - lowerMargin; } } else { upper = upper + upperMargin; lower = lower - lowerMargin; } } setRange(new Range(lower, upper), false, false); } } /** * Calculates the positions of the tick labels for the axis, storing the * results in the tick label list (ready for drawing). * * @param g2 the graphics device. * @param state the axis state. * @param dataArea the area in which the data should be drawn. * @param edge the location of the axis. * * @return A list of ticks. */ @Override public List refreshTicks(Graphics2D g2, AxisState state, Rectangle2D dataArea, RectangleEdge edge) { List ticks = null; if (RectangleEdge.isTopOrBottom(edge)) { ticks = refreshTicksHorizontal(g2, dataArea, edge); } else if (RectangleEdge.isLeftOrRight(edge)) { ticks = refreshTicksVertical(g2, dataArea, edge); } return ticks; } /** * Calculates the positions of the tick labels for the axis, storing the * results in the tick label list (ready for drawing). * * @param g2 the graphics device. * @param dataArea the area in which the data should be drawn. * @param edge the location of the axis. * * @return The ticks. */ @Override protected List refreshTicksHorizontal(Graphics2D g2, Rectangle2D dataArea, RectangleEdge edge) { List ticks = new java.util.ArrayList(); Font tickLabelFont = getTickLabelFont(); g2.setFont(tickLabelFont); double size = getTickUnit().getSize(); int count = calculateVisibleTickCount(); double lowestTickValue = calculateLowestVisibleTickValue(); double previousDrawnTickLabelPos = 0.0; double previousDrawnTickLabelLength = 0.0; if (count <= ValueAxis.MAXIMUM_TICK_COUNT) { for (int i = 0; i < count; i++) { double currentTickValue = lowestTickValue + (i * size); double xx = valueToJava2D(currentTickValue, dataArea, edge); String tickLabel; NumberFormat formatter = getNumberFormatOverride(); if (formatter != null) { tickLabel = formatter.format(currentTickValue); } else { tickLabel = valueToString(currentTickValue); } // avoid to draw overlapping tick labels Rectangle2D bounds = TextUtils.getTextBounds(tickLabel, g2, g2.getFontMetrics()); double tickLabelLength = isVerticalTickLabels() ? bounds.getHeight() : bounds.getWidth(); boolean tickLabelsOverlapping = false; if (i > 0) { double avgTickLabelLength = (previousDrawnTickLabelLength + tickLabelLength) / 2.0; if (Math.abs(xx - previousDrawnTickLabelPos) < avgTickLabelLength) { tickLabelsOverlapping = true; } } if (tickLabelsOverlapping) { tickLabel = ""; // don't draw this tick label } else { // remember these values for next comparison previousDrawnTickLabelPos = xx; previousDrawnTickLabelLength = tickLabelLength; } TextAnchor anchor; TextAnchor rotationAnchor; double angle = 0.0; if (isVerticalTickLabels()) { anchor = TextAnchor.CENTER_RIGHT; rotationAnchor = TextAnchor.CENTER_RIGHT; if (edge == RectangleEdge.TOP) { angle = Math.PI / 2.0; } else { angle = -Math.PI / 2.0; } } else { if (edge == RectangleEdge.TOP) { anchor = TextAnchor.BOTTOM_CENTER; rotationAnchor = TextAnchor.BOTTOM_CENTER; } else { anchor = TextAnchor.TOP_CENTER; rotationAnchor = TextAnchor.TOP_CENTER; } } Tick tick = new NumberTick(currentTickValue, tickLabel, anchor, rotationAnchor, angle); ticks.add(tick); } } return ticks; } /** * Calculates the positions of the tick labels for the axis, storing the * results in the tick label list (ready for drawing). * * @param g2 the graphics device. * @param dataArea the area in which the plot should be drawn. * @param edge the location of the axis. * * @return The ticks. */ @Override protected List refreshTicksVertical(Graphics2D g2, Rectangle2D dataArea, RectangleEdge edge) { List ticks = new java.util.ArrayList(); Font tickLabelFont = getTickLabelFont(); g2.setFont(tickLabelFont); double size = getTickUnit().getSize(); int count = calculateVisibleTickCount(); double lowestTickValue = calculateLowestVisibleTickValue(); double previousDrawnTickLabelPos = 0.0; double previousDrawnTickLabelLength = 0.0; if (count <= ValueAxis.MAXIMUM_TICK_COUNT) { for (int i = 0; i < count; i++) { double currentTickValue = lowestTickValue + (i * size); double yy = valueToJava2D(currentTickValue, dataArea, edge); String tickLabel; NumberFormat formatter = getNumberFormatOverride(); if (formatter != null) { tickLabel = formatter.format(currentTickValue); } else { tickLabel = valueToString(currentTickValue); } // avoid to draw overlapping tick labels Rectangle2D bounds = TextUtils.getTextBounds(tickLabel, g2, g2.getFontMetrics()); double tickLabelLength = isVerticalTickLabels() ? bounds.getWidth() : bounds.getHeight(); boolean tickLabelsOverlapping = false; if (i > 0) { double avgTickLabelLength = (previousDrawnTickLabelLength + tickLabelLength) / 2.0; if (Math.abs(yy - previousDrawnTickLabelPos) < avgTickLabelLength) { tickLabelsOverlapping = true; } } if (tickLabelsOverlapping) { tickLabel = ""; // don't draw this tick label } else { // remember these values for next comparison previousDrawnTickLabelPos = yy; previousDrawnTickLabelLength = tickLabelLength; } TextAnchor anchor; TextAnchor rotationAnchor; double angle = 0.0; if (isVerticalTickLabels()) { anchor = TextAnchor.BOTTOM_CENTER; rotationAnchor = TextAnchor.BOTTOM_CENTER; if (edge == RectangleEdge.LEFT) { angle = -Math.PI / 2.0; } else { angle = Math.PI / 2.0; } } else { if (edge == RectangleEdge.LEFT) { anchor = TextAnchor.CENTER_RIGHT; rotationAnchor = TextAnchor.CENTER_RIGHT; } else { anchor = TextAnchor.CENTER_LEFT; rotationAnchor = TextAnchor.CENTER_LEFT; } } Tick tick = new NumberTick(currentTickValue, tickLabel, anchor, rotationAnchor, angle); ticks.add(tick); } } return ticks; } /** * Converts a value to a string, using the list of symbols. * * @param value value to convert. * * @return The symbol. */ public String valueToString(double value) { String strToReturn; try { strToReturn = (String) this.symbols.get((int) value); } catch (IndexOutOfBoundsException ex) { strToReturn = ""; } return strToReturn; } /** * Tests this axis for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof SymbolAxis)) { return false; } SymbolAxis that = (SymbolAxis) obj; if (!this.symbols.equals(that.symbols)) { return false; } if (this.gridBandsVisible != that.gridBandsVisible) { return false; } if (!PaintUtils.equal(this.gridBandPaint, that.gridBandPaint)) { return false; } if (!PaintUtils.equal(this.gridBandAlternatePaint, that.gridBandAlternatePaint)) { return false; } return super.equals(obj); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.gridBandPaint, stream); SerialUtils.writePaint(this.gridBandAlternatePaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.gridBandPaint = SerialUtils.readPaint(stream); this.gridBandAlternatePaint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/Tick.java000066400000000000000000000137331463604235500255660ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------- * Tick.java * --------- * (C) Copyright 2000-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Nicolas Brodu; * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); */ package org.jfree.chart.axis; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.ui.TextAnchor; import org.jfree.chart.util.Args; /** * The base class used to represent labeled ticks along an axis. */ public abstract class Tick implements Serializable, Cloneable { /** For serialization. */ private static final long serialVersionUID = 6668230383875149773L; /** A text version of the tick value. */ private String text; /** The text anchor for the tick label. */ private TextAnchor textAnchor; /** The rotation anchor for the tick label. */ private TextAnchor rotationAnchor; /** The rotation angle. */ private double angle; /** * Creates a new tick. * * @param text the formatted version of the tick value. * @param textAnchor the text anchor ({@code null} not permitted). * @param rotationAnchor the rotation anchor ({@code null} not * permitted). * @param angle the angle. */ public Tick(String text, TextAnchor textAnchor, TextAnchor rotationAnchor, double angle) { Args.nullNotPermitted(textAnchor, "textAnchor"); Args.nullNotPermitted(rotationAnchor, "rotationAnchor"); this.text = text; this.textAnchor = textAnchor; this.rotationAnchor = rotationAnchor; this.angle = angle; } /** * Returns the text version of the tick value. * * @return A string (possibly {@code null}); */ public String getText() { return this.text; } /** * Returns the text anchor. * * @return The text anchor (never {@code null}). */ public TextAnchor getTextAnchor() { return this.textAnchor; } /** * Returns the text anchor that defines the point around which the label is * rotated. * * @return A text anchor (never {@code null}). */ public TextAnchor getRotationAnchor() { return this.rotationAnchor; } /** * Returns the angle. * * @return The angle. */ public double getAngle() { return this.angle; } /** * Tests this tick for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof Tick)) { return false; } Tick that = (Tick) obj; if (Double.doubleToLongBits(this.angle) != Double.doubleToLongBits(that.angle)) { return false; } if (!Objects.equals(this.text, that.text)) { return false; } if (!Objects.equals(this.textAnchor, that.textAnchor)) { return false; } if (!Objects.equals(this.rotationAnchor, that.rotationAnchor)) { return false; } if (!that.canEqual(this)) { return false; } return true; } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof Tick); } @Override public int hashCode() { int hash = 7; hash = 79 * hash + Objects.hashCode(this.text); hash = 79 * hash + Objects.hashCode(this.textAnchor); hash = 79 * hash + Objects.hashCode(this.rotationAnchor); hash = 79 * hash + (int) (Double.doubleToLongBits(this.angle) ^ (Double.doubleToLongBits(this.angle) >>> 32)); return hash; } /** * Returns a clone of the tick. * * @return A clone. * * @throws CloneNotSupportedException if there is a problem cloning. */ @Override public Object clone() throws CloneNotSupportedException { Tick clone = (Tick) super.clone(); return clone; } /** * Returns a string representation of the tick. * * @return A string. */ @Override public String toString() { return this.text; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/TickType.java000066400000000000000000000061711463604235500264260ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------- * TickType.java * ------------- * (C) Copyright 2007-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.io.ObjectStreamException; import java.io.Serializable; /** * Used to indicate the tick type (MAJOR or MINOR). */ public final class TickType implements Serializable { /** Major tick. */ public static final TickType MAJOR = new TickType("MAJOR"); /** Minor tick. */ public static final TickType MINOR = new TickType("MINOR"); /** The name. */ private String name; /** * Private constructor. * * @param name the name. */ private TickType(String name) { this.name = name; } /** * Returns a string representing the object. * * @return The string. */ @Override public String toString() { return this.name; } /** * Returns {@code true} if this object is equal to the specified * object, and {@code false} otherwise. * * @param obj the other object. * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof TickType)) { return false; } TickType that = (TickType) obj; if (!this.name.equals(that.name)) { return false; } return true; } /** * Ensures that serialization returns the unique instances. * * @return The object. * * @throws ObjectStreamException if there is a problem. */ private Object readResolve() throws ObjectStreamException { Object result = null; if (this.equals(TickType.MAJOR)) { result = TickType.MAJOR; } else if (this.equals(TickType.MINOR)) { result = TickType.MINOR; } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/TickUnit.java000066400000000000000000000121111463604235500264130ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------- * TickUnit.java * ------------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.io.Serializable; /** * Base class representing a tick unit. This determines the spacing of the * tick marks on an axis. *

* This class (and any subclasses) should be immutable, the reason being that * ORDERED collections of tick units are maintained and if one instance can be * changed, it may destroy the order of the collection that it belongs to. * In addition, if the implementations are immutable, they can belong to * multiple collections. * * @see ValueAxis */ public abstract class TickUnit implements Comparable, Serializable { /** For serialization. */ private static final long serialVersionUID = 510179855057013974L; /** The size of the tick unit. */ private double size; /** The number of minor ticks. */ private int minorTickCount; /** * Constructs a new tick unit. * * @param size the tick unit size. */ public TickUnit(double size) { this.size = size; } /** * Constructs a new tick unit. * * @param size the tick unit size. * @param minorTickCount the minor tick count. */ public TickUnit(double size, int minorTickCount) { this.size = size; this.minorTickCount = minorTickCount; } /** * Returns the size of the tick unit. * * @return The size of the tick unit. */ public double getSize() { return this.size; } /** * Returns the minor tick count. * * @return The minor tick count. */ public int getMinorTickCount() { return this.minorTickCount; } /** * Converts the supplied value to a string. *

* Subclasses may implement special formatting by overriding this method. * * @param value the data value. * * @return Value as string. */ public String valueToString(double value) { return String.valueOf(value); } /** * Compares this tick unit to an arbitrary object. * * @param object the object to compare against. * * @return {@code 1} if the size of the other object is less than this, * {@code 0} if both have the same size and {@code -1} this * size is less than the others. */ @Override public int compareTo(Object object) { if (object instanceof TickUnit) { TickUnit other = (TickUnit) object; if (this.size > other.getSize()) { return 1; } else if (this.size < other.getSize()) { return -1; } else { return 0; } } else { return -1; } } /** * Tests this unit for equality with another object. * * @param obj the object. * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof TickUnit)) { return false; } TickUnit that = (TickUnit) obj; if (this.size != that.size) { return false; } if (this.minorTickCount != that.minorTickCount) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { long temp = this.size != +0.0d ? Double.doubleToLongBits(this.size) : 0L; return (int) (temp ^ (temp >>> 32)); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/TickUnitSource.java000066400000000000000000000052201463604235500275770ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * TickUnitSource.java * ------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; /** * An interface used by the {@link DateAxis} and {@link NumberAxis} classes to * obtain a suitable {@link TickUnit}. */ public interface TickUnitSource { /** * Returns the smallest tick unit available in the source that is larger * than {@code unit} or, if there is no larger unit, returns {@code unit}. * * @param unit the unit ({@code null} not permitted). * * @return A tick unit that is larger than the supplied unit. */ TickUnit getLargerTickUnit(TickUnit unit); /** * Returns the tick unit in the collection that is greater than or equal * to (in size) the specified unit. * * @param unit the unit. * * @return A unit from the collection. */ TickUnit getCeilingTickUnit(TickUnit unit); /** * Returns the smallest tick unit available in the source that is greater * than or equal to the specified size. If there is no such tick unit, * the method should return the largest available tick in the source. * * @param size the size. * * @return A unit from the collection (never {@code null}). */ TickUnit getCeilingTickUnit(double size); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/TickUnits.java000066400000000000000000000131541463604235500266060ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * TickUnits.java * -------------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.io.Serializable; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * A collection of tick units, used by the {@link DateAxis} and * {@link NumberAxis} classes. */ public class TickUnits implements TickUnitSource, Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 1134174035901467545L; /** Storage for the tick units. */ private List tickUnits; /** * Constructs a new collection of tick units. */ public TickUnits() { this.tickUnits = new ArrayList(); } /** * Adds a tick unit to the collection. The tick units are maintained in * ascending order. * * @param unit the tick unit to add ({@code null} not permitted). */ public void add(TickUnit unit) { if (unit == null) { throw new NullPointerException("Null 'unit' argument."); } this.tickUnits.add(unit); Collections.sort(this.tickUnits); } /** * Returns the number of tick units in this collection. *

* This method is required for the XML writer. * * @return The number of units in this collection. */ public int size() { return this.tickUnits.size(); } /** * Returns the tickunit on the given position. *

* This method is required for the XML writer. * * @param pos the position in the list. * * @return The tickunit. */ public TickUnit get(int pos) { return (TickUnit) this.tickUnits.get(pos); } /** * Returns a tick unit that is larger than the supplied unit. * * @param unit the unit. * * @return A tick unit that is larger than the supplied unit. */ @Override public TickUnit getLargerTickUnit(TickUnit unit) { int index = Collections.binarySearch(this.tickUnits, unit); if (index >= 0) { index = index + 1; } else { index = -index; } return (TickUnit) this.tickUnits.get(Math.min(index, this.tickUnits.size() - 1)); } /** * Returns the tick unit in the collection that is greater than or equal * to (in size) the specified unit. * * @param unit the unit. * * @return A unit from the collection. */ @Override public TickUnit getCeilingTickUnit(TickUnit unit) { int index = Collections.binarySearch(this.tickUnits, unit); if (index >= 0) { return (TickUnit) this.tickUnits.get(index); } else { index = -(index + 1); return (TickUnit) this.tickUnits.get(Math.min(index, this.tickUnits.size() - 1)); } } /** * Returns the tick unit in the collection that is greater than or equal * to the specified size. * * @param size the size. * * @return A unit from the collection. */ @Override public TickUnit getCeilingTickUnit(double size) { return getCeilingTickUnit(new NumberTickUnit(size, NumberFormat.getInstance())); } /** * Returns a clone of the collection. * * @return A clone. * * @throws CloneNotSupportedException if an item in the collection does not * support cloning. */ @Override public Object clone() throws CloneNotSupportedException { TickUnits clone = (TickUnits) super.clone(); clone.tickUnits = new java.util.ArrayList(this.tickUnits); return clone; } /** * Tests an object for equality with this instance. * * @param obj the object to test ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof TickUnits)) { return false; } TickUnits that = (TickUnits) obj; return that.tickUnits.equals(this.tickUnits); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/Timeline.java000066400000000000000000000112671463604235500264420ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------- * Timeline.java * ------------- * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Original Author: Bill Kelemen; * Contributor(s): David Gilbert; * */ package org.jfree.chart.axis; import java.util.Date; /** * An interface that defines the contract for a Timeline. *

* A Timeline will present a series of values to be used for an axis. Each * Timeline must provide transformation methods between domain values and * timeline values. In theory many transformations are possible. *

* A timeline can be used as parameter to a * {@link org.jfree.chart.axis.DateAxis} to define the values that this axis * supports. *

* Because Timelines were created mainly for Date related axis, values are * represented as longs instead of doubles. In this case, the domain value is * just the number of milliseconds since January 1, 1970, 00:00:00 GMT as * defined by the getTime() method of {@link java.util.Date}. * * @see org.jfree.chart.axis.DateAxis */ public interface Timeline { /** * Translates a millisecond (as defined by java.util.Date) into an index * along this timeline. * * @param millisecond the millisecond. * * @return A timeline value. */ long toTimelineValue(long millisecond); /** * Translates a date into a value on this timeline. * * @param date the date. * * @return A timeline value */ long toTimelineValue(Date date); /** * Translates a value relative to this timeline into a domain value. The * domain value obtained by this method is not always the same domain value * that could have been supplied to * translateDomainValueToTimelineValue(domainValue). * This is because the original transformation may not be complete * reversable. * * @param timelineValue a timeline value. * * @return A domain value. */ long toMillisecond(long timelineValue); /** * Returns {@code true} if a value is contained in the timeline values. * * @param millisecond the millisecond. * * @return {@code true} if value is contained in the timeline and * {@code false} otherwise. */ boolean containsDomainValue(long millisecond); /** * Returns {@code true} if a date is contained in the timeline values. * * @param date the date to verify. * * @return {@code true} if value is contained in the timeline and * {@code false} otherwise. */ boolean containsDomainValue(Date date); /** * Returns {@code true} if a range of values are contained in the * timeline. * * @param fromMillisecond the start of the range to verify. * @param toMillisecond the end of the range to verify. * * @return {@code true} if the range is contained in the timeline or * {@code false} otherwise */ boolean containsDomainRange(long fromMillisecond, long toMillisecond); /** * Returns {@code true} if a range of dates are contained in the * timeline. * * @param fromDate the start of the range to verify. * @param toDate the end of the range to verify. * * @return {@code true} if the range is contained in the timeline or * {@code false} otherwise */ boolean containsDomainRange(Date fromDate, Date toDate); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/ValueAxis.java000066400000000000000000001577441463604235500266100ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * ValueAxis.java * -------------- * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Jonathan Nash; * Nicolas Brodu (for Astrium and EADS Corporate Research * Center); * Peter Kolb (patch 1934255); * Andrew Mickish (patch 1870189); * */ package org.jfree.chart.axis; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Polygon; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.font.LineMetrics; import java.awt.geom.AffineTransform; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Iterator; import java.util.List; import java.util.Objects; import org.jfree.chart.event.AxisChangeEvent; import org.jfree.chart.plot.Plot; import org.jfree.chart.text.TextUtils; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.util.AttrStringUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; import org.jfree.data.Range; /** * The base class for axes that display value data, where values are measured * using the {@code double} primitive. The two key subclasses are * {@link DateAxis} and {@link NumberAxis}. */ public abstract class ValueAxis extends Axis implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 3698345477322391456L; /** The default axis range. */ public static final Range DEFAULT_RANGE = new Range(0.0, 1.0); /** The default auto-range value. */ public static final boolean DEFAULT_AUTO_RANGE = true; /** The default inverted flag setting. */ public static final boolean DEFAULT_INVERTED = false; /** The default minimum auto range. */ public static final double DEFAULT_AUTO_RANGE_MINIMUM_SIZE = 0.00000001; /** The default value for the lower margin (0.05 = 5%). */ public static final double DEFAULT_LOWER_MARGIN = 0.05; /** The default value for the upper margin (0.05 = 5%). */ public static final double DEFAULT_UPPER_MARGIN = 0.05; /** The default auto-tick-unit-selection value. */ public static final boolean DEFAULT_AUTO_TICK_UNIT_SELECTION = true; /** The maximum tick count. */ public static final int MAXIMUM_TICK_COUNT = 500; /** * A flag that controls whether an arrow is drawn at the positive end of * the axis line. */ private boolean positiveArrowVisible; /** * A flag that controls whether an arrow is drawn at the negative end of * the axis line. */ private boolean negativeArrowVisible; /** The shape used for an up arrow. */ private transient Shape upArrow; /** The shape used for a down arrow. */ private transient Shape downArrow; /** The shape used for a left arrow. */ private transient Shape leftArrow; /** The shape used for a right arrow. */ private transient Shape rightArrow; /** A flag that affects the orientation of the values on the axis. */ private boolean inverted; /** The axis range. */ private Range range; /** * Flag that indicates whether the axis automatically scales to fit the * chart data. */ private boolean autoRange; /** The minimum size for the 'auto' axis range (excluding margins). */ private double autoRangeMinimumSize; /** * The default range is used when the dataset is empty and the axis needs * to determine the auto range. */ private Range defaultAutoRange; /** * The upper margin percentage. This indicates the amount by which the * maximum axis value exceeds the maximum data value (as a percentage of * the range on the axis) when the axis range is determined automatically. */ private double upperMargin; /** * The lower margin. This is a percentage that indicates the amount by * which the minimum axis value is "less than" the minimum data value when * the axis range is determined automatically. */ private double lowerMargin; /** * If this value is positive, the amount is subtracted from the maximum * data value to determine the lower axis range. This can be used to * provide a fixed "window" on dynamic data. */ private double fixedAutoRange; /** * Flag that indicates whether or not the tick unit is selected * automatically. */ private boolean autoTickUnitSelection; /** The standard tick units for the axis. */ private TickUnitSource standardTickUnits; /** An index into an array of standard tick values. */ private int autoTickIndex; /** * The number of minor ticks per major tick unit. This is an override * field, if the value is > 0 it is used, otherwise the axis refers to the * minorTickCount in the current tickUnit. */ private int minorTickCount; /** A flag indicating whether or not tick labels are rotated to vertical. */ private boolean verticalTickLabels; /** * Constructs a value axis. * * @param label the axis label ({@code null} permitted). * @param standardTickUnits the source for standard tick units * ({@code null} permitted). */ protected ValueAxis(String label, TickUnitSource standardTickUnits) { super(label); this.positiveArrowVisible = false; this.negativeArrowVisible = false; this.range = DEFAULT_RANGE; this.autoRange = DEFAULT_AUTO_RANGE; this.defaultAutoRange = DEFAULT_RANGE; this.inverted = DEFAULT_INVERTED; this.autoRangeMinimumSize = DEFAULT_AUTO_RANGE_MINIMUM_SIZE; this.lowerMargin = DEFAULT_LOWER_MARGIN; this.upperMargin = DEFAULT_UPPER_MARGIN; this.fixedAutoRange = 0.0; this.autoTickUnitSelection = DEFAULT_AUTO_TICK_UNIT_SELECTION; this.standardTickUnits = standardTickUnits; Polygon p1 = new Polygon(); p1.addPoint(0, 0); p1.addPoint(-2, 2); p1.addPoint(2, 2); this.upArrow = p1; Polygon p2 = new Polygon(); p2.addPoint(0, 0); p2.addPoint(-2, -2); p2.addPoint(2, -2); this.downArrow = p2; Polygon p3 = new Polygon(); p3.addPoint(0, 0); p3.addPoint(-2, -2); p3.addPoint(-2, 2); this.rightArrow = p3; Polygon p4 = new Polygon(); p4.addPoint(0, 0); p4.addPoint(2, -2); p4.addPoint(2, 2); this.leftArrow = p4; this.verticalTickLabels = false; this.minorTickCount = 0; } /** * Returns {@code true} if the tick labels should be rotated (to * vertical), and {@code false} otherwise. * * @return {@code true} or {@code false}. * * @see #setVerticalTickLabels(boolean) */ public boolean isVerticalTickLabels() { return this.verticalTickLabels; } /** * Sets the flag that controls whether the tick labels are displayed * vertically (that is, rotated 90 degrees from horizontal). If the flag * is changed, an {@link AxisChangeEvent} is sent to all registered * listeners. * * @param flag the flag. * * @see #isVerticalTickLabels() */ public void setVerticalTickLabels(boolean flag) { if (this.verticalTickLabels != flag) { this.verticalTickLabels = flag; fireChangeEvent(); } } /** * Returns a flag that controls whether or not the axis line has an arrow * drawn that points in the positive direction for the axis. * * @return A boolean. * * @see #setPositiveArrowVisible(boolean) */ public boolean isPositiveArrowVisible() { return this.positiveArrowVisible; } /** * Sets a flag that controls whether or not the axis lines has an arrow * drawn that points in the positive direction for the axis, and sends an * {@link AxisChangeEvent} to all registered listeners. * * @param visible the flag. * * @see #isPositiveArrowVisible() */ public void setPositiveArrowVisible(boolean visible) { this.positiveArrowVisible = visible; fireChangeEvent(); } /** * Returns a flag that controls whether or not the axis line has an arrow * drawn that points in the negative direction for the axis. * * @return A boolean. * * @see #setNegativeArrowVisible(boolean) */ public boolean isNegativeArrowVisible() { return this.negativeArrowVisible; } /** * Sets a flag that controls whether or not the axis lines has an arrow * drawn that points in the negative direction for the axis, and sends an * {@link AxisChangeEvent} to all registered listeners. * * @param visible the flag. * * @see #setNegativeArrowVisible(boolean) */ public void setNegativeArrowVisible(boolean visible) { this.negativeArrowVisible = visible; fireChangeEvent(); } /** * Returns a shape that can be displayed as an arrow pointing upwards at * the end of an axis line. * * @return A shape (never {@code null}). * * @see #setUpArrow(Shape) */ public Shape getUpArrow() { return this.upArrow; } /** * Sets the shape that can be displayed as an arrow pointing upwards at * the end of an axis line and sends an {@link AxisChangeEvent} to all * registered listeners. * * @param arrow the arrow shape ({@code null} not permitted). * * @see #getUpArrow() */ public void setUpArrow(Shape arrow) { Args.nullNotPermitted(arrow, "arrow"); this.upArrow = arrow; fireChangeEvent(); } /** * Returns a shape that can be displayed as an arrow pointing downwards at * the end of an axis line. * * @return A shape (never {@code null}). * * @see #setDownArrow(Shape) */ public Shape getDownArrow() { return this.downArrow; } /** * Sets the shape that can be displayed as an arrow pointing downwards at * the end of an axis line and sends an {@link AxisChangeEvent} to all * registered listeners. * * @param arrow the arrow shape ({@code null} not permitted). * * @see #getDownArrow() */ public void setDownArrow(Shape arrow) { Args.nullNotPermitted(arrow, "arrow"); this.downArrow = arrow; fireChangeEvent(); } /** * Returns a shape that can be displayed as an arrow pointing left at the * end of an axis line. * * @return A shape (never {@code null}). * * @see #setLeftArrow(Shape) */ public Shape getLeftArrow() { return this.leftArrow; } /** * Sets the shape that can be displayed as an arrow pointing left at the * end of an axis line and sends an {@link AxisChangeEvent} to all * registered listeners. * * @param arrow the arrow shape ({@code null} not permitted). * * @see #getLeftArrow() */ public void setLeftArrow(Shape arrow) { Args.nullNotPermitted(arrow, "arrow"); this.leftArrow = arrow; fireChangeEvent(); } /** * Returns a shape that can be displayed as an arrow pointing right at the * end of an axis line. * * @return A shape (never {@code null}). * * @see #setRightArrow(Shape) */ public Shape getRightArrow() { return this.rightArrow; } /** * Sets the shape that can be displayed as an arrow pointing rightwards at * the end of an axis line and sends an {@link AxisChangeEvent} to all * registered listeners. * * @param arrow the arrow shape ({@code null} not permitted). * * @see #getRightArrow() */ public void setRightArrow(Shape arrow) { Args.nullNotPermitted(arrow, "arrow"); this.rightArrow = arrow; fireChangeEvent(); } /** * Draws an axis line at the current cursor position and edge. * * @param g2 the graphics device ({@code null} not permitted). * @param cursor the cursor position. * @param dataArea the data area. * @param edge the edge. */ @Override protected void drawAxisLine(Graphics2D g2, double cursor, Rectangle2D dataArea, RectangleEdge edge) { Line2D axisLine = null; double c = cursor; if (edge == RectangleEdge.TOP) { axisLine = new Line2D.Double(dataArea.getX(), c, dataArea.getMaxX(), c); } else if (edge == RectangleEdge.BOTTOM) { axisLine = new Line2D.Double(dataArea.getX(), c, dataArea.getMaxX(), c); } else if (edge == RectangleEdge.LEFT) { axisLine = new Line2D.Double(c, dataArea.getY(), c, dataArea.getMaxY()); } else if (edge == RectangleEdge.RIGHT) { axisLine = new Line2D.Double(c, dataArea.getY(), c, dataArea.getMaxY()); } g2.setPaint(getAxisLinePaint()); g2.setStroke(getAxisLineStroke()); Object saved = g2.getRenderingHint(RenderingHints.KEY_STROKE_CONTROL); g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE); g2.draw(axisLine); g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, saved); boolean drawUpOrRight = false; boolean drawDownOrLeft = false; if (this.positiveArrowVisible) { if (this.inverted) { drawDownOrLeft = true; } else { drawUpOrRight = true; } } if (this.negativeArrowVisible) { if (this.inverted) { drawUpOrRight = true; } else { drawDownOrLeft = true; } } if (drawUpOrRight) { double x = 0.0; double y = 0.0; Shape arrow = null; if (edge == RectangleEdge.TOP || edge == RectangleEdge.BOTTOM) { x = dataArea.getMaxX(); y = cursor; arrow = this.rightArrow; } else if (edge == RectangleEdge.LEFT || edge == RectangleEdge.RIGHT) { x = cursor; y = dataArea.getMinY(); arrow = this.upArrow; } // draw the arrow... AffineTransform transformer = new AffineTransform(); transformer.setToTranslation(x, y); Shape shape = transformer.createTransformedShape(arrow); g2.fill(shape); g2.draw(shape); } if (drawDownOrLeft) { double x = 0.0; double y = 0.0; Shape arrow = null; if (edge == RectangleEdge.TOP || edge == RectangleEdge.BOTTOM) { x = dataArea.getMinX(); y = cursor; arrow = this.leftArrow; } else if (edge == RectangleEdge.LEFT || edge == RectangleEdge.RIGHT) { x = cursor; y = dataArea.getMaxY(); arrow = this.downArrow; } // draw the arrow... AffineTransform transformer = new AffineTransform(); transformer.setToTranslation(x, y); Shape shape = transformer.createTransformedShape(arrow); g2.fill(shape); g2.draw(shape); } } /** * Calculates the anchor point for a tick label. * * @param tick the tick. * @param cursor the cursor. * @param dataArea the data area. * @param edge the edge on which the axis is drawn. * * @return The x and y coordinates of the anchor point. */ protected float[] calculateAnchorPoint(ValueTick tick, double cursor, Rectangle2D dataArea, RectangleEdge edge) { RectangleInsets insets = getTickLabelInsets(); float[] result = new float[2]; if (edge == RectangleEdge.TOP) { result[0] = (float) valueToJava2D(tick.getValue(), dataArea, edge); result[1] = (float) (cursor - insets.getBottom() - 2.0); } else if (edge == RectangleEdge.BOTTOM) { result[0] = (float) valueToJava2D(tick.getValue(), dataArea, edge); result[1] = (float) (cursor + insets.getTop() + 2.0); } else if (edge == RectangleEdge.LEFT) { result[0] = (float) (cursor - insets.getLeft() - 2.0); result[1] = (float) valueToJava2D(tick.getValue(), dataArea, edge); } else if (edge == RectangleEdge.RIGHT) { result[0] = (float) (cursor + insets.getRight() + 2.0); result[1] = (float) valueToJava2D(tick.getValue(), dataArea, edge); } return result; } /** * Draws the axis line, tick marks and tick mark labels. * * @param g2 the graphics device ({@code null} not permitted). * @param cursor the cursor. * @param plotArea the plot area ({@code null} not permitted). * @param dataArea the data area ({@code null} not permitted). * @param edge the edge that the axis is aligned with ({@code null} * not permitted). * * @return The width or height used to draw the axis. */ protected AxisState drawTickMarksAndLabels(Graphics2D g2, double cursor, Rectangle2D plotArea, Rectangle2D dataArea, RectangleEdge edge) { AxisState state = new AxisState(cursor); if (isAxisLineVisible()) { drawAxisLine(g2, cursor, dataArea, edge); } List ticks = refreshTicks(g2, state, dataArea, edge); state.setTicks(ticks); g2.setFont(getTickLabelFont()); Object saved = g2.getRenderingHint(RenderingHints.KEY_STROKE_CONTROL); g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE); Iterator iterator = ticks.iterator(); while (iterator.hasNext()) { ValueTick tick = (ValueTick) iterator.next(); if (isTickLabelsVisible()) { g2.setPaint(getTickLabelPaint()); float[] anchorPoint = calculateAnchorPoint(tick, cursor, dataArea, edge); if (tick instanceof LogTick) { LogTick lt = (LogTick) tick; if (lt.getAttributedLabel() == null) { continue; } AttrStringUtils.drawRotatedString(lt.getAttributedLabel(), g2, anchorPoint[0], anchorPoint[1], tick.getTextAnchor(), tick.getAngle(), tick.getRotationAnchor()); } else { if (tick.getText() == null) { continue; } TextUtils.drawRotatedString(tick.getText(), g2, anchorPoint[0], anchorPoint[1], tick.getTextAnchor(), tick.getAngle(), tick.getRotationAnchor()); } } if ((isTickMarksVisible() && tick.getTickType().equals( TickType.MAJOR)) || (isMinorTickMarksVisible() && tick.getTickType().equals(TickType.MINOR))) { double ol = (tick.getTickType().equals(TickType.MINOR)) ? getMinorTickMarkOutsideLength() : getTickMarkOutsideLength(); double il = (tick.getTickType().equals(TickType.MINOR)) ? getMinorTickMarkInsideLength() : getTickMarkInsideLength(); float xx = (float) valueToJava2D(tick.getValue(), dataArea, edge); Line2D mark = null; g2.setStroke(getTickMarkStroke()); g2.setPaint(getTickMarkPaint()); if (edge == RectangleEdge.LEFT) { mark = new Line2D.Double(cursor - ol, xx, cursor + il, xx); } else if (edge == RectangleEdge.RIGHT) { mark = new Line2D.Double(cursor + ol, xx, cursor - il, xx); } else if (edge == RectangleEdge.TOP) { mark = new Line2D.Double(xx, cursor - ol, xx, cursor + il); } else if (edge == RectangleEdge.BOTTOM) { mark = new Line2D.Double(xx, cursor + ol, xx, cursor - il); } g2.draw(mark); } } g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, saved); // need to work out the space used by the tick labels... // so we can update the cursor... double used = 0.0; if (isTickLabelsVisible()) { if (edge == RectangleEdge.LEFT) { used += findMaximumTickLabelWidth(ticks, g2, plotArea, isVerticalTickLabels()); state.cursorLeft(used); } else if (edge == RectangleEdge.RIGHT) { used = findMaximumTickLabelWidth(ticks, g2, plotArea, isVerticalTickLabels()); state.cursorRight(used); } else if (edge == RectangleEdge.TOP) { used = findMaximumTickLabelHeight(ticks, g2, plotArea, isVerticalTickLabels()); state.cursorUp(used); } else if (edge == RectangleEdge.BOTTOM) { used = findMaximumTickLabelHeight(ticks, g2, plotArea, isVerticalTickLabels()); state.cursorDown(used); } } return state; } /** * Returns the space required to draw the axis. * * @param g2 the graphics device. * @param plot the plot that the axis belongs to. * @param plotArea the area within which the plot should be drawn. * @param edge the axis location. * @param space the space already reserved (for other axes). * * @return The space required to draw the axis (including pre-reserved * space). */ @Override public AxisSpace reserveSpace(Graphics2D g2, Plot plot, Rectangle2D plotArea, RectangleEdge edge, AxisSpace space) { // create a new space object if one wasn't supplied... if (space == null) { space = new AxisSpace(); } // if the axis is not visible, no additional space is required... if (!isVisible()) { return space; } // if the axis has a fixed dimension, return it... double dimension = getFixedDimension(); if (dimension > 0.0) { space.add(dimension, edge); return space; } // calculate the max size of the tick labels (if visible)... double tickLabelHeight = 0.0; double tickLabelWidth = 0.0; if (isTickLabelsVisible()) { g2.setFont(getTickLabelFont()); List ticks = refreshTicks(g2, new AxisState(), plotArea, edge); if (RectangleEdge.isTopOrBottom(edge)) { tickLabelHeight = findMaximumTickLabelHeight(ticks, g2, plotArea, isVerticalTickLabels()); } else if (RectangleEdge.isLeftOrRight(edge)) { tickLabelWidth = findMaximumTickLabelWidth(ticks, g2, plotArea, isVerticalTickLabels()); } } // get the axis label size and update the space object... Rectangle2D labelEnclosure = getLabelEnclosure(g2, edge); if (RectangleEdge.isTopOrBottom(edge)) { double labelHeight = labelEnclosure.getHeight(); space.add(labelHeight + tickLabelHeight, edge); } else if (RectangleEdge.isLeftOrRight(edge)) { double labelWidth = labelEnclosure.getWidth(); space.add(labelWidth + tickLabelWidth, edge); } return space; } /** * A utility method for determining the height of the tallest tick label. * * @param ticks the ticks. * @param g2 the graphics device. * @param drawArea the area within which the plot and axes should be drawn. * @param vertical a flag that indicates whether or not the tick labels * are 'vertical'. * * @return The height of the tallest tick label. */ protected double findMaximumTickLabelHeight(List ticks, Graphics2D g2, Rectangle2D drawArea, boolean vertical) { RectangleInsets insets = getTickLabelInsets(); Font font = getTickLabelFont(); g2.setFont(font); double maxHeight = 0.0; if (vertical) { FontMetrics fm = g2.getFontMetrics(font); Iterator iterator = ticks.iterator(); while (iterator.hasNext()) { Tick tick = (Tick) iterator.next(); Rectangle2D labelBounds = null; if (tick instanceof LogTick) { LogTick lt = (LogTick) tick; if (lt.getAttributedLabel() != null) { labelBounds = AttrStringUtils.getTextBounds( lt.getAttributedLabel(), g2); } } else if (tick.getText() != null) { labelBounds = TextUtils.getTextBounds( tick.getText(), g2, fm); } if (labelBounds != null && labelBounds.getWidth() + insets.getTop() + insets.getBottom() > maxHeight) { maxHeight = labelBounds.getWidth() + insets.getTop() + insets.getBottom(); } } } else { LineMetrics metrics = font.getLineMetrics("ABCxyz", g2.getFontRenderContext()); maxHeight = metrics.getHeight() + insets.getTop() + insets.getBottom(); } return maxHeight; } /** * A utility method for determining the width of the widest tick label. * * @param ticks the ticks. * @param g2 the graphics device. * @param drawArea the area within which the plot and axes should be drawn. * @param vertical a flag that indicates whether or not the tick labels * are 'vertical'. * * @return The width of the tallest tick label. */ protected double findMaximumTickLabelWidth(List ticks, Graphics2D g2, Rectangle2D drawArea, boolean vertical) { RectangleInsets insets = getTickLabelInsets(); Font font = getTickLabelFont(); double maxWidth = 0.0; if (!vertical) { FontMetrics fm = g2.getFontMetrics(font); Iterator iterator = ticks.iterator(); while (iterator.hasNext()) { Tick tick = (Tick) iterator.next(); Rectangle2D labelBounds = null; if (tick instanceof LogTick) { LogTick lt = (LogTick) tick; if (lt.getAttributedLabel() != null) { labelBounds = AttrStringUtils.getTextBounds( lt.getAttributedLabel(), g2); } } else if (tick.getText() != null) { labelBounds = TextUtils.getTextBounds(tick.getText(), g2, fm); } if (labelBounds != null && labelBounds.getWidth() + insets.getLeft() + insets.getRight() > maxWidth) { maxWidth = labelBounds.getWidth() + insets.getLeft() + insets.getRight(); } } } else { LineMetrics metrics = font.getLineMetrics("ABCxyz", g2.getFontRenderContext()); maxWidth = metrics.getHeight() + insets.getTop() + insets.getBottom(); } return maxWidth; } /** * Returns a flag that controls the direction of values on the axis. *

* For a regular axis, values increase from left to right (for a horizontal * axis) and bottom to top (for a vertical axis). When the axis is * 'inverted', the values increase in the opposite direction. * * @return The flag. * * @see #setInverted(boolean) */ public boolean isInverted() { return this.inverted; } /** * Sets a flag that controls the direction of values on the axis, and * notifies registered listeners that the axis has changed. * * @param flag the flag. * * @see #isInverted() */ public void setInverted(boolean flag) { if (this.inverted != flag) { this.inverted = flag; fireChangeEvent(); } } /** * Returns the flag that controls whether or not the axis range is * automatically adjusted to fit the data values. * * @return The flag. * * @see #setAutoRange(boolean) */ public boolean isAutoRange() { return this.autoRange; } /** * Sets a flag that determines whether or not the axis range is * automatically adjusted to fit the data, and notifies registered * listeners that the axis has been modified. * * @param auto the new value of the flag. * * @see #isAutoRange() */ public void setAutoRange(boolean auto) { setAutoRange(auto, true); } /** * Sets the auto range attribute. If the {@code notify} flag is set, * an {@link AxisChangeEvent} is sent to registered listeners. * * @param auto the flag. * @param notify notify listeners? * * @see #isAutoRange() */ protected void setAutoRange(boolean auto, boolean notify) { this.autoRange = auto; if (this.autoRange) { autoAdjustRange(); } if (notify) { fireChangeEvent(); } } /** * Returns the minimum size allowed for the axis range when it is * automatically calculated. * * @return The minimum range. * * @see #setAutoRangeMinimumSize(double) */ public double getAutoRangeMinimumSize() { return this.autoRangeMinimumSize; } /** * Sets the auto range minimum size and sends an {@link AxisChangeEvent} * to all registered listeners. * * @param size the size. * * @see #getAutoRangeMinimumSize() */ public void setAutoRangeMinimumSize(double size) { setAutoRangeMinimumSize(size, true); } /** * Sets the minimum size allowed for the axis range when it is * automatically calculated. *

* If requested, an {@link AxisChangeEvent} is forwarded to all registered * listeners. * * @param size the new minimum. * @param notify notify listeners? */ public void setAutoRangeMinimumSize(double size, boolean notify) { if (size <= 0.0) { throw new IllegalArgumentException( "NumberAxis.setAutoRangeMinimumSize(double): must be > 0.0."); } if (this.autoRangeMinimumSize != size) { this.autoRangeMinimumSize = size; if (this.autoRange) { autoAdjustRange(); } if (notify) { fireChangeEvent(); } } } /** * Returns the default auto range. * * @return The default auto range (never {@code null}). * * @see #setDefaultAutoRange(Range) */ public Range getDefaultAutoRange() { return this.defaultAutoRange; } /** * Sets the default auto range and sends an {@link AxisChangeEvent} to all * registered listeners. * * @param range the range ({@code null} not permitted). * * @see #getDefaultAutoRange() */ public void setDefaultAutoRange(Range range) { Args.nullNotPermitted(range, "range"); this.defaultAutoRange = range; fireChangeEvent(); } /** * Returns the lower margin for the axis, expressed as a percentage of the * axis range. This controls the space added to the lower end of the axis * when the axis range is automatically calculated (it is ignored when the * axis range is set explicitly). The default value is 0.05 (five percent). * * @return The lower margin. * * @see #setLowerMargin(double) */ public double getLowerMargin() { return this.lowerMargin; } /** * Sets the lower margin for the axis (as a percentage of the axis range) * and sends an {@link AxisChangeEvent} to all registered listeners. This * margin is added only when the axis range is auto-calculated - if you set * the axis range manually, the margin is ignored. * * @param margin the margin percentage (for example, 0.05 is five percent). * * @see #getLowerMargin() * @see #setUpperMargin(double) */ public void setLowerMargin(double margin) { this.lowerMargin = margin; if (isAutoRange()) { autoAdjustRange(); } fireChangeEvent(); } /** * Returns the upper margin for the axis, expressed as a percentage of the * axis range. This controls the space added to the lower end of the axis * when the axis range is automatically calculated (it is ignored when the * axis range is set explicitly). The default value is 0.05 (five percent). * * @return The upper margin. * * @see #setUpperMargin(double) */ public double getUpperMargin() { return this.upperMargin; } /** * Sets the upper margin for the axis (as a percentage of the axis range) * and sends an {@link AxisChangeEvent} to all registered listeners. This * margin is added only when the axis range is auto-calculated - if you set * the axis range manually, the margin is ignored. * * @param margin the margin percentage (for example, 0.05 is five percent). * * @see #getLowerMargin() * @see #setLowerMargin(double) */ public void setUpperMargin(double margin) { this.upperMargin = margin; if (isAutoRange()) { autoAdjustRange(); } fireChangeEvent(); } /** * Returns the fixed auto range. * * @return The length. * * @see #setFixedAutoRange(double) */ public double getFixedAutoRange() { return this.fixedAutoRange; } /** * Sets the fixed auto range for the axis. * * @param length the range length. * * @see #getFixedAutoRange() */ public void setFixedAutoRange(double length) { this.fixedAutoRange = length; if (isAutoRange()) { autoAdjustRange(); } fireChangeEvent(); } /** * Returns the lower bound of the axis range. * * @return The lower bound. * * @see #setLowerBound(double) */ public double getLowerBound() { return this.range.getLowerBound(); } /** * Sets the lower bound for the axis range. An {@link AxisChangeEvent} is * sent to all registered listeners. * * @param min the new minimum. * * @see #getLowerBound() */ public void setLowerBound(double min) { if (this.range.getUpperBound() > min) { setRange(new Range(min, this.range.getUpperBound())); } else { setRange(new Range(min, min + 1.0)); } } /** * Returns the upper bound for the axis range. * * @return The upper bound. * * @see #setUpperBound(double) */ public double getUpperBound() { return this.range.getUpperBound(); } /** * Sets the upper bound for the axis range, and sends an * {@link AxisChangeEvent} to all registered listeners. * * @param max the new maximum. * * @see #getUpperBound() */ public void setUpperBound(double max) { if (this.range.getLowerBound() < max) { setRange(new Range(this.range.getLowerBound(), max)); } else { setRange(max - 1.0, max); } } /** * Returns the range for the axis. * * @return The axis range (never {@code null}). * * @see #setRange(Range) */ public Range getRange() { return this.range; } /** * Sets the range for the axis and sends a change event to all registered * listeners. As a side-effect, the auto-range flag is set to * {@code false}. * * @param range the range ({@code null} not permitted). * * @see #getRange() */ public void setRange(Range range) { // defer argument checking setRange(range, true, true); } /** * Sets the range for the axis and, if requested, sends a change event to * all registered listeners. Furthermore, if {@code turnOffAutoRange} * is {@code true}, the auto-range flag is set to {@code false} * (normally when setting the axis range manually the caller expects that * range to remain in force). * * @param range the range ({@code null} not permitted). * @param turnOffAutoRange a flag that controls whether or not the auto * range is turned off. * @param notify a flag that controls whether or not listeners are * notified. * * @see #getRange() */ public void setRange(Range range, boolean turnOffAutoRange, boolean notify) { Args.nullNotPermitted(range, "range"); if (range.getLength() <= 0.0) { throw new IllegalArgumentException( "A positive range length is required: " + range); } if (turnOffAutoRange) { this.autoRange = false; } this.range = range; if (notify) { fireChangeEvent(); } } /** * Sets the range for the axis and sends a change event to all registered * listeners. As a side-effect, the auto-range flag is set to * {@code false}. * * @param lower the lower axis limit. * @param upper the upper axis limit. * * @see #getRange() * @see #setRange(Range) */ public void setRange(double lower, double upper) { setRange(new Range(lower, upper)); } /** * Sets the range for the axis (after first adding the current margins to * the specified range) and sends an {@link AxisChangeEvent} to all * registered listeners. * * @param range the range ({@code null} not permitted). */ public void setRangeWithMargins(Range range) { setRangeWithMargins(range, true, true); } /** * Sets the range for the axis after first adding the current margins to * the range and, if requested, sends an {@link AxisChangeEvent} to all * registered listeners. As a side-effect, the auto-range flag is set to * {@code false} (optional). * * @param range the range (excluding margins, {@code null} not * permitted). * @param turnOffAutoRange a flag that controls whether or not the auto * range is turned off. * @param notify a flag that controls whether or not listeners are * notified. */ public void setRangeWithMargins(Range range, boolean turnOffAutoRange, boolean notify) { Args.nullNotPermitted(range, "range"); setRange(Range.expand(range, getLowerMargin(), getUpperMargin()), turnOffAutoRange, notify); } /** * Sets the axis range (after first adding the current margins to the * range) and sends an {@link AxisChangeEvent} to all registered listeners. * As a side-effect, the auto-range flag is set to {@code false}. * * @param lower the lower axis limit. * @param upper the upper axis limit. */ public void setRangeWithMargins(double lower, double upper) { setRangeWithMargins(new Range(lower, upper)); } /** * Sets the axis range, where the new range is 'size' in length, and * centered on 'value'. * * @param value the central value. * @param length the range length. */ public void setRangeAboutValue(double value, double length) { setRange(new Range(value - length / 2, value + length / 2)); } /** * Returns a flag indicating whether or not the tick unit is automatically * selected from a range of standard tick units. * * @return A flag indicating whether or not the tick unit is automatically * selected. * * @see #setAutoTickUnitSelection(boolean) */ public boolean isAutoTickUnitSelection() { return this.autoTickUnitSelection; } /** * Sets a flag indicating whether or not the tick unit is automatically * selected from a range of standard tick units. If the flag is changed, * registered listeners are notified that the chart has changed. * * @param flag the new value of the flag. * * @see #isAutoTickUnitSelection() */ public void setAutoTickUnitSelection(boolean flag) { setAutoTickUnitSelection(flag, true); } /** * Sets a flag indicating whether or not the tick unit is automatically * selected from a range of standard tick units. * * @param flag the new value of the flag. * @param notify notify listeners? * * @see #isAutoTickUnitSelection() */ public void setAutoTickUnitSelection(boolean flag, boolean notify) { if (this.autoTickUnitSelection != flag) { this.autoTickUnitSelection = flag; if (notify) { fireChangeEvent(); } } } /** * Returns the source for obtaining standard tick units for the axis. * * @return The source (possibly {@code null}). * * @see #setStandardTickUnits(TickUnitSource) */ public TickUnitSource getStandardTickUnits() { return this.standardTickUnits; } /** * Sets the source for obtaining standard tick units for the axis and sends * an {@link AxisChangeEvent} to all registered listeners. The axis will * try to select the smallest tick unit from the source that does not cause * the tick labels to overlap (see also the * {@link #setAutoTickUnitSelection(boolean)} method. * * @param source the source for standard tick units ({@code null} * permitted). * * @see #getStandardTickUnits() */ public void setStandardTickUnits(TickUnitSource source) { this.standardTickUnits = source; fireChangeEvent(); } /** * Returns the number of minor tick marks to display. * * @return The number of minor tick marks to display. * * @see #setMinorTickCount(int) */ public int getMinorTickCount() { return this.minorTickCount; } /** * Sets the number of minor tick marks to display, and sends an * {@link AxisChangeEvent} to all registered listeners. * * @param count the count. * * @see #getMinorTickCount() */ public void setMinorTickCount(int count) { this.minorTickCount = count; fireChangeEvent(); } /** * Converts a data value to a coordinate in Java2D space, assuming that the * axis runs along one edge of the specified dataArea. *

* Note that it is possible for the coordinate to fall outside the area. * * @param value the data value. * @param area the area for plotting the data. * @param edge the edge along which the axis lies. * * @return The Java2D coordinate. * * @see #java2DToValue(double, Rectangle2D, RectangleEdge) */ public abstract double valueToJava2D(double value, Rectangle2D area, RectangleEdge edge); /** * Converts a length in data coordinates into the corresponding length in * Java2D coordinates. * * @param length the length. * @param area the plot area. * @param edge the edge along which the axis lies. * * @return The length in Java2D coordinates. */ public double lengthToJava2D(double length, Rectangle2D area, RectangleEdge edge) { double zero = valueToJava2D(0.0, area, edge); double l = valueToJava2D(length, area, edge); return Math.abs(l - zero); } /** * Converts a coordinate in Java2D space to the corresponding data value, * assuming that the axis runs along one edge of the specified dataArea. * * @param java2DValue the coordinate in Java2D space. * @param area the area in which the data is plotted. * @param edge the edge along which the axis lies. * * @return The data value. * * @see #valueToJava2D(double, Rectangle2D, RectangleEdge) */ public abstract double java2DToValue(double java2DValue, Rectangle2D area, RectangleEdge edge); /** * Automatically sets the axis range to fit the range of values in the * dataset. Sometimes this can depend on the renderer used as well (for * example, the renderer may "stack" values, requiring an axis range * greater than otherwise necessary). */ protected abstract void autoAdjustRange(); /** * Centers the axis range about the specified value and sends an * {@link AxisChangeEvent} to all registered listeners. * * @param value the center value. */ public void centerRange(double value) { double central = this.range.getCentralValue(); Range adjusted = new Range(this.range.getLowerBound() + value - central, this.range.getUpperBound() + value - central); setRange(adjusted); } /** * Increases or decreases the axis range by the specified percentage about * the central value and sends an {@link AxisChangeEvent} to all registered * listeners. *

* To double the length of the axis range, use 200% (2.0). * To halve the length of the axis range, use 50% (0.5). * * @param percent the resize factor. * * @see #resizeRange(double, double) */ public void resizeRange(double percent) { resizeRange(percent, this.range.getCentralValue()); } /** * Increases or decreases the axis range by the specified percentage about * the specified anchor value and sends an {@link AxisChangeEvent} to all * registered listeners. *

* To double the length of the axis range, use 200% (2.0). * To halve the length of the axis range, use 50% (0.5). * * @param percent the resize factor. * @param anchorValue the new central value after the resize. * * @see #resizeRange(double) */ public void resizeRange(double percent, double anchorValue) { if (percent > 0.0) { double halfLength = this.range.getLength() * percent / 2; Range adjusted = new Range(anchorValue - halfLength, anchorValue + halfLength); setRange(adjusted); } else { setAutoRange(true); } } /** * Increases or decreases the axis range by the specified percentage about * the specified anchor value and sends an {@link AxisChangeEvent} to all * registered listeners. *

* To double the length of the axis range, use 200% (2.0). * To halve the length of the axis range, use 50% (0.5). * * @param percent the resize factor. * @param anchorValue the new central value after the resize. * * @see #resizeRange(double) */ public void resizeRange2(double percent, double anchorValue) { if (percent > 0.0) { double left = anchorValue - getLowerBound(); double right = getUpperBound() - anchorValue; Range adjusted = new Range(anchorValue - left * percent, anchorValue + right * percent); setRange(adjusted); } else { setAutoRange(true); } } /** * Zooms in on the current range. * * @param lowerPercent the new lower bound. * @param upperPercent the new upper bound. */ public void zoomRange(double lowerPercent, double upperPercent) { double start = this.range.getLowerBound(); double length = this.range.getLength(); double r0, r1; if (isInverted()) { r0 = start + (length * (1 - upperPercent)); r1 = start + (length * (1 - lowerPercent)); } else { r0 = start + length * lowerPercent; r1 = start + length * upperPercent; } if ((r1 > r0) && !Double.isInfinite(r1 - r0)) { setRange(new Range(r0, r1)); } } /** * Slides the axis range by the specified percentage. * * @param percent the percentage. */ public void pan(double percent) { Range r = getRange(); double length = range.getLength(); double adj = length * percent; double lower = r.getLowerBound() + adj; double upper = r.getUpperBound() + adj; setRange(lower, upper); } /** * Returns the auto tick index. * * @return The auto tick index. * * @see #setAutoTickIndex(int) */ protected int getAutoTickIndex() { return this.autoTickIndex; } /** * Sets the auto tick index. * * @param index the new value. * * @see #getAutoTickIndex() */ protected void setAutoTickIndex(int index) { this.autoTickIndex = index; } /** * Tests the axis for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof ValueAxis)) { return false; } ValueAxis that = (ValueAxis) obj; if (this.positiveArrowVisible != that.positiveArrowVisible) { return false; } if (this.negativeArrowVisible != that.negativeArrowVisible) { return false; } if (this.inverted != that.inverted) { return false; } // if autoRange is true, then the current range is irrelevant if (!this.autoRange && !Objects.equals(this.range, that.range)) { return false; } if (this.autoRange != that.autoRange) { return false; } if (this.autoRangeMinimumSize != that.autoRangeMinimumSize) { return false; } if (!this.defaultAutoRange.equals(that.defaultAutoRange)) { return false; } if (this.upperMargin != that.upperMargin) { return false; } if (this.lowerMargin != that.lowerMargin) { return false; } if (this.fixedAutoRange != that.fixedAutoRange) { return false; } if (this.autoTickUnitSelection != that.autoTickUnitSelection) { return false; } if (!Objects.equals(this.standardTickUnits, that.standardTickUnits)) { return false; } if (this.verticalTickLabels != that.verticalTickLabels) { return false; } if (this.minorTickCount != that.minorTickCount) { return false; } return super.equals(obj); } /** * Returns a clone of the object. * * @return A clone. * * @throws CloneNotSupportedException if some component of the axis does * not support cloning. */ @Override public Object clone() throws CloneNotSupportedException { ValueAxis clone = (ValueAxis) super.clone(); return clone; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeShape(this.upArrow, stream); SerialUtils.writeShape(this.downArrow, stream); SerialUtils.writeShape(this.leftArrow, stream); SerialUtils.writeShape(this.rightArrow, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.upArrow = SerialUtils.readShape(stream); this.downArrow = SerialUtils.readShape(stream); this.leftArrow = SerialUtils.readShape(stream); this.rightArrow = SerialUtils.readShape(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/ValueTick.java000066400000000000000000000100641463604235500265550ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * ValueTick.java * -------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import org.jfree.chart.ui.TextAnchor; /** * A value tick. */ public abstract class ValueTick extends Tick { /** The value. */ private double value; /** * The tick type (major or minor). */ private TickType tickType; /** * Creates a new value tick. * * @param value the value. * @param label the label. * @param textAnchor the part of the label that is aligned to the anchor * point. * @param rotationAnchor defines the rotation point relative to the label. * @param angle the rotation angle (in radians). */ public ValueTick(double value, String label, TextAnchor textAnchor, TextAnchor rotationAnchor, double angle) { this(TickType.MAJOR, value, label, textAnchor, rotationAnchor, angle); this.value = value; } /** * Creates a new value tick. * * @param tickType the tick type (major or minor, {@code null} not * permitted). * @param value the value. * @param label the label. * @param textAnchor the part of the label that is aligned to the anchor * point. * @param rotationAnchor defines the rotation point relative to the label. * @param angle the rotation angle (in radians). */ public ValueTick(TickType tickType, double value, String label, TextAnchor textAnchor, TextAnchor rotationAnchor, double angle) { super(label, textAnchor, rotationAnchor, angle); this.value = value; this.tickType = tickType; } /** * Returns the value. * * @return The value. */ public double getValue() { return this.value; } /** * Returns the tick type (major or minor). * * @return The tick type. */ public TickType getTickType() { return this.tickType; } /** * Tests this tick for equality with an arbitrary object. * * @param obj the object to test ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof ValueTick)) { return false; } ValueTick that = (ValueTick) obj; if (this.value != that.value) { return false; } if (!this.tickType.equals(that.tickType)) { return false; } return super.equals(obj); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/axis/package.html000066400000000000000000000002111463604235500262750ustar00rootroot00000000000000 Axis classes and interfaces. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/block/000077500000000000000000000000001463604235500241505ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/block/AbstractBlock.java000066400000000000000000000463411463604235500275410ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * AbstractBlock.java * ------------------ * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.block; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.ui.Size2D; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; import org.jfree.chart.util.ShapeUtils; import org.jfree.data.Range; /** * A convenience class for creating new classes that implement * the {@link Block} interface. */ public class AbstractBlock implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 7689852412141274563L; /** The id for the block. */ private String id; /** The margin around the outside of the block. */ private RectangleInsets margin; /** The frame (or border) for the block. */ private BlockFrame frame; /** The padding between the block content and the border. */ private RectangleInsets padding; /** * The natural width of the block (may be overridden if there are * constraints in sizing). */ private double width; /** * The natural height of the block (may be overridden if there are * constraints in sizing). */ private double height; /** * The current bounds for the block (position of the block in Java2D space). */ private transient Rectangle2D bounds; /** * Creates a new block. */ protected AbstractBlock() { this.id = null; this.width = 0.0; this.height = 0.0; this.bounds = new Rectangle2D.Float(); this.margin = RectangleInsets.ZERO_INSETS; this.frame = BlockBorder.NONE; this.padding = RectangleInsets.ZERO_INSETS; } /** * Returns the id. * * @return The id (possibly {@code null}). * * @see #setID(String) */ public String getID() { return this.id; } /** * Sets the id for the block. * * @param id the id ({@code null} permitted). * * @see #getID() */ public void setID(String id) { this.id = id; } /** * Returns the natural width of the block, if this is known in advance. * The actual width of the block may be overridden if layout constraints * make this necessary. * * @return The width. * * @see #setWidth(double) */ public double getWidth() { return this.width; } /** * Sets the natural width of the block, if this is known in advance. * * @param width the width (in Java2D units) * * @see #getWidth() */ public void setWidth(double width) { this.width = width; } /** * Returns the natural height of the block, if this is known in advance. * The actual height of the block may be overridden if layout constraints * make this necessary. * * @return The height. * * @see #setHeight(double) */ public double getHeight() { return this.height; } /** * Sets the natural width of the block, if this is known in advance. * * @param height the width (in Java2D units) * * @see #getHeight() */ public void setHeight(double height) { this.height = height; } /** * Returns the margin. * * @return The margin (never {@code null}). * * @see #getMargin() */ public RectangleInsets getMargin() { return this.margin; } /** * Sets the margin (use {@link RectangleInsets#ZERO_INSETS} for no * padding). * * @param margin the margin ({@code null} not permitted). * * @see #getMargin() */ public void setMargin(RectangleInsets margin) { Args.nullNotPermitted(margin, "margin"); this.margin = margin; } /** * Sets the margin. * * @param top the top margin. * @param left the left margin. * @param bottom the bottom margin. * @param right the right margin. * * @see #getMargin() */ public void setMargin(double top, double left, double bottom, double right) { setMargin(new RectangleInsets(top, left, bottom, right)); } /** * Sets a black border with the specified line widths. * * @param top the top border line width. * @param left the left border line width. * @param bottom the bottom border line width. * @param right the right border line width. */ public void setBorder(double top, double left, double bottom, double right) { setFrame(new BlockBorder(top, left, bottom, right)); } /** * Returns the current frame (border). * * @return The frame. * * @see #setFrame(BlockFrame) */ public BlockFrame getFrame() { return this.frame; } /** * Sets the frame (or border). * * @param frame the frame ({@code null} not permitted). * * @see #getFrame() */ public void setFrame(BlockFrame frame) { Args.nullNotPermitted(frame, "frame"); this.frame = frame; } /** * Returns the padding. * * @return The padding (never {@code null}). * * @see #setPadding(RectangleInsets) */ public RectangleInsets getPadding() { return this.padding; } /** * Sets the padding (use {@link RectangleInsets#ZERO_INSETS} for no * padding). * * @param padding the padding ({@code null} not permitted). * * @see #getPadding() */ public void setPadding(RectangleInsets padding) { Args.nullNotPermitted(padding, "padding"); this.padding = padding; } /** * Sets the padding. * * @param top the top padding. * @param left the left padding. * @param bottom the bottom padding. * @param right the right padding. */ public void setPadding(double top, double left, double bottom, double right) { setPadding(new RectangleInsets(top, left, bottom, right)); } /** * Returns the x-offset for the content within the block. * * @return The x-offset. * * @see #getContentYOffset() */ public double getContentXOffset() { return this.margin.getLeft() + this.frame.getInsets().getLeft() + this.padding.getLeft(); } /** * Returns the y-offset for the content within the block. * * @return The y-offset. * * @see #getContentXOffset() */ public double getContentYOffset() { return this.margin.getTop() + this.frame.getInsets().getTop() + this.padding.getTop(); } /** * Arranges the contents of the block, with no constraints, and returns * the block size. * * @param g2 the graphics device. * * @return The block size (in Java2D units, never {@code null}). */ public Size2D arrange(Graphics2D g2) { return arrange(g2, RectangleConstraint.NONE); } /** * Arranges the contents of the block, within the given constraints, and * returns the block size. * * @param g2 the graphics device. * @param constraint the constraint ({@code null} not permitted). * * @return The block size (in Java2D units, never {@code null}). */ public Size2D arrange(Graphics2D g2, RectangleConstraint constraint) { Size2D base = new Size2D(getWidth(), getHeight()); return constraint.calculateConstrainedSize(base); } /** * Returns the current bounds of the block. * * @return The bounds. * * @see #setBounds(Rectangle2D) */ public Rectangle2D getBounds() { return this.bounds; } /** * Sets the bounds of the block. * * @param bounds the bounds ({@code null} not permitted). * * @see #getBounds() */ public void setBounds(Rectangle2D bounds) { Args.nullNotPermitted(bounds, "bounds"); this.bounds = bounds; } /** * Calculate the width available for content after subtracting * the margin, border and padding space from the specified fixed * width. * * @param fixedWidth the fixed width. * * @return The available space. * * @see #trimToContentHeight(double) */ protected double trimToContentWidth(double fixedWidth) { double result = this.margin.trimWidth(fixedWidth); result = this.frame.getInsets().trimWidth(result); result = this.padding.trimWidth(result); return Math.max(result, 0.0); } /** * Calculate the height available for content after subtracting * the margin, border and padding space from the specified fixed * height. * * @param fixedHeight the fixed height. * * @return The available space. * * @see #trimToContentWidth(double) */ protected double trimToContentHeight(double fixedHeight) { double result = this.margin.trimHeight(fixedHeight); result = this.frame.getInsets().trimHeight(result); result = this.padding.trimHeight(result); return Math.max(result, 0.0); } /** * Returns a constraint for the content of this block that will result in * the bounds of the block matching the specified constraint. * * @param c the outer constraint ({@code null} not permitted). * * @return The content constraint. */ protected RectangleConstraint toContentConstraint(RectangleConstraint c) { Args.nullNotPermitted(c, "c"); if (c.equals(RectangleConstraint.NONE)) { return c; } double w = c.getWidth(); Range wr = c.getWidthRange(); double h = c.getHeight(); Range hr = c.getHeightRange(); double ww = trimToContentWidth(w); double hh = trimToContentHeight(h); Range wwr = trimToContentWidth(wr); Range hhr = trimToContentHeight(hr); return new RectangleConstraint(ww, wwr, c.getWidthConstraintType(), hh, hhr, c.getHeightConstraintType()); } private Range trimToContentWidth(Range r) { if (r == null) { return null; } double lowerBound = 0.0; double upperBound = Double.POSITIVE_INFINITY; if (r.getLowerBound() > 0.0) { lowerBound = trimToContentWidth(r.getLowerBound()); } if (r.getUpperBound() < Double.POSITIVE_INFINITY) { upperBound = trimToContentWidth(r.getUpperBound()); } return new Range(lowerBound, upperBound); } private Range trimToContentHeight(Range r) { if (r == null) { return null; } double lowerBound = 0.0; double upperBound = Double.POSITIVE_INFINITY; if (r.getLowerBound() > 0.0) { lowerBound = trimToContentHeight(r.getLowerBound()); } if (r.getUpperBound() < Double.POSITIVE_INFINITY) { upperBound = trimToContentHeight(r.getUpperBound()); } return new Range(lowerBound, upperBound); } /** * Adds the margin, border and padding to the specified content width. * * @param contentWidth the content width. * * @return The adjusted width. */ protected double calculateTotalWidth(double contentWidth) { double result = contentWidth; result = this.padding.extendWidth(result); result = this.frame.getInsets().extendWidth(result); result = this.margin.extendWidth(result); return result; } /** * Adds the margin, border and padding to the specified content height. * * @param contentHeight the content height. * * @return The adjusted height. */ protected double calculateTotalHeight(double contentHeight) { double result = contentHeight; result = this.padding.extendHeight(result); result = this.frame.getInsets().extendHeight(result); result = this.margin.extendHeight(result); return result; } /** * Reduces the specified area by the amount of space consumed * by the margin. * * @param area the area ({@code null} not permitted). * * @return The trimmed area. */ protected Rectangle2D trimMargin(Rectangle2D area) { // defer argument checking... this.margin.trim(area); return area; } /** * Reduces the specified area by the amount of space consumed * by the border. * * @param area the area ({@code null} not permitted). * * @return The trimmed area. */ protected Rectangle2D trimBorder(Rectangle2D area) { // defer argument checking... this.frame.getInsets().trim(area); return area; } /** * Reduces the specified area by the amount of space consumed * by the padding. * * @param area the area ({@code null} not permitted). * * @return The trimmed area. */ protected Rectangle2D trimPadding(Rectangle2D area) { // defer argument checking... this.padding.trim(area); return area; } /** * Draws the border around the perimeter of the specified area. * * @param g2 the graphics device. * @param area the area. */ protected void drawBorder(Graphics2D g2, Rectangle2D area) { this.frame.draw(g2, area); } /** * Tests this block for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof AbstractBlock)) { return false; } AbstractBlock that = (AbstractBlock) obj; if (!Objects.equals(this.id, that.id)) { return false; } if (!Objects.equals(this.frame, that.frame)) { return false; } if (!Objects.equals(this.bounds, that.bounds)) { return false; } if (!Objects.equals(this.margin, that.margin)) { return false; } if (!Objects.equals(this.padding, that.padding)) { return false; } if (Double.doubleToLongBits(this.height) != Double.doubleToLongBits(that.height)) { return false; } if (Double.doubleToLongBits(this.width) != Double.doubleToLongBits(that.width)) { return false; } // fix the "equals not symmetric" problem if (!that.canEqual(this)) { return false; } return true; } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof AbstractBlock); } @Override public int hashCode() { int hash = 3; hash = 89 * hash + Objects.hashCode(this.id); hash = 89 * hash + Objects.hashCode(this.margin); hash = 89 * hash + Objects.hashCode(this.frame); hash = 89 * hash + Objects.hashCode(this.padding); hash = 89 * hash + Objects.hashCode(this.bounds); hash = 89 * hash + (int) (Double.doubleToLongBits(this.width) ^ (Double.doubleToLongBits(this.width) >>> 32)); hash = 89 * hash + (int) (Double.doubleToLongBits(this.height) ^ (Double.doubleToLongBits(this.height) >>> 32)); return hash; } /** * Returns a clone of this block. * * @return A clone. * * @throws CloneNotSupportedException if there is a problem creating the * clone. */ @Override public Object clone() throws CloneNotSupportedException { AbstractBlock clone = (AbstractBlock) super.clone(); clone.bounds = (Rectangle2D) ShapeUtils.clone(this.bounds); if (this.frame instanceof PublicCloneable) { PublicCloneable pc = (PublicCloneable) this.frame; clone.frame = (BlockFrame) pc.clone(); } return clone; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeShape(this.bounds, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.bounds = (Rectangle2D) SerialUtils.readShape(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/block/Arrangement.java000066400000000000000000000052751463604235500272670ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * Arrangement.java * ---------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.block; import java.awt.Graphics2D; import org.jfree.chart.ui.Size2D; /** * An object that is responsible for arranging a collection of {@link Block}s * within a {@link BlockContainer}. */ public interface Arrangement { /** * Adds a block and a key which can be used to determine the position of * the block in the arrangement. This method is called by the container * (you don't need to call this method directly) and gives the arrangement * an opportunity to record the details if they are required. * * @param block the block. * @param key the key ({@code null} permitted). */ void add(Block block, Object key); /** * Arranges the blocks within the specified container, subject to the given * constraint. * * @param container the container ({@code null} not permitted). * @param g2 the graphics device. * @param constraint the constraint. * * @return The container size after the arrangement. */ Size2D arrange(BlockContainer container, Graphics2D g2, RectangleConstraint constraint); /** * Clears any cached layout information retained by the arrangement. */ void clear(); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/block/Block.java000066400000000000000000000066251463604235500260560ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------- * Block.java * ---------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.block; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import org.jfree.chart.ui.Drawable; import org.jfree.chart.ui.Size2D; /** * A block is an arbitrary item that can be drawn (in Java2D space) within a * rectangular area, has a preferred size, and can be arranged by an * {@link Arrangement} manager. */ public interface Block extends Drawable { /** * Returns an ID for the block. * * @return An ID. */ String getID(); /** * Sets the ID for the block. * * @param id the ID. */ void setID(String id); /** * Arranges the contents of the block, with no constraints, and returns * the block size. * * @param g2 the graphics device. * * @return The size of the block. */ Size2D arrange(Graphics2D g2); /** * Arranges the contents of the block, within the given constraints, and * returns the block size. * * @param g2 the graphics device. * @param constraint the constraint ({@code null} not permitted). * * @return The block size (in Java2D units, never {@code null}). */ Size2D arrange(Graphics2D g2, RectangleConstraint constraint); /** * Returns the current bounds of the block. * * @return The bounds. */ Rectangle2D getBounds(); /** * Sets the bounds of the block. * * @param bounds the bounds. */ void setBounds(Rectangle2D bounds); /** * Draws the block within the specified area. Refer to the documentation * for the implementing class for information about the {@code params} * and return value supported. * * @param g2 the graphics device. * @param area the area. * @param params optional parameters ({@code null} permitted). * * @return An optional return value (possibly {@code null}). */ Object draw(Graphics2D g2, Rectangle2D area, Object params); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/block/BlockBorder.java000066400000000000000000000170761463604235500272160ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * BlockBorder.java * ---------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.block; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.HashUtils; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.SerialUtils; /** * A border for a block. This class is immutable. */ public class BlockBorder implements BlockFrame, Serializable { /** For serialization. */ private static final long serialVersionUID = 4961579220410228283L; /** An empty border. */ public static final BlockBorder NONE = new BlockBorder( RectangleInsets.ZERO_INSETS, Color.WHITE); /** The space reserved for the border. */ private final RectangleInsets insets; /** The border color. */ private transient Paint paint; /** * Creates a default border. */ public BlockBorder() { this(Color.BLACK); } /** * Creates a new border with the specified color. * * @param paint the color ({@code null} not permitted). */ public BlockBorder(Paint paint) { this(new RectangleInsets(1, 1, 1, 1), paint); } /** * Creates a new border with the specified line widths (in black). * * @param top the width of the top border. * @param left the width of the left border. * @param bottom the width of the bottom border. * @param right the width of the right border. */ public BlockBorder(double top, double left, double bottom, double right) { this(new RectangleInsets(top, left, bottom, right), Color.BLACK); } /** * Creates a new border with the specified line widths (in black). * * @param top the width of the top border. * @param left the width of the left border. * @param bottom the width of the bottom border. * @param right the width of the right border. * @param paint the border paint ({@code null} not permitted). */ public BlockBorder(double top, double left, double bottom, double right, Paint paint) { this(new RectangleInsets(top, left, bottom, right), paint); } /** * Creates a new border. * * @param insets the border insets ({@code null} not permitted). * @param paint the paint ({@code null} not permitted). */ public BlockBorder(RectangleInsets insets, Paint paint) { Args.nullNotPermitted(insets, "insets"); Args.nullNotPermitted(paint, "paint"); this.insets = insets; this.paint = paint; } /** * Returns the space reserved for the border. * * @return The space (never {@code null}). */ @Override public RectangleInsets getInsets() { return this.insets; } /** * Returns the paint used to draw the border. * * @return The paint (never {@code null}). */ public Paint getPaint() { return this.paint; } /** * Draws the border by filling in the reserved space. * * @param g2 the graphics device. * @param area the area. */ @Override public void draw(Graphics2D g2, Rectangle2D area) { // this default implementation will just fill the available // border space with a single color double t = this.insets.calculateTopInset(area.getHeight()); double b = this.insets.calculateBottomInset(area.getHeight()); double l = this.insets.calculateLeftInset(area.getWidth()); double r = this.insets.calculateRightInset(area.getWidth()); double x = area.getX(); double y = area.getY(); double w = area.getWidth(); double h = area.getHeight(); g2.setPaint(this.paint); Rectangle2D rect = new Rectangle2D.Double(); if (t > 0.0) { rect.setRect(x, y, w, t); g2.fill(rect); } if (b > 0.0) { rect.setRect(x, y + h - b, w, b); g2.fill(rect); } if (l > 0.0) { rect.setRect(x, y, l, h); g2.fill(rect); } if (r > 0.0) { rect.setRect(x + w - r, y, r, h); g2.fill(rect); } } /** * Tests this border for equality with an arbitrary instance. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof BlockBorder)) { return false; } BlockBorder that = (BlockBorder) obj; if (!Objects.equals(this.insets, that.insets)) { return false; } if (!PaintUtils.equal(this.paint, that.paint)) { return false; } return true; } @Override public int hashCode() { int hash = 5; hash = 37 * hash + Objects.hashCode(this.insets); hash = 37 * hash + HashUtils.hashCodeForPaint(this.paint); return hash; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.paint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.paint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/block/BlockContainer.java000066400000000000000000000226201463604235500277120ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * BlockContainer.java * ------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.block; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Objects; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.entity.StandardEntityCollection; import org.jfree.chart.ui.Size2D; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; /** * A container for a collection of {@link Block} objects. The container uses * an {@link Arrangement} object to handle the position of each block. */ public class BlockContainer extends AbstractBlock implements Block, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 8199508075695195293L; /** The blocks within the container. */ private final List blocks; /** The object responsible for laying out the blocks. */ private Arrangement arrangement; /** * Creates a new instance with default settings. */ public BlockContainer() { this(new BorderArrangement()); } /** * Creates a new instance with the specified arrangement. * * @param arrangement the arrangement manager ({@code null} not * permitted). */ public BlockContainer(Arrangement arrangement) { Args.nullNotPermitted(arrangement, "arrangement"); this.arrangement = arrangement; this.blocks = new ArrayList(); } /** * Returns the arrangement (layout) manager for the container. * * @return The arrangement manager (never {@code null}). */ public Arrangement getArrangement() { return this.arrangement; } /** * Sets the arrangement (layout) manager. * * @param arrangement the arrangement ({@code null} not permitted). */ public void setArrangement(Arrangement arrangement) { Args.nullNotPermitted(arrangement, "arrangement"); this.arrangement = arrangement; } /** * Returns {@code true} if there are no blocks in the container, and * {@code false} otherwise. * * @return A boolean. */ public boolean isEmpty() { return this.blocks.isEmpty(); } /** * Returns an unmodifiable list of the {@link Block} objects managed by * this arrangement. * * @return A list of blocks. */ public List getBlocks() { return Collections.unmodifiableList(this.blocks); } /** * Adds a block to the container. * * @param block the block ({@code null} permitted). */ public void add(Block block) { add(block, null); } /** * Adds a block to the container. * * @param block the block ({@code null} permitted). * @param key the key ({@code null} permitted). */ public void add(Block block, Object key) { this.blocks.add(block); this.arrangement.add(block, key); } /** * Clears all the blocks from the container. */ public void clear() { this.blocks.clear(); this.arrangement.clear(); } /** * Arranges the contents of the block, within the given constraints, and * returns the block size. * * @param g2 the graphics device. * @param constraint the constraint ({@code null} not permitted). * * @return The block size (in Java2D units, never {@code null}). */ @Override public Size2D arrange(Graphics2D g2, RectangleConstraint constraint) { return this.arrangement.arrange(this, g2, constraint); } /** * Draws the container and all the blocks within it. * * @param g2 the graphics device. * @param area the area. */ @Override public void draw(Graphics2D g2, Rectangle2D area) { draw(g2, area, null); } /** * Draws the block within the specified area. * * @param g2 the graphics device. * @param area the area. * @param params passed on to blocks within the container * ({@code null} permitted). * * @return An instance of {@link EntityBlockResult}, or {@code null}. */ @Override public Object draw(Graphics2D g2, Rectangle2D area, Object params) { // check if we need to collect chart entities from the container EntityBlockParams ebp; StandardEntityCollection sec = null; if (params instanceof EntityBlockParams) { ebp = (EntityBlockParams) params; if (ebp.getGenerateEntities()) { sec = new StandardEntityCollection(); } } Rectangle2D contentArea = (Rectangle2D) area.clone(); contentArea = trimMargin(contentArea); drawBorder(g2, contentArea); contentArea = trimBorder(contentArea); contentArea = trimPadding(contentArea); Iterator iterator = this.blocks.iterator(); while (iterator.hasNext()) { Block block = (Block) iterator.next(); Rectangle2D bounds = block.getBounds(); Rectangle2D drawArea = new Rectangle2D.Double(bounds.getX() + area.getX(), bounds.getY() + area.getY(), bounds.getWidth(), bounds.getHeight()); Object r = block.draw(g2, drawArea, params); if (sec != null) { if (r instanceof EntityBlockResult) { EntityBlockResult ebr = (EntityBlockResult) r; EntityCollection ec = ebr.getEntityCollection(); sec.addAll(ec); } } } BlockResult result = null; if (sec != null) { result = new BlockResult(); result.setEntityCollection(sec); } return result; } /** * Tests this container for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof BlockContainer)) { return false; } BlockContainer that = (BlockContainer) obj; // fix the "equals not symmetric" problem if (!that.canEqual(this)) { return false; } // compare fields in this class if (!Objects.equals(this.arrangement, that.arrangement)) { return false; } if (!Objects.equals(this.blocks, that.blocks)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // Solves Problem: equals not symmetric return (other instanceof BlockContainer); } @Override public int hashCode() { int hash = super.hashCode(); // equals calls superclass function, so hashCode must also hash = 79 * hash + Objects.hashCode(this.blocks); hash = 79 * hash + Objects.hashCode(this.arrangement); return hash; } /** * Returns a clone of the container. * * @return A clone. * * @throws CloneNotSupportedException if there is a problem cloning. */ @Override public Object clone() throws CloneNotSupportedException { BlockContainer clone = (BlockContainer) super.clone(); // TODO : complete this return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/block/BlockFrame.java000066400000000000000000000043171463604235500270250ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * BlockFrame.java * --------------- * (C) Copyright 2007-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.block; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.util.PublicCloneable; /** * A block frame is a type of border that can be drawn around the outside of * any {@link AbstractBlock}. Classes that implement this interface should * implement {@link PublicCloneable} OR be immutable. */ public interface BlockFrame { /** * Returns the space reserved for the border. * * @return The space (never {@code null}). */ RectangleInsets getInsets(); /** * Draws the border by filling in the reserved space (in black). * * @param g2 the graphics device. * @param area the area. */ void draw(Graphics2D g2, Rectangle2D area); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/block/BlockParams.java000066400000000000000000000077541463604235500272260ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * BlockParams.java * ---------------- * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.block; /** * A standard parameter object that can be passed to the draw() method defined * by the {@link Block} class. */ public class BlockParams implements EntityBlockParams { /** * A flag that controls whether or not the block should generate and * return chart entities for the items it draws. */ private boolean generateEntities; /** * The x-translation (used to enable chart entities to use global * coordinates rather than coordinates that are local to the container * they are within). */ private double translateX; /** * The y-translation (used to enable chart entities to use global * coordinates rather than coordinates that are local to the container * they are within). */ private double translateY; /** * Creates a new instance. */ public BlockParams() { this.translateX = 0.0; this.translateY = 0.0; this.generateEntities = false; } /** * Returns the flag that controls whether or not chart entities are * generated. * * @return A boolean. */ @Override public boolean getGenerateEntities() { return this.generateEntities; } /** * Sets the flag that controls whether or not chart entities are generated. * * @param generate the flag. */ public void setGenerateEntities(boolean generate) { this.generateEntities = generate; } /** * Returns the translation required to convert local x-coordinates back to * the coordinate space of the container. * * @return The x-translation amount. */ public double getTranslateX() { return this.translateX; } /** * Sets the translation required to convert local x-coordinates into the * coordinate space of the container. * * @param x the x-translation amount. */ public void setTranslateX(double x) { this.translateX = x; } /** * Returns the translation required to convert local y-coordinates back to * the coordinate space of the container. * * @return The y-translation amount. */ public double getTranslateY() { return this.translateY; } /** * Sets the translation required to convert local y-coordinates into the * coordinate space of the container. * * @param y the y-translation amount. */ public void setTranslateY(double y) { this.translateY = y; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/block/BlockResult.java000066400000000000000000000044171463604235500272520ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * BlockResult.java * ---------------- * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.block; import org.jfree.chart.entity.EntityCollection; /** * Used to return results from the draw() method in the {@link Block} * class. */ public class BlockResult implements EntityBlockResult { /** The entities from the block. */ private EntityCollection entities; /** * Creates a new result instance. */ public BlockResult() { this.entities = null; } /** * Returns the collection of entities from the block. * * @return The entities. */ @Override public EntityCollection getEntityCollection() { return this.entities; } /** * Sets the entities for the block. * * @param entities the entities. */ public void setEntityCollection(EntityCollection entities) { this.entities = entities; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/block/BorderArrangement.java000066400000000000000000000477171463604235500304340ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * BorderArrangement.java * ---------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (define hashCode); * */ package org.jfree.chart.block; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.Size2D; import org.jfree.data.Range; /** * An arrangement manager that lays out blocks in a similar way to * Swing's BorderLayout class. */ public class BorderArrangement implements Arrangement, Serializable { /** For serialization. */ private static final long serialVersionUID = 506071142274883745L; /** The block (if any) at the center of the layout. */ private Block centerBlock; /** The block (if any) at the top of the layout. */ private Block topBlock; /** The block (if any) at the bottom of the layout. */ private Block bottomBlock; /** The block (if any) at the left of the layout. */ private Block leftBlock; /** The block (if any) at the right of the layout. */ private Block rightBlock; /** * Creates a new instance. */ public BorderArrangement() { } /** * Adds a block to the arrangement manager at the specified edge. * If the key is not an instance of {@link RectangleEdge} the block will * be added in the center. * * @param block the block ({@code null} permitted). * @param key the edge (an instance of {@link RectangleEdge}) or * {@code null} for the center block. */ @Override public void add(Block block, Object key) { if (!(key instanceof RectangleEdge)) { // catches null also this.centerBlock = block; } else { RectangleEdge edge = (RectangleEdge) key; if (edge == RectangleEdge.TOP) { this.topBlock = block; } else if (edge == RectangleEdge.BOTTOM) { this.bottomBlock = block; } else if (edge == RectangleEdge.LEFT) { this.leftBlock = block; } else if (edge == RectangleEdge.RIGHT) { this.rightBlock = block; } } } /** * Arranges the items in the specified container, subject to the given * constraint. * * @param container the container. * @param g2 the graphics device. * @param constraint the constraint. * * @return The block size. */ @Override public Size2D arrange(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { RectangleConstraint contentConstraint = container.toContentConstraint(constraint); Size2D contentSize = null; LengthConstraintType w = contentConstraint.getWidthConstraintType(); LengthConstraintType h = contentConstraint.getHeightConstraintType(); if (w == LengthConstraintType.NONE) { if (h == LengthConstraintType.NONE) { contentSize = arrangeNN(container, g2); } else if (h == LengthConstraintType.FIXED) { throw new RuntimeException("Not implemented."); } else if (h == LengthConstraintType.RANGE) { throw new RuntimeException("Not implemented."); } } else if (w == LengthConstraintType.FIXED) { if (h == LengthConstraintType.NONE) { contentSize = arrangeFN(container, g2, constraint.getWidth()); } else if (h == LengthConstraintType.FIXED) { contentSize = arrangeFF(container, g2, constraint); } else if (h == LengthConstraintType.RANGE) { contentSize = arrangeFR(container, g2, constraint); } } else if (w == LengthConstraintType.RANGE) { if (h == LengthConstraintType.NONE) { throw new RuntimeException("Not implemented."); } else if (h == LengthConstraintType.FIXED) { throw new RuntimeException("Not implemented."); } else if (h == LengthConstraintType.RANGE) { contentSize = arrangeRR(container, constraint.getWidthRange(), constraint.getHeightRange(), g2); } } assert contentSize != null; return new Size2D(container.calculateTotalWidth(contentSize.getWidth()), container.calculateTotalHeight(contentSize.getHeight())); } /** * Performs an arrangement without constraints. * * @param container the container. * @param g2 the graphics device. * * @return The container size after the arrangement. */ protected Size2D arrangeNN(BlockContainer container, Graphics2D g2) { double[] w = new double[5]; double[] h = new double[5]; if (this.topBlock != null) { Size2D size = this.topBlock.arrange(g2, RectangleConstraint.NONE); w[0] = size.width; h[0] = size.height; } if (this.bottomBlock != null) { Size2D size = this.bottomBlock.arrange(g2, RectangleConstraint.NONE); w[1] = size.width; h[1] = size.height; } if (this.leftBlock != null) { Size2D size = this.leftBlock.arrange(g2, RectangleConstraint.NONE); w[2] = size.width; h[2] = size.height; } if (this.rightBlock != null) { Size2D size = this.rightBlock.arrange(g2, RectangleConstraint.NONE); w[3] = size.width; h[3] = size.height; } h[2] = Math.max(h[2], h[3]); h[3] = h[2]; if (this.centerBlock != null) { Size2D size = this.centerBlock.arrange(g2, RectangleConstraint.NONE); w[4] = size.width; h[4] = size.height; } double width = Math.max(w[0], Math.max(w[1], w[2] + w[4] + w[3])); double centerHeight = Math.max(h[2], Math.max(h[3], h[4])); double height = h[0] + h[1] + centerHeight; if (this.topBlock != null) { this.topBlock.setBounds(new Rectangle2D.Double(0.0, 0.0, width, h[0])); } if (this.bottomBlock != null) { this.bottomBlock.setBounds(new Rectangle2D.Double(0.0, height - h[1], width, h[1])); } if (this.leftBlock != null) { this.leftBlock.setBounds(new Rectangle2D.Double(0.0, h[0], w[2], centerHeight)); } if (this.rightBlock != null) { this.rightBlock.setBounds(new Rectangle2D.Double(width - w[3], h[0], w[3], centerHeight)); } if (this.centerBlock != null) { this.centerBlock.setBounds(new Rectangle2D.Double(w[2], h[0], width - w[2] - w[3], centerHeight)); } return new Size2D(width, height); } /** * Performs an arrangement with a fixed width and a range for the height. * * @param container the container. * @param g2 the graphics device. * @param constraint the constraint. * * @return The container size after the arrangement. */ protected Size2D arrangeFR(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { Size2D size1 = arrangeFN(container, g2, constraint.getWidth()); if (constraint.getHeightRange().contains(size1.getHeight())) { return size1; } else { double h = constraint.getHeightRange().constrain(size1.getHeight()); RectangleConstraint c2 = constraint.toFixedHeight(h); return arrange(container, g2, c2); } } /** * Arranges the container width a fixed width and no constraint on the * height. * * @param container the container. * @param g2 the graphics device. * @param width the fixed width. * * @return The container size after arranging the contents. */ protected Size2D arrangeFN(BlockContainer container, Graphics2D g2, double width) { double[] w = new double[5]; double[] h = new double[5]; RectangleConstraint c1 = new RectangleConstraint(width, null, LengthConstraintType.FIXED, 0.0, null, LengthConstraintType.NONE); if (this.topBlock != null) { Size2D size = this.topBlock.arrange(g2, c1); w[0] = size.width; h[0] = size.height; } if (this.bottomBlock != null) { Size2D size = this.bottomBlock.arrange(g2, c1); w[1] = size.width; h[1] = size.height; } RectangleConstraint c2 = new RectangleConstraint(0.0, new Range(0.0, width), LengthConstraintType.RANGE, 0.0, null, LengthConstraintType.NONE); if (this.leftBlock != null) { Size2D size = this.leftBlock.arrange(g2, c2); w[2] = size.width; h[2] = size.height; } if (this.rightBlock != null) { double maxW = Math.max(width - w[2], 0.0); RectangleConstraint c3 = new RectangleConstraint(0.0, new Range(Math.min(w[2], maxW), maxW), LengthConstraintType.RANGE, 0.0, null, LengthConstraintType.NONE); Size2D size = this.rightBlock.arrange(g2, c3); w[3] = size.width; h[3] = size.height; } h[2] = Math.max(h[2], h[3]); h[3] = h[2]; if (this.centerBlock != null) { RectangleConstraint c4 = new RectangleConstraint(width - w[2] - w[3], null, LengthConstraintType.FIXED, 0.0, null, LengthConstraintType.NONE); Size2D size = this.centerBlock.arrange(g2, c4); w[4] = size.width; h[4] = size.height; } double height = h[0] + h[1] + Math.max(h[2], Math.max(h[3], h[4])); return arrange(container, g2, new RectangleConstraint(width, height)); } /** * Performs an arrangement with range constraints on both the vertical * and horizontal sides. * * @param container the container. * @param widthRange the allowable range for the container width. * @param heightRange the allowable range for the container height. * @param g2 the graphics device. * * @return The container size. */ protected Size2D arrangeRR(BlockContainer container, Range widthRange, Range heightRange, Graphics2D g2) { double[] w = new double[5]; double[] h = new double[5]; if (this.topBlock != null) { RectangleConstraint c1 = new RectangleConstraint(widthRange, heightRange); Size2D size = this.topBlock.arrange(g2, c1); w[0] = size.width; h[0] = size.height; } if (this.bottomBlock != null) { Range heightRange2 = Range.shift(heightRange, -h[0], false); RectangleConstraint c2 = new RectangleConstraint(widthRange, heightRange2); Size2D size = this.bottomBlock.arrange(g2, c2); w[1] = size.width; h[1] = size.height; } Range heightRange3 = Range.shift(heightRange, -(h[0] + h[1])); if (this.leftBlock != null) { RectangleConstraint c3 = new RectangleConstraint(widthRange, heightRange3); Size2D size = this.leftBlock.arrange(g2, c3); w[2] = size.width; h[2] = size.height; } Range widthRange2 = Range.shift(widthRange, -w[2], false); if (this.rightBlock != null) { RectangleConstraint c4 = new RectangleConstraint(widthRange2, heightRange3); Size2D size = this.rightBlock.arrange(g2, c4); w[3] = size.width; h[3] = size.height; } h[2] = Math.max(h[2], h[3]); h[3] = h[2]; Range widthRange3 = Range.shift(widthRange, -(w[2] + w[3]), false); if (this.centerBlock != null) { RectangleConstraint c5 = new RectangleConstraint(widthRange3, heightRange3); Size2D size = this.centerBlock.arrange(g2, c5); w[4] = size.width; h[4] = size.height; } double width = Math.max(w[0], Math.max(w[1], w[2] + w[4] + w[3])); double height = h[0] + h[1] + Math.max(h[2], Math.max(h[3], h[4])); if (this.topBlock != null) { this.topBlock.setBounds(new Rectangle2D.Double(0.0, 0.0, width, h[0])); } if (this.bottomBlock != null) { this.bottomBlock.setBounds(new Rectangle2D.Double(0.0, height - h[1], width, h[1])); } if (this.leftBlock != null) { this.leftBlock.setBounds(new Rectangle2D.Double(0.0, h[0], w[2], h[2])); } if (this.rightBlock != null) { this.rightBlock.setBounds(new Rectangle2D.Double(width - w[3], h[0], w[3], h[3])); } if (this.centerBlock != null) { this.centerBlock.setBounds(new Rectangle2D.Double(w[2], h[0], width - w[2] - w[3], height - h[0] - h[1])); } return new Size2D(width, height); } /** * Arranges the items within a container. * * @param container the container. * @param constraint the constraint. * @param g2 the graphics device. * * @return The container size after the arrangement. */ protected Size2D arrangeFF(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { double[] w = new double[5]; double[] h = new double[5]; w[0] = constraint.getWidth(); if (this.topBlock != null) { RectangleConstraint c1 = new RectangleConstraint(w[0], null, LengthConstraintType.FIXED, 0.0, new Range(0.0, constraint.getHeight()), LengthConstraintType.RANGE); Size2D size = this.topBlock.arrange(g2, c1); h[0] = size.height; } w[1] = w[0]; if (this.bottomBlock != null) { RectangleConstraint c2 = new RectangleConstraint(w[0], null, LengthConstraintType.FIXED, 0.0, new Range(0.0, constraint.getHeight() - h[0]), LengthConstraintType.RANGE); Size2D size = this.bottomBlock.arrange(g2, c2); h[1] = size.height; } h[2] = constraint.getHeight() - h[1] - h[0]; if (this.leftBlock != null) { RectangleConstraint c3 = new RectangleConstraint(0.0, new Range(0.0, constraint.getWidth()), LengthConstraintType.RANGE, h[2], null, LengthConstraintType.FIXED); Size2D size = this.leftBlock.arrange(g2, c3); w[2] = size.width; } h[3] = h[2]; if (this.rightBlock != null) { RectangleConstraint c4 = new RectangleConstraint(0.0, new Range(0.0, Math.max(constraint.getWidth() - w[2], 0.0)), LengthConstraintType.RANGE, h[2], null, LengthConstraintType.FIXED); Size2D size = this.rightBlock.arrange(g2, c4); w[3] = size.width; } h[4] = h[2]; w[4] = constraint.getWidth() - w[3] - w[2]; RectangleConstraint c5 = new RectangleConstraint(w[4], h[4]); if (this.centerBlock != null) { this.centerBlock.arrange(g2, c5); } if (this.topBlock != null) { this.topBlock.setBounds(new Rectangle2D.Double(0.0, 0.0, w[0], h[0])); } if (this.bottomBlock != null) { this.bottomBlock.setBounds(new Rectangle2D.Double(0.0, h[0] + h[2], w[1], h[1])); } if (this.leftBlock != null) { this.leftBlock.setBounds(new Rectangle2D.Double(0.0, h[0], w[2], h[2])); } if (this.rightBlock != null) { this.rightBlock.setBounds(new Rectangle2D.Double(w[2] + w[4], h[0], w[3], h[3])); } if (this.centerBlock != null) { this.centerBlock.setBounds(new Rectangle2D.Double(w[2], h[0], w[4], h[4])); } return new Size2D(constraint.getWidth(), constraint.getHeight()); } /** * Clears the layout. */ @Override public void clear() { this.centerBlock = null; this.topBlock = null; this.bottomBlock = null; this.leftBlock = null; this.rightBlock = null; } /** * Tests this arrangement for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof BorderArrangement)) { return false; } BorderArrangement that = (BorderArrangement) obj; if (!Objects.equals(this.topBlock, that.topBlock)) { return false; } if (!Objects.equals(this.bottomBlock, that.bottomBlock)) { return false; } if (!Objects.equals(this.leftBlock, that.leftBlock)) { return false; } if (!Objects.equals(this.rightBlock, that.rightBlock)) { return false; } if (!Objects.equals(this.centerBlock, that.centerBlock)) { return false; } return true; } @Override public int hashCode() { int hash = 5; hash = 67 * hash + Objects.hashCode(this.centerBlock); hash = 67 * hash + Objects.hashCode(this.topBlock); hash = 67 * hash + Objects.hashCode(this.bottomBlock); hash = 67 * hash + Objects.hashCode(this.leftBlock); hash = 67 * hash + Objects.hashCode(this.rightBlock); return hash; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/block/CenterArrangement.java000066400000000000000000000264541463604235500304320ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * CenterArrangement.java * ---------------------- * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.block; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import java.util.List; import org.jfree.chart.ui.Size2D; /** * Arranges a block in the center of its container. This class is immutable. */ public class CenterArrangement implements Arrangement, Serializable { /** For serialization. */ private static final long serialVersionUID = -353308149220382047L; /** * Creates a new instance. */ public CenterArrangement() { } /** * Adds a block to be managed by this instance. This method is usually * called by the {@link BlockContainer}, you shouldn't need to call it * directly. * * @param block the block. * @param key a key that controls the position of the block. */ @Override public void add(Block block, Object key) { // since the flow layout is relatively straightforward, // no information needs to be recorded here } /** * Calculates and sets the bounds of all the items in the specified * container, subject to the given constraint. The {@code Graphics2D} * can be used by some items (particularly items containing text) to * calculate sizing parameters. * * @param container the container whose items are being arranged. * @param g2 the graphics device. * @param constraint the size constraint. * * @return The size of the container after arrangement of the contents. */ @Override public Size2D arrange(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { LengthConstraintType w = constraint.getWidthConstraintType(); LengthConstraintType h = constraint.getHeightConstraintType(); if (w == LengthConstraintType.NONE) { if (h == LengthConstraintType.NONE) { return arrangeNN(container, g2); } else if (h == LengthConstraintType.FIXED) { throw new RuntimeException("Not implemented."); } else if (h == LengthConstraintType.RANGE) { throw new RuntimeException("Not implemented."); } } else if (w == LengthConstraintType.FIXED) { if (h == LengthConstraintType.NONE) { return arrangeFN(container, g2, constraint); } else if (h == LengthConstraintType.FIXED) { throw new RuntimeException("Not implemented."); } else if (h == LengthConstraintType.RANGE) { throw new RuntimeException("Not implemented."); } } else if (w == LengthConstraintType.RANGE) { if (h == LengthConstraintType.NONE) { return arrangeRN(container, g2, constraint); } else if (h == LengthConstraintType.FIXED) { return arrangeRF(container, g2, constraint); } else if (h == LengthConstraintType.RANGE) { return arrangeRR(container, g2, constraint); } } throw new IllegalArgumentException("Unknown LengthConstraintType."); } /** * Arranges the blocks in the container with a fixed width and no height * constraint. * * @param container the container. * @param g2 the graphics device. * @param constraint the constraint. * * @return The size. */ protected Size2D arrangeFN(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { List blocks = container.getBlocks(); Block b = (Block) blocks.get(0); Size2D s = b.arrange(g2, RectangleConstraint.NONE); double width = constraint.getWidth(); Rectangle2D bounds = new Rectangle2D.Double((width - s.width) / 2.0, 0.0, s.width, s.height); b.setBounds(bounds); return new Size2D((width - s.width) / 2.0, s.height); } /** * Arranges the blocks in the container with a fixed with and a range * constraint on the height. * * @param container the container. * @param g2 the graphics device. * @param constraint the constraint. * * @return The size following the arrangement. */ protected Size2D arrangeFR(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { Size2D s = arrangeFN(container, g2, constraint); if (constraint.getHeightRange().contains(s.height)) { return s; } else { RectangleConstraint c = constraint.toFixedHeight( constraint.getHeightRange().constrain(s.getHeight())); return arrangeFF(container, g2, c); } } /** * Arranges the blocks in the container with the overall height and width * specified as fixed constraints. * * @param container the container. * @param g2 the graphics device. * @param constraint the constraint. * * @return The size following the arrangement. */ protected Size2D arrangeFF(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { // TODO: implement this properly return arrangeFN(container, g2, constraint); } /** * Arranges the blocks with the overall width and height to fit within * specified ranges. * * @param container the container. * @param g2 the graphics device. * @param constraint the constraint. * * @return The size after the arrangement. */ protected Size2D arrangeRR(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { // first arrange without constraints, and see if this fits within // the required ranges... Size2D s1 = arrangeNN(container, g2); if (constraint.getWidthRange().contains(s1.width)) { return s1; // TODO: we didn't check the height yet } else { RectangleConstraint c = constraint.toFixedWidth( constraint.getWidthRange().getUpperBound()); return arrangeFR(container, g2, c); } } /** * Arranges the blocks in the container with a range constraint on the * width and a fixed height. * * @param container the container. * @param g2 the graphics device. * @param constraint the constraint. * * @return The size following the arrangement. */ protected Size2D arrangeRF(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { Size2D s = arrangeNF(container, g2, constraint); if (constraint.getWidthRange().contains(s.width)) { return s; } else { RectangleConstraint c = constraint.toFixedWidth( constraint.getWidthRange().constrain(s.getWidth())); return arrangeFF(container, g2, c); } } /** * Arranges the block with a range constraint on the width, and no * constraint on the height. * * @param container the container. * @param g2 the graphics device. * @param constraint the constraint. * * @return The size following the arrangement. */ protected Size2D arrangeRN(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { // first arrange without constraints, then see if the width fits // within the required range...if not, call arrangeFN() at max width Size2D s1 = arrangeNN(container, g2); if (constraint.getWidthRange().contains(s1.width)) { return s1; } else { RectangleConstraint c = constraint.toFixedWidth( constraint.getWidthRange().getUpperBound()); return arrangeFN(container, g2, c); } } /** * Arranges the blocks without any constraints. This puts all blocks * into a single row. * * @param container the container. * @param g2 the graphics device. * * @return The size after the arrangement. */ protected Size2D arrangeNN(BlockContainer container, Graphics2D g2) { List blocks = container.getBlocks(); Block b = (Block) blocks.get(0); Size2D s = b.arrange(g2, RectangleConstraint.NONE); b.setBounds(new Rectangle2D.Double(0.0, 0.0, s.width, s.height)); return new Size2D(s.width, s.height); } /** * Arranges the blocks with no width constraint and a fixed height * constraint. This puts all blocks into a single row. * * @param container the container. * @param g2 the graphics device. * @param constraint the constraint. * * @return The size after the arrangement. */ protected Size2D arrangeNF(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { // TODO: for now we are ignoring the height constraint return arrangeNN(container, g2); } /** * Clears any cached information. */ @Override public void clear() { // no action required. } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof CenterArrangement)) { return false; } return true; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/block/ColorBlock.java000066400000000000000000000127751463604235500270600ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * ColorBlock.java * --------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.block; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import org.jfree.chart.HashUtils; import org.jfree.chart.ui.Size2D; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.SerialUtils; /** * A block that is filled with a single color. */ public class ColorBlock extends AbstractBlock implements Block { /** For serialization. */ static final long serialVersionUID = 3383866145634010865L; /** The paint. */ private transient Paint paint; /** * Creates a new block. * * @param paint the paint ({@code null} not permitted). * @param width the width. * @param height the height. */ public ColorBlock(Paint paint, double width, double height) { Args.nullNotPermitted(paint, "paint"); this.paint = paint; setWidth(width); setHeight(height); } /** * Returns the paint. * * @return The paint (never {@code null}). */ public Paint getPaint() { return this.paint; } /** * Arranges the contents of the block, within the given constraints, and * returns the block size. * * @param g2 the graphics device. * @param constraint the constraint ({@code null} not permitted). * * @return The block size (in Java2D units, never {@code null}). */ @Override public Size2D arrange(Graphics2D g2, RectangleConstraint constraint) { return new Size2D(calculateTotalWidth(getWidth()), calculateTotalHeight(getHeight())); } /** * Draws the block. * * @param g2 the graphics device. * @param area the area. */ @Override public void draw(Graphics2D g2, Rectangle2D area) { area = trimMargin(area); drawBorder(g2, area); area = trimBorder(area); area = trimPadding(area); g2.setPaint(this.paint); g2.fill(area); } /** * Draws the block within the specified area. * * @param g2 the graphics device. * @param area the area. * @param params ignored ({@code null} permitted). * * @return Always {@code null}. */ @Override public Object draw(Graphics2D g2, Rectangle2D area, Object params) { draw(g2, area); return null; } /** * Tests this block for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof ColorBlock)) { return false; } ColorBlock that = (ColorBlock) obj; if (!PaintUtils.equal(this.paint, that.paint)) { return false; } return super.equals(obj); } @Override public int hashCode() { int hash = super.hashCode(); hash = 79 * hash + HashUtils.hashCodeForPaint(this.paint); return hash; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.paint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.paint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/block/ColumnArrangement.java000066400000000000000000000350311463604235500304360ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * ColumnArrangement.java * ---------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.block; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Objects; import org.jfree.chart.ui.HorizontalAlignment; import org.jfree.chart.ui.Size2D; import org.jfree.chart.ui.VerticalAlignment; /** * Arranges blocks in a column layout. This class is immutable. */ public class ColumnArrangement implements Arrangement, Serializable { /** For serialization. */ private static final long serialVersionUID = -5315388482898581555L; /** The horizontal alignment of blocks. */ private HorizontalAlignment horizontalAlignment; /** The vertical alignment of blocks within each row. */ private VerticalAlignment verticalAlignment; /** The horizontal gap between columns. */ private double horizontalGap; /** The vertical gap between items in a column. */ private double verticalGap; /** * Creates a new instance. */ public ColumnArrangement() { } /** * Creates a new instance. * * @param hAlign the horizontal alignment (currently ignored). * @param vAlign the vertical alignment (currently ignored). * @param hGap the horizontal gap. * @param vGap the vertical gap. */ public ColumnArrangement(HorizontalAlignment hAlign, VerticalAlignment vAlign, double hGap, double vGap) { this.horizontalAlignment = hAlign; this.verticalAlignment = vAlign; this.horizontalGap = hGap; this.verticalGap = vGap; } /** * Adds a block to be managed by this instance. This method is usually * called by the {@link BlockContainer}, you shouldn't need to call it * directly. * * @param block the block. * @param key a key that controls the position of the block. */ @Override public void add(Block block, Object key) { // since the flow layout is relatively straightforward, no information // needs to be recorded here } /** * Calculates and sets the bounds of all the items in the specified * container, subject to the given constraint. The {@code Graphics2D} * can be used by some items (particularly items containing text) to * calculate sizing parameters. * * @param container the container whose items are being arranged. * @param g2 the graphics device. * @param constraint the size constraint. * * @return The size of the container after arrangement of the contents. */ @Override public Size2D arrange(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { LengthConstraintType w = constraint.getWidthConstraintType(); LengthConstraintType h = constraint.getHeightConstraintType(); if (w == LengthConstraintType.NONE) { if (h == LengthConstraintType.NONE) { return arrangeNN(container, g2); } else if (h == LengthConstraintType.FIXED) { throw new RuntimeException("Not implemented."); } else if (h == LengthConstraintType.RANGE) { throw new RuntimeException("Not implemented."); } } else if (w == LengthConstraintType.FIXED) { if (h == LengthConstraintType.NONE) { throw new RuntimeException("Not implemented."); } else if (h == LengthConstraintType.FIXED) { return arrangeFF(container, g2, constraint); } else if (h == LengthConstraintType.RANGE) { throw new RuntimeException("Not implemented."); } } else if (w == LengthConstraintType.RANGE) { if (h == LengthConstraintType.NONE) { throw new RuntimeException("Not implemented."); } else if (h == LengthConstraintType.FIXED) { return arrangeRF(container, g2, constraint); } else if (h == LengthConstraintType.RANGE) { return arrangeRR(container, g2, constraint); } } return new Size2D(); // TODO: complete this } /** * Calculates and sets the bounds of all the items in the specified * container, subject to the given constraint. The {@code Graphics2D} * can be used by some items (particularly items containing text) to * calculate sizing parameters. * * @param container the container whose items are being arranged. * @param g2 the graphics device. * @param constraint the size constraint. * * @return The container size after the arrangement. */ protected Size2D arrangeFF(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { // TODO: implement properly return arrangeNF(container, g2, constraint); } /** * Calculates and sets the bounds of all the items in the specified * container, subject to the given constraint. The {@code Graphics2D} * can be used by some items (particularly items containing text) to * calculate sizing parameters. * * @param container the container whose items are being arranged. * @param constraint the size constraint. * @param g2 the graphics device. * * @return The container size after the arrangement. */ protected Size2D arrangeNF(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { List blocks = container.getBlocks(); double height = constraint.getHeight(); if (height <= 0.0) { height = Double.POSITIVE_INFINITY; } double x = 0.0; double y = 0.0; double maxWidth = 0.0; List itemsInColumn = new ArrayList(); for (int i = 0; i < blocks.size(); i++) { Block block = (Block) blocks.get(i); Size2D size = block.arrange(g2, RectangleConstraint.NONE); if (y + size.height <= height) { itemsInColumn.add(block); block.setBounds( new Rectangle2D.Double(x, y, size.width, size.height) ); y = y + size.height + this.verticalGap; maxWidth = Math.max(maxWidth, size.width); } else { if (itemsInColumn.isEmpty()) { // place in this column (truncated) anyway block.setBounds( new Rectangle2D.Double( x, y, size.width, Math.min(size.height, height - y) ) ); y = 0.0; x = x + size.width + this.horizontalGap; } else { // start new column itemsInColumn.clear(); x = x + maxWidth + this.horizontalGap; y = 0.0; maxWidth = size.width; block.setBounds( new Rectangle2D.Double( x, y, size.width, Math.min(size.height, height) ) ); y = size.height + this.verticalGap; itemsInColumn.add(block); } } } return new Size2D(x + maxWidth, constraint.getHeight()); } /** * Arranges a container with range constraints for both the horizontal * and vertical. * * @param container the container. * @param g2 the graphics device. * @param constraint the constraint. * * @return The size of the container. */ protected Size2D arrangeRR(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { // first arrange without constraints, and see if this fits within // the required ranges... Size2D s1 = arrangeNN(container, g2); if (constraint.getHeightRange().contains(s1.height)) { return s1; // TODO: we didn't check the width yet } else { RectangleConstraint c = constraint.toFixedHeight( constraint.getHeightRange().getUpperBound() ); return arrangeRF(container, g2, c); } } /** * Arranges the blocks in the container using a fixed height and a * range for the width. * * @param container the container. * @param g2 the graphics device. * @param constraint the constraint. * * @return The size of the container after arrangement. */ protected Size2D arrangeRF(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { Size2D s = arrangeNF(container, g2, constraint); if (constraint.getWidthRange().contains(s.width)) { return s; } else { RectangleConstraint c = constraint.toFixedWidth( constraint.getWidthRange().constrain(s.getWidth()) ); return arrangeFF(container, g2, c); } } /** * Arranges the blocks without any constraints. This puts all blocks * into a single column. * * @param container the container. * @param g2 the graphics device. * * @return The size after the arrangement. */ protected Size2D arrangeNN(BlockContainer container, Graphics2D g2) { double y = 0.0; double height = 0.0; double maxWidth = 0.0; List blocks = container.getBlocks(); int blockCount = blocks.size(); if (blockCount > 0) { Size2D[] sizes = new Size2D[blocks.size()]; for (int i = 0; i < blocks.size(); i++) { Block block = (Block) blocks.get(i); sizes[i] = block.arrange(g2, RectangleConstraint.NONE); height = height + sizes[i].getHeight(); maxWidth = Math.max(sizes[i].width, maxWidth); block.setBounds( new Rectangle2D.Double( 0.0, y, sizes[i].width, sizes[i].height ) ); y = y + sizes[i].height + this.verticalGap; } if (blockCount > 1) { height = height + this.verticalGap * (blockCount - 1); } if (this.horizontalAlignment != HorizontalAlignment.LEFT) { for (int i = 0; i < blocks.size(); i++) { //Block b = (Block) blocks.get(i); if (this.horizontalAlignment == HorizontalAlignment.CENTER) { //TODO: shift block right by half } else if (this.horizontalAlignment == HorizontalAlignment.RIGHT) { //TODO: shift block over to right } } } } return new Size2D(maxWidth, height); } /** * Clears any cached information. */ @Override public void clear() { // no action required. } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof ColumnArrangement)) { return false; } ColumnArrangement that = (ColumnArrangement) obj; if (!Objects.equals(this.horizontalAlignment, that.horizontalAlignment)) { return false; } if (!Objects.equals(this.verticalAlignment, that.verticalAlignment)) { return false; } if (Double.doubleToLongBits(this.horizontalGap) != Double.doubleToLongBits(that.horizontalGap)) { return false; } if (Double.doubleToLongBits(this.verticalGap) != Double.doubleToLongBits(that.verticalGap)) { return false; } return true; } @Override public int hashCode() { int hash = 7; hash = 79 * hash + Objects.hashCode(this.horizontalAlignment); hash = 79 * hash + Objects.hashCode(this.verticalAlignment); hash = 79 * hash + (int) (Double.doubleToLongBits(this.horizontalGap) ^ (Double.doubleToLongBits(this.horizontalGap) >>> 32)); hash = 79 * hash + (int) (Double.doubleToLongBits(this.verticalGap) ^ (Double.doubleToLongBits(this.verticalGap) >>> 32)); return hash; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/block/EmptyBlock.java000066400000000000000000000075341463604235500270750ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * EmptyBlock.java * --------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.block; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import org.jfree.chart.ui.Size2D; import org.jfree.chart.util.PublicCloneable; /** * An empty block with a fixed size. */ public class EmptyBlock extends AbstractBlock implements Block, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -4083197869412648579L; /** * Creates a new block with the specified width and height. * * @param width the width. * @param height the height. */ public EmptyBlock(double width, double height) { setWidth(width); setHeight(height); } /** * Arranges the contents of the block, within the given constraints, and * returns the block size. * * @param g2 the graphics device. * @param constraint the constraint ({@code null} not permitted). * * @return The block size (in Java2D units, never {@code null}). */ @Override public Size2D arrange(Graphics2D g2, RectangleConstraint constraint) { Size2D base = new Size2D(calculateTotalWidth(getWidth()), calculateTotalHeight(getHeight())); return constraint.calculateConstrainedSize(base); } /** * Draws the block. Since the block is empty, there is nothing to draw * except the optional border. * * @param g2 the graphics device. * @param area the area. */ @Override public void draw(Graphics2D g2, Rectangle2D area) { draw(g2, area, null); } /** * Draws the block within the specified area. Since the block is empty, * there is nothing to draw except the optional border. * * @param g2 the graphics device. * @param area the area. * @param params ignored ({@code null} permitted). * * @return Always {@code null}. */ @Override public Object draw(Graphics2D g2, Rectangle2D area, Object params) { area = trimMargin(area); drawBorder(g2, area); return null; } /** * Returns a clone of the block. * * @return A clone. * * @throws CloneNotSupportedException if there is a problem cloning. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/block/EntityBlockParams.java000066400000000000000000000036041463604235500304110ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * EntityBlockParams.java * ---------------------- * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.block; /** * An interface that is used by the draw() method of some {@link Block} * implementations to determine whether or not to generate entities for the * items within the block. */ public interface EntityBlockParams { /** * Returns a flag that controls whether or not the block should return * entities for the items it draws. * * @return A boolean. */ boolean getGenerateEntities(); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/block/EntityBlockResult.java000066400000000000000000000034741463604235500304510ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * EntityBlockResult.java * ---------------------- * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.block; import org.jfree.chart.entity.EntityCollection; /** * Provides access to the {@link EntityCollection} generated when a block is * drawn. */ public interface EntityBlockResult { /** * Returns the entity collection. * * @return An entity collection (possibly {@code null}). */ EntityCollection getEntityCollection(); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/block/FlowArrangement.java000066400000000000000000000405761463604235500301220ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * FlowArrangement.java * -------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.block; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Objects; import org.jfree.chart.ui.HorizontalAlignment; import org.jfree.chart.ui.Size2D; import org.jfree.chart.ui.VerticalAlignment; /** * Arranges blocks in a flow layout. This class is immutable. */ public class FlowArrangement implements Arrangement, Serializable { /** For serialization. */ private static final long serialVersionUID = 4543632485478613800L; /** The horizontal alignment of blocks. */ private HorizontalAlignment horizontalAlignment; /** The vertical alignment of blocks within each row. */ private VerticalAlignment verticalAlignment; /** The horizontal gap between items within rows. */ private double horizontalGap; /** The vertical gap between rows. */ private double verticalGap; /** * Creates a new instance. */ public FlowArrangement() { this(HorizontalAlignment.CENTER, VerticalAlignment.CENTER, 2.0, 2.0); } /** * Creates a new instance. * * @param hAlign the horizontal alignment (currently ignored). * @param vAlign the vertical alignment (currently ignored). * @param hGap the horizontal gap. * @param vGap the vertical gap. */ public FlowArrangement(HorizontalAlignment hAlign, VerticalAlignment vAlign, double hGap, double vGap) { this.horizontalAlignment = hAlign; this.verticalAlignment = vAlign; this.horizontalGap = hGap; this.verticalGap = vGap; } /** * Adds a block to be managed by this instance. This method is usually * called by the {@link BlockContainer}, you shouldn't need to call it * directly. * * @param block the block. * @param key a key that controls the position of the block. */ @Override public void add(Block block, Object key) { // since the flow layout is relatively straightforward, // no information needs to be recorded here } /** * Calculates and sets the bounds of all the items in the specified * container, subject to the given constraint. The {@code Graphics2D} * can be used by some items (particularly items containing text) to * calculate sizing parameters. * * @param container the container whose items are being arranged. * @param constraint the size constraint. * @param g2 the graphics device. * * @return The size of the container after arrangement of the contents. */ @Override public Size2D arrange(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { LengthConstraintType w = constraint.getWidthConstraintType(); LengthConstraintType h = constraint.getHeightConstraintType(); if (w == LengthConstraintType.NONE) { if (h == LengthConstraintType.NONE) { return arrangeNN(container, g2); } else if (h == LengthConstraintType.FIXED) { return arrangeNF(container, g2, constraint); } else if (h == LengthConstraintType.RANGE) { throw new RuntimeException("Not implemented."); } } else if (w == LengthConstraintType.FIXED) { if (h == LengthConstraintType.NONE) { return arrangeFN(container, g2, constraint); } else if (h == LengthConstraintType.FIXED) { return arrangeFF(container, g2, constraint); } else if (h == LengthConstraintType.RANGE) { return arrangeFR(container, g2, constraint); } } else if (w == LengthConstraintType.RANGE) { if (h == LengthConstraintType.NONE) { return arrangeRN(container, g2, constraint); } else if (h == LengthConstraintType.FIXED) { return arrangeRF(container, g2, constraint); } else if (h == LengthConstraintType.RANGE) { return arrangeRR(container, g2, constraint); } } throw new RuntimeException("Unrecognised constraint type."); } /** * Arranges the blocks in the container with a fixed width and no height * constraint. * * @param container the container. * @param constraint the constraint. * @param g2 the graphics device. * * @return The size. */ protected Size2D arrangeFN(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { List blocks = container.getBlocks(); double width = constraint.getWidth(); double x = 0.0; double y = 0.0; double maxHeight = 0.0; List itemsInRow = new ArrayList(); for (int i = 0; i < blocks.size(); i++) { Block block = (Block) blocks.get(i); Size2D size = block.arrange(g2, RectangleConstraint.NONE); if (x + size.width <= width) { itemsInRow.add(block); block.setBounds( new Rectangle2D.Double(x, y, size.width, size.height) ); x = x + size.width + this.horizontalGap; maxHeight = Math.max(maxHeight, size.height); } else { if (itemsInRow.isEmpty()) { // place in this row (truncated) anyway block.setBounds( new Rectangle2D.Double( x, y, Math.min(size.width, width - x), size.height ) ); x = 0.0; y = y + size.height + this.verticalGap; } else { // start new row itemsInRow.clear(); x = 0.0; y = y + maxHeight + this.verticalGap; maxHeight = size.height; block.setBounds( new Rectangle2D.Double( x, y, Math.min(size.width, width), size.height ) ); x = size.width + this.horizontalGap; itemsInRow.add(block); } } } return new Size2D(constraint.getWidth(), y + maxHeight); } /** * Arranges the blocks in the container with a fixed width and a range * constraint on the height. * * @param container the container. * @param constraint the constraint. * @param g2 the graphics device. * * @return The size following the arrangement. */ protected Size2D arrangeFR(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { Size2D s = arrangeFN(container, g2, constraint); if (constraint.getHeightRange().contains(s.height)) { return s; } else { RectangleConstraint c = constraint.toFixedHeight( constraint.getHeightRange().constrain(s.getHeight()) ); return arrangeFF(container, g2, c); } } /** * Arranges the blocks in the container with the overall height and width * specified as fixed constraints. * * @param container the container. * @param constraint the constraint. * @param g2 the graphics device. * * @return The size following the arrangement. */ protected Size2D arrangeFF(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { // TODO: implement this properly return arrangeFN(container, g2, constraint); } /** * Arranges the blocks with the overall width and height to fit within * specified ranges. * * @param container the container. * @param constraint the constraint. * @param g2 the graphics device. * * @return The size after the arrangement. */ protected Size2D arrangeRR(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { // first arrange without constraints, and see if this fits within // the required ranges... Size2D s1 = arrangeNN(container, g2); if (constraint.getWidthRange().contains(s1.width)) { return s1; // TODO: we didn't check the height yet } else { RectangleConstraint c = constraint.toFixedWidth( constraint.getWidthRange().getUpperBound() ); return arrangeFR(container, g2, c); } } /** * Arranges the blocks in the container with a range constraint on the * width and a fixed height. * * @param container the container. * @param constraint the constraint. * @param g2 the graphics device. * * @return The size following the arrangement. */ protected Size2D arrangeRF(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { Size2D s = arrangeNF(container, g2, constraint); if (constraint.getWidthRange().contains(s.width)) { return s; } else { RectangleConstraint c = constraint.toFixedWidth( constraint.getWidthRange().constrain(s.getWidth()) ); return arrangeFF(container, g2, c); } } /** * Arranges the block with a range constraint on the width, and no * constraint on the height. * * @param container the container. * @param constraint the constraint. * @param g2 the graphics device. * * @return The size following the arrangement. */ protected Size2D arrangeRN(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { // first arrange without constraints, then see if the width fits // within the required range...if not, call arrangeFN() at max width Size2D s1 = arrangeNN(container, g2); if (constraint.getWidthRange().contains(s1.width)) { return s1; } else { RectangleConstraint c = constraint.toFixedWidth( constraint.getWidthRange().getUpperBound() ); return arrangeFN(container, g2, c); } } /** * Arranges the blocks without any constraints. This puts all blocks * into a single row. * * @param container the container. * @param g2 the graphics device. * * @return The size after the arrangement. */ protected Size2D arrangeNN(BlockContainer container, Graphics2D g2) { double x = 0.0; double width = 0.0; double maxHeight = 0.0; List blocks = container.getBlocks(); int blockCount = blocks.size(); if (blockCount > 0) { Size2D[] sizes = new Size2D[blocks.size()]; for (int i = 0; i < blocks.size(); i++) { Block block = (Block) blocks.get(i); sizes[i] = block.arrange(g2, RectangleConstraint.NONE); width = width + sizes[i].getWidth(); maxHeight = Math.max(sizes[i].height, maxHeight); block.setBounds( new Rectangle2D.Double( x, 0.0, sizes[i].width, sizes[i].height ) ); x = x + sizes[i].width + this.horizontalGap; } if (blockCount > 1) { width = width + this.horizontalGap * (blockCount - 1); } if (this.verticalAlignment != VerticalAlignment.TOP) { for (int i = 0; i < blocks.size(); i++) { //Block b = (Block) blocks.get(i); if (this.verticalAlignment == VerticalAlignment.CENTER) { //TODO: shift block down by half } else if (this.verticalAlignment == VerticalAlignment.BOTTOM) { //TODO: shift block down to bottom } } } } return new Size2D(width, maxHeight); } /** * Arranges the blocks with no width constraint and a fixed height * constraint. This puts all blocks into a single row. * * @param container the container. * @param constraint the constraint. * @param g2 the graphics device. * * @return The size after the arrangement. */ protected Size2D arrangeNF(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { // TODO: for now we are ignoring the height constraint return arrangeNN(container, g2); } /** * Clears any cached information. */ @Override public void clear() { // no action required. } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof FlowArrangement)) { return false; } FlowArrangement that = (FlowArrangement) obj; if (!Objects.equals(this.horizontalAlignment, that.horizontalAlignment)) { return false; } if (!Objects.equals(this.verticalAlignment, that.verticalAlignment)) { return false; } if (Double.doubleToLongBits(this.horizontalGap) != Double.doubleToLongBits(that.horizontalGap)) { return false; } if (Double.doubleToLongBits(this.verticalGap) != Double.doubleToLongBits(that.verticalGap)) { return false; } return true; } @Override public int hashCode() { int hash = 7; hash = 17 * hash + Objects.hashCode(this.horizontalAlignment); hash = 17 * hash + Objects.hashCode(this.verticalAlignment); hash = 17 * hash + (int) (Double.doubleToLongBits(this.horizontalGap) ^ (Double.doubleToLongBits(this.horizontalGap) >>> 32)); hash = 17 * hash + (int) (Double.doubleToLongBits(this.verticalGap) ^ (Double.doubleToLongBits(this.verticalGap) >>> 32)); return hash; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/block/GridArrangement.java000066400000000000000000000376031463604235500300750ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * GridArrangement.java * -------------------- * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (define hashCode); * */ package org.jfree.chart.block; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import java.util.Iterator; import java.util.List; import org.jfree.chart.ui.Size2D; /** * Arranges blocks in a grid within their container. */ public class GridArrangement implements Arrangement, Serializable { /** For serialization. */ private static final long serialVersionUID = -2563758090144655938L; /** The rows. */ private int rows; /** The columns. */ private int columns; /** * Creates a new grid arrangement. * * @param rows the row count. * @param columns the column count. */ public GridArrangement(int rows, int columns) { this.rows = rows; this.columns = columns; } /** * Adds a block and a key which can be used to determine the position of * the block in the arrangement. This method is called by the container * (you don't need to call this method directly) and gives the arrangement * an opportunity to record the details if they are required. * * @param block the block. * @param key the key ({@code null} permitted). */ @Override public void add(Block block, Object key) { // can safely ignore } /** * Arranges the blocks within the specified container, subject to the given * constraint. * * @param container the container ({@code null} not permitted). * @param constraint the constraint. * @param g2 the graphics device. * * @return The size following the arrangement. */ @Override public Size2D arrange(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { LengthConstraintType w = constraint.getWidthConstraintType(); LengthConstraintType h = constraint.getHeightConstraintType(); if (w == LengthConstraintType.NONE) { if (h == LengthConstraintType.NONE) { return arrangeNN(container, g2); } else if (h == LengthConstraintType.FIXED) { return arrangeNF(container, g2, constraint); } else if (h == LengthConstraintType.RANGE) { // find optimum height, then map to range return arrangeNR(container, g2, constraint); } } else if (w == LengthConstraintType.FIXED) { if (h == LengthConstraintType.NONE) { // find optimum height return arrangeFN(container, g2, constraint); } else if (h == LengthConstraintType.FIXED) { return arrangeFF(container, g2, constraint); } else if (h == LengthConstraintType.RANGE) { // find optimum height and map to range return arrangeFR(container, g2, constraint); } } else if (w == LengthConstraintType.RANGE) { // find optimum width and map to range if (h == LengthConstraintType.NONE) { // find optimum height return arrangeRN(container, g2, constraint); } else if (h == LengthConstraintType.FIXED) { // fixed width return arrangeRF(container, g2, constraint); } else if (h == LengthConstraintType.RANGE) { return arrangeRR(container, g2, constraint); } } throw new RuntimeException("Should never get to here!"); } /** * Arranges the container with no constraint on the width or height. * * @param container the container ({@code null} not permitted). * @param g2 the graphics device. * * @return The size. */ protected Size2D arrangeNN(BlockContainer container, Graphics2D g2) { double maxW = 0.0; double maxH = 0.0; List blocks = container.getBlocks(); Iterator iterator = blocks.iterator(); while (iterator.hasNext()) { Block b = (Block) iterator.next(); if (b != null) { Size2D s = b.arrange(g2, RectangleConstraint.NONE); maxW = Math.max(maxW, s.width); maxH = Math.max(maxH, s.height); } } double width = this.columns * maxW; double height = this.rows * maxH; RectangleConstraint c = new RectangleConstraint(width, height); return arrangeFF(container, g2, c); } /** * Arranges the container with a fixed overall width and height. * * @param container the container ({@code null} not permitted). * @param g2 the graphics device. * @param constraint the constraint ({@code null} not permitted). * * @return The size following the arrangement. */ protected Size2D arrangeFF(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { double width = constraint.getWidth() / this.columns; double height = constraint.getHeight() / this.rows; List blocks = container.getBlocks(); for (int c = 0; c < this.columns; c++) { for (int r = 0; r < this.rows; r++) { int index = r * this.columns + c; if (index >= blocks.size()) { break; } Block b = (Block) blocks.get(index); if (b != null) { b.setBounds(new Rectangle2D.Double(c * width, r * height, width, height)); } } } return new Size2D(this.columns * width, this.rows * height); } /** * Arrange with a fixed width and a height within a given range. * * @param container the container. * @param constraint the constraint. * @param g2 the graphics device. * * @return The size of the arrangement. */ protected Size2D arrangeFR(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { RectangleConstraint c1 = constraint.toUnconstrainedHeight(); Size2D size1 = arrange(container, g2, c1); if (constraint.getHeightRange().contains(size1.getHeight())) { return size1; } else { double h = constraint.getHeightRange().constrain(size1.getHeight()); RectangleConstraint c2 = constraint.toFixedHeight(h); return arrange(container, g2, c2); } } /** * Arrange with a fixed height and a width within a given range. * * @param container the container. * @param constraint the constraint. * @param g2 the graphics device. * * @return The size of the arrangement. */ protected Size2D arrangeRF(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { RectangleConstraint c1 = constraint.toUnconstrainedWidth(); Size2D size1 = arrange(container, g2, c1); if (constraint.getWidthRange().contains(size1.getWidth())) { return size1; } else { double w = constraint.getWidthRange().constrain(size1.getWidth()); RectangleConstraint c2 = constraint.toFixedWidth(w); return arrange(container, g2, c2); } } /** * Arrange with a fixed width and no height constraint. * * @param container the container. * @param constraint the constraint. * @param g2 the graphics device. * * @return The size of the arrangement. */ protected Size2D arrangeRN(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { RectangleConstraint c1 = constraint.toUnconstrainedWidth(); Size2D size1 = arrange(container, g2, c1); if (constraint.getWidthRange().contains(size1.getWidth())) { return size1; } else { double w = constraint.getWidthRange().constrain(size1.getWidth()); RectangleConstraint c2 = constraint.toFixedWidth(w); return arrange(container, g2, c2); } } /** * Arrange with a fixed height and no width constraint. * * @param container the container. * @param constraint the constraint. * @param g2 the graphics device. * * @return The size of the arrangement. */ protected Size2D arrangeNR(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { RectangleConstraint c1 = constraint.toUnconstrainedHeight(); Size2D size1 = arrange(container, g2, c1); if (constraint.getHeightRange().contains(size1.getHeight())) { return size1; } else { double h = constraint.getHeightRange().constrain(size1.getHeight()); RectangleConstraint c2 = constraint.toFixedHeight(h); return arrange(container, g2, c2); } } /** * Arrange with ranges for both the width and height constraints. * * @param container the container. * @param constraint the constraint. * @param g2 the graphics device. * * @return The size of the arrangement. */ protected Size2D arrangeRR(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { Size2D size1 = arrange(container, g2, RectangleConstraint.NONE); if (constraint.getWidthRange().contains(size1.getWidth())) { if (constraint.getHeightRange().contains(size1.getHeight())) { return size1; } else { // width is OK, but height must be constrained double h = constraint.getHeightRange().constrain( size1.getHeight()); RectangleConstraint cc = new RectangleConstraint( size1.getWidth(), h); return arrangeFF(container, g2, cc); } } else { if (constraint.getHeightRange().contains(size1.getHeight())) { // height is OK, but width must be constrained double w = constraint.getWidthRange().constrain( size1.getWidth()); RectangleConstraint cc = new RectangleConstraint(w, size1.getHeight()); return arrangeFF(container, g2, cc); } else { double w = constraint.getWidthRange().constrain( size1.getWidth()); double h = constraint.getHeightRange().constrain( size1.getHeight()); RectangleConstraint cc = new RectangleConstraint(w, h); return arrangeFF(container, g2, cc); } } } /** * Arrange with a fixed width and a height within a given range. * * @param container the container. * @param g2 the graphics device. * @param constraint the constraint. * * @return The size of the arrangement. */ protected Size2D arrangeFN(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { double width = constraint.getWidth() / this.columns; RectangleConstraint bc = constraint.toFixedWidth(width); List blocks = container.getBlocks(); double maxH = 0.0; for (int r = 0; r < this.rows; r++) { for (int c = 0; c < this.columns; c++) { int index = r * this.columns + c; if (index >= blocks.size()) { break; } Block b = (Block) blocks.get(index); if (b != null) { Size2D s = b.arrange(g2, bc); maxH = Math.max(maxH, s.getHeight()); } } } RectangleConstraint cc = constraint.toFixedHeight(maxH * this.rows); return arrange(container, g2, cc); } /** * Arrange with a fixed height and no constraint for the width. * * @param container the container. * @param g2 the graphics device. * @param constraint the constraint. * * @return The size of the arrangement. */ protected Size2D arrangeNF(BlockContainer container, Graphics2D g2, RectangleConstraint constraint) { double height = constraint.getHeight() / this.rows; RectangleConstraint bc = constraint.toFixedHeight(height); List blocks = container.getBlocks(); double maxW = 0.0; for (int r = 0; r < this.rows; r++) { for (int c = 0; c < this.columns; c++) { int index = r * this.columns + c; if (index >= blocks.size()) { break; } Block b = (Block) blocks.get(index); if (b != null) { Size2D s = b.arrange(g2, bc); maxW = Math.max(maxW, s.getWidth()); } } } RectangleConstraint cc = constraint.toFixedWidth(maxW * this.columns); return arrange(container, g2, cc); } /** * Clears any cached layout information retained by the arrangement. */ @Override public void clear() { // nothing to clear } /** * Compares this layout manager for equality with an arbitrary object. * * @param obj the object. * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof GridArrangement)) { return false; } GridArrangement that = (GridArrangement) obj; if (this.columns != that.columns) { return false; } if (this.rows != that.rows) { return false; } return true; } @Override public int hashCode() { int hash = 7; hash = 29 * hash + this.rows; hash = 29 * hash + this.columns; return hash; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/block/LabelBlock.java000066400000000000000000000330431463604235500270100ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * LabelBlock.java * --------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Pierre-Marie Le Biot; * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.block; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Objects; import org.jfree.chart.HashUtils; import org.jfree.chart.entity.ChartEntity; import org.jfree.chart.entity.StandardEntityCollection; import org.jfree.chart.text.TextBlock; import org.jfree.chart.text.TextBlockAnchor; import org.jfree.chart.text.TextUtils; import org.jfree.chart.ui.RectangleAnchor; import org.jfree.chart.ui.Size2D; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; /** * A block containing a label. */ public class LabelBlock extends AbstractBlock implements Block, PublicCloneable { /** For serialization. */ static final long serialVersionUID = 249626098864178017L; /** * The text for the label - retained in case the label needs * regenerating (for example, to change the font). */ private String text; /** The label. */ private TextBlock label; /** The font. */ private Font font; /** The tool tip text (can be {@code null}). */ private String toolTipText; /** The URL text (can be {@code null}). */ private String urlText; /** The default color. */ public static final Paint DEFAULT_PAINT = Color.BLACK; /** The paint. */ private transient Paint paint; /** * The content alignment point. */ private TextBlockAnchor contentAlignmentPoint; /** * The anchor point for the text. */ private RectangleAnchor textAnchor; /** * Creates a new label block. * * @param label the label ({@code null} not permitted). */ public LabelBlock(String label) { this(label, new Font("SansSerif", Font.PLAIN, 10), DEFAULT_PAINT); } /** * Creates a new label block. * * @param text the text for the label ({@code null} not permitted). * @param font the font ({@code null} not permitted). */ public LabelBlock(String text, Font font) { this(text, font, DEFAULT_PAINT); } /** * Creates a new label block. * * @param text the text for the label ({@code null} not permitted). * @param font the font ({@code null} not permitted). * @param paint the paint ({@code null} not permitted). */ public LabelBlock(String text, Font font, Paint paint) { this.text = text; this.paint = paint; this.label = TextUtils.createTextBlock(text, font, this.paint); this.font = font; this.toolTipText = null; this.urlText = null; this.contentAlignmentPoint = TextBlockAnchor.CENTER; this.textAnchor = RectangleAnchor.CENTER; } /** * Returns the font. * * @return The font (never {@code null}). * * @see #setFont(Font) */ public Font getFont() { return this.font; } /** * Sets the font and regenerates the label. * * @param font the font ({@code null} not permitted). * * @see #getFont() */ public void setFont(Font font) { Args.nullNotPermitted(font, "font"); this.font = font; this.label = TextUtils.createTextBlock(this.text, font, this.paint); } /** * Returns the paint. * * @return The paint (never {@code null}). * * @see #setPaint(Paint) */ public Paint getPaint() { return this.paint; } /** * Sets the paint and regenerates the label. * * @param paint the paint ({@code null} not permitted). * * @see #getPaint() */ public void setPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.paint = paint; this.label = TextUtils.createTextBlock(this.text, this.font, this.paint); } /** * Returns the tool tip text. * * @return The tool tip text (possibly {@code null}). * * @see #setToolTipText(String) */ public String getToolTipText() { return this.toolTipText; } /** * Sets the tool tip text. * * @param text the text ({@code null} permitted). * * @see #getToolTipText() */ public void setToolTipText(String text) { this.toolTipText = text; } /** * Returns the URL text. * * @return The URL text (possibly {@code null}). * * @see #setURLText(String) */ public String getURLText() { return this.urlText; } /** * Sets the URL text. * * @param text the text ({@code null} permitted). * * @see #getURLText() */ public void setURLText(String text) { this.urlText = text; } /** * Returns the content alignment point. * * @return The content alignment point (never {@code null}). */ public TextBlockAnchor getContentAlignmentPoint() { return this.contentAlignmentPoint; } /** * Sets the content alignment point. * * @param anchor the anchor used to determine the alignment point (never * {@code null}). */ public void setContentAlignmentPoint(TextBlockAnchor anchor) { Args.nullNotPermitted(anchor, "anchor"); this.contentAlignmentPoint = anchor; } /** * Returns the text anchor (never {@code null}). * * @return The text anchor. */ public RectangleAnchor getTextAnchor() { return this.textAnchor; } /** * Sets the text anchor. * * @param anchor the anchor ({@code null} not permitted). */ public void setTextAnchor(RectangleAnchor anchor) { this.textAnchor = anchor; } /** * Arranges the contents of the block, within the given constraints, and * returns the block size. * * @param g2 the graphics device. * @param constraint the constraint ({@code null} not permitted). * * @return The block size (in Java2D units, never {@code null}). */ @Override public Size2D arrange(Graphics2D g2, RectangleConstraint constraint) { g2.setFont(this.font); Size2D s = this.label.calculateDimensions(g2); return new Size2D(calculateTotalWidth(s.getWidth()), calculateTotalHeight(s.getHeight())); } /** * Draws the block. * * @param g2 the graphics device. * @param area the area. */ @Override public void draw(Graphics2D g2, Rectangle2D area) { draw(g2, area, null); } /** * Draws the block within the specified area. * * @param g2 the graphics device. * @param area the area. * @param params ignored ({@code null} permitted). * * @return Always {@code null}. */ @Override public Object draw(Graphics2D g2, Rectangle2D area, Object params) { area = trimMargin(area); drawBorder(g2, area); area = trimBorder(area); area = trimPadding(area); // check if we need to collect chart entities from the container EntityBlockParams ebp = null; StandardEntityCollection sec = null; Shape entityArea = null; if (params instanceof EntityBlockParams) { ebp = (EntityBlockParams) params; if (ebp.getGenerateEntities()) { sec = new StandardEntityCollection(); entityArea = (Shape) area.clone(); } } g2.setPaint(this.paint); g2.setFont(this.font); Point2D pt = this.textAnchor.getAnchorPoint(area); this.label.draw(g2, (float) pt.getX(), (float) pt.getY(), this.contentAlignmentPoint); BlockResult result = null; if (ebp != null && sec != null) { if (this.toolTipText != null || this.urlText != null) { ChartEntity entity = new ChartEntity(entityArea, this.toolTipText, this.urlText); sec.add(entity); result = new BlockResult(); result.setEntityCollection(sec); } } return result; } /** * Tests this {@code LabelBlock} for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof LabelBlock)) { return false; } LabelBlock that = (LabelBlock) obj; if (!Objects.equals(this.text, that.text)) { return false; } if (!Objects.equals(this.label, that.label)) { return false; } if (!Objects.equals(this.font, that.font)) { return false; } if (!PaintUtils.equal(this.paint, that.paint)) { return false; } if (!Objects.equals(this.toolTipText, that.toolTipText)) { return false; } if (!Objects.equals(this.urlText, that.urlText)) { return false; } if (!Objects.equals(this.contentAlignmentPoint, that.contentAlignmentPoint)) { return false; } if (!Objects.equals(this.textAnchor, that.textAnchor)) { return false; } if (!that.canEqual(this)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof LabelBlock); } @Override public int hashCode() { int hash = super.hashCode(); // equals calls superclass, hashCode must also hash = 71 * hash + Objects.hashCode(this.text); hash = 71 * hash + Objects.hashCode(this.label); hash = 71 * hash + Objects.hashCode(this.font); hash = 71 * hash + Objects.hashCode(this.toolTipText); hash = 71 * hash + Objects.hashCode(this.urlText); hash = 71 * hash + HashUtils.hashCodeForPaint(this.paint); hash = 71 * hash + Objects.hashCode(this.contentAlignmentPoint); hash = 71 * hash + Objects.hashCode(this.textAnchor); return hash; } /** * Returns a clone of this {@code LabelBlock} instance. * * @return A clone. * * @throws CloneNotSupportedException if there is a problem cloning. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.paint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.paint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/block/LengthConstraintType.java000066400000000000000000000100141463604235500311370ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * LengthConstraintType.java * ------------------------- * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.block; import java.io.ObjectStreamException; import java.io.Serializable; /** * Defines tokens used to indicate a length constraint type. */ public final class LengthConstraintType implements Serializable { /** For serialization. */ private static final long serialVersionUID = -1156658804028142978L; /** NONE. */ public static final LengthConstraintType NONE = new LengthConstraintType("LengthConstraintType.NONE"); /** Range. */ public static final LengthConstraintType RANGE = new LengthConstraintType("RectangleConstraintType.RANGE"); /** FIXED. */ public static final LengthConstraintType FIXED = new LengthConstraintType("LengthConstraintType.FIXED"); /** The name. */ private String name; /** * Private constructor. * * @param name the name. */ private LengthConstraintType(String name) { this.name = name; } /** * Returns a string representing the object. * * @return The string. */ @Override public String toString() { return this.name; } /** * Returns {@code true} if this object is equal to the specified * object, and {@code false} otherwise. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof LengthConstraintType)) { return false; } LengthConstraintType that = (LengthConstraintType) obj; if (!this.name.equals(that.toString())) { return false; } return true; } /** * Returns a hash code value for the object. * * @return The hashcode */ @Override public int hashCode() { return this.name.hashCode(); } /** * Ensures that serialization returns the unique instances. * * @return The object. * * @throws ObjectStreamException if there is a problem. */ private Object readResolve() throws ObjectStreamException { if (this.equals(LengthConstraintType.NONE)) { return LengthConstraintType.NONE; } else if (this.equals(LengthConstraintType.RANGE)) { return LengthConstraintType.RANGE; } else if (this.equals(LengthConstraintType.FIXED)) { return LengthConstraintType.FIXED; } return null; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/block/LineBorder.java000066400000000000000000000166521463604235500270520ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * LineBorder.java * --------------- * (C) Copyright 2007-present, by Christo Zietsman and Contributors. * * Original Author: Christo Zietsman; * Contributor(s): David Gilbert; * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.block; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.RenderingHints; import java.awt.Stroke; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.HashUtils; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.SerialUtils; /** * A line border for any {@link AbstractBlock}. */ public class LineBorder implements BlockFrame, Serializable { /** For serialization. */ static final long serialVersionUID = 4630356736707233924L; /** The line color. */ private transient Paint paint; /** The line stroke. */ private transient Stroke stroke; /** The insets. */ private RectangleInsets insets; /** * Creates a default border. */ public LineBorder() { this(Color.BLACK, new BasicStroke(1.0f), new RectangleInsets(1.0, 1.0, 1.0, 1.0)); } /** * Creates a new border with the specified color. * * @param paint the color ({@code null} not permitted). * @param stroke the border stroke ({@code null} not permitted). * @param insets the insets ({@code null} not permitted). */ public LineBorder(Paint paint, Stroke stroke, RectangleInsets insets) { Args.nullNotPermitted(paint, "paint"); Args.nullNotPermitted(stroke, "stroke"); Args.nullNotPermitted(insets, "insets"); this.paint = paint; this.stroke = stroke; this.insets = insets; } /** * Returns the paint. * * @return The paint (never {@code null}). */ public Paint getPaint() { return this.paint; } /** * Returns the insets. * * @return The insets (never {@code null}). */ @Override public RectangleInsets getInsets() { return this.insets; } /** * Returns the stroke. * * @return The stroke (never {@code null}). */ public Stroke getStroke() { return this.stroke; } /** * Draws the border by filling in the reserved space (in black). * * @param g2 the graphics device. * @param area the area. */ @Override public void draw(Graphics2D g2, Rectangle2D area) { double w = area.getWidth(); double h = area.getHeight(); // if the area has zero height or width, we shouldn't draw anything if (w <= 0.0 || h <= 0.0) { return; } double t = this.insets.calculateTopInset(h); double b = this.insets.calculateBottomInset(h); double l = this.insets.calculateLeftInset(w); double r = this.insets.calculateRightInset(w); double x = area.getX(); double y = area.getY(); double x0 = x + l / 2.0; double x1 = x + w - r / 2.0; double y0 = y + h - b / 2.0; double y1 = y + t / 2.0; g2.setPaint(getPaint()); g2.setStroke(getStroke()); Object saved = g2.getRenderingHint(RenderingHints.KEY_STROKE_CONTROL); g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE); Line2D line = new Line2D.Double(); if (t > 0.0) { line.setLine(x0, y1, x1, y1); g2.draw(line); } if (b > 0.0) { line.setLine(x0, y0, x1, y0); g2.draw(line); } if (l > 0.0) { line.setLine(x0, y0, x0, y1); g2.draw(line); } if (r > 0.0) { line.setLine(x1, y0, x1, y1); g2.draw(line); } g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, saved); } /** * Tests this border for equality with an arbitrary instance. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof LineBorder)) { return false; } LineBorder that = (LineBorder) obj; if (!PaintUtils.equal(this.paint, that.paint)) { return false; } if (!Objects.equals(this.stroke, that.stroke)) { return false; } if (!Objects.equals(this.insets, that.insets)) { return false; } return true; } @Override public int hashCode() { int hash = 3; hash = 47 * hash + HashUtils.hashCodeForPaint(this.paint); hash = 47 * hash + Objects.hashCode(this.stroke); hash = 47 * hash + Objects.hashCode(this.insets); return hash; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.paint, stream); SerialUtils.writeStroke(this.stroke, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.paint = SerialUtils.readPaint(stream); this.stroke = SerialUtils.readStroke(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/block/RectangleConstraint.java000066400000000000000000000265371463604235500310010ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * RectangleConstraint.java * ------------------------ * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.block; import org.jfree.chart.ui.Size2D; import org.jfree.chart.util.Args; import org.jfree.data.Range; /** * A description of a constraint for resizing a rectangle. Constraints are * immutable. */ public class RectangleConstraint { /** * An instance representing no constraint. */ public static final RectangleConstraint NONE = new RectangleConstraint( 0.0, null, LengthConstraintType.NONE, 0.0, null, LengthConstraintType.NONE); /** The width. */ private double width; /** The width range. */ private Range widthRange; /** The width constraint type. */ private LengthConstraintType widthConstraintType; /** The fixed or maximum height. */ private double height; private Range heightRange; /** The constraint type. */ private LengthConstraintType heightConstraintType; /** * Creates a new "fixed width and height" instance. * * @param w the fixed width. * @param h the fixed height. */ public RectangleConstraint(double w, double h) { this(w, null, LengthConstraintType.FIXED, h, null, LengthConstraintType.FIXED); } /** * Creates a new "range width and height" instance. * * @param w the width range. * @param h the height range. */ public RectangleConstraint(Range w, Range h) { this(0.0, w, LengthConstraintType.RANGE, 0.0, h, LengthConstraintType.RANGE); } /** * Creates a new constraint with a range for the width and a * fixed height. * * @param w the width range. * @param h the fixed height. */ public RectangleConstraint(Range w, double h) { this(0.0, w, LengthConstraintType.RANGE, h, null, LengthConstraintType.FIXED); } /** * Creates a new constraint with a fixed width and a range for * the height. * * @param w the fixed width. * @param h the height range. */ public RectangleConstraint(double w, Range h) { this(w, null, LengthConstraintType.FIXED, 0.0, h, LengthConstraintType.RANGE); } /** * Creates a new constraint. * * @param w the fixed or maximum width. * @param widthRange the width range. * @param widthConstraintType the width type. * @param h the fixed or maximum height. * @param heightRange the height range. * @param heightConstraintType the height type. */ public RectangleConstraint(double w, Range widthRange, LengthConstraintType widthConstraintType, double h, Range heightRange, LengthConstraintType heightConstraintType) { Args.nullNotPermitted(widthConstraintType, "widthConstraintType"); Args.nullNotPermitted(heightConstraintType, "heightConstraintType"); this.width = w; this.widthRange = widthRange; this.widthConstraintType = widthConstraintType; this.height = h; this.heightRange = heightRange; this.heightConstraintType = heightConstraintType; } /** * Returns the fixed width. * * @return The width. */ public double getWidth() { return this.width; } /** * Returns the width range. * * @return The range (possibly {@code null}). */ public Range getWidthRange() { return this.widthRange; } /** * Returns the constraint type. * * @return The constraint type (never {@code null}). */ public LengthConstraintType getWidthConstraintType() { return this.widthConstraintType; } /** * Returns the fixed height. * * @return The height. */ public double getHeight() { return this.height; } /** * Returns the width range. * * @return The range (possibly {@code null}). */ public Range getHeightRange() { return this.heightRange; } /** * Returns the constraint type. * * @return The constraint type (never {@code null}). */ public LengthConstraintType getHeightConstraintType() { return this.heightConstraintType; } /** * Returns a constraint that matches this one on the height attributes, * but has no width constraint. * * @return A new constraint. */ public RectangleConstraint toUnconstrainedWidth() { if (this.widthConstraintType == LengthConstraintType.NONE) { return this; } else { return new RectangleConstraint(this.width, this.widthRange, LengthConstraintType.NONE, this.height, this.heightRange, this.heightConstraintType); } } /** * Returns a constraint that matches this one on the width attributes, * but has no height constraint. * * @return A new constraint. */ public RectangleConstraint toUnconstrainedHeight() { if (this.heightConstraintType == LengthConstraintType.NONE) { return this; } else { return new RectangleConstraint(this.width, this.widthRange, this.widthConstraintType, 0.0, this.heightRange, LengthConstraintType.NONE); } } /** * Returns a constraint that matches this one on the height attributes, * but has a fixed width constraint. * * @param width the fixed width. * * @return A new constraint. */ public RectangleConstraint toFixedWidth(double width) { return new RectangleConstraint(width, this.widthRange, LengthConstraintType.FIXED, this.height, this.heightRange, this.heightConstraintType); } /** * Returns a constraint that matches this one on the width attributes, * but has a fixed height constraint. * * @param height the fixed height. * * @return A new constraint. */ public RectangleConstraint toFixedHeight(double height) { return new RectangleConstraint(this.width, this.widthRange, this.widthConstraintType, height, this.heightRange, LengthConstraintType.FIXED); } /** * Returns a constraint that matches this one on the height attributes, * but has a range width constraint. * * @param range the width range ({@code null} not permitted). * * @return A new constraint. */ public RectangleConstraint toRangeWidth(Range range) { Args.nullNotPermitted(range, "range"); return new RectangleConstraint(range.getUpperBound(), range, LengthConstraintType.RANGE, this.height, this.heightRange, this.heightConstraintType); } /** * Returns a constraint that matches this one on the width attributes, * but has a range height constraint. * * @param range the height range ({@code null} not permitted). * * @return A new constraint. */ public RectangleConstraint toRangeHeight(Range range) { Args.nullNotPermitted(range, "range"); return new RectangleConstraint(this.width, this.widthRange, this.widthConstraintType, range.getUpperBound(), range, LengthConstraintType.RANGE); } /** * Returns a string representation of this instance, mostly used for * debugging purposes. * * @return A string. */ @Override public String toString() { return "RectangleConstraint[" + this.widthConstraintType.toString() + ": width=" + this.width + ", height=" + this.height + "]"; } /** * Returns the new size that reflects the constraints defined by this * instance. * * @param base the base size. * * @return The constrained size. */ public Size2D calculateConstrainedSize(Size2D base) { Size2D result = new Size2D(); if (this.widthConstraintType == LengthConstraintType.NONE) { result.width = base.width; if (this.heightConstraintType == LengthConstraintType.NONE) { result.height = base.height; } else if (this.heightConstraintType == LengthConstraintType.RANGE) { result.height = this.heightRange.constrain(base.height); } else if (this.heightConstraintType == LengthConstraintType.FIXED) { result.height = this.height; } } else if (this.widthConstraintType == LengthConstraintType.RANGE) { result.width = this.widthRange.constrain(base.width); if (this.heightConstraintType == LengthConstraintType.NONE) { result.height = base.height; } else if (this.heightConstraintType == LengthConstraintType.RANGE) { result.height = this.heightRange.constrain(base.height); } else if (this.heightConstraintType == LengthConstraintType.FIXED) { result.height = this.height; } } else if (this.widthConstraintType == LengthConstraintType.FIXED) { result.width = this.width; if (this.heightConstraintType == LengthConstraintType.NONE) { result.height = base.height; } else if (this.heightConstraintType == LengthConstraintType.RANGE) { result.height = this.heightRange.constrain(base.height); } else if (this.heightConstraintType == LengthConstraintType.FIXED) { result.height = this.height; } } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/block/package.html000066400000000000000000000003171463604235500264320ustar00rootroot00000000000000 Blocks and layout classes used extensively by the {@link org.jfree.chart.title.LegendTitle} class. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/date/000077500000000000000000000000001463604235500237735ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/date/MonthConstants.java000066400000000000000000000042251463604235500276230ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * MonthConstants.java * ------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.date; /** * A set of constants for the month numbers (1 - 12). */ public interface MonthConstants { /** Constant for January. */ int JANUARY = 1; /** Constant for February. */ int FEBRUARY = 2; /** Constant for March. */ int MARCH = 3; /** Constant for April. */ int APRIL = 4; /** Constant for May. */ int MAY = 5; /** Constant for June. */ int JUNE = 6; /** Constant for July. */ int JULY = 7; /** Constant for August. */ int AUGUST = 8; /** Constant for September. */ int SEPTEMBER = 9; /** Constant for October. */ int OCTOBER = 10; /** Constant for November. */ int NOVEMBER = 11; /** Constant for December. */ int DECEMBER = 12; } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/date/SerialDate.java000066400000000000000000000735461463604235500266720ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * SerialDate.java * --------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.date; import java.io.Serializable; import java.text.DateFormatSymbols; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.GregorianCalendar; /** * An abstract class that defines our requirements for manipulating dates, * without tying down a particular implementation. *

* Requirement 1 : match at least what Excel does for dates; * Requirement 2 : the date represented by the class is immutable; *

* Why not just use java.util.Date? We will, when it makes sense. At times, * java.util.Date can be *too* precise - it represents an instant in time, * accurate to 1/1000th of a second (with the date itself depending on the * time-zone). Sometimes we just want to represent a particular day (e.g. 21 * January 2015) without concerning ourselves about the time of day, or the * time-zone, or anything else. That's what we've defined SerialDate for. *

* You can call getInstance() to get a concrete subclass of SerialDate, * without worrying about the exact implementation. */ public abstract class SerialDate implements Comparable, Serializable, MonthConstants { /** For serialization. */ private static final long serialVersionUID = -293716040467423637L; /** Date format symbols. */ public static final DateFormatSymbols DATE_FORMAT_SYMBOLS = new SimpleDateFormat().getDateFormatSymbols(); /** The serial number for 1 January 1900. */ public static final int SERIAL_LOWER_BOUND = 2; /** The serial number for 31 December 9999. */ public static final int SERIAL_UPPER_BOUND = 2958465; /** The lowest year value supported by this date format. */ public static final int MINIMUM_YEAR_SUPPORTED = 1900; /** The highest year value supported by this date format. */ public static final int MAXIMUM_YEAR_SUPPORTED = 9999; /** Useful constant for Monday. Equivalent to java.util.Calendar.MONDAY. */ public static final int MONDAY = Calendar.MONDAY; /** * Useful constant for Tuesday. Equivalent to java.util.Calendar.TUESDAY. */ public static final int TUESDAY = Calendar.TUESDAY; /** * Useful constant for Wednesday. Equivalent to * java.util.Calendar.WEDNESDAY. */ public static final int WEDNESDAY = Calendar.WEDNESDAY; /** * Useful constant for Thrusday. Equivalent to java.util.Calendar.THURSDAY. */ public static final int THURSDAY = Calendar.THURSDAY; /** Useful constant for Friday. Equivalent to java.util.Calendar.FRIDAY. */ public static final int FRIDAY = Calendar.FRIDAY; /** * Useful constant for Saturday. Equivalent to java.util.Calendar.SATURDAY. */ public static final int SATURDAY = Calendar.SATURDAY; /** Useful constant for Sunday. Equivalent to java.util.Calendar.SUNDAY. */ public static final int SUNDAY = Calendar.SUNDAY; /** The number of days in each month in non leap years. */ static final int[] LAST_DAY_OF_MONTH = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; /** The number of days in a (non-leap) year up to the end of each month. */ static final int[] AGGREGATE_DAYS_TO_END_OF_MONTH = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}; /** The number of days in a year up to the end of the preceding month. */ static final int[] AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH = {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365}; /** The number of days in a leap year up to the end of each month. */ static final int[] LEAP_YEAR_AGGREGATE_DAYS_TO_END_OF_MONTH = {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}; /** * The number of days in a leap year up to the end of the preceding month. */ static final int[] LEAP_YEAR_AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH = {0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}; /** A useful constant for referring to the first week in a month. */ public static final int FIRST_WEEK_IN_MONTH = 1; /** A useful constant for referring to the second week in a month. */ public static final int SECOND_WEEK_IN_MONTH = 2; /** A useful constant for referring to the third week in a month. */ public static final int THIRD_WEEK_IN_MONTH = 3; /** A useful constant for referring to the fourth week in a month. */ public static final int FOURTH_WEEK_IN_MONTH = 4; /** A useful constant for referring to the last week in a month. */ public static final int LAST_WEEK_IN_MONTH = 0; /** Useful range constant. */ public static final int INCLUDE_NONE = 0; /** Useful range constant. */ public static final int INCLUDE_FIRST = 1; /** Useful range constant. */ public static final int INCLUDE_SECOND = 2; /** Useful range constant. */ public static final int INCLUDE_BOTH = 3; /** * Useful constant for specifying a day of the week relative to a fixed * date. */ public static final int PRECEDING = -1; /** * Useful constant for specifying a day of the week relative to a fixed * date. */ public static final int NEAREST = 0; /** * Useful constant for specifying a day of the week relative to a fixed * date. */ public static final int FOLLOWING = 1; /** A description for the date. */ private String description; /** * Default constructor. */ protected SerialDate() { } /** * Returns {@code true} if the supplied integer code represents a * valid day-of-the-week, and {@code false} otherwise. * * @param code the code being checked for validity. * * @return {@code true} if the supplied integer code represents a * valid day-of-the-week, and {@code false} otherwise. */ public static boolean isValidWeekdayCode(int code) { switch(code) { case SUNDAY: case MONDAY: case TUESDAY: case WEDNESDAY: case THURSDAY: case FRIDAY: case SATURDAY: return true; default: return false; } } /** * Converts the supplied string to a day of the week. * * @param s a string representing the day of the week. * * @return {@code -1} if the string is not convertable, the day of * the week otherwise. */ public static int stringToWeekdayCode(String s) { final String[] shortWeekdayNames = DATE_FORMAT_SYMBOLS.getShortWeekdays(); final String[] weekDayNames = DATE_FORMAT_SYMBOLS.getWeekdays(); int result = -1; s = s.trim(); for (int i = 0; i < weekDayNames.length; i++) { if (s.equals(shortWeekdayNames[i])) { result = i; break; } if (s.equals(weekDayNames[i])) { result = i; break; } } return result; } /** * Returns a string representing the supplied day-of-the-week. *

* Need to find a better approach. * * @param weekday the day of the week. * * @return a string representing the supplied day-of-the-week. */ public static String weekdayCodeToString(int weekday) { final String[] weekdays = DATE_FORMAT_SYMBOLS.getWeekdays(); return weekdays[weekday]; } /** * Returns an array of month names. * * @return an array of month names. */ public static String[] getMonths() { return getMonths(false); } /** * Returns an array of month names. * * @param shortened a flag indicating that shortened month names should * be returned. * * @return an array of month names. */ public static String[] getMonths(boolean shortened) { if (shortened) { return DATE_FORMAT_SYMBOLS.getShortMonths(); } else { return DATE_FORMAT_SYMBOLS.getMonths(); } } /** * Returns true if the supplied integer code represents a valid month. * * @param code the code being checked for validity. * * @return {@code true} if the supplied integer code represents a * valid month. */ public static boolean isValidMonthCode(int code) { switch(code) { case JANUARY: case FEBRUARY: case MARCH: case APRIL: case MAY: case JUNE: case JULY: case AUGUST: case SEPTEMBER: case OCTOBER: case NOVEMBER: case DECEMBER: return true; default: return false; } } /** * Returns the quarter for the specified month. * * @param code the month code (1-12). * * @return the quarter that the month belongs to. */ public static int monthCodeToQuarter(int code) { switch(code) { case JANUARY: case FEBRUARY: case MARCH: return 1; case APRIL: case MAY: case JUNE: return 2; case JULY: case AUGUST: case SEPTEMBER: return 3; case OCTOBER: case NOVEMBER: case DECEMBER: return 4; default: throw new IllegalArgumentException( "SerialDate.monthCodeToQuarter: invalid month code."); } } /** * Returns a string representing the supplied month. *

* The string returned is the long form of the month name taken from the * default locale. * * @param month the month. * * @return a string representing the supplied month. */ public static String monthCodeToString(int month) { return monthCodeToString(month, false); } /** * Returns a string representing the supplied month. *

* The string returned is the long or short form of the month name taken * from the default locale. * * @param month the month. * @param shortened if {@code true} return the abbreviation of the month. * * @return a string representing the supplied month. */ public static String monthCodeToString(int month, boolean shortened) { // check arguments... if (!isValidMonthCode(month)) { throw new IllegalArgumentException( "SerialDate.monthCodeToString: month outside valid range."); } final String[] months; if (shortened) { months = DATE_FORMAT_SYMBOLS.getShortMonths(); } else { months = DATE_FORMAT_SYMBOLS.getMonths(); } return months[month - 1]; } /** * Converts a string to a month code. *

* This method will return one of the constants JANUARY, FEBRUARY, ..., * DECEMBER that corresponds to the string. If the string is not * recognised, this method returns -1. * * @param s the string to parse. * * @return {@code -1} if the string is not parseable, the month of the * year otherwise. */ public static int stringToMonthCode(String s) { final String[] shortMonthNames = DATE_FORMAT_SYMBOLS.getShortMonths(); final String[] monthNames = DATE_FORMAT_SYMBOLS.getMonths(); int result = -1; s = s.trim(); // first try parsing the string as an integer (1-12)... try { result = Integer.parseInt(s); } catch (NumberFormatException e) { // suppress } // now search through the month names... if ((result < 1) || (result > 12)) { for (int i = 0; i < monthNames.length; i++) { if (s.equals(shortMonthNames[i])) { result = i + 1; break; } if (s.equals(monthNames[i])) { result = i + 1; break; } } } return result; } /** * Returns true if the supplied integer code represents a valid * week-in-the-month, and false otherwise. * * @param code the code being checked for validity. * @return {@code true} if the supplied integer code represents a * valid week-in-the-month. */ public static boolean isValidWeekInMonthCode(int code) { switch(code) { case FIRST_WEEK_IN_MONTH: case SECOND_WEEK_IN_MONTH: case THIRD_WEEK_IN_MONTH: case FOURTH_WEEK_IN_MONTH: case LAST_WEEK_IN_MONTH: return true; default: return false; } } /** * Determines whether or not the specified year is a leap year. * * @param yyyy the year (in the range 1900 to 9999). * * @return {@code true} if the specified year is a leap year. */ public static boolean isLeapYear(int yyyy) { if ((yyyy % 4) != 0) { return false; } else if ((yyyy % 400) == 0) { return true; } else if ((yyyy % 100) == 0) { return false; } else { return true; } } /** * Returns the number of leap years from 1900 to the specified year * INCLUSIVE. *

* Note that 1900 is not a leap year. * * @param yyyy the year (in the range 1900 to 9999). * * @return the number of leap years from 1900 to the specified year. */ public static int leapYearCount(int yyyy) { int leap4 = (yyyy - 1896) / 4; int leap100 = (yyyy - 1800) / 100; int leap400 = (yyyy - 1600) / 400; return leap4 - leap100 + leap400; } /** * Returns the number of the last day of the month, taking into account * leap years. * * @param month the month. * @param yyyy the year (in the range 1900 to 9999). * * @return the number of the last day of the month. */ public static int lastDayOfMonth(int month, int yyyy) { final int result = LAST_DAY_OF_MONTH[month]; if (month != FEBRUARY) { return result; } else if (isLeapYear(yyyy)) { return result + 1; } else { return result; } } /** * Creates a new date by adding the specified number of days to the base * date. * * @param days the number of days to add (can be negative). * @param base the base date. * * @return a new date. */ public static SerialDate addDays(int days, SerialDate base) { int serialDayNumber = base.toSerial() + days; return SerialDate.createInstance(serialDayNumber); } /** * Creates a new date by adding the specified number of months to the base * date. *

* If the base date is close to the end of the month, the day on the result * may be adjusted slightly: 31 May + 1 month = 30 June. * * @param months the number of months to add (can be negative). * @param base the base date. * * @return a new date. */ public static SerialDate addMonths(int months, SerialDate base) { int yy = (12 * base.getYYYY() + base.getMonth() + months - 1) / 12; int mm = (12 * base.getYYYY() + base.getMonth() + months - 1) % 12 + 1; int dd = Math.min(base.getDayOfMonth(), SerialDate.lastDayOfMonth(mm, yy)); return SerialDate.createInstance(dd, mm, yy); } /** * Creates a new date by adding the specified number of years to the base * date. * * @param years the number of years to add (can be negative). * @param base the base date. * * @return A new date. */ public static SerialDate addYears(int years, SerialDate base) { int baseY = base.getYYYY(); int baseM = base.getMonth(); int baseD = base.getDayOfMonth(); int targetY = baseY + years; int targetD = Math.min(baseD, SerialDate.lastDayOfMonth(baseM, targetY)); return SerialDate.createInstance(targetD, baseM, targetY); } /** * Returns the latest date that falls on the specified day-of-the-week and * is BEFORE the base date. * * @param targetWeekday a code for the target day-of-the-week. * @param base the base date. * * @return the latest date that falls on the specified day-of-the-week and * is BEFORE the base date. */ public static SerialDate getPreviousDayOfWeek(int targetWeekday, SerialDate base) { // check arguments... if (!SerialDate.isValidWeekdayCode(targetWeekday)) { throw new IllegalArgumentException("Invalid day-of-the-week code."); } // find the date... int adjust; int baseDOW = base.getDayOfWeek(); if (baseDOW > targetWeekday) { adjust = Math.min(0, targetWeekday - baseDOW); } else { adjust = -7 + Math.max(0, targetWeekday - baseDOW); } return SerialDate.addDays(adjust, base); } /** * Returns the earliest date that falls on the specified day-of-the-week * and is AFTER the base date. * * @param targetWeekday a code for the target day-of-the-week. * @param base the base date. * * @return the earliest date that falls on the specified day-of-the-week * and is AFTER the base date. */ public static SerialDate getFollowingDayOfWeek(int targetWeekday, SerialDate base) { // check arguments... if (!SerialDate.isValidWeekdayCode(targetWeekday)) { throw new IllegalArgumentException( "Invalid day-of-the-week code." ); } // find the date... int adjust; int baseDOW = base.getDayOfWeek(); if (baseDOW > targetWeekday) { adjust = 7 + Math.min(0, targetWeekday - baseDOW); } else { adjust = Math.max(0, targetWeekday - baseDOW); } return SerialDate.addDays(adjust, base); } /** * Returns the date that falls on the specified day-of-the-week and is * CLOSEST to the base date. * * @param targetDOW a code for the target day-of-the-week. * @param base the base date. * * @return the date that falls on the specified day-of-the-week and is * CLOSEST to the base date. */ public static SerialDate getNearestDayOfWeek(int targetDOW, SerialDate base) { // check arguments... if (!SerialDate.isValidWeekdayCode(targetDOW)) { throw new IllegalArgumentException("Invalid day-of-the-week code."); } // find the date... final int baseDOW = base.getDayOfWeek(); int adjust = -Math.abs(targetDOW - baseDOW); if (adjust >= 4) { adjust = 7 - adjust; } if (adjust <= -4) { adjust = 7 + adjust; } return SerialDate.addDays(adjust, base); } /** * Rolls the date forward to the last day of the month. * * @param base the base date. * * @return a new serial date. */ public SerialDate getEndOfCurrentMonth(SerialDate base) { int last = SerialDate.lastDayOfMonth(base.getMonth(), base.getYYYY()); return SerialDate.createInstance(last, base.getMonth(), base.getYYYY()); } /** * Returns a string corresponding to the week-in-the-month code. *

* Need to find a better approach. * * @param count an integer code representing the week-in-the-month. * * @return a string corresponding to the week-in-the-month code. */ public static String weekInMonthToString(int count) { switch (count) { case SerialDate.FIRST_WEEK_IN_MONTH : return "First"; case SerialDate.SECOND_WEEK_IN_MONTH : return "Second"; case SerialDate.THIRD_WEEK_IN_MONTH : return "Third"; case SerialDate.FOURTH_WEEK_IN_MONTH : return "Fourth"; case SerialDate.LAST_WEEK_IN_MONTH : return "Last"; default : return "SerialDate.weekInMonthToString(): invalid code."; } } /** * Returns a string representing the supplied 'relative'. *

* Need to find a better approach. * * @param relative a constant representing the 'relative'. * * @return a string representing the supplied 'relative'. */ public static String relativeToString(int relative) { switch (relative) { case SerialDate.PRECEDING : return "Preceding"; case SerialDate.NEAREST : return "Nearest"; case SerialDate.FOLLOWING : return "Following"; default : return "ERROR : Relative To String"; } } /** * Factory method that returns an instance of some concrete subclass of * {@link SerialDate}. * * @param day the day (1-31). * @param month the month (1-12). * @param yyyy the year (in the range 1900 to 9999). * * @return An instance of {@link SerialDate}. */ public static SerialDate createInstance(int day, int month, int yyyy) { return new SpreadsheetDate(day, month, yyyy); } /** * Factory method that returns an instance of some concrete subclass of * {@link SerialDate}. * * @param serial the serial number for the day (1 January 1900 = 2). * * @return a instance of SerialDate. */ public static SerialDate createInstance(int serial) { return new SpreadsheetDate(serial); } /** * Factory method that returns an instance of a subclass of SerialDate. * * @param date A Java date object. * * @return a instance of SerialDate. */ public static SerialDate createInstance(java.util.Date date) { GregorianCalendar calendar = new GregorianCalendar(); calendar.setTime(date); return new SpreadsheetDate(calendar.get(Calendar.DATE), calendar.get(Calendar.MONTH) + 1, calendar.get(Calendar.YEAR)); } /** * Returns the serial number for the date, where 1 January 1900 = 2 (this * corresponds, almost, to the numbering system used in Microsoft Excel for * Windows and Lotus 1-2-3). * * @return the serial number for the date. */ public abstract int toSerial(); /** * Returns a java.util.Date. Since java.util.Date has more precision than * SerialDate, we need to define a convention for the 'time of day'. * * @return this as {@code java.util.Date}. */ public abstract java.util.Date toDate(); /** * Returns the description that is attached to the date. It is not * required that a date have a description, but for some applications it * is useful. * * @return The description (possibly {@code null}). */ public String getDescription() { return this.description; } /** * Sets the description for the date. * * @param description the description for this date ({@code null} * permitted). */ public void setDescription(String description) { this.description = description; } /** * Converts the date to a string. * * @return a string representation of the date. */ @Override public String toString() { return getDayOfMonth() + "-" + SerialDate.monthCodeToString(getMonth()) + "-" + getYYYY(); } /** * Returns the year (assume a valid range of 1900 to 9999). * * @return the year. */ public abstract int getYYYY(); /** * Returns the month (January = 1, February = 2, March = 3). * * @return the month of the year. */ public abstract int getMonth(); /** * Returns the day of the month. * * @return the day of the month. */ public abstract int getDayOfMonth(); /** * Returns the day of the week. * * @return the day of the week. */ public abstract int getDayOfWeek(); /** * Returns the difference (in days) between this date and the specified * 'other' date. *

* The result is positive if this date is after the 'other' date and * negative if it is before the 'other' date. * * @param other the date being compared to. * * @return the difference between this and the other date. */ public abstract int compare(SerialDate other); /** * Returns true if this SerialDate represents the same date as the * specified SerialDate. * * @param other the date being compared to. * * @return {@code true} if this SerialDate represents the same date as * the specified SerialDate. */ public abstract boolean isOn(SerialDate other); /** * Returns true if this SerialDate represents an earlier date compared to * the specified SerialDate. * * @param other The date being compared to. * * @return {@code true} if this SerialDate represents an earlier date * compared to the specified SerialDate. */ public abstract boolean isBefore(SerialDate other); /** * Returns true if this SerialDate represents the same date as the * specified SerialDate. * * @param other the date being compared to. * * @return {@code true} if this SerialDate represents the same date * as the specified SerialDate. */ public abstract boolean isOnOrBefore(SerialDate other); /** * Returns true if this SerialDate represents the same date as the * specified SerialDate. * * @param other the date being compared to. * * @return {@code true} if this SerialDate represents the same date * as the specified SerialDate. */ public abstract boolean isAfter(SerialDate other); /** * Returns true if this SerialDate represents the same date as the * specified SerialDate. * * @param other the date being compared to. * * @return {@code true} if this SerialDate represents the same date * as the specified SerialDate. */ public abstract boolean isOnOrAfter(SerialDate other); /** * Returns {@code true} if this {@link SerialDate} is within the * specified range (INCLUSIVE). The date order of d1 and d2 is not * important. * * @param d1 a boundary date for the range. * @param d2 the other boundary date for the range. * * @return A boolean. */ public abstract boolean isInRange(SerialDate d1, SerialDate d2); /** * Returns {@code true} if this {@link SerialDate} is within the * specified range (caller specifies whether or not the end-points are * included). The date order of d1 and d2 is not important. * * @param d1 a boundary date for the range. * @param d2 the other boundary date for the range. * @param include a code that controls whether or not the start and end * dates are included in the range. * * @return A boolean. */ public abstract boolean isInRange(SerialDate d1, SerialDate d2, int include); /** * Returns the latest date that falls on the specified day-of-the-week and * is BEFORE this date. * * @param targetDOW a code for the target day-of-the-week. * * @return the latest date that falls on the specified day-of-the-week and * is BEFORE this date. */ public SerialDate getPreviousDayOfWeek(int targetDOW) { return getPreviousDayOfWeek(targetDOW, this); } /** * Returns the earliest date that falls on the specified day-of-the-week * and is AFTER this date. * * @param targetDOW a code for the target day-of-the-week. * * @return the earliest date that falls on the specified day-of-the-week * and is AFTER this date. */ public SerialDate getFollowingDayOfWeek(int targetDOW) { return getFollowingDayOfWeek(targetDOW, this); } /** * Returns the nearest date that falls on the specified day-of-the-week. * * @param targetDOW a code for the target day-of-the-week. * * @return the nearest date that falls on the specified day-of-the-week. */ public SerialDate getNearestDayOfWeek(int targetDOW) { return getNearestDayOfWeek(targetDOW, this); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/date/SpreadsheetDate.java000066400000000000000000000331541463604235500277110ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.date; import java.util.Calendar; import java.util.Date; /** * Represents a date using an integer, in a similar fashion to the * implementation in Microsoft Excel. The range of dates supported is * 1-Jan-1900 to 31-Dec-9999. *

* Be aware that there is a deliberate bug in Excel that recognises the year * 1900 as a leap year when in fact it is not a leap year. You can find more * information on the Microsoft website in article Q181370: *

* http://support.microsoft.com/support/kb/articles/Q181/3/70.asp *

* Excel uses the convention that 1-Jan-1900 = 1. This class uses the * convention 1-Jan-1900 = 2. * The result is that the day number in this class will be different to the * Excel figure for January and February 1900...but then Excel adds in an extra * day (29-Feb-1900 which does not actually exist!) and from that point forward * the day numbers will match. */ public class SpreadsheetDate extends SerialDate { /** For serialization. */ private static final long serialVersionUID = -2039586705374454461L; /** * The day number (1-Jan-1900 = 2, 2-Jan-1900 = 3, ..., 31-Dec-9999 = * 2958465). */ private final int serial; /** The day of the month (1 to 28, 29, 30 or 31 depending on the month). */ private final int day; /** The month of the year (1 to 12). */ private final int month; /** The year (1900 to 9999). */ private final int year; /** * Creates a new date instance. * * @param day the day (in the range 1 to 28/29/30/31). * @param month the month (in the range 1 to 12). * @param year the year (in the range 1900 to 9999). */ public SpreadsheetDate(int day, int month, int year) { if ((year >= 1900) && (year <= 9999)) { this.year = year; } else { throw new IllegalArgumentException( "The 'year' argument must be in range 1900 to 9999."); } if ((month >= MonthConstants.JANUARY) && (month <= MonthConstants.DECEMBER)) { this.month = month; } else { throw new IllegalArgumentException( "The 'month' argument must be in the range 1 to 12."); } if ((day >= 1) && (day <= SerialDate.lastDayOfMonth(month, year))) { this.day = day; } else { throw new IllegalArgumentException("Invalid 'day' argument."); } // the serial number needs to be synchronised with the day-month-year... this.serial = calcSerial(day, month, year); } /** * Standard constructor - creates a new date object representing the * specified day number (which should be in the range 2 to 2958465. * * @param serial the serial number for the day (range: 2 to 2958465). */ public SpreadsheetDate(int serial) { if ((serial >= SERIAL_LOWER_BOUND) && (serial <= SERIAL_UPPER_BOUND)) { this.serial = serial; } else { throw new IllegalArgumentException( "SpreadsheetDate: Serial must be in range 2 to 2958465."); } // the day-month-year needs to be synchronised with the serial number... // get the year from the serial date final int days = this.serial - SERIAL_LOWER_BOUND; // overestimated because we ignored leap days final int overestimatedYYYY = 1900 + (days / 365); final int leaps = SerialDate.leapYearCount(overestimatedYYYY); final int nonleapdays = days - leaps; // underestimated because we overestimated years int underestimatedYYYY = 1900 + (nonleapdays / 365); if (underestimatedYYYY == overestimatedYYYY) { this.year = underestimatedYYYY; } else { int ss1 = calcSerial(1, 1, underestimatedYYYY); while (ss1 <= this.serial) { underestimatedYYYY = underestimatedYYYY + 1; ss1 = calcSerial(1, 1, underestimatedYYYY); } this.year = underestimatedYYYY - 1; } final int ss2 = calcSerial(1, 1, this.year); int[] daysToEndOfPrecedingMonth = AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH; if (isLeapYear(this.year)) { daysToEndOfPrecedingMonth = LEAP_YEAR_AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH; } // get the month from the serial date int mm = 1; int sss = ss2 + daysToEndOfPrecedingMonth[mm] - 1; while (sss < this.serial) { mm = mm + 1; sss = ss2 + daysToEndOfPrecedingMonth[mm] - 1; } this.month = mm - 1; // what's left is d(+1); this.day = this.serial - ss2 - daysToEndOfPrecedingMonth[this.month] + 1; } /** * Returns the serial number for the date, where 1 January 1900 = 2 * (this corresponds, almost, to the numbering system used in Microsoft * Excel for Windows and Lotus 1-2-3). * * @return The serial number of this date. */ @Override public int toSerial() { return this.serial; } /** * Returns a {@code java.util.Date} equivalent to this date. * * @return The date. */ @Override public Date toDate() { Calendar calendar = Calendar.getInstance(); calendar.set(getYYYY(), getMonth() - 1, getDayOfMonth(), 0, 0, 0); return calendar.getTime(); } /** * Returns the year (assume a valid range of 1900 to 9999). * * @return The year. */ @Override public int getYYYY() { return this.year; } /** * Returns the month (January = 1, February = 2, March = 3). * * @return The month of the year. */ @Override public int getMonth() { return this.month; } /** * Returns the day of the month. * * @return The day of the month. */ @Override public int getDayOfMonth() { return this.day; } /** * Returns a code representing the day of the week. *

* The codes are defined in the {@link SerialDate} class as: * {@code SUNDAY}, {@code MONDAY}, {@code TUESDAY}, * {@code WEDNESDAY}, {@code THURSDAY}, {@code FRIDAY}, and * {@code SATURDAY}. * * @return A code representing the day of the week. */ @Override public int getDayOfWeek() { return (this.serial + 6) % 7 + 1; } /** * Tests the equality of this date with an arbitrary object. *

* This method will return true ONLY if the object is an instance of the * {@link SerialDate} base class, and it represents the same day as this * {@link SpreadsheetDate}. * * @param object the object to compare ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object object) { if (object instanceof SerialDate) { SerialDate s = (SerialDate) object; return (s.toSerial() == this.toSerial()); } else { return false; } } /** * Returns a hash code for this object instance. * * @return A hash code. */ @Override public int hashCode() { return toSerial(); } /** * Returns the difference (in days) between this date and the specified * 'other' date. * * @param other the date being compared to. * * @return The difference (in days) between this date and the specified * 'other' date. */ @Override public int compare(SerialDate other) { return this.serial - other.toSerial(); } /** * Implements the method required by the Comparable interface. * * @param other the other object (usually another SerialDate). * * @return A negative integer, zero, or a positive integer as this object * is less than, equal to, or greater than the specified object. */ @Override public int compareTo(Object other) { return compare((SerialDate) other); } /** * Returns true if this SerialDate represents the same date as the * specified SerialDate. * * @param other the date being compared to. * * @return {@code true} if this SerialDate represents the same date as * the specified SerialDate. */ @Override public boolean isOn(SerialDate other) { return (this.serial == other.toSerial()); } /** * Returns true if this SerialDate represents an earlier date compared to * the specified SerialDate. * * @param other the date being compared to. * * @return {@code true} if this SerialDate represents an earlier date * compared to the specified SerialDate. */ @Override public boolean isBefore(SerialDate other) { return (this.serial < other.toSerial()); } /** * Returns true if this SerialDate represents the same date as the * specified SerialDate. * * @param other the date being compared to. * * @return {@code true} if this SerialDate represents the same date * as the specified SerialDate. */ @Override public boolean isOnOrBefore(SerialDate other) { return (this.serial <= other.toSerial()); } /** * Returns true if this SerialDate represents the same date as the * specified SerialDate. * * @param other the date being compared to. * * @return {@code true} if this SerialDate represents the same date * as the specified SerialDate. */ @Override public boolean isAfter(SerialDate other) { return (this.serial > other.toSerial()); } /** * Returns true if this SerialDate represents the same date as the * specified SerialDate. * * @param other the date being compared to. * * @return {@code true} if this SerialDate represents the same date as * the specified SerialDate. */ @Override public boolean isOnOrAfter(SerialDate other) { return (this.serial >= other.toSerial()); } /** * Returns {@code true} if this {@link SerialDate} is within the * specified range (INCLUSIVE). The date order of d1 and d2 is not * important. * * @param d1 a boundary date for the range. * @param d2 the other boundary date for the range. * * @return A boolean. */ @Override public boolean isInRange(SerialDate d1, SerialDate d2) { return isInRange(d1, d2, SerialDate.INCLUDE_BOTH); } /** * Returns true if this SerialDate is within the specified range (caller * specifies whether or not the end-points are included). The order of d1 * and d2 is not important. * * @param d1 one boundary date for the range. * @param d2 a second boundary date for the range. * @param include a code that controls whether or not the start and end * dates are included in the range. * * @return {@code true} if this SerialDate is within the specified * range. */ @Override public boolean isInRange(SerialDate d1, SerialDate d2, int include) { int s1 = d1.toSerial(); int s2 = d2.toSerial(); int start = Math.min(s1, s2); int end = Math.max(s1, s2); int s = toSerial(); if (include == SerialDate.INCLUDE_BOTH) { return (s >= start && s <= end); } else if (include == SerialDate.INCLUDE_FIRST) { return (s >= start && s < end); } else if (include == SerialDate.INCLUDE_SECOND) { return (s > start && s <= end); } else { return (s > start && s < end); } } /** * Calculate the serial number from the day, month and year. *

* 1-Jan-1900 = 2. * * @param d the day. * @param m the month. * @param y the year. * * @return the serial number from the day, month and year. */ private int calcSerial(int d, int m, int y) { int yy = ((y - 1900) * 365) + SerialDate.leapYearCount(y - 1); int mm = SerialDate.AGGREGATE_DAYS_TO_END_OF_PRECEDING_MONTH[m]; if (m > MonthConstants.FEBRUARY) { if (SerialDate.isLeapYear(y)) { mm = mm + 1; } } int dd = d; return yy + mm + dd + 1; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/date/package-info.java000066400000000000000000000001451463604235500271620ustar00rootroot00000000000000/** * Date-related classes formerly in the JCommon class library. */ package org.jfree.chart.date; jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/editor/000077500000000000000000000000001463604235500243445ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/editor/ChartEditor.java000066400000000000000000000036051463604235500274230ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * ChartEditor.java * ---------------- * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): ; * */ package org.jfree.chart.editor; import javax.swing.JComponent; import org.jfree.chart.JFreeChart; /** * A chart editor is typically a {@link JComponent} containing a user interface * for modifying the properties of a chart. * * @see ChartEditorManager#getChartEditor(JFreeChart) */ public interface ChartEditor { /** * Applies the changes to the specified chart. * * @param chart the chart. */ void updateChart(JFreeChart chart); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/editor/ChartEditorFactory.java000066400000000000000000000034571463604235500307600ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * ChartEditorFactory.java * ----------------------- * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): ; * */ package org.jfree.chart.editor; import org.jfree.chart.JFreeChart; /** * A factory for creating new {@link ChartEditor} instances. */ public interface ChartEditorFactory { /** * Creates an editor for the given chart. * * @param chart the chart. * * @return A chart editor. */ ChartEditor createEditor(JFreeChart chart); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/editor/ChartEditorManager.java000066400000000000000000000056071463604235500307220ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * ChartEditorManager.java * ----------------------- * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): ; * */ package org.jfree.chart.editor; import org.jfree.chart.JFreeChart; import org.jfree.chart.util.Args; /** * The central point for obtaining {@link ChartEditor} instances for editing * charts. Right now, the API is minimal - the plan is to extend this class * to provide customisation options for chart editors (for example, make some * editor items read-only). */ public class ChartEditorManager { /** This factory creates new {@link ChartEditor} instances as required. */ static ChartEditorFactory factory = new DefaultChartEditorFactory(); /** * Private constructor prevents instantiation. */ private ChartEditorManager() { // nothing to do } /** * Returns the current factory. * * @return The current factory (never {@code null}). */ public static ChartEditorFactory getChartEditorFactory() { return factory; } /** * Sets the chart editor factory. * * @param f the new factory ({@code null} not permitted). */ public static void setChartEditorFactory(ChartEditorFactory f) { Args.nullNotPermitted(f, "f"); factory = f; } /** * Returns a component that can be used to edit the given chart. * * @param chart the chart. * * @return The chart editor. */ public static ChartEditor getChartEditor(JFreeChart chart) { return factory.createEditor(chart); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/editor/DefaultAxisEditor.java000066400000000000000000000417741463604235500306040ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * DefaultAxisEditor.java * ---------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Andrzej Porebski; * Arnaud Lelievre; * */ package org.jfree.chart.editor; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Font; import java.awt.Paint; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ResourceBundle; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JColorChooser; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTabbedPane; import javax.swing.JTextField; import org.jfree.chart.axis.Axis; import org.jfree.chart.axis.LogAxis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.ui.FontChooserPanel; import org.jfree.chart.ui.FontDisplayField; import org.jfree.chart.ui.LCBLayout; import org.jfree.chart.ui.PaintSample; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.util.ResourceBundleWrapper; /** * A panel for editing the properties of an axis. */ class DefaultAxisEditor extends JPanel implements ActionListener { /** The axis label. */ private JTextField label; /** The label font. */ private Font labelFont; /** The label paint. */ private PaintSample labelPaintSample; /** A field showing a description of the label font. */ private JTextField labelFontField; /** The font for displaying tick labels on the axis. */ private Font tickLabelFont; /** * A field containing a description of the font for displaying tick labels * on the axis. */ private JTextField tickLabelFontField; /** The paint (color) for the tick labels. */ private PaintSample tickLabelPaintSample; /** * An empty sub-panel for extending the user interface to handle more * complex axes. */ private JPanel slot1; /** * An empty sub-panel for extending the user interface to handle more * complex axes. */ private JPanel slot2; /** A flag that indicates whether or not the tick labels are visible. */ private JCheckBox showTickLabelsCheckBox; /** A flag that indicates whether or not the tick marks are visible. */ private JCheckBox showTickMarksCheckBox; // /** Insets text field. */ // private InsetsTextField tickLabelInsetsTextField; // // /** Label insets text field. */ // private InsetsTextField labelInsetsTextField; /** The tick label insets. */ private RectangleInsets tickLabelInsets; /** The label insets. */ private RectangleInsets labelInsets; /** A tabbed pane for... */ private JTabbedPane otherTabs; /** The resourceBundle for the localization. */ protected static ResourceBundle localizationResources = ResourceBundleWrapper.getBundle( "org.jfree.chart.editor.LocalizationBundle"); /** * A static method that returns a panel that is appropriate for the axis * type. * * @param axis the axis whose properties are to be displayed/edited in * the panel. * * @return A panel or {@code null} if axis is {@code null}. */ public static DefaultAxisEditor getInstance(Axis axis) { if (axis != null) { // figure out what type of axis we have and instantiate the // appropriate panel if (axis instanceof NumberAxis) { return new DefaultNumberAxisEditor((NumberAxis) axis); } if (axis instanceof LogAxis) { return new DefaultLogAxisEditor((LogAxis) axis); } else { return new DefaultAxisEditor(axis); } } else { return null; } } /** * Standard constructor: builds a panel for displaying/editing the * properties of the specified axis. * * @param axis the axis whose properties are to be displayed/edited in * the panel. */ public DefaultAxisEditor(Axis axis) { this.labelFont = axis.getLabelFont(); this.labelPaintSample = new PaintSample(axis.getLabelPaint()); this.tickLabelFont = axis.getTickLabelFont(); this.tickLabelPaintSample = new PaintSample(axis.getTickLabelPaint()); // Insets values this.tickLabelInsets = axis.getTickLabelInsets(); this.labelInsets = axis.getLabelInsets(); setLayout(new BorderLayout()); JPanel general = new JPanel(new BorderLayout()); general.setBorder( BorderFactory.createTitledBorder( BorderFactory.createEtchedBorder(), localizationResources.getString("General") ) ); JPanel interior = new JPanel(new LCBLayout(5)); interior.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5)); interior.add(new JLabel(localizationResources.getString("Label"))); this.label = new JTextField(axis.getLabel()); interior.add(this.label); interior.add(new JPanel()); interior.add(new JLabel(localizationResources.getString("Font"))); this.labelFontField = new FontDisplayField(this.labelFont); interior.add(this.labelFontField); JButton b = new JButton(localizationResources.getString("Select...")); b.setActionCommand("SelectLabelFont"); b.addActionListener(this); interior.add(b); interior.add(new JLabel(localizationResources.getString("Paint"))); interior.add(this.labelPaintSample); b = new JButton(localizationResources.getString("Select...")); b.setActionCommand("SelectLabelPaint"); b.addActionListener(this); interior.add(b); // interior.add( // new JLabel(localizationResources.getString("Label_Insets")) // ); // b = new JButton(localizationResources.getString("Edit...")); // b.setActionCommand("LabelInsets"); // b.addActionListener(this); // this.labelInsetsTextField = new InsetsTextField(this.labelInsets); // interior.add(this.labelInsetsTextField); // interior.add(b); // // interior.add( // new JLabel(localizationResources.getString("Tick_Label_Insets")) // ); // b = new JButton(localizationResources.getString("Edit...")); // b.setActionCommand("TickLabelInsets"); // b.addActionListener(this); // this.tickLabelInsetsTextField // = new InsetsTextField(this.tickLabelInsets); // interior.add(this.tickLabelInsetsTextField); // interior.add(b); general.add(interior); add(general, BorderLayout.NORTH); this.slot1 = new JPanel(new BorderLayout()); JPanel other = new JPanel(new BorderLayout()); other.setBorder(BorderFactory.createTitledBorder( BorderFactory.createEtchedBorder(), localizationResources.getString("Other"))); this.otherTabs = new JTabbedPane(); this.otherTabs.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5)); JPanel ticks = new JPanel(new LCBLayout(3)); ticks.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); this.showTickLabelsCheckBox = new JCheckBox( localizationResources.getString("Show_tick_labels"), axis.isTickLabelsVisible() ); ticks.add(this.showTickLabelsCheckBox); ticks.add(new JPanel()); ticks.add(new JPanel()); ticks.add( new JLabel(localizationResources.getString("Tick_label_font")) ); this.tickLabelFontField = new FontDisplayField(this.tickLabelFont); ticks.add(this.tickLabelFontField); b = new JButton(localizationResources.getString("Select...")); b.setActionCommand("SelectTickLabelFont"); b.addActionListener(this); ticks.add(b); this.showTickMarksCheckBox = new JCheckBox( localizationResources.getString("Show_tick_marks"), axis.isTickMarksVisible() ); ticks.add(this.showTickMarksCheckBox); ticks.add(new JPanel()); ticks.add(new JPanel()); this.otherTabs.add(localizationResources.getString("Ticks"), ticks); other.add(this.otherTabs); this.slot1.add(other); this.slot2 = new JPanel(new BorderLayout()); this.slot2.add(this.slot1, BorderLayout.NORTH); add(this.slot2); } /** * Returns the current axis label. * * @return The current axis label. */ public String getLabel() { return this.label.getText(); } /** * Returns the current label font. * * @return The current label font. */ public Font getLabelFont() { return this.labelFont; } /** * Returns the current label paint. * * @return The current label paint. */ public Paint getLabelPaint() { return this.labelPaintSample.getPaint(); } /** * Returns a flag that indicates whether or not the tick labels are visible. * * @return {@code true} if tick mark labels are visible. */ public boolean isTickLabelsVisible() { return this.showTickLabelsCheckBox.isSelected(); } /** * Returns the font used to draw the tick labels (if they are showing). * * @return The font used to draw the tick labels. */ public Font getTickLabelFont() { return this.tickLabelFont; } /** * Returns the current tick label paint. * * @return The current tick label paint. */ public Paint getTickLabelPaint() { return this.tickLabelPaintSample.getPaint(); } /** * Returns the current value of the flag that determines whether or not * tick marks are visible. * * @return {@code true} if tick marks are visible. */ public boolean isTickMarksVisible() { return this.showTickMarksCheckBox.isSelected(); } /** * Returns the current tick label insets value * * @return The current tick label insets value. */ public RectangleInsets getTickLabelInsets() { return (this.tickLabelInsets == null) ? new RectangleInsets(0, 0, 0, 0) : this.tickLabelInsets; } /** * Returns the current label insets value * * @return The current label insets value. */ public RectangleInsets getLabelInsets() { return (this.labelInsets == null) ? new RectangleInsets(0, 0, 0, 0) : this.labelInsets; } /** * Returns a reference to the tabbed pane. * * @return A reference to the tabbed pane. */ public JTabbedPane getOtherTabs() { return this.otherTabs; } /** * Handles user interaction with the property panel. * * @param event information about the event that triggered the call to * this method. */ @Override public void actionPerformed(ActionEvent event) { String command = event.getActionCommand(); if (command.equals("SelectLabelFont")) { attemptLabelFontSelection(); } else if (command.equals("SelectLabelPaint")) { attemptModifyLabelPaint(); } else if (command.equals("SelectTickLabelFont")) { attemptTickLabelFontSelection(); } // else if (command.equals("LabelInsets")) { // editLabelInsets(); // } // else if (command.equals("TickLabelInsets")) { // editTickLabelInsets(); // } } /** * Presents a font selection dialog to the user. */ private void attemptLabelFontSelection() { FontChooserPanel panel = new FontChooserPanel(this.labelFont); int result = JOptionPane.showConfirmDialog(this, panel, localizationResources.getString("Font_Selection"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE); if (result == JOptionPane.OK_OPTION) { this.labelFont = panel.getSelectedFont(); this.labelFontField.setText( this.labelFont.getFontName() + " " + this.labelFont.getSize() ); } } /** * Allows the user the opportunity to change the outline paint. */ private void attemptModifyLabelPaint() { Color c; c = JColorChooser.showDialog( this, localizationResources.getString("Label_Color"), Color.BLUE ); if (c != null) { this.labelPaintSample.setPaint(c); } } /** * Presents a tick label font selection dialog to the user. */ public void attemptTickLabelFontSelection() { FontChooserPanel panel = new FontChooserPanel(this.tickLabelFont); int result = JOptionPane.showConfirmDialog(this, panel, localizationResources.getString("Font_Selection"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE); if (result == JOptionPane.OK_OPTION) { this.tickLabelFont = panel.getSelectedFont(); this.tickLabelFontField.setText( this.tickLabelFont.getFontName() + " " + this.tickLabelFont.getSize() ); } } // /** // * Presents insets chooser panel allowing user to modify tick label's // * individual insets values. Updates the current insets text field if // * edit is accepted. // */ // private void editTickLabelInsets() { // InsetsChooserPanel panel = new InsetsChooserPanel( // this.tickLabelInsets); // int result = JOptionPane.showConfirmDialog( // this, panel, localizationResources.getString("Edit_Insets"), // JOptionPane.PLAIN_MESSAGE // ); // // if (result == JOptionPane.OK_OPTION) { // this.tickLabelInsets = panel.getInsets(); // this.tickLabelInsetsTextField.setInsets(this.tickLabelInsets); // } // } // // /** // * Presents insets chooser panel allowing user to modify label's // * individual insets values. Updates the current insets text field if edit // * is accepted. // */ // private void editLabelInsets() { // InsetsChooserPanel panel = new InsetsChooserPanel(this.labelInsets); // int result = JOptionPane.showConfirmDialog( // this, panel, localizationResources.getString("Edit_Insets"), // JOptionPane.PLAIN_MESSAGE // ); // // if (result == JOptionPane.OK_OPTION) { // this.labelInsets = panel.getInsets(); // this.labelInsetsTextField.setInsets(this.labelInsets); // } // } /** * Sets the properties of the specified axis to match the properties * defined on this panel. * * @param axis the axis. */ public void setAxisProperties(Axis axis) { axis.setLabel(getLabel()); axis.setLabelFont(getLabelFont()); axis.setLabelPaint(getLabelPaint()); axis.setTickMarksVisible(isTickMarksVisible()); // axis.setTickMarkStroke(getTickMarkStroke()); axis.setTickLabelsVisible(isTickLabelsVisible()); axis.setTickLabelFont(getTickLabelFont()); axis.setTickLabelPaint(getTickLabelPaint()); axis.setTickLabelInsets(getTickLabelInsets()); axis.setLabelInsets(getLabelInsets()); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/editor/DefaultChartEditor.java000066400000000000000000000226301463604235500307270ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * DefaultChartEditor.java * ----------------------- * (C) Copyright 2000-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Arnaud Lelievre; * Daniel Gredler; * */ package org.jfree.chart.editor; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Paint; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ResourceBundle; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JColorChooser; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTabbedPane; import javax.swing.JTextField; import org.jfree.chart.JFreeChart; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PolarPlot; import org.jfree.chart.title.Title; import org.jfree.chart.ui.LCBLayout; import org.jfree.chart.ui.PaintSample; import org.jfree.chart.util.ResourceBundleWrapper; /** * A panel for editing chart properties (includes subpanels for the title, * legend and plot). */ class DefaultChartEditor extends JPanel implements ActionListener, ChartEditor { /** A panel for displaying/editing the properties of the title. */ private DefaultTitleEditor titleEditor; /** A panel for displaying/editing the properties of the plot. */ private DefaultPlotEditor plotEditor; /** * A checkbox indicating whether or not the chart is drawn with * anti-aliasing. */ private JCheckBox antialias; /** The chart background color. */ private PaintSample background; /** The resourceBundle for the localization. */ protected static ResourceBundle localizationResources = ResourceBundleWrapper.getBundle( "org.jfree.chart.editor.LocalizationBundle"); /** * Standard constructor - the property panel is made up of a number of * sub-panels that are displayed in the tabbed pane. * * @param chart the chart, whichs properties should be changed. */ public DefaultChartEditor(JFreeChart chart) { setLayout(new BorderLayout()); JPanel other = new JPanel(new BorderLayout()); other.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); JPanel general = new JPanel(new BorderLayout()); general.setBorder(BorderFactory.createTitledBorder( BorderFactory.createEtchedBorder(), localizationResources.getString("General"))); JPanel interior = new JPanel(new LCBLayout(6)); interior.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5)); this.antialias = new JCheckBox(localizationResources.getString( "Draw_anti-aliased")); this.antialias.setSelected(chart.getAntiAlias()); interior.add(this.antialias); interior.add(new JLabel("")); interior.add(new JLabel("")); interior.add(new JLabel(localizationResources.getString( "Background_paint"))); this.background = new PaintSample(chart.getBackgroundPaint()); interior.add(this.background); JButton button = new JButton(localizationResources.getString( "Select...")); button.setActionCommand("BackgroundPaint"); button.addActionListener(this); interior.add(button); interior.add(new JLabel(localizationResources.getString( "Series_Paint"))); JTextField info = new JTextField(localizationResources.getString( "No_editor_implemented")); info.setEnabled(false); interior.add(info); button = new JButton(localizationResources.getString("Edit...")); button.setEnabled(false); interior.add(button); interior.add(new JLabel(localizationResources.getString( "Series_Stroke"))); info = new JTextField(localizationResources.getString( "No_editor_implemented")); info.setEnabled(false); interior.add(info); button = new JButton(localizationResources.getString("Edit...")); button.setEnabled(false); interior.add(button); interior.add(new JLabel(localizationResources.getString( "Series_Outline_Paint"))); info = new JTextField(localizationResources.getString( "No_editor_implemented")); info.setEnabled(false); interior.add(info); button = new JButton(localizationResources.getString("Edit...")); button.setEnabled(false); interior.add(button); interior.add(new JLabel(localizationResources.getString( "Series_Outline_Stroke"))); info = new JTextField(localizationResources.getString( "No_editor_implemented")); info.setEnabled(false); interior.add(info); button = new JButton(localizationResources.getString("Edit...")); button.setEnabled(false); interior.add(button); general.add(interior, BorderLayout.NORTH); other.add(general, BorderLayout.NORTH); JPanel parts = new JPanel(new BorderLayout()); Title title = chart.getTitle(); Plot plot = chart.getPlot(); JTabbedPane tabs = new JTabbedPane(); this.titleEditor = new DefaultTitleEditor(title); this.titleEditor.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); tabs.addTab(localizationResources.getString("Title"), this.titleEditor); if (plot instanceof PolarPlot) { this.plotEditor = new DefaultPolarPlotEditor((PolarPlot) plot); } else { this.plotEditor = new DefaultPlotEditor(plot); } this.plotEditor.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); tabs.addTab(localizationResources.getString("Plot"), this.plotEditor); tabs.add(localizationResources.getString("Other"), other); parts.add(tabs, BorderLayout.NORTH); add(parts); } /** * Returns a reference to the title editor. * * @return A panel for editing the title. */ public DefaultTitleEditor getTitleEditor() { return this.titleEditor; } /** * Returns a reference to the plot property sub-panel. * * @return A panel for editing the plot properties. */ public DefaultPlotEditor getPlotEditor() { return this.plotEditor; } /** * Returns the current setting of the anti-alias flag. * * @return {@code true} if anti-aliasing is enabled. */ public boolean getAntiAlias() { return this.antialias.isSelected(); } /** * Returns the current background paint. * * @return The current background paint. */ public Paint getBackgroundPaint() { return this.background.getPaint(); } /** * Handles user interactions with the panel. * * @param event a BackgroundPaint action. */ @Override public void actionPerformed(ActionEvent event) { String command = event.getActionCommand(); if (command.equals("BackgroundPaint")) { attemptModifyBackgroundPaint(); } } /** * Allows the user the opportunity to select a new background paint. Uses * JColorChooser, so we are only allowing a subset of all Paint objects to * be selected (fix later). */ private void attemptModifyBackgroundPaint() { Color c; c = JColorChooser.showDialog(this, localizationResources.getString( "Background_Color"), Color.BLUE); if (c != null) { this.background.setPaint(c); } } /** * Updates the properties of a chart to match the properties defined on the * panel. * * @param chart the chart. */ @Override public void updateChart(JFreeChart chart) { this.titleEditor.setTitleProperties(chart); this.plotEditor.updatePlotProperties(chart.getPlot()); chart.setAntiAlias(getAntiAlias()); chart.setBackgroundPaint(getBackgroundPaint()); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/editor/DefaultChartEditorFactory.java000066400000000000000000000041011463604235500322500ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------ * DefaultChartEditorFactory.java * ------------------------------ * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): ; * */ package org.jfree.chart.editor; import org.jfree.chart.JFreeChart; /** * A default implementation of the {@link ChartEditorFactory} interface. */ public class DefaultChartEditorFactory implements ChartEditorFactory { /** * Creates a new instance. */ public DefaultChartEditorFactory() { } /** * Returns a new instance of a {@link ChartEditor}. * * @param chart the chart. * * @return A chart editor for the given chart. */ @Override public ChartEditor createEditor(JFreeChart chart) { return new DefaultChartEditor(chart); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/editor/DefaultLogAxisEditor.java000066400000000000000000000120031463604235500312250ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * DefaultLogAxisEditor.java * ------------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: Martin Hoeller; * Contributor(s): -; * */ package org.jfree.chart.editor; import java.awt.event.ActionEvent; import java.awt.event.FocusEvent; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; import org.jfree.chart.axis.Axis; import org.jfree.chart.axis.LogAxis; import org.jfree.chart.axis.NumberTickUnit; /** * A panel for editing properties of a {@link LogAxis}. */ public class DefaultLogAxisEditor extends DefaultValueAxisEditor { private double manualTickUnitValue; private JTextField manualTickUnit; /** * Standard constructor: builds a property panel for the specified axis. * * @param axis the axis, which should be changed. */ public DefaultLogAxisEditor(LogAxis axis) { super(axis); this.manualTickUnitValue = axis.getTickUnit().getSize(); manualTickUnit.setText(Double.toString(this.manualTickUnitValue)); } /** * Creates a panel for editing the tick unit. * * @return A panel. */ @Override protected JPanel createTickUnitPanel() { JPanel tickUnitPanel = super.createTickUnitPanel(); tickUnitPanel.add(new JLabel(localizationResources.getString( "Manual_TickUnit_value"))); this.manualTickUnit = new JTextField(Double.toString( this.manualTickUnitValue)); this.manualTickUnit.setEnabled(!isAutoTickUnitSelection()); this.manualTickUnit.setActionCommand("TickUnitValue"); this.manualTickUnit.addActionListener(this); this.manualTickUnit.addFocusListener(this); tickUnitPanel.add(this.manualTickUnit); tickUnitPanel.add(new JPanel()); return tickUnitPanel; } /** * Handles actions from within the property panel. * * @param event an event. */ @Override public void actionPerformed(ActionEvent event) { String command = event.getActionCommand(); if (command.equals("TickUnitValue")) { validateTickUnit(); } else { // pass to the super-class for handling super.actionPerformed(event); } } @Override public void focusLost(FocusEvent event) { super.focusLost(event); if (event.getSource() == this.manualTickUnit) { validateTickUnit(); } } /** * Toggles the auto-tick-unit setting. */ @Override public void toggleAutoTick() { super.toggleAutoTick(); if (isAutoTickUnitSelection()) { this.manualTickUnit.setText(Double.toString(this.manualTickUnitValue)); this.manualTickUnit.setEnabled(false); } else { this.manualTickUnit.setEnabled(true); } } /** * Validates the tick unit entered. */ public void validateTickUnit() { double newTickUnit; try { newTickUnit = Double.parseDouble(this.manualTickUnit.getText()); } catch (NumberFormatException e) { newTickUnit = this.manualTickUnitValue; } if (newTickUnit > 0.0) { this.manualTickUnitValue = newTickUnit; } this.manualTickUnit.setText(Double.toString(this.manualTickUnitValue)); } /** * Sets the properties of the specified axis to match the properties * defined on this panel. * * @param axis the axis. */ @Override public void setAxisProperties(Axis axis) { super.setAxisProperties(axis); LogAxis logAxis = (LogAxis) axis; if (!isAutoTickUnitSelection()) { logAxis.setTickUnit(new NumberTickUnit(manualTickUnitValue)); } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/editor/DefaultNumberAxisEditor.java000066400000000000000000000133401463604235500317410ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------- * DefaultNumberAxisEditor.java * ---------------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Arnaud Lelievre; * */ package org.jfree.chart.editor; import java.awt.event.ActionEvent; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import javax.swing.BorderFactory; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; import org.jfree.chart.axis.Axis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.axis.NumberTickUnit; import org.jfree.chart.ui.LCBLayout; /** * A panel for editing the properties of a value axis. */ class DefaultNumberAxisEditor extends DefaultValueAxisEditor implements FocusListener { private double manualTickUnitValue; private JTextField manualTickUnit; /** * Standard constructor: builds a property panel for the specified axis. * * @param axis the axis, which should be changed. */ public DefaultNumberAxisEditor(NumberAxis axis) { super(axis); this.manualTickUnitValue = axis.getTickUnit().getSize(); validateTickUnit(); } @Override protected JPanel createTickUnitPanel() { JPanel tickUnitPanel = new JPanel(new LCBLayout(3)); tickUnitPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); tickUnitPanel.add(new JPanel()); JCheckBox autoTickUnitSelectionCheckBox = new JCheckBox( localizationResources.getString("Auto-TickUnit_Selection"), isAutoTickUnitSelection()); autoTickUnitSelectionCheckBox.setActionCommand("AutoTickOnOff"); autoTickUnitSelectionCheckBox.addActionListener(this); setAutoTickUnitSelectionCheckBox(autoTickUnitSelectionCheckBox); tickUnitPanel.add(getAutoTickUnitSelectionCheckBox()); tickUnitPanel.add(new JPanel()); tickUnitPanel.add(new JLabel(localizationResources.getString( "Manual_TickUnit_value"))); this.manualTickUnit = new JTextField(Double.toString( this.manualTickUnitValue)); this.manualTickUnit.setEnabled(!isAutoTickUnitSelection()); this.manualTickUnit.setActionCommand("TickUnitValue"); this.manualTickUnit.addActionListener(this); this.manualTickUnit.addFocusListener(this); tickUnitPanel.add(this.manualTickUnit); tickUnitPanel.add(new JPanel()); return tickUnitPanel; } /** * Handles actions from within the property panel. * @param event an event. */ @Override public void actionPerformed(ActionEvent event) { String command = event.getActionCommand(); if (command.equals("TickUnitValue")) { validateTickUnit(); } else { // pass to the super-class for handling super.actionPerformed(event); } } @Override public void focusLost(FocusEvent event) { super.focusLost(event); if (event.getSource() == this.manualTickUnit) { validateTickUnit(); } } @Override public void toggleAutoTick() { super.toggleAutoTick(); if (isAutoTickUnitSelection()) { this.manualTickUnit.setText(Double.toString(this.manualTickUnitValue)); this.manualTickUnit.setEnabled(false); } else { this.manualTickUnit.setEnabled(true); } } public void validateTickUnit() { double newTickUnit; try { newTickUnit = Double.parseDouble(this.manualTickUnit.getText()); } catch (NumberFormatException e) { newTickUnit = this.manualTickUnitValue; } if (newTickUnit > 0.0) { this.manualTickUnitValue = newTickUnit; } this.manualTickUnit.setText(Double.toString(this.manualTickUnitValue)); } /** * Sets the properties of the specified axis to match the properties * defined on this panel. * * @param axis the axis. */ @Override public void setAxisProperties(Axis axis) { super.setAxisProperties(axis); NumberAxis numberAxis = (NumberAxis) axis; if (!isAutoTickUnitSelection()) { numberAxis.setTickUnit(new NumberTickUnit(manualTickUnitValue)); } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/editor/DefaultPlotEditor.java000066400000000000000000000556331463604235500306150ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * DefaultPlotEditor.java * ---------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Andrzej Porebski; * Arnaud Lelievre; * Daniel Gredler; * */ package org.jfree.chart.editor; import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Paint; import java.awt.Stroke; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ResourceBundle; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JColorChooser; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTabbedPane; import org.jfree.chart.axis.Axis; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PolarPlot; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.category.CategoryItemRenderer; import org.jfree.chart.renderer.category.LineAndShapeRenderer; import org.jfree.chart.renderer.xy.StandardXYItemRenderer; import org.jfree.chart.renderer.xy.XYItemRenderer; import org.jfree.chart.ui.LCBLayout; import org.jfree.chart.ui.PaintSample; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.ui.StrokeChooserPanel; import org.jfree.chart.ui.StrokeSample; import org.jfree.chart.util.ResourceBundleWrapper; /** * A panel for editing the properties of a {@link Plot}. */ class DefaultPlotEditor extends JPanel implements ActionListener { /** Orientation constants. */ private final static String[] orientationNames = {"Vertical", "Horizontal"}; private final static int ORIENTATION_VERTICAL = 0; private final static int ORIENTATION_HORIZONTAL = 1; /** The paint (color) used to fill the background of the plot. */ private PaintSample backgroundPaintSample; /** The stroke used to draw the outline of the plot. */ private StrokeSample outlineStrokeSample; /** The paint (color) used to draw the outline of the plot. */ private PaintSample outlinePaintSample; /** * A panel used to display/edit the properties of the domain axis (if any). */ private DefaultAxisEditor domainAxisPropertyPanel; /** * A panel used to display/edit the properties of the range axis (if any). */ private DefaultAxisEditor rangeAxisPropertyPanel; /** An array of stroke samples to choose from. */ private StrokeSample[] availableStrokeSamples; /** The insets for the plot. */ private RectangleInsets plotInsets; /** * The orientation for the plot (for CategoryPlots and * XYPlots). */ private PlotOrientation plotOrientation; /** * The orientation combo box (for CategoryPlots and * XYPlots). */ private JComboBox orientationCombo; /** Whether or not to draw lines between each data point (for * LineAndShapeRenderers and StandardXYItemRenderers). */ private Boolean drawLines; /** * The checkbox for whether or not to draw lines between each data point. */ private JCheckBox drawLinesCheckBox; /** Whether or not to draw shapes at each data point (for * LineAndShapeRenderers and StandardXYItemRenderers). */ private Boolean drawShapes; /** * The checkbox for whether or not to draw shapes at each data point. */ private JCheckBox drawShapesCheckBox; /** The resourceBundle for the localization. */ protected static ResourceBundle localizationResources = ResourceBundleWrapper.getBundle( "org.jfree.chart.editor.LocalizationBundle"); /** * Standard constructor - constructs a panel for editing the properties of * the specified plot. *

* In designing the panel, we need to be aware that subclasses of Plot will * need to implement subclasses of PlotPropertyEditPanel - so we need to * leave one or two 'slots' where the subclasses can extend the user * interface. * * @param plot the plot, which should be changed. */ public DefaultPlotEditor(Plot plot) { JPanel panel = createPlotPanel(plot); add(panel); } /** * Creates and returns a panel for editing the settings of the specified * plot. * * @param plot the plot. * * @return A panel. */ protected JPanel createPlotPanel(Plot plot) { this.plotInsets = plot.getInsets(); this.backgroundPaintSample = new PaintSample(plot.getBackgroundPaint()); this.outlineStrokeSample = new StrokeSample(plot.getOutlineStroke()); this.outlinePaintSample = new PaintSample(plot.getOutlinePaint()); if (plot instanceof CategoryPlot) { this.plotOrientation = ((CategoryPlot) plot).getOrientation(); } else if (plot instanceof XYPlot) { this.plotOrientation = ((XYPlot) plot).getOrientation(); } if (plot instanceof CategoryPlot) { CategoryItemRenderer renderer = ((CategoryPlot) plot).getRenderer(); if (renderer instanceof LineAndShapeRenderer) { LineAndShapeRenderer r = (LineAndShapeRenderer) renderer; this.drawLines = r.getDefaultLinesVisible(); this.drawShapes = r.getDefaultShapesVisible(); } } else if (plot instanceof XYPlot) { XYItemRenderer renderer = ((XYPlot) plot).getRenderer(); if (renderer instanceof StandardXYItemRenderer) { StandardXYItemRenderer r = (StandardXYItemRenderer) renderer; this.drawLines = r.getPlotLines(); this.drawShapes = r.getBaseShapesVisible(); } } setLayout(new BorderLayout()); this.availableStrokeSamples = new StrokeSample[4]; this.availableStrokeSamples[0] = new StrokeSample(null); this.availableStrokeSamples[1] = new StrokeSample( new BasicStroke(1.0f)); this.availableStrokeSamples[2] = new StrokeSample( new BasicStroke(2.0f)); this.availableStrokeSamples[3] = new StrokeSample( new BasicStroke(3.0f)); // create a panel for the settings... JPanel panel = new JPanel(new BorderLayout()); panel.setBorder(BorderFactory.createTitledBorder( BorderFactory.createEtchedBorder(), plot.getPlotType() + localizationResources.getString(":"))); JPanel general = new JPanel(new BorderLayout()); general.setBorder(BorderFactory.createTitledBorder( localizationResources.getString("General"))); JPanel interior = new JPanel(new LCBLayout(7)); interior.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5)); // interior.add(new JLabel(localizationResources.getString("Insets"))); // JButton button = new JButton( // localizationResources.getString("Edit...") // ); // button.setActionCommand("Insets"); // button.addActionListener(this); // // this.insetsTextField = new InsetsTextField(this.plotInsets); // this.insetsTextField.setEnabled(false); // interior.add(this.insetsTextField); // interior.add(button); interior.add(new JLabel(localizationResources.getString( "Outline_stroke"))); JButton button = new JButton(localizationResources.getString( "Select...")); button.setActionCommand("OutlineStroke"); button.addActionListener(this); interior.add(this.outlineStrokeSample); interior.add(button); interior.add(new JLabel(localizationResources.getString( "Outline_Paint"))); button = new JButton(localizationResources.getString("Select...")); button.setActionCommand("OutlinePaint"); button.addActionListener(this); interior.add(this.outlinePaintSample); interior.add(button); interior.add(new JLabel(localizationResources.getString( "Background_paint"))); button = new JButton(localizationResources.getString("Select...")); button.setActionCommand("BackgroundPaint"); button.addActionListener(this); interior.add(this.backgroundPaintSample); interior.add(button); if (this.plotOrientation != null) { boolean isVertical = this.plotOrientation.equals( PlotOrientation.VERTICAL); int index = isVertical ? ORIENTATION_VERTICAL : ORIENTATION_HORIZONTAL; interior.add(new JLabel(localizationResources.getString( "Orientation"))); this.orientationCombo = new JComboBox(orientationNames); this.orientationCombo.setSelectedIndex(index); this.orientationCombo.setActionCommand("Orientation"); this.orientationCombo.addActionListener(this); interior.add(new JPanel()); interior.add(this.orientationCombo); } if (this.drawLines != null) { interior.add(new JLabel(localizationResources.getString( "Draw_lines"))); this.drawLinesCheckBox = new JCheckBox(); this.drawLinesCheckBox.setSelected(this.drawLines); this.drawLinesCheckBox.setActionCommand("DrawLines"); this.drawLinesCheckBox.addActionListener(this); interior.add(new JPanel()); interior.add(this.drawLinesCheckBox); } if (this.drawShapes != null) { interior.add(new JLabel(localizationResources.getString( "Draw_shapes"))); this.drawShapesCheckBox = new JCheckBox(); this.drawShapesCheckBox.setSelected(this.drawShapes); this.drawShapesCheckBox.setActionCommand("DrawShapes"); this.drawShapesCheckBox.addActionListener(this); interior.add(new JPanel()); interior.add(this.drawShapesCheckBox); } general.add(interior, BorderLayout.NORTH); JPanel appearance = new JPanel(new BorderLayout()); appearance.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); appearance.add(general, BorderLayout.NORTH); JTabbedPane tabs = createPlotTabs(plot); tabs.add(localizationResources.getString("Appearance"), appearance); panel.add(tabs); return panel; } /** * Creates and returns a tabbed pane containing controls for setting * the attributes of the specified plot. * * @param plot the plot. * * @return A tabbed pane. */ protected JTabbedPane createPlotTabs(Plot plot) { JTabbedPane tabs = new JTabbedPane(); tabs.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5)); Axis domainAxis = null; if (plot instanceof CategoryPlot) { domainAxis = ((CategoryPlot) plot).getDomainAxis(); } else if (plot instanceof XYPlot) { domainAxis = ((XYPlot) plot).getDomainAxis(); } this.domainAxisPropertyPanel = DefaultAxisEditor.getInstance( domainAxis); if (this.domainAxisPropertyPanel != null) { this.domainAxisPropertyPanel.setBorder( BorderFactory.createEmptyBorder(2, 2, 2, 2)); tabs.add(localizationResources.getString("Domain_Axis"), this.domainAxisPropertyPanel); } Axis rangeAxis = null; if (plot instanceof CategoryPlot) { rangeAxis = ((CategoryPlot) plot).getRangeAxis(); } else if (plot instanceof XYPlot) { rangeAxis = ((XYPlot) plot).getRangeAxis(); } else if (plot instanceof PolarPlot) { rangeAxis = ((PolarPlot) plot).getAxis(); } this.rangeAxisPropertyPanel = DefaultAxisEditor.getInstance(rangeAxis); if (this.rangeAxisPropertyPanel != null) { this.rangeAxisPropertyPanel.setBorder( BorderFactory.createEmptyBorder(2, 2, 2, 2)); tabs.add(localizationResources.getString("Range_Axis"), this.rangeAxisPropertyPanel); } return tabs; } /** * Returns the current plot insets. * * @return The current plot insets. */ public RectangleInsets getPlotInsets() { if (this.plotInsets == null) { this.plotInsets = new RectangleInsets(0.0, 0.0, 0.0, 0.0); } return this.plotInsets; } /** * Returns the current background paint. * * @return The current background paint. */ public Paint getBackgroundPaint() { return this.backgroundPaintSample.getPaint(); } /** * Returns the current outline stroke. * * @return The current outline stroke (possibly {@code null}). */ public Stroke getOutlineStroke() { return this.outlineStrokeSample.getStroke(); } /** * Returns the current outline paint. * * @return The current outline paint. */ public Paint getOutlinePaint() { return this.outlinePaintSample.getPaint(); } /** * Returns a reference to the panel for editing the properties of the * domain axis. * * @return A reference to a panel. */ public DefaultAxisEditor getDomainAxisPropertyEditPanel() { return this.domainAxisPropertyPanel; } /** * Returns a reference to the panel for editing the properties of the * range axis. * * @return A reference to a panel. */ public DefaultAxisEditor getRangeAxisPropertyEditPanel() { return this.rangeAxisPropertyPanel; } /** * Handles user actions generated within the panel. * @param event the event */ @Override public void actionPerformed(ActionEvent event) { String command = event.getActionCommand(); if (command.equals("BackgroundPaint")) { attemptBackgroundPaintSelection(); } else if (command.equals("OutlineStroke")) { attemptOutlineStrokeSelection(); } else if (command.equals("OutlinePaint")) { attemptOutlinePaintSelection(); } // else if (command.equals("Insets")) { // editInsets(); // } else if (command.equals("Orientation")) { attemptOrientationSelection(); } else if (command.equals("DrawLines")) { attemptDrawLinesSelection(); } else if (command.equals("DrawShapes")) { attemptDrawShapesSelection(); } } /** * Allow the user to change the background paint. */ private void attemptBackgroundPaintSelection() { Color c; c = JColorChooser.showDialog(this, localizationResources.getString( "Background_Color"), Color.BLUE); if (c != null) { this.backgroundPaintSample.setPaint(c); } } /** * Allow the user to change the outline stroke. */ private void attemptOutlineStrokeSelection() { StrokeChooserPanel panel = new StrokeChooserPanel( this.outlineStrokeSample, this.availableStrokeSamples); int result = JOptionPane.showConfirmDialog(this, panel, localizationResources.getString("Stroke_Selection"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE); if (result == JOptionPane.OK_OPTION) { this.outlineStrokeSample.setStroke(panel.getSelectedStroke()); } } /** * Allow the user to change the outline paint. We use JColorChooser, so * the user can only choose colors (a subset of all possible paints). */ private void attemptOutlinePaintSelection() { Color c; c = JColorChooser.showDialog(this, localizationResources.getString( "Outline_Color"), Color.BLUE); if (c != null) { this.outlinePaintSample.setPaint(c); } } // /** // * Allow the user to edit the individual insets' values. // */ // private void editInsets() { // InsetsChooserPanel panel = new InsetsChooserPanel(this.plotInsets); // int result = JOptionPane.showConfirmDialog( // this, panel, localizationResources.getString("Edit_Insets"), // JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE // ); // // if (result == JOptionPane.OK_OPTION) { // this.plotInsets = panel.getInsets(); // this.insetsTextField.setInsets(this.plotInsets); // } // // } // /** * Allow the user to modify the plot orientation if this is an editor for a * CategoryPlot or a XYPlot. */ private void attemptOrientationSelection() { int index = this.orientationCombo.getSelectedIndex(); if (index == ORIENTATION_VERTICAL) { this.plotOrientation = PlotOrientation.VERTICAL; } else { this.plotOrientation = PlotOrientation.HORIZONTAL; } } /** * Allow the user to modify whether or not lines are drawn between data * points by LineAndShapeRenderers and * StandardXYItemRenderers. */ private void attemptDrawLinesSelection() { this.drawLines = this.drawLinesCheckBox.isSelected(); } /** * Allow the user to modify whether or not shapes are drawn at data points * by LineAndShapeRenderers and StandardXYItemRenderers. */ private void attemptDrawShapesSelection() { this.drawShapes = this.drawShapesCheckBox.isSelected(); } /** * Updates the plot properties to match the properties defined on the panel. * * @param plot The plot. */ public void updatePlotProperties(Plot plot) { // set the plot properties... plot.setOutlinePaint(getOutlinePaint()); plot.setOutlineStroke(getOutlineStroke()); plot.setBackgroundPaint(getBackgroundPaint()); plot.setInsets(getPlotInsets()); // then the axis properties... if (this.domainAxisPropertyPanel != null) { Axis domainAxis = null; if (plot instanceof CategoryPlot) { CategoryPlot p = (CategoryPlot) plot; domainAxis = p.getDomainAxis(); } else if (plot instanceof XYPlot) { XYPlot p = (XYPlot) plot; domainAxis = p.getDomainAxis(); } if (domainAxis != null) { this.domainAxisPropertyPanel.setAxisProperties(domainAxis); } } if (this.rangeAxisPropertyPanel != null) { Axis rangeAxis = null; if (plot instanceof CategoryPlot) { CategoryPlot p = (CategoryPlot) plot; rangeAxis = p.getRangeAxis(); } else if (plot instanceof XYPlot) { XYPlot p = (XYPlot) plot; rangeAxis = p.getRangeAxis(); } else if (plot instanceof PolarPlot) { PolarPlot p = (PolarPlot) plot; rangeAxis = p.getAxis(); } if (rangeAxis != null) { this.rangeAxisPropertyPanel.setAxisProperties(rangeAxis); } } if (this.plotOrientation != null) { if (plot instanceof CategoryPlot) { CategoryPlot p = (CategoryPlot) plot; p.setOrientation(this.plotOrientation); } else if (plot instanceof XYPlot) { XYPlot p = (XYPlot) plot; p.setOrientation(this.plotOrientation); } } if (this.drawLines != null) { if (plot instanceof CategoryPlot) { CategoryPlot p = (CategoryPlot) plot; CategoryItemRenderer r = p.getRenderer(); if (r instanceof LineAndShapeRenderer) { ((LineAndShapeRenderer) r).setDefaultLinesVisible(this.drawLines); } } else if (plot instanceof XYPlot) { XYPlot p = (XYPlot) plot; XYItemRenderer r = p.getRenderer(); if (r instanceof StandardXYItemRenderer) { ((StandardXYItemRenderer) r).setPlotLines(this.drawLines); } } } if (this.drawShapes != null) { if (plot instanceof CategoryPlot) { CategoryPlot p = (CategoryPlot) plot; CategoryItemRenderer r = p.getRenderer(); if (r instanceof LineAndShapeRenderer) { ((LineAndShapeRenderer) r).setDefaultShapesVisible(this.drawShapes); } } else if (plot instanceof XYPlot) { XYPlot p = (XYPlot) plot; XYItemRenderer r = p.getRenderer(); if (r instanceof StandardXYItemRenderer) { ((StandardXYItemRenderer) r).setBaseShapesVisible( this.drawShapes); } } } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/editor/DefaultPolarPlotEditor.java000066400000000000000000000152341463604235500316040ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * DefaultPolarPlotEditor.java * ---------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: Martin Hoeller; * Contributor(s): -; * */ package org.jfree.chart.editor; import java.awt.event.ActionEvent; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import javax.swing.BorderFactory; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTabbedPane; import javax.swing.JTextField; import org.jfree.chart.axis.NumberTickUnit; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PolarPlot; import org.jfree.chart.ui.LCBLayout; /** * A panel for editing the properties of a {@link PolarPlot}. */ public class DefaultPolarPlotEditor extends DefaultPlotEditor implements FocusListener { /** A text field to enter a manual TickUnit. */ private JTextField manualTickUnit; /** A text field to enter the angleOffset. */ private JTextField angleOffset; /** The size for the manual TickUnit. */ private double manualTickUnitValue; /** The value for the plot's angle offset. */ private double angleOffsetValue; /** * Standard constructor - constructs a panel for editing the properties of * the specified plot. * * @param plot the plot, which should be changed. */ public DefaultPolarPlotEditor(PolarPlot plot) { super(plot); this.angleOffsetValue = plot.getAngleOffset(); this.angleOffset.setText(Double.toString(this.angleOffsetValue)); this.manualTickUnitValue = plot.getAngleTickUnit().getSize(); this.manualTickUnit.setText(Double.toString(this.manualTickUnitValue)); } /** * Creates a tabbed pane for editing the plot attributes. * * @param plot the plot. * * @return A tabbed pane. */ @Override protected JTabbedPane createPlotTabs(Plot plot) { JTabbedPane tabs = super.createPlotTabs(plot); // TODO find a better localization key tabs.insertTab(localizationResources.getString("General1"), null, createPlotPanel(), null, 0); tabs.setSelectedIndex(0); return tabs; } private JPanel createPlotPanel() { JPanel plotPanel = new JPanel(new LCBLayout(3)); plotPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); plotPanel.add(new JLabel(localizationResources.getString( "AngleOffset"))); this.angleOffset = new JTextField(Double.toString( this.angleOffsetValue)); this.angleOffset.setActionCommand("AngleOffsetValue"); this.angleOffset.addActionListener(this); this.angleOffset.addFocusListener(this); plotPanel.add(this.angleOffset); plotPanel.add(new JPanel()); plotPanel.add(new JLabel(localizationResources.getString( "Manual_TickUnit_value"))); this.manualTickUnit = new JTextField(Double.toString( this.manualTickUnitValue)); this.manualTickUnit.setActionCommand("TickUnitValue"); this.manualTickUnit.addActionListener(this); this.manualTickUnit.addFocusListener(this); plotPanel.add(this.manualTickUnit); plotPanel.add(new JPanel()); return plotPanel; } /** * Does nothing. * * @param event the event. */ @Override public void focusGained(FocusEvent event) { // don't need to do anything } /** * Revalidates minimum/maximum range. * * @param event the event. */ @Override public void focusLost(FocusEvent event) { if (event.getSource() == this.angleOffset) { validateAngleOffset(); } else if (event.getSource() == this.manualTickUnit) { validateTickUnit(); } } /** * Handles actions from within the property panel. * @param event an event. */ @Override public void actionPerformed(ActionEvent event) { String command = event.getActionCommand(); if (command.equals("AngleOffsetValue")) { validateAngleOffset(); } else if (command.equals("TickUnitValue")) { validateTickUnit(); } } /** * Validates the angle offset entered by the user. */ public void validateAngleOffset() { double newOffset; try { newOffset = Double.parseDouble(this.angleOffset.getText()); } catch (NumberFormatException e) { newOffset = this.angleOffsetValue; } this.angleOffsetValue = newOffset; this.angleOffset.setText(Double.toString(this.angleOffsetValue)); } /** * Validates the tick unit entered by the user. */ public void validateTickUnit() { double newTickUnit; try { newTickUnit = Double.parseDouble(this.manualTickUnit.getText()); } catch (NumberFormatException e) { newTickUnit = this.manualTickUnitValue; } if (newTickUnit > 0.0 && newTickUnit < 360.0) { this.manualTickUnitValue = newTickUnit; } this.manualTickUnit.setText(Double.toString(this.manualTickUnitValue)); } @Override public void updatePlotProperties(Plot plot) { super.updatePlotProperties(plot); PolarPlot pp = (PolarPlot) plot; pp.setAngleTickUnit(new NumberTickUnit(this.manualTickUnitValue)); pp.setAngleOffset(this.angleOffsetValue); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/editor/DefaultTitleEditor.java000066400000000000000000000237171463604235500307560ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * DefaultTitleEditor.java * ----------------------- * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Arnaud Lelievre; * Daniel Gredler; * */ package org.jfree.chart.editor; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Font; import java.awt.Paint; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ResourceBundle; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JColorChooser; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextField; import org.jfree.chart.JFreeChart; import org.jfree.chart.title.TextTitle; import org.jfree.chart.title.Title; import org.jfree.chart.ui.FontChooserPanel; import org.jfree.chart.ui.FontDisplayField; import org.jfree.chart.ui.LCBLayout; import org.jfree.chart.ui.PaintSample; import org.jfree.chart.util.ResourceBundleWrapper; /** * A panel for editing the properties of a chart title. */ class DefaultTitleEditor extends JPanel implements ActionListener { /** Whether or not to display the title on the chart. */ private boolean showTitle; /** The checkbox to indicate whether or not to display the title. */ private JCheckBox showTitleCheckBox; /** A field for displaying/editing the title text. */ private JTextField titleField; /** The font used to draw the title. */ private Font titleFont; /** A field for displaying a description of the title font. */ private JTextField fontfield; /** The button to use to select a new title font. */ private JButton selectFontButton; /** The paint (color) used to draw the title. */ private PaintSample titlePaint; /** The button to use to select a new paint (color) to draw the title. */ private JButton selectPaintButton; /** The resourceBundle for the localization. */ protected static ResourceBundle localizationResources = ResourceBundleWrapper.getBundle( "org.jfree.chart.editor.LocalizationBundle"); /** * Standard constructor: builds a panel for displaying/editing the * properties of the specified title. * * @param title the title, which should be changed. */ public DefaultTitleEditor(Title title) { TextTitle t = (title != null ? (TextTitle) title : new TextTitle(localizationResources.getString("Title"))); this.showTitle = (title != null); this.titleFont = t.getFont(); this.titleField = new JTextField(t.getText()); this.titlePaint = new PaintSample(t.getPaint()); setLayout(new BorderLayout()); JPanel general = new JPanel(new BorderLayout()); general.setBorder( BorderFactory.createTitledBorder( BorderFactory.createEtchedBorder(), localizationResources.getString("General") ) ); JPanel interior = new JPanel(new LCBLayout(4)); interior.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5)); interior.add(new JLabel(localizationResources.getString("Show_Title"))); this.showTitleCheckBox = new JCheckBox(); this.showTitleCheckBox.setSelected(this.showTitle); this.showTitleCheckBox.setActionCommand("ShowTitle"); this.showTitleCheckBox.addActionListener(this); interior.add(new JPanel()); interior.add(this.showTitleCheckBox); JLabel titleLabel = new JLabel(localizationResources.getString("Text")); interior.add(titleLabel); interior.add(this.titleField); interior.add(new JPanel()); JLabel fontLabel = new JLabel(localizationResources.getString("Font")); this.fontfield = new FontDisplayField(this.titleFont); this.selectFontButton = new JButton( localizationResources.getString("Select...") ); this.selectFontButton.setActionCommand("SelectFont"); this.selectFontButton.addActionListener(this); interior.add(fontLabel); interior.add(this.fontfield); interior.add(this.selectFontButton); JLabel colorLabel = new JLabel( localizationResources.getString("Color") ); this.selectPaintButton = new JButton( localizationResources.getString("Select...") ); this.selectPaintButton.setActionCommand("SelectPaint"); this.selectPaintButton.addActionListener(this); interior.add(colorLabel); interior.add(this.titlePaint); interior.add(this.selectPaintButton); this.enableOrDisableControls(); general.add(interior); add(general, BorderLayout.NORTH); } /** * Returns the title text entered in the panel. * * @return The title text entered in the panel. */ public String getTitleText() { return this.titleField.getText(); } /** * Returns the font selected in the panel. * * @return The font selected in the panel. */ public Font getTitleFont() { return this.titleFont; } /** * Returns the paint selected in the panel. * * @return The paint selected in the panel. */ public Paint getTitlePaint() { return this.titlePaint.getPaint(); } /** * Handles button clicks by passing control to an appropriate handler * method. * * @param event the event */ @Override public void actionPerformed(ActionEvent event) { String command = event.getActionCommand(); if (command.equals("SelectFont")) { attemptFontSelection(); } else if (command.equals("SelectPaint")) { attemptPaintSelection(); } else if (command.equals("ShowTitle")) { attemptModifyShowTitle(); } } /** * Presents a font selection dialog to the user. */ public void attemptFontSelection() { FontChooserPanel panel = new FontChooserPanel(this.titleFont); int result = JOptionPane.showConfirmDialog( this, panel, localizationResources.getString("Font_Selection"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE ); if (result == JOptionPane.OK_OPTION) { this.titleFont = panel.getSelectedFont(); this.fontfield.setText( this.titleFont.getFontName() + " " + this.titleFont.getSize() ); } } /** * Allow the user the opportunity to select a Paint object. For now, we * just use the standard color chooser - all colors are Paint objects, but * not all Paint objects are colors (later we can implement a more general * Paint chooser). */ public void attemptPaintSelection() { Paint p = this.titlePaint.getPaint(); Color defaultColor = (p instanceof Color ? (Color) p : Color.BLUE); Color c = JColorChooser.showDialog( this, localizationResources.getString("Title_Color"), defaultColor ); if (c != null) { this.titlePaint.setPaint(c); } } /** * Allow the user the opportunity to change whether the title is * displayed on the chart or not. */ private void attemptModifyShowTitle() { this.showTitle = this.showTitleCheckBox.isSelected(); this.enableOrDisableControls(); } /** * If we are supposed to show the title, the controls are enabled. * If we are not supposed to show the title, the controls are disabled. */ private void enableOrDisableControls() { boolean enabled = this.showTitle; this.titleField.setEnabled(enabled); this.selectFontButton.setEnabled(enabled); this.selectPaintButton.setEnabled(enabled); } /** * Sets the properties of the specified title to match the properties * defined on this panel. * * @param chart the chart whose title is to be modified. */ public void setTitleProperties(JFreeChart chart) { if (this.showTitle) { TextTitle title = chart.getTitle(); if (title == null) { title = new TextTitle(); chart.setTitle(title); } title.setText(getTitleText()); title.setFont(getTitleFont()); title.setPaint(getTitlePaint()); } else { chart.setTitle((TextTitle) null); } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/editor/DefaultValueAxisEditor.java000066400000000000000000000334051463604235500315710ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * DefaultValueAxisEditor.java * ---------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: Martin Hoeller (base on DefaultNumberAxisEditor * by David Gilbert); * Contributor(s): -; * */ package org.jfree.chart.editor; import java.awt.BasicStroke; import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.util.ResourceBundle; import javax.swing.BorderFactory; import javax.swing.JCheckBox; import javax.swing.JColorChooser; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTabbedPane; import javax.swing.JTextField; import org.jfree.chart.axis.Axis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.ui.LCBLayout; import org.jfree.chart.ui.PaintSample; import org.jfree.chart.ui.StrokeChooserPanel; import org.jfree.chart.ui.StrokeSample; import org.jfree.chart.util.ResourceBundleWrapper; /** * A panel for editing properties of a {@link ValueAxis}. */ class DefaultValueAxisEditor extends DefaultAxisEditor implements FocusListener { /** A flag that indicates whether or not the axis range is determined * automatically. */ private boolean autoRange; /** Flag if auto-tickunit-selection is enabled. */ private boolean autoTickUnitSelection; /** The lowest value in the axis range. */ private double minimumValue; /** The highest value in the axis range. */ private double maximumValue; /** A checkbox that indicates whether or not the axis range is determined * automatically. */ private JCheckBox autoRangeCheckBox; /** A check-box enabling/disabling auto-tickunit-selection. */ private JCheckBox autoTickUnitSelectionCheckBox; /** A text field for entering the minimum value in the axis range. */ private JTextField minimumRangeValue; /** A text field for entering the maximum value in the axis range. */ private JTextField maximumRangeValue; /** The paint selected for drawing the gridlines. */ private PaintSample gridPaintSample; /** The stroke selected for drawing the gridlines. */ private StrokeSample gridStrokeSample; /** An array of stroke samples to choose from (since I haven't written a * decent StrokeChooser component yet). */ private StrokeSample[] availableStrokeSamples; /** The resourceBundle for the localization. */ protected static ResourceBundle localizationResources = ResourceBundleWrapper.getBundle( "org.jfree.chart.editor.LocalizationBundle"); /** * Standard constructor: builds a property panel for the specified axis. * * @param axis the axis, which should be changed. */ public DefaultValueAxisEditor(ValueAxis axis) { super(axis); this.autoRange = axis.isAutoRange(); this.minimumValue = axis.getLowerBound(); this.maximumValue = axis.getUpperBound(); this.autoTickUnitSelection = axis.isAutoTickUnitSelection(); this.gridPaintSample = new PaintSample(Color.BLUE); this.gridStrokeSample = new StrokeSample(new BasicStroke(1.0f)); this.availableStrokeSamples = new StrokeSample[3]; this.availableStrokeSamples[0] = new StrokeSample( new BasicStroke(1.0f)); this.availableStrokeSamples[1] = new StrokeSample( new BasicStroke(2.0f)); this.availableStrokeSamples[2] = new StrokeSample( new BasicStroke(3.0f)); JTabbedPane other = getOtherTabs(); JPanel range = new JPanel(new LCBLayout(3)); range.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); range.add(new JPanel()); this.autoRangeCheckBox = new JCheckBox(localizationResources.getString( "Auto-adjust_range"), this.autoRange); this.autoRangeCheckBox.setActionCommand("AutoRangeOnOff"); this.autoRangeCheckBox.addActionListener(this); range.add(this.autoRangeCheckBox); range.add(new JPanel()); range.add(new JLabel(localizationResources.getString( "Minimum_range_value"))); this.minimumRangeValue = new JTextField(Double.toString( this.minimumValue)); this.minimumRangeValue.setEnabled(!this.autoRange); this.minimumRangeValue.setActionCommand("MinimumRange"); this.minimumRangeValue.addActionListener(this); this.minimumRangeValue.addFocusListener(this); range.add(this.minimumRangeValue); range.add(new JPanel()); range.add(new JLabel(localizationResources.getString( "Maximum_range_value"))); this.maximumRangeValue = new JTextField(Double.toString( this.maximumValue)); this.maximumRangeValue.setEnabled(!this.autoRange); this.maximumRangeValue.setActionCommand("MaximumRange"); this.maximumRangeValue.addActionListener(this); this.maximumRangeValue.addFocusListener(this); range.add(this.maximumRangeValue); range.add(new JPanel()); other.add(localizationResources.getString("Range"), range); other.add(localizationResources.getString("TickUnit"), createTickUnitPanel()); } /** * Creates and returns a panel for displaying tick unit settings. * * @return A panel. */ protected JPanel createTickUnitPanel() { JPanel tickUnitPanel = new JPanel(new LCBLayout(3)); tickUnitPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); tickUnitPanel.add(new JPanel()); this.autoTickUnitSelectionCheckBox = new JCheckBox( localizationResources.getString("Auto-TickUnit_Selection"), this.autoTickUnitSelection); this.autoTickUnitSelectionCheckBox.setActionCommand("AutoTickOnOff"); this.autoTickUnitSelectionCheckBox.addActionListener(this); tickUnitPanel.add(this.autoTickUnitSelectionCheckBox); tickUnitPanel.add(new JPanel()); return tickUnitPanel; } /** * Getter for the {@link #autoTickUnitSelection} flag. * * @return The value of the flag for enabling auto-tickunit-selection. */ protected boolean isAutoTickUnitSelection() { return autoTickUnitSelection; } /** * Setter for the {@link #autoTickUnitSelection} flag. * @param autoTickUnitSelection The new value for auto-tickunit-selection. */ protected void setAutoTickUnitSelection(boolean autoTickUnitSelection) { this.autoTickUnitSelection = autoTickUnitSelection; } /** * Get the checkbox that enables/disables auto-tickunit-selection. * * @return The checkbox. */ protected JCheckBox getAutoTickUnitSelectionCheckBox() { return autoTickUnitSelectionCheckBox; } /** * Set the checkbox that enables/disables auto-tickunit-selection. * * @param autoTickUnitSelectionCheckBox The checkbox. */ protected void setAutoTickUnitSelectionCheckBox( JCheckBox autoTickUnitSelectionCheckBox) { this.autoTickUnitSelectionCheckBox = autoTickUnitSelectionCheckBox; } /** * Returns the current setting of the auto-range property. * * @return {@code true} if auto range is enabled. */ public boolean isAutoRange() { return this.autoRange; } /** * Returns the current setting of the minimum value in the axis range. * * @return The current setting of the minimum value in the axis range. */ public double getMinimumValue() { return this.minimumValue; } /** * Returns the current setting of the maximum value in the axis range. * * @return The current setting of the maximum value in the axis range. */ public double getMaximumValue() { return this.maximumValue; } /** * Handles actions from within the property panel. * @param event an event. */ @Override public void actionPerformed(ActionEvent event) { String command = event.getActionCommand(); if (command.equals("GridStroke")) { attemptGridStrokeSelection(); } else if (command.equals("GridPaint")) { attemptGridPaintSelection(); } else if (command.equals("AutoRangeOnOff")) { toggleAutoRange(); } else if (command.equals("MinimumRange")) { validateMinimum(); } else if (command.equals("MaximumRange")) { validateMaximum(); } else if (command.equals("AutoTickOnOff")) { toggleAutoTick(); } else { // pass to the super-class for handling super.actionPerformed(event); } } /** * Handle a grid stroke selection. */ protected void attemptGridStrokeSelection() { StrokeChooserPanel panel = new StrokeChooserPanel(this.gridStrokeSample, this.availableStrokeSamples); int result = JOptionPane.showConfirmDialog(this, panel, localizationResources.getString("Stroke_Selection"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE); if (result == JOptionPane.OK_OPTION) { this.gridStrokeSample.setStroke(panel.getSelectedStroke()); } } /** * Handle a grid paint selection. */ protected void attemptGridPaintSelection() { Color c; c = JColorChooser.showDialog(this, localizationResources.getString( "Grid_Color"), Color.BLUE); if (c != null) { this.gridPaintSample.setPaint(c); } } /** * Does nothing. * * @param event the event. */ @Override public void focusGained(FocusEvent event) { // don't need to do anything } /** * Revalidates minimum/maximum range. * * @param event the event. */ @Override public void focusLost(FocusEvent event) { if (event.getSource() == this.minimumRangeValue) { validateMinimum(); } else if (event.getSource() == this.maximumRangeValue) { validateMaximum(); } } /** * Toggle the auto range setting. */ public void toggleAutoRange() { this.autoRange = this.autoRangeCheckBox.isSelected(); if (this.autoRange) { this.minimumRangeValue.setText(Double.toString(this.minimumValue)); this.minimumRangeValue.setEnabled(false); this.maximumRangeValue.setText(Double.toString(this.maximumValue)); this.maximumRangeValue.setEnabled(false); } else { this.minimumRangeValue.setEnabled(true); this.maximumRangeValue.setEnabled(true); } } /** * Sets the {@code autoTickUnitSelection} flag to match the control. */ public void toggleAutoTick() { this.autoTickUnitSelection = this.autoTickUnitSelectionCheckBox.isSelected(); } /** * Revalidate the range minimum. */ public void validateMinimum() { double newMin; try { newMin = Double.parseDouble(this.minimumRangeValue.getText()); if (newMin >= this.maximumValue) { newMin = this.minimumValue; } } catch (NumberFormatException e) { newMin = this.minimumValue; } this.minimumValue = newMin; this.minimumRangeValue.setText(Double.toString(this.minimumValue)); } /** * Revalidate the range maximum. */ public void validateMaximum() { double newMax; try { newMax = Double.parseDouble(this.maximumRangeValue.getText()); if (newMax <= this.minimumValue) { newMax = this.maximumValue; } } catch (NumberFormatException e) { newMax = this.maximumValue; } this.maximumValue = newMax; this.maximumRangeValue.setText(Double.toString(this.maximumValue)); } /** * Sets the properties of the specified axis to match the properties * defined on this panel. * * @param axis the axis. */ @Override public void setAxisProperties(Axis axis) { super.setAxisProperties(axis); ValueAxis valueAxis = (ValueAxis) axis; valueAxis.setAutoRange(this.autoRange); if (!this.autoRange) { valueAxis.setRange(this.minimumValue, this.maximumValue); } valueAxis.setAutoTickUnitSelection(this.autoTickUnitSelection); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/editor/package.html000066400000000000000000000003001463604235500266160ustar00rootroot00000000000000 Provides a simple (but so far incomplete) framework for editing chart properties. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/encoders/000077500000000000000000000000001463604235500246605ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/encoders/EncoderUtil.java000066400000000000000000000170301463604235500277410ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * EncoderUtil.java * ---------------- * (C) Copyright 2004-present, by Richard Atkinson and Contributors. * * Original Author: Richard Atkinson; * Contributor(s): -; * */ package org.jfree.chart.encoders; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.OutputStream; /** * A collection of utility methods for encoding images and returning them as a * byte[] or writing them directly to an OutputStream. */ public class EncoderUtil { /** * Encode the image in a specific format. * * @param image The image to be encoded. * @param format The {@link ImageFormat} to use. * * @return The byte[] that is the encoded image. * @throws IOException if there is an IO problem. */ public static byte[] encode(BufferedImage image, String format) throws IOException { ImageEncoder imageEncoder = ImageEncoderFactory.newInstance(format); return imageEncoder.encode(image); } /** * Encode the image in a specific format. * * @param image The image to be encoded. * @param format The {@link ImageFormat} to use. * @param encodeAlpha Whether to encode alpha transparency (not supported * by all ImageEncoders). * @return The byte[] that is the encoded image. * @throws IOException if there is an IO problem. */ public static byte[] encode(BufferedImage image, String format, boolean encodeAlpha) throws IOException { ImageEncoder imageEncoder = ImageEncoderFactory.newInstance(format, encodeAlpha); return imageEncoder.encode(image); } /** * Encode the image in a specific format. * * @param image The image to be encoded. * @param format The {@link ImageFormat} to use. * @param quality The quality to use for the image encoding (not supported * by all ImageEncoders). * @return The byte[] that is the encoded image. * @throws IOException if there is an IO problem. */ public static byte[] encode(BufferedImage image, String format, float quality) throws IOException { ImageEncoder imageEncoder = ImageEncoderFactory.newInstance(format, quality); return imageEncoder.encode(image); } /** * Encode the image in a specific format. * * @param image The image to be encoded. * @param format The {@link ImageFormat} to use. * @param quality The quality to use for the image encoding (not supported * by all ImageEncoders). * @param encodeAlpha Whether to encode alpha transparency (not supported * by all ImageEncoders). * @return The byte[] that is the encoded image. * @throws IOException if there is an IO problem. */ public static byte[] encode(BufferedImage image, String format, float quality, boolean encodeAlpha) throws IOException { ImageEncoder imageEncoder = ImageEncoderFactory.newInstance(format, quality, encodeAlpha); return imageEncoder.encode(image); } /** * Encode the image in a specific format and write it to an OutputStream. * * @param image The image to be encoded. * @param format The {@link ImageFormat} to use. * @param outputStream The OutputStream to write the encoded image to. * @throws IOException if there is an IO problem. */ public static void writeBufferedImage(BufferedImage image, String format, OutputStream outputStream) throws IOException { ImageEncoder imageEncoder = ImageEncoderFactory.newInstance(format); imageEncoder.encode(image, outputStream); } /** * Encode the image in a specific format and write it to an OutputStream. * * @param image The image to be encoded. * @param format The {@link ImageFormat} to use. * @param outputStream The OutputStream to write the encoded image to. * @param quality The quality to use for the image encoding (not * supported by all ImageEncoders). * @throws IOException if there is an IO problem. */ public static void writeBufferedImage(BufferedImage image, String format, OutputStream outputStream, float quality) throws IOException { ImageEncoder imageEncoder = ImageEncoderFactory.newInstance(format, quality); imageEncoder.encode(image, outputStream); } /** * Encode the image in a specific format and write it to an OutputStream. * * @param image The image to be encoded. * @param format The {@link ImageFormat} to use. * @param outputStream The OutputStream to write the encoded image to. * @param encodeAlpha Whether to encode alpha transparency (not * supported by all ImageEncoders). * @throws IOException if there is an IO problem. */ public static void writeBufferedImage(BufferedImage image, String format, OutputStream outputStream, boolean encodeAlpha) throws IOException { ImageEncoder imageEncoder = ImageEncoderFactory.newInstance(format, encodeAlpha); imageEncoder.encode(image, outputStream); } /** * Encode the image in a specific format and write it to an OutputStream. * * @param image The image to be encoded. * @param format The {@link ImageFormat} to use. * @param outputStream The OutputStream to write the encoded image to. * @param quality The quality to use for the image encoding (not * supported by all ImageEncoders). * @param encodeAlpha Whether to encode alpha transparency (not supported * by all ImageEncoders). * @throws IOException if there is an IO problem. */ public static void writeBufferedImage(BufferedImage image, String format, OutputStream outputStream, float quality, boolean encodeAlpha) throws IOException { ImageEncoder imageEncoder = ImageEncoderFactory.newInstance(format, quality, encodeAlpha); imageEncoder.encode(image, outputStream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/encoders/ImageEncoder.java000066400000000000000000000063371463604235500300560ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * ImageEncoder.java * ----------------- * (C) Copyright 2004-present, by Richard Atkinson and Contributors. * * Original Author: Richard Atkinson; * Contributor(s): -; * */ package org.jfree.chart.encoders; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.OutputStream; /** * Interface for abstracting different types of image encoders. */ public interface ImageEncoder { /** * Encodes an image in a particular format. * * @param bufferedImage The image to be encoded. * * @return The byte[] that is the encoded image. * * @throws IOException if there is an IO problem. */ byte[] encode(BufferedImage bufferedImage) throws IOException; /** * Encodes an image in a particular format and writes it to an OutputStream. * * @param bufferedImage The image to be encoded. * @param outputStream The OutputStream to write the encoded image to. * @throws IOException if there is an IO problem. */ void encode(BufferedImage bufferedImage, OutputStream outputStream) throws IOException; /** * Get the quality of the image encoding. * * @return A float representing the quality. */ float getQuality(); /** * Set the quality of the image encoding (not supported by all * ImageEncoders). * * @param quality A float representing the quality. */ void setQuality(float quality); /** * Get whether the encoder should encode alpha transparency. * * @return Whether the encoder is encoding alpha transparency. */ boolean isEncodingAlpha(); /** * Set whether the encoder should encode alpha transparency (not * supported by all ImageEncoders). * * @param encodingAlpha Whether the encoder should encode alpha * transparency. */ void setEncodingAlpha(boolean encodingAlpha); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/encoders/ImageEncoderFactory.java000066400000000000000000000122271463604235500314010ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * ImageEncoderFactory.java * ------------------------ * (C) Copyright 2004-present, by Richard Atkinson and Contributors. * * Original Author: Richard Atkinson; * Contributor(s): David Gilbert; * */ package org.jfree.chart.encoders; import java.util.HashMap; import java.util.Map; /** * Factory class for returning {@link ImageEncoder}s for different * {@link ImageFormat}s. */ public class ImageEncoderFactory { /** Storage for the encoders. */ private static Map encoders = null; static { init(); } /** * Sets up default encoders (uses Sun PNG Encoder if JDK 1.4+ and the * SunPNGEncoderAdapter class is available). */ private static void init() { encoders = new HashMap(); encoders.put("jpeg", "org.jfree.chart.encoders.SunJPEGEncoderAdapter"); encoders.put("png", "org.jfree.chart.encoders.SunPNGEncoderAdapter"); } /** * Used to set additional encoders or replace default ones. * * @param format The image format name. * @param imageEncoderClassName The name of the ImageEncoder class. */ public static void setImageEncoder(String format, String imageEncoderClassName) { encoders.put(format, imageEncoderClassName); } /** * Used to retrieve an ImageEncoder for a specific image format. * * @param format The image format required. * * @return The ImageEncoder or {@code null} if none available. */ public static ImageEncoder newInstance(String format) { ImageEncoder imageEncoder = null; String className = (String) encoders.get(format); if (className == null) { throw new IllegalArgumentException("Unsupported image format - " + format); } try { Class imageEncoderClass = Class.forName(className); imageEncoder = (ImageEncoder) imageEncoderClass.newInstance(); } catch (Exception e) { throw new IllegalArgumentException(e.toString()); } return imageEncoder; } /** * Used to retrieve an ImageEncoder for a specific image format. * * @param format The image format required. * @param quality The quality to be set before returning. * * @return The ImageEncoder or {@code null} if none available. */ public static ImageEncoder newInstance(String format, float quality) { ImageEncoder imageEncoder = newInstance(format); imageEncoder.setQuality(quality); return imageEncoder; } /** * Used to retrieve an ImageEncoder for a specific image format. * * @param format The image format required. * @param encodingAlpha Sets whether alpha transparency should be encoded. * * @return The ImageEncoder or {@code null} if none available. */ public static ImageEncoder newInstance(String format, boolean encodingAlpha) { ImageEncoder imageEncoder = newInstance(format); imageEncoder.setEncodingAlpha(encodingAlpha); return imageEncoder; } /** * Used to retrieve an ImageEncoder for a specific image format. * * @param format The image format required. * @param quality The quality to be set before returning. * @param encodingAlpha Sets whether alpha transparency should be encoded. * * @return The ImageEncoder or {@code null} if none available. */ public static ImageEncoder newInstance(String format, float quality, boolean encodingAlpha) { ImageEncoder imageEncoder = newInstance(format); imageEncoder.setQuality(quality); imageEncoder.setEncodingAlpha(encodingAlpha); return imageEncoder; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/encoders/ImageFormat.java000066400000000000000000000035171463604235500277240ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * ImageFormat.java * ---------------- * (C) Copyright 2004-present, by Richard Atkinson and Contributors. * * Original Author: Richard Atkinson; * Contributor(s): -; * */ package org.jfree.chart.encoders; /** * Interface used for referencing different image formats. */ public interface ImageFormat { /** Portable Network Graphics - lossless */ String PNG = "png"; /** Joint Photographic Experts Group format - lossy */ String JPEG = "jpeg"; /** Graphics Interchange Format - lossless, but 256 colour restriction */ String GIF = "gif"; } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/encoders/SunJPEGEncoderAdapter.java000066400000000000000000000133471463604235500315470ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * SunJPEGEncoderAdapter.java * -------------------------- * (C) Copyright 2004-present, by Richard Atkinson and Contributors. * * Original Author: Richard Atkinson; * Contributor(s): David Gilbert; * */ package org.jfree.chart.encoders; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.Iterator; import javax.imageio.IIOImage; import javax.imageio.ImageIO; import javax.imageio.ImageWriteParam; import javax.imageio.ImageWriter; import javax.imageio.stream.ImageOutputStream; import org.jfree.chart.util.Args; /** * Adapter class for the Sun JPEG Encoder. The {@link ImageEncoderFactory} * will only return a reference to this class by default if the library has * been compiled under a JDK 1.4+ and is being run using a JRE 1.4+. */ public class SunJPEGEncoderAdapter implements ImageEncoder { /** The quality setting (in the range 0.0f to 1.0f). */ private float quality = 0.95f; /** * Creates a new {@code SunJPEGEncoderAdapter} instance. */ public SunJPEGEncoderAdapter() { } /** * Returns the quality of the image encoding, which is a number in the * range 0.0f to 1.0f (higher values give better quality output, but larger * file sizes). The default value is 0.95f. * * @return A float representing the quality, in the range 0.0f to 1.0f. * * @see #setQuality(float) */ @Override public float getQuality() { return this.quality; } /** * Set the quality of the image encoding. * * @param quality A float representing the quality (in the range 0.0f to * 1.0f). * * @see #getQuality() */ @Override public void setQuality(float quality) { if (quality < 0.0f || quality > 1.0f) { throw new IllegalArgumentException( "The 'quality' must be in the range 0.0f to 1.0f"); } this.quality = quality; } /** * Returns {@code false} always, indicating that this encoder does not * encode alpha transparency. * * @return {@code false}. */ @Override public boolean isEncodingAlpha() { return false; } /** * Set whether the encoder should encode alpha transparency (this is not * supported for JPEG, so this method does nothing). * * @param encodingAlpha ignored. */ @Override public void setEncodingAlpha(boolean encodingAlpha) { // No op } /** * Encodes an image in JPEG format. * * @param bufferedImage the image to be encoded ({@code null} not * permitted). * * @return The byte[] that is the encoded image. * * @throws IOException if there is an I/O problem. * @throws NullPointerException if {@code bufferedImage} is * {@code null}. */ @Override public byte[] encode(BufferedImage bufferedImage) throws IOException { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); encode(bufferedImage, outputStream); return outputStream.toByteArray(); } /** * Encodes an image in JPEG format and writes it to an output stream. * * @param bufferedImage the image to be encoded ({@code null} not * permitted). * @param outputStream the OutputStream to write the encoded image to * ({@code null} not permitted). * * @throws IOException if there is an I/O problem. * @throws NullPointerException if {@code bufferedImage} is {@code null}. */ @Override public void encode(BufferedImage bufferedImage, OutputStream outputStream) throws IOException { Args.nullNotPermitted(bufferedImage, "bufferedImage"); Args.nullNotPermitted(outputStream, "outputStream"); Iterator iterator = ImageIO.getImageWritersByFormatName("jpeg"); ImageWriter writer = (ImageWriter) iterator.next(); ImageWriteParam p = writer.getDefaultWriteParam(); p.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); p.setCompressionQuality(this.quality); ImageOutputStream ios = ImageIO.createImageOutputStream(outputStream); writer.setOutput(ios); writer.write(null, new IIOImage(bufferedImage, null, null), p); ios.flush(); writer.dispose(); ios.close(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/encoders/SunPNGEncoderAdapter.java000066400000000000000000000102221463604235500314330ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * SunPNGEncoderAdapter.java * ------------------------- * (C) Copyright 2004-present, by Richard Atkinson and Contributors. * * Original Author: Richard Atkinson; * Contributor(s): -; * */ package org.jfree.chart.encoders; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import javax.imageio.ImageIO; import org.jfree.chart.util.Args; /** * Adapter class for the Sun PNG Encoder. The ImageEncoderFactory will only * return a reference to this class by default if the library has been compiled * under a JDK 1.4+ and is being run using a JDK 1.4+. */ public class SunPNGEncoderAdapter implements ImageEncoder { /** * Get the quality of the image encoding (always 0.0). * * @return A float representing the quality. */ @Override public float getQuality() { return 0.0f; } /** * Set the quality of the image encoding (not supported in this * ImageEncoder). * * @param quality A float representing the quality. */ @Override public void setQuality(float quality) { // No op } /** * Get whether the encoder should encode alpha transparency (always false). * * @return Whether the encoder is encoding alpha transparency. */ @Override public boolean isEncodingAlpha() { return false; } /** * Set whether the encoder should encode alpha transparency (not * supported in this ImageEncoder). * * @param encodingAlpha Whether the encoder should encode alpha * transparency. */ @Override public void setEncodingAlpha(boolean encodingAlpha) { // No op } /** * Encodes an image in PNG format. * * @param bufferedImage The image to be encoded. * * @return The byte[] that is the encoded image. * * @throws IOException if there is an IO problem. */ @Override public byte[] encode(BufferedImage bufferedImage) throws IOException { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); encode(bufferedImage, outputStream); return outputStream.toByteArray(); } /** * Encodes an image in PNG format and writes it to an OutputStream. * * @param bufferedImage The image to be encoded. * @param outputStream The OutputStream to write the encoded image to. * @throws IOException if there is an IO problem. */ @Override public void encode(BufferedImage bufferedImage, OutputStream outputStream) throws IOException { Args.nullNotPermitted(bufferedImage, "bufferedImage"); Args.nullNotPermitted(outputStream, "outputStream"); ImageIO.write(bufferedImage, ImageFormat.PNG, outputStream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/encoders/package.html000066400000000000000000000002621463604235500271410ustar00rootroot00000000000000 Classes related to the encoding of charts to different image formats. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/entity/000077500000000000000000000000001463604235500243725ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/entity/AxisEntity.java000066400000000000000000000142211463604235500273360ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * AxisEntity.java * ---------------- * (C) Copyright 2009-present, by David Gilbert and Contributors. * * Original Author: Peter Kolb; * Contributor(s): David Gilbert; * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.entity; import java.awt.Shape; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Objects; import org.jfree.chart.axis.Axis; import org.jfree.chart.util.Args; import org.jfree.chart.util.SerialUtils; /** * A class that captures information about an {@link Axis} belonging to a * chart. */ public class AxisEntity extends ChartEntity { /** For serialization. */ private static final long serialVersionUID = -4445994133561919083L; //same as for ChartEntity! /** The axis for the entity. */ private final Axis axis; /** * Creates a new axis entity. * * @param area the area ({@code null} not permitted). * @param axis the axis ({@code null} not permitted). */ public AxisEntity(Shape area, Axis axis) { // defer argument checks... this(area, axis, null); } /** * Creates a new axis entity. * * @param area the area ({@code null} not permitted). * @param axis the axis ({@code null} not permitted). * @param toolTipText the tool tip text ({@code null} permitted). */ public AxisEntity(Shape area, Axis axis, String toolTipText) { // defer argument checks... this(area, axis, toolTipText, null); } /** * Creates a new axis entity. * * @param area the area ({@code null} not permitted). * @param axis the axis ({@code null} not permitted). * @param toolTipText the tool tip text ({@code null} permitted). * @param urlText the URL text for HTML image maps ({@code null} * permitted). */ public AxisEntity(Shape area, Axis axis, String toolTipText, String urlText) { super(area, toolTipText, urlText); Args.nullNotPermitted(axis, "axis"); this.axis = axis; } /** * Returns the axis that occupies the entity area. * * @return The axis (never {@code null}). */ public Axis getAxis() { return this.axis; } /** * Returns a string representation of the chart entity, useful for * debugging. * * @return A string. */ @Override public String toString() { return "AxisEntity: tooltip = " + getToolTipText(); } /** * Tests the entity for equality with an arbitrary object. * * @param obj the object to test against ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof AxisEntity)) { return false; } AxisEntity that = (AxisEntity) obj; if (!(Objects.equals(this.axis, that.axis))) { return false; } // fix the "equals not symmetric" problem if (!that.canEqual(this)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof AxisEntity); } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int hash = 5; hash = 31 * hash + Objects.hashCode(this.axis); return hash; } /** * Returns a clone of the entity. * * @return A clone. * * @throws CloneNotSupportedException if there is a problem cloning the * entity. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeShape(getArea(), stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); setArea(SerialUtils.readShape(stream)); } }jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/entity/CategoryItemEntity.java000066400000000000000000000150341463604235500310310ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * CategoryItemEntity.java * ----------------------- * (C) Copyright 2002-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Richard Atkinson; * Christian W. Zuckschwerdt; * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.entity; import java.awt.Shape; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.util.Args; import org.jfree.data.category.CategoryDataset; /** * A chart entity that represents one item within a category plot. */ public class CategoryItemEntity extends ChartEntity implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -8657249457902337349L; /** The dataset. */ private CategoryDataset dataset; /** * The row key. */ private Comparable rowKey; /** * The column key. */ private Comparable columnKey; /** * Creates a new entity instance for an item in the specified dataset. * * @param area the 'hotspot' area ({@code null} not permitted). * @param toolTipText the tool tip text. * @param urlText the URL text. * @param dataset the dataset ({@code null} not permitted). * @param rowKey the row key ({@code null} not permitted). * @param columnKey the column key ({@code null} not permitted). */ public CategoryItemEntity(Shape area, String toolTipText, String urlText, CategoryDataset dataset, Comparable rowKey, Comparable columnKey) { super(area, toolTipText, urlText); Args.nullNotPermitted(dataset, "dataset"); this.dataset = dataset; this.rowKey = rowKey; this.columnKey = columnKey; } /** * Returns the dataset this entity refers to. This can be used to * differentiate between items in a chart that displays more than one * dataset. * * @return The dataset (never {@code null}). * * @see #setDataset(CategoryDataset) */ public CategoryDataset getDataset() { return this.dataset; } /** * Sets the dataset this entity refers to. * * @param dataset the dataset ({@code null} not permitted). * * @see #getDataset() */ public void setDataset(CategoryDataset dataset) { Args.nullNotPermitted(dataset, "dataset"); this.dataset = dataset; } /** * Returns the row key. * * @return The row key (never {@code null}). * * @see #setRowKey(Comparable) */ public Comparable getRowKey() { return this.rowKey; } /** * Sets the row key. * * @param rowKey the row key ({@code null} not permitted). * * @see #getRowKey() */ public void setRowKey(Comparable rowKey) { this.rowKey = rowKey; } /** * Returns the column key. * * @return The column key (never {@code null}). * * @see #setColumnKey(Comparable) */ public Comparable getColumnKey() { return this.columnKey; } /** * Sets the column key. * * @param columnKey the column key ({@code null} not permitted). * * @see #getColumnKey() */ public void setColumnKey(Comparable columnKey) { this.columnKey = columnKey; } /** * Returns a string representing this object (useful for debugging * purposes). * * @return A string (never {@code null}). */ @Override public String toString() { return "CategoryItemEntity: rowKey=" + this.rowKey + ", columnKey=" + this.columnKey + ", dataset=" + this.dataset; } /** * Tests the entity for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof CategoryItemEntity)) { return false; } CategoryItemEntity that = (CategoryItemEntity) obj; if (!Objects.equals(this.rowKey, that.rowKey)) { return false; } if (!Objects.equals(this.columnKey, that.columnKey)) { return false; } if (!Objects.equals(this.dataset, that.dataset)) { return false; } // fix the "equals not symmetric" problem if (!that.canEqual(this)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof CategoryItemEntity); } @Override public int hashCode() { int hash = super.hashCode(); // equals calls superclass, hashCode must also hash = 37 * hash + Objects.hashCode(this.dataset); hash = 37 * hash + Objects.hashCode(this.rowKey); hash = 37 * hash + Objects.hashCode(this.columnKey); return hash; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/entity/CategoryLabelEntity.java000066400000000000000000000104711463604235500311520ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * CategoryLabelEntity.java * ------------------------ * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.entity; import java.awt.Shape; import java.util.Objects; import org.jfree.chart.HashUtils; import org.jfree.chart.axis.CategoryAxis; /** * An entity to represent the labels on a {@link CategoryAxis}. */ public class CategoryLabelEntity extends TickLabelEntity { /** The category key. */ private final Comparable key; /** * Creates a new entity. * * @param key the category key ({@code null} not permitted). * @param area the hotspot. * @param toolTipText the tool tip text. * @param urlText the URL text. */ public CategoryLabelEntity(Comparable key, Shape area, String toolTipText, String urlText) { super(area, toolTipText, urlText); Objects.requireNonNull(key); this.key = key; } /** * Returns the category key. * * @return The category key. */ public Comparable getKey() { return this.key; } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof CategoryLabelEntity)) { return false; } CategoryLabelEntity that = (CategoryLabelEntity) obj; // fix the "equals not symmetric" problem if (!that.canEqual(this)) { return false; } if (!Objects.equals(this.key, that.key)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // Solves Problem: equals not symmetric return (other instanceof CategoryLabelEntity); } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = super.hashCode(); // equals calls superclass, hashCode must also result = HashUtils.hashCode(result, this.key); return result; } /** * Returns a string representation of this entity. This is primarily * useful for debugging. * * @return A string representation of this entity. */ @Override public String toString() { StringBuilder sb = new StringBuilder("CategoryLabelEntity: "); sb.append("category="); sb.append(this.key); sb.append(", tooltip=").append(getToolTipText()); sb.append(", url=").append(getURLText()); return sb.toString(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/entity/ChartEntity.java000066400000000000000000000322631463604235500275010ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * ChartEntity.java * ---------------- * (C) Copyright 2002-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Richard Atkinson; * Xavier Poinsard; * Robert Fuller; * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); */ package org.jfree.chart.entity; import java.awt.Shape; import java.awt.geom.PathIterator; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.HashUtils; import org.jfree.chart.imagemap.ToolTipTagFragmentGenerator; import org.jfree.chart.imagemap.URLTagFragmentGenerator; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; /** * A class that captures information about some component of a chart (a bar, * line etc). */ public class ChartEntity implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -4445994133561919083L; /** The area occupied by the entity (in Java 2D space). */ private transient Shape area; /** The tool tip text for the entity. */ private String toolTipText; /** The URL text for the entity. */ private String urlText; /** * Creates a new chart entity. * * @param area the area ({@code null} not permitted). */ public ChartEntity(Shape area) { // defer argument checks... this(area, null); } /** * Creates a new chart entity. * * @param area the area ({@code null} not permitted). * @param toolTipText the tool tip text ({@code null} permitted). */ public ChartEntity(Shape area, String toolTipText) { // defer argument checks... this(area, toolTipText, null); } /** * Creates a new entity. * * @param area the area ({@code null} not permitted). * @param toolTipText the tool tip text ({@code null} permitted). * @param urlText the URL text for HTML image maps ({@code null} * permitted). */ public ChartEntity(Shape area, String toolTipText, String urlText) { Args.nullNotPermitted(area, "area"); this.area = area; this.toolTipText = toolTipText; this.urlText = urlText; } /** * Returns the area occupied by the entity (in Java 2D space). * * @return The area (never {@code null}). */ public Shape getArea() { return this.area; } /** * Sets the area for the entity. *

* This class conveys information about chart entities back to a client. * Setting this area doesn't change the entity (which has already been * drawn). * * @param area the area ({@code null} not permitted). */ public void setArea(Shape area) { Args.nullNotPermitted(area, "area"); this.area = area; } /** * Returns the tool tip text for the entity. Be aware that this text * may have been generated from user supplied data, so for security * reasons some form of filtering should be applied before incorporating * this text into any HTML output. * * @return The tool tip text (possibly {@code null}). */ public String getToolTipText() { return this.toolTipText; } /** * Sets the tool tip text. * * @param text the text ({@code null} permitted). */ public void setToolTipText(String text) { this.toolTipText = text; } /** * Returns the URL text for the entity. Be aware that this text * may have been generated from user supplied data, so some form of * filtering should be applied before this "URL" is used in any output. * * @return The URL text (possibly {@code null}). */ public String getURLText() { return this.urlText; } /** * Sets the URL text. * * @param text the text ({@code null} permitted). */ public void setURLText(String text) { this.urlText = text; } /** * Returns a string describing the entity area. This string is intended * for use in an AREA tag when generating an image map. * * @return The shape type (never {@code null}). */ public String getShapeType() { if (this.area instanceof Rectangle2D) { return "rect"; } else { return "poly"; } } /** * Returns the shape coordinates as a string. * * @return The shape coordinates (never {@code null}). */ public String getShapeCoords() { if (this.area instanceof Rectangle2D) { return getRectCoords((Rectangle2D) this.area); } else { return getPolyCoords(this.area); } } /** * Returns a string containing the coordinates (x1, y1, x2, y2) for a given * rectangle. This string is intended for use in an image map. * * @param rectangle the rectangle ({@code null} not permitted). * * @return Upper left and lower right corner of a rectangle. */ private String getRectCoords(Rectangle2D rectangle) { Args.nullNotPermitted(rectangle, "rectangle"); int x1 = (int) rectangle.getX(); int y1 = (int) rectangle.getY(); int x2 = x1 + (int) rectangle.getWidth(); int y2 = y1 + (int) rectangle.getHeight(); // fix by rfuller if (x2 == x1) { x2++; } if (y2 == y1) { y2++; } // end fix by rfuller return x1 + "," + y1 + "," + x2 + "," + y2; } /** * Returns a string containing the coordinates for a given shape. This * string is intended for use in an image map. * * @param shape the shape ({@code null} not permitted). * * @return The coordinates for a given shape as string. */ private String getPolyCoords(Shape shape) { Args.nullNotPermitted(shape, "shape"); StringBuilder result = new StringBuilder(); boolean first = true; float[] coords = new float[6]; PathIterator pi = shape.getPathIterator(null, 1.0); while (!pi.isDone()) { pi.currentSegment(coords); if (first) { first = false; result.append((int) coords[0]); result.append(",").append((int) coords[1]); } else { result.append(","); result.append((int) coords[0]); result.append(","); result.append((int) coords[1]); } pi.next(); } return result.toString(); } /** * Returns an HTML image map tag for this entity. The returned fragment * should be {@code XHTML 1.0} compliant. * * @param toolTipTagFragmentGenerator a generator for the HTML fragment * that will contain the tooltip text ({@code null} not permitted * if this entity contains tooltip information). * @param urlTagFragmentGenerator a generator for the HTML fragment that * will contain the URL reference ({@code null} not permitted if * this entity has a URL). * * @return The HTML tag. */ public String getImageMapAreaTag( ToolTipTagFragmentGenerator toolTipTagFragmentGenerator, URLTagFragmentGenerator urlTagFragmentGenerator) { StringBuilder tag = new StringBuilder(); boolean hasURL = (this.urlText == null ? false : !this.urlText.equals("")); boolean hasToolTip = (this.toolTipText == null ? false : !this.toolTipText.equals("")); if (hasURL || hasToolTip) { tag.append(""); } return tag.toString(); } /** * Returns a string representation of the chart entity, useful for * debugging. * * @return A string. */ @Override public String toString() { return "ChartEntity: tooltip = " + this.toolTipText; } /** * Tests the entity for equality with an arbitrary object. * * @param obj the object to test against ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof ChartEntity)) { return false; } ChartEntity that = (ChartEntity) obj; if (!Objects.equals(this.area, that.area)) { return false; } if (!Objects.equals(this.toolTipText, that.toolTipText)) { return false; } if (!Objects.equals(this.urlText, that.urlText)) { return false; } // fix the "equals not symmetric" problem if (!that.canEqual(this)) { return false; } return true; } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof ChartEntity); } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = 37; result = HashUtils.hashCode(result, this.toolTipText); result = HashUtils.hashCode(result, this.urlText); result = HashUtils.hashCode(result, this.area); return result; } /** * Returns a clone of the entity. * * @return A clone. * * @throws CloneNotSupportedException if there is a problem cloning the * entity. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeShape(this.area, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.area = SerialUtils.readShape(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/entity/EntityCollection.java000066400000000000000000000056651463604235500305410ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * EntityCollection.java * --------------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.entity; import java.util.Collection; import java.util.Iterator; /** * This interface defines the methods used to access an ordered list of * {@link ChartEntity} objects. */ public interface EntityCollection { /** * Clears all entities. */ void clear(); /** * Adds an entity to the collection. * * @param entity the entity ({@code null} not permitted). */ void add(ChartEntity entity); /** * Adds the entities from another collection to this collection. * * @param collection the other collection. */ void addAll(EntityCollection collection); /** * Returns an entity whose area contains the specified point. * * @param x the x coordinate. * @param y the y coordinate. * * @return The entity. */ ChartEntity getEntity(double x, double y); /** * Returns an entity from the collection. * * @param index the index (zero-based). * * @return An entity. */ ChartEntity getEntity(int index); /** * Returns the entity count. * * @return The entity count. */ int getEntityCount(); /** * Returns the entities in an unmodifiable collection. * * @return The entities. */ Collection getEntities(); /** * Returns an iterator for the entities in the collection. * * @return An iterator. */ Iterator iterator(); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/entity/FlowEntity.java000066400000000000000000000076361463604235500273550ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * FlowEntity.java * --------------- * (C) Copyright 2021-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.entity; import java.awt.Shape; import java.util.Objects; import org.jfree.chart.plot.flow.FlowPlot; import org.jfree.chart.util.Args; import org.jfree.data.flow.FlowKey; /** * A chart entity representing the flow between two nodes in a {@link FlowPlot}. * * @since 1.5.3 */ public class FlowEntity extends ChartEntity { private FlowKey key; /** * Creates a new instance. * * @param key the key identifying the flow ({@code null} not permitted). * @param area the outline of the entity ({@code null} not permitted). * @param toolTipText the tool tip text. * @param urlText the URL text. */ public FlowEntity(FlowKey key, Shape area, String toolTipText, String urlText) { super(area, toolTipText, urlText); Args.nullNotPermitted(key, "key"); this.key = key; } /** * Returns the key identifying the flow. * * @return The flow key (never {@code null}). */ public FlowKey getKey() { return this.key; } /** * Returns a string representation of this instance, primarily for * debugging purposes. * * @return A string. */ @Override public String toString() { return "[FlowEntity: " + this.key + "]"; } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (!(obj instanceof FlowEntity)) { return false; } FlowEntity that = (FlowEntity) obj; if (!java.util.Objects.equals(this.key, that.key)) { return false; } // fix the "equals not symmetric" problem if (!that.canEqual(this)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof FlowEntity); } @Override public int hashCode() { int hash = super.hashCode(); hash = 79 * hash + Objects.hashCode(this.key); return hash; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/entity/JFreeChartEntity.java000066400000000000000000000144031463604235500304110ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * JFreeChartEntity.java * -------------------- * (C) Copyright 2009-present, by David Gilbert and Contributors. * * Original Author: Peter Kolb; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.entity; import java.awt.Shape; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Objects; import org.jfree.chart.JFreeChart; import org.jfree.chart.util.Args; import org.jfree.chart.util.SerialUtils; /** * A class that captures information about an entire chart. */ public class JFreeChartEntity extends ChartEntity { /** For serialization. */ private static final long serialVersionUID = -4445994133561919083L; //same as for ChartEntity! /** The chart. */ private JFreeChart chart; /** * Creates a new chart entity. * * @param area the area ({@code null} not permitted). * @param chart the chart ({@code null} not permitted). */ public JFreeChartEntity(Shape area, JFreeChart chart) { // defer argument checks... this(area, chart, null); } /** * Creates a new chart entity. * * @param area the area ({@code null} not permitted). * @param chart the chart ({@code null} not permitted). * @param toolTipText the tool tip text ({@code null} permitted). */ public JFreeChartEntity(Shape area, JFreeChart chart, String toolTipText) { // defer argument checks... this(area, chart, toolTipText, null); } /** * Creates a new chart entity. * * @param area the area ({@code null} not permitted). * @param chart the chart ({@code null} not permitted). * @param toolTipText the tool tip text ({@code null} permitted). * @param urlText the URL text for HTML image maps ({@code null} * permitted). */ public JFreeChartEntity(Shape area, JFreeChart chart, String toolTipText, String urlText) { super(area, toolTipText, urlText); Args.nullNotPermitted(chart, "chart"); this.chart = chart; } /** * Returns the chart that occupies the entity area. * * @return The chart (never {@code null}). */ public JFreeChart getChart() { return this.chart; } /** * Returns a string representation of the chart entity, useful for * debugging. * * @return A string. */ @Override public String toString() { StringBuilder sb = new StringBuilder("JFreeChartEntity: "); sb.append("tooltip = "); sb.append(getToolTipText()); return sb.toString(); } /** * Tests the entity for equality with an arbitrary object. * * @param obj the object to test against ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof JFreeChartEntity)) { return false; } JFreeChartEntity that = (JFreeChartEntity) obj; // fix the "equals not symmetric" problem if (!that.canEqual(this)) { return false; } if (!(Objects.equals(this.chart, that.chart))) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // Solves Problem: equals not symmetric return (other instanceof JFreeChartEntity); } @Override public int hashCode() { int hash = super.hashCode(); // equals calls superclass function, so hashCode must also hash = 59 * hash + Objects.hashCode(this.chart); return hash; } /** * Returns a clone of the entity. * * @return A clone. * * @throws CloneNotSupportedException if there is a problem cloning the * entity. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeShape(getArea(), stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); setArea(SerialUtils.readShape(stream)); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/entity/LegendItemEntity.java000066400000000000000000000125401463604235500304510ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * LegendItemEntity.java * --------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.entity; import java.awt.Shape; import java.io.Serializable; import java.util.Objects; import org.jfree.data.general.Dataset; /** * An entity that represents an item within a legend. */ public class LegendItemEntity extends ChartEntity implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -7435683933545666702L; /** * The dataset. */ private Dataset dataset; /** * The series key. */ private Comparable seriesKey; /** * Creates a legend item entity. * * @param area the area. */ public LegendItemEntity(Shape area) { super(area); } /** * Returns a reference to the dataset that this legend item is derived * from. * * @return The dataset. * * @see #setDataset(Dataset) */ public Dataset getDataset() { return this.dataset; } /** * Sets a reference to the dataset that this legend item is derived from. * * @param dataset the dataset. */ public void setDataset(Dataset dataset) { this.dataset = dataset; } /** * Returns the series key that identifies the legend item. * * @return The series key. * * @see #setSeriesKey(Comparable) */ public Comparable getSeriesKey() { return this.seriesKey; } /** * Sets the key for the series. * * @param key the key. * * @see #getSeriesKey() */ public void setSeriesKey(Comparable key) { this.seriesKey = key; } /** * Tests this object for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof LegendItemEntity)) { return false; } LegendItemEntity that = (LegendItemEntity) obj; // fix the "equals not symmetric" problem if (!that.canEqual(this)) { return false; } if (!Objects.equals(this.seriesKey, that.seriesKey)) { return false; } if (!Objects.equals(this.dataset, that.dataset)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // Solves Problem: equals not symmetric return (other instanceof LegendItemEntity); } @Override public int hashCode() { int hash = super.hashCode(); // equals calls superclass function, so hashCode must also hash = 97 * hash + Objects.hashCode(this.dataset); hash = 97 * hash + Objects.hashCode(this.seriesKey); return hash; } /** * Returns a clone of the entity. * * @return A clone. * * @throws CloneNotSupportedException if there is a problem cloning the * object. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Returns a string representing this object (useful for debugging * purposes). * * @return A string (never {@code null}). */ @Override public String toString() { return "LegendItemEntity: seriesKey=" + this.seriesKey + ", dataset=" + this.dataset; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/entity/NodeEntity.java000066400000000000000000000055551463604235500273310ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * NodeEntity.java * --------------- * (C) Copyright 2021-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.entity; import java.awt.Shape; import org.jfree.chart.plot.flow.FlowPlot; import org.jfree.chart.util.Args; import org.jfree.data.flow.NodeKey; /** * A chart entity representing a node in a {@link FlowPlot}. * * @since 1.5.3 */ public class NodeEntity extends ChartEntity { private NodeKey key; /** * Creates a new instance. * * @param key the node key ({@code null} not permitted). * @param area the outline of the entity ({@code null} not permitted). * @param toolTipText the tool tip text. */ public NodeEntity(NodeKey key, Shape area, String toolTipText) { super(area, toolTipText); Args.nullNotPermitted(key, "key"); this.key = key; } /** * Creates a new instance. * * @param area the outline of the entity ({@code null} not permitted). * @param toolTipText the tool tip text. * @param urlText the URL text. */ public NodeEntity(Shape area, String toolTipText, String urlText) { super(area, toolTipText, urlText); } /** * Returns the node key. * * @return The node key (never {@code null}). */ public NodeKey getKey() { return this.key; } /** * Returns a string representation of this instance, primarily for * debugging purposes. * * @return A string. */ @Override public String toString() { return "[NodeEntity: " + this.key + "]"; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/entity/PieSectionEntity.java000066400000000000000000000162401463604235500304770ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * PieSectionEntity.java * --------------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Richard Atkinson; * Christian W. Zuckschwerdt; * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.entity; import java.awt.Shape; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.HashUtils; import org.jfree.data.general.PieDataset; /** * A chart entity that represents one section within a pie plot. */ public class PieSectionEntity extends ChartEntity implements Serializable { /** For serialization. */ private static final long serialVersionUID = 9199892576531984162L; /** The dataset. */ private PieDataset dataset; /** The pie index. */ private int pieIndex; /** The section index. */ private int sectionIndex; /** The section key. */ private Comparable sectionKey; /** * Creates a new pie section entity. * * @param area the area. * @param dataset the pie dataset. * @param pieIndex the pie index (zero-based). * @param sectionIndex the section index (zero-based). * @param sectionKey the section key. * @param toolTipText the tool tip text. * @param urlText the URL text for HTML image maps. */ public PieSectionEntity(Shape area, PieDataset dataset, int pieIndex, int sectionIndex, Comparable sectionKey, String toolTipText, String urlText) { super(area, toolTipText, urlText); this.dataset = dataset; this.pieIndex = pieIndex; this.sectionIndex = sectionIndex; this.sectionKey = sectionKey; } /** * Returns the dataset this entity refers to. * * @return The dataset. * * @see #setDataset(PieDataset) */ public PieDataset getDataset() { return this.dataset; } /** * Sets the dataset this entity refers to. * * @param dataset the dataset. * * @see #getDataset() */ public void setDataset(PieDataset dataset) { this.dataset = dataset; } /** * Returns the pie index. For a regular pie chart, the section index is 0. * For a pie chart containing multiple pie plots, the pie index is the row * or column index from which the pie data is extracted. * * @return The pie index. * * @see #setPieIndex(int) */ public int getPieIndex() { return this.pieIndex; } /** * Sets the pie index. * * @param index the new index value. * * @see #getPieIndex() */ public void setPieIndex(int index) { this.pieIndex = index; } /** * Returns the section index. * * @return The section index. * * @see #setSectionIndex(int) */ public int getSectionIndex() { return this.sectionIndex; } /** * Sets the section index. * * @param index the section index. * * @see #getSectionIndex() */ public void setSectionIndex(int index) { this.sectionIndex = index; } /** * Returns the section key. * * @return The section key. * * @see #setSectionKey(Comparable) */ public Comparable getSectionKey() { return this.sectionKey; } /** * Sets the section key. * * @param key the section key. * * @see #getSectionKey() */ public void setSectionKey(Comparable key) { this.sectionKey = key; } /** * Tests this entity for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof PieSectionEntity)) { return false; } PieSectionEntity that = (PieSectionEntity) obj; // fix the "equals not symmetric" problem if (!that.canEqual(this)) { return false; } if (!Objects.equals(this.dataset, that.dataset)) { return false; } if (this.pieIndex != that.pieIndex) { return false; } if (this.sectionIndex != that.sectionIndex) { return false; } if (!Objects.equals(this.sectionKey, that.sectionKey)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // Solves Problem: equals not symmetric return (other instanceof PieSectionEntity); } @Override public int hashCode() { int result = super.hashCode(); result = HashUtils.hashCode(result, this.dataset); result = HashUtils.hashCode(result, this.pieIndex); result = HashUtils.hashCode(result, this.sectionIndex); result = HashUtils.hashCode(result, this.sectionKey); return result; } /** * Returns a string representing the entity. * * @return A string representing the entity. */ @Override public String toString() { return "PieSection: " + this.pieIndex + ", " + this.sectionIndex + "(" + this.sectionKey.toString() + ")"; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/entity/PlotEntity.java000066400000000000000000000144421463604235500273550ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * PlotEntity.java * --------------- * (C) Copyright 2009-present, by David Gilbert and Contributors. * * Original Author: Peter Kolb; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.entity; import java.awt.Shape; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import org.jfree.chart.plot.Plot; import org.jfree.chart.HashUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.SerialUtils; /** * A class that captures information about a plot. */ public class PlotEntity extends ChartEntity { /** For serialization. */ private static final long serialVersionUID = -4445994133561919083L; //same as for ChartEntity! /** The plot. */ private Plot plot; /** * Creates a new plot entity. * * @param area the area ({@code null} not permitted). * @param plot the plot ({@code null} not permitted). */ public PlotEntity(Shape area, Plot plot) { // defer argument checks... this(area, plot, null); } /** * Creates a new plot entity. * * @param area the area ({@code null} not permitted). * @param plot the plot ({@code null} not permitted). * @param toolTipText the tool tip text ({@code null} permitted). */ public PlotEntity(Shape area, Plot plot, String toolTipText) { // defer argument checks... this(area, plot, toolTipText, null); } /** * Creates a new plot entity. * * @param area the area ({@code null} not permitted). * @param plot the plot ({@code null} not permitted). * @param toolTipText the tool tip text ({@code null} permitted). * @param urlText the URL text for HTML image maps ({@code null} * permitted). */ public PlotEntity(Shape area, Plot plot, String toolTipText, String urlText) { super(area, toolTipText, urlText); Args.nullNotPermitted(plot, "plot"); this.plot = plot; } /** * Returns the plot that occupies the entity area. * * @return The plot (never {@code null}). */ public Plot getPlot() { return this.plot; } /** * Returns a string representation of the plot entity, useful for * debugging. * * @return A string. */ @Override public String toString() { StringBuilder sb = new StringBuilder("PlotEntity: "); sb.append("tooltip = "); sb.append(getToolTipText()); return sb.toString(); } /** * Tests the entity for equality with an arbitrary object. * * @param obj the object to test against ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof PlotEntity)) { return false; } PlotEntity that = (PlotEntity) obj; // fix the "equals not symmetric" problem if (!that.canEqual(this)) { return false; } if (!(this.plot.equals(that.plot))) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // Solves Problem: equals not symmetric return (other instanceof PlotEntity); } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = super.hashCode(); // equals calls superclass function, so hashCode must also result = HashUtils.hashCode(result, getToolTipText()); result = HashUtils.hashCode(result, getURLText()); return result; } /** * Returns a clone of the entity. * * @return A clone. * * @throws CloneNotSupportedException if there is a problem cloning the * entity. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeShape(getArea(), stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); setArea(SerialUtils.readShape(stream)); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/entity/StandardEntityCollection.java000066400000000000000000000135511463604235500322130ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------- * StandardEntityCollection.java * ----------------------------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.entity; import java.io.Serializable; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Objects; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; /** * A standard implementation of the {@link EntityCollection} interface. */ public class StandardEntityCollection implements EntityCollection, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 5384773031184897047L; /** Storage for the entities. */ private List entities; /** * Constructs a new entity collection (initially empty). */ public StandardEntityCollection() { this.entities = new java.util.ArrayList(); } /** * Returns the number of entities in the collection. * * @return The entity count. */ @Override public int getEntityCount() { return this.entities.size(); } /** * Returns a chart entity from the collection. * * @param index the entity index. * * @return The entity. * * @see #add(ChartEntity) */ @Override public ChartEntity getEntity(int index) { return (ChartEntity) this.entities.get(index); } /** * Clears all the entities from the collection. */ @Override public void clear() { this.entities.clear(); } /** * Adds an entity to the collection. * * @param entity the entity ({@code null} not permitted). */ @Override public void add(ChartEntity entity) { Args.nullNotPermitted(entity, "entity"); this.entities.add(entity); } /** * Adds all the entities from the specified collection. * * @param collection the collection of entities ({@code null} not * permitted). */ @Override public void addAll(EntityCollection collection) { this.entities.addAll(collection.getEntities()); } /** * Returns the last entity in the list with an area that encloses the * specified coordinates, or {@code null} if there is no such entity. * * @param x the x coordinate. * @param y the y coordinate. * * @return The entity (possibly {@code null}). */ @Override public ChartEntity getEntity(double x, double y) { int entityCount = this.entities.size(); for (int i = entityCount - 1; i >= 0; i--) { ChartEntity entity = (ChartEntity) this.entities.get(i); if (entity.getArea().contains(x, y)) { return entity; } } return null; } /** * Returns the entities in an unmodifiable collection. * * @return The entities. */ @Override public Collection getEntities() { return Collections.unmodifiableCollection(this.entities); } /** * Returns an iterator for the entities in the collection. * * @return An iterator. */ @Override public Iterator iterator() { return this.entities.iterator(); } /** * Tests this object for equality with an arbitrary object. * * @param obj the object to test against ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj instanceof StandardEntityCollection) { StandardEntityCollection that = (StandardEntityCollection) obj; return Objects.equals(this.entities, that.entities); } return false; } /** * Returns a clone of this entity collection. * * @return A clone. * * @throws CloneNotSupportedException if the object cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { StandardEntityCollection clone = (StandardEntityCollection) super.clone(); clone.entities = new java.util.ArrayList(this.entities.size()); for (int i = 0; i < this.entities.size(); i++) { ChartEntity entity = (ChartEntity) this.entities.get(i); clone.entities.add(entity.clone()); } return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/entity/TickLabelEntity.java000066400000000000000000000043641463604235500302730ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * TickLabelEntity.java * -------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.entity; import java.awt.Shape; import java.io.Serializable; /** * A chart entity representing a tick label. */ public class TickLabelEntity extends ChartEntity implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 681583956588092095L; /** * Creates a new entity. * * @param area the area ({@code null} not permitted). * @param toolTipText the tool tip text ({@code null} permitted). * @param urlText the URL text for HTML image maps ({@code null} * permitted). */ public TickLabelEntity(Shape area, String toolTipText, String urlText) { super(area, toolTipText, urlText); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/entity/TitleEntity.java000066400000000000000000000152541463604235500275220ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * TitleEntity.java * ---------------- * (C) Copyright 2009-present, by David Gilbert and Contributors. * * Original Author: Peter Kolb; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.entity; import java.awt.Shape; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Objects; import org.jfree.chart.HashUtils; import org.jfree.chart.title.Title; import org.jfree.chart.util.Args; import org.jfree.chart.util.SerialUtils; /** * A class that captures information about a Title of a chart. */ public class TitleEntity extends ChartEntity { /** For serialization. */ private static final long serialVersionUID = -4445994133561919083L; //same as for ChartEntity! /** The Title for the entity. */ private Title title; /** * Creates a new chart entity. * * @param area the area ({@code null} not permitted). * @param title the title ({@code null} not permitted). */ public TitleEntity(Shape area, Title title) { // defer argument checks... this(area, title, null); } /** * Creates a new chart entity. * * @param area the area ({@code null} not permitted). * @param title the title ({@code null} not permitted). * @param toolTipText the tool tip text ({@code null} permitted). */ public TitleEntity(Shape area, Title title, String toolTipText) { // defer argument checks... this(area, title, toolTipText, null); } /** * Creates a new entity. * * @param area the area ({@code null} not permitted). * @param title the title ({@code null} not permitted). * @param toolTipText the tool tip text ({@code null} permitted). * @param urlText the URL text for HTML image maps ({@code null} * permitted). */ public TitleEntity(Shape area, Title title, String toolTipText, String urlText) { super(area, toolTipText, urlText); Args.nullNotPermitted(title, "title"); this.title = title; } /** * Returns the title that occupies the entity area. * * @return The title (never {@code null}). */ public Title getTitle() { return this.title; } /** * Returns a string representation of the chart entity, useful for * debugging. * * @return A string. */ @Override public String toString() { StringBuilder sb = new StringBuilder("TitleEntity: "); sb.append("tooltip = "); sb.append(getToolTipText()); return sb.toString(); } /** * Tests the entity for equality with an arbitrary object. * * @param obj the object to test against ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof TitleEntity)) { return false; } TitleEntity that = (TitleEntity) obj; // fix the "equals not symmetric" problem if (!that.canEqual(this)) { return false; } if (!getArea().equals(that.getArea())) { return false; } if (!Objects.equals(getToolTipText(), that.getToolTipText())) { return false; } if (!Objects.equals(getURLText(), that.getURLText())) { return false; } if (!(Objects.equals(this.title, that.title))) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // Solves Problem: equals not symmetric return (other instanceof TitleEntity); } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = super.hashCode(); // equals calls superclass function, so hashCode must also result = HashUtils.hashCode(result, getToolTipText()); result = HashUtils.hashCode(result, getURLText()); return result; } /** * Returns a clone of the entity. * * @return A clone. * * @throws CloneNotSupportedException if there is a problem cloning the * entity. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeShape(getArea(), stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); setArea(SerialUtils.readShape(stream)); } }jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/entity/XYAnnotationEntity.java000066400000000000000000000103301463604235500310220ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * XYAnnotationEntity.java * ----------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.entity; import java.awt.Shape; import java.io.Serializable; /** * A chart entity that represents an annotation on an * {@link org.jfree.chart.plot.XYPlot}. */ public class XYAnnotationEntity extends ChartEntity implements Serializable { /** For serialization. */ private static final long serialVersionUID = 2340334068383660799L; /** The renderer index. */ private int rendererIndex; /** * Creates a new entity. * * @param hotspot the area. * @param rendererIndex the rendererIndex (zero-based index). * @param toolTipText the tool tip text. * @param urlText the URL text for HTML image maps. */ public XYAnnotationEntity(Shape hotspot, int rendererIndex, String toolTipText, String urlText) { super(hotspot, toolTipText, urlText); this.rendererIndex = rendererIndex; } /** * Returns the renderer index. * * @return The renderer index. */ public int getRendererIndex() { return this.rendererIndex; } /** * Sets the renderer index. * * @param index the item index (zero-based). */ public void setRendererIndex(int index) { this.rendererIndex = index; } /** * Tests the entity for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof XYAnnotationEntity)) { return false; } XYAnnotationEntity that = (XYAnnotationEntity) obj; // fix the "equals not symmetric" problem if (!that.canEqual(this)) { return false; } // compare fields in this class if (this.rendererIndex != that.rendererIndex) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // Solves Problem: equals not symmetric return (other instanceof XYAnnotationEntity); } @Override public int hashCode() { int hash = super.hashCode(); // equals calls superclass function, so hashCode must also hash = 37 * hash + this.rendererIndex; return hash; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/entity/XYItemEntity.java000066400000000000000000000130611463604235500276120ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * XYItemEntity.java * ----------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Richard Atkinson; * Christian W. Zuckschwerdt; * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.entity; import java.awt.Shape; import org.jfree.data.xy.XYDataset; /** * A chart entity that represents one item within an * {@link org.jfree.chart.plot.XYPlot}. */ public class XYItemEntity extends ChartEntity { /** For serialization. */ private static final long serialVersionUID = -3870862224880283771L; /** The dataset. */ private transient XYDataset dataset; /** The series. */ private int series; /** The item. */ private int item; /** * Creates a new entity. * * @param area the area. * @param dataset the dataset. * @param series the series (zero-based index). * @param item the item (zero-based index). * @param toolTipText the tool tip text. * @param urlText the URL text for HTML image maps. */ public XYItemEntity(Shape area, XYDataset dataset, int series, int item, String toolTipText, String urlText) { super(area, toolTipText, urlText); this.dataset = dataset; this.series = series; this.item = item; } /** * Returns the dataset this entity refers to. * * @return The dataset. */ public XYDataset getDataset() { return this.dataset; } /** * Sets the dataset this entity refers to. * * @param dataset the dataset. */ public void setDataset(XYDataset dataset) { this.dataset = dataset; } /** * Returns the series index. * * @return The series index. */ public int getSeriesIndex() { return this.series; } /** * Sets the series index. * * @param series the series index (zero-based). */ public void setSeriesIndex(int series) { this.series = series; } /** * Returns the item index. * * @return The item index. */ public int getItem() { return this.item; } /** * Sets the item index. * * @param item the item index (zero-based). */ public void setItem(int item) { this.item = item; } /** * Tests the entity for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof XYItemEntity)) { return false; } XYItemEntity that = (XYItemEntity) obj; // fix the "equals not symmetric" problem if (!that.canEqual(this)) { return false; } // compare fields in this class if (this.series != that.series) { return false; } if (this.item != that.item) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // Solves Problem: equals not symmetric return (other instanceof XYItemEntity); } @Override public int hashCode() { int hash = super.hashCode(); // equals calls superclass function, so hashCode must also hash = 37 * hash + this.series; hash = 37 * hash + this.item; return hash; } /** * Returns a string representation of this instance, useful for debugging * purposes. * * @return A string. */ @Override public String toString() { return "XYItemEntity: series = " + getSeriesIndex() + ", item = " + getItem() + ", dataset = " + getDataset(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/entity/package.html000066400000000000000000000002511463604235500266510ustar00rootroot00000000000000 Classes representing components of (or entities in) a chart. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/event/000077500000000000000000000000001463604235500241775ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/event/AnnotationChangeEvent.java000066400000000000000000000047321463604235500312720ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * AnnotationChangeEvent.java * -------------------------- * (C) Copyright 2009-present, by David Gilbert and Contributors. * * Original Author: Peter Kolb (patch 2809117); * Contributor(s): ; * */ package org.jfree.chart.event; import org.jfree.chart.annotations.Annotation; import org.jfree.chart.util.Args; /** * An event that can be forwarded to any {@link AnnotationChangeListener} to * signal a change to an {@link Annotation}. */ public class AnnotationChangeEvent extends ChartChangeEvent { /** The annotation that generated the event. */ private final Annotation annotation; /** * Creates a new {@code AnnotationChangeEvent} instance. * * @param source the event source. * @param annotation the annotation that triggered the event * ({@code null} not permitted). */ public AnnotationChangeEvent(Object source, Annotation annotation) { super(source); Args.nullNotPermitted(annotation, "annotation"); this.annotation = annotation; } /** * Returns the annotation that triggered the event. * * @return The annotation that triggered the event (never {@code null}). */ public Annotation getAnnotation() { return this.annotation; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/event/AnnotationChangeListener.java000066400000000000000000000036311463604235500317730ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * AnnotationChangeListener.java * ------------------------- * (C) Copyright 2009-present, by David Gilbert and Contributors. * * Original Author: Peter Kolb (patch 2809117); * Contributor(s): ; * */ package org.jfree.chart.event; import java.util.EventListener; import org.jfree.chart.annotations.Annotation; /** * The interface that must be supported by classes that wish to receive * notification of changes to an {@link Annotation}. */ public interface AnnotationChangeListener extends EventListener { /** * Receives notification of an annotation change event. * * @param event the event. */ void annotationChanged(AnnotationChangeEvent event); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/event/AxisChangeEvent.java000066400000000000000000000041631463604235500300620ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * AxisChangeEvent.java * -------------------- * (C) Copyright 2000-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.event; import org.jfree.chart.axis.Axis; /** * A change event that encapsulates information about a change to an axis. */ public class AxisChangeEvent extends ChartChangeEvent { /** The axis that generated the change event. */ private final Axis axis; /** * Creates a new AxisChangeEvent. * * @param axis the axis that generated the event. */ public AxisChangeEvent(Axis axis) { super(axis); this.axis = axis; } /** * Returns the axis that generated the event. * * @return The axis that generated the event. */ public Axis getAxis() { return this.axis; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/event/AxisChangeListener.java000066400000000000000000000042111463604235500305600ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * AxisChangeEvent.java * -------------------- * (C) Copyright 2000-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.event; import java.util.EventListener; /** * The interface that must be supported by classes that wish to receive * notification of changes to an axis. *

* The Plot class implements this interface, and automatically registers with * its axes (if any). Any axis changes are passed on by the plot as a plot * change event. This is part of the notification mechanism that ensures that * charts are redrawn whenever changes are made to any chart component. * */ public interface AxisChangeListener extends EventListener { /** * Receives notification of an axis change event. * * @param event the event. */ void axisChanged(AxisChangeEvent event); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/event/ChartChangeEvent.java000066400000000000000000000073331463604235500302210ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * ChartChangeEvent.java * --------------------- * (C) Copyright 2000-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.event; import java.util.EventObject; import org.jfree.chart.JFreeChart; /** * A change event that encapsulates information about a change to a chart. */ public class ChartChangeEvent extends EventObject { /** The type of event. */ private ChartChangeEventType type; /** The chart that generated the event. */ private JFreeChart chart; /** * Creates a new chart change event. * * @param source the source of the event (could be the chart, a title, * an axis etc.) */ public ChartChangeEvent(Object source) { this(source, null, ChartChangeEventType.GENERAL); } /** * Creates a new chart change event. * * @param source the source of the event (could be the chart, a title, an * axis etc.) * @param chart the chart that generated the event. */ public ChartChangeEvent(Object source, JFreeChart chart) { this(source, chart, ChartChangeEventType.GENERAL); } /** * Creates a new chart change event. * * @param source the source of the event (could be the chart, a title, an axis etc.) * @param chart the chart that generated the event. * @param type the type of event. */ public ChartChangeEvent(Object source, JFreeChart chart, ChartChangeEventType type) { super(source); this.chart = chart; this.type = type; } /** * Returns the chart that generated the change event. * * @return The chart that generated the change event. */ public JFreeChart getChart() { return this.chart; } /** * Sets the chart that generated the change event. * * @param chart the chart that generated the event. */ public void setChart(JFreeChart chart) { this.chart = chart; } /** * Returns the event type. * * @return The event type. */ public ChartChangeEventType getType() { return this.type; } /** * Sets the event type. * * @param type the event type. */ public void setType(ChartChangeEventType type) { this.type = type; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/event/ChartChangeEventType.java000066400000000000000000000101561463604235500310600ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * ChartChangeEventType.java * ------------------------- * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.event; import java.io.ObjectStreamException; import java.io.Serializable; /** * Defines tokens used to indicate an event type. */ public final class ChartChangeEventType implements Serializable { /** For serialization. */ private static final long serialVersionUID = 5481917022435735602L; /** GENERAL. */ public static final ChartChangeEventType GENERAL = new ChartChangeEventType("ChartChangeEventType.GENERAL"); /** NEW_DATASET. */ public static final ChartChangeEventType NEW_DATASET = new ChartChangeEventType("ChartChangeEventType.NEW_DATASET"); /** DATASET_UPDATED. */ public static final ChartChangeEventType DATASET_UPDATED = new ChartChangeEventType("ChartChangeEventType.DATASET_UPDATED"); /** The name. */ private final String name; /** * Private constructor. * * @param name the name. */ private ChartChangeEventType(String name) { this.name = name; } /** * Returns a string representing the object. * * @return The string. */ @Override public String toString() { return this.name; } /** * Returns {@code true} if this object is equal to the specified * object, and {@code false} otherwise. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof ChartChangeEventType)) { return false; } ChartChangeEventType that = (ChartChangeEventType) obj; if (!this.name.equals(that.toString())) { return false; } return true; } /** * Returns a hash code value for the object. * * @return The hashcode */ @Override public int hashCode() { return this.name.hashCode(); } /** * Ensures that serialization returns the unique instances. * * @return The object. * * @throws ObjectStreamException if there is a problem. */ private Object readResolve() throws ObjectStreamException { if (this.equals(ChartChangeEventType.GENERAL)) { return ChartChangeEventType.GENERAL; } else if (this.equals(ChartChangeEventType.NEW_DATASET)) { return ChartChangeEventType.NEW_DATASET; } else if (this.equals(ChartChangeEventType.DATASET_UPDATED)) { return ChartChangeEventType.DATASET_UPDATED; } return null; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/event/ChartChangeListener.java000066400000000000000000000037721463604235500307300ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * ChartChangeListener.java * ------------------------ * (C) Copyright 2000-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.event; import java.util.EventListener; /** * The interface that must be supported by classes that wish to receive * notification of chart events. *

* The {@link org.jfree.chart.ChartPanel} class registers itself with the * chart it displays, and whenever the chart changes, the panel redraws itself. * */ public interface ChartChangeListener extends EventListener { /** * Receives notification of a chart change event. * * @param event the event. */ void chartChanged(ChartChangeEvent event); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/event/ChartProgressEvent.java000066400000000000000000000073121463604235500306350ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * ChartProgressEvent.java * ----------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.event; import org.jfree.chart.JFreeChart; /** * An event that contains information about the drawing progress of a chart. */ public class ChartProgressEvent extends java.util.EventObject { /** Indicates drawing has started. */ public static final int DRAWING_STARTED = 1; /** Indicates drawing has finished. */ public static final int DRAWING_FINISHED = 2; /** The type of event. */ private int type; /** The percentage of completion. */ private int percent; /** The chart that generated the event. */ private JFreeChart chart; /** * Creates a new chart change event. * * @param source the source of the event (could be the chart, a title, an * axis etc.) * @param chart the chart that generated the event. * @param type the type of event. * @param percent the percentage of completion. */ public ChartProgressEvent(Object source, JFreeChart chart, int type, int percent) { super(source); this.chart = chart; this.type = type; this.percent = percent; } /** * Returns the chart that generated the change event. * * @return The chart that generated the change event. */ public JFreeChart getChart() { return this.chart; } /** * Sets the chart that generated the change event. * * @param chart the chart that generated the event. */ public void setChart(JFreeChart chart) { this.chart = chart; } /** * Returns the event type. * * @return The event type. */ public int getType() { return this.type; } /** * Sets the event type. * * @param type the event type. */ public void setType(int type) { this.type = type; } /** * Returns the percentage complete. * * @return The percentage complete. */ public int getPercent() { return this.percent; } /** * Sets the percentage complete. * * @param percent the percentage. */ public void setPercent(int percent) { this.percent = percent; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/event/ChartProgressListener.java000066400000000000000000000035501463604235500313410ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * ChartProgressListener.java * -------------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.event; import java.util.EventListener; /** * The interface that must be supported by classes that wish to receive * notification of chart progress events. */ public interface ChartProgressListener extends EventListener { /** * Receives notification of a chart progress event. * * @param event the event. */ void chartProgress(ChartProgressEvent event); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/event/MarkerChangeEvent.java000066400000000000000000000044451463604235500304020ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * MarkerChangeEvent.java * ---------------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.event; import org.jfree.chart.plot.Marker; /** * An event that can be forwarded to any {@link MarkerChangeListener} to * signal a change to a {@link Marker}. */ public class MarkerChangeEvent extends ChartChangeEvent { /** The plot that generated the event. */ private final Marker marker; /** * Creates a new {@code MarkerChangeEvent} instance. * * @param marker the marker that triggered the event ({@code null} * not permitted). */ public MarkerChangeEvent(Marker marker) { super(marker); // null check is in here this.marker = marker; } /** * Returns the marker that triggered the event. * * @return The marker that triggered the event (never {@code null}). */ public Marker getMarker() { return this.marker; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/event/MarkerChangeListener.java000066400000000000000000000036171463604235500311060ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * MarkerChangeListener.java * ------------------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.event; import java.util.EventListener; import org.jfree.chart.plot.Marker; /** * The interface that must be supported by classes that wish to receive * notification of changes to a {@link Marker}. */ public interface MarkerChangeListener extends EventListener { /** * Receives notification of a marker change event. * * @param event the event. */ void markerChanged(MarkerChangeEvent event); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/event/OverlayChangeEvent.java000066400000000000000000000035031463604235500305740ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * OverlayChangeEvent.java * ----------------------- * (C) Copyright 2009-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.event; import java.util.EventObject; import org.jfree.chart.panel.Overlay; /** * A change event for an {@link Overlay}. */ public class OverlayChangeEvent extends EventObject { /** * Creates a new change event. * * @param source the event source ({@code null} not permitted). */ public OverlayChangeEvent(Object source) { super(source); // null check is in here } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/event/OverlayChangeListener.java000066400000000000000000000034451463604235500313050ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * OverlayChangeListener.java * -------------------------- * (C) Copyright 2009-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.event; import java.util.EventListener; import org.jfree.chart.panel.Overlay; /** * A listener for changes to an {@link Overlay}. */ public interface OverlayChangeListener extends EventListener { /** * This method is called to notify a listener of a change event. * * @param event the event. */ void overlayChanged(OverlayChangeEvent event); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/event/PlotChangeEvent.java000066400000000000000000000043451463604235500300760ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * PlotChangeEvent.java * -------------------- * (C) Copyright 2000-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.event; import org.jfree.chart.plot.Plot; /** * An event that can be forwarded to any * {@link org.jfree.chart.event.PlotChangeListener} to signal a change to a * plot. */ public class PlotChangeEvent extends ChartChangeEvent { /** The plot that generated the event. */ private Plot plot; /** * Creates a new PlotChangeEvent. * * @param plot the plot that generated the event ({@code null} not * permitted). */ public PlotChangeEvent(Plot plot) { super(plot); this.plot = plot; } /** * Returns the plot that generated the event (set in the constructor). * * @return The plot that generated the event. */ public Plot getPlot() { return this.plot; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/event/PlotChangeListener.java000066400000000000000000000035241463604235500306000ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * PlotChangeListener.java * ----------------------- * (C) Copyright 2000-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.event; import java.util.EventListener; /** * The interface that must be supported by classes that wish to receive * notification of changes to a plot. * */ public interface PlotChangeListener extends EventListener { /** * Receives notification of a plot change event. * * @param event the event. */ void plotChanged(PlotChangeEvent event); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/event/RendererChangeEvent.java000066400000000000000000000062171463604235500307260ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * RendererChangeEvent.java * ------------------------ * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.event; /** * An event that can be forwarded to any {@link RendererChangeListener} to * signal a change to a renderer. */ public class RendererChangeEvent extends ChartChangeEvent { /** The renderer that generated the event. */ private Object renderer; /** * A flag that indicates whether this event relates to a change in the * series visibility. If so, the receiver (if it is a plot) may want to * update the axis bounds. */ private boolean seriesVisibilityChanged; /** * Creates a new event. * * @param renderer the renderer that generated the event. */ public RendererChangeEvent(Object renderer) { this(renderer, false); } /** * Creates a new event. * * @param renderer the renderer that generated the event. * @param seriesVisibilityChanged a flag that indicates whether or not * the event relates to a change in the series visibility flags. */ public RendererChangeEvent(Object renderer, boolean seriesVisibilityChanged) { super(renderer); this.renderer = renderer; this.seriesVisibilityChanged = seriesVisibilityChanged; } /** * Returns the renderer that generated the event. * * @return The renderer that generated the event. */ public Object getRenderer() { return this.renderer; } /** * Returns the flag that indicates whether or not the event relates to * a change in series visibility. * * @return A boolean. */ public boolean getSeriesVisibilityChanged() { return this.seriesVisibilityChanged; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/event/RendererChangeListener.java000066400000000000000000000035601463604235500314300ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * RendererChangeListener.java * --------------------------- * (C) Copyright 2000-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.event; import java.util.EventListener; /** * The interface that must be supported by classes that wish to receive * notification of changes to a renderer. */ public interface RendererChangeListener extends EventListener { /** * Receives notification of a renderer change event. * * @param event the event. */ void rendererChanged(RendererChangeEvent event); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/event/TitleChangeEvent.java000066400000000000000000000042041463604235500302330ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * TitleChangeEvent.java * --------------------- * (C) Copyright 2000-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.event; import org.jfree.chart.title.Title; /** * A change event that encapsulates information about a change to a chart title. */ public class TitleChangeEvent extends ChartChangeEvent { /** The chart title that generated the event. */ private Title title; /** * Default constructor. * * @param title the chart title that generated the event. */ public TitleChangeEvent(Title title) { super(title); this.title = title; } /** * Returns the title that generated the event. * * @return The title that generated the event. */ public Title getTitle() { return this.title; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/event/TitleChangeListener.java000066400000000000000000000035501463604235500307420ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * TitleChangeListener.java * ------------------------ * (C) Copyright 2000-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.event; import java.util.EventListener; /** * The interface that must be supported by classes that wish to receive * notification of changes to a chart title. * */ public interface TitleChangeListener extends EventListener { /** * Receives notification of a chart title change event. * * @param event the event. */ void titleChanged(TitleChangeEvent event); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/event/package.html000066400000000000000000000004401463604235500264560ustar00rootroot00000000000000 Event classes and listener interfaces, used to provide a change notification mechanism so that charts are automatically redrawn whenever changes are made to any chart component. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/imagemap/000077500000000000000000000000001463604235500246365ustar00rootroot00000000000000DynamicDriveToolTipTagFragmentGenerator.java000066400000000000000000000061231463604235500353040ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/imagemap/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------------------------- * DynamicDriveToolTipTagFragmentGenerator.java * -------------------------------------------- * (C) Copyright 2003-present, by Richard Atkinson and Contributors. * * Original Author: Richard Atkinson; * Contributors: David Gilbert; * Fawad Halim - bug 2690293; * */ package org.jfree.chart.imagemap; /** * Generates tooltips using the Dynamic Drive DHTML Tip Message * library (http://www.dynamicdrive.com). */ public class DynamicDriveToolTipTagFragmentGenerator implements ToolTipTagFragmentGenerator { /** The title, empty string not to display */ protected String title = ""; /** The style number */ protected int style = 1; /** * Blank constructor. */ public DynamicDriveToolTipTagFragmentGenerator() { super(); } /** * Creates a new generator with specific title and style settings. * * @param title title for use in all tooltips, use empty String not to * display a title. * @param style style number, see http://www.dynamicdrive.com for more * information. */ public DynamicDriveToolTipTagFragmentGenerator(String title, int style) { this.title = title; this.style = style; } /** * Generates a tooltip string to go in an HTML image map. * * @param toolTipText the tooltip. * * @return The formatted HTML area tag attribute(s). */ @Override public String generateToolTipFragment(String toolTipText) { return " onMouseOver=\"return stm(['" + ImageMapUtils.javascriptEscape(this.title) + "','" + ImageMapUtils.javascriptEscape(toolTipText) + "'],Style[" + this.style + "]);\"" + " onMouseOut=\"return htm();\""; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/imagemap/ImageMapUtils.java000066400000000000000000000231461463604235500302100ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * ImageMapUtils.java * ------------------ * (C) Copyright 2004-present, by Richard Atkinson and Contributors. * * Original Author: Richard Atkinson; * Contributor(s): David Gilbert; * Fawad Halim - bug 2690293; * */ package org.jfree.chart.imagemap; import java.io.IOException; import java.io.PrintWriter; import org.jfree.chart.ChartRenderingInfo; import org.jfree.chart.entity.ChartEntity; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.util.Args; import org.jfree.chart.util.StringUtils; /** * Collection of utility methods related to producing image maps. * Functionality was originally in {@link org.jfree.chart.ChartUtils}. */ public class ImageMapUtils { /** * Writes an image map to an output stream. * * @param writer the writer ({@code null} not permitted). * @param name the map name ({@code null} not permitted). * @param info the chart rendering info ({@code null} not permitted). * * @throws java.io.IOException if there are any I/O errors. */ public static void writeImageMap(PrintWriter writer, String name, ChartRenderingInfo info) throws IOException { // defer argument checking... writeImageMap(writer, name, info, new StandardToolTipTagFragmentGenerator(), new StandardURLTagFragmentGenerator()); } /** * Writes an image map to an output stream. * * @param writer the writer ({@code null} not permitted). * @param name the map name ({@code null} not permitted). * @param info the chart rendering info ({@code null} not permitted). * @param useOverLibForToolTips whether to use OverLIB for tooltips * (http://www.bosrup.com/web/overlib/). * * @throws java.io.IOException if there are any I/O errors. */ public static void writeImageMap(PrintWriter writer, String name, ChartRenderingInfo info, boolean useOverLibForToolTips) throws IOException { ToolTipTagFragmentGenerator toolTipTagFragmentGenerator; if (useOverLibForToolTips) { toolTipTagFragmentGenerator = new OverLIBToolTipTagFragmentGenerator(); } else { toolTipTagFragmentGenerator = new StandardToolTipTagFragmentGenerator(); } writeImageMap(writer, name, info, toolTipTagFragmentGenerator, new StandardURLTagFragmentGenerator()); } /** * Writes an image map to an output stream. * * @param writer the writer ({@code null} not permitted). * @param name the map name ({@code null} not permitted). * @param info the chart rendering info ({@code null} not permitted). * @param toolTipTagFragmentGenerator a generator for the HTML fragment * that will contain the tooltip text ({@code null} not permitted * if {@code info} contains tooltip information). * @param urlTagFragmentGenerator a generator for the HTML fragment that * will contain the URL reference ({@code null} not permitted if * {@code info} contains URLs). * * @throws java.io.IOException if there are any I/O errors. */ public static void writeImageMap(PrintWriter writer, String name, ChartRenderingInfo info, ToolTipTagFragmentGenerator toolTipTagFragmentGenerator, URLTagFragmentGenerator urlTagFragmentGenerator) throws IOException { writer.println(ImageMapUtils.getImageMap(name, info, toolTipTagFragmentGenerator, urlTagFragmentGenerator)); } /** * Creates an image map element that complies with the XHTML 1.0 * specification. * * @param name the map name ({@code null} not permitted). * @param info the chart rendering info ({@code null} not permitted). * * @return The map element. */ public static String getImageMap(String name, ChartRenderingInfo info) { return ImageMapUtils.getImageMap(name, info, new StandardToolTipTagFragmentGenerator(), new StandardURLTagFragmentGenerator()); } /** * Creates an image map element that complies with the XHTML 1.0 * specification. * * @param name the map name ({@code null} not permitted). * @param info the chart rendering info ({@code null} not permitted). * @param toolTipTagFragmentGenerator a generator for the HTML fragment * that will contain the tooltip text ({@code null} not permitted * if {@code info} contains tooltip information). * @param urlTagFragmentGenerator a generator for the HTML fragment that * will contain the URL reference ({@code null} not permitted if * {@code info} contains URLs). * * @return The map tag. */ public static String getImageMap(String name, ChartRenderingInfo info, ToolTipTagFragmentGenerator toolTipTagFragmentGenerator, URLTagFragmentGenerator urlTagFragmentGenerator) { StringBuilder sb = new StringBuilder(); sb.append(""); sb.append(StringUtils.getLineSeparator()); EntityCollection entities = info.getEntityCollection(); if (entities != null) { int count = entities.getEntityCount(); for (int i = count - 1; i >= 0; i--) { ChartEntity entity = entities.getEntity(i); if (entity.getToolTipText() != null || entity.getURLText() != null) { String area = entity.getImageMapAreaTag( toolTipTagFragmentGenerator, urlTagFragmentGenerator); if (area.length() > 0) { sb.append(area); sb.append(StringUtils.getLineSeparator()); } } } } sb.append(""); return sb.toString(); } /** * Returns a string that is equivalent to the input string, but with * special characters converted to HTML escape sequences. * * @param input the string to escape ({@code null} not permitted). * * @return A string with characters escaped. */ public static String htmlEscape(String input) { Args.nullNotPermitted(input, "input"); StringBuilder result = new StringBuilder(); int length = input.length(); for (int i = 0; i < length; i++) { char c = input.charAt(i); if (c == '&') { result.append("&"); } else if (c == '\"') { result.append("""); } else if (c == '<') { result.append("<"); } else if (c == '>') { result.append(">"); } else if (c == '\'') { result.append("'"); } else if (c == '\\') { result.append("\"); } else { result.append(c); } } return result.toString(); } /** * Returns a string that is equivalent to the input string, but with * special characters converted to JavaScript escape sequences. * * @param input the string to escape ({@code null} not permitted). * * @return A string with characters escaped. */ public static String javascriptEscape(String input) { Args.nullNotPermitted(input, "input"); StringBuilder result = new StringBuilder(); int length = input.length(); for (int i = 0; i < length; i++) { char c = input.charAt(i); if (c == '\"') { result.append("\\\""); } else if (c == '\'') { result.append("\\'"); } else if (c == '\\') { result.append("\\\\"); } else { result.append(c); } } return result.toString(); } } OverLIBToolTipTagFragmentGenerator.java000066400000000000000000000045571463604235500342010ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/imagemap/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------------------- * OverLibToolTipTagFragmentGenerator.java * --------------------------------------- * (C) Copyright 2003-present, by Richard Atkinson and Contributors. * * Original Author: Richard Atkinson; * Contributors: David Gilbert; * Fawad Halim - bug 2690293; * */ package org.jfree.chart.imagemap; /** * Generates tooltips using the OverLIB library * (http://www.bosrup.com/web/overlib/). */ public class OverLIBToolTipTagFragmentGenerator implements ToolTipTagFragmentGenerator { /** * Creates a new instance. */ public OverLIBToolTipTagFragmentGenerator() { super(); } /** * Generates a tooltip string to go in an HTML image map. * * @param toolTipText the tooltip text. * * @return The formatted HTML area tag attribute(s). */ @Override public String generateToolTipFragment(String toolTipText) { return " onMouseOver=\"return overlib('" + ImageMapUtils.javascriptEscape(toolTipText) + "');\" onMouseOut=\"return nd();\""; } } StandardToolTipTagFragmentGenerator.java000066400000000000000000000043531463604235500344710ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/imagemap/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------------------- * StandardToolTipTagFragmentGenerator.java * ---------------------------------------- * (C) Copyright 2003-present, by Richard Atkinson and Contributors. * * Original Author: Richard Atkinson; * Contributors: David Gilbert; * */ package org.jfree.chart.imagemap; /** * Generates tooltips using the HTML title attribute for image map area tags. */ public class StandardToolTipTagFragmentGenerator implements ToolTipTagFragmentGenerator { /** * Creates a new instance. */ public StandardToolTipTagFragmentGenerator() { super(); } /** * Generates a tooltip string to go in an HTML image map. * * @param toolTipText the tooltip. * * @return The formatted HTML area tag attribute(s). */ @Override public String generateToolTipFragment(String toolTipText) { return " title=\"" + ImageMapUtils.htmlEscape(toolTipText) + "\" alt=\"\""; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/imagemap/StandardURLTagFragmentGenerator.java000066400000000000000000000043221463604235500336140ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------------ * StandardURLTagFragmentGenerator.java * ------------------------------------ * (C) Copyright 2003-present, by Richard Atkinson and Contributors. * * Original Author: Richard Atkinson; * Contributors: David Gilbert; * */ package org.jfree.chart.imagemap; /** * Generates URLs using the HTML href attribute for image map area tags. */ public class StandardURLTagFragmentGenerator implements URLTagFragmentGenerator { /** * Creates a new instance. */ public StandardURLTagFragmentGenerator() { super(); } /** * Generates a URL string to go in an HTML image map. * * @param urlText the URL text (fully escaped). * * @return The formatted text */ @Override public String generateURLFragment(String urlText) { // the URL text should already have been escaped by the URL generator return " href=\"" + urlText + "\""; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/imagemap/ToolTipTagFragmentGenerator.java000066400000000000000000000045401463604235500330650ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------------- * ToolTipTagFragmentGenerator.java * -------------------------------- * (C) Copyright 2003-present, by Richard Atkinson and Contributors. * * Original Author: Richard Atkinson; * */ package org.jfree.chart.imagemap; /** * Interface for generating the tooltip fragment of an HTML image map area tag. * The fragment should be {@code XHTML 1.0} compliant. */ public interface ToolTipTagFragmentGenerator { /** * Generates a tooltip string to go in an HTML image map. To allow for * varying standards compliance among browsers, this method is expected * to return an 'alt' attribute IN ADDITION TO whatever it does to create * the tooltip (often a 'title' attribute). *

* Note that the {@code toolTipText} may have been generated from * user-defined data, so care should be taken to filter/escape any * characters that may corrupt the HTML tag. * * @param toolTipText the tooltip. * * @return The formatted HTML area tag attribute(s). */ String generateToolTipFragment(String toolTipText); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/imagemap/URLTagFragmentGenerator.java000066400000000000000000000045421463604235500321370ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------- * URLTagFragmentGenerator.java * ---------------------------- * (C) Copyright 2003-present, by Richard Atkinson and Contributors. * * Original Author: Richard Atkinson; * */ package org.jfree.chart.imagemap; import org.jfree.chart.urls.CategoryURLGenerator; import org.jfree.chart.urls.PieURLGenerator; import org.jfree.chart.urls.XYURLGenerator; import org.jfree.chart.urls.XYZURLGenerator; /** * Interface for generating the URL fragment of an HTML image map area tag. */ public interface URLTagFragmentGenerator { /** * Generates a URL string to go in an HTML image map. *

* Note that the {@code urlText} will be created by a URL generator * (such as {@link CategoryURLGenerator}, {@link PieURLGenerator}, * {@link XYURLGenerator} or {@link XYZURLGenerator}) and that generator is * responsible for ensuring that the URL text is correctly escaped. * * @param urlText the URL text (fully escaped). * * @return The formatted HTML area tag attribute(s). */ String generateURLFragment(String urlText); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/imagemap/package.html000066400000000000000000000003151463604235500271160ustar00rootroot00000000000000 Classes, including {@link org.jfree.chart.imagemap.ImageMapUtils}, for creating HTML image maps. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/000077500000000000000000000000001463604235500243205ustar00rootroot00000000000000AbstractCategoryItemLabelGenerator.java000066400000000000000000000261051463604235500337770ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------------------- * AbstractCategoryItemLabelGenerator.java * --------------------------------------- * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.labels; import java.io.Serializable; import java.text.DateFormat; import java.text.MessageFormat; import java.text.NumberFormat; import java.util.Objects; import org.jfree.chart.HashUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.DataUtils; import org.jfree.data.category.CategoryDataset; /** * A base class that can be used to create a label or tooltip generator that * can be assigned to a * {@link org.jfree.chart.renderer.category.CategoryItemRenderer}. */ public abstract class AbstractCategoryItemLabelGenerator implements PublicCloneable, Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -7108591260223293197L; /** * The label format string used by a {@code MessageFormat} object to * combine the standard items: {0} = series name, {1} = category, * {2} = value, {3} = value as a percentage of the column total. */ private final String labelFormat; /** The string used to represent a null value. */ private final String nullValueString; /** * A number formatter used to preformat the value before it is passed to * the MessageFormat object. */ private NumberFormat numberFormat; /** * A date formatter used to preformat the value before it is passed to the * MessageFormat object. */ private DateFormat dateFormat; /** * A number formatter used to preformat the percentage value before it is * passed to the MessageFormat object. */ private final NumberFormat percentFormat; /** * Creates a label generator with the specified number formatter. * * @param labelFormat the label format string ({@code null} not * permitted). * @param formatter the number formatter ({@code null} not permitted). */ protected AbstractCategoryItemLabelGenerator(String labelFormat, NumberFormat formatter) { this(labelFormat, formatter, NumberFormat.getPercentInstance()); } /** * Creates a label generator with the specified number formatter. * * @param labelFormat the label format string ({@code null} not * permitted). * @param formatter the number formatter ({@code null} not permitted). * @param percentFormatter the percent formatter ({@code null} not * permitted). */ protected AbstractCategoryItemLabelGenerator(String labelFormat, NumberFormat formatter, NumberFormat percentFormatter) { Args.nullNotPermitted(labelFormat, "labelFormat"); Args.nullNotPermitted(formatter, "formatter"); Args.nullNotPermitted(percentFormatter, "percentFormatter"); this.labelFormat = labelFormat; this.numberFormat = formatter; this.percentFormat = percentFormatter; this.dateFormat = null; this.nullValueString = "-"; } /** * Creates a label generator with the specified date formatter. * * @param labelFormat the label format string ({@code null} not * permitted). * @param formatter the date formatter ({@code null} not permitted). */ protected AbstractCategoryItemLabelGenerator(String labelFormat, DateFormat formatter) { Args.nullNotPermitted(labelFormat, "labelFormat"); Args.nullNotPermitted(formatter, "formatter"); this.labelFormat = labelFormat; this.numberFormat = null; this.percentFormat = NumberFormat.getPercentInstance(); this.dateFormat = formatter; this.nullValueString = "-"; } /** * Generates a label for the specified row. * * @param dataset the dataset ({@code null} not permitted). * @param row the row index (zero-based). * * @return The label. */ public String generateRowLabel(CategoryDataset dataset, int row) { return dataset.getRowKey(row).toString(); } /** * Generates a label for the specified row. * * @param dataset the dataset ({@code null} not permitted). * @param column the column index (zero-based). * * @return The label. */ public String generateColumnLabel(CategoryDataset dataset, int column) { return dataset.getColumnKey(column).toString(); } /** * Returns the label format string. * * @return The label format string (never {@code null}). */ public String getLabelFormat() { return this.labelFormat; } /** * Returns the number formatter. * * @return The number formatter (possibly {@code null}). */ public NumberFormat getNumberFormat() { return this.numberFormat; } /** * Returns the date formatter. * * @return The date formatter (possibly {@code null}). */ public DateFormat getDateFormat() { return this.dateFormat; } /** * Generates a for the specified item. * * @param dataset the dataset ({@code null} not permitted). * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The label (possibly {@code null}). */ protected String generateLabelString(CategoryDataset dataset, int row, int column) { Args.nullNotPermitted(dataset, "dataset"); String result; Object[] items = createItemArray(dataset, row, column); result = MessageFormat.format(this.labelFormat, items); return result; } /** * Creates the array of items that can be passed to the * {@link MessageFormat} class for creating labels. * * @param dataset the dataset ({@code null} not permitted). * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The items (never {@code null}). */ protected Object[] createItemArray(CategoryDataset dataset, int row, int column) { Object[] result = new Object[4]; result[0] = dataset.getRowKey(row).toString(); result[1] = dataset.getColumnKey(column).toString(); Number value = dataset.getValue(row, column); if (value != null) { if (this.numberFormat != null) { result[2] = this.numberFormat.format(value); } else if (this.dateFormat != null) { result[2] = this.dateFormat.format(value); } } else { result[2] = this.nullValueString; } if (value != null) { double total = DataUtils.calculateColumnTotal(dataset, column); double percent = value.doubleValue() / total; result[3] = this.percentFormat.format(percent); } return result; } /** * Tests this object for equality with an arbitrary object. * * @param obj the other object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof AbstractCategoryItemLabelGenerator)) { return false; } AbstractCategoryItemLabelGenerator that = (AbstractCategoryItemLabelGenerator) obj; if (!Objects.equals(this.labelFormat, that.labelFormat)) { return false; } if (!Objects.equals(this.dateFormat, that.dateFormat)) { return false; } if (!Objects.equals(this.nullValueString, that.nullValueString)) { return false; } if (!Objects.equals(this.numberFormat, that.numberFormat)) { return false; } if (!Objects.equals(this.percentFormat, that.percentFormat)) { return false; } if (!that.canEqual(this)) { return false; } return true; } public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof AbstractCategoryItemLabelGenerator); } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = 127; result = HashUtils.hashCode(result, this.labelFormat); result = HashUtils.hashCode(result, this.nullValueString); result = HashUtils.hashCode(result, this.dateFormat); result = HashUtils.hashCode(result, this.numberFormat); result = HashUtils.hashCode(result, this.percentFormat); return result; } /** * Returns an independent copy of the generator. * * @return A clone. * * @throws CloneNotSupportedException should not happen. */ @Override public Object clone() throws CloneNotSupportedException { AbstractCategoryItemLabelGenerator clone = (AbstractCategoryItemLabelGenerator) super.clone(); if (this.numberFormat != null) { clone.numberFormat = (NumberFormat) this.numberFormat.clone(); } if (this.dateFormat != null) { clone.dateFormat = (DateFormat) this.dateFormat.clone(); } return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/AbstractPieItemLabelGenerator.java000066400000000000000000000171261463604235500330210ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------------- * AbstractPieItemLabelGenerator.java * ---------------------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import java.io.Serializable; import java.text.MessageFormat; import java.text.NumberFormat; import org.jfree.chart.HashUtils; import org.jfree.chart.util.Args; import org.jfree.data.general.DatasetUtils; import org.jfree.data.general.PieDataset; /** * A base class used for generating pie chart item labels. */ public class AbstractPieItemLabelGenerator implements Serializable { /** For serialization. */ private static final long serialVersionUID = 7347703325267846275L; /** The label format string. */ private final String labelFormat; /** A number formatter for the value. */ private NumberFormat numberFormat; /** A number formatter for the percentage. */ private NumberFormat percentFormat; /** * Creates an item label generator using the specified number formatters. * * @param labelFormat the label format string ({@code null} not * permitted). * @param numberFormat the format object for the values ({@code null} * not permitted). * @param percentFormat the format object for the percentages * ({@code null} not permitted). */ protected AbstractPieItemLabelGenerator(String labelFormat, NumberFormat numberFormat, NumberFormat percentFormat) { Args.nullNotPermitted(labelFormat, "labelFormat"); Args.nullNotPermitted(numberFormat, "numberFormat"); Args.nullNotPermitted(percentFormat, "percentFormat"); this.labelFormat = labelFormat; this.numberFormat = numberFormat; this.percentFormat = percentFormat; } /** * Returns the label format string. * * @return The label format string (never {@code null}). */ public String getLabelFormat() { return this.labelFormat; } /** * Returns the number formatter. * * @return The formatter (never {@code null}). */ public NumberFormat getNumberFormat() { return this.numberFormat; } /** * Returns the percent formatter. * * @return The formatter (never {@code null}). */ public NumberFormat getPercentFormat() { return this.percentFormat; } /** * Creates the array of items that can be passed to the * {@link MessageFormat} class for creating labels. The returned array * contains four values: *

    *
  • result[0] = the section key converted to a {@code String};
  • *
  • result[1] = the formatted data value;
  • *
  • result[2] = the formatted percentage (of the total);
  • *
  • result[3] = the formatted total value.
  • *
* * @param dataset the dataset ({@code null} not permitted). * @param key the key ({@code null} not permitted). * * @return The items (never {@code null}). */ protected Object[] createItemArray(PieDataset dataset, Comparable key) { Object[] result = new Object[4]; double total = DatasetUtils.calculatePieDatasetTotal(dataset); result[0] = key.toString(); Number value = dataset.getValue(key); if (value != null) { result[1] = this.numberFormat.format(value); } else { result[1] = "null"; } double percent = 0.0; if (value != null) { double v = value.doubleValue(); if (v > 0.0) { percent = v / total; } } result[2] = this.percentFormat.format(percent); result[3] = this.numberFormat.format(total); return result; } /** * Generates a label for a pie section. * * @param dataset the dataset ({@code null} not permitted). * @param key the section key ({@code null} not permitted). * * @return The label (possibly {@code null}). */ protected String generateSectionLabel(PieDataset dataset, Comparable key) { String result = null; if (dataset != null) { Object[] items = createItemArray(dataset, key); result = MessageFormat.format(this.labelFormat, items); } return result; } /** * Tests the generator for equality with an arbitrary object. * * @param obj the object to test against ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof AbstractPieItemLabelGenerator)) { return false; } AbstractPieItemLabelGenerator that = (AbstractPieItemLabelGenerator) obj; if (!this.labelFormat.equals(that.labelFormat)) { return false; } if (!this.numberFormat.equals(that.numberFormat)) { return false; } if (!this.percentFormat.equals(that.percentFormat)) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = 127; result = HashUtils.hashCode(result, this.labelFormat); result = HashUtils.hashCode(result, this.numberFormat); result = HashUtils.hashCode(result, this.percentFormat); return result; } /** * Returns an independent copy of the generator. * * @return A clone. * * @throws CloneNotSupportedException should not happen. */ @Override public Object clone() throws CloneNotSupportedException { AbstractPieItemLabelGenerator clone = (AbstractPieItemLabelGenerator) super.clone(); if (this.numberFormat != null) { clone.numberFormat = (NumberFormat) this.numberFormat.clone(); } if (this.percentFormat != null) { clone.percentFormat = (NumberFormat) this.percentFormat.clone(); } return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/AbstractXYItemLabelGenerator.java000066400000000000000000000270001463604235500326340ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------------- * AbstractXYItemLabelGenerator.java * --------------------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import java.io.Serializable; import java.text.DateFormat; import java.text.MessageFormat; import java.text.NumberFormat; import java.util.Date; import java.util.Objects; import org.jfree.chart.HashUtils; import org.jfree.chart.util.Args; import org.jfree.data.xy.XYDataset; /** * A base class for creating item label generators. */ public class AbstractXYItemLabelGenerator implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 5869744396278660636L; /** The item label format string. */ private final String formatString; /** A number formatter for the x value. */ private NumberFormat xFormat; /** A date formatter for the x value. */ private DateFormat xDateFormat; /** A formatter for the y value. */ private NumberFormat yFormat; /** A date formatter for the y value. */ private DateFormat yDateFormat; /** The string used to represent 'null' for the y-value. */ private final String nullYString = "null"; /** * Creates an item label generator using default number formatters. */ protected AbstractXYItemLabelGenerator() { this("{2}", NumberFormat.getNumberInstance(), NumberFormat.getNumberInstance()); } /** * Creates an item label generator using the specified number formatters. * * @param formatString the item label format string ({@code null} * not permitted). * @param xFormat the format object for the x values ({@code null} * not permitted). * @param yFormat the format object for the y values ({@code null} * not permitted). */ protected AbstractXYItemLabelGenerator(String formatString, NumberFormat xFormat, NumberFormat yFormat) { Args.nullNotPermitted(formatString, "formatString"); Args.nullNotPermitted(xFormat, "xFormat"); Args.nullNotPermitted(yFormat, "yFormat"); this.formatString = formatString; this.xFormat = xFormat; this.yFormat = yFormat; } /** * Creates an item label generator using the specified number formatters. * * @param formatString the item label format string ({@code null} * not permitted). * @param xFormat the format object for the x values ({@code null} * permitted). * @param yFormat the format object for the y values ({@code null} * not permitted). */ protected AbstractXYItemLabelGenerator(String formatString, DateFormat xFormat, NumberFormat yFormat) { this(formatString, NumberFormat.getInstance(), yFormat); this.xDateFormat = xFormat; } /** * Creates an item label generator using the specified formatters (a * number formatter for the x-values and a date formatter for the * y-values). * * @param formatString the item label format string ({@code null} * not permitted). * @param xFormat the format object for the x values ({@code null} * permitted). * @param yFormat the format object for the y values ({@code null} * not permitted). */ protected AbstractXYItemLabelGenerator(String formatString, NumberFormat xFormat, DateFormat yFormat) { this(formatString, xFormat, NumberFormat.getInstance()); this.yDateFormat = yFormat; } /** * Creates an item label generator using the specified number formatters. * * @param formatString the item label format string ({@code null} * not permitted). * @param xFormat the format object for the x values ({@code null} * permitted). * @param yFormat the format object for the y values ({@code null} * not permitted). */ protected AbstractXYItemLabelGenerator(String formatString, DateFormat xFormat, DateFormat yFormat) { this(formatString, NumberFormat.getInstance(), NumberFormat.getInstance()); this.xDateFormat = xFormat; this.yDateFormat = yFormat; } /** * Returns the format string (this controls the overall structure of the * label). * * @return The format string (never {@code null}). */ public String getFormatString() { return this.formatString; } /** * Returns the number formatter for the x-values. * * @return The number formatter (possibly {@code null}). */ public NumberFormat getXFormat() { return this.xFormat; } /** * Returns the date formatter for the x-values. * * @return The date formatter (possibly {@code null}). */ public DateFormat getXDateFormat() { return this.xDateFormat; } /** * Returns the number formatter for the y-values. * * @return The number formatter (possibly {@code null}). */ public NumberFormat getYFormat() { return this.yFormat; } /** * Returns the date formatter for the y-values. * * @return The date formatter (possibly {@code null}). */ public DateFormat getYDateFormat() { return this.yDateFormat; } /** * Generates a label string for an item in the dataset. * * @param dataset the dataset ({@code null} not permitted). * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The label (possibly {@code null}). */ public String generateLabelString(XYDataset dataset, int series, int item) { String result; Object[] items = createItemArray(dataset, series, item); result = MessageFormat.format(this.formatString, items); return result; } /** * Returns the string representing a null value. * * @return The string representing a null value. */ public String getNullYString() { return this.nullYString; } /** * Creates the array of items that can be passed to the * {@link MessageFormat} class for creating labels. * * @param dataset the dataset ({@code null} not permitted). * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return An array of three items from the dataset formatted as * {@code String} objects (never {@code null}). */ protected Object[] createItemArray(XYDataset dataset, int series, int item) { Object[] result = new Object[3]; result[0] = dataset.getSeriesKey(series).toString(); double x = dataset.getXValue(series, item); if (this.xDateFormat != null) { result[1] = this.xDateFormat.format(new Date((long) x)); } else { result[1] = this.xFormat.format(x); } double y = dataset.getYValue(series, item); if (Double.isNaN(y) && dataset.getY(series, item) == null) { result[2] = this.nullYString; } else { if (this.yDateFormat != null) { result[2] = this.yDateFormat.format(new Date((long) y)); } else { result[2] = this.yFormat.format(y); } } return result; } /** * Tests this object for equality with an arbitrary object. * * @param obj the other object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof AbstractXYItemLabelGenerator)) { return false; } AbstractXYItemLabelGenerator that = (AbstractXYItemLabelGenerator) obj; if (!this.formatString.equals(that.formatString)) { return false; } if (!Objects.equals(this.xFormat, that.xFormat)) { return false; } if (!Objects.equals(this.xDateFormat, that.xDateFormat)) { return false; } if (!Objects.equals(this.yFormat, that.yFormat)) { return false; } if (!Objects.equals(this.yDateFormat, that.yDateFormat)) { return false; } if (!this.nullYString.equals(that.nullYString)) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = 127; result = HashUtils.hashCode(result, this.formatString); result = HashUtils.hashCode(result, this.xFormat); result = HashUtils.hashCode(result, this.xDateFormat); result = HashUtils.hashCode(result, this.yFormat); result = HashUtils.hashCode(result, this.yDateFormat); return result; } /** * Returns an independent copy of the generator. * * @return A clone. * * @throws CloneNotSupportedException if cloning is not supported. */ @Override public Object clone() throws CloneNotSupportedException { AbstractXYItemLabelGenerator clone = (AbstractXYItemLabelGenerator) super.clone(); if (this.xFormat != null) { clone.xFormat = (NumberFormat) this.xFormat.clone(); } if (this.yFormat != null) { clone.yFormat = (NumberFormat) this.yFormat.clone(); } if (this.xDateFormat != null) { clone.xDateFormat = (DateFormat) this.xDateFormat.clone(); } if (this.yDateFormat != null) { clone.yDateFormat = (DateFormat) this.yDateFormat.clone(); } return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/BoxAndWhiskerToolTipGenerator.java000066400000000000000000000120751463604235500330620ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------------ * BoxAndWhiskerToolTipGenerator.java * ------------------------------------ * (C) Copyright 2004-present, by David Browning and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import java.io.Serializable; import java.text.MessageFormat; import java.text.NumberFormat; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.category.CategoryDataset; import org.jfree.data.statistics.BoxAndWhiskerCategoryDataset; /** * An item label generator for plots that use data from a * {@link BoxAndWhiskerCategoryDataset}. *

* The tooltip text and item label text are composed using a * {@link java.text.MessageFormat} object, that can aggregate some or all of * the following string values into a message. *

    *
  • 0 : Series Name
  • *
  • 1 : X (value or date)
  • *
  • 2 : Mean
  • *
  • 3 : Median
  • *
  • 4 : Minimum
  • *
  • 5 : Maximum
  • *
  • 6 : Quartile 1
  • *
  • 7 : Quartile 3
  • *
*/ public class BoxAndWhiskerToolTipGenerator extends StandardCategoryToolTipGenerator implements CategoryToolTipGenerator, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -6076837753823076334L; /** The default tooltip format string. */ public static final String DEFAULT_TOOL_TIP_FORMAT = "X: {1} Mean: {2} Median: {3} Min: {4} Max: {5} Q1: {6} Q3: {7} "; /** * Creates a default tool tip generator. */ public BoxAndWhiskerToolTipGenerator() { super(DEFAULT_TOOL_TIP_FORMAT, NumberFormat.getInstance()); } /** * Creates a tool tip formatter. * * @param format the tool tip format string. * @param formatter the formatter. */ public BoxAndWhiskerToolTipGenerator(String format, NumberFormat formatter) { super(format, formatter); } /** * Creates the array of items that can be passed to the * {@link MessageFormat} class for creating labels. * * @param dataset the dataset ({@code null} not permitted). * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The items (never {@code null}). */ @Override protected Object[] createItemArray(CategoryDataset dataset, int series, int item) { Object[] result = new Object[8]; result[0] = dataset.getRowKey(series); Number y = dataset.getValue(series, item); NumberFormat formatter = getNumberFormat(); result[1] = formatter.format(y); if (dataset instanceof BoxAndWhiskerCategoryDataset) { BoxAndWhiskerCategoryDataset d = (BoxAndWhiskerCategoryDataset) dataset; result[2] = formatter.format(d.getMeanValue(series, item)); result[3] = formatter.format(d.getMedianValue(series, item)); result[4] = formatter.format(d.getMinRegularValue(series, item)); result[5] = formatter.format(d.getMaxRegularValue(series, item)); result[6] = formatter.format(d.getQ1Value(series, item)); result[7] = formatter.format(d.getQ3Value(series, item)); } return result; } /** * Tests if this object is equal to another. * * @param obj the other object. * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj instanceof BoxAndWhiskerToolTipGenerator) { return super.equals(obj); } return false; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/BoxAndWhiskerXYToolTipGenerator.java000066400000000000000000000131251463604235500333400ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------------ * BoxAndWhiskerXYToolTipGenerator.java * ------------------------------------ * (C) Copyright 2003-present, by David Browning and Contributors. * * Original Author: David Browning; * Contributor(s): David Gilbert; * */ package org.jfree.chart.labels; import java.io.Serializable; import java.text.DateFormat; import java.text.MessageFormat; import java.text.NumberFormat; import java.util.Date; import org.jfree.data.statistics.BoxAndWhiskerXYDataset; import org.jfree.data.xy.XYDataset; /** * An item label generator for plots that use data from a * {@link BoxAndWhiskerXYDataset}. *

* The tooltip text and item label text are composed using a * {@link java.text.MessageFormat} object, that can aggregate some or all of * the following string values into a message. *

    *
  • 0 : Series Name
  • *
  • 1 : X (value or date)
  • *
  • 2 : Mean
  • *
  • 3 : Median
  • *
  • 4 : Minimum
  • *
  • 5 : Maximum
  • *
  • 6 : Quartile 1
  • *
  • 7 : Quartile 3
  • *
*/ public class BoxAndWhiskerXYToolTipGenerator extends StandardXYToolTipGenerator implements XYToolTipGenerator, Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -2648775791161459710L; /** The default tooltip format string. */ public static final String DEFAULT_TOOL_TIP_FORMAT = "X: {1} Mean: {2} Median: {3} Min: {4} Max: {5} Q1: {6} Q3: {7} "; /** * Creates a default item label generator. */ public BoxAndWhiskerXYToolTipGenerator() { super(DEFAULT_TOOL_TIP_FORMAT, NumberFormat.getInstance(), NumberFormat.getInstance()); } /** * Creates a new item label generator. If the date formatter is not * {@code null}, the x-values will be formatted as dates. * * @param toolTipFormat the tool tip format string ({@code null} not * permitted). * @param numberFormat the number formatter ({@code null} not * permitted). * @param dateFormat the date formatter ({@code null} permitted). */ public BoxAndWhiskerXYToolTipGenerator(String toolTipFormat, DateFormat dateFormat, NumberFormat numberFormat) { super(toolTipFormat, dateFormat, numberFormat); } /** * Creates the array of items that can be passed to the * {@link MessageFormat} class for creating labels. * * @param dataset the dataset ({@code null} not permitted). * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The items (never {@code null}). */ @Override protected Object[] createItemArray(XYDataset dataset, int series, int item) { Object[] result = new Object[8]; result[0] = dataset.getSeriesKey(series).toString(); Number x = dataset.getX(series, item); if (getXDateFormat() != null) { result[1] = getXDateFormat().format(new Date(x.longValue())); } else { result[1] = getXFormat().format(x); } NumberFormat formatter = getYFormat(); if (dataset instanceof BoxAndWhiskerXYDataset) { BoxAndWhiskerXYDataset d = (BoxAndWhiskerXYDataset) dataset; result[2] = formatter.format(d.getMeanValue(series, item)); result[3] = formatter.format(d.getMedianValue(series, item)); result[4] = formatter.format(d.getMinRegularValue(series, item)); result[5] = formatter.format(d.getMaxRegularValue(series, item)); result[6] = formatter.format(d.getQ1Value(series, item)); result[7] = formatter.format(d.getQ3Value(series, item)); } return result; } /** * Tests if this object is equal to another. * * @param obj the other object. * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof BoxAndWhiskerXYToolTipGenerator)) { return false; } return super.equals(obj); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/BubbleXYItemLabelGenerator.java000066400000000000000000000212341463604235500322670ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------- * BubbleXYItemLabelGenerator.java * ------------------------------- * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import java.io.Serializable; import java.text.DateFormat; import java.text.MessageFormat; import java.text.NumberFormat; import java.util.Objects; import org.jfree.chart.HashUtils; import org.jfree.chart.renderer.xy.XYBubbleRenderer; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYZDataset; /** * An item label generator defined for use with the {@link XYBubbleRenderer} * class, or any other class that uses an {@link XYZDataset}. */ public class BubbleXYItemLabelGenerator extends AbstractXYItemLabelGenerator implements XYItemLabelGenerator, PublicCloneable, Serializable { /** For serialization. */ static final long serialVersionUID = -8458568928021240922L; /** The default item label format. */ public static final String DEFAULT_FORMAT_STRING = "{3}"; /** * A number formatter for the z value - if this is {@code null}, then * zDateFormat must be non-null. */ private NumberFormat zFormat; /** * A date formatter for the z-value - if this is null, then zFormat must be * non-null. */ private DateFormat zDateFormat; /** * Creates a new tool tip generator using default number formatters for the * x, y and z-values. */ public BubbleXYItemLabelGenerator() { this(DEFAULT_FORMAT_STRING, NumberFormat.getNumberInstance(), NumberFormat.getNumberInstance(), NumberFormat.getNumberInstance()); } /** * Constructs a new tool tip generator using the specified number * formatters. * * @param formatString the format string. * @param xFormat the format object for the x values ({@code null} * not permitted). * @param yFormat the format object for the y values ({@code null} * not permitted). * @param zFormat the format object for the z values ({@code null} * not permitted). */ public BubbleXYItemLabelGenerator(String formatString, NumberFormat xFormat, NumberFormat yFormat, NumberFormat zFormat) { super(formatString, xFormat, yFormat); Args.nullNotPermitted(zFormat, "zFormat"); this.zFormat = zFormat; } /** * Constructs a new item label generator using the specified date * formatters. * * @param formatString the format string. * @param xFormat the format object for the x values ({@code null} * not permitted). * @param yFormat the format object for the y values ({@code null} * not permitted). * @param zFormat the format object for the z values ({@code null} * not permitted). */ public BubbleXYItemLabelGenerator(String formatString, DateFormat xFormat, DateFormat yFormat, DateFormat zFormat) { super(formatString, xFormat, yFormat); Args.nullNotPermitted(zFormat, "zFormat"); this.zDateFormat = zFormat; } /** * Returns the number formatter for the z-values. * * @return The number formatter (possibly {@code null}). */ public NumberFormat getZFormat() { return this.zFormat; } /** * Returns the date formatter for the z-values. * * @return The date formatter (possibly {@code null}). */ public DateFormat getZDateFormat() { return this.zDateFormat; } /** * Generates an item label for a particular item within a series. * * @param dataset the dataset ({@code null} not permitted). * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The item label (possibly {@code null}). */ @Override public String generateLabel(XYDataset dataset, int series, int item) { return generateLabelString(dataset, series, item); } /** * Generates a label string for an item in the dataset. * * @param dataset the dataset ({@code null} not permitted). * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The label (possibly {@code null}). */ @Override public String generateLabelString(XYDataset dataset, int series, int item) { String result; Object[] items; if (dataset instanceof XYZDataset) { items = createItemArray((XYZDataset) dataset, series, item); } else { items = createItemArray(dataset, series, item); } result = MessageFormat.format(getFormatString(), items); return result; } /** * Creates the array of items that can be passed to the * {@link MessageFormat} class for creating labels. * * @param dataset the dataset ({@code null} not permitted). * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The items (never {@code null}). */ protected Object[] createItemArray(XYZDataset dataset, int series, int item) { Object[] result = new Object[4]; result[0] = dataset.getSeriesKey(series).toString(); Number x = dataset.getX(series, item); DateFormat xf = getXDateFormat(); if (xf != null) { result[1] = xf.format(x); } else { result[1] = getXFormat().format(x); } Number y = dataset.getY(series, item); DateFormat yf = getYDateFormat(); if (yf != null) { result[2] = yf.format(y); } else { result[2] = getYFormat().format(y); } Number z = dataset.getZ(series, item); if (this.zDateFormat != null) { result[3] = this.zDateFormat.format(z); } else { result[3] = this.zFormat.format(z); } return result; } /** * Tests this object for equality with an arbitrary object. * * @param obj the other object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof BubbleXYItemLabelGenerator)) { return false; } if (!super.equals(obj)) { return false; } BubbleXYItemLabelGenerator that = (BubbleXYItemLabelGenerator) obj; if (!Objects.equals(this.zFormat, that.zFormat)) { return false; } if (!Objects.equals(this.zDateFormat, that.zDateFormat)) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int h = super.hashCode(); h = HashUtils.hashCode(h, this.zFormat); h = HashUtils.hashCode(h, this.zDateFormat); return h; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/CategoryItemLabelGenerator.java000066400000000000000000000061231463604235500323700ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------- * CategoryItemLabelGenerator.java * ------------------------------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import org.jfree.data.category.CategoryDataset; /** * A category item label generator is an object that can be assigned to a * {@link org.jfree.chart.renderer.category.CategoryItemRenderer} and that * assumes responsibility for creating text items to be used as labels for the * items in a {@link org.jfree.chart.plot.CategoryPlot}. *

* To assist with cloning charts, classes that implement this interface should * also implement the {@link org.jfree.chart.util.PublicCloneable} interface. */ public interface CategoryItemLabelGenerator { /** * Generates a label for the specified row. * * @param dataset the dataset ({@code null} not permitted). * @param row the row index (zero-based). * * @return The label. */ String generateRowLabel(CategoryDataset dataset, int row); /** * Generates a label for the specified row. * * @param dataset the dataset ({@code null} not permitted). * @param column the column index (zero-based). * * @return The label. */ String generateColumnLabel(CategoryDataset dataset, int column); /** * Generates a label for the specified item. The label is typically a * formatted version of the data value, but any text can be used. * * @param dataset the dataset ({@code null} not permitted). * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The label (possibly {@code null}). */ String generateLabel(CategoryDataset dataset, int row, int column); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/CategorySeriesLabelGenerator.java000066400000000000000000000043621463604235500327270ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------------- * CategorySeriesLabelGenerator.java * --------------------------------- * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import org.jfree.data.category.CategoryDataset; /** * A generator that creates labels for the series in a {@link CategoryDataset}. *

* Classes that implement this interface should be either (a) immutable, or * (b) cloneable via the {@code PublicCloneable} interface (defined in * the JCommon class library). This provides a mechanism for the referring * renderer to clone the generator if necessary. */ public interface CategorySeriesLabelGenerator { /** * Generates a label for the specified series. * * @param dataset the dataset ({@code null} not permitted). * @param series the series index. * * @return A series label. */ String generateLabel(CategoryDataset dataset, int series); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/CategoryToolTipGenerator.java000066400000000000000000000051121463604235500321210ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------- * CategoryToolTipGenerator.java * ----------------------------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import org.jfree.data.category.CategoryDataset; /** * A category tool tip generator is an object that can be assigned to a * {@link org.jfree.chart.renderer.category.CategoryItemRenderer} and that * assumes responsibility for creating text items to be used as tooltips for the * items in a {@link org.jfree.chart.plot.CategoryPlot}. *

* To assist with cloning charts, classes that implement this interface should * also implement the {@code org.jfree.util.PublicCloneable} interface (in * JCommon). */ public interface CategoryToolTipGenerator { /** * Generates the tool tip text for an item in a dataset. Note: in the * current dataset implementation, each row is a series, and each column * contains values for a particular category. * * @param dataset the dataset ({@code null} not permitted). * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The tooltip text (possibly {@code null}). */ String generateToolTip(CategoryDataset dataset, int row, int column); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/CrosshairLabelGenerator.java000066400000000000000000000035131463604235500317310ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------- * CrosshairLabelGenerator.java * ---------------------------- * (C) Copyright 2009-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import org.jfree.chart.plot.Crosshair; /** * A label generator for crosshairs. */ public interface CrosshairLabelGenerator { /** * Returns a string that can be used as the label for a crosshair. * * @param crosshair the crosshair ({@code null} not permitted). * * @return The label (possibly {@code null}). */ String generateLabel(Crosshair crosshair); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/CustomXYToolTipGenerator.java000066400000000000000000000132451463604235500321050ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------- * CustomXYToolTipGenerator.java * ----------------------------- * (C) Copyright 2002-present, by Richard Atkinson and Contributors. * * Original Author: Richard Atkinson; * Contributor(s): David Gilbert; * */ package org.jfree.chart.labels; import java.io.Serializable; import java.util.List; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.xy.XYDataset; /** * A tool tip generator that stores custom tooltips. The dataset passed into * the generateToolTip method is ignored. */ public class CustomXYToolTipGenerator implements XYToolTipGenerator, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 8636030004670141362L; /** Storage for the tooltip lists. */ private List toolTipSeries = new java.util.ArrayList(); /** * Default constructor. */ public CustomXYToolTipGenerator() { super(); } /** * Returns the number of tool tip lists stored by the renderer. * * @return The list count. */ public int getListCount() { return this.toolTipSeries.size(); } /** * Returns the number of tool tips in a given list. * * @param list the list index (zero based). * * @return The tooltip count. */ public int getToolTipCount(int list) { int result = 0; List tooltips = (List) this.toolTipSeries.get(list); if (tooltips != null) { result = tooltips.size(); } return result; } /** * Returns the tool tip text for an item. * * @param series the series index. * @param item the item index. * * @return The tool tip text. */ public String getToolTipText(int series, int item) { String result = null; if (series < getListCount()) { List tooltips = (List) this.toolTipSeries.get(series); if (tooltips != null) { if (item < tooltips.size()) { result = (String) tooltips.get(item); } } } return result; } /** * Adds a list of tooltips for a series. * * @param toolTips the list of tool tips. */ public void addToolTipSeries(List toolTips) { this.toolTipSeries.add(toolTips); } /** * Generates a tool tip text item for a particular item within a series. * * @param data the dataset (ignored in this implementation). * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The tooltip text. */ @Override public String generateToolTip(XYDataset data, int series, int item) { return getToolTipText(series, item); } /** * Returns an independent copy of the generator. * * @return A clone. * * @throws CloneNotSupportedException if cloning is not supported. */ @Override public Object clone() throws CloneNotSupportedException { CustomXYToolTipGenerator clone = (CustomXYToolTipGenerator) super.clone(); if (this.toolTipSeries != null) { clone.toolTipSeries = new java.util.ArrayList(this.toolTipSeries); } return clone; } /** * Tests if this object is equal to another. * * @param obj the other object. * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj instanceof CustomXYToolTipGenerator) { CustomXYToolTipGenerator generator = (CustomXYToolTipGenerator) obj; boolean result = true; for (int series = 0; series < getListCount(); series++) { for (int item = 0; item < getToolTipCount(series); item++) { String t1 = getToolTipText(series, item); String t2 = generator.getToolTipText(series, item); if (t1 != null) { result = result && t1.equals(t2); } else { result = result && (t2 == null); } } } return result; } return false; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/FlowLabelGenerator.java000066400000000000000000000037051463604235500307060ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * FlowLabelGenerator.java * ----------------------- * (C) Copyright 2022, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import org.jfree.data.flow.FlowDataset; import org.jfree.data.flow.FlowKey; /** * A label generator for a flow in a flow dataset. * * @since 1.5.3 */ public interface FlowLabelGenerator { /** * Returns a label for the specified flow. * * @param dataset the flow dataset ({@code null} not permitted). * @param key the flow key ({@code null} not permitted). * * @return The label (possibly {@code null}). */ String generateLabel(FlowDataset dataset, FlowKey key); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/HighLowItemLabelGenerator.java000066400000000000000000000164111463604235500321550ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------ * HighLowItemLabelGenerator.java * ------------------------------ * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): David Basten; * */ package org.jfree.chart.labels; import java.io.Serializable; import java.text.DateFormat; import java.text.NumberFormat; import java.util.Date; import org.jfree.chart.HashUtils; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.xy.OHLCDataset; import org.jfree.data.xy.XYDataset; /** * A standard item label generator for plots that use data from a * {@link OHLCDataset}. */ public class HighLowItemLabelGenerator implements XYItemLabelGenerator, XYToolTipGenerator, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 5617111754832211830L; /** The date formatter. */ private DateFormat dateFormatter; /** The number formatter. */ private NumberFormat numberFormatter; /** * Creates an item label generator using the default date and number * formats. */ public HighLowItemLabelGenerator() { this(DateFormat.getInstance(), NumberFormat.getInstance()); } /** * Creates a tool tip generator using the supplied date formatter. * * @param dateFormatter the date formatter ({@code null} not * permitted). * @param numberFormatter the number formatter ({@code null} not * permitted). */ public HighLowItemLabelGenerator(DateFormat dateFormatter, NumberFormat numberFormatter) { if (dateFormatter == null) { throw new IllegalArgumentException( "Null 'dateFormatter' argument."); } if (numberFormatter == null) { throw new IllegalArgumentException( "Null 'numberFormatter' argument."); } this.dateFormatter = dateFormatter; this.numberFormatter = numberFormatter; } /** * Generates a tooltip text item for a particular item within a series. * * @param dataset the dataset. * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The tooltip text. */ @Override public String generateToolTip(XYDataset dataset, int series, int item) { if (!(dataset instanceof OHLCDataset)) { return null; } StringBuilder sb = new StringBuilder(); OHLCDataset d = (OHLCDataset) dataset; Number high = d.getHigh(series, item); Number low = d.getLow(series, item); Number open = d.getOpen(series, item); Number close = d.getClose(series, item); Number x = d.getX(series, item); sb.append(d.getSeriesKey(series).toString()); if (x != null) { Date date = new Date(x.longValue()); sb.append("--> Date=").append(this.dateFormatter.format(date)); if (high != null) { sb.append(" High="); sb.append(this.numberFormatter.format(high.doubleValue())); } if (low != null) { sb.append(" Low="); sb.append(this.numberFormatter.format(low.doubleValue())); } if (open != null) { sb.append(" Open="); sb.append(this.numberFormatter.format(open.doubleValue())); } if (close != null) { sb.append(" Close="); sb.append(this.numberFormatter.format(close.doubleValue())); } } return sb.toString(); } /** * Generates a label for the specified item. The label is typically a * formatted version of the data value, but any text can be used. * * @param dataset the dataset ({@code null} not permitted). * @param series the series index (zero-based). * @param category the category index (zero-based). * * @return The label (possibly {@code null}). */ @Override public String generateLabel(XYDataset dataset, int series, int category) { return null; //TODO: implement this method properly } /** * Returns an independent copy of the generator. * * @return A clone. * * @throws CloneNotSupportedException if cloning is not supported. */ @Override public Object clone() throws CloneNotSupportedException { HighLowItemLabelGenerator clone = (HighLowItemLabelGenerator) super.clone(); if (this.dateFormatter != null) { clone.dateFormatter = (DateFormat) this.dateFormatter.clone(); } if (this.numberFormatter != null) { clone.numberFormatter = (NumberFormat) this.numberFormatter.clone(); } return clone; } /** * Tests if this object is equal to another. * * @param obj the other object. * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof HighLowItemLabelGenerator)) { return false; } HighLowItemLabelGenerator generator = (HighLowItemLabelGenerator) obj; if (!this.dateFormatter.equals(generator.dateFormatter)) { return false; } if (!this.numberFormatter.equals(generator.numberFormatter)) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = 127; result = HashUtils.hashCode(result, this.dateFormatter); result = HashUtils.hashCode(result, this.numberFormatter); return result; } } IntervalCategoryItemLabelGenerator.java000066400000000000000000000117531463604235500340230ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------------------- * IntervalCategoryItemLabelGenerator.java * --------------------------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import java.io.Serializable; import java.text.DateFormat; import java.text.NumberFormat; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.category.CategoryDataset; import org.jfree.data.category.IntervalCategoryDataset; /** * A label generator for plots that use data from an * {@link IntervalCategoryDataset}. */ public class IntervalCategoryItemLabelGenerator extends StandardCategoryItemLabelGenerator implements CategoryItemLabelGenerator, PublicCloneable, Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 5056909225610630529L; /** The default format string. */ public static final String DEFAULT_LABEL_FORMAT_STRING = "({0}, {1}) = {3} - {4}"; /** * Creates a new generator with a default number formatter. */ public IntervalCategoryItemLabelGenerator() { super(DEFAULT_LABEL_FORMAT_STRING, NumberFormat.getInstance()); } /** * Creates a new generator with the specified number formatter. * * @param labelFormat the label format string ({@code null} not * permitted). * @param formatter the number formatter ({@code null} not permitted). */ public IntervalCategoryItemLabelGenerator(String labelFormat, NumberFormat formatter) { super(labelFormat, formatter); } /** * Creates a new generator with the specified date formatter. * * @param labelFormat the label format string ({@code null} not * permitted). * @param formatter the date formatter ({@code null} not permitted). */ public IntervalCategoryItemLabelGenerator(String labelFormat, DateFormat formatter) { super(labelFormat, formatter); } /** * Creates the array of items that can be passed to the * {@code MessageFormat} class for creating labels. * * @param dataset the dataset ({@code null} not permitted). * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The items (never {@code null}). */ @Override protected Object[] createItemArray(CategoryDataset dataset, int row, int column) { Object[] result = new Object[5]; result[0] = dataset.getRowKey(row).toString(); result[1] = dataset.getColumnKey(column).toString(); Number value = dataset.getValue(row, column); if (getNumberFormat() != null) { result[2] = getNumberFormat().format(value); } else if (getDateFormat() != null) { result[2] = getDateFormat().format(value); } if (dataset instanceof IntervalCategoryDataset) { IntervalCategoryDataset icd = (IntervalCategoryDataset) dataset; Number start = icd.getStartValue(row, column); Number end = icd.getEndValue(row, column); if (getNumberFormat() != null) { result[3] = getNumberFormat().format(start); result[4] = getNumberFormat().format(end); } else if (getDateFormat() != null) { result[3] = getDateFormat().format(start); result[4] = getDateFormat().format(end); } } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/IntervalCategoryToolTipGenerator.java000066400000000000000000000124561463604235500336370ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------------- * IntervalCategoryToolTipGenerator.java * ------------------------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import java.text.DateFormat; import java.text.NumberFormat; import org.jfree.data.category.CategoryDataset; import org.jfree.data.category.IntervalCategoryDataset; /** * A tooltip generator for plots that use data from an * {@link IntervalCategoryDataset}. */ public class IntervalCategoryToolTipGenerator extends StandardCategoryToolTipGenerator { /** For serialization. */ private static final long serialVersionUID = -3853824986520333437L; /** The default format string. */ public static final String DEFAULT_TOOL_TIP_FORMAT_STRING = "({0}, {1}) = {3} - {4}"; /** * Creates a new generator with a default number formatter. */ public IntervalCategoryToolTipGenerator() { super(DEFAULT_TOOL_TIP_FORMAT_STRING, NumberFormat.getInstance()); } /** * Creates a new generator with the specified number formatter. * * @param labelFormat the label format string ({@code null} not * permitted). * @param formatter the number formatter ({@code null} not permitted). */ public IntervalCategoryToolTipGenerator(String labelFormat, NumberFormat formatter) { super(labelFormat, formatter); } /** * Creates a new generator with the specified date formatter. * * @param labelFormat the label format string ({@code null} not * permitted). * @param formatter the date formatter ({@code null} not permitted). */ public IntervalCategoryToolTipGenerator(String labelFormat, DateFormat formatter) { super(labelFormat, formatter); } /** * Creates the array of items that can be passed to the * {@code MessageFormat} class for creating labels. * * @param dataset the dataset ({@code null} not permitted). * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The items (never {@code null}). */ @Override protected Object[] createItemArray(CategoryDataset dataset, int row, int column) { Object[] result = new Object[5]; result[0] = dataset.getRowKey(row).toString(); result[1] = dataset.getColumnKey(column).toString(); Number value = dataset.getValue(row, column); if (getNumberFormat() != null) { result[2] = getNumberFormat().format(value); } else if (getDateFormat() != null) { result[2] = getDateFormat().format(value); } if (dataset instanceof IntervalCategoryDataset) { IntervalCategoryDataset icd = (IntervalCategoryDataset) dataset; Number start = icd.getStartValue(row, column); Number end = icd.getEndValue(row, column); if (getNumberFormat() != null) { result[3] = getNumberFormat().format(start); result[4] = getNumberFormat().format(end); } else if (getDateFormat() != null) { result[3] = getDateFormat().format(start); result[4] = getDateFormat().format(end); } } return result; } /** * Tests this tool tip generator for equality with an arbitrary * object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof IntervalCategoryToolTipGenerator)) { return false; } // no fields to test return super.equals(obj); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/IntervalXYItemLabelGenerator.java000066400000000000000000000222331463604235500326600ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------------- * IntervalXYItemLabelGenerator.java * --------------------------------- * (C) Copyright 2008-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import java.io.Serializable; import java.text.DateFormat; import java.text.MessageFormat; import java.text.NumberFormat; import java.util.Date; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.xy.IntervalXYDataset; import org.jfree.data.xy.XYDataset; /** * An item label generator for datasets that implement the * {@link IntervalXYDataset} interface. */ public class IntervalXYItemLabelGenerator extends AbstractXYItemLabelGenerator implements XYItemLabelGenerator, Cloneable, PublicCloneable, Serializable { /** The default item label format. */ public static final String DEFAULT_ITEM_LABEL_FORMAT = "{5} - {6}"; /** * Creates an item label generator using default number formatters. */ public IntervalXYItemLabelGenerator() { this(DEFAULT_ITEM_LABEL_FORMAT, NumberFormat.getNumberInstance(), NumberFormat.getNumberInstance()); } /** * Creates an item label generator using the specified number formatters. * * @param formatString the item label format string ({@code null} not * permitted). * @param xFormat the format object for the x values ({@code null} * not permitted). * @param yFormat the format object for the y values ({@code null} * not permitted). */ public IntervalXYItemLabelGenerator(String formatString, NumberFormat xFormat, NumberFormat yFormat) { super(formatString, xFormat, yFormat); } /** * Creates an item label generator using the specified formatters. * * @param formatString the item label format string ({@code null} * not permitted). * @param xFormat the format object for the x values ({@code null} * not permitted). * @param yFormat the format object for the y values ({@code null} * not permitted). */ public IntervalXYItemLabelGenerator(String formatString, DateFormat xFormat, NumberFormat yFormat) { super(formatString, xFormat, yFormat); } /** * Creates an item label generator using the specified formatters (a * number formatter for the x-values and a date formatter for the * y-values). * * @param formatString the item label format string ({@code null} * not permitted). * @param xFormat the format object for the x values ({@code null} * permitted). * @param yFormat the format object for the y values ({@code null} * not permitted). */ public IntervalXYItemLabelGenerator(String formatString, NumberFormat xFormat, DateFormat yFormat) { super(formatString, xFormat, yFormat); } /** * Creates a label generator using the specified date formatters. * * @param formatString the label format string ({@code null} not * permitted). * @param xFormat the format object for the x values ({@code null} * not permitted). * @param yFormat the format object for the y values ({@code null} * not permitted). */ public IntervalXYItemLabelGenerator(String formatString, DateFormat xFormat, DateFormat yFormat) { super(formatString, xFormat, yFormat); } /** * Creates the array of items that can be passed to the * {@link MessageFormat} class for creating labels. * * @param dataset the dataset ({@code null} not permitted). * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return An array of seven items from the dataset formatted as * {@code String} objects (never {@code null}). */ @Override protected Object[] createItemArray(XYDataset dataset, int series, int item) { IntervalXYDataset intervalDataset = null; if (dataset instanceof IntervalXYDataset) { intervalDataset = (IntervalXYDataset) dataset; } Object[] result = new Object[7]; result[0] = dataset.getSeriesKey(series).toString(); double x = dataset.getXValue(series, item); double xs = x; double xe = x; double y = dataset.getYValue(series, item); double ys = y; double ye = y; if (intervalDataset != null) { xs = intervalDataset.getStartXValue(series, item); xe = intervalDataset.getEndXValue(series, item); ys = intervalDataset.getStartYValue(series, item); ye = intervalDataset.getEndYValue(series, item); } DateFormat xdf = getXDateFormat(); if (xdf != null) { result[1] = xdf.format(new Date((long) x)); result[2] = xdf.format(new Date((long) xs)); result[3] = xdf.format(new Date((long) xe)); } else { NumberFormat xnf = getXFormat(); result[1] = xnf.format(x); result[2] = xnf.format(xs); result[3] = xnf.format(xe); } NumberFormat ynf = getYFormat(); DateFormat ydf = getYDateFormat(); if (Double.isNaN(y) && dataset.getY(series, item) == null) { result[4] = getNullYString(); } else { if (ydf != null) { result[4] = ydf.format(new Date((long) y)); } else { result[4] = ynf.format(y); } } if (Double.isNaN(ys) && intervalDataset != null && intervalDataset.getStartY(series, item) == null) { result[5] = getNullYString(); } else { if (ydf != null) { result[5] = ydf.format(new Date((long) ys)); } else { result[5] = ynf.format(ys); } } if (Double.isNaN(ye) && intervalDataset != null && intervalDataset.getEndY(series, item) == null) { result[6] = getNullYString(); } else { if (ydf != null) { result[6] = ydf.format(new Date((long) ye)); } else { result[6] = ynf.format(ye); } } return result; } /** * Generates the item label text for an item in a dataset. * * @param dataset the dataset ({@code null} not permitted). * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The label text (possibly {@code null}). */ @Override public String generateLabel(XYDataset dataset, int series, int item) { return generateLabelString(dataset, series, item); } /** * Returns an independent copy of the generator. * * @return A clone. * * @throws CloneNotSupportedException if cloning is not supported. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Tests this object for equality with an arbitrary object. * * @param obj the other object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof IntervalXYItemLabelGenerator)) { return false; } return super.equals(obj); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/IntervalXYToolTipGenerator.java000066400000000000000000000215521463604235500324170ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------- * IntervalXYToolTipGenerator.java * ------------------------------- * (C) Copyright 2015-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import java.io.Serializable; import java.text.DateFormat; import java.text.MessageFormat; import java.text.NumberFormat; import java.util.Date; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.xy.IntervalXYDataset; import org.jfree.data.xy.XYDataset; /** * A tooltip generator for datasets that implement the * {@link IntervalXYDataset} interface. */ public class IntervalXYToolTipGenerator extends AbstractXYItemLabelGenerator implements XYToolTipGenerator, Cloneable, PublicCloneable, Serializable { /** The default item label format. */ public static final String DEFAULT_TOOL_TIP_FORMAT = "{0}: ({1} - {2}), ({5} - {6})"; /** * Creates a new tooltip generator using default number formatters. */ public IntervalXYToolTipGenerator() { this(DEFAULT_TOOL_TIP_FORMAT, NumberFormat.getNumberInstance(), NumberFormat.getNumberInstance()); } /** * Creates a new tooltip generator using the specified number formatters. * * @param formatString the item label format string ({@code null} not * permitted). * @param xFormat the format object for the x values ({@code null} * not permitted). * @param yFormat the format object for the y values ({@code null} * not permitted). */ public IntervalXYToolTipGenerator(String formatString, NumberFormat xFormat, NumberFormat yFormat) { super(formatString, xFormat, yFormat); } /** * Creates a new tool tip generator using the specified formatters. * * @param formatString the item label format string ({@code null} * not permitted). * @param xFormat the format object for the x values ({@code null} * not permitted). * @param yFormat the format object for the y values ({@code null} * not permitted). */ public IntervalXYToolTipGenerator(String formatString, DateFormat xFormat, NumberFormat yFormat) { super(formatString, xFormat, yFormat); } /** * Creates a new tool tip generator using the specified formatters (a * number formatter for the x-values and a date formatter for the * y-values). * * @param formatString the item label format string ({@code null} * not permitted). * @param xFormat the format object for the x values ({@code null} * permitted). * @param yFormat the format object for the y values ({@code null} * not permitted). */ public IntervalXYToolTipGenerator(String formatString, NumberFormat xFormat, DateFormat yFormat) { super(formatString, xFormat, yFormat); } /** * Creates a new tool tip generator using the specified date formatters. * * @param formatString the label format string ({@code null} not * permitted). * @param xFormat the format object for the x values ({@code null} not * permitted). * @param yFormat the format object for the y values ({@code null} * not permitted). */ public IntervalXYToolTipGenerator(String formatString, DateFormat xFormat, DateFormat yFormat) { super(formatString, xFormat, yFormat); } /** * Creates the array of items that can be passed to the * {@link MessageFormat} class for creating labels. * * @param dataset the dataset ({@code null} not permitted). * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return An array of seven items from the dataset formatted as * {@code String} objects (never {@code null}). */ @Override protected Object[] createItemArray(XYDataset dataset, int series, int item) { IntervalXYDataset intervalDataset = null; if (dataset instanceof IntervalXYDataset) { intervalDataset = (IntervalXYDataset) dataset; } Object[] result = new Object[7]; result[0] = dataset.getSeriesKey(series).toString(); double x = dataset.getXValue(series, item); double xs = x; double xe = x; double y = dataset.getYValue(series, item); double ys = y; double ye = y; if (intervalDataset != null) { xs = intervalDataset.getStartXValue(series, item); xe = intervalDataset.getEndXValue(series, item); ys = intervalDataset.getStartYValue(series, item); ye = intervalDataset.getEndYValue(series, item); } DateFormat xdf = getXDateFormat(); if (xdf != null) { result[1] = xdf.format(new Date((long) x)); result[2] = xdf.format(new Date((long) xs)); result[3] = xdf.format(new Date((long) xe)); } else { NumberFormat xnf = getXFormat(); result[1] = xnf.format(x); result[2] = xnf.format(xs); result[3] = xnf.format(xe); } NumberFormat ynf = getYFormat(); DateFormat ydf = getYDateFormat(); if (Double.isNaN(y) && dataset.getY(series, item) == null) { result[4] = getNullYString(); } else { if (ydf != null) { result[4] = ydf.format(new Date((long) y)); } else { result[4] = ynf.format(y); } } if (Double.isNaN(ys) && intervalDataset != null && intervalDataset.getStartY(series, item) == null) { result[5] = getNullYString(); } else { if (ydf != null) { result[5] = ydf.format(new Date((long) ys)); } else { result[5] = ynf.format(ys); } } if (Double.isNaN(ye) && intervalDataset != null && intervalDataset.getEndY(series, item) == null) { result[6] = getNullYString(); } else { if (ydf != null) { result[6] = ydf.format(new Date((long) ye)); } else { result[6] = ynf.format(ye); } } return result; } /** * Generates the tool tip text for an item in a dataset. * * @param dataset the dataset ({@code null} not permitted). * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The tool tip text (possibly {@code null}). */ @Override public String generateToolTip(XYDataset dataset, int series, int item) { return generateLabelString(dataset, series, item); } /** * Returns an independent copy of the generator. * * @return A clone. * * @throws CloneNotSupportedException if cloning is not supported. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Tests this object for equality with an arbitrary object. * * @param obj the other object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof IntervalXYToolTipGenerator)) { return false; } return super.equals(obj); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/ItemLabelAnchor.java000066400000000000000000000241751463604235500301650ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * ItemLabelAnchor.java * -------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import java.io.ObjectStreamException; import java.io.Serializable; /** * An enumeration of the positions that a value label can take, relative to an * item in a {@link org.jfree.chart.plot.CategoryPlot}. */ public final class ItemLabelAnchor implements Serializable { /** For serialization. */ private static final long serialVersionUID = -1233101616128695658L; /** CENTER. */ public static final ItemLabelAnchor CENTER = new ItemLabelAnchor("ItemLabelAnchor.CENTER"); /** INSIDE1. */ public static final ItemLabelAnchor INSIDE1 = new ItemLabelAnchor("ItemLabelAnchor.INSIDE1"); /** INSIDE2. */ public static final ItemLabelAnchor INSIDE2 = new ItemLabelAnchor("ItemLabelAnchor.INSIDE2"); /** INSIDE3. */ public static final ItemLabelAnchor INSIDE3 = new ItemLabelAnchor("ItemLabelAnchor.INSIDE3"); /** INSIDE4. */ public static final ItemLabelAnchor INSIDE4 = new ItemLabelAnchor("ItemLabelAnchor.INSIDE4"); /** INSIDE5. */ public static final ItemLabelAnchor INSIDE5 = new ItemLabelAnchor("ItemLabelAnchor.INSIDE5"); /** INSIDE6. */ public static final ItemLabelAnchor INSIDE6 = new ItemLabelAnchor("ItemLabelAnchor.INSIDE6"); /** INSIDE7. */ public static final ItemLabelAnchor INSIDE7 = new ItemLabelAnchor("ItemLabelAnchor.INSIDE7"); /** INSIDE8. */ public static final ItemLabelAnchor INSIDE8 = new ItemLabelAnchor("ItemLabelAnchor.INSIDE8"); /** INSIDE9. */ public static final ItemLabelAnchor INSIDE9 = new ItemLabelAnchor("ItemLabelAnchor.INSIDE9"); /** INSIDE10. */ public static final ItemLabelAnchor INSIDE10 = new ItemLabelAnchor("ItemLabelAnchor.INSIDE10"); /** INSIDE11. */ public static final ItemLabelAnchor INSIDE11 = new ItemLabelAnchor("ItemLabelAnchor.INSIDE11"); /** INSIDE12. */ public static final ItemLabelAnchor INSIDE12 = new ItemLabelAnchor("ItemLabelAnchor.INSIDE12"); /** OUTSIDE1. */ public static final ItemLabelAnchor OUTSIDE1 = new ItemLabelAnchor("ItemLabelAnchor.OUTSIDE1"); /** OUTSIDE2. */ public static final ItemLabelAnchor OUTSIDE2 = new ItemLabelAnchor("ItemLabelAnchor.OUTSIDE2"); /** OUTSIDE3. */ public static final ItemLabelAnchor OUTSIDE3 = new ItemLabelAnchor("ItemLabelAnchor.OUTSIDE3"); /** OUTSIDE4. */ public static final ItemLabelAnchor OUTSIDE4 = new ItemLabelAnchor("ItemLabelAnchor.OUTSIDE4"); /** OUTSIDE5. */ public static final ItemLabelAnchor OUTSIDE5 = new ItemLabelAnchor("ItemLabelAnchor.OUTSIDE5"); /** OUTSIDE6. */ public static final ItemLabelAnchor OUTSIDE6 = new ItemLabelAnchor("ItemLabelAnchor.OUTSIDE6"); /** OUTSIDE7. */ public static final ItemLabelAnchor OUTSIDE7 = new ItemLabelAnchor("ItemLabelAnchor.OUTSIDE7"); /** OUTSIDE8. */ public static final ItemLabelAnchor OUTSIDE8 = new ItemLabelAnchor("ItemLabelAnchor.OUTSIDE8"); /** OUTSIDE9. */ public static final ItemLabelAnchor OUTSIDE9 = new ItemLabelAnchor("ItemLabelAnchor.OUTSIDE9"); /** OUTSIDE10. */ public static final ItemLabelAnchor OUTSIDE10 = new ItemLabelAnchor("ItemLabelAnchor.OUTSIDE10"); /** OUTSIDE11. */ public static final ItemLabelAnchor OUTSIDE11 = new ItemLabelAnchor("ItemLabelAnchor.OUTSIDE11"); /** OUTSIDE12. */ public static final ItemLabelAnchor OUTSIDE12 = new ItemLabelAnchor("ItemLabelAnchor.OUTSIDE12"); /** The name. */ private String name; /** * Private constructor. * * @param name the name. */ private ItemLabelAnchor(String name) { this.name = name; } /** * Returns {@code true} if this anchor point is inside an area. * * @return {@code true} if this anchor point is inside an area, * {@code false} otherwise. */ public boolean isInternal() { return this == ItemLabelAnchor.CENTER || this == ItemLabelAnchor.INSIDE1 || this == ItemLabelAnchor.INSIDE2 || this == ItemLabelAnchor.INSIDE3 || this == ItemLabelAnchor.INSIDE4 || this == ItemLabelAnchor.INSIDE5 || this == ItemLabelAnchor.INSIDE6 || this == ItemLabelAnchor.INSIDE7 || this == ItemLabelAnchor.INSIDE8 || this == ItemLabelAnchor.INSIDE9 || this == ItemLabelAnchor.INSIDE10 || this == ItemLabelAnchor.INSIDE11 || this == ItemLabelAnchor.INSIDE12; } /** * Returns a string representing the object. * * @return The string. */ @Override public String toString() { return this.name; } /** * Returns {@code true} if this object is equal to the specified * object, and {@code false} otherwise. * * @param obj the other object. * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof ItemLabelAnchor)) { return false; } ItemLabelAnchor that = (ItemLabelAnchor) obj; if (!this.name.equals(that.toString())) { return false; } return true; } /** * Ensures that serialization returns the unique instances. * * @return The object. * * @throws ObjectStreamException if there is a problem. */ private Object readResolve() throws ObjectStreamException { ItemLabelAnchor result = null; if (this.equals(ItemLabelAnchor.CENTER)) { result = ItemLabelAnchor.CENTER; } else if (this.equals(ItemLabelAnchor.INSIDE1)) { result = ItemLabelAnchor.INSIDE1; } else if (this.equals(ItemLabelAnchor.INSIDE2)) { result = ItemLabelAnchor.INSIDE2; } else if (this.equals(ItemLabelAnchor.INSIDE3)) { result = ItemLabelAnchor.INSIDE3; } else if (this.equals(ItemLabelAnchor.INSIDE4)) { result = ItemLabelAnchor.INSIDE4; } else if (this.equals(ItemLabelAnchor.INSIDE5)) { result = ItemLabelAnchor.INSIDE5; } else if (this.equals(ItemLabelAnchor.INSIDE6)) { result = ItemLabelAnchor.INSIDE6; } else if (this.equals(ItemLabelAnchor.INSIDE7)) { result = ItemLabelAnchor.INSIDE7; } else if (this.equals(ItemLabelAnchor.INSIDE8)) { result = ItemLabelAnchor.INSIDE8; } else if (this.equals(ItemLabelAnchor.INSIDE9)) { result = ItemLabelAnchor.INSIDE9; } else if (this.equals(ItemLabelAnchor.INSIDE10)) { result = ItemLabelAnchor.INSIDE10; } else if (this.equals(ItemLabelAnchor.INSIDE11)) { result = ItemLabelAnchor.INSIDE11; } else if (this.equals(ItemLabelAnchor.INSIDE12)) { result = ItemLabelAnchor.INSIDE12; } else if (this.equals(ItemLabelAnchor.OUTSIDE1)) { result = ItemLabelAnchor.OUTSIDE1; } else if (this.equals(ItemLabelAnchor.OUTSIDE2)) { result = ItemLabelAnchor.OUTSIDE2; } else if (this.equals(ItemLabelAnchor.OUTSIDE3)) { result = ItemLabelAnchor.OUTSIDE3; } else if (this.equals(ItemLabelAnchor.OUTSIDE4)) { result = ItemLabelAnchor.OUTSIDE4; } else if (this.equals(ItemLabelAnchor.OUTSIDE5)) { result = ItemLabelAnchor.OUTSIDE5; } else if (this.equals(ItemLabelAnchor.OUTSIDE6)) { result = ItemLabelAnchor.OUTSIDE6; } else if (this.equals(ItemLabelAnchor.OUTSIDE7)) { result = ItemLabelAnchor.OUTSIDE7; } else if (this.equals(ItemLabelAnchor.OUTSIDE8)) { result = ItemLabelAnchor.OUTSIDE8; } else if (this.equals(ItemLabelAnchor.OUTSIDE9)) { result = ItemLabelAnchor.OUTSIDE9; } else if (this.equals(ItemLabelAnchor.OUTSIDE10)) { result = ItemLabelAnchor.OUTSIDE10; } else if (this.equals(ItemLabelAnchor.OUTSIDE11)) { result = ItemLabelAnchor.OUTSIDE11; } else if (this.equals(ItemLabelAnchor.OUTSIDE12)) { result = ItemLabelAnchor.OUTSIDE12; } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/ItemLabelClip.java000066400000000000000000000040001463604235500276230ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * ItemLabelClip.java * ------------------ * (C) Copyright 2021-present, by Yuri Blankenstein and Contributors. * * Original Author: Yuri Blankenstein; * Contributor(s): -; * */ package org.jfree.chart.labels; /** * The clip type for the label. Only used when * {@link ItemLabelAnchor#isInternal()} returns {@code true}, if {@code false} * {@code labelClip} is always considered to be {@link ItemLabelClip#NONE}) */ public enum ItemLabelClip { /** Only draw label when it fits the item */ FIT, /** No clipping, labels might overlap */ NONE, /** Does not draw outside the item, just clips the label */ CLIP, /** Truncates the label with '...' to fit the item */ TRUNCATE, /** Truncates the label on whole words with '...' to fit the item */ TRUNCATE_WORD }jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/ItemLabelPosition.java000066400000000000000000000222031463604235500305450ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * ItemLabelPosition.java * ---------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Yuri Blankenstein; * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.labels; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.ui.TextAnchor; import org.jfree.chart.util.Args; /** * The attributes that control the position of the label for each data item on * a chart. Instances of this class are immutable. */ public class ItemLabelPosition implements Serializable { /** For serialization. */ private static final long serialVersionUID = 5845390630157034499L; /** The item label anchor point. */ private ItemLabelAnchor itemLabelAnchor; /** The text anchor. */ private TextAnchor textAnchor; /** The rotation anchor. */ private TextAnchor rotationAnchor; /** The rotation angle. */ private double angle; /** The item label clip type. */ private ItemLabelClip itemLabelClip; /** * Creates a new position record with default settings. */ public ItemLabelPosition() { this(ItemLabelAnchor.OUTSIDE12, TextAnchor.BOTTOM_CENTER, TextAnchor.CENTER, 0.0); } /** * Creates a new position record (with zero rotation). * * @param itemLabelAnchor the item label anchor ({@code null} not * permitted). * @param textAnchor the text anchor ({@code null} not permitted). */ public ItemLabelPosition(ItemLabelAnchor itemLabelAnchor, TextAnchor textAnchor) { this(itemLabelAnchor, textAnchor, TextAnchor.CENTER, 0.0); } /** * Creates a new position record. The item label anchor is a point relative * to the data item (dot, bar or other visual item) on a chart. The item * label is aligned by aligning the text anchor with the item label anchor. * * @param itemLabelAnchor the item label anchor ({@code null} not * permitted). * @param textAnchor the text anchor ({@code null} not permitted). * @param itemLabelClip The clip type for the label ({@code null} not * permitted. Only used when * {@link ItemLabelAnchor#isInternal()} returns * {@code true}, if {@code false} {@code labelClip} * is always considered to be * {@link ItemLabelClip#NONE}) */ public ItemLabelPosition(ItemLabelAnchor itemLabelAnchor, TextAnchor textAnchor, ItemLabelClip itemLabelClip) { this(itemLabelAnchor, textAnchor, TextAnchor.CENTER, 0.0, itemLabelClip); } /** * Creates a new position record. The item label anchor is a point * relative to the data item (dot, bar or other visual item) on a chart. * The item label is aligned by aligning the text anchor with the * item label anchor. * * @param itemLabelAnchor the item label anchor ({@code null} not * permitted). * @param textAnchor the text anchor ({@code null} not permitted). * @param rotationAnchor the rotation anchor ({@code null} not * permitted). * @param angle the rotation angle (in radians). */ public ItemLabelPosition(ItemLabelAnchor itemLabelAnchor, TextAnchor textAnchor, TextAnchor rotationAnchor, double angle) { this(itemLabelAnchor, textAnchor, rotationAnchor, angle, ItemLabelClip.FIT); } /** * Creates a new position record. The item label anchor is a point relative * to the data item (dot, bar or other visual item) on a chart. The item * label is aligned by aligning the text anchor with the item label anchor. * * @param itemLabelAnchor the item label anchor ({@code null} not * permitted). * @param textAnchor the text anchor ({@code null} not permitted). * @param rotationAnchor the rotation anchor ({@code null} not permitted). * @param angle the rotation angle (in radians). * @param itemLabelClip The clip type for the label ({@code null} not * permitted. Only used when * {@link ItemLabelAnchor#isInternal()} returns * {@code true}, if {@code false} {@code labelClip} * is always considered to be * {@link ItemLabelClip#NONE}) */ public ItemLabelPosition(ItemLabelAnchor itemLabelAnchor, TextAnchor textAnchor, TextAnchor rotationAnchor, double angle, ItemLabelClip itemLabelClip) { Args.nullNotPermitted(itemLabelAnchor, "itemLabelAnchor"); Args.nullNotPermitted(textAnchor, "textAnchor"); Args.nullNotPermitted(rotationAnchor, "rotationAnchor"); Args.nullNotPermitted(itemLabelClip, "labelClip"); this.itemLabelAnchor = itemLabelAnchor; this.textAnchor = textAnchor; this.rotationAnchor = rotationAnchor; this.angle = angle; this.itemLabelClip = itemLabelClip; } /** * Returns the item label anchor. * * @return The item label anchor (never {@code null}). */ public ItemLabelAnchor getItemLabelAnchor() { return this.itemLabelAnchor; } /** * Returns the text anchor. * * @return The text anchor (never {@code null}). */ public TextAnchor getTextAnchor() { return this.textAnchor; } /** * Returns the rotation anchor point. * * @return The rotation anchor point (never {@code null}). */ public TextAnchor getRotationAnchor() { return this.rotationAnchor; } /** * Returns the angle of rotation for the label. * * @return The angle (in radians). */ public double getAngle() { return this.angle; } /** * Returns the clip type for the label. * * @return The clip type for the label. */ public ItemLabelClip getItemLabelClip() { return this.itemLabelClip; } /** * Tests this object for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof ItemLabelPosition)) { return false; } ItemLabelPosition that = (ItemLabelPosition) obj; if (!Objects.equals(this.itemLabelAnchor, that.itemLabelAnchor)) { return false; } if (!Objects.equals(this.textAnchor, that.textAnchor)) { return false; } if (!Objects.equals(this.rotationAnchor, that.rotationAnchor)) { return false; } if (Double.doubleToLongBits(this.angle) != Double.doubleToLongBits(that.angle)) { return false; } if (!Objects.equals(this.itemLabelClip, that.itemLabelClip)) { return false; } return true; } @Override public int hashCode() { int hash = 5; hash = 97 * hash + Objects.hashCode(this.itemLabelAnchor); hash = 97 * hash + Objects.hashCode(this.textAnchor); hash = 97 * hash + Objects.hashCode(this.rotationAnchor); hash = 97 * hash + (int) (Double.doubleToLongBits(this.angle) ^ (Double.doubleToLongBits(this.angle) >>> 32)); hash = 97 * hash + Objects.hashCode(this.itemLabelClip); return hash; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/MultipleXYSeriesLabelGenerator.java000066400000000000000000000172111463604235500332230ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------------- * MultipleXYSeriesLabelGenerator.java * ----------------------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import java.io.Serializable; import java.text.MessageFormat; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.jfree.chart.HashUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.xy.XYDataset; /** * A series label generator for plots that use data from * an {@link org.jfree.data.xy.XYDataset}. */ public class MultipleXYSeriesLabelGenerator implements XYSeriesLabelGenerator, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 138976236941898560L; /** The default item label format. */ public static final String DEFAULT_LABEL_FORMAT = "{0}"; /** The format pattern for the initial part of the label. */ private String formatPattern; /** The format pattern for additional labels. */ private String additionalFormatPattern; /** Storage for the additional series labels. */ private Map seriesLabelLists; /** * Creates an item label generator using default number formatters. */ public MultipleXYSeriesLabelGenerator() { this(DEFAULT_LABEL_FORMAT); } /** * Creates a new series label generator. * * @param format the format pattern ({@code null} not permitted). */ public MultipleXYSeriesLabelGenerator(String format) { Args.nullNotPermitted(format, "format"); this.formatPattern = format; this.additionalFormatPattern = "\n{0}"; this.seriesLabelLists = new HashMap(); } /** * Adds an extra label for the specified series. * * @param series the series index. * @param label the label. */ public void addSeriesLabel(int series, String label) { Integer key = series; List labelList = (List) this.seriesLabelLists.get(key); if (labelList == null) { labelList = new java.util.ArrayList(); this.seriesLabelLists.put(key, labelList); } labelList.add(label); } /** * Clears the extra labels for the specified series. * * @param series the series index. */ public void clearSeriesLabels(int series) { this.seriesLabelLists.put(series, null); } /** * Generates a label for the specified series. This label will be * used for the chart legend. * * @param dataset the dataset ({@code null} not permitted). * @param series the series. * * @return A series label. */ @Override public String generateLabel(XYDataset dataset, int series) { Args.nullNotPermitted(dataset, "dataset"); StringBuilder label = new StringBuilder(); label.append(MessageFormat.format(this.formatPattern, createItemArray(dataset, series))); List extraLabels = (List) this.seriesLabelLists.get(series); if (extraLabels != null) { Object[] temp = new Object[1]; for (int i = 0; i < extraLabels.size(); i++) { temp[0] = extraLabels.get(i); String labelAddition = MessageFormat.format( this.additionalFormatPattern, temp); label.append(labelAddition); } } return label.toString(); } /** * Creates the array of items that can be passed to the * {@link MessageFormat} class for creating labels. * * @param dataset the dataset ({@code null} not permitted). * @param series the series (zero-based index). * * @return The items (never {@code null}). */ protected Object[] createItemArray(XYDataset dataset, int series) { Object[] result = new Object[1]; result[0] = dataset.getSeriesKey(series).toString(); return result; } /** * Returns an independent copy of the generator. * * @return A clone. * * @throws CloneNotSupportedException if cloning is not supported. */ @Override public Object clone() throws CloneNotSupportedException { MultipleXYSeriesLabelGenerator clone = (MultipleXYSeriesLabelGenerator) super.clone(); clone.seriesLabelLists = new HashMap(); Set keys = this.seriesLabelLists.keySet(); Iterator iterator = keys.iterator(); while (iterator.hasNext()) { Object key = iterator.next(); Object entry = this.seriesLabelLists.get(key); Object toAdd = entry; if (entry instanceof PublicCloneable) { PublicCloneable pc = (PublicCloneable) entry; toAdd = pc.clone(); } clone.seriesLabelLists.put(key, toAdd); } return clone; } /** * Tests this object for equality with an arbitrary object. * * @param obj the other object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof MultipleXYSeriesLabelGenerator)) { return false; } MultipleXYSeriesLabelGenerator that = (MultipleXYSeriesLabelGenerator) obj; if (!this.formatPattern.equals(that.formatPattern)) { return false; } if (!this.additionalFormatPattern.equals( that.additionalFormatPattern)) { return false; } if (!this.seriesLabelLists.equals(that.seriesLabelLists)) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = 127; result = HashUtils.hashCode(result, this.formatPattern); result = HashUtils.hashCode(result, this.additionalFormatPattern); result = HashUtils.hashCode(result, this.seriesLabelLists); return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/PieSectionLabelGenerator.java000066400000000000000000000067411463604235500320440ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------- * PieSectionLabelGenerator.java * ----------------------------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import java.awt.Font; import java.awt.Paint; import java.awt.font.TextAttribute; import java.text.AttributedString; import org.jfree.data.general.PieDataset; /** * Interface for a label generator for plots that use data from * a {@link PieDataset}. */ public interface PieSectionLabelGenerator { /** * Generates a label for a pie section. * * @param dataset the dataset ({@code null} not permitted). * @param key the section key ({@code null} not permitted). * * @return The label (possibly {@code null}). */ String generateSectionLabel(PieDataset dataset, Comparable key); /** * Generates an attributed label for the specified series, or * {@code null} if no attributed label is available (in which case, * the string returned by * {@link #generateSectionLabel(PieDataset, Comparable)} will * provide the fallback). Only certain attributes are recognised by the * code that ultimately displays the labels: *

    *
  • {@link TextAttribute#FONT}: will set the font;
  • *
  • {@link TextAttribute#POSTURE}: a value of * {@link TextAttribute#POSTURE_OBLIQUE} will add {@link Font#ITALIC} to * the current font;
  • *
  • {@link TextAttribute#WEIGHT}: a value of * {@link TextAttribute#WEIGHT_BOLD} will add {@link Font#BOLD} to the * current font;
  • *
  • {@link TextAttribute#FOREGROUND}: this will set the {@link Paint} * for the current
  • *
  • {@link TextAttribute#SUPERSCRIPT}: the values * {@link TextAttribute#SUPERSCRIPT_SUB} and * {@link TextAttribute#SUPERSCRIPT_SUPER} are recognised.
  • *
* * @param dataset the dataset. * @param key the key. * * @return An attributed label (possibly {@code null}). */ AttributedString generateAttributedSectionLabel(PieDataset dataset, Comparable key); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/PieToolTipGenerator.java000066400000000000000000000041711463604235500310650ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * PieToolTipGenerator.java * ------------------------ * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import org.jfree.data.general.PieDataset; /** * A tool tip generator that is used by the * {@link org.jfree.chart.plot.PiePlot} class. */ public interface PieToolTipGenerator { /** * Generates a tool tip text item for the specified item in the dataset. * This method can return {@code null} to indicate that no tool tip * should be displayed for an item. * * @param dataset the dataset ({@code null} not permitted). * @param key the section key ({@code null} not permitted). * * @return The tool tip text (possibly {@code null}). */ String generateToolTip(PieDataset dataset, Comparable key); } StandardCategoryItemLabelGenerator.java000066400000000000000000000131071463604235500337720ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------------------- * StandardCategoryItemLabelGenerator.java * --------------------------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.labels; import java.io.Serializable; import java.text.DateFormat; import java.text.NumberFormat; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.category.CategoryDataset; /** * A standard label generator that can be used with a * {@link org.jfree.chart.renderer.category.CategoryItemRenderer}. */ public class StandardCategoryItemLabelGenerator extends AbstractCategoryItemLabelGenerator implements CategoryItemLabelGenerator, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 3499701401211412882L; /** The default format string. */ public static final String DEFAULT_LABEL_FORMAT_STRING = "{2}"; /** * Creates a new generator with a default number formatter. */ public StandardCategoryItemLabelGenerator() { super(DEFAULT_LABEL_FORMAT_STRING, NumberFormat.getInstance()); } /** * Creates a new generator with the specified number formatter. * * @param labelFormat the label format string ({@code null} not * permitted). * @param formatter the number formatter ({@code null} not permitted). */ public StandardCategoryItemLabelGenerator(String labelFormat, NumberFormat formatter) { super(labelFormat, formatter); } /** * Creates a new generator with the specified number formatter. * * @param labelFormat the label format string ({@code null} not * permitted). * @param formatter the number formatter ({@code null} not permitted). * @param percentFormatter the percent formatter ({@code null} not * permitted). */ public StandardCategoryItemLabelGenerator(String labelFormat, NumberFormat formatter, NumberFormat percentFormatter) { super(labelFormat, formatter, percentFormatter); } /** * Creates a new generator with the specified date formatter. * * @param labelFormat the label format string ({@code null} not * permitted). * @param formatter the date formatter ({@code null} not permitted). */ public StandardCategoryItemLabelGenerator(String labelFormat, DateFormat formatter) { super(labelFormat, formatter); } /** * Generates the label for an item in a dataset. Note: in the current * dataset implementation, each row is a series, and each column contains * values for a particular category. * * @param dataset the dataset ({@code null} not permitted). * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The label (possibly {@code null}). */ @Override public String generateLabel(CategoryDataset dataset, int row, int column) { return generateLabelString(dataset, row, column); } /** * Tests this generator for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return {@code true} if this generator is equal to * {@code obj}, and {@code false} otherwise. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof StandardCategoryItemLabelGenerator)) { return false; } StandardCategoryItemLabelGenerator that = (StandardCategoryItemLabelGenerator) obj; if (!that.canEqual(this)) { return false; } return super.equals(obj); } @Override public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof StandardCategoryItemLabelGenerator); } @Override public int hashCode() { int hash = super.hashCode(); return hash; } } StandardCategorySeriesLabelGenerator.java000066400000000000000000000121021463604235500343200ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------------------- * StandardCategorySeriesLabelGenerator.java * ----------------------------------------- * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.labels; import java.io.Serializable; import java.text.MessageFormat; import java.util.Objects; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.category.CategoryDataset; /** * A standard series label generator for plots that use data from * a {@link org.jfree.data.category.CategoryDataset}. */ public class StandardCategorySeriesLabelGenerator implements CategorySeriesLabelGenerator, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 4630760091523940820L; /** The default item label format. */ public static final String DEFAULT_LABEL_FORMAT = "{0}"; /** The format pattern. */ private String formatPattern; /** * Creates a default series label generator (uses * {@link #DEFAULT_LABEL_FORMAT}). */ public StandardCategorySeriesLabelGenerator() { this(DEFAULT_LABEL_FORMAT); } /** * Creates a new series label generator. * * @param format the format pattern ({@code null} not permitted). */ public StandardCategorySeriesLabelGenerator(String format) { Args.nullNotPermitted(format, "format"); this.formatPattern = format; } /** * Generates a label for the specified series. * * @param dataset the dataset ({@code null} not permitted). * @param series the series. * * @return A series label. */ @Override public String generateLabel(CategoryDataset dataset, int series) { Args.nullNotPermitted(dataset, "dataset"); String label = MessageFormat.format(this.formatPattern, createItemArray(dataset, series)); return label; } /** * Creates the array of items that can be passed to the * {@link MessageFormat} class for creating labels. * * @param dataset the dataset ({@code null} not permitted). * @param series the series (zero-based index). * * @return The items (never {@code null}). */ protected Object[] createItemArray(CategoryDataset dataset, int series) { Object[] result = new Object[1]; result[0] = dataset.getRowKey(series).toString(); return result; } /** * Returns an independent copy of the generator. * * @return A clone. * * @throws CloneNotSupportedException if cloning is not supported. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Tests this object for equality with an arbitrary object. * * @param obj the other object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof StandardCategorySeriesLabelGenerator)) { return false; } StandardCategorySeriesLabelGenerator that = (StandardCategorySeriesLabelGenerator) obj; if (!Objects.equals(this.formatPattern, that.formatPattern)) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int hash = 5; hash = 53 * hash + Objects.hashCode(this.formatPattern); return hash; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/StandardCategoryToolTipGenerator.java000066400000000000000000000123341463604235500336060ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------------- * StandardCategoryToolTipGenerator.java * ------------------------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.labels; import java.io.Serializable; import java.text.DateFormat; import java.text.NumberFormat; import org.jfree.data.category.CategoryDataset; /** * A standard tool tip generator that can be used with a * {@link org.jfree.chart.renderer.category.CategoryItemRenderer}. */ public class StandardCategoryToolTipGenerator extends AbstractCategoryItemLabelGenerator implements CategoryToolTipGenerator, Serializable { /** For serialization. */ private static final long serialVersionUID = -6768806592218710764L; /** The default format string. */ public static final String DEFAULT_TOOL_TIP_FORMAT_STRING = "({0}, {1}) = {2}"; /** * Creates a new generator with a default number formatter. */ public StandardCategoryToolTipGenerator() { super(DEFAULT_TOOL_TIP_FORMAT_STRING, NumberFormat.getInstance()); } /** * Creates a new generator with the specified number formatter. * * @param labelFormat the label format string ({@code null} not * permitted). * @param formatter the number formatter ({@code null} not permitted). */ public StandardCategoryToolTipGenerator(String labelFormat, NumberFormat formatter) { super(labelFormat, formatter); } /** * Creates a new generator with the specified date formatter. * * @param labelFormat the label format string ({@code null} not * permitted). * @param formatter the date formatter ({@code null} not permitted). */ public StandardCategoryToolTipGenerator(String labelFormat, DateFormat formatter) { super(labelFormat, formatter); } /** * Generates the tool tip text for an item in a dataset. Note: in the * current dataset implementation, each row is a series, and each column * contains values for a particular category. * * @param dataset the dataset ({@code null} not permitted). * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The tooltip text (possibly {@code null}). */ @Override public String generateToolTip(CategoryDataset dataset, int row, int column) { return generateLabelString(dataset, row, column); } /** * Tests this generator for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof StandardCategoryToolTipGenerator)) { return false; } StandardCategoryToolTipGenerator that = (StandardCategoryToolTipGenerator) obj; if (!that.canEqual(this)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof StandardCategoryToolTipGenerator); } @Override public int hashCode() { int hash = super.hashCode(); // equals calls superclass, hashCode must also return hash; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/StandardCrosshairLabelGenerator.java000066400000000000000000000111421463604235500334070ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------------ * StandardCrosshairLabelGenerator.java * ------------------------------------ * (C) Copyright 2009-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import java.io.Serializable; import java.text.MessageFormat; import java.text.NumberFormat; import org.jfree.chart.plot.Crosshair; /** * A default label generator. */ public class StandardCrosshairLabelGenerator implements CrosshairLabelGenerator, Serializable { /** The label format string. */ private String labelTemplate; /** A number formatter for the value. */ private NumberFormat numberFormat; /** * Creates a new instance with default attributes. */ public StandardCrosshairLabelGenerator() { this("{0}", NumberFormat.getNumberInstance()); } /** * Creates a new instance with the specified attributes. * * @param labelTemplate the label template ({@code null} not * permitted). * @param numberFormat the number formatter ({@code null} not * permitted). */ public StandardCrosshairLabelGenerator(String labelTemplate, NumberFormat numberFormat) { super(); if (labelTemplate == null) { throw new IllegalArgumentException( "Null 'labelTemplate' argument."); } if (numberFormat == null) { throw new IllegalArgumentException( "Null 'numberFormat' argument."); } this.labelTemplate = labelTemplate; this.numberFormat = numberFormat; } /** * Returns the label template string. * * @return The label template string (never {@code null}). */ public String getLabelTemplate() { return this.labelTemplate; } /** * Returns the number formatter. * * @return The formatter (never {@code null}). */ public NumberFormat getNumberFormat() { return this.numberFormat; } /** * Returns a string that can be used as the label for a crosshair. * * @param crosshair the crosshair ({@code null} not permitted). * * @return The label (possibly {@code null}). */ @Override public String generateLabel(Crosshair crosshair) { Object[] v = new Object[] {this.numberFormat.format( crosshair.getValue())}; String result = MessageFormat.format(this.labelTemplate, v); return result; } /** * Tests this generator for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof StandardCrosshairLabelGenerator)) { return false; } StandardCrosshairLabelGenerator that = (StandardCrosshairLabelGenerator) obj; if (!this.labelTemplate.equals(that.labelTemplate)) { return false; } if (!this.numberFormat.equals(that.numberFormat)) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code for this instance. */ @Override public int hashCode() { return this.labelTemplate.hashCode(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/StandardFlowLabelGenerator.java000066400000000000000000000102451463604235500323640ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------- * StandardFlowLabelGenerator.java * ------------------------------- * (C) Copyright 2021-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import java.io.Serializable; import java.util.Formatter; import java.util.Objects; import org.jfree.chart.util.Args; import org.jfree.data.flow.FlowDataset; import org.jfree.data.flow.FlowKey; /** * Standard flow label generator. Instances of this class are immutable. * * @since 1.5.3 */ public class StandardFlowLabelGenerator implements FlowLabelGenerator, Serializable { /** The default template for formatting the label. */ public static final String DEFAULT_TEMPLATE = "%2$s to %3$s = %4$,.2f"; /** The template. */ private String template; /** * Creates a new instance with the default template. */ public StandardFlowLabelGenerator() { this(DEFAULT_TEMPLATE); } /** * Creates a new generator with the specified template. The template * is passed to a Java Formatter instance along with four arguments, the * stage (an integer), the source (a String), the destination (a String) * and the flow value (a Number). * * @param template the template ({@code null} not permitted). */ public StandardFlowLabelGenerator(String template) { Args.nullNotPermitted(template, "template"); this.template = template; } /** * Returns a label for the specified flow. * * @param dataset the flow dataset ({@code null} not permitted). * @param key the flow key ({@code null} not permitted). * * @return The label (possibly {@code null}). */ @Override public String generateLabel(FlowDataset dataset, FlowKey key) { Args.nullNotPermitted(dataset, "dataset"); Args.nullNotPermitted(key, "key"); String result; try (Formatter formatter = new Formatter(new StringBuilder())) { Number value = dataset.getFlow(key.getStage(), key.getSource(), key.getDestination()); formatter.format(this.template, key.getStage(), key.getSource(), key.getDestination(), value); result = formatter.toString(); } return result; } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object to test ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (!(obj instanceof StandardFlowLabelGenerator)) { return false; } StandardFlowLabelGenerator that = (StandardFlowLabelGenerator) obj; if (!this.template.equals(that.template)) { return false; } return true; } @Override public int hashCode() { int hash = 3; hash = 97 * hash + Objects.hashCode(this.template); return hash; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/StandardPieSectionLabelGenerator.java000066400000000000000000000212521463604235500335170ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------------- * StandardPieSectionLabelGenerator.java * ------------------------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import java.awt.Font; import java.awt.Paint; import java.awt.font.TextAttribute; import java.io.Serializable; import java.text.AttributedString; import java.text.NumberFormat; import java.util.HashMap; import java.util.Locale; import java.util.Map; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.general.PieDataset; /** * A standard item label generator for plots that use data from a * {@link PieDataset}. *

* For the label format, use {0} where the pie section key should be inserted, * {1} for the absolute section value and {2} for the percent amount of the pie * section, e.g. {@code "{0} = {1} ({2})"} will display as * {@code apple = 120 (5%)}. */ public class StandardPieSectionLabelGenerator extends AbstractPieItemLabelGenerator implements PieSectionLabelGenerator, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 3064190563760203668L; /** The default section label format. */ public static final String DEFAULT_SECTION_LABEL_FORMAT = "{0}"; /** * An optional map between item indices (Integer) and attributed labels * (instances of AttributedString). */ private Map attributedLabels; /** * Creates a new section label generator using * {@link #DEFAULT_SECTION_LABEL_FORMAT} as the label format string, and * platform default number and percentage formatters. */ public StandardPieSectionLabelGenerator() { this(DEFAULT_SECTION_LABEL_FORMAT, NumberFormat.getNumberInstance(), NumberFormat.getPercentInstance()); } /** * Creates a new instance for the specified locale. * * @param locale the local ({@code null} not permitted). */ public StandardPieSectionLabelGenerator(Locale locale) { this(DEFAULT_SECTION_LABEL_FORMAT, locale); } /** * Creates a new section label generator using the specified label format * string, and platform default number and percentage formatters. * * @param labelFormat the label format ({@code null} not permitted). */ public StandardPieSectionLabelGenerator(String labelFormat) { this(labelFormat, NumberFormat.getNumberInstance(), NumberFormat.getPercentInstance()); } /** * Creates a new instance for the specified locale. * * @param labelFormat the label format ({@code null} not permitted). * @param locale the local ({@code null} not permitted). */ public StandardPieSectionLabelGenerator(String labelFormat, Locale locale) { this(labelFormat, NumberFormat.getNumberInstance(locale), NumberFormat.getPercentInstance(locale)); } /** * Creates an item label generator using the specified number formatters. * * @param labelFormat the label format string ({@code null} not * permitted). * @param numberFormat the format object for the values ({@code null} * not permitted). * @param percentFormat the format object for the percentages * ({@code null} not permitted). */ public StandardPieSectionLabelGenerator(String labelFormat, NumberFormat numberFormat, NumberFormat percentFormat) { super(labelFormat, numberFormat, percentFormat); this.attributedLabels = new HashMap(); } /** * Returns the attributed label for a section, or {@code null} if none * is defined. * * @param section the section index. * * @return The attributed label. */ public AttributedString getAttributedLabel(int section) { return (AttributedString) this.attributedLabels.get(section); } /** * Sets the attributed label for a section. * * @param section the section index. * @param label the label ({@code null} permitted). */ public void setAttributedLabel(int section, AttributedString label) { this.attributedLabels.put(section, label); } /** * Generates a label for a pie section. * * @param dataset the dataset ({@code null} not permitted). * @param key the section key ({@code null} not permitted). * * @return The label (possibly {@code null}). */ @Override public String generateSectionLabel(PieDataset dataset, Comparable key) { return super.generateSectionLabel(dataset, key); } /** * Generates an attributed label for the specified series, or * {@code null} if no attributed label is available (in which case, * the string returned by * {@link #generateSectionLabel(PieDataset, Comparable)} will * provide the fallback). Only certain attributes are recognised by the * code that ultimately displays the labels: *

    *
  • {@link TextAttribute#FONT}: will set the font;
  • *
  • {@link TextAttribute#POSTURE}: a value of * {@link TextAttribute#POSTURE_OBLIQUE} will add {@link Font#ITALIC} to * the current font;
  • *
  • {@link TextAttribute#WEIGHT}: a value of * {@link TextAttribute#WEIGHT_BOLD} will add {@link Font#BOLD} to the * current font;
  • *
  • {@link TextAttribute#FOREGROUND}: this will set the {@link Paint} * for the current
  • *
  • {@link TextAttribute#SUPERSCRIPT}: the values * {@link TextAttribute#SUPERSCRIPT_SUB} and * {@link TextAttribute#SUPERSCRIPT_SUPER} are recognised.
  • *
* * @param dataset the dataset ({@code null} not permitted). * @param key the key. * * @return An attributed label (possibly {@code null}). */ @Override public AttributedString generateAttributedSectionLabel(PieDataset dataset, Comparable key) { return getAttributedLabel(dataset.getIndex(key)); } /** * Tests the generator for equality with an arbitrary object. * * @param obj the object to test against ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof StandardPieSectionLabelGenerator)) { return false; } StandardPieSectionLabelGenerator that = (StandardPieSectionLabelGenerator) obj; if (!this.attributedLabels.equals(that.attributedLabels)) { return false; } return super.equals(obj); } /** * Returns an independent copy of the generator. * * @return A clone. * * @throws CloneNotSupportedException should not happen. */ @Override public Object clone() throws CloneNotSupportedException { StandardPieSectionLabelGenerator clone = (StandardPieSectionLabelGenerator) super.clone(); clone.attributedLabels = new HashMap(); clone.attributedLabels.putAll(this.attributedLabels); return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/StandardPieToolTipGenerator.java000066400000000000000000000120261463604235500325440ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------------- * StandardPieToolTipGenerator.java * -------------------------------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Richard Atkinson; * Andreas Schroeder; * */ package org.jfree.chart.labels; import java.io.Serializable; import java.text.NumberFormat; import java.util.Locale; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.general.PieDataset; /** * A standard item label generator for plots that use data from a * {@link PieDataset}. *

* For the label format, use {0} where the pie section key should be inserted, * {1} for the absolute section value and {2} for the percent amount of the pie * section, e.g. {@code "{0} = {1} ({2})"} will display as * {@code apple = 120 (5%)}. */ public class StandardPieToolTipGenerator extends AbstractPieItemLabelGenerator implements PieToolTipGenerator, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 2995304200445733779L; /** The default tooltip format. */ public static final String DEFAULT_TOOLTIP_FORMAT = "{0}: ({1}, {2})"; /** * Creates an item label generator using default number formatters. */ public StandardPieToolTipGenerator() { this(DEFAULT_TOOLTIP_FORMAT); } /** * Creates a pie tool tip generator for the specified locale, using the * default format string. * * @param locale the locale ({@code null} not permitted). */ public StandardPieToolTipGenerator(Locale locale) { this(DEFAULT_TOOLTIP_FORMAT, locale); } /** * Creates a pie tool tip generator for the default locale. * * @param labelFormat the label format ({@code null} not permitted). */ public StandardPieToolTipGenerator(String labelFormat) { this(labelFormat, Locale.getDefault()); } /** * Creates a pie tool tip generator for the specified locale. * * @param labelFormat the label format ({@code null} not permitted). * @param locale the locale ({@code null} not permitted). */ public StandardPieToolTipGenerator(String labelFormat, Locale locale) { this(labelFormat, NumberFormat.getNumberInstance(locale), NumberFormat.getPercentInstance(locale)); } /** * Creates an item label generator using the specified number formatters. * * @param labelFormat the label format string ({@code null} not * permitted). * @param numberFormat the format object for the values ({@code null} * not permitted). * @param percentFormat the format object for the percentages * ({@code null} not permitted). */ public StandardPieToolTipGenerator(String labelFormat, NumberFormat numberFormat, NumberFormat percentFormat) { super(labelFormat, numberFormat, percentFormat); } /** * Generates a tool tip text item for one section in a pie chart. * * @param dataset the dataset ({@code null} not permitted). * @param key the section key ({@code null} not permitted). * * @return The tool tip text (possibly {@code null}). */ @Override public String generateToolTip(PieDataset dataset, Comparable key) { return generateSectionLabel(dataset, key); } /** * Returns an independent copy of the generator. * * @return A clone. * * @throws CloneNotSupportedException should not happen. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/StandardXYItemLabelGenerator.java000066400000000000000000000150661463604235500326420ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------------- * StandardXYItemLabelGenerator.java * --------------------------------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import java.io.Serializable; import java.text.DateFormat; import java.text.NumberFormat; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.xy.XYDataset; /** * A standard item label generator for plots that use data from an * {@link org.jfree.data.xy.XYDataset}. */ public class StandardXYItemLabelGenerator extends AbstractXYItemLabelGenerator implements XYItemLabelGenerator, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 7807668053171837925L; /** The default item label format. */ public static final String DEFAULT_ITEM_LABEL_FORMAT = "{2}"; /** * Creates an item label generator using default number formatters. */ public StandardXYItemLabelGenerator() { this(DEFAULT_ITEM_LABEL_FORMAT, NumberFormat.getNumberInstance(), NumberFormat.getNumberInstance()); } /** * Creates an item label generator using the specified number formatters. * * @param formatString the item label format string ({@code null} not * permitted). */ public StandardXYItemLabelGenerator(String formatString) { this(formatString, NumberFormat.getNumberInstance(), NumberFormat.getNumberInstance()); } /** * Creates an item label generator using the specified number formatters. * * @param formatString the item label format string ({@code null} not * permitted). * @param xFormat the format object for the x values ({@code null} * not permitted). * @param yFormat the format object for the y values ({@code null} * not permitted). */ public StandardXYItemLabelGenerator(String formatString, NumberFormat xFormat, NumberFormat yFormat) { super(formatString, xFormat, yFormat); } /** * Creates an item label generator using the specified formatters. * * @param formatString the item label format string ({@code null} * not permitted). * @param xFormat the format object for the x values ({@code null} * not permitted). * @param yFormat the format object for the y values ({@code null} * not permitted). */ public StandardXYItemLabelGenerator(String formatString, DateFormat xFormat, NumberFormat yFormat) { super(formatString, xFormat, yFormat); } /** * Creates an item label generator using the specified formatters (a * number formatter for the x-values and a date formatter for the * y-values). * * @param formatString the item label format string ({@code null} * not permitted). * @param xFormat the format object for the x values ({@code null} * permitted). * @param yFormat the format object for the y values ({@code null} * not permitted). */ public StandardXYItemLabelGenerator(String formatString, NumberFormat xFormat, DateFormat yFormat) { super(formatString, xFormat, yFormat); } /** * Creates a label generator using the specified date formatters. * * @param formatString the label format string ({@code null} not * permitted). * @param xFormat the format object for the x values ({@code null} * not permitted). * @param yFormat the format object for the y values ({@code null} * not permitted). */ public StandardXYItemLabelGenerator(String formatString, DateFormat xFormat, DateFormat yFormat) { super(formatString, xFormat, yFormat); } /** * Generates the item label text for an item in a dataset. * * @param dataset the dataset ({@code null} not permitted). * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The label text (possibly {@code null}). */ @Override public String generateLabel(XYDataset dataset, int series, int item) { return generateLabelString(dataset, series, item); } /** * Returns an independent copy of the generator. * * @return A clone. * * @throws CloneNotSupportedException if cloning is not supported. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Tests this object for equality with an arbitrary object. * * @param obj the other object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof StandardXYItemLabelGenerator)) { return false; } return super.equals(obj); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/StandardXYSeriesLabelGenerator.java000066400000000000000000000124031463604235500331660ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------------- * StandardXYSeriesLabelGenerator.java * ----------------------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import java.io.Serializable; import java.text.MessageFormat; import org.jfree.chart.HashUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.xy.XYDataset; /** * A standard series label generator for plots that use data from * an {@link org.jfree.data.xy.XYDataset}. *

* This class implements {@code PublicCloneable} by mistake but we retain * this for the sake of backward compatibility. */ public class StandardXYSeriesLabelGenerator implements XYSeriesLabelGenerator, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 1916017081848400024L; /** The default item label format. */ public static final String DEFAULT_LABEL_FORMAT = "{0}"; /** The format pattern. */ private String formatPattern; /** * Creates a default series label generator (uses * {@link #DEFAULT_LABEL_FORMAT}). */ public StandardXYSeriesLabelGenerator() { this(DEFAULT_LABEL_FORMAT); } /** * Creates a new series label generator. * * @param format the format pattern ({@code null} not permitted). */ public StandardXYSeriesLabelGenerator(String format) { Args.nullNotPermitted(format, "format"); this.formatPattern = format; } /** * Generates a label for the specified series. This label will be * used for the chart legend. * * @param dataset the dataset ({@code null} not permitted). * @param series the series. * * @return A series label. */ @Override public String generateLabel(XYDataset dataset, int series) { Args.nullNotPermitted(dataset, "dataset"); String label = MessageFormat.format( this.formatPattern, createItemArray(dataset, series) ); return label; } /** * Creates the array of items that can be passed to the * {@link MessageFormat} class for creating labels. * * @param dataset the dataset ({@code null} not permitted). * @param series the series (zero-based index). * * @return The items (never {@code null}). */ protected Object[] createItemArray(XYDataset dataset, int series) { Object[] result = new Object[1]; result[0] = dataset.getSeriesKey(series).toString(); return result; } /** * Returns an independent copy of the generator. This is unnecessary, * because instances are immutable anyway, but we retain this * behaviour for backwards compatibility. * * @return A clone. * * @throws CloneNotSupportedException if cloning is not supported. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Tests this object for equality with an arbitrary object. * * @param obj the other object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof StandardXYSeriesLabelGenerator)) { return false; } StandardXYSeriesLabelGenerator that = (StandardXYSeriesLabelGenerator) obj; if (!this.formatPattern.equals(that.formatPattern)) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = 127; result = HashUtils.hashCode(result, this.formatPattern); return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/StandardXYToolTipGenerator.java000066400000000000000000000150561463604235500323750ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------- * StandardXYToolTipGenerator.java * ------------------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import java.io.Serializable; import java.text.DateFormat; import java.text.NumberFormat; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.xy.XYDataset; /** * A standard tool tip generator for use with an * {@link org.jfree.chart.renderer.xy.XYItemRenderer}. */ public class StandardXYToolTipGenerator extends AbstractXYItemLabelGenerator implements XYToolTipGenerator, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -3564164459039540784L; /** The default tooltip format. */ public static final String DEFAULT_TOOL_TIP_FORMAT = "{0}: ({1}, {2})"; /** * Returns a tool tip generator that formats the x-values as dates and the * y-values as numbers. * * @return A tool tip generator (never {@code null}). */ public static StandardXYToolTipGenerator getTimeSeriesInstance() { return new StandardXYToolTipGenerator(DEFAULT_TOOL_TIP_FORMAT, DateFormat.getInstance(), NumberFormat.getInstance()); } /** * Creates a tool tip generator using default number formatters. */ public StandardXYToolTipGenerator() { this(DEFAULT_TOOL_TIP_FORMAT, NumberFormat.getNumberInstance(), NumberFormat.getNumberInstance()); } /** * Creates a tool tip generator using the specified number formatters. * * @param formatString the item label format string ({@code null} not * permitted). * @param xFormat the format object for the x values ({@code null} * not permitted). * @param yFormat the format object for the y values ({@code null} * not permitted). */ public StandardXYToolTipGenerator(String formatString, NumberFormat xFormat, NumberFormat yFormat) { super(formatString, xFormat, yFormat); } /** * Creates a tool tip generator using the specified number formatters. * * @param formatString the label format string ({@code null} not * permitted). * @param xFormat the format object for the x values ({@code null} * not permitted). * @param yFormat the format object for the y values ({@code null} * not permitted). */ public StandardXYToolTipGenerator(String formatString, DateFormat xFormat, NumberFormat yFormat) { super(formatString, xFormat, yFormat); } /** * Creates a tool tip generator using the specified formatters (a * number formatter for the x-values and a date formatter for the * y-values). * * @param formatString the item label format string ({@code null} * not permitted). * @param xFormat the format object for the x values ({@code null} * permitted). * @param yFormat the format object for the y values ({@code null} * not permitted). */ public StandardXYToolTipGenerator(String formatString, NumberFormat xFormat, DateFormat yFormat) { super(formatString, xFormat, yFormat); } /** * Creates a tool tip generator using the specified date formatters. * * @param formatString the label format string ({@code null} not * permitted). * @param xFormat the format object for the x values ({@code null} * not permitted). * @param yFormat the format object for the y values ({@code null} * not permitted). */ public StandardXYToolTipGenerator(String formatString, DateFormat xFormat, DateFormat yFormat) { super(formatString, xFormat, yFormat); } /** * Generates the tool tip text for an item in a dataset. * * @param dataset the dataset ({@code null} not permitted). * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The tooltip text (possibly {@code null}). */ @Override public String generateToolTip(XYDataset dataset, int series, int item) { return generateLabelString(dataset, series, item); } /** * Tests this object for equality with an arbitrary object. * * @param obj the other object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof StandardXYToolTipGenerator)) { return false; } return super.equals(obj); } /** * Returns an independent copy of the generator. * * @return A clone. * * @throws CloneNotSupportedException if cloning is not supported. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/StandardXYZToolTipGenerator.java000066400000000000000000000202531463604235500325220ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------------- * StandardXYZToolTipGenerator.java * -------------------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import java.io.Serializable; import java.text.DateFormat; import java.text.MessageFormat; import java.text.NumberFormat; import java.util.Objects; import org.jfree.chart.util.Args; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYZDataset; /** * A standard item label generator for use with {@link XYZDataset} data. Each * value can be formatted as a number or as a date. */ public class StandardXYZToolTipGenerator extends StandardXYToolTipGenerator implements XYZToolTipGenerator, Serializable { /** For serialization. */ private static final long serialVersionUID = -2961577421889473503L; /** The default tooltip format. */ public static final String DEFAULT_TOOL_TIP_FORMAT = "{0}: ({1}, {2}, {3})"; /** * A number formatter for the z value - if this is null, then zDateFormat * must be non-null. */ private NumberFormat zFormat; /** * A date formatter for the z-value - if this is null, then zFormat must be * non-null. */ private DateFormat zDateFormat; /** * Creates a new tool tip generator using default number formatters for the * x, y and z-values. */ public StandardXYZToolTipGenerator() { this( DEFAULT_TOOL_TIP_FORMAT, NumberFormat.getNumberInstance(), NumberFormat.getNumberInstance(), NumberFormat.getNumberInstance() ); } /** * Constructs a new tool tip generator using the specified number * formatters. * * @param formatString the format string. * @param xFormat the format object for the x values ({@code null} * not permitted). * @param yFormat the format object for the y values ({@code null} * not permitted). * @param zFormat the format object for the z values ({@code null} * not permitted). */ public StandardXYZToolTipGenerator(String formatString, NumberFormat xFormat, NumberFormat yFormat, NumberFormat zFormat) { super(formatString, xFormat, yFormat); Args.nullNotPermitted(zFormat, "zFormat"); this.zFormat = zFormat; } /** * Constructs a new tool tip generator using the specified date formatters. * * @param formatString the format string. * @param xFormat the format object for the x values ({@code null} * not permitted). * @param yFormat the format object for the y values ({@code null} * not permitted). * @param zFormat the format object for the z values ({@code null} * not permitted). */ public StandardXYZToolTipGenerator(String formatString, DateFormat xFormat, DateFormat yFormat, DateFormat zFormat) { super(formatString, xFormat, yFormat); Args.nullNotPermitted(zFormat, "zFormat"); this.zDateFormat = zFormat; } // TODO: add constructors for combinations of number and date formatters. /** * Returns the number formatter for the z-values. * * @return The number formatter (possibly {@code null}). */ public NumberFormat getZFormat() { return this.zFormat; } /** * Returns the date formatter for the z-values. * * @return The date formatter (possibly {@code null}). */ public DateFormat getZDateFormat() { return this.zDateFormat; } /** * Generates a tool tip text item for a particular item within a series. * * @param dataset the dataset ({@code null} not permitted). * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The tooltip text (possibly {@code null}). */ @Override public String generateToolTip(XYZDataset dataset, int series, int item) { return generateLabelString(dataset, series, item); } /** * Generates a label string for an item in the dataset. * * @param dataset the dataset ({@code null} not permitted). * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The label (possibly {@code null}). */ @Override public String generateLabelString(XYDataset dataset, int series, int item) { String result; Object[] items = createItemArray((XYZDataset) dataset, series, item); result = MessageFormat.format(getFormatString(), items); return result; } /** * Creates the array of items that can be passed to the * {@link MessageFormat} class for creating labels. * * @param dataset the dataset ({@code null} not permitted). * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The items (never {@code null}). */ protected Object[] createItemArray(XYZDataset dataset, int series, int item) { Object[] result = new Object[4]; result[0] = dataset.getSeriesKey(series).toString(); Number x = dataset.getX(series, item); DateFormat xf = getXDateFormat(); if (xf != null) { result[1] = xf.format(x); } else { result[1] = getXFormat().format(x); } Number y = dataset.getY(series, item); DateFormat yf = getYDateFormat(); if (yf != null) { result[2] = yf.format(y); } else { result[2] = getYFormat().format(y); } Number z = dataset.getZ(series, item); if (this.zDateFormat != null) { result[3] = this.zDateFormat.format(z); } else { result[3] = this.zFormat.format(z); } return result; } /** * Tests this object for equality with an arbitrary object. * * @param obj the other object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof StandardXYZToolTipGenerator)) { return false; } if (!super.equals(obj)) { return false; } StandardXYZToolTipGenerator that = (StandardXYZToolTipGenerator) obj; if (!Objects.equals(this.zFormat, that.zFormat)) { return false; } if (!Objects.equals(this.zDateFormat, that.zDateFormat)) { return false; } return true; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/SymbolicXYItemLabelGenerator.java000066400000000000000000000124131463604235500326540ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------------- * SymbolicXYItemLabelGenerator.java * --------------------------------- * (C) Copyright 2001-present, by Anthony Boulestreau and Contributors. * * Original Author: Anthony Boulestreau; * Contributor(s): David Gilbert; * */ package org.jfree.chart.labels; import java.io.Serializable; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.time.RegularTimePeriod; import org.jfree.data.time.TimeSeriesCollection; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XisSymbolic; import org.jfree.data.xy.YisSymbolic; /** * A standard item label generator for plots that use data from an * {@link XYDataset}. */ public class SymbolicXYItemLabelGenerator implements XYItemLabelGenerator, XYToolTipGenerator, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 3963400354475494395L; /** * Generates a tool tip text item for a particular item within a series. * * @param data the dataset. * @param series the series number (zero-based index). * @param item the item number (zero-based index). * * @return The tool tip text (possibly {@code null}). */ @Override public String generateToolTip(XYDataset data, int series, int item) { String xStr, yStr; if (data instanceof YisSymbolic) { yStr = ((YisSymbolic) data).getYSymbolicValue(series, item); } else { double y = data.getYValue(series, item); yStr = Double.toString(round(y, 2)); } if (data instanceof XisSymbolic) { xStr = ((XisSymbolic) data).getXSymbolicValue(series, item); } else if (data instanceof TimeSeriesCollection) { RegularTimePeriod p = ((TimeSeriesCollection) data).getSeries(series) .getTimePeriod(item); xStr = p.toString(); } else { double x = data.getXValue(series, item); xStr = Double.toString(round(x, 2)); } return "X: " + xStr + ", Y: " + yStr; } /** * Generates a label for the specified item. The label is typically a * formatted version of the data value, but any text can be used. * * @param dataset the dataset ({@code null} not permitted). * @param series the series index (zero-based). * @param category the category index (zero-based). * * @return The label (possibly {@code null}). */ @Override public String generateLabel(XYDataset dataset, int series, int category) { return null; //TODO: implement this method properly } /** * Round a double value. * * @param value the value. * @param nb the exponent. * * @return The rounded value. */ private static double round(double value, int nb) { if (nb <= 0) { return Math.floor(value + 0.5d); } double p = Math.pow(10, nb); double tempval = Math.floor(value * p + 0.5d); return tempval / p; } /** * Returns an independent copy of the generator. * * @return A clone. * * @throws CloneNotSupportedException if cloning is not supported. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Tests if this object is equal to another. * * @param obj the other object. * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj instanceof SymbolicXYItemLabelGenerator) { return true; } return false; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = 127; return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/XYItemLabelGenerator.java000066400000000000000000000041501463604235500311510ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * XYItemLabelGenerator.java * ------------------------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import org.jfree.data.xy.XYDataset; /** * Interface for a label generator for plots that use data from an * {@link XYDataset}. */ public interface XYItemLabelGenerator { /** * Generates a label for the specified item. The label is typically a * formatted version of the data value, but any text can be used. * * @param dataset the dataset ({@code null} not permitted). * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The label (possibly {@code null}). */ String generateLabel(XYDataset dataset, int series, int item); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/XYSeriesLabelGenerator.java000066400000000000000000000043641463604235500315140ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * XYSeriesLabelGenerator.java * --------------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import org.jfree.data.xy.XYDataset; /** * A generator that creates labels for the series in an {@link XYDataset}. *

* Classes that implement this interface should be either (a) immutable, or * (b) cloneable via the {@code PublicCloneable} interface (defined in * the JCommon class library). This provides a mechanism for the referring * renderer to clone the generator if necessary. */ public interface XYSeriesLabelGenerator { /** * Generates a label for the specified series. This label will be * used for the chart legend. * * @param dataset the dataset ({@code null} not permitted). * @param series the series. * * @return A series label. */ String generateLabel(XYDataset dataset, int series); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/XYToolTipGenerator.java000066400000000000000000000040241463604235500307050ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * XYToolTipGenerator.java * ----------------------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import org.jfree.data.xy.XYDataset; /** * Interface for a tooltip generator for plots that use data from an * {@link XYDataset}. */ public interface XYToolTipGenerator { /** * Generates the tooltip text for the specified item. * * @param dataset the dataset ({@code null} not permitted). * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The tooltip text (possibly {@code null}). */ String generateToolTip(XYDataset dataset, int series, int item); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/XYZToolTipGenerator.java000066400000000000000000000041111463604235500310340ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * XYZToolTipGenerator.java * ------------------------ * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import org.jfree.data.xy.XYZDataset; /** * Interface for a tooltip generator for plots that use data from an * {@link XYZDataset}. */ public interface XYZToolTipGenerator extends XYToolTipGenerator { /** * Generates a tool tip text item for a particular item within a series. * * @param dataset the dataset ({@code null} not permitted). * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The tooltip text (possibly {@code null}). */ String generateToolTip(XYZDataset dataset, int series, int item); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/labels/package.html000066400000000000000000000002731463604235500266030ustar00rootroot00000000000000 Generators and other classes used for the display of item labels and tooltips. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/needle/000077500000000000000000000000001463604235500243125ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/needle/ArrowNeedle.java000066400000000000000000000122651463604235500273720ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * ArrowNeedle.java * ---------------- * (C) Copyright 2002-present, by the Australian Antarctic Division and * Contributors. * * Original Author: Bryan Scott (for the Australian Antarctic Division); * Contributor(s): David Gilbert; * */ package org.jfree.chart.needle; import java.awt.Graphics2D; import java.awt.Shape; import java.awt.geom.GeneralPath; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import org.jfree.chart.HashUtils; /** * A needle in the shape of an arrow. */ public class ArrowNeedle extends MeterNeedle implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -5334056511213782357L; /** * A flag controlling whether or not there is an arrow at the top of the * needle. */ private boolean isArrowAtTop = true; /** * Constructs a new arrow needle. * * @param isArrowAtTop a flag that controls whether or not there is an * arrow at the top of the needle. */ public ArrowNeedle(boolean isArrowAtTop) { this.isArrowAtTop = isArrowAtTop; } /** * Draws the needle. * * @param g2 the graphics device. * @param plotArea the plot area. * @param rotate the rotation point. * @param angle the angle. */ @Override protected void drawNeedle(Graphics2D g2, Rectangle2D plotArea, Point2D rotate, double angle) { Line2D shape = new Line2D.Float(); Shape d; float x = (float) (plotArea.getMinX() + (plotArea.getWidth() / 2)); float minY = (float) plotArea.getMinY(); float maxY = (float) plotArea.getMaxY(); shape.setLine(x, minY, x, maxY); GeneralPath shape1 = new GeneralPath(); if (this.isArrowAtTop) { shape1.moveTo(x, minY); minY += 4 * getSize(); } else { shape1.moveTo(x, maxY); minY = maxY - 4 * getSize(); } shape1.lineTo(x + getSize(), minY); shape1.lineTo(x - getSize(), minY); shape1.closePath(); if ((rotate != null) && (angle != 0)) { getTransform().setToRotation(angle, rotate.getX(), rotate.getY()); d = getTransform().createTransformedShape(shape); } else { d = shape; } defaultDisplay(g2, d); if ((rotate != null) && (angle != 0)) { d = getTransform().createTransformedShape(shape1); } else { d = shape1; } defaultDisplay(g2, d); } /** * Tests another object for equality with this object. * * @param obj the object to test ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof ArrowNeedle)) { return false; } if (!super.equals(obj)) { return false; } ArrowNeedle that = (ArrowNeedle) obj; if (this.isArrowAtTop != that.isArrowAtTop) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = super.hashCode(); result = HashUtils.hashCode(result, this.isArrowAtTop); return result; } /** * Returns a clone of this needle. * * @return A clone. * * @throws CloneNotSupportedException if the {@code ArrowNeedle} * cannot be cloned (in theory, this should not happen). */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/needle/LineNeedle.java000066400000000000000000000073471463604235500271740ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * LineNeedle.java * --------------- * (C) Copyright 2002-present, by the Australian Antarctic Division and * Contributors. * * Original Author: Bryan Scott (for the Australian Antarctic Division); * Contributor(s): David Gilbert; * */ package org.jfree.chart.needle; import java.awt.Graphics2D; import java.awt.Shape; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; /** * A needle that is represented by a line. */ public class LineNeedle extends MeterNeedle implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 6215321387896748945L; /** * Draws the needle. * * @param g2 the graphics device. * @param plotArea the plot area. * @param rotate the rotation point. * @param angle the angle. */ @Override protected void drawNeedle(Graphics2D g2, Rectangle2D plotArea, Point2D rotate, double angle) { Line2D shape = new Line2D.Double(); double x = plotArea.getMinX() + (plotArea.getWidth() / 2); shape.setLine(x, plotArea.getMinY(), x, plotArea.getMaxY()); Shape s = shape; if ((rotate != null) && (angle != 0)) { /// we have rotation getTransform().setToRotation(angle, rotate.getX(), rotate.getY()); s = getTransform().createTransformedShape(s); } defaultDisplay(g2, s); } /** * Tests another object for equality with this object. * * @param obj the object to test ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof LineNeedle)) { return false; } return super.equals(obj); } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { return super.hashCode(); } /** * Returns a clone of this needle. * * @return A clone. * * @throws CloneNotSupportedException if the {@code LineNeedle} * cannot be cloned (in theory, this should not happen). */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/needle/LongNeedle.java000066400000000000000000000127711463604235500272010ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * LongNeedle.java * --------------- * (C) Copyright 2002-present, by the Australian Antarctic Division and * Contributors. * * Original Author: Bryan Scott (for the Australian Antarctic Division); * Contributor(s): David Gilbert; * */ package org.jfree.chart.needle; import java.awt.Graphics2D; import java.awt.Shape; import java.awt.geom.GeneralPath; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; /** * A needle that is represented by a long line. */ public class LongNeedle extends MeterNeedle implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -4319985779783688159L; /** * Default constructor. */ public LongNeedle() { super(); setRotateY(0.8); } /** * Draws the needle. * * @param g2 the graphics device. * @param plotArea the plot area. * @param rotate the rotation point. * @param angle the angle. */ @Override protected void drawNeedle(Graphics2D g2, Rectangle2D plotArea, Point2D rotate, double angle) { GeneralPath shape1 = new GeneralPath(); GeneralPath shape2 = new GeneralPath(); GeneralPath shape3 = new GeneralPath(); float minX = (float) plotArea.getMinX(); float minY = (float) plotArea.getMinY(); float maxX = (float) plotArea.getMaxX(); float maxY = (float) plotArea.getMaxY(); //float midX = (float) (minX + (plotArea.getWidth() * getRotateX())); //float midY = (float) (minY + (plotArea.getHeight() * getRotateY())); float midX = (float) (minX + (plotArea.getWidth() * 0.5)); float midY = (float) (minY + (plotArea.getHeight() * 0.8)); float y = maxY - (2 * (maxY - midY)); if (y < minY) { y = minY; } shape1.moveTo(minX, midY); shape1.lineTo(midX, minY); shape1.lineTo(midX, y); shape1.closePath(); shape2.moveTo(maxX, midY); shape2.lineTo(midX, minY); shape2.lineTo(midX, y); shape2.closePath(); shape3.moveTo(minX, midY); shape3.lineTo(midX, maxY); shape3.lineTo(maxX, midY); shape3.lineTo(midX, y); shape3.closePath(); Shape s1 = shape1; Shape s2 = shape2; Shape s3 = shape3; if ((rotate != null) && (angle != 0)) { /// we have rotation huston, please spin me getTransform().setToRotation(angle, rotate.getX(), rotate.getY()); s1 = shape1.createTransformedShape(transform); s2 = shape2.createTransformedShape(transform); s3 = shape3.createTransformedShape(transform); } if (getHighlightPaint() != null) { g2.setPaint(getHighlightPaint()); g2.fill(s3); } if (getFillPaint() != null) { g2.setPaint(getFillPaint()); g2.fill(s1); g2.fill(s2); } if (getOutlinePaint() != null) { g2.setStroke(getOutlineStroke()); g2.setPaint(getOutlinePaint()); g2.draw(s1); g2.draw(s2); g2.draw(s3); } } /** * Tests another object for equality with this object. * * @param obj the object to test ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof LongNeedle)) { return false; } return super.equals(obj); } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { return super.hashCode(); } /** * Returns a clone of this needle. * * @return A clone. * * @throws CloneNotSupportedException if the {@code LongNeedle} * cannot be cloned (in theory, this should not happen). */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } }jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/needle/MeterNeedle.java000066400000000000000000000265751463604235500273650ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * MeterNeedle.java * ---------------- * (C) Copyright 2002-present, by the Australian Antarctic Division and * Contributors. * * Original Author: Bryan Scott (for the Australian Antarctic Division); * Contributor(s): David Gilbert; * Nicolas Brodu (for Astrium and EADS Corporate Research * Center); */ package org.jfree.chart.needle; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.HashUtils; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.SerialUtils; /** * The base class used to represent the needle on a * {@link org.jfree.chart.plot.CompassPlot}. */ public abstract class MeterNeedle implements Serializable { /** For serialization. */ private static final long serialVersionUID = 5203064851510951052L; /** The outline paint. */ private transient Paint outlinePaint = Color.BLACK; /** The outline stroke. */ private transient Stroke outlineStroke = new BasicStroke(2); /** The fill paint. */ private transient Paint fillPaint = null; /** The highlight paint. */ private transient Paint highlightPaint = null; /** The size. */ private int size = 5; /** Scalar to apply to locate the rotation x point. */ private double rotateX = 0.5; /** Scalar to apply to locate the rotation y point. */ private double rotateY = 0.5; /** A transform. */ protected static AffineTransform transform = new AffineTransform(); /** * Creates a new needle. */ public MeterNeedle() { this(null, null, null); } /** * Creates a new needle. * * @param outline the outline paint ({@code null} permitted). * @param fill the fill paint ({@code null} permitted). * @param highlight the highlight paint ({@code null} permitted). */ public MeterNeedle(Paint outline, Paint fill, Paint highlight) { this.fillPaint = fill; this.highlightPaint = highlight; this.outlinePaint = outline; } /** * Returns the outline paint. * * @return The outline paint. */ public Paint getOutlinePaint() { return this.outlinePaint; } /** * Sets the outline paint. * * @param p the new paint. */ public void setOutlinePaint(Paint p) { if (p != null) { this.outlinePaint = p; } } /** * Returns the outline stroke. * * @return The outline stroke. */ public Stroke getOutlineStroke() { return this.outlineStroke; } /** * Sets the outline stroke. * * @param s the new stroke. */ public void setOutlineStroke(Stroke s) { if (s != null) { this.outlineStroke = s; } } /** * Returns the fill paint. * * @return The fill paint. */ public Paint getFillPaint() { return this.fillPaint; } /** * Sets the fill paint. * * @param p the fill paint. */ public void setFillPaint(Paint p) { if (p != null) { this.fillPaint = p; } } /** * Returns the highlight paint. * * @return The highlight paint. */ public Paint getHighlightPaint() { return this.highlightPaint; } /** * Sets the highlight paint. * * @param p the highlight paint. */ public void setHighlightPaint(Paint p) { if (p != null) { this.highlightPaint = p; } } /** * Returns the scalar used for determining the rotation x value. * * @return The x rotate scalar. */ public double getRotateX() { return this.rotateX; } /** * Sets the rotateX value. * * @param x the new value. */ public void setRotateX(double x) { this.rotateX = x; } /** * Sets the rotateY value. * * @param y the new value. */ public void setRotateY(double y) { this.rotateY = y; } /** * Returns the scalar used for determining the rotation y value. * * @return The y rotate scalar. */ public double getRotateY() { return this.rotateY; } /** * Draws the needle. * * @param g2 the graphics device. * @param plotArea the plot area. */ public void draw(Graphics2D g2, Rectangle2D plotArea) { draw(g2, plotArea, 0); } /** * Draws the needle. * * @param g2 the graphics device. * @param plotArea the plot area. * @param angle the angle. */ public void draw(Graphics2D g2, Rectangle2D plotArea, double angle) { Point2D.Double pt = new Point2D.Double(); pt.setLocation(plotArea.getMinX() + this.rotateX * plotArea.getWidth(), plotArea.getMinY() + this.rotateY * plotArea.getHeight()); draw(g2, plotArea, pt, angle); } /** * Draws the needle. * * @param g2 the graphics device. * @param plotArea the plot area. * @param rotate the rotation point. * @param angle the angle. */ public void draw(Graphics2D g2, Rectangle2D plotArea, Point2D rotate, double angle) { Paint savePaint = g2.getColor(); Stroke saveStroke = g2.getStroke(); drawNeedle(g2, plotArea, rotate, Math.toRadians(angle)); g2.setStroke(saveStroke); g2.setPaint(savePaint); } /** * Draws the needle. * * @param g2 the graphics device. * @param plotArea the plot area. * @param rotate the rotation point. * @param angle the angle. */ protected abstract void drawNeedle(Graphics2D g2, Rectangle2D plotArea, Point2D rotate, double angle); /** * Displays a shape. * * @param g2 the graphics device. * @param shape the shape. */ protected void defaultDisplay(Graphics2D g2, Shape shape) { if (this.fillPaint != null) { g2.setPaint(this.fillPaint); g2.fill(shape); } if (this.outlinePaint != null) { g2.setStroke(this.outlineStroke); g2.setPaint(this.outlinePaint); g2.draw(shape); } } /** * Returns the size. * * @return The size. */ public int getSize() { return this.size; } /** * Sets the size. * * @param pixels the new size. */ public void setSize(int pixels) { this.size = pixels; } /** * Returns the transform. * * @return The transform. */ public AffineTransform getTransform() { return MeterNeedle.transform; } /** * Tests another object for equality with this object. * * @param obj the object to test ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof MeterNeedle)) { return false; } MeterNeedle that = (MeterNeedle) obj; if (!PaintUtils.equal(this.outlinePaint, that.outlinePaint)) { return false; } if (!Objects.equals(this.outlineStroke, that.outlineStroke)) { return false; } if (!PaintUtils.equal(this.fillPaint, that.fillPaint)) { return false; } if (!PaintUtils.equal(this.highlightPaint, that.highlightPaint)) { return false; } if (this.size != that.size) { return false; } if (this.rotateX != that.rotateX) { return false; } if (this.rotateY != that.rotateY) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = HashUtils.hashCode(193, this.fillPaint); result = HashUtils.hashCode(result, this.highlightPaint); result = HashUtils.hashCode(result, this.outlinePaint); result = HashUtils.hashCode(result, this.outlineStroke); result = HashUtils.hashCode(result, this.rotateX); result = HashUtils.hashCode(result, this.rotateY); result = HashUtils.hashCode(result, this.size); return result; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeStroke(this.outlineStroke, stream); SerialUtils.writePaint(this.outlinePaint, stream); SerialUtils.writePaint(this.fillPaint, stream); SerialUtils.writePaint(this.highlightPaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.outlineStroke = SerialUtils.readStroke(stream); this.outlinePaint = SerialUtils.readPaint(stream); this.fillPaint = SerialUtils.readPaint(stream); this.highlightPaint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/needle/MiddlePinNeedle.java000066400000000000000000000111221463604235500301340ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * MiddlePinNeedle.java * -------------------- * (C) Copyright 2002-present, by the Australian Antarctic Division and * Contributors. * * Original Author: Bryan Scott (for the Australian Antarctic Division); * Contributor(s): David Gilbert; * */ package org.jfree.chart.needle; import java.awt.Graphics2D; import java.awt.geom.Area; import java.awt.geom.Ellipse2D; import java.awt.geom.GeneralPath; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; /** * A needle that is drawn as a pin shape. */ public class MiddlePinNeedle extends MeterNeedle implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 6237073996403125310L; /** * Draws the needle. * * @param g2 the graphics device. * @param plotArea the plot area. * @param rotate the rotation point. * @param angle the angle. */ @Override protected void drawNeedle(Graphics2D g2, Rectangle2D plotArea, Point2D rotate, double angle) { Area shape; GeneralPath pointer = new GeneralPath(); int minY = (int) (plotArea.getMinY()); //int maxX = (int) (plotArea.getMaxX()); int maxY = (int) (plotArea.getMaxY()); int midY = ((maxY - minY) / 2) + minY; int midX = (int) (plotArea.getMinX() + (plotArea.getWidth() / 2)); //int midY = (int) (plotArea.getMinY() + (plotArea.getHeight() / 2)); int lenX = (int) (plotArea.getWidth() / 10); if (lenX < 2) { lenX = 2; } pointer.moveTo(midX - lenX, midY - lenX); pointer.lineTo(midX + lenX, midY - lenX); pointer.lineTo(midX, minY); pointer.closePath(); lenX = 4 * lenX; Ellipse2D circle = new Ellipse2D.Double(midX - lenX / 2.0, midY - lenX, lenX, lenX); shape = new Area(circle); shape.add(new Area(pointer)); if ((rotate != null) && (angle != 0)) { /// we have rotation getTransform().setToRotation(angle, rotate.getX(), rotate.getY()); shape.transform(getTransform()); } defaultDisplay(g2, shape); } /** * Tests another object for equality with this object. * * @param object the object to test. * * @return A boolean. */ @Override public boolean equals(Object object) { if (object == null) { return false; } if (object == this) { return true; } if (super.equals(object) && object instanceof MiddlePinNeedle) { return true; } return false; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { return super.hashCode(); } /** * Returns a clone of this needle. * * @return A clone. * * @throws CloneNotSupportedException if the {@code MiddlePinNeedle} * cannot be cloned (in theory, this should not happen). */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/needle/PinNeedle.java000066400000000000000000000107441463604235500270260ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * PinNeedle.java * -------------- * (C) Copyright 2002-present, by the Australian Antarctic Division and * Contributors. * * Original Author: Bryan Scott (for the Australian Antarctic Division); * Contributor(s): David Gilbert; * */ package org.jfree.chart.needle; import java.awt.Graphics2D; import java.awt.geom.Area; import java.awt.geom.Ellipse2D; import java.awt.geom.GeneralPath; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; /** * A needle that is drawn as a pin shape. */ public class PinNeedle extends MeterNeedle implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -3787089953079863373L; /** * Draws the needle. * * @param g2 the graphics device. * @param plotArea the plot area. * @param rotate the rotation point. * @param angle the angle. */ @Override protected void drawNeedle(Graphics2D g2, Rectangle2D plotArea, Point2D rotate, double angle) { Area shape; GeneralPath pointer = new GeneralPath(); int minY = (int) (plotArea.getMinY()); //int maxX = (int) (plotArea.getMaxX()); int maxY = (int) (plotArea.getMaxY()); int midX = (int) (plotArea.getMinX() + (plotArea.getWidth() / 2)); //int midY = (int) (plotArea.getMinY() + (plotArea.getHeight() / 2)); int lenX = (int) (plotArea.getWidth() / 10); if (lenX < 2) { lenX = 2; } pointer.moveTo(midX - lenX, maxY - lenX); pointer.lineTo(midX + lenX, maxY - lenX); pointer.lineTo(midX, minY + lenX); pointer.closePath(); lenX = 4 * lenX; Ellipse2D circle = new Ellipse2D.Double(midX - lenX / 2.0, plotArea.getMaxY() - lenX, lenX, lenX); shape = new Area(circle); shape.add(new Area(pointer)); if ((rotate != null) && (angle != 0)) { /// we have rotation getTransform().setToRotation(angle, rotate.getX(), rotate.getY()); shape.transform(getTransform()); } defaultDisplay(g2, shape); } /** * Tests another object for equality with this object. * * @param obj the object to test ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof PinNeedle)) { return false; } if (!super.equals(obj)) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { return super.hashCode(); } /** * Returns a clone of this needle. * * @return A clone. * * @throws CloneNotSupportedException if the {@code PinNeedle} * cannot be cloned (in theory, this should not happen). */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/needle/PlumNeedle.java000066400000000000000000000102151463604235500272060ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * PlumNeedle.java * --------------- * (C) Copyright 2002-present, by the Australian Antarctic Division and * Contributors. * * Original Author: Bryan Scott (for the Australian Antarctic Division); * Contributor(s): David Gilbert; * */ package org.jfree.chart.needle; import java.awt.Graphics2D; import java.awt.geom.Arc2D; import java.awt.geom.Area; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; /** * A needle for use with the {@link org.jfree.chart.plot.CompassPlot} class. */ public class PlumNeedle extends MeterNeedle implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -3082660488660600718L; /** * Draws the needle. * * @param g2 the graphics device. * @param plotArea the plot area. * @param rotate the rotation point. * @param angle the angle. */ @Override protected void drawNeedle(Graphics2D g2, Rectangle2D plotArea, Point2D rotate, double angle) { Arc2D shape = new Arc2D.Double(Arc2D.PIE); double radius = plotArea.getHeight(); double halfX = plotArea.getWidth() / 2; double diameter = 2 * radius; shape.setFrame(plotArea.getMinX() + halfX - radius, plotArea.getMinY() - radius, diameter, diameter); radius = Math.toDegrees(Math.asin(halfX / radius)); shape.setAngleStart(270 - radius); shape.setAngleExtent(2 * radius); Area s = new Area(shape); if ((rotate != null) && (angle != 0)) { /// we have rotation houston, please spin me getTransform().setToRotation(angle, rotate.getX(), rotate.getY()); s.transform(getTransform()); } defaultDisplay(g2, s); } /** * Tests another object for equality with this object. * * @param obj the object to test ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof PlumNeedle)) { return false; } if (!super.equals(obj)) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { return super.hashCode(); } /** * Returns a clone of this needle. * * @return A clone. * * @throws CloneNotSupportedException if the {@code PlumNeedle} * cannot be cloned (in theory, this should not happen). */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/needle/PointerNeedle.java000066400000000000000000000115041463604235500277130ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * PointerNeedle.java * ------------------ * (C) Copyright 2002-present, by the Australian Antarctic Division and * Contributors. * * Original Author: Bryan Scott (for the Australian Antarctic Division); * Contributor(s): David Gilbert; * */ package org.jfree.chart.needle; import java.awt.Graphics2D; import java.awt.geom.GeneralPath; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; /** * A needle in the shape of a pointer, for use with the * {@link org.jfree.chart.plot.CompassPlot} class. */ public class PointerNeedle extends MeterNeedle implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -4744677345334729606L; /** * Draws the needle. * * @param g2 the graphics device. * @param plotArea the plot area. * @param rotate the rotation point. * @param angle the angle. */ @Override protected void drawNeedle(Graphics2D g2, Rectangle2D plotArea, Point2D rotate, double angle) { GeneralPath shape1 = new GeneralPath(); GeneralPath shape2 = new GeneralPath(); float minX = (float) plotArea.getMinX(); float minY = (float) plotArea.getMinY(); float maxX = (float) plotArea.getMaxX(); float maxY = (float) plotArea.getMaxY(); float midX = (float) (minX + (plotArea.getWidth() / 2)); float midY = (float) (minY + (plotArea.getHeight() / 2)); shape1.moveTo(minX, midY); shape1.lineTo(midX, minY); shape1.lineTo(maxX, midY); shape1.closePath(); shape2.moveTo(minX, midY); shape2.lineTo(midX, maxY); shape2.lineTo(maxX, midY); shape2.closePath(); if ((rotate != null) && (angle != 0)) { /// we have rotation huston, please spin me getTransform().setToRotation(angle, rotate.getX(), rotate.getY()); shape1.transform(getTransform()); shape2.transform(getTransform()); } if (getFillPaint() != null) { g2.setPaint(getFillPaint()); g2.fill(shape1); } if (getHighlightPaint() != null) { g2.setPaint(getHighlightPaint()); g2.fill(shape2); } if (getOutlinePaint() != null) { g2.setStroke(getOutlineStroke()); g2.setPaint(getOutlinePaint()); g2.draw(shape1); g2.draw(shape2); } } /** * Tests another object for equality with this object. * * @param obj the object to test ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof PointerNeedle)) { return false; } if (!super.equals(obj)) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { return super.hashCode(); } /** * Returns a clone of this needle. * * @return A clone. * * @throws CloneNotSupportedException if the {@code PointerNeedle} * cannot be cloned (in theory, this should not happen). */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/needle/ShipNeedle.java000066400000000000000000000101741463604235500272000ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * ShipNeedle.java * --------------- * (C) Copyright 2002-present, by the Australian Antarctic Division and * Contributors. * * Original Author: Bryan Scott (for the Australian Antarctic Division); * Contributor(s): David Gilbert; * */ package org.jfree.chart.needle; import java.awt.Graphics2D; import java.awt.geom.Arc2D; import java.awt.geom.GeneralPath; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; /** * A needle in the shape of a ship, for use with the * {@link org.jfree.chart.plot.CompassPlot} class. */ public class ShipNeedle extends MeterNeedle implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 149554868169435612L; /** * Draws the needle. * * @param g2 the graphics device. * @param plotArea the plot area. * @param rotate the rotation point. * @param angle the angle. */ @Override protected void drawNeedle(Graphics2D g2, Rectangle2D plotArea, Point2D rotate, double angle) { GeneralPath shape = new GeneralPath(); shape.append(new Arc2D.Double(-9.0, -7.0, 10, 14, 0.0, 25.5, Arc2D.OPEN), true); shape.append(new Arc2D.Double(0.0, -7.0, 10, 14, 154.5, 25.5, Arc2D.OPEN), true); shape.closePath(); getTransform().setToTranslation(plotArea.getMinX(), plotArea.getMaxY()); getTransform().scale(plotArea.getWidth(), plotArea.getHeight() / 3); shape.transform(getTransform()); if ((rotate != null) && (angle != 0)) { /// we have rotation getTransform().setToRotation(angle, rotate.getX(), rotate.getY()); shape.transform(getTransform()); } defaultDisplay(g2, shape); } /** * Tests another object for equality with this object. * * @param object the object to test. * * @return A boolean. */ @Override public boolean equals(Object object) { if (object == null) { return false; } if (object == this) { return true; } if (super.equals(object) && object instanceof ShipNeedle) { return true; } return false; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { return super.hashCode(); } /** * Returns a clone of this needle. * * @return A clone. * * @throws CloneNotSupportedException if the {@code ShipNeedle} * cannot be cloned (in theory, this should not happen). */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/needle/WindNeedle.java000066400000000000000000000074621463604235500272040ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * WindNeedle.java * --------------- * (C) Copyright 2002-present, by the Australian Antarctic Division and * Contributors. * * Original Author: Bryan Scott (for the Australian Antarctic Division); * Contributor(s): David Gilbert; * */ package org.jfree.chart.needle; import java.awt.Graphics2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; /** * A needle that indicates wind direction, for use with the * {@link org.jfree.chart.plot.CompassPlot} class. */ public class WindNeedle extends ArrowNeedle implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -2861061368907167834L; /** * Default constructor. */ public WindNeedle() { super(false); // isArrowAtTop } /** * Draws the needle. * * @param g2 the graphics device. * @param plotArea the plot area. * @param rotate the rotation point. * @param angle the angle. */ @Override protected void drawNeedle(Graphics2D g2, Rectangle2D plotArea, Point2D rotate, double angle) { super.drawNeedle(g2, plotArea, rotate, angle); if ((rotate != null) && (plotArea != null)) { int spacing = getSize() * 3; Rectangle2D newArea = new Rectangle2D.Double(); Point2D newRotate = rotate; newArea.setRect(plotArea.getMinX() - spacing, plotArea.getMinY(), plotArea.getWidth(), plotArea.getHeight()); super.drawNeedle(g2, newArea, newRotate, angle); newArea.setRect(plotArea.getMinX() + spacing, plotArea.getMinY(), plotArea.getWidth(), plotArea.getHeight()); super.drawNeedle(g2, newArea, newRotate, angle); } } /** * Tests another object for equality with this object. * * @param object the object to test. * * @return A boolean. */ @Override public boolean equals(Object object) { if (object == null) { return false; } if (object == this) { return true; } if (super.equals(object) && object instanceof WindNeedle) { return true; } return false; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { return super.hashCode(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/needle/package.html000066400000000000000000000003301463604235500265670ustar00rootroot00000000000000 A range of objects that can be used to represent the needle on a {@link org.jfree.chart.plot.CompassPlot}. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/package.html000066400000000000000000000003201463604235500253320ustar00rootroot00000000000000 Core classes, including {@link org.jfree.chart.JFreeChart} and {@link org.jfree.chart.ChartPanel}. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/panel/000077500000000000000000000000001463604235500241555ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/panel/AbstractOverlay.java000066400000000000000000000073331463604235500301330ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * AbstractOverlay.java * -------------------- * (C) Copyright 2009-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.panel; import javax.swing.event.EventListenerList; import org.jfree.chart.ChartPanel; import org.jfree.chart.event.ChartChangeEvent; import org.jfree.chart.event.OverlayChangeEvent; import org.jfree.chart.event.OverlayChangeListener; import org.jfree.chart.util.Args; /** * A base class for implementing overlays for a {@link ChartPanel}. */ public class AbstractOverlay { /** Storage for registered change listeners. */ private final transient EventListenerList changeListeners; /** * Default constructor. */ public AbstractOverlay() { this.changeListeners = new EventListenerList(); } /** * Registers an object for notification of changes to the overlay. * * @param listener the listener ({@code null} not permitted). * * @see #removeChangeListener(OverlayChangeListener) */ public void addChangeListener(OverlayChangeListener listener) { Args.nullNotPermitted(listener, "listener"); this.changeListeners.add(OverlayChangeListener.class, listener); } /** * Deregisters an object for notification of changes to the overlay. * * @param listener the listener ({@code null} not permitted) * * @see #addChangeListener(OverlayChangeListener) */ public void removeChangeListener(OverlayChangeListener listener) { Args.nullNotPermitted(listener, "listener"); this.changeListeners.remove(OverlayChangeListener.class, listener); } /** * Sends a default {@link ChartChangeEvent} to all registered listeners. *

* This method is for convenience only. */ public void fireOverlayChanged() { OverlayChangeEvent event = new OverlayChangeEvent(this); notifyListeners(event); } /** * Sends a {@link ChartChangeEvent} to all registered listeners. * * @param event information about the event that triggered the * notification. */ protected void notifyListeners(OverlayChangeEvent event) { Object[] listeners = this.changeListeners.getListenerList(); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == OverlayChangeListener.class) { ((OverlayChangeListener) listeners[i + 1]).overlayChanged( event); } } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/panel/CrosshairOverlay.java000066400000000000000000000537251463604235500303330ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * CrosshairOverlay.java * --------------------- * (C) Copyright 2011-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): John Matthews, Michal Wozniak; * */ package org.jfree.chart.panel; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Rectangle; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import org.jfree.chart.ChartPanel; import org.jfree.chart.JFreeChart; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.OverlayChangeEvent; import org.jfree.chart.plot.Crosshair; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.text.TextUtils; import org.jfree.chart.ui.RectangleAnchor; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.TextAnchor; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; /** * An overlay for a {@link ChartPanel} that draws crosshairs on a chart. If * you are using the JavaFX extensions for JFreeChart, then you should use * the {@code CrosshairOverlayFX} class. */ public class CrosshairOverlay extends AbstractOverlay implements Overlay, PropertyChangeListener, PublicCloneable, Cloneable, Serializable { /** Storage for the crosshairs along the x-axis. */ private List xCrosshairs; /** Storage for the crosshairs along the y-axis. */ private List yCrosshairs; /** * Creates a new overlay that initially contains no crosshairs. */ public CrosshairOverlay() { super(); this.xCrosshairs = new ArrayList<>(); this.yCrosshairs = new ArrayList<>(); } /** * Adds a crosshair against the domain axis (x-axis) and sends an * {@link OverlayChangeEvent} to all registered listeners. * * @param crosshair the crosshair ({@code null} not permitted). * * @see #removeDomainCrosshair(org.jfree.chart.plot.Crosshair) * @see #addRangeCrosshair(org.jfree.chart.plot.Crosshair) */ public void addDomainCrosshair(Crosshair crosshair) { Args.nullNotPermitted(crosshair, "crosshair"); this.xCrosshairs.add(crosshair); crosshair.addPropertyChangeListener(this); fireOverlayChanged(); } /** * Removes a domain axis crosshair and sends an {@link OverlayChangeEvent} * to all registered listeners. * * @param crosshair the crosshair ({@code null} not permitted). * * @see #addDomainCrosshair(org.jfree.chart.plot.Crosshair) */ public void removeDomainCrosshair(Crosshair crosshair) { Args.nullNotPermitted(crosshair, "crosshair"); if (this.xCrosshairs.remove(crosshair)) { crosshair.removePropertyChangeListener(this); fireOverlayChanged(); } } /** * Clears all the domain crosshairs from the overlay and sends an * {@link OverlayChangeEvent} to all registered listeners (unless there * were no crosshairs to begin with). */ public void clearDomainCrosshairs() { if (this.xCrosshairs.isEmpty()) { return; // nothing to do - avoids firing change event } for (Crosshair c : getDomainCrosshairs()) { this.xCrosshairs.remove(c); c.removePropertyChangeListener(this); } fireOverlayChanged(); } /** * Returns a new list containing the domain crosshairs for this overlay. * * @return A list of crosshairs. */ public List getDomainCrosshairs() { return new ArrayList<>(this.xCrosshairs); } /** * Adds a crosshair against the range axis and sends an * {@link OverlayChangeEvent} to all registered listeners. * * @param crosshair the crosshair ({@code null} not permitted). */ public void addRangeCrosshair(Crosshair crosshair) { Args.nullNotPermitted(crosshair, "crosshair"); this.yCrosshairs.add(crosshair); crosshair.addPropertyChangeListener(this); fireOverlayChanged(); } /** * Removes a range axis crosshair and sends an {@link OverlayChangeEvent} * to all registered listeners. * * @param crosshair the crosshair ({@code null} not permitted). * * @see #addRangeCrosshair(org.jfree.chart.plot.Crosshair) */ public void removeRangeCrosshair(Crosshair crosshair) { Args.nullNotPermitted(crosshair, "crosshair"); if (this.yCrosshairs.remove(crosshair)) { crosshair.removePropertyChangeListener(this); fireOverlayChanged(); } } /** * Clears all the range crosshairs from the overlay and sends an * {@link OverlayChangeEvent} to all registered listeners (unless there * were no crosshairs to begin with). */ public void clearRangeCrosshairs() { if (this.yCrosshairs.isEmpty()) { return; // nothing to do - avoids change notification } for (Crosshair c : getRangeCrosshairs()) { this.yCrosshairs.remove(c); c.removePropertyChangeListener(this); } fireOverlayChanged(); } /** * Returns a new list containing the range crosshairs for this overlay. * * @return A list of crosshairs. */ public List getRangeCrosshairs() { return new ArrayList<>(this.yCrosshairs); } /** * Receives a property change event (typically a change in one of the * crosshairs). * * @param e the event. */ @Override public void propertyChange(PropertyChangeEvent e) { fireOverlayChanged(); } /** * Renders the crosshairs in the overlay on top of the chart that has just * been rendered in the specified {@code chartPanel}. This method is * called by the JFreeChart framework, you won't normally call it from * user code. * * @param g2 the graphics target. * @param chartPanel the chart panel. */ @Override public void paintOverlay(Graphics2D g2, ChartPanel chartPanel) { Shape savedClip = g2.getClip(); Rectangle2D dataArea = chartPanel.getScreenDataArea(); g2.clip(dataArea); JFreeChart chart = chartPanel.getChart(); XYPlot plot = (XYPlot) chart.getPlot(); ValueAxis xAxis = plot.getDomainAxis(); RectangleEdge xAxisEdge = plot.getDomainAxisEdge(); for (Crosshair ch : this.xCrosshairs) { if (ch.isVisible()) { double x = ch.getValue(); double xx = xAxis.valueToJava2D(x, dataArea, xAxisEdge); if (plot.getOrientation() == PlotOrientation.VERTICAL) { drawVerticalCrosshair(g2, dataArea, xx, ch); } else { drawHorizontalCrosshair(g2, dataArea, xx, ch); } } } ValueAxis yAxis = plot.getRangeAxis(); RectangleEdge yAxisEdge = plot.getRangeAxisEdge(); for (Crosshair ch : this.yCrosshairs) { if (ch.isVisible()) { double y = ch.getValue(); double yy = yAxis.valueToJava2D(y, dataArea, yAxisEdge); if (plot.getOrientation() == PlotOrientation.VERTICAL) { drawHorizontalCrosshair(g2, dataArea, yy, ch); } else { drawVerticalCrosshair(g2, dataArea, yy, ch); } } } g2.setClip(savedClip); } /** * Draws a crosshair horizontally across the plot. * * @param g2 the graphics target. * @param dataArea the data area. * @param y the y-value in Java2D space. * @param crosshair the crosshair. */ protected void drawHorizontalCrosshair(Graphics2D g2, Rectangle2D dataArea, double y, Crosshair crosshair) { if (y >= dataArea.getMinY() && y <= dataArea.getMaxY()) { Line2D line = new Line2D.Double(dataArea.getMinX(), y, dataArea.getMaxX(), y); Paint savedPaint = g2.getPaint(); Stroke savedStroke = g2.getStroke(); g2.setPaint(crosshair.getPaint()); g2.setStroke(crosshair.getStroke()); g2.draw(line); if (crosshair.isLabelVisible()) { String label = crosshair.getLabelGenerator().generateLabel( crosshair); if (label != null && !label.isEmpty()) { Font savedFont = g2.getFont(); g2.setFont(crosshair.getLabelFont()); RectangleAnchor anchor = crosshair.getLabelAnchor(); Point2D pt = calculateLabelPoint(line, anchor, crosshair.getLabelXOffset(), crosshair.getLabelYOffset()); float xx = (float) pt.getX(); float yy = (float) pt.getY(); TextAnchor alignPt = textAlignPtForLabelAnchorH(anchor); Shape hotspot = TextUtils.calculateRotatedStringBounds( label, g2, xx, yy, alignPt, 0.0, TextAnchor.CENTER); if (!dataArea.contains(hotspot.getBounds2D())) { anchor = flipAnchorV(anchor); pt = calculateLabelPoint(line, anchor, crosshair.getLabelXOffset(), crosshair.getLabelYOffset()); xx = (float) pt.getX(); yy = (float) pt.getY(); alignPt = textAlignPtForLabelAnchorH(anchor); hotspot = TextUtils.calculateRotatedStringBounds( label, g2, xx, yy, alignPt, 0.0, TextAnchor.CENTER); } g2.setPaint(crosshair.getLabelBackgroundPaint()); g2.fill(hotspot); if (crosshair.isLabelOutlineVisible()) { g2.setPaint(crosshair.getLabelOutlinePaint()); g2.setStroke(crosshair.getLabelOutlineStroke()); g2.draw(hotspot); } g2.setPaint(crosshair.getLabelPaint()); TextUtils.drawAlignedString(label, g2, xx, yy, alignPt); g2.setFont(savedFont); } } g2.setPaint(savedPaint); g2.setStroke(savedStroke); } } /** * Draws a crosshair vertically on the plot. * * @param g2 the graphics target. * @param dataArea the data area. * @param x the x-value in Java2D space. * @param crosshair the crosshair. */ protected void drawVerticalCrosshair(Graphics2D g2, Rectangle2D dataArea, double x, Crosshair crosshair) { if (x >= dataArea.getMinX() && x <= dataArea.getMaxX()) { Line2D line = new Line2D.Double(x, dataArea.getMinY(), x, dataArea.getMaxY()); Paint savedPaint = g2.getPaint(); Stroke savedStroke = g2.getStroke(); g2.setPaint(crosshair.getPaint()); g2.setStroke(crosshair.getStroke()); g2.draw(line); if (crosshair.isLabelVisible()) { String label = crosshair.getLabelGenerator().generateLabel( crosshair); if (label != null && !label.isEmpty()) { Font savedFont = g2.getFont(); g2.setFont(crosshair.getLabelFont()); RectangleAnchor anchor = crosshair.getLabelAnchor(); Point2D pt = calculateLabelPoint(line, anchor, crosshair.getLabelXOffset(), crosshair.getLabelYOffset()); float xx = (float) pt.getX(); float yy = (float) pt.getY(); TextAnchor alignPt = textAlignPtForLabelAnchorV(anchor); Shape hotspot = TextUtils.calculateRotatedStringBounds( label, g2, xx, yy, alignPt, 0.0, TextAnchor.CENTER); if (!dataArea.contains(hotspot.getBounds2D())) { anchor = flipAnchorH(anchor); pt = calculateLabelPoint(line, anchor, crosshair.getLabelXOffset(), crosshair.getLabelYOffset()); xx = (float) pt.getX(); yy = (float) pt.getY(); alignPt = textAlignPtForLabelAnchorV(anchor); hotspot = TextUtils.calculateRotatedStringBounds( label, g2, xx, yy, alignPt, 0.0, TextAnchor.CENTER); } g2.setPaint(crosshair.getLabelBackgroundPaint()); g2.fill(hotspot); if (crosshair.isLabelOutlineVisible()) { g2.setPaint(crosshair.getLabelOutlinePaint()); g2.setStroke(crosshair.getLabelOutlineStroke()); g2.draw(hotspot); } g2.setPaint(crosshair.getLabelPaint()); TextUtils.drawAlignedString(label, g2, xx, yy, alignPt); g2.setFont(savedFont); } } g2.setPaint(savedPaint); g2.setStroke(savedStroke); } } /** * Calculates the anchor point for a label. * * @param line the line for the crosshair. * @param anchor the anchor point. * @param deltaX the x-offset. * @param deltaY the y-offset. * * @return The anchor point. */ private Point2D calculateLabelPoint(Line2D line, RectangleAnchor anchor, double deltaX, double deltaY) { double x, y; boolean left = (anchor == RectangleAnchor.BOTTOM_LEFT || anchor == RectangleAnchor.LEFT || anchor == RectangleAnchor.TOP_LEFT); boolean right = (anchor == RectangleAnchor.BOTTOM_RIGHT || anchor == RectangleAnchor.RIGHT || anchor == RectangleAnchor.TOP_RIGHT); boolean top = (anchor == RectangleAnchor.TOP_LEFT || anchor == RectangleAnchor.TOP || anchor == RectangleAnchor.TOP_RIGHT); boolean bottom = (anchor == RectangleAnchor.BOTTOM_LEFT || anchor == RectangleAnchor.BOTTOM || anchor == RectangleAnchor.BOTTOM_RIGHT); Rectangle rect = line.getBounds(); // we expect the line to be vertical or horizontal if (line.getX1() == line.getX2()) { // vertical x = line.getX1(); y = (line.getY1() + line.getY2()) / 2.0; if (left) { x = x - deltaX; } if (right) { x = x + deltaX; } if (top) { y = Math.min(line.getY1(), line.getY2()) + deltaY; } if (bottom) { y = Math.max(line.getY1(), line.getY2()) - deltaY; } } else { // horizontal x = (line.getX1() + line.getX2()) / 2.0; y = line.getY1(); if (left) { x = Math.min(line.getX1(), line.getX2()) + deltaX; } if (right) { x = Math.max(line.getX1(), line.getX2()) - deltaX; } if (top) { y = y - deltaY; } if (bottom) { y = y + deltaY; } } return new Point2D.Double(x, y); } /** * Returns the text anchor that is used to align a label to its anchor * point. * * @param anchor the anchor. * * @return The text alignment point. */ private TextAnchor textAlignPtForLabelAnchorV(RectangleAnchor anchor) { TextAnchor result = TextAnchor.CENTER; if (anchor.equals(RectangleAnchor.TOP_LEFT)) { result = TextAnchor.TOP_RIGHT; } else if (anchor.equals(RectangleAnchor.TOP)) { result = TextAnchor.TOP_CENTER; } else if (anchor.equals(RectangleAnchor.TOP_RIGHT)) { result = TextAnchor.TOP_LEFT; } else if (anchor.equals(RectangleAnchor.LEFT)) { result = TextAnchor.HALF_ASCENT_RIGHT; } else if (anchor.equals(RectangleAnchor.RIGHT)) { result = TextAnchor.HALF_ASCENT_LEFT; } else if (anchor.equals(RectangleAnchor.BOTTOM_LEFT)) { result = TextAnchor.BOTTOM_RIGHT; } else if (anchor.equals(RectangleAnchor.BOTTOM)) { result = TextAnchor.BOTTOM_CENTER; } else if (anchor.equals(RectangleAnchor.BOTTOM_RIGHT)) { result = TextAnchor.BOTTOM_LEFT; } return result; } /** * Returns the text anchor that is used to align a label to its anchor * point. * * @param anchor the anchor. * * @return The text alignment point. */ private TextAnchor textAlignPtForLabelAnchorH(RectangleAnchor anchor) { TextAnchor result = TextAnchor.CENTER; if (anchor.equals(RectangleAnchor.TOP_LEFT)) { result = TextAnchor.BOTTOM_LEFT; } else if (anchor.equals(RectangleAnchor.TOP)) { result = TextAnchor.BOTTOM_CENTER; } else if (anchor.equals(RectangleAnchor.TOP_RIGHT)) { result = TextAnchor.BOTTOM_RIGHT; } else if (anchor.equals(RectangleAnchor.LEFT)) { result = TextAnchor.HALF_ASCENT_LEFT; } else if (anchor.equals(RectangleAnchor.RIGHT)) { result = TextAnchor.HALF_ASCENT_RIGHT; } else if (anchor.equals(RectangleAnchor.BOTTOM_LEFT)) { result = TextAnchor.TOP_LEFT; } else if (anchor.equals(RectangleAnchor.BOTTOM)) { result = TextAnchor.TOP_CENTER; } else if (anchor.equals(RectangleAnchor.BOTTOM_RIGHT)) { result = TextAnchor.TOP_RIGHT; } return result; } private RectangleAnchor flipAnchorH(RectangleAnchor anchor) { RectangleAnchor result = anchor; if (anchor.equals(RectangleAnchor.TOP_LEFT)) { result = RectangleAnchor.TOP_RIGHT; } else if (anchor.equals(RectangleAnchor.TOP_RIGHT)) { result = RectangleAnchor.TOP_LEFT; } else if (anchor.equals(RectangleAnchor.LEFT)) { result = RectangleAnchor.RIGHT; } else if (anchor.equals(RectangleAnchor.RIGHT)) { result = RectangleAnchor.LEFT; } else if (anchor.equals(RectangleAnchor.BOTTOM_LEFT)) { result = RectangleAnchor.BOTTOM_RIGHT; } else if (anchor.equals(RectangleAnchor.BOTTOM_RIGHT)) { result = RectangleAnchor.BOTTOM_LEFT; } return result; } private RectangleAnchor flipAnchorV(RectangleAnchor anchor) { RectangleAnchor result = anchor; if (anchor.equals(RectangleAnchor.TOP_LEFT)) { result = RectangleAnchor.BOTTOM_LEFT; } else if (anchor.equals(RectangleAnchor.TOP_RIGHT)) { result = RectangleAnchor.BOTTOM_RIGHT; } else if (anchor.equals(RectangleAnchor.TOP)) { result = RectangleAnchor.BOTTOM; } else if (anchor.equals(RectangleAnchor.BOTTOM)) { result = RectangleAnchor.TOP; } else if (anchor.equals(RectangleAnchor.BOTTOM_LEFT)) { result = RectangleAnchor.TOP_LEFT; } else if (anchor.equals(RectangleAnchor.BOTTOM_RIGHT)) { result = RectangleAnchor.TOP_RIGHT; } return result; } /** * Tests this overlay for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof CrosshairOverlay)) { return false; } CrosshairOverlay that = (CrosshairOverlay) obj; if (!this.xCrosshairs.equals(that.xCrosshairs)) { return false; } if (!this.yCrosshairs.equals(that.yCrosshairs)) { return false; } return true; } /** * Returns a clone of this instance. * * @return A clone of this instance. * * @throws java.lang.CloneNotSupportedException if there is some problem * with the cloning. */ @Override public Object clone() throws CloneNotSupportedException { CrosshairOverlay clone = (CrosshairOverlay) super.clone(); clone.xCrosshairs = (List) ObjectUtils.deepClone(this.xCrosshairs); clone.yCrosshairs = (List) ObjectUtils.deepClone(this.yCrosshairs); return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/panel/Overlay.java000066400000000000000000000042501463604235500264420ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------ * Overlay.java * ------------ * (C) Copyright 2009-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.panel; import java.awt.Graphics2D; import org.jfree.chart.ChartPanel; import org.jfree.chart.event.OverlayChangeListener; /** * Defines the interface for an overlay that can be added to a * {@link ChartPanel}. */ public interface Overlay { /** * Paints the crosshairs in the layer. * * @param g2 the graphics target. * @param chartPanel the chart panel. */ void paintOverlay(Graphics2D g2, ChartPanel chartPanel); /** * Registers a change listener with the overlay. * * @param listener the listener. */ void addChangeListener(OverlayChangeListener listener); /** * Deregisters a listener from the overlay. * * @param listener the listener. */ void removeChangeListener(OverlayChangeListener listener); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/panel/package.html000066400000000000000000000002471463604235500264410ustar00rootroot00000000000000 Classes related to the {@link org.jfree.chart.ChartPanel} class. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/000077500000000000000000000000001463604235500240345ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/AbstractPieLabelDistributor.java000066400000000000000000000064651463604235500323060ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------------- * AbstractPieLabelDistributor.java * -------------------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import java.io.Serializable; import java.util.List; import org.jfree.chart.util.Args; /** * A base class for handling the distribution of pie section labels. Create * your own subclass and set it using the * {@link PiePlot#setLabelDistributor(AbstractPieLabelDistributor)} method * if you want to customise the label distribution. */ public abstract class AbstractPieLabelDistributor implements Serializable { /** The label records. */ protected List labels; /** * Creates a new instance. */ public AbstractPieLabelDistributor() { this.labels = new java.util.ArrayList(); } /** * Returns a label record from the list. * * @param index the index. * * @return The label record. */ public PieLabelRecord getPieLabelRecord(int index) { return (PieLabelRecord) this.labels.get(index); } /** * Adds a label record. * * @param record the label record ({@code null} not permitted). */ public void addPieLabelRecord(PieLabelRecord record) { Args.nullNotPermitted(record, "record"); this.labels.add(record); } /** * Returns the number of items in the list. * * @return The item count. */ public int getItemCount() { return this.labels.size(); } /** * Clears the list of labels. */ public void clear() { this.labels.clear(); } /** * Called by the {@link PiePlot} class. Implementations should distribute * the labels in this.labels then return. * * @param minY the y-coordinate for the top of the label area. * @param height the height of the label area. */ public abstract void distributeLabels(double minY, double height); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/CategoryCrosshairState.java000066400000000000000000000130021463604235500313270ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * CategoryCrosshairState.java * --------------------------- * (C) Copyright 2008-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import java.awt.geom.Point2D; import org.jfree.chart.renderer.category.CategoryItemRenderer; /** * Represents state information for the crosshairs in a {@link CategoryPlot}. * An instance of this class is created at the start of the rendering process, * and updated as each data item is rendered. At the end of the rendering * process, this class holds the row key, column key and value for the * crosshair location. */ public class CategoryCrosshairState extends CrosshairState { /** * The row key for the crosshair point. */ private Comparable rowKey; /** * The column key for the crosshair point. */ private Comparable columnKey; /** * Creates a new instance. */ public CategoryCrosshairState() { this.rowKey = null; this.columnKey = null; } /** * Returns the row key. * * @return The row key. */ public Comparable getRowKey() { return this.rowKey; } /** * Sets the row key. * * @param key the row key. */ public void setRowKey(Comparable key) { this.rowKey = key; } /** * Returns the column key. * * @return The column key. */ public Comparable getColumnKey() { return this.columnKey; } /** * Sets the column key. * * @param key the key. */ public void setColumnKey(Comparable key) { this.columnKey = key; } /** * Evaluates a data point from a {@link CategoryItemRenderer} and if it is * the closest to the anchor point it becomes the new crosshair point. * * @param rowKey the row key. * @param columnKey the column key. * @param value y coordinate (measured against the range axis). * @param datasetIndex the dataset index for this point. * @param transX x translated into Java2D space. * @param transY y translated into Java2D space. * @param orientation the plot orientation. */ public void updateCrosshairPoint(Comparable rowKey, Comparable columnKey, double value, int datasetIndex, double transX, double transY, PlotOrientation orientation) { Point2D anchor = getAnchor(); if (anchor != null) { double xx = anchor.getX(); double yy = anchor.getY(); if (orientation == PlotOrientation.HORIZONTAL) { double temp = yy; yy = xx; xx = temp; } double d = (transX - xx) * (transX - xx) + (transY - yy) * (transY - yy); if (d < getCrosshairDistance()) { this.rowKey = rowKey; this.columnKey = columnKey; setCrosshairY(value); setDatasetIndex(datasetIndex); setCrosshairDistance(d); } } } /** * Updates only the crosshair row and column keys (this is for the case * where the range crosshair does NOT lock onto the nearest data value). * * @param rowKey the row key. * @param columnKey the column key. * @param datasetIndex the dataset axis index. * @param transX the translated x-value. * @param orientation the plot orientation. */ public void updateCrosshairX(Comparable rowKey, Comparable columnKey, int datasetIndex, double transX, PlotOrientation orientation) { Point2D anchor = getAnchor(); if (anchor != null) { double anchorX = anchor.getX(); if (orientation == PlotOrientation.HORIZONTAL) { anchorX = anchor.getY(); } double d = Math.abs(transX - anchorX); if (d < getCrosshairDistance()) { this.rowKey = rowKey; this.columnKey = columnKey; setDatasetIndex(datasetIndex); setCrosshairDistance(d); } } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/CategoryMarker.java000066400000000000000000000142511463604235500276210ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * CategoryMarker.java * ------------------- * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Nicolas Brodu; * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.plot; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Paint; import java.awt.Stroke; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.event.MarkerChangeEvent; import org.jfree.chart.ui.LengthAdjustmentType; import org.jfree.chart.util.Args; /** * A marker for a category. *

* Note that for serialization to work correctly, the category key must be an * instance of a serializable class. * * @see CategoryPlot#addDomainMarker(CategoryMarker) */ public class CategoryMarker extends Marker implements Cloneable, Serializable { /** The category key. */ private Comparable key; /** * A hint that the marker should be drawn as a line rather than a region. */ private boolean drawAsLine = false; /** * Creates a new category marker for the specified category. * * @param key the category key. */ public CategoryMarker(Comparable key) { this(key, Color.GRAY, new BasicStroke(1.0f)); } /** * Creates a new category marker. * * @param key the key. * @param paint the paint ({@code null} not permitted). * @param stroke the stroke ({@code null} not permitted). */ public CategoryMarker(Comparable key, Paint paint, Stroke stroke) { this(key, paint, stroke, paint, stroke, 1.0f); } /** * Creates a new category marker. * * @param key the key. * @param paint the paint ({@code null} not permitted). * @param stroke the stroke ({@code null} not permitted). * @param outlinePaint the outline paint ({@code null} permitted). * @param outlineStroke the outline stroke ({@code null} permitted). * @param alpha the alpha transparency. */ public CategoryMarker(Comparable key, Paint paint, Stroke stroke, Paint outlinePaint, Stroke outlineStroke, float alpha) { super(paint, stroke, outlinePaint, outlineStroke, alpha); this.key = key; setLabelOffsetType(LengthAdjustmentType.EXPAND); } /** * Returns the key. * * @return The key. */ public Comparable getKey() { return this.key; } /** * Sets the key and sends a {@link MarkerChangeEvent} to all registered * listeners. * * @param key the key ({@code null} not permitted). */ public void setKey(Comparable key) { Args.nullNotPermitted(key, "key"); this.key = key; notifyListeners(new MarkerChangeEvent(this)); } /** * Returns the flag that controls whether the marker is drawn as a region * or a line. * * @return A line. */ public boolean getDrawAsLine() { return this.drawAsLine; } /** * Sets the flag that controls whether the marker is drawn as a region or * as a line, and sends a {@link MarkerChangeEvent} to all registered * listeners. * * @param drawAsLine the flag. */ public void setDrawAsLine(boolean drawAsLine) { this.drawAsLine = drawAsLine; notifyListeners(new MarkerChangeEvent(this)); } /** * Tests the marker for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (!(obj instanceof CategoryMarker)) { return false; } CategoryMarker that = (CategoryMarker) obj; if (!that.canEqual(this)) { return false; } if (!Objects.equals(this.key, that.key)) { return false; } if (this.drawAsLine != that.drawAsLine) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // Solves Problem: equals not symmetric return (other instanceof CategoryMarker); } @Override public int hashCode() { int hash = super.hashCode(); // equals calls superclass, hashCode must also hash = 89 * hash + Objects.hashCode(this.key); hash = 89 * hash + (this.drawAsLine ? 1 : 0); return hash; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/CategoryPlot.java000066400000000000000000005025561463604235500273300ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * CategoryPlot.java * ----------------- * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Jeremy Bowman; * Arnaud Lelievre; * Richard West, Advanced Micro Devices, Inc.; * Ulrich Voigt - patch 2686040; * Peter Kolb - patches 2603321 and 2809117; * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.plot; import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Composite; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Rectangle; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.ResourceBundle; import java.util.Set; import java.util.TreeMap; import org.jfree.chart.JFreeChart; import org.jfree.chart.LegendItemCollection; import org.jfree.chart.annotations.Annotation; import org.jfree.chart.annotations.CategoryAnnotation; import org.jfree.chart.axis.Axis; import org.jfree.chart.axis.AxisCollection; import org.jfree.chart.axis.AxisLocation; import org.jfree.chart.axis.AxisSpace; import org.jfree.chart.axis.AxisState; import org.jfree.chart.axis.CategoryAnchor; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.TickType; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.axis.ValueTick; import org.jfree.chart.event.AnnotationChangeEvent; import org.jfree.chart.event.AnnotationChangeListener; import org.jfree.chart.event.ChartChangeEventType; import org.jfree.chart.event.PlotChangeEvent; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.event.RendererChangeListener; import org.jfree.chart.renderer.category.CategoryItemRenderer; import org.jfree.chart.renderer.category.CategoryItemRendererState; import org.jfree.chart.ui.Layer; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.util.CloneUtils; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.ResourceBundleWrapper; import org.jfree.chart.util.SerialUtils; import org.jfree.chart.util.ShadowGenerator; import org.jfree.chart.util.ShapeUtils; import org.jfree.chart.util.SortOrder; import org.jfree.data.Range; import org.jfree.data.category.CategoryDataset; import org.jfree.data.general.DatasetChangeEvent; import org.jfree.data.general.DatasetUtils; /** * A general plotting class that uses data from a {@link CategoryDataset} and * renders each data item using a {@link CategoryItemRenderer}. */ public class CategoryPlot extends Plot implements ValueAxisPlot, Pannable, Zoomable, AnnotationChangeListener, RendererChangeListener, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -3537691700434728188L; /** * The default visibility of the grid lines plotted against the domain * axis. */ public static final boolean DEFAULT_DOMAIN_GRIDLINES_VISIBLE = false; /** * The default visibility of the grid lines plotted against the range * axis. */ public static final boolean DEFAULT_RANGE_GRIDLINES_VISIBLE = true; /** The default grid line stroke. */ public static final Stroke DEFAULT_GRIDLINE_STROKE = new BasicStroke(0.5f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0.0f, new float[] {2.0f, 2.0f}, 0.0f); /** The default grid line paint. */ public static final Paint DEFAULT_GRIDLINE_PAINT = Color.LIGHT_GRAY; /** The default value label font. */ public static final Font DEFAULT_VALUE_LABEL_FONT = new Font("SansSerif", Font.PLAIN, 10); /** * The default crosshair visibility. */ public static final boolean DEFAULT_CROSSHAIR_VISIBLE = false; /** * The default crosshair stroke. */ public static final Stroke DEFAULT_CROSSHAIR_STROKE = DEFAULT_GRIDLINE_STROKE; /** * The default crosshair paint. */ public static final Paint DEFAULT_CROSSHAIR_PAINT = Color.BLUE; /** The resourceBundle for the localization. */ protected static ResourceBundle localizationResources = ResourceBundleWrapper.getBundle( "org.jfree.chart.plot.LocalizationBundle"); /** The plot orientation. */ private PlotOrientation orientation; /** The offset between the data area and the axes. */ private RectangleInsets axisOffset; /** Storage for the domain axes. */ private Map domainAxes; /** Storage for the domain axis locations. */ private Map domainAxisLocations; /** * A flag that controls whether or not the shared domain axis is drawn * (only relevant when the plot is being used as a subplot). */ private boolean drawSharedDomainAxis; /** Storage for the range axes. */ private Map rangeAxes; /** Storage for the range axis locations. */ private Map rangeAxisLocations; /** Storage for the datasets. */ private Map datasets; /** * Storage for keys that map each dataset to one or more domain axes. * Typically a dataset is rendered using the scale of a single axis, but * a dataset can contribute to the "auto-range" of any number of axes. */ private TreeMap> datasetToDomainAxesMap; /** * Storage for keys that map each dataset to one or more range axes. * Typically a dataset is rendered using the scale of a single axis, but * a dataset can contribute to the "auto-range" of any number of axes. */ private TreeMap> datasetToRangeAxesMap; /** Storage for the renderers. */ private Map renderers; /** The dataset rendering order. */ private DatasetRenderingOrder renderingOrder = DatasetRenderingOrder.REVERSE; /** * Controls the order in which the columns are traversed when rendering the * data items. */ private SortOrder columnRenderingOrder = SortOrder.ASCENDING; /** * Controls the order in which the rows are traversed when rendering the * data items. */ private SortOrder rowRenderingOrder = SortOrder.ASCENDING; /** * A flag that controls whether the grid-lines for the domain axis are * visible. */ private boolean domainGridlinesVisible; /** The position of the domain gridlines relative to the category. */ private CategoryAnchor domainGridlinePosition; /** The stroke used to draw the domain grid-lines. */ private transient Stroke domainGridlineStroke; /** The paint used to draw the domain grid-lines. */ private transient Paint domainGridlinePaint; /** * A flag that controls whether or not the zero baseline against the range * axis is visible. */ private boolean rangeZeroBaselineVisible; /** * The stroke used for the zero baseline against the range axis. */ private transient Stroke rangeZeroBaselineStroke; /** * The paint used for the zero baseline against the range axis. */ private transient Paint rangeZeroBaselinePaint; /** * A flag that controls whether the grid-lines for the range axis are * visible. */ private boolean rangeGridlinesVisible; /** The stroke used to draw the range axis grid-lines. */ private transient Stroke rangeGridlineStroke; /** The paint used to draw the range axis grid-lines. */ private transient Paint rangeGridlinePaint; /** * A flag that controls whether or not gridlines are shown for the minor * tick values on the primary range axis. */ private boolean rangeMinorGridlinesVisible; /** * The stroke used to draw the range minor grid-lines. */ private transient Stroke rangeMinorGridlineStroke; /** * The paint used to draw the range minor grid-lines. */ private transient Paint rangeMinorGridlinePaint; /** The anchor value. */ private double anchorValue; /** * The index for the dataset that the crosshairs are linked to (this * determines which axes the crosshairs are plotted against). */ private int crosshairDatasetIndex; /** * A flag that controls the visibility of the domain crosshair. */ private boolean domainCrosshairVisible; /** * The row key for the crosshair point. */ private Comparable domainCrosshairRowKey; /** * The column key for the crosshair point. */ private Comparable domainCrosshairColumnKey; /** * The stroke used to draw the domain crosshair if it is visible. */ private transient Stroke domainCrosshairStroke; /** * The paint used to draw the domain crosshair if it is visible. */ private transient Paint domainCrosshairPaint; /** A flag that controls whether or not a range crosshair is drawn. */ private boolean rangeCrosshairVisible; /** The range crosshair value. */ private double rangeCrosshairValue; /** The pen/brush used to draw the crosshair (if any). */ private transient Stroke rangeCrosshairStroke; /** The color used to draw the crosshair (if any). */ private transient Paint rangeCrosshairPaint; /** * A flag that controls whether or not the crosshair locks onto actual * data points. */ private boolean rangeCrosshairLockedOnData = true; /** A map containing lists of markers for the domain axes. */ private Map> foregroundDomainMarkers; /** A map containing lists of markers for the domain axes. */ private Map> backgroundDomainMarkers; /** A map containing lists of markers for the range axes. */ private Map> foregroundRangeMarkers; /** A map containing lists of markers for the range axes. */ private Map> backgroundRangeMarkers; /** * A (possibly empty) list of annotations for the plot. The list should * be initialised in the constructor and never allowed to be * {@code null}. */ private List annotations; /** * The weight for the plot (only relevant when the plot is used as a subplot * within a combined plot). */ private int weight; /** The fixed space for the domain axis. */ private AxisSpace fixedDomainAxisSpace; /** The fixed space for the range axis. */ private AxisSpace fixedRangeAxisSpace; /** * An optional collection of legend items that can be returned by the * getLegendItems() method. */ private LegendItemCollection fixedLegendItems; /** * A flag that controls whether or not panning is enabled for the * range axis/axes. */ private boolean rangePannable; /** * The shadow generator for the plot ({@code null} permitted). */ private ShadowGenerator shadowGenerator; /** * Default constructor. */ public CategoryPlot() { this(null, null, null, null); } /** * Creates a new plot. * * @param dataset the dataset ({@code null} permitted). * @param domainAxis the domain axis ({@code null} permitted). * @param rangeAxis the range axis ({@code null} permitted). * @param renderer the item renderer ({@code null} permitted). * */ public CategoryPlot(CategoryDataset dataset, CategoryAxis domainAxis, ValueAxis rangeAxis, CategoryItemRenderer renderer) { super(); this.orientation = PlotOrientation.VERTICAL; // allocate storage for dataset, axes and renderers this.domainAxes = new HashMap<>(); this.domainAxisLocations = new HashMap<>(); this.rangeAxes = new HashMap<>(); this.rangeAxisLocations = new HashMap<>(); this.datasetToDomainAxesMap = new TreeMap<>(); this.datasetToRangeAxesMap = new TreeMap<>(); this.renderers = new HashMap<>(); this.datasets = new HashMap<>(); this.datasets.put(0, dataset); if (dataset != null) { dataset.addChangeListener(this); } this.axisOffset = RectangleInsets.ZERO_INSETS; this.domainAxisLocations.put(0, AxisLocation.BOTTOM_OR_LEFT); this.rangeAxisLocations.put(0, AxisLocation.TOP_OR_LEFT); this.renderers.put(0, renderer); if (renderer != null) { renderer.setPlot(this); renderer.addChangeListener(this); } this.domainAxes.put(0, domainAxis); mapDatasetToDomainAxis(0, 0); if (domainAxis != null) { domainAxis.setPlot(this); domainAxis.addChangeListener(this); } this.drawSharedDomainAxis = false; this.rangeAxes.put(0, rangeAxis); mapDatasetToRangeAxis(0, 0); if (rangeAxis != null) { rangeAxis.setPlot(this); rangeAxis.addChangeListener(this); } configureDomainAxes(); configureRangeAxes(); this.domainGridlinesVisible = DEFAULT_DOMAIN_GRIDLINES_VISIBLE; this.domainGridlinePosition = CategoryAnchor.MIDDLE; this.domainGridlineStroke = DEFAULT_GRIDLINE_STROKE; this.domainGridlinePaint = DEFAULT_GRIDLINE_PAINT; this.rangeZeroBaselineVisible = false; this.rangeZeroBaselinePaint = Color.BLACK; this.rangeZeroBaselineStroke = new BasicStroke(0.5f); this.rangeGridlinesVisible = DEFAULT_RANGE_GRIDLINES_VISIBLE; this.rangeGridlineStroke = DEFAULT_GRIDLINE_STROKE; this.rangeGridlinePaint = DEFAULT_GRIDLINE_PAINT; this.rangeMinorGridlinesVisible = false; this.rangeMinorGridlineStroke = DEFAULT_GRIDLINE_STROKE; this.rangeMinorGridlinePaint = Color.WHITE; this.foregroundDomainMarkers = new HashMap<>(); this.backgroundDomainMarkers = new HashMap<>(); this.foregroundRangeMarkers = new HashMap<>(); this.backgroundRangeMarkers = new HashMap<>(); this.anchorValue = 0.0; this.domainCrosshairVisible = false; this.domainCrosshairStroke = DEFAULT_CROSSHAIR_STROKE; this.domainCrosshairPaint = DEFAULT_CROSSHAIR_PAINT; this.rangeCrosshairVisible = DEFAULT_CROSSHAIR_VISIBLE; this.rangeCrosshairValue = 0.0; this.rangeCrosshairStroke = DEFAULT_CROSSHAIR_STROKE; this.rangeCrosshairPaint = DEFAULT_CROSSHAIR_PAINT; this.annotations = new ArrayList<>(); this.rangePannable = false; this.shadowGenerator = null; } /** * Returns a string describing the type of plot. * * @return The type. */ @Override public String getPlotType() { return localizationResources.getString("Category_Plot"); } /** * Returns the orientation of the plot. * * @return The orientation of the plot (never {@code null}). * * @see #setOrientation(PlotOrientation) */ @Override public PlotOrientation getOrientation() { return this.orientation; } /** * Sets the orientation for the plot and sends a {@link PlotChangeEvent} to * all registered listeners. * * @param orientation the orientation ({@code null} not permitted). * * @see #getOrientation() */ public void setOrientation(PlotOrientation orientation) { Args.nullNotPermitted(orientation, "orientation"); this.orientation = orientation; fireChangeEvent(); } /** * Returns the axis offset. * * @return The axis offset (never {@code null}). * * @see #setAxisOffset(RectangleInsets) */ public RectangleInsets getAxisOffset() { return this.axisOffset; } /** * Sets the axis offsets (gap between the data area and the axes) and * sends a {@link PlotChangeEvent} to all registered listeners. * * @param offset the offset ({@code null} not permitted). * * @see #getAxisOffset() */ public void setAxisOffset(RectangleInsets offset) { Args.nullNotPermitted(offset, "offset"); this.axisOffset = offset; fireChangeEvent(); } /** * Returns the domain axis for the plot. If the domain axis for this plot * is {@code null}, then the method will return the parent plot's * domain axis (if there is a parent plot). * * @return The domain axis ({@code null} permitted). * * @see #setDomainAxis(CategoryAxis) */ public CategoryAxis getDomainAxis() { return getDomainAxis(0); } /** * Returns a domain axis. * * @param index the axis index. * * @return The axis ({@code null} possible). * * @see #setDomainAxis(int, CategoryAxis) */ public CategoryAxis getDomainAxis(int index) { CategoryAxis result = this.domainAxes.get(index); if (result == null) { Plot parent = getParent(); if (parent instanceof CategoryPlot) { CategoryPlot cp = (CategoryPlot) parent; result = cp.getDomainAxis(index); } } return result; } /** * Returns a map containing the domain axes that are assigned to this plot. * The map is unmodifiable. * * @return A map containing the domain axes that are assigned to the plot * (never {@code null}). * * @since 1.5.4 */ public Map getDomainAxes() { return Collections.unmodifiableMap(this.domainAxes); } /** * Sets the domain axis for the plot and sends a {@link PlotChangeEvent} to * all registered listeners. * * @param axis the axis ({@code null} permitted). * * @see #getDomainAxis() */ public void setDomainAxis(CategoryAxis axis) { setDomainAxis(0, axis); } /** * Sets a domain axis and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param index the axis index. * @param axis the axis ({@code null} permitted). * * @see #getDomainAxis(int) */ public void setDomainAxis(int index, CategoryAxis axis) { setDomainAxis(index, axis, true); } /** * Sets a domain axis and, if requested, sends a {@link PlotChangeEvent} to * all registered listeners. * * @param index the axis index. * @param axis the axis ({@code null} permitted). * @param notify notify listeners? */ public void setDomainAxis(int index, CategoryAxis axis, boolean notify) { CategoryAxis existing = this.domainAxes.get(index); if (existing != null) { existing.removeChangeListener(this); } if (axis != null) { axis.setPlot(this); } this.domainAxes.put(index, axis); if (axis != null) { axis.configure(); axis.addChangeListener(this); } if (notify) { fireChangeEvent(); } } /** * Sets the domain axes for this plot and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param axes the axes ({@code null} not permitted). * * @see #setRangeAxes(ValueAxis[]) */ public void setDomainAxes(CategoryAxis[] axes) { for (int i = 0; i < axes.length; i++) { setDomainAxis(i, axes[i], false); } fireChangeEvent(); } /** * Returns the index of the specified axis, or {@code -1} if the axis * is not assigned to the plot. * * @param axis the axis ({@code null} not permitted). * * @return The axis index. * * @see #getDomainAxis(int) * @see #getRangeAxisIndex(ValueAxis) */ public int getDomainAxisIndex(CategoryAxis axis) { Args.nullNotPermitted(axis, "axis"); for (Entry entry : this.domainAxes.entrySet()) { if (entry.getValue() == axis) { return entry.getKey(); } } return -1; } /** * Returns the domain axis location for the primary domain axis. * * @return The location (never {@code null}). * * @see #getRangeAxisLocation() */ public AxisLocation getDomainAxisLocation() { return getDomainAxisLocation(0); } /** * Returns the location for a domain axis. * * @param index the axis index. * * @return The location. * * @see #setDomainAxisLocation(int, AxisLocation) */ public AxisLocation getDomainAxisLocation(int index) { AxisLocation result = this.domainAxisLocations.get(index); if (result == null) { result = AxisLocation.getOpposite(getDomainAxisLocation(0)); } return result; } /** * Sets the location of the domain axis and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param location the axis location ({@code null} not permitted). * * @see #getDomainAxisLocation() * @see #setDomainAxisLocation(int, AxisLocation) */ public void setDomainAxisLocation(AxisLocation location) { // delegate... setDomainAxisLocation(0, location, true); } /** * Sets the location of the domain axis and, if requested, sends a * {@link PlotChangeEvent} to all registered listeners. * * @param location the axis location ({@code null} not permitted). * @param notify a flag that controls whether listeners are notified. */ public void setDomainAxisLocation(AxisLocation location, boolean notify) { // delegate... setDomainAxisLocation(0, location, notify); } /** * Sets the location for a domain axis and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param index the axis index. * @param location the location. * * @see #getDomainAxisLocation(int) * @see #setRangeAxisLocation(int, AxisLocation) */ public void setDomainAxisLocation(int index, AxisLocation location) { // delegate... setDomainAxisLocation(index, location, true); } /** * Sets the location for a domain axis and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param index the axis index. * @param location the location. * @param notify notify listeners? * * @see #getDomainAxisLocation(int) * @see #setRangeAxisLocation(int, AxisLocation, boolean) */ public void setDomainAxisLocation(int index, AxisLocation location, boolean notify) { if (index == 0 && location == null) { throw new IllegalArgumentException( "Null 'location' for index 0 not permitted."); } this.domainAxisLocations.put(index, location); if (notify) { fireChangeEvent(); } } /** * Returns the domain axis edge. This is derived from the axis location * and the plot orientation. * * @return The edge (never {@code null}). */ public RectangleEdge getDomainAxisEdge() { return getDomainAxisEdge(0); } /** * Returns the edge for a domain axis. * * @param index the axis index. * * @return The edge (never {@code null}). */ public RectangleEdge getDomainAxisEdge(int index) { RectangleEdge result; AxisLocation location = getDomainAxisLocation(index); if (location != null) { result = Plot.resolveDomainAxisLocation(location, this.orientation); } else { result = RectangleEdge.opposite(getDomainAxisEdge(0)); } return result; } /** * Returns the number of domain axes. * * @return The axis count. */ public int getDomainAxisCount() { return this.domainAxes.size(); } /** * Clears the domain axes from the plot and sends a {@link PlotChangeEvent} * to all registered listeners. */ public void clearDomainAxes() { for (CategoryAxis xAxis : this.domainAxes.values()) { if (xAxis != null) { xAxis.removeChangeListener(this); } } this.domainAxes.clear(); fireChangeEvent(); } /** * Configures the domain axes. */ public void configureDomainAxes() { for (CategoryAxis xAxis : this.domainAxes.values()) { if (xAxis != null) { xAxis.configure(); } } } /** * Returns the range axis for the plot. If the range axis for this plot is * null, then the method will return the parent plot's range axis (if there * is a parent plot). * * @return The range axis (possibly {@code null}). */ public ValueAxis getRangeAxis() { return getRangeAxis(0); } /** * Returns a range axis. * * @param index the axis index. * * @return The axis ({@code null} possible). */ public ValueAxis getRangeAxis(int index) { ValueAxis result = this.rangeAxes.get(index); if (result == null) { Plot parent = getParent(); if (parent instanceof CategoryPlot) { CategoryPlot cp = (CategoryPlot) parent; result = cp.getRangeAxis(index); } } return result; } /** * Returns a map containing the range axes that are assigned to this plot. * The map is unmodifiable. * * @return A map containing the domain axes that are assigned to the plot * (never {@code null}). * * @since 1.5.4 */ public Map getRangeAxes() { return Collections.unmodifiableMap(this.rangeAxes); } /** * Sets the range axis for the plot and sends a {@link PlotChangeEvent} to * all registered listeners. * * @param axis the axis ({@code null} permitted). */ public void setRangeAxis(ValueAxis axis) { setRangeAxis(0, axis); } /** * Sets a range axis and sends a {@link PlotChangeEvent} to all registered * listeners. * * @param index the axis index. * @param axis the axis. */ public void setRangeAxis(int index, ValueAxis axis) { setRangeAxis(index, axis, true); } /** * Sets a range axis and, if requested, sends a {@link PlotChangeEvent} to * all registered listeners. * * @param index the axis index. * @param axis the axis. * @param notify notify listeners? */ public void setRangeAxis(int index, ValueAxis axis, boolean notify) { ValueAxis existing = this.rangeAxes.get(index); if (existing != null) { existing.removeChangeListener(this); } if (axis != null) { axis.setPlot(this); } this.rangeAxes.put(index, axis); if (axis != null) { axis.configure(); axis.addChangeListener(this); } if (notify) { fireChangeEvent(); } } /** * Sets the range axes for this plot and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param axes the axes ({@code null} not permitted). * * @see #setDomainAxes(CategoryAxis[]) */ public void setRangeAxes(ValueAxis[] axes) { for (int i = 0; i < axes.length; i++) { setRangeAxis(i, axes[i], false); } fireChangeEvent(); } /** * Returns the index of the specified axis, or {@code -1} if the axis * is not assigned to the plot. * * @param axis the axis ({@code null} not permitted). * * @return The axis index. * * @see #getRangeAxis(int) * @see #getDomainAxisIndex(CategoryAxis) */ public int getRangeAxisIndex(ValueAxis axis) { Args.nullNotPermitted(axis, "axis"); int result = findRangeAxisIndex(axis); if (result < 0) { // try the parent plot Plot parent = getParent(); if (parent instanceof CategoryPlot) { CategoryPlot p = (CategoryPlot) parent; result = p.getRangeAxisIndex(axis); } } return result; } private int findRangeAxisIndex(ValueAxis axis) { for (Entry entry : this.rangeAxes.entrySet()) { if (entry.getValue() == axis) { return entry.getKey(); } } return -1; } /** * Returns the range axis location. * * @return The location (never {@code null}). */ public AxisLocation getRangeAxisLocation() { return getRangeAxisLocation(0); } /** * Returns the location for a range axis. * * @param index the axis index. * * @return The location. * * @see #setRangeAxisLocation(int, AxisLocation) */ public AxisLocation getRangeAxisLocation(int index) { AxisLocation result = this.rangeAxisLocations.get(index); if (result == null) { result = AxisLocation.getOpposite(getRangeAxisLocation(0)); } return result; } /** * Sets the location of the range axis and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param location the location ({@code null} not permitted). * * @see #setRangeAxisLocation(AxisLocation, boolean) * @see #setDomainAxisLocation(AxisLocation) */ public void setRangeAxisLocation(AxisLocation location) { // defer argument checking... setRangeAxisLocation(location, true); } /** * Sets the location of the range axis and, if requested, sends a * {@link PlotChangeEvent} to all registered listeners. * * @param location the location ({@code null} not permitted). * @param notify notify listeners? * * @see #setDomainAxisLocation(AxisLocation, boolean) */ public void setRangeAxisLocation(AxisLocation location, boolean notify) { setRangeAxisLocation(0, location, notify); } /** * Sets the location for a range axis and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param index the axis index. * @param location the location. * * @see #getRangeAxisLocation(int) * @see #setRangeAxisLocation(int, AxisLocation, boolean) */ public void setRangeAxisLocation(int index, AxisLocation location) { setRangeAxisLocation(index, location, true); } /** * Sets the location for a range axis and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param index the axis index. * @param location the location. * @param notify notify listeners? * * @see #getRangeAxisLocation(int) * @see #setDomainAxisLocation(int, AxisLocation, boolean) */ public void setRangeAxisLocation(int index, AxisLocation location, boolean notify) { if (index == 0 && location == null) { throw new IllegalArgumentException( "Null 'location' for index 0 not permitted."); } this.rangeAxisLocations.put(index, location); if (notify) { fireChangeEvent(); } } /** * Returns the edge where the primary range axis is located. * * @return The edge (never {@code null}). */ public RectangleEdge getRangeAxisEdge() { return getRangeAxisEdge(0); } /** * Returns the edge for a range axis. * * @param index the axis index. * * @return The edge. */ public RectangleEdge getRangeAxisEdge(int index) { AxisLocation location = getRangeAxisLocation(index); return Plot.resolveRangeAxisLocation(location, this.orientation); } /** * Returns the number of range axes. * * @return The axis count. */ public int getRangeAxisCount() { return this.rangeAxes.size(); } /** * Clears the range axes from the plot and sends a {@link PlotChangeEvent} * to all registered listeners. */ public void clearRangeAxes() { for (ValueAxis yAxis : this.rangeAxes.values()) { if (yAxis != null) { yAxis.removeChangeListener(this); } } this.rangeAxes.clear(); fireChangeEvent(); } /** * Configures the range axes. */ public void configureRangeAxes() { for (ValueAxis yAxis : this.rangeAxes.values()) { if (yAxis != null) { yAxis.configure(); } } } /** * Returns the primary dataset for the plot. * * @return The primary dataset (possibly {@code null}). * * @see #setDataset(CategoryDataset) */ public CategoryDataset getDataset() { return getDataset(0); } /** * Returns the dataset with the given index, or {@code null} if there is * no dataset. * * @param index the dataset index (must be >= 0). * * @return The dataset (possibly {@code null}). * * @see #setDataset(int, CategoryDataset) */ public CategoryDataset getDataset(int index) { return this.datasets.get(index); } /** * Returns a map containing the datasets that are assigned to this plot. * The map is unmodifiable. * * @return A map containing the datasets that are assigned to the plot * (never {@code null}). * * @since 1.5.4 */ public Map getDatasets() { return Collections.unmodifiableMap(this.datasets); } /** * Sets the dataset for the plot, replacing the existing dataset, if there * is one. This method also calls the * {@link #datasetChanged(DatasetChangeEvent)} method, which adjusts the * axis ranges if necessary and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param dataset the dataset ({@code null} permitted). * * @see #getDataset() */ public void setDataset(CategoryDataset dataset) { setDataset(0, dataset); } /** * Sets a dataset for the plot and sends a change notification to all * registered listeners. * * @param index the dataset index (must be >= 0). * @param dataset the dataset ({@code null} permitted). * * @see #getDataset(int) */ public void setDataset(int index, CategoryDataset dataset) { CategoryDataset existing = this.datasets.get(index); if (existing != null) { existing.removeChangeListener(this); } this.datasets.put(index, dataset); if (dataset != null) { dataset.addChangeListener(this); } // send a dataset change event to self... DatasetChangeEvent event = new DatasetChangeEvent(this, dataset); datasetChanged(event); } /** * Returns the number of datasets. * * @return The number of datasets. */ public int getDatasetCount() { return this.datasets.size(); } /** * Returns the index of the specified dataset, or {@code -1} if the * dataset does not belong to the plot. * * @param dataset the dataset ({@code null} not permitted). * * @return The index. */ public int indexOf(CategoryDataset dataset) { for (Entry entry: this.datasets.entrySet()) { if (entry.getValue() == dataset) { return entry.getKey(); } } return -1; } /** * Maps a dataset to a particular domain axis. * * @param index the dataset index (zero-based). * @param axisIndex the axis index (zero-based). * * @see #getDomainAxisForDataset(int) */ public void mapDatasetToDomainAxis(int index, int axisIndex) { List axisIndices = new ArrayList<>(1); axisIndices.add(axisIndex); mapDatasetToDomainAxes(index, axisIndices); } /** * Maps the specified dataset to the axes in the list. Note that the * conversion of data values into Java2D space is always performed using * the first axis in the list. * * @param index the dataset index (zero-based). * @param axisIndices the axis indices ({@code null} permitted). */ public void mapDatasetToDomainAxes(int index, List axisIndices) { Args.requireNonNegative(index, "index"); checkAxisIndices(axisIndices); this.datasetToDomainAxesMap.put(index, new ArrayList<>(axisIndices)); // fake a dataset change event to update axes... datasetChanged(new DatasetChangeEvent(this, getDataset(index))); } /** * This method is used to perform argument checking on the list of * axis indices passed to mapDatasetToDomainAxes() and * mapDatasetToRangeAxes(). * * @param indices the list of indices ({@code null} permitted). */ private void checkAxisIndices(List indices) { // axisIndices can be: // 1. null; // 2. non-empty, containing only Integer objects that are unique. if (indices == null) { return; // OK } int count = indices.size(); if (count == 0) { throw new IllegalArgumentException("Empty list not permitted."); } HashSet set = new HashSet<>(); for (int i = 0; i < count; i++) { Integer item = indices.get(i); if (set.contains(item)) { throw new IllegalArgumentException("Indices must be unique."); } set.add(item); } } /** * Returns the domain axis for a dataset. You can change the axis for a * dataset using the {@link #mapDatasetToDomainAxis(int, int)} method. * * @param index the dataset index (must be >= 0). * * @return The domain axis. * * @see #mapDatasetToDomainAxis(int, int) */ public CategoryAxis getDomainAxisForDataset(int index) { Args.requireNonNegative(index, "index"); CategoryAxis axis; List axisIndices = this.datasetToDomainAxesMap.get(index); if (axisIndices != null) { // the first axis in the list is used for data <--> Java2D Integer axisIndex = axisIndices.get(0); axis = getDomainAxis(axisIndex); } else { axis = getDomainAxis(0); } return axis; } /** * Maps a dataset to a particular range axis. * * @param index the dataset index (zero-based). * @param axisIndex the axis index (zero-based). * * @see #getRangeAxisForDataset(int) */ public void mapDatasetToRangeAxis(int index, int axisIndex) { List axisIndices = new ArrayList<>(1); axisIndices.add(axisIndex); mapDatasetToRangeAxes(index, axisIndices); } /** * Maps the specified dataset to the axes in the list. Note that the * conversion of data values into Java2D space is always performed using * the first axis in the list. * * @param index the dataset index (zero-based). * @param axisIndices the axis indices ({@code null} permitted). */ public void mapDatasetToRangeAxes(int index, List axisIndices) { Args.requireNonNegative(index, "index"); checkAxisIndices(axisIndices); this.datasetToRangeAxesMap.put(index, new ArrayList<>(axisIndices)); // fake a dataset change event to update axes... datasetChanged(new DatasetChangeEvent(this, getDataset(index))); } /** * Returns the range axis for a dataset. You can change the axis for a * dataset using the {@link #mapDatasetToRangeAxis(int, int)} method. * * @param index the dataset index (must be >= 0). * * @return The range axis. * * @see #mapDatasetToRangeAxis(int, int) */ public ValueAxis getRangeAxisForDataset(int index) { Args.requireNonNegative(index, "index"); ValueAxis axis; List axisIndices = this.datasetToRangeAxesMap.get(index); if (axisIndices != null) { // the first axis in the list is used for data <--> Java2D axis = getRangeAxis(axisIndices.get(0)); } else { axis = getRangeAxis(0); } return axis; } /** * Returns the number of renderer slots for this plot. * * @return The number of renderer slots. */ public int getRendererCount() { return this.renderers.size(); } /** * Returns a reference to the renderer for the plot. * * @return The renderer. * * @see #setRenderer(CategoryItemRenderer) */ public CategoryItemRenderer getRenderer() { return getRenderer(0); } /** * Returns the renderer at the given index. * * @param index the renderer index. * * @return The renderer (possibly {@code null}). * * @see #setRenderer(int, CategoryItemRenderer) */ public CategoryItemRenderer getRenderer(int index) { CategoryItemRenderer renderer = this.renderers.get(index); if (renderer == null) { return this.renderers.get(0); } return renderer; } /** * Returns a map containing the renderers that are assigned to this plot. * The map is unmodifiable. * * @return A map containing the renderers that are assigned to the plot * (never {@code null}). * * @since 1.5.4 */ public Map getRenderers() { return Collections.unmodifiableMap(this.renderers); } /** * Sets the renderer at index 0 (sometimes referred to as the "primary" * renderer) and sends a change event to all registered listeners. * * @param renderer the renderer ({@code null} permitted. * * @see #getRenderer() */ public void setRenderer(CategoryItemRenderer renderer) { setRenderer(0, renderer, true); } /** * Sets the renderer at index 0 (sometimes referred to as the "primary" * renderer) and, if requested, sends a change event to all registered * listeners. *

* You can set the renderer to {@code null}, but this is not * recommended because: *

    *
  • no data will be displayed;
  • *
  • the plot background will not be painted;
  • *
* * @param renderer the renderer ({@code null} permitted). * @param notify notify listeners? * * @see #getRenderer() */ public void setRenderer(CategoryItemRenderer renderer, boolean notify) { setRenderer(0, renderer, notify); } /** * Sets the renderer to use for the dataset with the specified index and * sends a change event to all registered listeners. Note that each * dataset should have its own renderer, you should not use one renderer * for multiple datasets. * * @param index the index. * @param renderer the renderer ({@code null} permitted). * * @see #getRenderer(int) * @see #setRenderer(int, CategoryItemRenderer, boolean) */ public void setRenderer(int index, CategoryItemRenderer renderer) { setRenderer(index, renderer, true); } /** * Sets the renderer to use for the dataset with the specified index and, * if requested, sends a change event to all registered listeners. Note * that each dataset should have its own renderer, you should not use one * renderer for multiple datasets. * * @param index the index. * @param renderer the renderer ({@code null} permitted). * @param notify notify listeners? * * @see #getRenderer(int) */ public void setRenderer(int index, CategoryItemRenderer renderer, boolean notify) { CategoryItemRenderer existing = this.renderers.get(index); if (existing != null) { existing.removeChangeListener(this); } this.renderers.put(index, renderer); if (renderer != null) { renderer.setPlot(this); renderer.addChangeListener(this); } configureDomainAxes(); configureRangeAxes(); if (notify) { fireChangeEvent(); } } /** * Sets the renderers for this plot and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param renderers the renderers. */ public void setRenderers(CategoryItemRenderer[] renderers) { for (int i = 0; i < renderers.length; i++) { setRenderer(i, renderers[i], false); } fireChangeEvent(); } /** * Returns the renderer for the specified dataset. If the dataset doesn't * belong to the plot, this method will return {@code null}. * * @param dataset the dataset ({@code null} permitted). * * @return The renderer (possibly {@code null}). */ public CategoryItemRenderer getRendererForDataset(CategoryDataset dataset) { int datasetIndex = indexOf(dataset); if (datasetIndex < 0) { return null; } CategoryItemRenderer renderer = this.renderers.get(datasetIndex); if (renderer == null) { return getRenderer(); } return renderer; } /** * Returns the index of the specified renderer, or {@code -1} if the * renderer is not assigned to this plot. * * @param renderer the renderer ({@code null} permitted). * * @return The renderer index. */ public int getIndexOf(CategoryItemRenderer renderer) { for (Entry entry : this.renderers.entrySet()) { if (entry.getValue() == renderer) { return entry.getKey(); } } return -1; } /** * Returns the dataset rendering order. * * @return The order (never {@code null}). * * @see #setDatasetRenderingOrder(DatasetRenderingOrder) */ public DatasetRenderingOrder getDatasetRenderingOrder() { return this.renderingOrder; } /** * Sets the rendering order and sends a {@link PlotChangeEvent} to all * registered listeners. By default, the plot renders the primary dataset * last (so that the primary dataset overlays the secondary datasets). You * can reverse this if you want to. * * @param order the rendering order ({@code null} not permitted). * * @see #getDatasetRenderingOrder() */ public void setDatasetRenderingOrder(DatasetRenderingOrder order) { Args.nullNotPermitted(order, "order"); this.renderingOrder = order; fireChangeEvent(); } /** * Returns the order in which the columns are rendered. The default value * is {@code SortOrder.ASCENDING}. * * @return The column rendering order (never {@code null}). * * @see #setColumnRenderingOrder(SortOrder) */ public SortOrder getColumnRenderingOrder() { return this.columnRenderingOrder; } /** * Sets the column order in which the items in each dataset should be * rendered and sends a {@link PlotChangeEvent} to all registered * listeners. Note that this affects the order in which items are drawn, * NOT their position in the chart. * * @param order the order ({@code null} not permitted). * * @see #getColumnRenderingOrder() * @see #setRowRenderingOrder(SortOrder) */ public void setColumnRenderingOrder(SortOrder order) { Args.nullNotPermitted(order, "order"); this.columnRenderingOrder = order; fireChangeEvent(); } /** * Returns the order in which the rows should be rendered. The default * value is {@code SortOrder.ASCENDING}. * * @return The order (never {@code null}). * * @see #setRowRenderingOrder(SortOrder) */ public SortOrder getRowRenderingOrder() { return this.rowRenderingOrder; } /** * Sets the row order in which the items in each dataset should be * rendered and sends a {@link PlotChangeEvent} to all registered * listeners. Note that this affects the order in which items are drawn, * NOT their position in the chart. * * @param order the order ({@code null} not permitted). * * @see #getRowRenderingOrder() * @see #setColumnRenderingOrder(SortOrder) */ public void setRowRenderingOrder(SortOrder order) { Args.nullNotPermitted(order, "order"); this.rowRenderingOrder = order; fireChangeEvent(); } /** * Returns the flag that controls whether the domain grid-lines are visible. * * @return The {@code true} or {@code false}. * * @see #setDomainGridlinesVisible(boolean) */ public boolean isDomainGridlinesVisible() { return this.domainGridlinesVisible; } /** * Sets the flag that controls whether or not grid-lines are drawn against * the domain axis. *

* If the flag value changes, a {@link PlotChangeEvent} is sent to all * registered listeners. * * @param visible the new value of the flag. * * @see #isDomainGridlinesVisible() */ public void setDomainGridlinesVisible(boolean visible) { if (this.domainGridlinesVisible != visible) { this.domainGridlinesVisible = visible; fireChangeEvent(); } } /** * Returns the position used for the domain gridlines. * * @return The gridline position (never {@code null}). * * @see #setDomainGridlinePosition(CategoryAnchor) */ public CategoryAnchor getDomainGridlinePosition() { return this.domainGridlinePosition; } /** * Sets the position used for the domain gridlines and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param position the position ({@code null} not permitted). * * @see #getDomainGridlinePosition() */ public void setDomainGridlinePosition(CategoryAnchor position) { Args.nullNotPermitted(position, "position"); this.domainGridlinePosition = position; fireChangeEvent(); } /** * Returns the stroke used to draw grid-lines against the domain axis. * * @return The stroke (never {@code null}). * * @see #setDomainGridlineStroke(Stroke) */ public Stroke getDomainGridlineStroke() { return this.domainGridlineStroke; } /** * Sets the stroke used to draw grid-lines against the domain axis and * sends a {@link PlotChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getDomainGridlineStroke() */ public void setDomainGridlineStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.domainGridlineStroke = stroke; fireChangeEvent(); } /** * Returns the paint used to draw grid-lines against the domain axis. * * @return The paint (never {@code null}). * * @see #setDomainGridlinePaint(Paint) */ public Paint getDomainGridlinePaint() { return this.domainGridlinePaint; } /** * Sets the paint used to draw the grid-lines (if any) against the domain * axis and sends a {@link PlotChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getDomainGridlinePaint() */ public void setDomainGridlinePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.domainGridlinePaint = paint; fireChangeEvent(); } /** * Returns a flag that controls whether or not a zero baseline is * displayed for the range axis. * * @return A boolean. * * @see #setRangeZeroBaselineVisible(boolean) */ public boolean isRangeZeroBaselineVisible() { return this.rangeZeroBaselineVisible; } /** * Sets the flag that controls whether or not the zero baseline is * displayed for the range axis, and sends a {@link PlotChangeEvent} to * all registered listeners. * * @param visible the flag. * * @see #isRangeZeroBaselineVisible() */ public void setRangeZeroBaselineVisible(boolean visible) { this.rangeZeroBaselineVisible = visible; fireChangeEvent(); } /** * Returns the stroke used for the zero baseline against the range axis. * * @return The stroke (never {@code null}). * * @see #setRangeZeroBaselineStroke(Stroke) */ public Stroke getRangeZeroBaselineStroke() { return this.rangeZeroBaselineStroke; } /** * Sets the stroke for the zero baseline for the range axis, * and sends a {@link PlotChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getRangeZeroBaselineStroke() */ public void setRangeZeroBaselineStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.rangeZeroBaselineStroke = stroke; fireChangeEvent(); } /** * Returns the paint for the zero baseline (if any) plotted against the * range axis. * * @return The paint (never {@code null}). * * @see #setRangeZeroBaselinePaint(Paint) */ public Paint getRangeZeroBaselinePaint() { return this.rangeZeroBaselinePaint; } /** * Sets the paint for the zero baseline plotted against the range axis and * sends a {@link PlotChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getRangeZeroBaselinePaint() */ public void setRangeZeroBaselinePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.rangeZeroBaselinePaint = paint; fireChangeEvent(); } /** * Returns the flag that controls whether the range grid-lines are visible. * * @return The flag. * * @see #setRangeGridlinesVisible(boolean) */ public boolean isRangeGridlinesVisible() { return this.rangeGridlinesVisible; } /** * Sets the flag that controls whether or not grid-lines are drawn against * the range axis. If the flag changes value, a {@link PlotChangeEvent} is * sent to all registered listeners. * * @param visible the new value of the flag. * * @see #isRangeGridlinesVisible() */ public void setRangeGridlinesVisible(boolean visible) { if (this.rangeGridlinesVisible != visible) { this.rangeGridlinesVisible = visible; fireChangeEvent(); } } /** * Returns the stroke used to draw the grid-lines against the range axis. * * @return The stroke (never {@code null}). * * @see #setRangeGridlineStroke(Stroke) */ public Stroke getRangeGridlineStroke() { return this.rangeGridlineStroke; } /** * Sets the stroke used to draw the grid-lines against the range axis and * sends a {@link PlotChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getRangeGridlineStroke() */ public void setRangeGridlineStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.rangeGridlineStroke = stroke; fireChangeEvent(); } /** * Returns the paint used to draw the grid-lines against the range axis. * * @return The paint (never {@code null}). * * @see #setRangeGridlinePaint(Paint) */ public Paint getRangeGridlinePaint() { return this.rangeGridlinePaint; } /** * Sets the paint used to draw the grid lines against the range axis and * sends a {@link PlotChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getRangeGridlinePaint() */ public void setRangeGridlinePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.rangeGridlinePaint = paint; fireChangeEvent(); } /** * Returns {@code true} if the range axis minor grid is visible, and * {@code false} otherwise. * * @return A boolean. * * @see #setRangeMinorGridlinesVisible(boolean) */ public boolean isRangeMinorGridlinesVisible() { return this.rangeMinorGridlinesVisible; } /** * Sets the flag that controls whether or not the range axis minor grid * lines are visible. *

* If the flag value is changed, a {@link PlotChangeEvent} is sent to all * registered listeners. * * @param visible the new value of the flag. * * @see #isRangeMinorGridlinesVisible() */ public void setRangeMinorGridlinesVisible(boolean visible) { if (this.rangeMinorGridlinesVisible != visible) { this.rangeMinorGridlinesVisible = visible; fireChangeEvent(); } } /** * Returns the stroke for the minor grid lines (if any) plotted against the * range axis. * * @return The stroke (never {@code null}). * * @see #setRangeMinorGridlineStroke(Stroke) */ public Stroke getRangeMinorGridlineStroke() { return this.rangeMinorGridlineStroke; } /** * Sets the stroke for the minor grid lines plotted against the range axis, * and sends a {@link PlotChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getRangeMinorGridlineStroke() */ public void setRangeMinorGridlineStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.rangeMinorGridlineStroke = stroke; fireChangeEvent(); } /** * Returns the paint for the minor grid lines (if any) plotted against the * range axis. * * @return The paint (never {@code null}). * * @see #setRangeMinorGridlinePaint(Paint) */ public Paint getRangeMinorGridlinePaint() { return this.rangeMinorGridlinePaint; } /** * Sets the paint for the minor grid lines plotted against the range axis * and sends a {@link PlotChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getRangeMinorGridlinePaint() */ public void setRangeMinorGridlinePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.rangeMinorGridlinePaint = paint; fireChangeEvent(); } /** * Returns the fixed legend items, if any. * * @return The legend items (possibly {@code null}). * * @see #setFixedLegendItems(LegendItemCollection) */ public LegendItemCollection getFixedLegendItems() { return this.fixedLegendItems; } /** * Sets the fixed legend items for the plot. Leave this set to * {@code null} if you prefer the legend items to be created * automatically. * * @param items the legend items ({@code null} permitted). * * @see #getFixedLegendItems() */ public void setFixedLegendItems(LegendItemCollection items) { this.fixedLegendItems = items; fireChangeEvent(); } /** * Returns the legend items for the plot. By default, this method creates * a legend item for each series in each of the datasets. You can change * this behaviour by overriding this method. * * @return The legend items. */ @Override public LegendItemCollection getLegendItems() { if (this.fixedLegendItems != null) { return this.fixedLegendItems; } LegendItemCollection result = new LegendItemCollection(); // get the legend items for the datasets... for (CategoryDataset dataset: this.datasets.values()) { if (dataset != null) { int datasetIndex = indexOf(dataset); CategoryItemRenderer renderer = getRenderer(datasetIndex); if (renderer != null) { result.addAll(renderer.getLegendItems()); } } } return result; } /** * Handles a 'click' on the plot by updating the anchor value. * * @param x x-coordinate of the click (in Java2D space). * @param y y-coordinate of the click (in Java2D space). * @param info information about the plot's dimensions. * */ @Override public void handleClick(int x, int y, PlotRenderingInfo info) { Rectangle2D dataArea = info.getDataArea(); if (dataArea.contains(x, y)) { // set the anchor value for the range axis... double java2D = 0.0; if (this.orientation == PlotOrientation.HORIZONTAL) { java2D = x; } else if (this.orientation == PlotOrientation.VERTICAL) { java2D = y; } RectangleEdge edge = Plot.resolveRangeAxisLocation( getRangeAxisLocation(), this.orientation); double value = getRangeAxis().java2DToValue( java2D, info.getDataArea(), edge); setAnchorValue(value); setRangeCrosshairValue(value); } } /** * Zooms (in or out) on the plot's value axis. *

* If the value 0.0 is passed in as the zoom percent, the auto-range * calculation for the axis is restored (which sets the range to include * the minimum and maximum data values, thus displaying all the data). * * @param percent the zoom amount. */ @Override public void zoom(double percent) { if (percent > 0.0) { double range = getRangeAxis().getRange().getLength(); double scaledRange = range * percent; getRangeAxis().setRange(this.anchorValue - scaledRange / 2.0, this.anchorValue + scaledRange / 2.0); } else { getRangeAxis().setAutoRange(true); } } /** * Receives notification of a change to an {@link Annotation} added to * this plot. * * @param event information about the event (not used here). */ @Override public void annotationChanged(AnnotationChangeEvent event) { if (getParent() != null) { getParent().annotationChanged(event); } else { PlotChangeEvent e = new PlotChangeEvent(this); notifyListeners(e); } } /** * Receives notification of a change to the plot's dataset. *

* The range axis bounds will be recalculated if necessary. * * @param event information about the event (not used here). */ @Override public void datasetChanged(DatasetChangeEvent event) { for (ValueAxis yAxis : this.rangeAxes.values()) { if (yAxis != null) { yAxis.configure(); } } if (getParent() != null) { getParent().datasetChanged(event); } else { PlotChangeEvent e = new PlotChangeEvent(this); e.setType(ChartChangeEventType.DATASET_UPDATED); notifyListeners(e); } } /** * Receives notification of a renderer change event. * * @param event the event. */ @Override public void rendererChanged(RendererChangeEvent event) { Plot parent = getParent(); if (parent != null) { if (parent instanceof RendererChangeListener) { RendererChangeListener rcl = (RendererChangeListener) parent; rcl.rendererChanged(event); } else { // this should never happen with the existing code, but throw // an exception in case future changes make it possible... throw new RuntimeException( "The renderer has changed and I don't know what to do!"); } } else { configureRangeAxes(); PlotChangeEvent e = new PlotChangeEvent(this); notifyListeners(e); } } /** * Adds a marker for display (in the foreground) against the domain axis and * sends a {@link PlotChangeEvent} to all registered listeners. Typically a * marker will be drawn by the renderer as a line perpendicular to the * domain axis, however this is entirely up to the renderer. * * @param marker the marker ({@code null} not permitted). * * @see #removeDomainMarker(Marker) */ public void addDomainMarker(CategoryMarker marker) { addDomainMarker(marker, Layer.FOREGROUND); } /** * Adds a marker for display against the domain axis and sends a * {@link PlotChangeEvent} to all registered listeners. Typically a marker * will be drawn by the renderer as a line perpendicular to the domain * axis, however this is entirely up to the renderer. * * @param marker the marker ({@code null} not permitted). * @param layer the layer (foreground or background) ({@code null} * not permitted). * * @see #removeDomainMarker(Marker, Layer) */ public void addDomainMarker(CategoryMarker marker, Layer layer) { addDomainMarker(0, marker, layer); } /** * Adds a marker for display by a particular renderer and sends a * {@link PlotChangeEvent} to all registered listeners. *

* Typically a marker will be drawn by the renderer as a line perpendicular * to a domain axis, however this is entirely up to the renderer. * * @param index the renderer index. * @param marker the marker ({@code null} not permitted). * @param layer the layer ({@code null} not permitted). * * @see #removeDomainMarker(int, Marker, Layer) */ public void addDomainMarker(int index, CategoryMarker marker, Layer layer) { addDomainMarker(index, marker, layer, true); } /** * Adds a marker for display by a particular renderer and, if requested, * sends a {@link PlotChangeEvent} to all registered listeners. *

* Typically a marker will be drawn by the renderer as a line perpendicular * to a domain axis, however this is entirely up to the renderer. * * @param index the renderer index. * @param marker the marker ({@code null} not permitted). * @param layer the layer ({@code null} not permitted). * @param notify notify listeners? * * @see #removeDomainMarker(int, Marker, Layer, boolean) */ public void addDomainMarker(int index, CategoryMarker marker, Layer layer, boolean notify) { Args.nullNotPermitted(marker, "marker"); Args.nullNotPermitted(layer, "layer"); Collection markers; if (layer == Layer.FOREGROUND) { markers = this.foregroundDomainMarkers.get(index); if (markers == null) { markers = new java.util.ArrayList(); this.foregroundDomainMarkers.put(index, markers); } markers.add(marker); } else if (layer == Layer.BACKGROUND) { markers = this.backgroundDomainMarkers.get(index); if (markers == null) { markers = new java.util.ArrayList(); this.backgroundDomainMarkers.put(index, markers); } markers.add(marker); } marker.addChangeListener(this); if (notify) { fireChangeEvent(); } } /** * Clears all the domain markers for the plot and sends a * {@link PlotChangeEvent} to all registered listeners. * * @see #clearRangeMarkers() */ public void clearDomainMarkers() { if (this.backgroundDomainMarkers != null) { Set keys = this.backgroundDomainMarkers.keySet(); Iterator iterator = keys.iterator(); while (iterator.hasNext()) { Integer key = (Integer) iterator.next(); clearDomainMarkers(key); } this.backgroundDomainMarkers.clear(); } if (this.foregroundDomainMarkers != null) { Set keys = this.foregroundDomainMarkers.keySet(); Iterator iterator = keys.iterator(); while (iterator.hasNext()) { Integer key = (Integer) iterator.next(); clearDomainMarkers(key); } this.foregroundDomainMarkers.clear(); } fireChangeEvent(); } /** * Returns the list of domain markers (read only) for the specified layer. * * @param layer the layer (foreground or background). * * @return The list of domain markers. */ public Collection getDomainMarkers(Layer layer) { return getDomainMarkers(0, layer); } /** * Returns a collection of domain markers for a particular renderer and * layer. * * @param index the renderer index. * @param layer the layer. * * @return A collection of markers (possibly {@code null}). */ public Collection getDomainMarkers(int index, Layer layer) { Collection result = null; Integer key = index; if (layer == Layer.FOREGROUND) { result = this.foregroundDomainMarkers.get(key); } else if (layer == Layer.BACKGROUND) { result = this.backgroundDomainMarkers.get(key); } if (result != null) { result = Collections.unmodifiableCollection(result); } return result; } /** * Clears all the domain markers for the specified renderer. * * @param index the renderer index. * * @see #clearRangeMarkers(int) */ public void clearDomainMarkers(int index) { Integer key = index; if (this.backgroundDomainMarkers != null) { Collection markers = this.backgroundDomainMarkers.get(key); if (markers != null) { Iterator iterator = markers.iterator(); while (iterator.hasNext()) { Marker m = (Marker) iterator.next(); m.removeChangeListener(this); } markers.clear(); } } if (this.foregroundDomainMarkers != null) { Collection markers = this.foregroundDomainMarkers.get(key); if (markers != null) { Iterator iterator = markers.iterator(); while (iterator.hasNext()) { Marker m = (Marker) iterator.next(); m.removeChangeListener(this); } markers.clear(); } } fireChangeEvent(); } /** * Removes a marker for the domain axis and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param marker the marker. * * @return A boolean indicating whether or not the marker was actually * removed. */ public boolean removeDomainMarker(Marker marker) { return removeDomainMarker(marker, Layer.FOREGROUND); } /** * Removes a marker for the domain axis in the specified layer and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param marker the marker ({@code null} not permitted). * @param layer the layer (foreground or background). * * @return A boolean indicating whether or not the marker was actually * removed. */ public boolean removeDomainMarker(Marker marker, Layer layer) { return removeDomainMarker(0, marker, layer); } /** * Removes a marker for a specific dataset/renderer and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param index the dataset/renderer index. * @param marker the marker. * @param layer the layer (foreground or background). * * @return A boolean indicating whether or not the marker was actually * removed. */ public boolean removeDomainMarker(int index, Marker marker, Layer layer) { return removeDomainMarker(index, marker, layer, true); } /** * Removes a marker for a specific dataset/renderer and, if requested, * sends a {@link PlotChangeEvent} to all registered listeners. * * @param index the dataset/renderer index. * @param marker the marker. * @param layer the layer (foreground or background). * @param notify notify listeners? * * @return A boolean indicating whether or not the marker was actually * removed. */ public boolean removeDomainMarker(int index, Marker marker, Layer layer, boolean notify) { ArrayList markers; if (layer == Layer.FOREGROUND) { markers = (ArrayList) this.foregroundDomainMarkers.get(index); } else { markers = (ArrayList) this.backgroundDomainMarkers.get(index); } if (markers == null) { return false; } boolean removed = markers.remove(marker); if (removed && notify) { fireChangeEvent(); } return removed; } /** * Adds a marker for display (in the foreground) against the range axis and * sends a {@link PlotChangeEvent} to all registered listeners. Typically a * marker will be drawn by the renderer as a line perpendicular to the * range axis, however this is entirely up to the renderer. * * @param marker the marker ({@code null} not permitted). * * @see #removeRangeMarker(Marker) */ public void addRangeMarker(Marker marker) { addRangeMarker(marker, Layer.FOREGROUND); } /** * Adds a marker for display against the range axis and sends a * {@link PlotChangeEvent} to all registered listeners. Typically a marker * will be drawn by the renderer as a line perpendicular to the range axis, * however this is entirely up to the renderer. * * @param marker the marker ({@code null} not permitted). * @param layer the layer (foreground or background) ({@code null} * not permitted). * * @see #removeRangeMarker(Marker, Layer) */ public void addRangeMarker(Marker marker, Layer layer) { addRangeMarker(0, marker, layer); } /** * Adds a marker for display by a particular renderer and sends a * {@link PlotChangeEvent} to all registered listeners. *

* Typically a marker will be drawn by the renderer as a line perpendicular * to a range axis, however this is entirely up to the renderer. * * @param index the renderer index. * @param marker the marker. * @param layer the layer. * * @see #removeRangeMarker(int, Marker, Layer) */ public void addRangeMarker(int index, Marker marker, Layer layer) { addRangeMarker(index, marker, layer, true); } /** * Adds a marker for display by a particular renderer and sends a * {@link PlotChangeEvent} to all registered listeners. *

* Typically a marker will be drawn by the renderer as a line perpendicular * to a range axis, however this is entirely up to the renderer. * * @param index the renderer index. * @param marker the marker. * @param layer the layer. * @param notify notify listeners? * * @see #removeRangeMarker(int, Marker, Layer, boolean) */ public void addRangeMarker(int index, Marker marker, Layer layer, boolean notify) { Collection markers; if (layer == Layer.FOREGROUND) { markers = this.foregroundRangeMarkers.get(index); if (markers == null) { markers = new java.util.ArrayList(); this.foregroundRangeMarkers.put(index, markers); } markers.add(marker); } else if (layer == Layer.BACKGROUND) { markers = this.backgroundRangeMarkers.get(index); if (markers == null) { markers = new java.util.ArrayList(); this.backgroundRangeMarkers.put(index, markers); } markers.add(marker); } marker.addChangeListener(this); if (notify) { fireChangeEvent(); } } /** * Clears all the range markers for the plot and sends a * {@link PlotChangeEvent} to all registered listeners. * * @see #clearDomainMarkers() */ public void clearRangeMarkers() { if (this.backgroundRangeMarkers != null) { Set keys = this.backgroundRangeMarkers.keySet(); Iterator iterator = keys.iterator(); while (iterator.hasNext()) { Integer key = (Integer) iterator.next(); clearRangeMarkers(key); } this.backgroundRangeMarkers.clear(); } if (this.foregroundRangeMarkers != null) { Set keys = this.foregroundRangeMarkers.keySet(); Iterator iterator = keys.iterator(); while (iterator.hasNext()) { Integer key = (Integer) iterator.next(); clearRangeMarkers(key); } this.foregroundRangeMarkers.clear(); } fireChangeEvent(); } /** * Returns the list of range markers (read only) for the specified layer. * * @param layer the layer (foreground or background). * * @return The list of range markers. * * @see #getRangeMarkers(int, Layer) */ public Collection getRangeMarkers(Layer layer) { return getRangeMarkers(0, layer); } /** * Returns a collection of range markers for a particular renderer and * layer. * * @param index the renderer index. * @param layer the layer. * * @return A collection of markers (possibly {@code null}). */ public Collection getRangeMarkers(int index, Layer layer) { Collection result = null; if (layer == Layer.FOREGROUND) { result = this.foregroundRangeMarkers.get(index); } else if (layer == Layer.BACKGROUND) { result = this.backgroundRangeMarkers.get(index); } if (result != null) { result = Collections.unmodifiableCollection(result); } return result; } /** * Clears all the range markers for the specified renderer. * * @param index the renderer index. * * @see #clearDomainMarkers(int) */ public void clearRangeMarkers(int index) { Integer key = index; if (this.backgroundRangeMarkers != null) { Collection markers = this.backgroundRangeMarkers.get(key); if (markers != null) { Iterator iterator = markers.iterator(); while (iterator.hasNext()) { Marker m = (Marker) iterator.next(); m.removeChangeListener(this); } markers.clear(); } } if (this.foregroundRangeMarkers != null) { Collection markers = this.foregroundRangeMarkers.get(key); if (markers != null) { Iterator iterator = markers.iterator(); while (iterator.hasNext()) { Marker m = (Marker) iterator.next(); m.removeChangeListener(this); } markers.clear(); } } fireChangeEvent(); } /** * Removes a marker for the range axis and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param marker the marker. * * @return A boolean indicating whether or not the marker was actually * removed. * * @see #addRangeMarker(Marker) */ public boolean removeRangeMarker(Marker marker) { return removeRangeMarker(marker, Layer.FOREGROUND); } /** * Removes a marker for the range axis in the specified layer and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param marker the marker ({@code null} not permitted). * @param layer the layer (foreground or background). * * @return A boolean indicating whether or not the marker was actually * removed. * * @see #addRangeMarker(Marker, Layer) */ public boolean removeRangeMarker(Marker marker, Layer layer) { return removeRangeMarker(0, marker, layer); } /** * Removes a marker for a specific dataset/renderer and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param index the dataset/renderer index. * @param marker the marker. * @param layer the layer (foreground or background). * * @return A boolean indicating whether or not the marker was actually * removed. * * @see #addRangeMarker(int, Marker, Layer) */ public boolean removeRangeMarker(int index, Marker marker, Layer layer) { return removeRangeMarker(index, marker, layer, true); } /** * Removes a marker for a specific dataset/renderer and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param index the dataset/renderer index. * @param marker the marker. * @param layer the layer (foreground or background). * @param notify notify listeners. * * @return A boolean indicating whether or not the marker was actually * removed. * * @see #addRangeMarker(int, Marker, Layer, boolean) */ public boolean removeRangeMarker(int index, Marker marker, Layer layer, boolean notify) { Args.nullNotPermitted(marker, "marker"); ArrayList markers; if (layer == Layer.FOREGROUND) { markers = (ArrayList) this.foregroundRangeMarkers.get(index); } else { markers = (ArrayList) this.backgroundRangeMarkers.get(index); } if (markers == null) { return false; } boolean removed = markers.remove(marker); if (removed && notify) { fireChangeEvent(); } return removed; } /** * Returns the flag that controls whether or not the domain crosshair is * displayed by the plot. * * @return A boolean. * * @see #setDomainCrosshairVisible(boolean) */ public boolean isDomainCrosshairVisible() { return this.domainCrosshairVisible; } /** * Sets the flag that controls whether or not the domain crosshair is * displayed by the plot, and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param flag the new flag value. * * @see #isDomainCrosshairVisible() * @see #setRangeCrosshairVisible(boolean) */ public void setDomainCrosshairVisible(boolean flag) { if (this.domainCrosshairVisible != flag) { this.domainCrosshairVisible = flag; fireChangeEvent(); } } /** * Returns the row key for the domain crosshair. * * @return The row key. */ public Comparable getDomainCrosshairRowKey() { return this.domainCrosshairRowKey; } /** * Sets the row key for the domain crosshair and sends a * {PlotChangeEvent} to all registered listeners. * * @param key the key. */ public void setDomainCrosshairRowKey(Comparable key) { setDomainCrosshairRowKey(key, true); } /** * Sets the row key for the domain crosshair and, if requested, sends a * {PlotChangeEvent} to all registered listeners. * * @param key the key. * @param notify notify listeners? */ public void setDomainCrosshairRowKey(Comparable key, boolean notify) { this.domainCrosshairRowKey = key; if (notify) { fireChangeEvent(); } } /** * Returns the column key for the domain crosshair. * * @return The column key. */ public Comparable getDomainCrosshairColumnKey() { return this.domainCrosshairColumnKey; } /** * Sets the column key for the domain crosshair and sends * a {@link PlotChangeEvent} to all registered listeners. * * @param key the key. */ public void setDomainCrosshairColumnKey(Comparable key) { setDomainCrosshairColumnKey(key, true); } /** * Sets the column key for the domain crosshair and, if requested, sends * a {@link PlotChangeEvent} to all registered listeners. * * @param key the key. * @param notify notify listeners? */ public void setDomainCrosshairColumnKey(Comparable key, boolean notify) { this.domainCrosshairColumnKey = key; if (notify) { fireChangeEvent(); } } /** * Returns the dataset index for the crosshair. * * @return The dataset index. */ public int getCrosshairDatasetIndex() { return this.crosshairDatasetIndex; } /** * Sets the dataset index for the crosshair and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param index the index. */ public void setCrosshairDatasetIndex(int index) { setCrosshairDatasetIndex(index, true); } /** * Sets the dataset index for the crosshair and, if requested, sends a * {@link PlotChangeEvent} to all registered listeners. * * @param index the index. * @param notify notify listeners? */ public void setCrosshairDatasetIndex(int index, boolean notify) { this.crosshairDatasetIndex = index; if (notify) { fireChangeEvent(); } } /** * Returns the paint used to draw the domain crosshair. * * @return The paint (never {@code null}). * * @see #setDomainCrosshairPaint(Paint) * @see #getDomainCrosshairStroke() */ public Paint getDomainCrosshairPaint() { return this.domainCrosshairPaint; } /** * Sets the paint used to draw the domain crosshair. * * @param paint the paint ({@code null} not permitted). * * @see #getDomainCrosshairPaint() */ public void setDomainCrosshairPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.domainCrosshairPaint = paint; fireChangeEvent(); } /** * Returns the stroke used to draw the domain crosshair. * * @return The stroke (never {@code null}). * * @see #setDomainCrosshairStroke(Stroke) * @see #getDomainCrosshairPaint() */ public Stroke getDomainCrosshairStroke() { return this.domainCrosshairStroke; } /** * Sets the stroke used to draw the domain crosshair, and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getDomainCrosshairStroke() */ public void setDomainCrosshairStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.domainCrosshairStroke = stroke; } /** * Returns a flag indicating whether or not the range crosshair is visible. * * @return The flag. * * @see #setRangeCrosshairVisible(boolean) */ public boolean isRangeCrosshairVisible() { return this.rangeCrosshairVisible; } /** * Sets the flag indicating whether or not the range crosshair is visible. * * @param flag the new value of the flag. * * @see #isRangeCrosshairVisible() */ public void setRangeCrosshairVisible(boolean flag) { if (this.rangeCrosshairVisible != flag) { this.rangeCrosshairVisible = flag; fireChangeEvent(); } } /** * Returns a flag indicating whether or not the crosshair should "lock-on" * to actual data values. * * @return The flag. * * @see #setRangeCrosshairLockedOnData(boolean) */ public boolean isRangeCrosshairLockedOnData() { return this.rangeCrosshairLockedOnData; } /** * Sets the flag indicating whether or not the range crosshair should * "lock-on" to actual data values, and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param flag the flag. * * @see #isRangeCrosshairLockedOnData() */ public void setRangeCrosshairLockedOnData(boolean flag) { if (this.rangeCrosshairLockedOnData != flag) { this.rangeCrosshairLockedOnData = flag; fireChangeEvent(); } } /** * Returns the range crosshair value. * * @return The value. * * @see #setRangeCrosshairValue(double) */ public double getRangeCrosshairValue() { return this.rangeCrosshairValue; } /** * Sets the range crosshair value and, if the crosshair is visible, sends * a {@link PlotChangeEvent} to all registered listeners. * * @param value the new value. * * @see #getRangeCrosshairValue() */ public void setRangeCrosshairValue(double value) { setRangeCrosshairValue(value, true); } /** * Sets the range crosshair value and, if requested, sends a * {@link PlotChangeEvent} to all registered listeners (but only if the * crosshair is visible). * * @param value the new value. * @param notify a flag that controls whether or not listeners are * notified. * * @see #getRangeCrosshairValue() */ public void setRangeCrosshairValue(double value, boolean notify) { this.rangeCrosshairValue = value; if (isRangeCrosshairVisible() && notify) { fireChangeEvent(); } } /** * Returns the pen-style ({@code Stroke}) used to draw the crosshair * (if visible). * * @return The crosshair stroke (never {@code null}). * * @see #setRangeCrosshairStroke(Stroke) * @see #isRangeCrosshairVisible() * @see #getRangeCrosshairPaint() */ public Stroke getRangeCrosshairStroke() { return this.rangeCrosshairStroke; } /** * Sets the pen-style ({@code Stroke}) used to draw the range * crosshair (if visible), and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param stroke the new crosshair stroke ({@code null} not * permitted). * * @see #getRangeCrosshairStroke() */ public void setRangeCrosshairStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.rangeCrosshairStroke = stroke; fireChangeEvent(); } /** * Returns the paint used to draw the range crosshair. * * @return The paint (never {@code null}). * * @see #setRangeCrosshairPaint(Paint) * @see #isRangeCrosshairVisible() * @see #getRangeCrosshairStroke() */ public Paint getRangeCrosshairPaint() { return this.rangeCrosshairPaint; } /** * Sets the paint used to draw the range crosshair (if visible) and * sends a {@link PlotChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getRangeCrosshairPaint() */ public void setRangeCrosshairPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.rangeCrosshairPaint = paint; fireChangeEvent(); } /** * Returns the list of annotations. * * @return The list of annotations (never {@code null}). * * @see #addAnnotation(CategoryAnnotation) * @see #clearAnnotations() */ public List getAnnotations() { return this.annotations; } /** * Adds an annotation to the plot and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param annotation the annotation ({@code null} not permitted). * * @see #removeAnnotation(CategoryAnnotation) */ public void addAnnotation(CategoryAnnotation annotation) { addAnnotation(annotation, true); } /** * Adds an annotation to the plot and, if requested, sends a * {@link PlotChangeEvent} to all registered listeners. * * @param annotation the annotation ({@code null} not permitted). * @param notify notify listeners? */ public void addAnnotation(CategoryAnnotation annotation, boolean notify) { Args.nullNotPermitted(annotation, "annotation"); this.annotations.add(annotation); annotation.addChangeListener(this); if (notify) { fireChangeEvent(); } } /** * Removes an annotation from the plot and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param annotation the annotation ({@code null} not permitted). * * @return A boolean (indicates whether or not the annotation was removed). * * @see #addAnnotation(CategoryAnnotation) */ public boolean removeAnnotation(CategoryAnnotation annotation) { return removeAnnotation(annotation, true); } /** * Removes an annotation from the plot and, if requested, sends a * {@link PlotChangeEvent} to all registered listeners. * * @param annotation the annotation ({@code null} not permitted). * @param notify notify listeners? * * @return A boolean (indicates whether or not the annotation was removed). */ public boolean removeAnnotation(CategoryAnnotation annotation, boolean notify) { Args.nullNotPermitted(annotation, "annotation"); boolean removed = this.annotations.remove(annotation); annotation.removeChangeListener(this); if (removed && notify) { fireChangeEvent(); } return removed; } /** * Clears all the annotations and sends a {@link PlotChangeEvent} to all * registered listeners. */ public void clearAnnotations() { for (int i = 0; i < this.annotations.size(); i++) { CategoryAnnotation annotation = this.annotations.get(i); annotation.removeChangeListener(this); } this.annotations.clear(); fireChangeEvent(); } /** * Returns the shadow generator for the plot, if any. * * @return The shadow generator (possibly {@code null}). */ public ShadowGenerator getShadowGenerator() { return this.shadowGenerator; } /** * Sets the shadow generator for the plot and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param generator the generator ({@code null} permitted). */ public void setShadowGenerator(ShadowGenerator generator) { this.shadowGenerator = generator; fireChangeEvent(); } /** * Calculates the space required for the domain axis/axes. * * @param g2 the graphics device. * @param plotArea the plot area. * @param space a carrier for the result ({@code null} permitted). * * @return The required space. */ protected AxisSpace calculateDomainAxisSpace(Graphics2D g2, Rectangle2D plotArea, AxisSpace space) { if (space == null) { space = new AxisSpace(); } // reserve some space for the domain axis... if (this.fixedDomainAxisSpace != null) { if (this.orientation.isHorizontal()) { space.ensureAtLeast( this.fixedDomainAxisSpace.getLeft(), RectangleEdge.LEFT); space.ensureAtLeast(this.fixedDomainAxisSpace.getRight(), RectangleEdge.RIGHT); } else if (this.orientation.isVertical()) { space.ensureAtLeast(this.fixedDomainAxisSpace.getTop(), RectangleEdge.TOP); space.ensureAtLeast(this.fixedDomainAxisSpace.getBottom(), RectangleEdge.BOTTOM); } } else { // reserve space for the primary domain axis... RectangleEdge domainEdge = Plot.resolveDomainAxisLocation( getDomainAxisLocation(), this.orientation); if (this.drawSharedDomainAxis) { space = getDomainAxis().reserveSpace(g2, this, plotArea, domainEdge, space); } // reserve space for any domain axes... for (CategoryAxis xAxis : this.domainAxes.values()) { if (xAxis != null) { int i = getDomainAxisIndex(xAxis); RectangleEdge edge = getDomainAxisEdge(i); space = xAxis.reserveSpace(g2, this, plotArea, edge, space); } } } return space; } /** * Calculates the space required for the range axis/axes. * * @param g2 the graphics device. * @param plotArea the plot area. * @param space a carrier for the result ({@code null} permitted). * * @return The required space. */ protected AxisSpace calculateRangeAxisSpace(Graphics2D g2, Rectangle2D plotArea, AxisSpace space) { if (space == null) { space = new AxisSpace(); } // reserve some space for the range axis... if (this.fixedRangeAxisSpace != null) { if (this.orientation.isHorizontal()) { space.ensureAtLeast(this.fixedRangeAxisSpace.getTop(), RectangleEdge.TOP); space.ensureAtLeast(this.fixedRangeAxisSpace.getBottom(), RectangleEdge.BOTTOM); } else if (this.orientation == PlotOrientation.VERTICAL) { space.ensureAtLeast(this.fixedRangeAxisSpace.getLeft(), RectangleEdge.LEFT); space.ensureAtLeast(this.fixedRangeAxisSpace.getRight(), RectangleEdge.RIGHT); } } else { // reserve space for the range axes (if any)... for (ValueAxis yAxis : this.rangeAxes.values()) { if (yAxis != null) { int i = findRangeAxisIndex(yAxis); RectangleEdge edge = getRangeAxisEdge(i); space = yAxis.reserveSpace(g2, this, plotArea, edge, space); } } } return space; } /** * Trims a rectangle to integer coordinates. * * @param rect the incoming rectangle. * * @return A rectangle with integer coordinates. */ private Rectangle integerise(Rectangle2D rect) { int x0 = (int) Math.ceil(rect.getMinX()); int y0 = (int) Math.ceil(rect.getMinY()); int x1 = (int) Math.floor(rect.getMaxX()); int y1 = (int) Math.floor(rect.getMaxY()); return new Rectangle(x0, y0, (x1 - x0), (y1 - y0)); } /** * Calculates the space required for the axes. * * @param g2 the graphics device. * @param plotArea the plot area. * * @return The space required for the axes. */ protected AxisSpace calculateAxisSpace(Graphics2D g2, Rectangle2D plotArea) { AxisSpace space = new AxisSpace(); space = calculateRangeAxisSpace(g2, plotArea, space); space = calculateDomainAxisSpace(g2, plotArea, space); return space; } /** * Draws the plot on a Java 2D graphics device (such as the screen or a * printer). *

* At your option, you may supply an instance of {@link PlotRenderingInfo}. * If you do, it will be populated with information about the drawing, * including various plot dimensions and tooltip info. * * @param g2 the graphics device. * @param area the area within which the plot (including axes) should * be drawn. * @param anchor the anchor point ({@code null} permitted). * @param parentState the state from the parent plot, if there is one. * @param state collects info as the chart is drawn (possibly * {@code null}). */ @Override public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, PlotState parentState, PlotRenderingInfo state) { // if the plot area is too small, just return... boolean b1 = (area.getWidth() <= MINIMUM_WIDTH_TO_DRAW); boolean b2 = (area.getHeight() <= MINIMUM_HEIGHT_TO_DRAW); if (b1 || b2) { return; } // record the plot area... if (state == null) { // if the incoming state is null, no information will be passed // back to the caller - but we create a temporary state to record // the plot area, since that is used later by the axes state = new PlotRenderingInfo(null); } state.setPlotArea(area); // adjust the drawing area for the plot insets (if any)... RectangleInsets insets = getInsets(); insets.trim(area); // calculate the data area... AxisSpace space = calculateAxisSpace(g2, area); Rectangle2D dataArea = space.shrink(area, null); this.axisOffset.trim(dataArea); dataArea = integerise(dataArea); if (dataArea.isEmpty()) { return; } state.setDataArea(dataArea); createAndAddEntity((Rectangle2D) dataArea.clone(), state, null, null); // if there is a renderer, it draws the background, otherwise use the // default background... if (getRenderer() != null) { getRenderer().drawBackground(g2, this, dataArea); } else { drawBackground(g2, dataArea); } Map axisStateMap = drawAxes(g2, area, dataArea, state); // the anchor point is typically the point where the mouse last // clicked - the crosshairs will be driven off this point... if (anchor != null && !dataArea.contains(anchor)) { anchor = ShapeUtils.getPointInRectangle(anchor.getX(), anchor.getY(), dataArea); } CategoryCrosshairState crosshairState = new CategoryCrosshairState(); crosshairState.setCrosshairDistance(Double.POSITIVE_INFINITY); crosshairState.setAnchor(anchor); // specify the anchor X and Y coordinates in Java2D space, for the // cases where these are not updated during rendering (i.e. no lock // on data) crosshairState.setAnchorX(Double.NaN); crosshairState.setAnchorY(Double.NaN); if (anchor != null) { ValueAxis rangeAxis = getRangeAxis(); if (rangeAxis != null) { double y; if (getOrientation() == PlotOrientation.VERTICAL) { y = rangeAxis.java2DToValue(anchor.getY(), dataArea, getRangeAxisEdge()); } else { y = rangeAxis.java2DToValue(anchor.getX(), dataArea, getRangeAxisEdge()); } crosshairState.setAnchorY(y); } } crosshairState.setRowKey(getDomainCrosshairRowKey()); crosshairState.setColumnKey(getDomainCrosshairColumnKey()); crosshairState.setCrosshairY(getRangeCrosshairValue()); // don't let anyone draw outside the data area Shape savedClip = g2.getClip(); g2.clip(dataArea); drawDomainGridlines(g2, dataArea); AxisState rangeAxisState = (AxisState) axisStateMap.get(getRangeAxis()); if (rangeAxisState == null) { if (parentState != null) { rangeAxisState = (AxisState) parentState.getSharedAxisStates() .get(getRangeAxis()); } } if (rangeAxisState != null) { drawRangeGridlines(g2, dataArea, rangeAxisState.getTicks()); drawZeroRangeBaseline(g2, dataArea); } Graphics2D savedG2 = g2; BufferedImage dataImage = null; boolean suppressShadow = Boolean.TRUE.equals(g2.getRenderingHint( JFreeChart.KEY_SUPPRESS_SHADOW_GENERATION)); if (this.shadowGenerator != null && !suppressShadow) { dataImage = new BufferedImage((int) dataArea.getWidth(), (int)dataArea.getHeight(), BufferedImage.TYPE_INT_ARGB); g2 = dataImage.createGraphics(); g2.translate(-dataArea.getX(), -dataArea.getY()); g2.setRenderingHints(savedG2.getRenderingHints()); } // draw the markers... for (CategoryItemRenderer renderer : this.renderers.values()) { int i = getIndexOf(renderer); drawDomainMarkers(g2, dataArea, i, Layer.BACKGROUND); } for (CategoryItemRenderer renderer : this.renderers.values()) { int i = getIndexOf(renderer); drawRangeMarkers(g2, dataArea, i, Layer.BACKGROUND); } // now render data items... boolean foundData = false; // set up the alpha-transparency... Composite originalComposite = g2.getComposite(); g2.setComposite(AlphaComposite.getInstance( AlphaComposite.SRC_OVER, getForegroundAlpha())); DatasetRenderingOrder order = getDatasetRenderingOrder(); List datasetIndices = getDatasetIndices(order); for (int i : datasetIndices) { foundData = render(g2, dataArea, i, state, crosshairState) || foundData; } // draw the foreground markers... List rendererIndices = getRendererIndices(order); for (int i : rendererIndices) { drawDomainMarkers(g2, dataArea, i, Layer.FOREGROUND); } for (int i : rendererIndices) { drawRangeMarkers(g2, dataArea, i, Layer.FOREGROUND); } // draw the annotations (if any)... drawAnnotations(g2, dataArea); if (this.shadowGenerator != null && !suppressShadow) { BufferedImage shadowImage = this.shadowGenerator.createDropShadow( dataImage); g2 = savedG2; g2.drawImage(shadowImage, (int) dataArea.getX() + this.shadowGenerator.calculateOffsetX(), (int) dataArea.getY() + this.shadowGenerator.calculateOffsetY(), null); g2.drawImage(dataImage, (int) dataArea.getX(), (int) dataArea.getY(), null); } g2.setClip(savedClip); g2.setComposite(originalComposite); if (!foundData) { drawNoDataMessage(g2, dataArea); } int datasetIndex = crosshairState.getDatasetIndex(); setCrosshairDatasetIndex(datasetIndex, false); // draw domain crosshair if required... Comparable rowKey = crosshairState.getRowKey(); Comparable columnKey = crosshairState.getColumnKey(); setDomainCrosshairRowKey(rowKey, false); setDomainCrosshairColumnKey(columnKey, false); if (isDomainCrosshairVisible() && columnKey != null) { Paint paint = getDomainCrosshairPaint(); Stroke stroke = getDomainCrosshairStroke(); drawDomainCrosshair(g2, dataArea, this.orientation, datasetIndex, rowKey, columnKey, stroke, paint); } // draw range crosshair if required... ValueAxis yAxis = getRangeAxisForDataset(datasetIndex); RectangleEdge yAxisEdge = getRangeAxisEdge(); if (!this.rangeCrosshairLockedOnData && anchor != null) { double yy; if (getOrientation() == PlotOrientation.VERTICAL) { yy = yAxis.java2DToValue(anchor.getY(), dataArea, yAxisEdge); } else { yy = yAxis.java2DToValue(anchor.getX(), dataArea, yAxisEdge); } crosshairState.setCrosshairY(yy); } setRangeCrosshairValue(crosshairState.getCrosshairY(), false); if (isRangeCrosshairVisible()) { double y = getRangeCrosshairValue(); Paint paint = getRangeCrosshairPaint(); Stroke stroke = getRangeCrosshairStroke(); drawRangeCrosshair(g2, dataArea, getOrientation(), y, yAxis, stroke, paint); } // draw an outline around the plot area... if (isOutlineVisible()) { if (getRenderer() != null) { getRenderer().drawOutline(g2, this, dataArea); } else { drawOutline(g2, dataArea); } } } /** * Returns the indices of the non-null datasets in the specified order. * * @param order the order ({@code null} not permitted). * * @return The list of indices. */ private List getDatasetIndices(DatasetRenderingOrder order) { List result = new ArrayList<>(); for (Map.Entry entry : this.datasets.entrySet()) { if (entry.getValue() != null) { result.add(entry.getKey()); } } Collections.sort(result); if (order == DatasetRenderingOrder.REVERSE) { Collections.reverse(result); } return result; } /** * Returns the indices of the non-null renderers for the plot, in the * specified order. * * @param order the rendering order {@code null} not permitted). * * @return A list of indices. */ private List getRendererIndices(DatasetRenderingOrder order) { List result = new ArrayList<>(); for (Map.Entry entry: this.renderers.entrySet()) { if (entry.getValue() != null) { result.add(entry.getKey()); } } Collections.sort(result); if (order == DatasetRenderingOrder.REVERSE) { Collections.reverse(result); } return result; } /** * Draws the plot background (the background color and/or image). *

* This method will be called during the chart drawing process and is * declared public so that it can be accessed by the renderers used by * certain subclasses. You shouldn't need to call this method directly. * * @param g2 the graphics device. * @param area the area within which the plot should be drawn. */ @Override public void drawBackground(Graphics2D g2, Rectangle2D area) { fillBackground(g2, area, this.orientation); drawBackgroundImage(g2, area); } /** * A utility method for drawing the plot's axes. * * @param g2 the graphics device. * @param plotArea the plot area. * @param dataArea the data area. * @param plotState collects information about the plot ({@code null} * permitted). * * @return A map containing the axis states. */ protected Map drawAxes(Graphics2D g2, Rectangle2D plotArea, Rectangle2D dataArea, PlotRenderingInfo plotState) { AxisCollection axisCollection = new AxisCollection(); // add domain axes to lists... for (CategoryAxis xAxis : this.domainAxes.values()) { if (xAxis != null) { int index = getDomainAxisIndex(xAxis); axisCollection.add(xAxis, getDomainAxisEdge(index)); } } // add range axes to lists... for (ValueAxis yAxis : this.rangeAxes.values()) { if (yAxis != null) { int index = findRangeAxisIndex(yAxis); axisCollection.add(yAxis, getRangeAxisEdge(index)); } } Map axisStateMap = new HashMap(); // draw the top axes double cursor = dataArea.getMinY() - this.axisOffset.calculateTopOutset( dataArea.getHeight()); Iterator iterator = axisCollection.getAxesAtTop().iterator(); while (iterator.hasNext()) { Axis axis = (Axis) iterator.next(); if (axis != null) { AxisState axisState = axis.draw(g2, cursor, plotArea, dataArea, RectangleEdge.TOP, plotState); cursor = axisState.getCursor(); axisStateMap.put(axis, axisState); } } // draw the bottom axes cursor = dataArea.getMaxY() + this.axisOffset.calculateBottomOutset(dataArea.getHeight()); iterator = axisCollection.getAxesAtBottom().iterator(); while (iterator.hasNext()) { Axis axis = (Axis) iterator.next(); if (axis != null) { AxisState axisState = axis.draw(g2, cursor, plotArea, dataArea, RectangleEdge.BOTTOM, plotState); cursor = axisState.getCursor(); axisStateMap.put(axis, axisState); } } // draw the left axes cursor = dataArea.getMinX() - this.axisOffset.calculateLeftOutset(dataArea.getWidth()); iterator = axisCollection.getAxesAtLeft().iterator(); while (iterator.hasNext()) { Axis axis = (Axis) iterator.next(); if (axis != null) { AxisState axisState = axis.draw(g2, cursor, plotArea, dataArea, RectangleEdge.LEFT, plotState); cursor = axisState.getCursor(); axisStateMap.put(axis, axisState); } } // draw the right axes cursor = dataArea.getMaxX() + this.axisOffset.calculateRightOutset(dataArea.getWidth()); iterator = axisCollection.getAxesAtRight().iterator(); while (iterator.hasNext()) { Axis axis = (Axis) iterator.next(); if (axis != null) { AxisState axisState = axis.draw(g2, cursor, plotArea, dataArea, RectangleEdge.RIGHT, plotState); cursor = axisState.getCursor(); axisStateMap.put(axis, axisState); } } return axisStateMap; } /** * Draws a representation of a dataset within the dataArea region using the * appropriate renderer. * * @param g2 the graphics device. * @param dataArea the region in which the data is to be drawn. * @param index the dataset and renderer index. * @param info an optional object for collection dimension information. * @param crosshairState a state object for tracking crosshair info * ({@code null} permitted). * * @return A boolean that indicates whether or not real data was found. */ public boolean render(Graphics2D g2, Rectangle2D dataArea, int index, PlotRenderingInfo info, CategoryCrosshairState crosshairState) { boolean foundData = false; CategoryDataset currentDataset = getDataset(index); CategoryItemRenderer renderer = getRenderer(index); CategoryAxis domainAxis = getDomainAxisForDataset(index); ValueAxis rangeAxis = getRangeAxisForDataset(index); boolean hasData = !DatasetUtils.isEmptyOrNull(currentDataset); if (hasData && renderer != null) { foundData = true; CategoryItemRendererState state = renderer.initialise(g2, dataArea, this, index, info); state.setCrosshairState(crosshairState); int columnCount = currentDataset.getColumnCount(); int rowCount = currentDataset.getRowCount(); int passCount = renderer.getPassCount(); for (int pass = 0; pass < passCount; pass++) { if (this.columnRenderingOrder == SortOrder.ASCENDING) { for (int column = 0; column < columnCount; column++) { if (this.rowRenderingOrder == SortOrder.ASCENDING) { for (int row = 0; row < rowCount; row++) { renderer.drawItem(g2, state, dataArea, this, domainAxis, rangeAxis, currentDataset, row, column, pass); } } else { for (int row = rowCount - 1; row >= 0; row--) { renderer.drawItem(g2, state, dataArea, this, domainAxis, rangeAxis, currentDataset, row, column, pass); } } } } else { for (int column = columnCount - 1; column >= 0; column--) { if (this.rowRenderingOrder == SortOrder.ASCENDING) { for (int row = 0; row < rowCount; row++) { renderer.drawItem(g2, state, dataArea, this, domainAxis, rangeAxis, currentDataset, row, column, pass); } } else { for (int row = rowCount - 1; row >= 0; row--) { renderer.drawItem(g2, state, dataArea, this, domainAxis, rangeAxis, currentDataset, row, column, pass); } } } } } } return foundData; } /** * Draws the domain gridlines for the plot, if they are visible. * * @param g2 the graphics device. * @param dataArea the area inside the axes. * * @see #drawRangeGridlines(Graphics2D, Rectangle2D, List) */ protected void drawDomainGridlines(Graphics2D g2, Rectangle2D dataArea) { if (!isDomainGridlinesVisible()) { return; } CategoryAnchor anchor = getDomainGridlinePosition(); RectangleEdge domainAxisEdge = getDomainAxisEdge(); CategoryDataset dataset = getDataset(); if (dataset == null) { return; } CategoryAxis axis = getDomainAxis(); if (axis != null) { int columnCount = dataset.getColumnCount(); for (int c = 0; c < columnCount; c++) { double xx = axis.getCategoryJava2DCoordinate(anchor, c, columnCount, dataArea, domainAxisEdge); CategoryItemRenderer renderer1 = getRenderer(); if (renderer1 != null) { renderer1.drawDomainGridline(g2, this, dataArea, xx); } } } } /** * Draws the range gridlines for the plot, if they are visible. * * @param g2 the graphics device ({@code null} not permitted). * @param dataArea the area inside the axes ({@code null} not permitted). * @param ticks the ticks. * * @see #drawDomainGridlines(Graphics2D, Rectangle2D) */ protected void drawRangeGridlines(Graphics2D g2, Rectangle2D dataArea, List ticks) { // draw the range grid lines, if any... if (!isRangeGridlinesVisible() && !isRangeMinorGridlinesVisible()) { return; } // no axis, no gridlines... ValueAxis axis = getRangeAxis(); if (axis == null) { return; } // no renderer, no gridlines... CategoryItemRenderer r = getRenderer(); if (r == null) { return; } Stroke gridStroke = null; Paint gridPaint = null; boolean paintLine; Iterator iterator = ticks.iterator(); while (iterator.hasNext()) { paintLine = false; ValueTick tick = (ValueTick) iterator.next(); if ((tick.getTickType() == TickType.MINOR) && isRangeMinorGridlinesVisible()) { gridStroke = getRangeMinorGridlineStroke(); gridPaint = getRangeMinorGridlinePaint(); paintLine = true; } else if ((tick.getTickType() == TickType.MAJOR) && isRangeGridlinesVisible()) { gridStroke = getRangeGridlineStroke(); gridPaint = getRangeGridlinePaint(); paintLine = true; } if (((tick.getValue() != 0.0) || !isRangeZeroBaselineVisible()) && paintLine) { r .drawRangeLine(g2, this, axis, dataArea, tick.getValue(), gridPaint, gridStroke); } } } /** * Draws a base line across the chart at value zero on the range axis. * * @param g2 the graphics device. * @param area the data area. * * @see #setRangeZeroBaselineVisible(boolean) */ protected void drawZeroRangeBaseline(Graphics2D g2, Rectangle2D area) { if (!isRangeZeroBaselineVisible()) { return; } CategoryItemRenderer r = getRenderer(); r.drawRangeLine(g2, this, getRangeAxis(), area, 0.0, this.rangeZeroBaselinePaint, this.rangeZeroBaselineStroke); } /** * Draws the annotations. * * @param g2 the graphics device. * @param dataArea the data area. */ protected void drawAnnotations(Graphics2D g2, Rectangle2D dataArea) { if (getAnnotations() != null) { Iterator iterator = getAnnotations().iterator(); while (iterator.hasNext()) { CategoryAnnotation annotation = (CategoryAnnotation) iterator.next(); annotation.draw(g2, this, dataArea, getDomainAxis(), getRangeAxis()); } } } /** * Draws the domain markers (if any) for an axis and layer. This method is * typically called from within the draw() method. * * @param g2 the graphics device. * @param dataArea the data area. * @param index the renderer index. * @param layer the layer (foreground or background). * * @see #drawRangeMarkers(Graphics2D, Rectangle2D, int, Layer) */ protected void drawDomainMarkers(Graphics2D g2, Rectangle2D dataArea, int index, Layer layer) { CategoryItemRenderer r = getRenderer(index); if (r == null) { return; } Collection markers = getDomainMarkers(index, layer); CategoryAxis axis = getDomainAxisForDataset(index); if (markers != null && axis != null) { for (CategoryMarker marker : markers) { r.drawDomainMarker(g2, this, axis, marker, dataArea); } } } /** * Draws the range markers (if any) for an axis and layer. This method is * typically called from within the draw() method. * * @param g2 the graphics device. * @param dataArea the data area. * @param index the renderer index. * @param layer the layer (foreground or background). * * @see #drawDomainMarkers(Graphics2D, Rectangle2D, int, Layer) */ protected void drawRangeMarkers(Graphics2D g2, Rectangle2D dataArea, int index, Layer layer) { CategoryItemRenderer r = getRenderer(index); if (r == null) { return; } Collection markers = getRangeMarkers(index, layer); ValueAxis axis = getRangeAxisForDataset(index); if (markers != null && axis != null) { for (Marker marker : markers) { r.drawRangeMarker(g2, this, axis, marker, dataArea); } } } /** * Utility method for drawing a line perpendicular to the range axis (used * for crosshairs). * * @param g2 the graphics device. * @param dataArea the area defined by the axes. * @param value the data value. * @param stroke the line stroke ({@code null} not permitted). * @param paint the line paint ({@code null} not permitted). */ protected void drawRangeLine(Graphics2D g2, Rectangle2D dataArea, double value, Stroke stroke, Paint paint) { double java2D = getRangeAxis().valueToJava2D(value, dataArea, getRangeAxisEdge()); Line2D line = null; if (this.orientation == PlotOrientation.HORIZONTAL) { line = new Line2D.Double(java2D, dataArea.getMinY(), java2D, dataArea.getMaxY()); } else if (this.orientation == PlotOrientation.VERTICAL) { line = new Line2D.Double(dataArea.getMinX(), java2D, dataArea.getMaxX(), java2D); } g2.setStroke(stroke); g2.setPaint(paint); g2.draw(line); } /** * Draws a domain crosshair. * * @param g2 the graphics target. * @param dataArea the data area. * @param orientation the plot orientation. * @param datasetIndex the dataset index. * @param rowKey the row key. * @param columnKey the column key. * @param stroke the stroke used to draw the crosshair line. * @param paint the paint used to draw the crosshair line. * * @see #drawRangeCrosshair(Graphics2D, Rectangle2D, PlotOrientation, * double, ValueAxis, Stroke, Paint) */ protected void drawDomainCrosshair(Graphics2D g2, Rectangle2D dataArea, PlotOrientation orientation, int datasetIndex, Comparable rowKey, Comparable columnKey, Stroke stroke, Paint paint) { CategoryDataset dataset = getDataset(datasetIndex); CategoryAxis axis = getDomainAxisForDataset(datasetIndex); CategoryItemRenderer renderer = getRenderer(datasetIndex); Line2D line; if (orientation == PlotOrientation.VERTICAL) { double xx = renderer.getItemMiddle(rowKey, columnKey, dataset, axis, dataArea, RectangleEdge.BOTTOM); line = new Line2D.Double(xx, dataArea.getMinY(), xx, dataArea.getMaxY()); } else { double yy = renderer.getItemMiddle(rowKey, columnKey, dataset, axis, dataArea, RectangleEdge.LEFT); line = new Line2D.Double(dataArea.getMinX(), yy, dataArea.getMaxX(), yy); } g2.setStroke(stroke); g2.setPaint(paint); g2.draw(line); } /** * Draws a range crosshair. * * @param g2 the graphics target. * @param dataArea the data area. * @param orientation the plot orientation. * @param value the crosshair value. * @param axis the axis against which the value is measured. * @param stroke the stroke used to draw the crosshair line. * @param paint the paint used to draw the crosshair line. * * @see #drawDomainCrosshair(Graphics2D, Rectangle2D, PlotOrientation, int, * Comparable, Comparable, Stroke, Paint) */ protected void drawRangeCrosshair(Graphics2D g2, Rectangle2D dataArea, PlotOrientation orientation, double value, ValueAxis axis, Stroke stroke, Paint paint) { if (!axis.getRange().contains(value)) { return; } Line2D line; if (orientation == PlotOrientation.HORIZONTAL) { double xx = axis.valueToJava2D(value, dataArea, RectangleEdge.BOTTOM); line = new Line2D.Double(xx, dataArea.getMinY(), xx, dataArea.getMaxY()); } else { double yy = axis.valueToJava2D(value, dataArea, RectangleEdge.LEFT); line = new Line2D.Double(dataArea.getMinX(), yy, dataArea.getMaxX(), yy); } g2.setStroke(stroke); g2.setPaint(paint); g2.draw(line); } /** * Returns the range of data values that will be plotted against the range * axis. If the dataset is {@code null}, this method returns * {@code null}. * * @param axis the axis. * * @return The data range. */ @Override public Range getDataRange(ValueAxis axis) { Range result = null; List mappedDatasets = new ArrayList<>(); int rangeIndex = findRangeAxisIndex(axis); if (rangeIndex >= 0) { mappedDatasets.addAll(datasetsMappedToRangeAxis(rangeIndex)); } else if (axis == getRangeAxis()) { mappedDatasets.addAll(datasetsMappedToRangeAxis(0)); } // iterate through the datasets that map to the axis and get the union // of the ranges. for (CategoryDataset d : mappedDatasets) { CategoryItemRenderer r = getRendererForDataset(d); if (r != null) { result = Range.combine(result, r.findRangeBounds(d)); } } return result; } /** * Returns a list of the datasets that are mapped to the axis with the * specified index. * * @param axisIndex the axis index. * * @return The list (possibly empty, but never {@code null}). */ private List datasetsMappedToDomainAxis(int axisIndex) { List result = new ArrayList<>(); for (Entry entry : this.datasets.entrySet()) { CategoryDataset dataset = entry.getValue(); if (dataset == null) { continue; } Integer datasetIndex = entry.getKey(); List mappedAxes = this.datasetToDomainAxesMap.get(datasetIndex); if (mappedAxes == null) { if (axisIndex == 0) { result.add(dataset); } } else { if (mappedAxes.contains(axisIndex)) { result.add(dataset); } } } return result; } /** * A utility method that returns a list of datasets that are mapped to a * given range axis. * * @param axisIndex the axis index. * * @return The list (possibly empty, but never {@code null}). */ private List datasetsMappedToRangeAxis(int axisIndex) { List result = new ArrayList<>(); for (Entry entry : this.datasets.entrySet()) { Integer datasetIndex = entry.getKey(); CategoryDataset dataset = entry.getValue(); List mappedAxes = this.datasetToRangeAxesMap.get(datasetIndex); if (mappedAxes == null) { if (axisIndex == 0) { result.add(dataset); } } else { if (mappedAxes.contains(axisIndex)) { result.add(dataset); } } } return result; } /** * Returns the weight for this plot when it is used as a subplot within a * combined plot. * * @return The weight. * * @see #setWeight(int) */ public int getWeight() { return this.weight; } /** * Sets the weight for the plot and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param weight the weight. * * @see #getWeight() */ public void setWeight(int weight) { this.weight = weight; fireChangeEvent(); } /** * Returns the fixed domain axis space. * * @return The fixed domain axis space (possibly {@code null}). * * @see #setFixedDomainAxisSpace(AxisSpace) */ public AxisSpace getFixedDomainAxisSpace() { return this.fixedDomainAxisSpace; } /** * Sets the fixed domain axis space and sends a {@link PlotChangeEvent} to * all registered listeners. * * @param space the space ({@code null} permitted). * * @see #getFixedDomainAxisSpace() */ public void setFixedDomainAxisSpace(AxisSpace space) { setFixedDomainAxisSpace(space, true); } /** * Sets the fixed domain axis space and sends a {@link PlotChangeEvent} to * all registered listeners. * * @param space the space ({@code null} permitted). * @param notify notify listeners? * * @see #getFixedDomainAxisSpace() */ public void setFixedDomainAxisSpace(AxisSpace space, boolean notify) { this.fixedDomainAxisSpace = space; if (notify) { fireChangeEvent(); } } /** * Returns the fixed range axis space. * * @return The fixed range axis space (possibly {@code null}). * * @see #setFixedRangeAxisSpace(AxisSpace) */ public AxisSpace getFixedRangeAxisSpace() { return this.fixedRangeAxisSpace; } /** * Sets the fixed range axis space and sends a {@link PlotChangeEvent} to * all registered listeners. * * @param space the space ({@code null} permitted). * * @see #getFixedRangeAxisSpace() */ public void setFixedRangeAxisSpace(AxisSpace space) { setFixedRangeAxisSpace(space, true); } /** * Sets the fixed range axis space and sends a {@link PlotChangeEvent} to * all registered listeners. * * @param space the space ({@code null} permitted). * @param notify notify listeners? * * @see #getFixedRangeAxisSpace() */ public void setFixedRangeAxisSpace(AxisSpace space, boolean notify) { this.fixedRangeAxisSpace = space; if (notify) { fireChangeEvent(); } } /** * Returns a list of the categories in the plot's primary dataset. * * @return A list of the categories in the plot's primary dataset. * * @see #getCategoriesForAxis(CategoryAxis) */ public List getCategories() { List result = null; if (getDataset() != null) { result = Collections.unmodifiableList(getDataset().getColumnKeys()); } return result; } /** * Returns a list of the categories that should be displayed for the * specified axis. * * @param axis the axis ({@code null} not permitted) * * @return The categories. */ public List getCategoriesForAxis(CategoryAxis axis) { List result = new ArrayList(); int axisIndex = getDomainAxisIndex(axis); for (CategoryDataset dataset : datasetsMappedToDomainAxis(axisIndex)) { // add the unique categories from this dataset for (int i = 0; i < dataset.getColumnCount(); i++) { Comparable category = dataset.getColumnKey(i); if (!result.contains(category)) { result.add(category); } } } return result; } /** * Returns the flag that controls whether or not the shared domain axis is * drawn for each subplot. * * @return A boolean. * * @see #setDrawSharedDomainAxis(boolean) */ public boolean getDrawSharedDomainAxis() { return this.drawSharedDomainAxis; } /** * Sets the flag that controls whether the shared domain axis is drawn when * this plot is being used as a subplot. * * @param draw a boolean. * * @see #getDrawSharedDomainAxis() */ public void setDrawSharedDomainAxis(boolean draw) { this.drawSharedDomainAxis = draw; fireChangeEvent(); } /** * Returns {@code false} always, because the plot cannot be panned * along the domain axis/axes. * * @return A boolean. * * @see #isRangePannable() */ @Override public boolean isDomainPannable() { return false; } /** * Returns {@code true} if panning is enabled for the range axes, * and {@code false} otherwise. * * @return A boolean. * * @see #setRangePannable(boolean) * @see #isDomainPannable() */ @Override public boolean isRangePannable() { return this.rangePannable; } /** * Sets the flag that enables or disables panning of the plot along * the range axes. * * @param pannable the new flag value. * * @see #isRangePannable() */ public void setRangePannable(boolean pannable) { this.rangePannable = pannable; } /** * Pans the domain axes by the specified percentage. * * @param percent the distance to pan (as a percentage of the axis length). * @param info the plot info * @param source the source point where the pan action started. */ @Override public void panDomainAxes(double percent, PlotRenderingInfo info, Point2D source) { // do nothing, because the plot is not pannable along the domain axes } /** * Pans the range axes by the specified percentage. * * @param percent the distance to pan (as a percentage of the axis length). * @param info the plot info * @param source the source point where the pan action started. */ @Override public void panRangeAxes(double percent, PlotRenderingInfo info, Point2D source) { if (!isRangePannable()) { return; } for (ValueAxis axis : this.rangeAxes.values()) { if (axis == null) { continue; } double length = axis.getRange().getLength(); double adj = percent * length; if (axis.isInverted()) { adj = -adj; } axis.setRange(axis.getLowerBound() + adj, axis.getUpperBound() + adj); } } /** * Returns {@code false} to indicate that the domain axes are not * zoomable. * * @return A boolean. * * @see #isRangeZoomable() */ @Override public boolean isDomainZoomable() { return false; } /** * Returns {@code true} to indicate that the range axes are zoomable. * * @return A boolean. * * @see #isDomainZoomable() */ @Override public boolean isRangeZoomable() { return true; } /** * This method does nothing, because {@code CategoryPlot} doesn't * support zooming on the domain. * * @param factor the zoom factor. * @param state the plot state. * @param source the source point (in Java2D space) for the zoom. */ @Override public void zoomDomainAxes(double factor, PlotRenderingInfo state, Point2D source) { // can't zoom domain axis } /** * This method does nothing, because {@code CategoryPlot} doesn't * support zooming on the domain. * * @param lowerPercent the lower bound. * @param upperPercent the upper bound. * @param state the plot state. * @param source the source point (in Java2D space) for the zoom. */ @Override public void zoomDomainAxes(double lowerPercent, double upperPercent, PlotRenderingInfo state, Point2D source) { // can't zoom domain axis } /** * This method does nothing, because {@code CategoryPlot} doesn't * support zooming on the domain. * * @param factor the zoom factor. * @param info the plot rendering info. * @param source the source point (in Java2D space). * @param useAnchor use source point as zoom anchor? * * @see #zoomRangeAxes(double, PlotRenderingInfo, Point2D, boolean) */ @Override public void zoomDomainAxes(double factor, PlotRenderingInfo info, Point2D source, boolean useAnchor) { // can't zoom domain axis } /** * Multiplies the range on the range axis/axes by the specified factor. * * @param factor the zoom factor. * @param state the plot state. * @param source the source point (in Java2D space) for the zoom. */ @Override public void zoomRangeAxes(double factor, PlotRenderingInfo state, Point2D source) { // delegate to other method zoomRangeAxes(factor, state, source, false); } /** * Multiplies the range on the range axis/axes by the specified factor. * * @param factor the zoom factor. * @param info the plot rendering info. * @param source the source point. * @param useAnchor a flag that controls whether or not the source point * is used for the zoom anchor. * * @see #zoomDomainAxes(double, PlotRenderingInfo, Point2D, boolean) */ @Override public void zoomRangeAxes(double factor, PlotRenderingInfo info, Point2D source, boolean useAnchor) { // perform the zoom on each range axis for (ValueAxis rangeAxis : this.rangeAxes.values()) { if (rangeAxis == null) { continue; } if (useAnchor) { // get the relevant source coordinate given the plot orientation double sourceY = source.getY(); if (this.orientation.isHorizontal()) { sourceY = source.getX(); } double anchorY = rangeAxis.java2DToValue(sourceY, info.getDataArea(), getRangeAxisEdge()); rangeAxis.resizeRange2(factor, anchorY); } else { rangeAxis.resizeRange(factor); } } } /** * Zooms in on the range axes. * * @param lowerPercent the lower bound. * @param upperPercent the upper bound. * @param state the plot state. * @param source the source point (in Java2D space) for the zoom. */ @Override public void zoomRangeAxes(double lowerPercent, double upperPercent, PlotRenderingInfo state, Point2D source) { for (ValueAxis yAxis : this.rangeAxes.values()) { if (yAxis != null) { yAxis.zoomRange(lowerPercent, upperPercent); } } } /** * Returns the anchor value. * * @return The anchor value. * * @see #setAnchorValue(double) */ public double getAnchorValue() { return this.anchorValue; } /** * Sets the anchor value and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param value the anchor value. * * @see #getAnchorValue() */ public void setAnchorValue(double value) { setAnchorValue(value, true); } /** * Sets the anchor value and, if requested, sends a {@link PlotChangeEvent} * to all registered listeners. * * @param value the value. * @param notify notify listeners? * * @see #getAnchorValue() */ public void setAnchorValue(double value, boolean notify) { this.anchorValue = value; if (notify) { fireChangeEvent(); } } /** * Tests the plot for equality with an arbitrary object. * * @param obj the object to test against ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof CategoryPlot)) { return false; } CategoryPlot that = (CategoryPlot) obj; if (!that.canEqual(this)) { return false; } if (!Objects.equals(this.orientation, that.orientation)) { return false; } if (!Objects.equals(this.datasets, that.datasets)) { return false; } if (!Objects.equals(this.axisOffset, that.axisOffset)) { return false; } if (!Objects.equals(this.domainAxes, that.domainAxes)) { return false; } if (!Objects.equals(this.domainAxisLocations, that.domainAxisLocations)) { return false; } if (this.drawSharedDomainAxis != that.drawSharedDomainAxis) { return false; } if (!Objects.equals(this.rangeAxes, that.rangeAxes)) { return false; } if (!Objects.equals(this.rangeAxisLocations, that.rangeAxisLocations)) { return false; } if (!Objects.equals(this.datasetToDomainAxesMap, that.datasetToDomainAxesMap)) { return false; } if (!Objects.equals(this.datasetToRangeAxesMap, that.datasetToRangeAxesMap)) { return false; } if (!Objects.equals(this.renderers, that.renderers)) { return false; } if (!Objects.equals(this.renderingOrder, that.renderingOrder)) { return false; } if (!Objects.equals(this.columnRenderingOrder, that.columnRenderingOrder)) { return false; } if (!Objects.equals(this.rowRenderingOrder, that.rowRenderingOrder)) { return false; } if (this.domainGridlinesVisible != that.domainGridlinesVisible) { return false; } if (this.rangePannable != that.rangePannable) { return false; } if (!Objects.equals(this.domainGridlinePosition, that.domainGridlinePosition)) { return false; } if (!Objects.equals(this.domainGridlineStroke, that.domainGridlineStroke)) { return false; } if (!PaintUtils.equal(this.domainGridlinePaint, that.domainGridlinePaint)) { return false; } if (this.rangeGridlinesVisible != that.rangeGridlinesVisible) { return false; } if (!Objects.equals(this.rangeGridlineStroke, that.rangeGridlineStroke)) { return false; } if (!PaintUtils.equal(this.rangeGridlinePaint, that.rangeGridlinePaint)) { return false; } if (Double.compare(this.anchorValue, that.anchorValue) != 0) { return false; } if (this.rangeCrosshairVisible != that.rangeCrosshairVisible) { return false; } if (Double.doubleToLongBits(this.rangeCrosshairValue) != Double.doubleToLongBits(that.rangeCrosshairValue)) { return false; } if (!Objects.equals(this.rangeCrosshairStroke, that.rangeCrosshairStroke)) { return false; } if (!PaintUtils.equal(this.rangeCrosshairPaint, that.rangeCrosshairPaint)) { return false; } if (this.rangeCrosshairLockedOnData != that.rangeCrosshairLockedOnData) { return false; } if (!Objects.equals(this.foregroundDomainMarkers, that.foregroundDomainMarkers)) { return false; } if (!Objects.equals(this.backgroundDomainMarkers, that.backgroundDomainMarkers)) { return false; } if (!Objects.equals(this.foregroundRangeMarkers, that.foregroundRangeMarkers)) { return false; } if (!Objects.equals(this.backgroundRangeMarkers, that.backgroundRangeMarkers)) { return false; } if (!Objects.equals(this.annotations, that.annotations)) { return false; } if (this.weight != that.weight) { return false; } if (!Objects.equals(this.fixedDomainAxisSpace, that.fixedDomainAxisSpace)) { return false; } if (!Objects.equals(this.fixedRangeAxisSpace, that.fixedRangeAxisSpace)) { return false; } if (!Objects.equals(this.fixedLegendItems, that.fixedLegendItems)) { return false; } if (this.domainCrosshairVisible != that.domainCrosshairVisible) { return false; } if (this.crosshairDatasetIndex != that.crosshairDatasetIndex) { return false; } if (!Objects.equals(this.domainCrosshairColumnKey, that.domainCrosshairColumnKey)) { return false; } if (!Objects.equals(this.domainCrosshairRowKey, that.domainCrosshairRowKey)) { return false; } if (!PaintUtils.equal(this.domainCrosshairPaint, that.domainCrosshairPaint)) { return false; } if (!Objects.equals(this.domainCrosshairStroke, that.domainCrosshairStroke)) { return false; } if (this.rangeMinorGridlinesVisible != that.rangeMinorGridlinesVisible) { return false; } if (!PaintUtils.equal(this.rangeMinorGridlinePaint, that.rangeMinorGridlinePaint)) { return false; } if (!Objects.equals(this.rangeMinorGridlineStroke, that.rangeMinorGridlineStroke)) { return false; } if (this.rangeZeroBaselineVisible != that.rangeZeroBaselineVisible) { return false; } if (!PaintUtils.equal(this.rangeZeroBaselinePaint, that.rangeZeroBaselinePaint)) { return false; } if (!Objects.equals(this.rangeZeroBaselineStroke, that.rangeZeroBaselineStroke)) { return false; } if (!Objects.equals(this.shadowGenerator, that.shadowGenerator)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // Solves Problem: equals not symmetric return (other instanceof CategoryPlot); } @Override public int hashCode() { int hash = super.hashCode(); hash = 71 * hash + Objects.hashCode(this.orientation); hash = 71 * hash + Objects.hashCode(this.axisOffset); hash = 71 * hash + Objects.hashCode(this.domainAxes); hash = 71 * hash + Objects.hashCode(this.domainAxisLocations); hash = 71 * hash + (this.drawSharedDomainAxis ? 1 : 0); hash = 71 * hash + Objects.hashCode(this.rangeAxes); hash = 71 * hash + Objects.hashCode(this.rangeAxisLocations); hash = 71 * hash + Objects.hashCode(this.datasets); hash = 71 * hash + Objects.hashCode(this.datasetToDomainAxesMap); hash = 71 * hash + Objects.hashCode(this.datasetToRangeAxesMap); hash = 71 * hash + Objects.hashCode(this.renderers); hash = 71 * hash + Objects.hashCode(this.renderingOrder); hash = 71 * hash + Objects.hashCode(this.columnRenderingOrder); hash = 71 * hash + Objects.hashCode(this.rowRenderingOrder); hash = 71 * hash + (this.domainGridlinesVisible ? 1 : 0); hash = 71 * hash + Objects.hashCode(this.domainGridlinePosition); hash = 71 * hash + Objects.hashCode(this.domainGridlineStroke); hash = 71 * hash + Objects.hashCode(this.domainGridlinePaint); hash = 71 * hash + (this.rangeZeroBaselineVisible ? 1 : 0); hash = 71 * hash + Objects.hashCode(this.rangeZeroBaselineStroke); hash = 71 * hash + Objects.hashCode(this.rangeZeroBaselinePaint); hash = 71 * hash + (this.rangeGridlinesVisible ? 1 : 0); hash = 71 * hash + Objects.hashCode(this.rangeGridlineStroke); hash = 71 * hash + Objects.hashCode(this.rangeGridlinePaint); hash = 71 * hash + (this.rangeMinorGridlinesVisible ? 1 : 0); hash = 71 * hash + Objects.hashCode(this.rangeMinorGridlineStroke); hash = 71 * hash + Objects.hashCode(this.rangeMinorGridlinePaint); hash = 71 * hash + (int) (Double.doubleToLongBits(this.anchorValue) ^ (Double.doubleToLongBits(this.anchorValue) >>> 32)); hash = 71 * hash + this.crosshairDatasetIndex; hash = 71 * hash + (this.domainCrosshairVisible ? 1 : 0); hash = 71 * hash + Objects.hashCode(this.domainCrosshairRowKey); hash = 71 * hash + Objects.hashCode(this.domainCrosshairColumnKey); hash = 71 * hash + Objects.hashCode(this.domainCrosshairStroke); hash = 71 * hash + Objects.hashCode(this.domainCrosshairPaint); hash = 71 * hash + (this.rangeCrosshairVisible ? 1 : 0); hash = 71 * hash + (int) (Double.doubleToLongBits(this.rangeCrosshairValue) ^ (Double.doubleToLongBits(this.rangeCrosshairValue) >>> 32)); hash = 71 * hash + Objects.hashCode(this.rangeCrosshairStroke); hash = 71 * hash + Objects.hashCode(this.rangeCrosshairPaint); hash = 71 * hash + (this.rangeCrosshairLockedOnData ? 1 : 0); hash = 71 * hash + Objects.hashCode(this.foregroundDomainMarkers); hash = 71 * hash + Objects.hashCode(this.backgroundDomainMarkers); hash = 71 * hash + Objects.hashCode(this.foregroundRangeMarkers); hash = 71 * hash + Objects.hashCode(this.backgroundRangeMarkers); hash = 71 * hash + Objects.hashCode(this.annotations); hash = 71 * hash + this.weight; hash = 71 * hash + Objects.hashCode(this.fixedDomainAxisSpace); hash = 71 * hash + Objects.hashCode(this.fixedRangeAxisSpace); hash = 71 * hash + Objects.hashCode(this.fixedLegendItems); hash = 71 * hash + (this.rangePannable ? 1 : 0); hash = 71 * hash + Objects.hashCode(this.shadowGenerator); return hash; } /** * Returns a clone of the plot. * * @return A clone. * * @throws CloneNotSupportedException if the cloning is not supported. */ @Override public Object clone() throws CloneNotSupportedException { CategoryPlot clone = (CategoryPlot) super.clone(); clone.domainAxes = CloneUtils.cloneMapValues(this.domainAxes); for (CategoryAxis axis : clone.domainAxes.values()) { if (axis != null) { axis.setPlot(clone); axis.addChangeListener(clone); } } clone.rangeAxes = CloneUtils.cloneMapValues(this.rangeAxes); for (ValueAxis axis : clone.rangeAxes.values()) { if (axis != null) { axis.setPlot(clone); axis.addChangeListener(clone); } } // AxisLocation is immutable, so we can just copy the maps clone.domainAxisLocations = new HashMap<>( this.domainAxisLocations); clone.rangeAxisLocations = new HashMap<>( this.rangeAxisLocations); clone.datasets = new HashMap<>(this.datasets); for (CategoryDataset dataset : clone.datasets.values()) { if (dataset != null) { dataset.addChangeListener(clone); } } clone.datasetToDomainAxesMap = new TreeMap(); clone.datasetToDomainAxesMap.putAll(this.datasetToDomainAxesMap); clone.datasetToRangeAxesMap = new TreeMap(); clone.datasetToRangeAxesMap.putAll(this.datasetToRangeAxesMap); clone.renderers = CloneUtils.cloneMapValues(this.renderers); for (CategoryItemRenderer renderer : clone.renderers.values()) { if (renderer != null) { renderer.setPlot(clone); renderer.addChangeListener(clone); } } if (this.fixedDomainAxisSpace != null) { clone.fixedDomainAxisSpace = (AxisSpace) ObjectUtils.clone( this.fixedDomainAxisSpace); } if (this.fixedRangeAxisSpace != null) { clone.fixedRangeAxisSpace = (AxisSpace) ObjectUtils.clone( this.fixedRangeAxisSpace); } clone.annotations = (List) ObjectUtils.deepClone(this.annotations); clone.foregroundDomainMarkers = cloneMarkerMap( this.foregroundDomainMarkers); clone.backgroundDomainMarkers = cloneMarkerMap( this.backgroundDomainMarkers); clone.foregroundRangeMarkers = cloneMarkerMap( this.foregroundRangeMarkers); clone.backgroundRangeMarkers = cloneMarkerMap( this.backgroundRangeMarkers); if (this.fixedLegendItems != null) { clone.fixedLegendItems = (LegendItemCollection) this.fixedLegendItems.clone(); } return clone; } /** * A utility method to clone the marker maps. * * @param map the map to clone. * * @return A clone of the map. * * @throws CloneNotSupportedException if there is some problem cloning the * map. */ private Map cloneMarkerMap(Map map) throws CloneNotSupportedException { Map clone = new HashMap(); Set keys = map.keySet(); Iterator iterator = keys.iterator(); while (iterator.hasNext()) { Object key = iterator.next(); List entry = (List) map.get(key); Object toAdd = ObjectUtils.deepClone(entry); clone.put(key, toAdd); } return clone; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeStroke(this.domainGridlineStroke, stream); SerialUtils.writePaint(this.domainGridlinePaint, stream); SerialUtils.writeStroke(this.rangeGridlineStroke, stream); SerialUtils.writePaint(this.rangeGridlinePaint, stream); SerialUtils.writeStroke(this.rangeCrosshairStroke, stream); SerialUtils.writePaint(this.rangeCrosshairPaint, stream); SerialUtils.writeStroke(this.domainCrosshairStroke, stream); SerialUtils.writePaint(this.domainCrosshairPaint, stream); SerialUtils.writeStroke(this.rangeMinorGridlineStroke, stream); SerialUtils.writePaint(this.rangeMinorGridlinePaint, stream); SerialUtils.writeStroke(this.rangeZeroBaselineStroke, stream); SerialUtils.writePaint(this.rangeZeroBaselinePaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.domainGridlineStroke = SerialUtils.readStroke(stream); this.domainGridlinePaint = SerialUtils.readPaint(stream); this.rangeGridlineStroke = SerialUtils.readStroke(stream); this.rangeGridlinePaint = SerialUtils.readPaint(stream); this.rangeCrosshairStroke = SerialUtils.readStroke(stream); this.rangeCrosshairPaint = SerialUtils.readPaint(stream); this.domainCrosshairStroke = SerialUtils.readStroke(stream); this.domainCrosshairPaint = SerialUtils.readPaint(stream); this.rangeMinorGridlineStroke = SerialUtils.readStroke(stream); this.rangeMinorGridlinePaint = SerialUtils.readPaint(stream); this.rangeZeroBaselineStroke = SerialUtils.readStroke(stream); this.rangeZeroBaselinePaint = SerialUtils.readPaint(stream); for (CategoryAxis xAxis : this.domainAxes.values()) { if (xAxis != null) { xAxis.setPlot(this); xAxis.addChangeListener(this); } } for (ValueAxis yAxis : this.rangeAxes.values()) { if (yAxis != null) { yAxis.setPlot(this); yAxis.addChangeListener(this); } } for (CategoryDataset dataset : this.datasets.values()) { if (dataset != null) { dataset.addChangeListener(this); } } for (CategoryItemRenderer renderer : this.renderers.values()) { if (renderer != null) { renderer.addChangeListener(this); } } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/CenterTextMode.java000066400000000000000000000032521463604235500275730ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * CenterTextMode.java * ------------------- * (C) Copyright 2014-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; /** * The mode for the center text on a {@link RingPlot}. */ public enum CenterTextMode { /** A fixed text item */ FIXED, /** A value item (taken from the first item in the dataset). */ VALUE, /** No center text. */ NONE } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/CombinedDomainCategoryPlot.java000066400000000000000000000601201463604235500321030ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------- * CombinedDomainCategoryPlot.java * ------------------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Nicolas Brodu; * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.plot; import java.awt.Graphics2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Objects; import org.jfree.chart.LegendItemCollection; import org.jfree.chart.axis.AxisSpace; import org.jfree.chart.axis.AxisState; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.PlotChangeEvent; import org.jfree.chart.event.PlotChangeListener; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.ShadowGenerator; import org.jfree.data.Range; /** * A combined category plot where the domain axis is shared. */ public class CombinedDomainCategoryPlot extends CategoryPlot implements PlotChangeListener { /** For serialization. */ private static final long serialVersionUID = 8207194522653701572L; /** Storage for the subplot references. */ private List subplots; /** The gap between subplots. */ private double gap; /** Temporary storage for the subplot areas. */ private transient Rectangle2D[] subplotAreas; // TODO: move the above to the plot state /** * Default constructor. */ public CombinedDomainCategoryPlot() { this(new CategoryAxis()); } /** * Creates a new plot. * * @param domainAxis the shared domain axis ({@code null} not * permitted). */ public CombinedDomainCategoryPlot(CategoryAxis domainAxis) { super(null, domainAxis, null, null); this.subplots = new java.util.ArrayList(); this.gap = 5.0; } /** * Returns the space between subplots. The default value is 5.0. * * @return The gap (in Java2D units). * * @see #setGap(double) */ public double getGap() { return this.gap; } /** * Sets the amount of space between subplots and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param gap the gap between subplots (in Java2D units). * * @see #getGap() */ public void setGap(double gap) { this.gap = gap; fireChangeEvent(); } /** * Adds a subplot to the combined chart and sends a {@link PlotChangeEvent} * to all registered listeners. *

* The domain axis for the subplot will be set to {@code null}. You * must ensure that the subplot has a non-null range axis. * * @param subplot the subplot ({@code null} not permitted). */ public void add(CategoryPlot subplot) { add(subplot, 1); } /** * Adds a subplot to the combined chart and sends a {@link PlotChangeEvent} * to all registered listeners. *

* The domain axis for the subplot will be set to {@code null}. You * must ensure that the subplot has a non-null range axis. * * @param subplot the subplot ({@code null} not permitted). * @param weight the weight (must be >= 1). */ public void add(CategoryPlot subplot, int weight) { Args.nullNotPermitted(subplot, "subplot"); if (weight < 1) { throw new IllegalArgumentException("Require weight >= 1."); } subplot.setParent(this); subplot.setWeight(weight); subplot.setInsets(new RectangleInsets(0.0, 0.0, 0.0, 0.0)); subplot.setDomainAxis(null); subplot.setOrientation(getOrientation()); subplot.addChangeListener(this); this.subplots.add(subplot); CategoryAxis axis = getDomainAxis(); if (axis != null) { axis.configure(); } fireChangeEvent(); } /** * Removes a subplot from the combined chart. Potentially, this removes * some unique categories from the overall union of the datasets...so the * domain axis is reconfigured, then a {@link PlotChangeEvent} is sent to * all registered listeners. * * @param subplot the subplot ({@code null} not permitted). */ public void remove(CategoryPlot subplot) { Args.nullNotPermitted(subplot, "subplot"); int position = -1; int size = this.subplots.size(); int i = 0; while (position == -1 && i < size) { if (this.subplots.get(i) == subplot) { position = i; } i++; } if (position != -1) { this.subplots.remove(position); subplot.setParent(null); subplot.removeChangeListener(this); CategoryAxis domain = getDomainAxis(); if (domain != null) { domain.configure(); } fireChangeEvent(); } } /** * Returns the list of subplots. The returned list may be empty, but is * never {@code null}. * * @return An unmodifiable list of subplots. */ public List getSubplots() { if (this.subplots != null) { return Collections.unmodifiableList(this.subplots); } else { return Collections.EMPTY_LIST; } } /** * Returns the subplot (if any) that contains the (x, y) point (specified * in Java2D space). * * @param info the chart rendering info ({@code null} not permitted). * @param source the source point ({@code null} not permitted). * * @return A subplot (possibly {@code null}). */ public CategoryPlot findSubplot(PlotRenderingInfo info, Point2D source) { Args.nullNotPermitted(info, "info"); Args.nullNotPermitted(source, "source"); CategoryPlot result = null; int subplotIndex = info.getSubplotIndex(source); if (subplotIndex >= 0) { result = (CategoryPlot) this.subplots.get(subplotIndex); } return result; } /** * Multiplies the range on the range axis/axes by the specified factor. * * @param factor the zoom factor. * @param info the plot rendering info ({@code null} not permitted). * @param source the source point ({@code null} not permitted). */ @Override public void zoomRangeAxes(double factor, PlotRenderingInfo info, Point2D source) { zoomRangeAxes(factor, info, source, false); } /** * Multiplies the range on the range axis/axes by the specified factor. * * @param factor the zoom factor. * @param info the plot rendering info ({@code null} not permitted). * @param source the source point ({@code null} not permitted). * @param useAnchor zoom about the anchor point? */ @Override public void zoomRangeAxes(double factor, PlotRenderingInfo info, Point2D source, boolean useAnchor) { // delegate 'info' and 'source' argument checks... CategoryPlot subplot = findSubplot(info, source); if (subplot != null) { subplot.zoomRangeAxes(factor, info, source, useAnchor); } else { // if the source point doesn't fall within a subplot, we do the // zoom on all subplots... Iterator iterator = getSubplots().iterator(); while (iterator.hasNext()) { subplot = (CategoryPlot) iterator.next(); subplot.zoomRangeAxes(factor, info, source, useAnchor); } } } /** * Zooms in on the range axes. * * @param lowerPercent the lower bound. * @param upperPercent the upper bound. * @param info the plot rendering info ({@code null} not permitted). * @param source the source point ({@code null} not permitted). */ @Override public void zoomRangeAxes(double lowerPercent, double upperPercent, PlotRenderingInfo info, Point2D source) { // delegate 'info' and 'source' argument checks... CategoryPlot subplot = findSubplot(info, source); if (subplot != null) { subplot.zoomRangeAxes(lowerPercent, upperPercent, info, source); } else { // if the source point doesn't fall within a subplot, we do the // zoom on all subplots... Iterator iterator = getSubplots().iterator(); while (iterator.hasNext()) { subplot = (CategoryPlot) iterator.next(); subplot.zoomRangeAxes(lowerPercent, upperPercent, info, source); } } } /** * Calculates the space required for the axes. * * @param g2 the graphics device. * @param plotArea the plot area. * * @return The space required for the axes. */ @Override protected AxisSpace calculateAxisSpace(Graphics2D g2, Rectangle2D plotArea) { AxisSpace space = new AxisSpace(); PlotOrientation orientation = getOrientation(); // work out the space required by the domain axis... AxisSpace fixed = getFixedDomainAxisSpace(); if (fixed != null) { if (orientation == PlotOrientation.HORIZONTAL) { space.setLeft(fixed.getLeft()); space.setRight(fixed.getRight()); } else if (orientation == PlotOrientation.VERTICAL) { space.setTop(fixed.getTop()); space.setBottom(fixed.getBottom()); } } else { CategoryAxis categoryAxis = getDomainAxis(); RectangleEdge categoryEdge = Plot.resolveDomainAxisLocation( getDomainAxisLocation(), orientation); if (categoryAxis != null) { space = categoryAxis.reserveSpace(g2, this, plotArea, categoryEdge, space); } else { if (getDrawSharedDomainAxis()) { space = getDomainAxis().reserveSpace(g2, this, plotArea, categoryEdge, space); } } } Rectangle2D adjustedPlotArea = space.shrink(plotArea, null); // work out the maximum height or width of the non-shared axes... int n = this.subplots.size(); int totalWeight = 0; for (int i = 0; i < n; i++) { CategoryPlot sub = (CategoryPlot) this.subplots.get(i); totalWeight += sub.getWeight(); } this.subplotAreas = new Rectangle2D[n]; double x = adjustedPlotArea.getX(); double y = adjustedPlotArea.getY(); double usableSize = 0.0; if (orientation == PlotOrientation.HORIZONTAL) { usableSize = adjustedPlotArea.getWidth() - this.gap * (n - 1); } else if (orientation == PlotOrientation.VERTICAL) { usableSize = adjustedPlotArea.getHeight() - this.gap * (n - 1); } for (int i = 0; i < n; i++) { CategoryPlot plot = (CategoryPlot) this.subplots.get(i); // calculate sub-plot area if (orientation == PlotOrientation.HORIZONTAL) { double w = usableSize * plot.getWeight() / totalWeight; this.subplotAreas[i] = new Rectangle2D.Double(x, y, w, adjustedPlotArea.getHeight()); x = x + w + this.gap; } else if (orientation == PlotOrientation.VERTICAL) { double h = usableSize * plot.getWeight() / totalWeight; this.subplotAreas[i] = new Rectangle2D.Double(x, y, adjustedPlotArea.getWidth(), h); y = y + h + this.gap; } AxisSpace subSpace = plot.calculateRangeAxisSpace(g2, this.subplotAreas[i], null); space.ensureAtLeast(subSpace); } return space; } /** * Draws the plot on a Java 2D graphics device (such as the screen or a * printer). Will perform all the placement calculations for each of the * sub-plots and then tell these to draw themselves. * * @param g2 the graphics device. * @param area the area within which the plot (including axis labels) * should be drawn. * @param anchor the anchor point ({@code null} permitted). * @param parentState the state from the parent plot, if there is one. * @param info collects information about the drawing ({@code null} * permitted). */ @Override public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, PlotState parentState, PlotRenderingInfo info) { // set up info collection... if (info != null) { info.setPlotArea(area); } // adjust the drawing area for plot insets (if any)... RectangleInsets insets = getInsets(); area.setRect(area.getX() + insets.getLeft(), area.getY() + insets.getTop(), area.getWidth() - insets.getLeft() - insets.getRight(), area.getHeight() - insets.getTop() - insets.getBottom()); // calculate the data area... setFixedRangeAxisSpaceForSubplots(null); AxisSpace space = calculateAxisSpace(g2, area); Rectangle2D dataArea = space.shrink(area, null); // set the width and height of non-shared axis of all sub-plots setFixedRangeAxisSpaceForSubplots(space); // draw the shared axis CategoryAxis axis = getDomainAxis(); RectangleEdge domainEdge = getDomainAxisEdge(); double cursor = RectangleEdge.coordinate(dataArea, domainEdge); AxisState axisState = axis.draw(g2, cursor, area, dataArea, domainEdge, info); if (parentState == null) { parentState = new PlotState(); } parentState.getSharedAxisStates().put(axis, axisState); // draw all the subplots for (int i = 0; i < this.subplots.size(); i++) { CategoryPlot plot = (CategoryPlot) this.subplots.get(i); PlotRenderingInfo subplotInfo = null; if (info != null) { subplotInfo = new PlotRenderingInfo(info.getOwner()); info.addSubplotInfo(subplotInfo); } Point2D subAnchor = null; if (anchor != null && this.subplotAreas[i].contains(anchor)) { subAnchor = anchor; } plot.draw(g2, this.subplotAreas[i], subAnchor, parentState, subplotInfo); } if (info != null) { info.setDataArea(dataArea); } } /** * Sets the size (width or height, depending on the orientation of the * plot) for the range axis of each subplot. * * @param space the space ({@code null} permitted). */ protected void setFixedRangeAxisSpaceForSubplots(AxisSpace space) { Iterator iterator = this.subplots.iterator(); while (iterator.hasNext()) { CategoryPlot plot = (CategoryPlot) iterator.next(); plot.setFixedRangeAxisSpace(space, false); } } /** * Sets the orientation of the plot (and all subplots). * * @param orientation the orientation ({@code null} not permitted). */ @Override public void setOrientation(PlotOrientation orientation) { super.setOrientation(orientation); Iterator iterator = this.subplots.iterator(); while (iterator.hasNext()) { CategoryPlot plot = (CategoryPlot) iterator.next(); plot.setOrientation(orientation); } } /** * Sets the shadow generator for the plot (and all subplots) and sends * a {@link PlotChangeEvent} to all registered listeners. * * @param generator the new generator ({@code null} permitted). */ @Override public void setShadowGenerator(ShadowGenerator generator) { setNotify(false); super.setShadowGenerator(generator); Iterator iterator = this.subplots.iterator(); while (iterator.hasNext()) { CategoryPlot plot = (CategoryPlot) iterator.next(); plot.setShadowGenerator(generator); } setNotify(true); } /** * Returns a range representing the extent of the data values in this plot * (obtained from the subplots) that will be rendered against the specified * axis. NOTE: This method is intended for internal JFreeChart use, and * is public only so that code in the axis classes can call it. Since, * for this class, the domain axis is a {@link CategoryAxis} * (not a {@code ValueAxis}) and subplots have independent range axes, * the JFreeChart code will never call this method (although this is not * checked/enforced). * * @param axis the axis. * * @return The range. */ @Override public Range getDataRange(ValueAxis axis) { // override is only for documentation purposes return super.getDataRange(axis); } /** * Returns a collection of legend items for the plot. * * @return The legend items. */ @Override public LegendItemCollection getLegendItems() { LegendItemCollection result = getFixedLegendItems(); if (result == null) { result = new LegendItemCollection(); if (this.subplots != null) { Iterator iterator = this.subplots.iterator(); while (iterator.hasNext()) { CategoryPlot plot = (CategoryPlot) iterator.next(); LegendItemCollection more = plot.getLegendItems(); result.addAll(more); } } } return result; } /** * Returns an unmodifiable list of the categories contained in all the * subplots. * * @return The list. */ @Override public List getCategories() { List result = new java.util.ArrayList(); if (this.subplots != null) { Iterator iterator = this.subplots.iterator(); while (iterator.hasNext()) { CategoryPlot plot = (CategoryPlot) iterator.next(); List more = plot.getCategories(); Iterator moreIterator = more.iterator(); while (moreIterator.hasNext()) { Comparable category = (Comparable) moreIterator.next(); if (!result.contains(category)) { result.add(category); } } } } return Collections.unmodifiableList(result); } /** * Overridden to return the categories in the subplots. * * @param axis ignored. * * @return A list of the categories in the subplots. */ @Override public List getCategoriesForAxis(CategoryAxis axis) { // FIXME: this code means that it is not possible to use more than // one domain axis for the combined plots... return getCategories(); } /** * Handles a 'click' on the plot. * * @param x x-coordinate of the click. * @param y y-coordinate of the click. * @param info information about the plot's dimensions. * */ @Override public void handleClick(int x, int y, PlotRenderingInfo info) { Rectangle2D dataArea = info.getDataArea(); if (dataArea.contains(x, y)) { for (int i = 0; i < this.subplots.size(); i++) { CategoryPlot subplot = (CategoryPlot) this.subplots.get(i); PlotRenderingInfo subplotInfo = info.getSubplotInfo(i); subplot.handleClick(x, y, subplotInfo); } } } /** * Receives a {@link PlotChangeEvent} and responds by notifying all * listeners. * * @param event the event. */ @Override public void plotChanged(PlotChangeEvent event) { notifyListeners(event); } /** * Tests the plot for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof CombinedDomainCategoryPlot)) { return false; } CombinedDomainCategoryPlot that = (CombinedDomainCategoryPlot) obj; if (!that.canEqual(this)){ return false; } if (Double.compare(this.gap, that.gap) != 0) { return false; } if (!Objects.equals(this.subplots, that.subplots)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // Solves Problem: equals not symmetric return (other instanceof CombinedDomainCategoryPlot); } @Override public int hashCode() { int hash = super.hashCode(); hash = 97 * hash + Objects.hashCode(this.subplots); hash = 97 * hash + (int) (Double.doubleToLongBits(this.gap) ^ (Double.doubleToLongBits(this.gap) >>> 32)); return hash; } /** * Returns a clone of the plot. * * @return A clone. * * @throws CloneNotSupportedException this class will not throw this * exception, but subclasses (if any) might. */ @Override public Object clone() throws CloneNotSupportedException { CombinedDomainCategoryPlot result = (CombinedDomainCategoryPlot) super.clone(); result.subplots = (List) ObjectUtils.deepClone(this.subplots); for (Iterator it = result.subplots.iterator(); it.hasNext();) { Plot child = (Plot) it.next(); child.setParent(result); } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/CombinedDomainXYPlot.java000066400000000000000000000601741463604235500306770ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * CombinedDomainXYPlot.java * ------------------------- * (C) Copyright 2001-present, by Bill Kelemen and Contributors. * * Original Author: Bill Kelemen; * Contributor(s): David Gilbert; * Anthony Boulestreau; * David Basten; * Kevin Frechette (for ISTI); * Nicolas Brodu; * Petr Kubanek (bug 1606205); * Vladimir Shirokov (bug 986); */ package org.jfree.chart.plot; import java.awt.Graphics2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Objects; import org.jfree.chart.LegendItemCollection; import org.jfree.chart.axis.AxisSpace; import org.jfree.chart.axis.AxisState; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.PlotChangeEvent; import org.jfree.chart.event.PlotChangeListener; import org.jfree.chart.renderer.xy.XYItemRenderer; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.ShadowGenerator; import org.jfree.data.Range; import org.jfree.data.general.DatasetChangeEvent; import org.jfree.data.xy.XYDataset; /** * An extension of {@link XYPlot} that contains multiple subplots that share a * common domain axis. */ public class CombinedDomainXYPlot extends XYPlot implements PlotChangeListener { /** For serialization. */ private static final long serialVersionUID = -7765545541261907383L; /** Storage for the subplot references (possibly empty but never null). */ private List subplots; /** The gap between subplots. */ private double gap = 5.0; /** Temporary storage for the subplot areas. */ private transient Rectangle2D[] subplotAreas; // TODO: the subplot areas needs to be moved out of the plot into the plot // state /** * Default constructor. */ public CombinedDomainXYPlot() { this(new NumberAxis()); } /** * Creates a new combined plot that shares a domain axis among multiple * subplots. * * @param domainAxis the shared axis. */ public CombinedDomainXYPlot(ValueAxis domainAxis) { super(null, // no data in the parent plot domainAxis, null, // no range axis null); // no renderer this.subplots = new java.util.ArrayList<>(); } /** * Returns a string describing the type of plot. * * @return The type of plot. */ @Override public String getPlotType() { return "Combined_Domain_XYPlot"; } /** * Returns the gap between subplots, measured in Java2D units. * * @return The gap (in Java2D units). * * @see #setGap(double) */ public double getGap() { return this.gap; } /** * Sets the amount of space between subplots and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param gap the gap between subplots (in Java2D units). * * @see #getGap() */ public void setGap(double gap) { this.gap = gap; fireChangeEvent(); } /** * Returns {@code true} if the range is pannable for at least one subplot, * and {@code false} otherwise. * * @return A boolean. */ @Override public boolean isRangePannable() { for (XYPlot subplot : this.subplots) { if (subplot.isRangePannable()) { return true; } } return false; } /** * Sets the flag, on each of the subplots, that controls whether or not the * range is pannable. * * @param pannable the new flag value. */ @Override public void setRangePannable(boolean pannable) { for (XYPlot subplot : this.subplots) { subplot.setRangePannable(pannable); } } /** * Sets the orientation for the plot (also changes the orientation for all * the subplots to match). * * @param orientation the orientation ({@code null} not allowed). */ @Override public void setOrientation(PlotOrientation orientation) { super.setOrientation(orientation); for (XYPlot p : this.subplots) { p.setOrientation(orientation); } } /** * Sets the shadow generator for the plot (and all subplots) and sends * a {@link PlotChangeEvent} to all registered listeners. * * @param generator the new generator ({@code null} permitted). */ @Override public void setShadowGenerator(ShadowGenerator generator) { setNotify(false); super.setShadowGenerator(generator); for (XYPlot p : this.subplots) { p.setShadowGenerator(generator); } setNotify(true); } /** * Returns a range representing the extent of the data values in this plot * (obtained from the subplots) that will be rendered against the specified * axis. NOTE: This method is intended for internal JFreeChart use, and * is public only so that code in the axis classes can call it. Since * only the domain axis is shared between subplots, the JFreeChart code * will only call this method for the domain values (although this is not * checked/enforced). * * @param axis the axis. * * @return The range (possibly {@code null}). */ @Override public Range getDataRange(ValueAxis axis) { if (this.subplots == null) { return null; } Range result = null; for (XYPlot p : this.subplots) { result = Range.combine(result, p.getDataRange(axis)); } return result; } /** * Adds a subplot (with a default 'weight' of 1) and sends a * {@link PlotChangeEvent} to all registered listeners. *

* The domain axis for the subplot will be set to {@code null}. You * must ensure that the subplot has a non-null range axis. * * @param subplot the subplot ({@code null} not permitted). */ public void add(XYPlot subplot) { // defer argument checking add(subplot, 1); } /** * Adds a subplot with the specified weight and sends a * {@link PlotChangeEvent} to all registered listeners. The weight * determines how much space is allocated to the subplot relative to all * the other subplots. *

* The domain axis for the subplot will be set to {@code null}. You * must ensure that the subplot has a non-null range axis. * * @param subplot the subplot ({@code null} not permitted). * @param weight the weight (must be >= 1). */ public void add(XYPlot subplot, int weight) { Args.nullNotPermitted(subplot, "subplot"); if (weight <= 0) { throw new IllegalArgumentException("Require weight >= 1."); } // store the plot and its weight subplot.setParent(this); subplot.setWeight(weight); subplot.setInsets(RectangleInsets.ZERO_INSETS, false); subplot.setDomainAxis(null); subplot.addChangeListener(this); this.subplots.add(subplot); ValueAxis axis = getDomainAxis(); if (axis != null) { axis.configure(); } fireChangeEvent(); } /** * Removes a subplot from the combined chart and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param subplot the subplot ({@code null} not permitted). */ public void remove(XYPlot subplot) { Args.nullNotPermitted(subplot, "subplot"); int position = -1; int size = this.subplots.size(); int i = 0; while (position == -1 && i < size) { if (this.subplots.get(i) == subplot) { position = i; } i++; } if (position != -1) { this.subplots.remove(position); subplot.setParent(null); subplot.removeChangeListener(this); ValueAxis domain = getDomainAxis(); if (domain != null) { domain.configure(); } fireChangeEvent(); } } /** * Returns the list of subplots. The returned list may be empty, but is * never {@code null}. * * @return An unmodifiable list of subplots. */ public List getSubplots() { return Collections.unmodifiableList(this.subplots); } /** * Calculates the axis space required. * * @param g2 the graphics device. * @param plotArea the plot area. * * @return The space. */ @Override protected AxisSpace calculateAxisSpace(Graphics2D g2, Rectangle2D plotArea) { AxisSpace space = new AxisSpace(); PlotOrientation orientation = getOrientation(); // work out the space required by the domain axis... AxisSpace fixed = getFixedDomainAxisSpace(); if (fixed != null) { if (orientation == PlotOrientation.HORIZONTAL) { space.setLeft(fixed.getLeft()); space.setRight(fixed.getRight()); } else if (orientation == PlotOrientation.VERTICAL) { space.setTop(fixed.getTop()); space.setBottom(fixed.getBottom()); } } else { ValueAxis xAxis = getDomainAxis(); RectangleEdge xEdge = Plot.resolveDomainAxisLocation( getDomainAxisLocation(), orientation); if (xAxis != null) { space = xAxis.reserveSpace(g2, this, plotArea, xEdge, space); } } Rectangle2D adjustedPlotArea = space.shrink(plotArea, null); // work out the maximum height or width of the non-shared axes... int n = this.subplots.size(); int totalWeight = 0; for (int i = 0; i < n; i++) { XYPlot sub = (XYPlot) this.subplots.get(i); totalWeight += sub.getWeight(); } this.subplotAreas = new Rectangle2D[n]; double x = adjustedPlotArea.getX(); double y = adjustedPlotArea.getY(); double usableSize = 0.0; if (orientation == PlotOrientation.HORIZONTAL) { usableSize = adjustedPlotArea.getWidth() - this.gap * (n - 1); } else if (orientation == PlotOrientation.VERTICAL) { usableSize = adjustedPlotArea.getHeight() - this.gap * (n - 1); } for (int i = 0; i < n; i++) { XYPlot plot = (XYPlot) this.subplots.get(i); // calculate sub-plot area if (orientation == PlotOrientation.HORIZONTAL) { double w = usableSize * plot.getWeight() / totalWeight; this.subplotAreas[i] = new Rectangle2D.Double(x, y, w, adjustedPlotArea.getHeight()); x = x + w + this.gap; } else if (orientation == PlotOrientation.VERTICAL) { double h = usableSize * plot.getWeight() / totalWeight; this.subplotAreas[i] = new Rectangle2D.Double(x, y, adjustedPlotArea.getWidth(), h); y = y + h + this.gap; } AxisSpace subSpace = plot.calculateRangeAxisSpace(g2, this.subplotAreas[i], null); space.ensureAtLeast(subSpace); } return space; } /** * Draws the plot within the specified area on a graphics device. * * @param g2 the graphics device. * @param area the plot area (in Java2D space). * @param anchor an anchor point in Java2D space ({@code null} * permitted). * @param parentState the state from the parent plot, if there is one * ({@code null} permitted). * @param info collects chart drawing information ({@code null} * permitted). */ @Override public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, PlotState parentState, PlotRenderingInfo info) { // set up info collection... if (info != null) { info.setPlotArea(area); } // adjust the drawing area for plot insets (if any)... RectangleInsets insets = getInsets(); insets.trim(area); setFixedRangeAxisSpaceForSubplots(null); AxisSpace space = calculateAxisSpace(g2, area); Rectangle2D dataArea = space.shrink(area, null); // set the width and height of non-shared axis of all sub-plots setFixedRangeAxisSpaceForSubplots(space); // draw the shared axis ValueAxis axis = getDomainAxis(); RectangleEdge edge = getDomainAxisEdge(); double cursor = RectangleEdge.coordinate(dataArea, edge); AxisState axisState = axis.draw(g2, cursor, area, dataArea, edge, info); if (parentState == null) { parentState = new PlotState(); } parentState.getSharedAxisStates().put(axis, axisState); // draw all the subplots for (int i = 0; i < this.subplots.size(); i++) { XYPlot plot = (XYPlot) this.subplots.get(i); PlotRenderingInfo subplotInfo = null; if (info != null) { subplotInfo = new PlotRenderingInfo(info.getOwner()); info.addSubplotInfo(subplotInfo); } plot.draw(g2, this.subplotAreas[i], anchor, parentState, subplotInfo); } if (info != null) { info.setDataArea(dataArea); } } /** * Returns a collection of legend items for the plot. * * @return The legend items. */ @Override public LegendItemCollection getLegendItems() { LegendItemCollection result = getFixedLegendItems(); if (result == null) { result = new LegendItemCollection(); if (this.subplots != null) { Iterator iterator = this.subplots.iterator(); while (iterator.hasNext()) { XYPlot plot = (XYPlot) iterator.next(); LegendItemCollection more = plot.getLegendItems(); result.addAll(more); } } } return result; } /** * Multiplies the range on the range axis/axes by the specified factor. * * @param factor the zoom factor. * @param info the plot rendering info ({@code null} not permitted). * @param source the source point ({@code null} not permitted). */ @Override public void zoomRangeAxes(double factor, PlotRenderingInfo info, Point2D source) { zoomRangeAxes(factor, info, source, false); } /** * Multiplies the range on the range axis/axes by the specified factor. * * @param factor the zoom factor. * @param state the plot state. * @param source the source point (in Java2D coordinates). * @param useAnchor use source point as zoom anchor? */ @Override public void zoomRangeAxes(double factor, PlotRenderingInfo state, Point2D source, boolean useAnchor) { // delegate 'state' and 'source' argument checks... XYPlot subplot = findSubplot(state, source); if (subplot != null) { subplot.zoomRangeAxes(factor, state, source, useAnchor); } else { // if the source point doesn't fall within a subplot, we do the // zoom on all subplots... for (XYPlot p : this.subplots) { p.zoomRangeAxes(factor, state, source, useAnchor); } } } /** * Zooms in on the range axes. * * @param lowerPercent the lower bound. * @param upperPercent the upper bound. * @param info the plot rendering info ({@code null} not permitted). * @param source the source point ({@code null} not permitted). */ @Override public void zoomRangeAxes(double lowerPercent, double upperPercent, PlotRenderingInfo info, Point2D source) { // delegate 'info' and 'source' argument checks... XYPlot subplot = findSubplot(info, source); if (subplot != null) { subplot.zoomRangeAxes(lowerPercent, upperPercent, info, source); } else { // if the source point doesn't fall within a subplot, we do the // zoom on all subplots... for (XYPlot p : this.subplots) { p.zoomRangeAxes(lowerPercent, upperPercent, info, source); } } } /** * Pans all range axes by the specified percentage. * * @param panRange the distance to pan (as a percentage of the axis length). * @param info the plot info ({@code null} not permitted). * @param source the source point where the pan action started. */ @Override public void panRangeAxes(double panRange, PlotRenderingInfo info, Point2D source) { XYPlot subplot = findSubplot(info, source); if (subplot == null) { return; } if (!subplot.isRangePannable()) { return; } PlotRenderingInfo subplotInfo = info.getSubplotInfo( info.getSubplotIndex(source)); if (subplotInfo == null) { return; } for (int i = 0; i < subplot.getRangeAxisCount(); i++) { ValueAxis rangeAxis = subplot.getRangeAxis(i); if (rangeAxis != null) { rangeAxis.pan(panRange); } } } /** * Returns the subplot (if any) that contains the (x, y) point (specified * in Java2D space). * * @param info the chart rendering info ({@code null} not permitted). * @param source the source point ({@code null} not permitted). * * @return A subplot (possibly {@code null}). */ public XYPlot findSubplot(PlotRenderingInfo info, Point2D source) { Args.nullNotPermitted(info, "info"); Args.nullNotPermitted(source, "source"); XYPlot result = null; int subplotIndex = info.getSubplotIndex(source); if (subplotIndex >= 0) { result = (XYPlot) this.subplots.get(subplotIndex); } return result; } /** * Sets the item renderer FOR ALL SUBPLOTS. Registered listeners are * notified that the plot has been modified. *

* Note: usually you will want to set the renderer independently for each * subplot, which is NOT what this method does. * * @param renderer the new renderer. */ @Override public void setRenderer(XYItemRenderer renderer) { super.setRenderer(renderer); // not strictly necessary, since the // renderer set for the // parent plot is not used for (XYPlot p : this.subplots) { p.setRenderer(renderer); } } /** * Sets the fixed range axis space and sends a {@link PlotChangeEvent} to * all registered listeners. * * @param space the space ({@code null} permitted). */ @Override public void setFixedRangeAxisSpace(AxisSpace space) { super.setFixedRangeAxisSpace(space); setFixedRangeAxisSpaceForSubplots(space); fireChangeEvent(); } /** * Sets the size (width or height, depending on the orientation of the * plot) for the domain axis of each subplot. * * @param space the space. */ protected void setFixedRangeAxisSpaceForSubplots(AxisSpace space) { for (XYPlot p : this.subplots) { p.setFixedRangeAxisSpace(space, false); } } /** * Handles a 'click' on the plot by updating the anchor values. * * @param x x-coordinate, where the click occurred. * @param y y-coordinate, where the click occurred. * @param info object containing information about the plot dimensions. */ @Override public void handleClick(int x, int y, PlotRenderingInfo info) { Rectangle2D dataArea = info.getDataArea(); if (dataArea.contains(x, y)) { for (int i = 0; i < this.subplots.size(); i++) { XYPlot subplot = (XYPlot) this.subplots.get(i); PlotRenderingInfo subplotInfo = info.getSubplotInfo(i); subplot.handleClick(x, y, subplotInfo); } } } /** * Receives notification of a change to the plot's dataset. *

* The axis ranges are updated if necessary. * * @param event information about the event (not used here). */ @Override public void datasetChanged(DatasetChangeEvent event) { super.datasetChanged(event); if (this.subplots == null) { return; // this can happen during plot construction } XYDataset dataset = null; if (event.getDataset() instanceof XYDataset) { dataset = (XYDataset) event.getDataset(); } for (XYPlot subplot : this.subplots) { if (subplot.indexOf(dataset) >= 0) { subplot.configureRangeAxes(); } } } /** * Receives a {@link PlotChangeEvent} and responds by notifying all * listeners. * * @param event the event. */ @Override public void plotChanged(PlotChangeEvent event) { notifyListeners(event); } /** * Tests this plot for equality with another object. * * @param obj the other object. * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof CombinedDomainXYPlot)) { return false; } CombinedDomainXYPlot that = (CombinedDomainXYPlot) obj; if (this.gap != that.gap) { return false; } if (!Objects.equals(this.subplots, that.subplots)) { return false; } return super.equals(obj); } /** * Returns a clone of the annotation. * * @return A clone. * * @throws CloneNotSupportedException this class will not throw this * exception, but subclasses (if any) might. */ @Override public Object clone() throws CloneNotSupportedException { CombinedDomainXYPlot result = (CombinedDomainXYPlot) super.clone(); result.subplots = (List) ObjectUtils.deepClone(this.subplots); for (Iterator it = result.subplots.iterator(); it.hasNext();) { Plot child = (Plot) it.next(); child.setParent(result); } // after setting up all the subplots, the shared domain axis may need // reconfiguring ValueAxis domainAxis = result.getDomainAxis(); if (domainAxis != null) { domainAxis.configure(); } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/CombinedRangeCategoryPlot.java000066400000000000000000000475301463604235500317420ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------ * CombinedRangeCategoryPlot.java * ------------------------------ * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Nicolas Brodu; * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.plot; import java.awt.Graphics2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Objects; import org.jfree.chart.LegendItemCollection; import org.jfree.chart.axis.AxisSpace; import org.jfree.chart.axis.AxisState; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.PlotChangeEvent; import org.jfree.chart.event.PlotChangeListener; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.ShadowGenerator; import org.jfree.data.Range; /** * A combined category plot where the range axis is shared. */ public class CombinedRangeCategoryPlot extends CategoryPlot implements PlotChangeListener { /** For serialization. */ private static final long serialVersionUID = 7260210007554504515L; /** Storage for the subplot references. */ private List subplots; /** The gap between subplots. */ private double gap; /** Temporary storage for the subplot areas. */ private transient Rectangle2D[] subplotArea; // TODO: move to plot state /** * Default constructor. */ public CombinedRangeCategoryPlot() { this(new NumberAxis()); } /** * Creates a new plot. * * @param rangeAxis the shared range axis. */ public CombinedRangeCategoryPlot(ValueAxis rangeAxis) { super(null, null, rangeAxis, null); this.subplots = new java.util.ArrayList(); this.gap = 5.0; } /** * Returns the space between subplots. * * @return The gap (in Java2D units). */ public double getGap() { return this.gap; } /** * Sets the amount of space between subplots and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param gap the gap between subplots (in Java2D units). */ public void setGap(double gap) { this.gap = gap; fireChangeEvent(); } /** * Adds a subplot (with a default 'weight' of 1) and sends a * {@link PlotChangeEvent} to all registered listeners. *

* You must ensure that the subplot has a non-null domain axis. The range * axis for the subplot will be set to {@code null}. * * @param subplot the subplot ({@code null} not permitted). */ public void add(CategoryPlot subplot) { // defer argument checking add(subplot, 1); } /** * Adds a subplot and sends a {@link PlotChangeEvent} to all registered * listeners. *

* You must ensure that the subplot has a non-null domain axis. The range * axis for the subplot will be set to {@code null}. * * @param subplot the subplot ({@code null} not permitted). * @param weight the weight (must be >= 1). */ public void add(CategoryPlot subplot, int weight) { Args.nullNotPermitted(subplot, "subplot"); if (weight <= 0) { throw new IllegalArgumentException("Require weight >= 1."); } // store the plot and its weight subplot.setParent(this); subplot.setWeight(weight); subplot.setInsets(new RectangleInsets(0.0, 0.0, 0.0, 0.0)); subplot.setRangeAxis(null); subplot.setOrientation(getOrientation()); subplot.addChangeListener(this); this.subplots.add(subplot); // configure the range axis... ValueAxis axis = getRangeAxis(); if (axis != null) { axis.configure(); } fireChangeEvent(); } /** * Removes a subplot from the combined chart. * * @param subplot the subplot ({@code null} not permitted). */ public void remove(CategoryPlot subplot) { Args.nullNotPermitted(subplot, "subplot"); int position = -1; int size = this.subplots.size(); int i = 0; while (position == -1 && i < size) { if (this.subplots.get(i) == subplot) { position = i; } i++; } if (position != -1) { this.subplots.remove(position); subplot.setParent(null); subplot.removeChangeListener(this); ValueAxis range = getRangeAxis(); if (range != null) { range.configure(); } ValueAxis range2 = getRangeAxis(1); if (range2 != null) { range2.configure(); } fireChangeEvent(); } } /** * Returns the list of subplots. The returned list may be empty, but is * never {@code null}. * * @return An unmodifiable list of subplots. */ public List getSubplots() { if (this.subplots != null) { return Collections.unmodifiableList(this.subplots); } else { return Collections.EMPTY_LIST; } } /** * Calculates the space required for the axes. * * @param g2 the graphics device. * @param plotArea the plot area. * * @return The space required for the axes. */ @Override protected AxisSpace calculateAxisSpace(Graphics2D g2, Rectangle2D plotArea) { AxisSpace space = new AxisSpace(); PlotOrientation orientation = getOrientation(); // work out the space required by the domain axis... AxisSpace fixed = getFixedRangeAxisSpace(); if (fixed != null) { if (orientation == PlotOrientation.VERTICAL) { space.setLeft(fixed.getLeft()); space.setRight(fixed.getRight()); } else if (orientation == PlotOrientation.HORIZONTAL) { space.setTop(fixed.getTop()); space.setBottom(fixed.getBottom()); } } else { ValueAxis valueAxis = getRangeAxis(); RectangleEdge valueEdge = Plot.resolveRangeAxisLocation( getRangeAxisLocation(), orientation); if (valueAxis != null) { space = valueAxis.reserveSpace(g2, this, plotArea, valueEdge, space); } } Rectangle2D adjustedPlotArea = space.shrink(plotArea, null); // work out the maximum height or width of the non-shared axes... int n = this.subplots.size(); int totalWeight = 0; for (int i = 0; i < n; i++) { CategoryPlot sub = (CategoryPlot) this.subplots.get(i); totalWeight += sub.getWeight(); } // calculate plotAreas of all sub-plots, maximum vertical/horizontal // axis width/height this.subplotArea = new Rectangle2D[n]; double x = adjustedPlotArea.getX(); double y = adjustedPlotArea.getY(); double usableSize = 0.0; if (orientation == PlotOrientation.VERTICAL) { usableSize = adjustedPlotArea.getWidth() - this.gap * (n - 1); } else if (orientation == PlotOrientation.HORIZONTAL) { usableSize = adjustedPlotArea.getHeight() - this.gap * (n - 1); } for (int i = 0; i < n; i++) { CategoryPlot plot = (CategoryPlot) this.subplots.get(i); // calculate sub-plot area if (orientation == PlotOrientation.VERTICAL) { double w = usableSize * plot.getWeight() / totalWeight; this.subplotArea[i] = new Rectangle2D.Double(x, y, w, adjustedPlotArea.getHeight()); x = x + w + this.gap; } else if (orientation == PlotOrientation.HORIZONTAL) { double h = usableSize * plot.getWeight() / totalWeight; this.subplotArea[i] = new Rectangle2D.Double(x, y, adjustedPlotArea.getWidth(), h); y = y + h + this.gap; } AxisSpace subSpace = plot.calculateDomainAxisSpace(g2, this.subplotArea[i], null); space.ensureAtLeast(subSpace); } return space; } /** * Draws the plot on a Java 2D graphics device (such as the screen or a * printer). Will perform all the placement calculations for each * sub-plots and then tell these to draw themselves. * * @param g2 the graphics device. * @param area the area within which the plot (including axis labels) * should be drawn. * @param anchor the anchor point ({@code null} permitted). * @param parentState the parent state. * @param info collects information about the drawing ({@code null} * permitted). */ @Override public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, PlotState parentState, PlotRenderingInfo info) { // set up info collection... if (info != null) { info.setPlotArea(area); } // adjust the drawing area for plot insets (if any)... RectangleInsets insets = getInsets(); insets.trim(area); // calculate the data area... AxisSpace space = calculateAxisSpace(g2, area); Rectangle2D dataArea = space.shrink(area, null); // set the width and height of non-shared axis of all sub-plots setFixedDomainAxisSpaceForSubplots(space); // draw the shared axis ValueAxis axis = getRangeAxis(); RectangleEdge rangeEdge = getRangeAxisEdge(); double cursor = RectangleEdge.coordinate(dataArea, rangeEdge); AxisState state = axis.draw(g2, cursor, area, dataArea, rangeEdge, info); if (parentState == null) { parentState = new PlotState(); } parentState.getSharedAxisStates().put(axis, state); // draw all the charts for (int i = 0; i < this.subplots.size(); i++) { CategoryPlot plot = (CategoryPlot) this.subplots.get(i); PlotRenderingInfo subplotInfo = null; if (info != null) { subplotInfo = new PlotRenderingInfo(info.getOwner()); info.addSubplotInfo(subplotInfo); } Point2D subAnchor = null; if (anchor != null && this.subplotArea[i].contains(anchor)) { subAnchor = anchor; } plot.draw(g2, this.subplotArea[i], subAnchor, parentState, subplotInfo); } if (info != null) { info.setDataArea(dataArea); } } /** * Sets the orientation for the plot (and all the subplots). * * @param orientation the orientation. */ @Override public void setOrientation(PlotOrientation orientation) { super.setOrientation(orientation); Iterator iterator = this.subplots.iterator(); while (iterator.hasNext()) { CategoryPlot plot = (CategoryPlot) iterator.next(); plot.setOrientation(orientation); } } /** * Sets the shadow generator for the plot (and all subplots) and sends * a {@link PlotChangeEvent} to all registered listeners. * * @param generator the new generator ({@code null} permitted). */ @Override public void setShadowGenerator(ShadowGenerator generator) { setNotify(false); super.setShadowGenerator(generator); Iterator iterator = this.subplots.iterator(); while (iterator.hasNext()) { CategoryPlot plot = (CategoryPlot) iterator.next(); plot.setShadowGenerator(generator); } setNotify(true); } /** * Returns a range representing the extent of the data values in this plot * (obtained from the subplots) that will be rendered against the specified * axis. NOTE: This method is intended for internal JFreeChart use, and * is public only so that code in the axis classes can call it. Since * only the range axis is shared between subplots, the JFreeChart code * will only call this method for the range values (although this is not * checked/enforced). * * @param axis the axis. * * @return The range. */ @Override public Range getDataRange(ValueAxis axis) { Range result = null; if (this.subplots != null) { Iterator iterator = this.subplots.iterator(); while (iterator.hasNext()) { CategoryPlot subplot = (CategoryPlot) iterator.next(); result = Range.combine(result, subplot.getDataRange(axis)); } } return result; } /** * Returns a collection of legend items for the plot. * * @return The legend items. */ @Override public LegendItemCollection getLegendItems() { LegendItemCollection result = getFixedLegendItems(); if (result == null) { result = new LegendItemCollection(); if (this.subplots != null) { Iterator iterator = this.subplots.iterator(); while (iterator.hasNext()) { CategoryPlot plot = (CategoryPlot) iterator.next(); LegendItemCollection more = plot.getLegendItems(); result.addAll(more); } } } return result; } /** * Sets the size (width or height, depending on the orientation of the * plot) for the domain axis of each subplot. * * @param space the space. */ protected void setFixedDomainAxisSpaceForSubplots(AxisSpace space) { Iterator iterator = this.subplots.iterator(); while (iterator.hasNext()) { CategoryPlot plot = (CategoryPlot) iterator.next(); plot.setFixedDomainAxisSpace(space, false); } } /** * Handles a 'click' on the plot by updating the anchor value. * * @param x x-coordinate of the click. * @param y y-coordinate of the click. * @param info information about the plot's dimensions. * */ @Override public void handleClick(int x, int y, PlotRenderingInfo info) { Rectangle2D dataArea = info.getDataArea(); if (dataArea.contains(x, y)) { for (int i = 0; i < this.subplots.size(); i++) { CategoryPlot subplot = (CategoryPlot) this.subplots.get(i); PlotRenderingInfo subplotInfo = info.getSubplotInfo(i); subplot.handleClick(x, y, subplotInfo); } } } /** * Receives a {@link PlotChangeEvent} and responds by notifying all * listeners. * * @param event the event. */ @Override public void plotChanged(PlotChangeEvent event) { notifyListeners(event); } /** * Tests the plot for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof CombinedRangeCategoryPlot)) { return false; } CombinedRangeCategoryPlot that = (CombinedRangeCategoryPlot) obj; if (!that.canEqual(this)){ return false; } if (Double.compare(this.gap, that.gap) != 0) { return false; } if (!Objects.equals(this.subplots, that.subplots)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // Solves Problem: equals not symmetric return (other instanceof CombinedRangeCategoryPlot); } @Override public int hashCode() { int hash = super.hashCode(); hash = 61 * hash + Objects.hashCode(this.subplots); hash = 61 * hash + (int) (Double.doubleToLongBits(this.gap) ^ (Double.doubleToLongBits(this.gap) >>> 32)); return hash; } /** * Returns a clone of the plot. * * @return A clone. * * @throws CloneNotSupportedException this class will not throw this * exception, but subclasses (if any) might. */ @Override public Object clone() throws CloneNotSupportedException { CombinedRangeCategoryPlot result = (CombinedRangeCategoryPlot) super.clone(); result.subplots = (List) ObjectUtils.deepClone(this.subplots); for (Iterator it = result.subplots.iterator(); it.hasNext();) { Plot child = (Plot) it.next(); child.setParent(result); } // after setting up all the subplots, the shared range axis may need // reconfiguring ValueAxis rangeAxis = result.getRangeAxis(); if (rangeAxis != null) { rangeAxis.configure(); } return result; } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); // the range axis is deserialized before the subplots, so its value // range is likely to be incorrect... ValueAxis rangeAxis = getRangeAxis(); if (rangeAxis != null) { rangeAxis.configure(); } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/CombinedRangeXYPlot.java000066400000000000000000000576231463604235500305310ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * CombinedRangeXYPlot.java * ------------------------ * (C) Copyright 2001-present, by Bill Kelemen and Contributors. * * Original Author: Bill Kelemen; * Contributor(s): David Gilbert; * Anthony Boulestreau; * David Basten; * Kevin Frechette (for ISTI); * Arnaud Lelievre; * Nicolas Brodu; * Petr Kubanek (bug 1606205); */ package org.jfree.chart.plot; import java.awt.Graphics2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Objects; import org.jfree.chart.LegendItemCollection; import org.jfree.chart.axis.AxisSpace; import org.jfree.chart.axis.AxisState; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.PlotChangeEvent; import org.jfree.chart.event.PlotChangeListener; import org.jfree.chart.renderer.xy.XYItemRenderer; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.ShadowGenerator; import org.jfree.data.Range; /** * An extension of {@link XYPlot} that contains multiple subplots that share a * common range axis. */ public class CombinedRangeXYPlot extends XYPlot implements PlotChangeListener { /** For serialization. */ private static final long serialVersionUID = -5177814085082031168L; /** Storage for the subplot references. */ private List subplots; /** The gap between subplots. */ private double gap = 5.0; /** Temporary storage for the subplot areas. */ private transient Rectangle2D[] subplotAreas; /** * Default constructor. */ public CombinedRangeXYPlot() { this(new NumberAxis()); } /** * Creates a new plot. * * @param rangeAxis the shared axis. */ public CombinedRangeXYPlot(ValueAxis rangeAxis) { super(null, // no data in the parent plot null, rangeAxis, null); this.subplots = new java.util.ArrayList<>(); } /** * Returns a string describing the type of plot. * * @return The type of plot. */ @Override public String getPlotType() { return localizationResources.getString("Combined_Range_XYPlot"); } /** * Returns the space between subplots. * * @return The gap. * * @see #setGap(double) */ public double getGap() { return this.gap; } /** * Sets the amount of space between subplots. * * @param gap the gap between subplots. * * @see #getGap() */ public void setGap(double gap) { this.gap = gap; } /** * Returns {@code true} if the domain is pannable for at least one subplot, * and {@code false} otherwise. * * @return A boolean. */ @Override public boolean isDomainPannable() { for (XYPlot subplot : this.subplots) { if (subplot.isDomainPannable()) { return true; } } return false; } /** * Sets the flag, on each of the subplots, that controls whether or not the * domain is pannable. * * @param pannable the new flag value. */ @Override public void setDomainPannable(boolean pannable) { for (XYPlot subplot : this.subplots) { subplot.setDomainPannable(pannable); } } /** * Adds a subplot, with a default 'weight' of 1. *

* You must ensure that the subplot has a non-null domain axis. The range * axis for the subplot will be set to {@code null}. * * @param subplot the subplot. */ public void add(XYPlot subplot) { add(subplot, 1); } /** * Adds a subplot with a particular weight (greater than or equal to one). * The weight determines how much space is allocated to the subplot * relative to all the other subplots. *

* You must ensure that the subplot has a non-null domain axis. The range * axis for the subplot will be set to {@code null}. * * @param subplot the subplot ({@code null} not permitted). * @param weight the weight (must be 1 or greater). */ public void add(XYPlot subplot, int weight) { Args.nullNotPermitted(subplot, "subplot"); if (weight <= 0) { String msg = "The 'weight' must be positive."; throw new IllegalArgumentException(msg); } // store the plot and its weight subplot.setParent(this); subplot.setWeight(weight); subplot.setInsets(new RectangleInsets(0.0, 0.0, 0.0, 0.0)); subplot.setRangeAxis(null); subplot.addChangeListener(this); this.subplots.add(subplot); configureRangeAxes(); fireChangeEvent(); } /** * Removes a subplot from the combined chart. * * @param subplot the subplot ({@code null} not permitted). */ public void remove(XYPlot subplot) { Args.nullNotPermitted(subplot, "subplot"); int position = -1; int size = this.subplots.size(); int i = 0; while (position == -1 && i < size) { if (this.subplots.get(i) == subplot) { position = i; } i++; } if (position != -1) { this.subplots.remove(position); subplot.setParent(null); subplot.removeChangeListener(this); configureRangeAxes(); fireChangeEvent(); } } /** * Returns the list of subplots. The returned list may be empty, but is * never {@code null}. * * @return An unmodifiable list of subplots. */ public List getSubplots() { if (this.subplots != null) { return Collections.unmodifiableList(this.subplots); } else { return Collections.EMPTY_LIST; } } /** * Calculates the space required for the axes. * * @param g2 the graphics device. * @param plotArea the plot area. * * @return The space required for the axes. */ @Override protected AxisSpace calculateAxisSpace(Graphics2D g2, Rectangle2D plotArea) { AxisSpace space = new AxisSpace(); PlotOrientation orientation = getOrientation(); // work out the space required by the domain axis... AxisSpace fixed = getFixedRangeAxisSpace(); if (fixed != null) { if (orientation == PlotOrientation.VERTICAL) { space.setLeft(fixed.getLeft()); space.setRight(fixed.getRight()); } else if (orientation == PlotOrientation.HORIZONTAL) { space.setTop(fixed.getTop()); space.setBottom(fixed.getBottom()); } } else { ValueAxis valueAxis = getRangeAxis(); RectangleEdge valueEdge = Plot.resolveRangeAxisLocation( getRangeAxisLocation(), orientation ); if (valueAxis != null) { space = valueAxis.reserveSpace(g2, this, plotArea, valueEdge, space); } } Rectangle2D adjustedPlotArea = space.shrink(plotArea, null); // work out the maximum height or width of the non-shared axes... int n = this.subplots.size(); int totalWeight = 0; for (int i = 0; i < n; i++) { XYPlot sub = (XYPlot) this.subplots.get(i); totalWeight += sub.getWeight(); } // calculate plotAreas of all sub-plots, maximum vertical/horizontal // axis width/height this.subplotAreas = new Rectangle2D[n]; double x = adjustedPlotArea.getX(); double y = adjustedPlotArea.getY(); double usableSize = 0.0; if (orientation == PlotOrientation.VERTICAL) { usableSize = adjustedPlotArea.getWidth() - this.gap * (n - 1); } else if (orientation == PlotOrientation.HORIZONTAL) { usableSize = adjustedPlotArea.getHeight() - this.gap * (n - 1); } for (int i = 0; i < n; i++) { XYPlot plot = (XYPlot) this.subplots.get(i); // calculate sub-plot area if (orientation == PlotOrientation.VERTICAL) { double w = usableSize * plot.getWeight() / totalWeight; this.subplotAreas[i] = new Rectangle2D.Double(x, y, w, adjustedPlotArea.getHeight()); x = x + w + this.gap; } else if (orientation == PlotOrientation.HORIZONTAL) { double h = usableSize * plot.getWeight() / totalWeight; this.subplotAreas[i] = new Rectangle2D.Double(x, y, adjustedPlotArea.getWidth(), h); y = y + h + this.gap; } AxisSpace subSpace = plot.calculateDomainAxisSpace(g2, this.subplotAreas[i], null); space.ensureAtLeast(subSpace); } return space; } /** * Draws the plot within the specified area on a graphics device. * * @param g2 the graphics device. * @param area the plot area (in Java2D space). * @param anchor an anchor point in Java2D space ({@code null} * permitted). * @param parentState the state from the parent plot, if there is one * ({@code null} permitted). * @param info collects chart drawing information ({@code null} * permitted). */ @Override public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, PlotState parentState, PlotRenderingInfo info) { // set up info collection... if (info != null) { info.setPlotArea(area); } // adjust the drawing area for plot insets (if any)... RectangleInsets insets = getInsets(); insets.trim(area); AxisSpace space = calculateAxisSpace(g2, area); Rectangle2D dataArea = space.shrink(area, null); //this.axisOffset.trim(dataArea); // set the width and height of non-shared axis of all sub-plots setFixedDomainAxisSpaceForSubplots(space); // draw the shared axis ValueAxis axis = getRangeAxis(); RectangleEdge edge = getRangeAxisEdge(); double cursor = RectangleEdge.coordinate(dataArea, edge); AxisState axisState = axis.draw(g2, cursor, area, dataArea, edge, info); if (parentState == null) { parentState = new PlotState(); } parentState.getSharedAxisStates().put(axis, axisState); // draw all the charts for (int i = 0; i < this.subplots.size(); i++) { XYPlot plot = (XYPlot) this.subplots.get(i); PlotRenderingInfo subplotInfo = null; if (info != null) { subplotInfo = new PlotRenderingInfo(info.getOwner()); info.addSubplotInfo(subplotInfo); } plot.draw(g2, this.subplotAreas[i], anchor, parentState, subplotInfo); } if (info != null) { info.setDataArea(dataArea); } } /** * Returns a collection of legend items for the plot. * * @return The legend items. */ @Override public LegendItemCollection getLegendItems() { LegendItemCollection result = getFixedLegendItems(); if (result == null) { result = new LegendItemCollection(); if (this.subplots != null) { Iterator iterator = this.subplots.iterator(); while (iterator.hasNext()) { XYPlot plot = (XYPlot) iterator.next(); LegendItemCollection more = plot.getLegendItems(); result.addAll(more); } } } return result; } /** * Multiplies the range on the domain axis/axes by the specified factor. * * @param factor the zoom factor. * @param info the plot rendering info ({@code null} not permitted). * @param source the source point ({@code null} not permitted). */ @Override public void zoomDomainAxes(double factor, PlotRenderingInfo info, Point2D source) { zoomDomainAxes(factor, info, source, false); } /** * Multiplies the range on the domain axis/axes by the specified factor. * * @param factor the zoom factor. * @param info the plot rendering info ({@code null} not permitted). * @param source the source point ({@code null} not permitted). * @param useAnchor zoom about the anchor point? */ @Override public void zoomDomainAxes(double factor, PlotRenderingInfo info, Point2D source, boolean useAnchor) { // delegate 'info' and 'source' argument checks... XYPlot subplot = findSubplot(info, source); if (subplot != null) { subplot.zoomDomainAxes(factor, info, source, useAnchor); } else { // if the source point doesn't fall within a subplot, we do the // zoom on all subplots... Iterator iterator = getSubplots().iterator(); while (iterator.hasNext()) { subplot = (XYPlot) iterator.next(); subplot.zoomDomainAxes(factor, info, source, useAnchor); } } } /** * Zooms in on the domain axes. * * @param lowerPercent the lower bound. * @param upperPercent the upper bound. * @param info the plot rendering info ({@code null} not permitted). * @param source the source point ({@code null} not permitted). */ @Override public void zoomDomainAxes(double lowerPercent, double upperPercent, PlotRenderingInfo info, Point2D source) { // delegate 'info' and 'source' argument checks... XYPlot subplot = findSubplot(info, source); if (subplot != null) { subplot.zoomDomainAxes(lowerPercent, upperPercent, info, source); } else { // if the source point doesn't fall within a subplot, we do the // zoom on all subplots... Iterator iterator = getSubplots().iterator(); while (iterator.hasNext()) { subplot = (XYPlot) iterator.next(); subplot.zoomDomainAxes(lowerPercent, upperPercent, info, source); } } } /** * Pans all domain axes by the specified percentage. * * @param panRange the distance to pan (as a percentage of the axis length). * @param info the plot info * @param source the source point where the pan action started. */ @Override public void panDomainAxes(double panRange, PlotRenderingInfo info, Point2D source) { XYPlot subplot = findSubplot(info, source); if (subplot == null) { return; } if (!subplot.isDomainPannable()) { return; } PlotRenderingInfo subplotInfo = info.getSubplotInfo( info.getSubplotIndex(source)); if (subplotInfo == null) { return; } for (int i = 0; i < subplot.getDomainAxisCount(); i++) { ValueAxis domainAxis = subplot.getDomainAxis(i); if (domainAxis != null) { domainAxis.pan(panRange); } } } /** * Returns the subplot (if any) that contains the (x, y) point (specified * in Java2D space). * * @param info the chart rendering info ({@code null} not permitted). * @param source the source point ({@code null} not permitted). * * @return A subplot (possibly {@code null}). */ public XYPlot findSubplot(PlotRenderingInfo info, Point2D source) { Args.nullNotPermitted(info, "info"); Args.nullNotPermitted(source, "source"); XYPlot result = null; int subplotIndex = info.getSubplotIndex(source); if (subplotIndex >= 0) { result = (XYPlot) this.subplots.get(subplotIndex); } return result; } /** * Sets the item renderer FOR ALL SUBPLOTS. Registered listeners are * notified that the plot has been modified. *

* Note: usually you will want to set the renderer independently for each * subplot, which is NOT what this method does. * * @param renderer the new renderer. */ @Override public void setRenderer(XYItemRenderer renderer) { super.setRenderer(renderer); // not strictly necessary, since the // renderer set for the // parent plot is not used Iterator iterator = this.subplots.iterator(); while (iterator.hasNext()) { XYPlot plot = (XYPlot) iterator.next(); plot.setRenderer(renderer); } } /** * Sets the orientation for the plot (and all its subplots). * * @param orientation the orientation. */ @Override public void setOrientation(PlotOrientation orientation) { super.setOrientation(orientation); Iterator iterator = this.subplots.iterator(); while (iterator.hasNext()) { XYPlot plot = (XYPlot) iterator.next(); plot.setOrientation(orientation); } } /** * Sets the shadow generator for the plot (and all subplots) and sends * a {@link PlotChangeEvent} to all registered listeners. * * @param generator the new generator ({@code null} permitted). */ @Override public void setShadowGenerator(ShadowGenerator generator) { setNotify(false); super.setShadowGenerator(generator); Iterator iterator = this.subplots.iterator(); while (iterator.hasNext()) { XYPlot plot = (XYPlot) iterator.next(); plot.setShadowGenerator(generator); } setNotify(true); } /** * Returns a range representing the extent of the data values in this plot * (obtained from the subplots) that will be rendered against the specified * axis. NOTE: This method is intended for internal JFreeChart use, and * is public only so that code in the axis classes can call it. Since * only the range axis is shared between subplots, the JFreeChart code * will only call this method for the range values (although this is not * checked/enforced). * * @param axis the axis. * * @return The range. */ @Override public Range getDataRange(ValueAxis axis) { Range result = null; if (this.subplots != null) { Iterator iterator = this.subplots.iterator(); while (iterator.hasNext()) { XYPlot subplot = (XYPlot) iterator.next(); result = Range.combine(result, subplot.getDataRange(axis)); } } return result; } /** * Sets the space (width or height, depending on the orientation of the * plot) for the domain axis of each subplot. * * @param space the space. */ protected void setFixedDomainAxisSpaceForSubplots(AxisSpace space) { Iterator iterator = this.subplots.iterator(); while (iterator.hasNext()) { XYPlot plot = (XYPlot) iterator.next(); plot.setFixedDomainAxisSpace(space, false); } } /** * Handles a 'click' on the plot by updating the anchor values... * * @param x x-coordinate, where the click occured. * @param y y-coordinate, where the click occured. * @param info object containing information about the plot dimensions. */ @Override public void handleClick(int x, int y, PlotRenderingInfo info) { Rectangle2D dataArea = info.getDataArea(); if (dataArea.contains(x, y)) { for (int i = 0; i < this.subplots.size(); i++) { XYPlot subplot = (XYPlot) this.subplots.get(i); PlotRenderingInfo subplotInfo = info.getSubplotInfo(i); subplot.handleClick(x, y, subplotInfo); } } } /** * Receives a {@link PlotChangeEvent} and responds by notifying all * listeners. * * @param event the event. */ @Override public void plotChanged(PlotChangeEvent event) { notifyListeners(event); } /** * Tests this plot for equality with another object. * * @param obj the other object. * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof CombinedRangeXYPlot)) { return false; } CombinedRangeXYPlot that = (CombinedRangeXYPlot) obj; if (this.gap != that.gap) { return false; } if (!Objects.equals(this.subplots, that.subplots)) { return false; } return super.equals(obj); } /** * Returns a clone of the plot. * * @return A clone. * * @throws CloneNotSupportedException this class will not throw this * exception, but subclasses (if any) might. */ @Override public Object clone() throws CloneNotSupportedException { CombinedRangeXYPlot result = (CombinedRangeXYPlot) super.clone(); result.subplots = (List) ObjectUtils.deepClone(this.subplots); for (Iterator it = result.subplots.iterator(); it.hasNext();) { Plot child = (Plot) it.next(); child.setParent(result); } // after setting up all the subplots, the shared range axis may need // reconfiguring ValueAxis rangeAxis = result.getRangeAxis(); if (rangeAxis != null) { rangeAxis.configure(); } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/CompassPlot.java000066400000000000000000000650251463604235500271530ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * CompassPlot.java * ---------------- * (C) Copyright 2002-present, by the Australian Antarctic Division and * Contributors. * * Original Author: Bryan Scott (for the Australian Antarctic Division); * Contributor(s): David Gilbert; * Arnaud Lelievre; * Martin Hoeller; * */ package org.jfree.chart.plot; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Polygon; import java.awt.Stroke; import java.awt.geom.Area; import java.awt.geom.Ellipse2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Arrays; import java.util.Objects; import java.util.ResourceBundle; import org.jfree.chart.LegendItemCollection; import org.jfree.chart.event.PlotChangeEvent; import org.jfree.chart.needle.ArrowNeedle; import org.jfree.chart.needle.LineNeedle; import org.jfree.chart.needle.LongNeedle; import org.jfree.chart.needle.MeterNeedle; import org.jfree.chart.needle.MiddlePinNeedle; import org.jfree.chart.needle.PinNeedle; import org.jfree.chart.needle.PlumNeedle; import org.jfree.chart.needle.PointerNeedle; import org.jfree.chart.needle.ShipNeedle; import org.jfree.chart.needle.WindNeedle; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.ResourceBundleWrapper; import org.jfree.chart.util.SerialUtils; import org.jfree.data.general.DefaultValueDataset; import org.jfree.data.general.ValueDataset; /** * A specialised plot that draws a compass to indicate a direction based on the * value from a {@link ValueDataset}. */ public class CompassPlot extends Plot implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 6924382802125527395L; /** The default label font. */ public static final Font DEFAULT_LABEL_FONT = new Font("SansSerif", Font.BOLD, 10); /** A constant for the label type. */ public static final int NO_LABELS = 0; /** A constant for the label type. */ public static final int VALUE_LABELS = 1; /** The label type (NO_LABELS, VALUE_LABELS). */ private int labelType; /** The label font. */ private Font labelFont; /** A flag that controls whether or not a border is drawn. */ private boolean drawBorder = false; /** The rose highlight paint. */ private transient Paint roseHighlightPaint = Color.BLACK; /** The rose paint. */ private transient Paint rosePaint = Color.YELLOW; /** The rose center paint. */ private transient Paint roseCenterPaint = Color.WHITE; /** The compass font. */ private Font compassFont = new Font("Arial", Font.PLAIN, 10); /** A working shape. */ private transient Ellipse2D circle1; /** A working shape. */ private transient Ellipse2D circle2; /** A working area. */ private transient Area a1; /** A working area. */ private transient Area a2; /** A working shape. */ private transient Rectangle2D rect1; /** An array of value datasets. */ private ValueDataset[] datasets = new ValueDataset[1]; /** An array of needles. */ private MeterNeedle[] seriesNeedle = new MeterNeedle[1]; /** The resourceBundle for the localization. */ protected static ResourceBundle localizationResources = ResourceBundleWrapper.getBundle( "org.jfree.chart.plot.LocalizationBundle"); /** * The count to complete one revolution. Can be arbitrarily set * For degrees (the default) it is 360, for radians this is 2*Pi, etc */ protected double revolutionDistance = 360; /** * Default constructor. */ public CompassPlot() { this(new DefaultValueDataset()); } /** * Constructs a new compass plot. * * @param dataset the dataset for the plot ({@code null} permitted). */ public CompassPlot(ValueDataset dataset) { super(); if (dataset != null) { this.datasets[0] = dataset; dataset.addChangeListener(this); } this.circle1 = new Ellipse2D.Double(); this.circle2 = new Ellipse2D.Double(); this.rect1 = new Rectangle2D.Double(); setSeriesNeedle(0); } /** * Returns the label type. Defined by the constants: {@link #NO_LABELS} * and {@link #VALUE_LABELS}. * * @return The label type. * * @see #setLabelType(int) */ public int getLabelType() { // FIXME: this attribute is never used - deprecate? return this.labelType; } /** * Sets the label type (either {@link #NO_LABELS} or {@link #VALUE_LABELS}. * * @param type the type. * * @see #getLabelType() */ public void setLabelType(int type) { // FIXME: this attribute is never used - deprecate? if ((type != NO_LABELS) && (type != VALUE_LABELS)) { throw new IllegalArgumentException( "MeterPlot.setLabelType(int): unrecognised type."); } if (this.labelType != type) { this.labelType = type; fireChangeEvent(); } } /** * Returns the label font. * * @return The label font. * * @see #setLabelFont(Font) */ public Font getLabelFont() { // FIXME: this attribute is not used - deprecate? return this.labelFont; } /** * Sets the label font and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param font the new label font. * * @see #getLabelFont() */ public void setLabelFont(Font font) { // FIXME: this attribute is not used - deprecate? Args.nullNotPermitted(font, "font"); this.labelFont = font; fireChangeEvent(); } /** * Returns the paint used to fill the outer circle of the compass. * * @return The paint (never {@code null}). * * @see #setRosePaint(Paint) */ public Paint getRosePaint() { return this.rosePaint; } /** * Sets the paint used to fill the outer circle of the compass, * and sends a {@link PlotChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getRosePaint() */ public void setRosePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.rosePaint = paint; fireChangeEvent(); } /** * Returns the paint used to fill the inner background area of the * compass. * * @return The paint (never {@code null}). * * @see #setRoseCenterPaint(Paint) */ public Paint getRoseCenterPaint() { return this.roseCenterPaint; } /** * Sets the paint used to fill the inner background area of the compass, * and sends a {@link PlotChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getRoseCenterPaint() */ public void setRoseCenterPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.roseCenterPaint = paint; fireChangeEvent(); } /** * Returns the paint used to draw the circles, symbols and labels on the * compass. * * @return The paint (never {@code null}). * * @see #setRoseHighlightPaint(Paint) */ public Paint getRoseHighlightPaint() { return this.roseHighlightPaint; } /** * Sets the paint used to draw the circles, symbols and labels of the * compass, and sends a {@link PlotChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getRoseHighlightPaint() */ public void setRoseHighlightPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.roseHighlightPaint = paint; fireChangeEvent(); } /** * Returns a flag that controls whether or not a border is drawn. * * @return The flag. * * @see #setDrawBorder(boolean) */ public boolean getDrawBorder() { return this.drawBorder; } /** * Sets a flag that controls whether or not a border is drawn. * * @param status the flag status. * * @see #getDrawBorder() */ public void setDrawBorder(boolean status) { this.drawBorder = status; fireChangeEvent(); } /** * Sets the series paint. * * @param series the series index. * @param paint the paint. * * @see #setSeriesOutlinePaint(int, Paint) */ public void setSeriesPaint(int series, Paint paint) { // super.setSeriesPaint(series, paint); if ((series >= 0) && (series < this.seriesNeedle.length)) { this.seriesNeedle[series].setFillPaint(paint); } } /** * Sets the series outline paint. * * @param series the series index. * @param p the paint. * * @see #setSeriesPaint(int, Paint) */ public void setSeriesOutlinePaint(int series, Paint p) { if ((series >= 0) && (series < this.seriesNeedle.length)) { this.seriesNeedle[series].setOutlinePaint(p); } } /** * Sets the series outline stroke. * * @param series the series index. * @param stroke the stroke. * * @see #setSeriesOutlinePaint(int, Paint) */ public void setSeriesOutlineStroke(int series, Stroke stroke) { if ((series >= 0) && (series < this.seriesNeedle.length)) { this.seriesNeedle[series].setOutlineStroke(stroke); } } /** * Sets the needle type. * * @param type the type. * * @see #setSeriesNeedle(int, int) */ public void setSeriesNeedle(int type) { setSeriesNeedle(0, type); } /** * Sets the needle for a series. The needle type is one of the following: *

    *
  • 0 = {@link ArrowNeedle};
  • *
  • 1 = {@link LineNeedle};
  • *
  • 2 = {@link LongNeedle};
  • *
  • 3 = {@link PinNeedle};
  • *
  • 4 = {@link PlumNeedle};
  • *
  • 5 = {@link PointerNeedle};
  • *
  • 6 = {@link ShipNeedle};
  • *
  • 7 = {@link WindNeedle};
  • *
  • 8 = {@link ArrowNeedle};
  • *
  • 9 = {@link MiddlePinNeedle};
  • *
* @param index the series index. * @param type the needle type. * * @see #setSeriesNeedle(int) */ public void setSeriesNeedle(int index, int type) { switch (type) { case 0: setSeriesNeedle(index, new ArrowNeedle(true)); setSeriesPaint(index, Color.RED); this.seriesNeedle[index].setHighlightPaint(Color.WHITE); break; case 1: setSeriesNeedle(index, new LineNeedle()); break; case 2: MeterNeedle longNeedle = new LongNeedle(); longNeedle.setRotateY(0.5); setSeriesNeedle(index, longNeedle); break; case 3: setSeriesNeedle(index, new PinNeedle()); break; case 4: setSeriesNeedle(index, new PlumNeedle()); break; case 5: setSeriesNeedle(index, new PointerNeedle()); break; case 6: setSeriesPaint(index, null); setSeriesOutlineStroke(index, new BasicStroke(3)); setSeriesNeedle(index, new ShipNeedle()); break; case 7: setSeriesPaint(index, Color.BLUE); setSeriesNeedle(index, new WindNeedle()); break; case 8: setSeriesNeedle(index, new ArrowNeedle(true)); break; case 9: setSeriesNeedle(index, new MiddlePinNeedle()); break; default: throw new IllegalArgumentException("Unrecognised type."); } } /** * Sets the needle for a series and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param index the series index. * @param needle the needle. */ public void setSeriesNeedle(int index, MeterNeedle needle) { if ((needle != null) && (index >= 0) && (index < this.seriesNeedle.length)) { this.seriesNeedle[index] = needle; } fireChangeEvent(); } /** * Returns an array of dataset references for the plot. * * @return The dataset for the plot, cast as a ValueDataset. * * @see #addDataset(ValueDataset) */ public ValueDataset[] getDatasets() { return this.datasets; } /** * Adds a dataset to the compass. * * @param dataset the new dataset ({@code null} ignored). * * @see #addDataset(ValueDataset, MeterNeedle) */ public void addDataset(ValueDataset dataset) { addDataset(dataset, null); } /** * Adds a dataset to the compass. * * @param dataset the new dataset ({@code null} ignored). * @param needle the needle ({@code null} permitted). */ public void addDataset(ValueDataset dataset, MeterNeedle needle) { if (dataset != null) { int i = this.datasets.length + 1; ValueDataset[] t = new ValueDataset[i]; MeterNeedle[] p = new MeterNeedle[i]; i = i - 2; for (; i >= 0; --i) { t[i] = this.datasets[i]; p[i] = this.seriesNeedle[i]; } i = this.datasets.length; t[i] = dataset; p[i] = ((needle != null) ? needle : p[i - 1]); ValueDataset[] a = this.datasets; MeterNeedle[] b = this.seriesNeedle; this.datasets = t; this.seriesNeedle = p; for (--i; i >= 0; --i) { a[i] = null; b[i] = null; } dataset.addChangeListener(this); } } /** * Draws the plot on a Java 2D graphics device (such as the screen or a * printer). * * @param g2 the graphics device. * @param area the area within which the plot should be drawn. * @param anchor the anchor point ({@code null} permitted). * @param parentState the state from the parent plot, if there is one. * @param info collects info about the drawing. */ @Override public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, PlotState parentState, PlotRenderingInfo info) { int outerRadius, innerRadius; int x1, y1, x2, y2; double a; if (info != null) { info.setPlotArea(area); } // adjust for insets... RectangleInsets insets = getInsets(); insets.trim(area); // draw the background if (this.drawBorder) { drawBackground(g2, area); } int midX = (int) (area.getWidth() / 2); int midY = (int) (area.getHeight() / 2); int radius = midX; if (midY < midX) { radius = midY; } --radius; int diameter = 2 * radius; midX += (int) area.getMinX(); midY += (int) area.getMinY(); this.circle1.setFrame(midX - radius, midY - radius, diameter, diameter); this.circle2.setFrame( midX - radius + 15, midY - radius + 15, diameter - 30, diameter - 30 ); g2.setPaint(this.rosePaint); this.a1 = new Area(this.circle1); this.a2 = new Area(this.circle2); this.a1.subtract(this.a2); g2.fill(this.a1); g2.setPaint(this.roseCenterPaint); x1 = diameter - 30; g2.fillOval(midX - radius + 15, midY - radius + 15, x1, x1); g2.setPaint(this.roseHighlightPaint); g2.drawOval(midX - radius, midY - radius, diameter, diameter); x1 = diameter - 20; g2.drawOval(midX - radius + 10, midY - radius + 10, x1, x1); x1 = diameter - 30; g2.drawOval(midX - radius + 15, midY - radius + 15, x1, x1); x1 = diameter - 80; g2.drawOval(midX - radius + 40, midY - radius + 40, x1, x1); outerRadius = radius - 20; innerRadius = radius - 32; for (int w = 0; w < 360; w += 15) { a = Math.toRadians(w); x1 = midX - ((int) (Math.sin(a) * innerRadius)); x2 = midX - ((int) (Math.sin(a) * outerRadius)); y1 = midY - ((int) (Math.cos(a) * innerRadius)); y2 = midY - ((int) (Math.cos(a) * outerRadius)); g2.drawLine(x1, y1, x2, y2); } g2.setPaint(this.roseHighlightPaint); innerRadius = radius - 26; outerRadius = 7; for (int w = 45; w < 360; w += 90) { a = Math.toRadians(w); x1 = midX - ((int) (Math.sin(a) * innerRadius)); y1 = midY - ((int) (Math.cos(a) * innerRadius)); g2.fillOval(x1 - outerRadius, y1 - outerRadius, 2 * outerRadius, 2 * outerRadius); } /// Squares for (int w = 0; w < 360; w += 90) { a = Math.toRadians(w); x1 = midX - ((int) (Math.sin(a) * innerRadius)); y1 = midY - ((int) (Math.cos(a) * innerRadius)); Polygon p = new Polygon(); p.addPoint(x1 - outerRadius, y1); p.addPoint(x1, y1 + outerRadius); p.addPoint(x1 + outerRadius, y1); p.addPoint(x1, y1 - outerRadius); g2.fillPolygon(p); } /// Draw N, S, E, W innerRadius = radius - 42; Font f = getCompassFont(radius); g2.setFont(f); g2.drawString(localizationResources.getString("N"), midX - 5, midY - innerRadius + f.getSize()); g2.drawString(localizationResources.getString("S"), midX - 5, midY + innerRadius - 5); g2.drawString(localizationResources.getString("W"), midX - innerRadius + 5, midY + 5); g2.drawString(localizationResources.getString("E"), midX + innerRadius - f.getSize(), midY + 5); // plot the data (unless the dataset is null)... y1 = radius / 2; x1 = radius / 6; Rectangle2D needleArea = new Rectangle2D.Double( (midX - x1), (midY - y1), (2 * x1), (2 * y1) ); int x = this.seriesNeedle.length; int current; double value; int i = (this.datasets.length - 1); for (; i >= 0; --i) { ValueDataset data = this.datasets[i]; if (data != null && data.getValue() != null) { value = (data.getValue().doubleValue()) % this.revolutionDistance; value = value / this.revolutionDistance * 360; current = i % x; this.seriesNeedle[current].draw(g2, needleArea, value); } } if (this.drawBorder) { drawOutline(g2, area); } } /** * Returns a short string describing the type of plot. * * @return A string describing the plot. */ @Override public String getPlotType() { return localizationResources.getString("Compass_Plot"); } /** * Returns the legend items for the plot. For now, no legend is available * - this method returns null. * * @return The legend items. */ @Override public LegendItemCollection getLegendItems() { return null; } /** * No zooming is implemented for compass plot, so this method is empty. * * @param percent the zoom amount. */ @Override public void zoom(double percent) { // no zooming possible } /** * Returns the font for the compass, adjusted for the size of the plot. * * @param radius the radius. * * @return The font. */ protected Font getCompassFont(int radius) { float fontSize = radius / 10.0f; if (fontSize < 8) { fontSize = 8; } Font newFont = this.compassFont.deriveFont(fontSize); return newFont; } /** * Tests an object for equality with this plot. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof CompassPlot)) { return false; } if (!super.equals(obj)) { return false; } CompassPlot that = (CompassPlot) obj; if (this.labelType != that.labelType) { return false; } if (!Objects.equals(this.labelFont, that.labelFont)) { return false; } if (this.drawBorder != that.drawBorder) { return false; } if (!PaintUtils.equal(this.roseHighlightPaint, that.roseHighlightPaint)) { return false; } if (!PaintUtils.equal(this.rosePaint, that.rosePaint)) { return false; } if (!PaintUtils.equal(this.roseCenterPaint, that.roseCenterPaint)) { return false; } if (!Objects.equals(this.compassFont, that.compassFont)) { return false; } if (!Arrays.equals(this.seriesNeedle, that.seriesNeedle)) { return false; } if (getRevolutionDistance() != that.getRevolutionDistance()) { return false; } return true; } /** * Returns a clone of the plot. * * @return A clone. * * @throws CloneNotSupportedException this class will not throw this * exception, but subclasses (if any) might. */ @Override public Object clone() throws CloneNotSupportedException { CompassPlot clone = (CompassPlot) super.clone(); if (this.circle1 != null) { clone.circle1 = (Ellipse2D) this.circle1.clone(); } if (this.circle2 != null) { clone.circle2 = (Ellipse2D) this.circle2.clone(); } if (this.a1 != null) { clone.a1 = (Area) this.a1.clone(); } if (this.a2 != null) { clone.a2 = (Area) this.a2.clone(); } if (this.rect1 != null) { clone.rect1 = (Rectangle2D) this.rect1.clone(); } clone.datasets = (ValueDataset[]) this.datasets.clone(); clone.seriesNeedle = (MeterNeedle[]) this.seriesNeedle.clone(); // clone share data sets => add the clone as listener to the dataset for (int i = 0; i < this.datasets.length; ++i) { if (clone.datasets[i] != null) { clone.datasets[i].addChangeListener(clone); } } return clone; } /** * Sets the count to complete one revolution. Can be arbitrarily set * For degrees (the default) it is 360, for radians this is 2*Pi, etc * * @param size the count to complete one revolution. * * @see #getRevolutionDistance() */ public void setRevolutionDistance(double size) { if (size > 0) { this.revolutionDistance = size; } } /** * Gets the count to complete one revolution. * * @return The count to complete one revolution. * * @see #setRevolutionDistance(double) */ public double getRevolutionDistance() { return this.revolutionDistance; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.rosePaint, stream); SerialUtils.writePaint(this.roseCenterPaint, stream); SerialUtils.writePaint(this.roseHighlightPaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.rosePaint = SerialUtils.readPaint(stream); this.roseCenterPaint = SerialUtils.readPaint(stream); this.roseHighlightPaint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/Crosshair.java000066400000000000000000000521721463604235500266430ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * Crosshair.java * -------------- * (C) Copyright 2009-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.Paint; import java.awt.Stroke; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import org.jfree.chart.HashUtils; import org.jfree.chart.labels.CrosshairLabelGenerator; import org.jfree.chart.labels.StandardCrosshairLabelGenerator; import org.jfree.chart.ui.RectangleAnchor; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; /** * A crosshair for display on a plot. */ public class Crosshair implements Cloneable, PublicCloneable, Serializable { /** Flag controlling visibility. */ private boolean visible; /** The crosshair value. */ private double value; /** The paint for the crosshair line. */ private transient Paint paint; /** The stroke for the crosshair line. */ private transient Stroke stroke; /** * A flag that controls whether or not the crosshair has a label * visible. */ private boolean labelVisible; /** * The label anchor. */ private RectangleAnchor labelAnchor; /** A label generator. */ private CrosshairLabelGenerator labelGenerator; /** * The x-offset in Java2D units. */ private double labelXOffset; /** * The y-offset in Java2D units. */ private double labelYOffset; /** * The label font. */ private Font labelFont; /** * The label paint. */ private transient Paint labelPaint; /** * The label background paint. */ private transient Paint labelBackgroundPaint; /** A flag that controls the visibility of the label outline. */ private boolean labelOutlineVisible; /** The label outline stroke. */ private transient Stroke labelOutlineStroke; /** The label outline paint. */ private transient Paint labelOutlinePaint; /** Property change support. */ private transient PropertyChangeSupport pcs; /** * Creates a new crosshair with value 0.0. */ public Crosshair() { this(0.0); } /** * Creates a new crosshair with the specified value. * * @param value the value. */ public Crosshair(double value) { this(value, Color.BLACK, new BasicStroke(1.0f)); } /** * Creates a new crosshair value with the specified value and line style. * * @param value the value. * @param paint the line paint ({@code null} not permitted). * @param stroke the line stroke ({@code null} not permitted). */ public Crosshair(double value, Paint paint, Stroke stroke) { Args.nullNotPermitted(paint, "paint"); Args.nullNotPermitted(stroke, "stroke"); this.visible = true; this.value = value; this.paint = paint; this.stroke = stroke; this.labelVisible = false; this.labelGenerator = new StandardCrosshairLabelGenerator(); this.labelAnchor = RectangleAnchor.BOTTOM_LEFT; this.labelXOffset = 3.0; this.labelYOffset = 3.0; this.labelFont = new Font("Tahoma", Font.PLAIN, 12); this.labelPaint = Color.BLACK; this.labelBackgroundPaint = new Color(0, 0, 255, 63); this.labelOutlineVisible = true; this.labelOutlinePaint = Color.BLACK; this.labelOutlineStroke = new BasicStroke(0.5f); this.pcs = new PropertyChangeSupport(this); } /** * Returns the flag that indicates whether or not the crosshair is * currently visible. * * @return A boolean. * * @see #setVisible(boolean) */ public boolean isVisible() { return this.visible; } /** * Sets the flag that controls the visibility of the crosshair and sends * a proerty change event (with the name 'visible') to all registered * listeners. * * @param visible the new flag value. * * @see #isVisible() */ public void setVisible(boolean visible) { boolean old = this.visible; this.visible = visible; this.pcs.firePropertyChange("visible", old, visible); } /** * Returns the crosshair value. * * @return The crosshair value. * * @see #setValue(double) */ public double getValue() { return this.value; } /** * Sets the crosshair value and sends a property change event with the name * 'value' to all registered listeners. * * @param value the value. * * @see #getValue() */ public void setValue(double value) { Double oldValue = this.value; this.value = value; this.pcs.firePropertyChange("value", oldValue, value); } /** * Returns the paint for the crosshair line. * * @return The paint (never {@code null}). * * @see #setPaint(java.awt.Paint) */ public Paint getPaint() { return this.paint; } /** * Sets the paint for the crosshair line and sends a property change event * with the name "paint" to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getPaint() */ public void setPaint(Paint paint) { Paint old = this.paint; this.paint = paint; this.pcs.firePropertyChange("paint", old, paint); } /** * Returns the stroke for the crosshair line. * * @return The stroke (never {@code null}). * * @see #setStroke(java.awt.Stroke) */ public Stroke getStroke() { return this.stroke; } /** * Sets the stroke for the crosshair line and sends a property change event * with the name "stroke" to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getStroke() */ public void setStroke(Stroke stroke) { Stroke old = this.stroke; this.stroke = stroke; this.pcs.firePropertyChange("stroke", old, stroke); } /** * Returns the flag that controls whether or not a label is drawn for * this crosshair. * * @return A boolean. * * @see #setLabelVisible(boolean) */ public boolean isLabelVisible() { return this.labelVisible; } /** * Sets the flag that controls whether or not a label is drawn for the * crosshair and sends a property change event (with the name * 'labelVisible') to all registered listeners. * * @param visible the new flag value. * * @see #isLabelVisible() */ public void setLabelVisible(boolean visible) { boolean old = this.labelVisible; this.labelVisible = visible; this.pcs.firePropertyChange("labelVisible", old, visible); } /** * Returns the crosshair label generator. * * @return The label crosshair generator (never {@code null}). * * @see #setLabelGenerator(org.jfree.chart.labels.CrosshairLabelGenerator) */ public CrosshairLabelGenerator getLabelGenerator() { return this.labelGenerator; } /** * Sets the crosshair label generator and sends a property change event * (with the name 'labelGenerator') to all registered listeners. * * @param generator the new generator ({@code null} not permitted). * * @see #getLabelGenerator() */ public void setLabelGenerator(CrosshairLabelGenerator generator) { Args.nullNotPermitted(generator, "generator"); CrosshairLabelGenerator old = this.labelGenerator; this.labelGenerator = generator; this.pcs.firePropertyChange("labelGenerator", old, generator); } /** * Returns the label anchor point. * * @return the label anchor point (never {@code null}. * * @see #setLabelAnchor(org.jfree.chart.ui.RectangleAnchor) */ public RectangleAnchor getLabelAnchor() { return this.labelAnchor; } /** * Sets the label anchor point and sends a property change event (with the * name 'labelAnchor') to all registered listeners. * * @param anchor the anchor ({@code null} not permitted). * * @see #getLabelAnchor() */ public void setLabelAnchor(RectangleAnchor anchor) { RectangleAnchor old = this.labelAnchor; this.labelAnchor = anchor; this.pcs.firePropertyChange("labelAnchor", old, anchor); } /** * Returns the x-offset for the label (in Java2D units). * * @return The x-offset. * * @see #setLabelXOffset(double) */ public double getLabelXOffset() { return this.labelXOffset; } /** * Sets the x-offset and sends a property change event (with the name * 'labelXOffset') to all registered listeners. * * @param offset the new offset. * * @see #getLabelXOffset() */ public void setLabelXOffset(double offset) { Double old = this.labelXOffset; this.labelXOffset = offset; this.pcs.firePropertyChange("labelXOffset", old, offset); } /** * Returns the y-offset for the label (in Java2D units). * * @return The y-offset. * * @see #setLabelYOffset(double) */ public double getLabelYOffset() { return this.labelYOffset; } /** * Sets the y-offset and sends a property change event (with the name * 'labelYOffset') to all registered listeners. * * @param offset the new offset. * * @see #getLabelYOffset() */ public void setLabelYOffset(double offset) { Double old = this.labelYOffset; this.labelYOffset = offset; this.pcs.firePropertyChange("labelYOffset", old, offset); } /** * Returns the label font. * * @return The label font (never {@code null}). * * @see #setLabelFont(java.awt.Font) */ public Font getLabelFont() { return this.labelFont; } /** * Sets the label font and sends a property change event (with the name * 'labelFont') to all registered listeners. * * @param font the font ({@code null} not permitted). * * @see #getLabelFont() */ public void setLabelFont(Font font) { Args.nullNotPermitted(font, "font"); Font old = this.labelFont; this.labelFont = font; this.pcs.firePropertyChange("labelFont", old, font); } /** * Returns the label paint. The default value is {@code Color.BLACK}. * * @return The label paint (never {@code null}). * * @see #setLabelPaint */ public Paint getLabelPaint() { return this.labelPaint; } /** * Sets the label paint and sends a property change event (with the name * 'labelPaint') to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getLabelPaint() */ public void setLabelPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); Paint old = this.labelPaint; this.labelPaint = paint; this.pcs.firePropertyChange("labelPaint", old, paint); } /** * Returns the label background paint. * * @return The label background paint (possibly {@code null}). * * @see #setLabelBackgroundPaint(java.awt.Paint) */ public Paint getLabelBackgroundPaint() { return this.labelBackgroundPaint; } /** * Sets the label background paint and sends a property change event with * the name 'labelBackgroundPaint') to all registered listeners. * * @param paint the paint ({@code null} permitted). * * @see #getLabelBackgroundPaint() */ public void setLabelBackgroundPaint(Paint paint) { Paint old = this.labelBackgroundPaint; this.labelBackgroundPaint = paint; this.pcs.firePropertyChange("labelBackgroundPaint", old, paint); } /** * Returns the flag that controls the visibility of the label outline. * The default value is {@code true}. * * @return A boolean. * * @see #setLabelOutlineVisible(boolean) */ public boolean isLabelOutlineVisible() { return this.labelOutlineVisible; } /** * Sets the flag that controls the visibility of the label outlines and * sends a property change event (with the name "labelOutlineVisible") to * all registered listeners. * * @param visible the new flag value. * * @see #isLabelOutlineVisible() */ public void setLabelOutlineVisible(boolean visible) { boolean old = this.labelOutlineVisible; this.labelOutlineVisible = visible; this.pcs.firePropertyChange("labelOutlineVisible", old, visible); } /** * Returns the label outline paint. * * @return The label outline paint (never {@code null}). * * @see #setLabelOutlinePaint(java.awt.Paint) */ public Paint getLabelOutlinePaint() { return this.labelOutlinePaint; } /** * Sets the label outline paint and sends a property change event (with the * name "labelOutlinePaint") to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getLabelOutlinePaint() */ public void setLabelOutlinePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); Paint old = this.labelOutlinePaint; this.labelOutlinePaint = paint; this.pcs.firePropertyChange("labelOutlinePaint", old, paint); } /** * Returns the label outline stroke. The default value is * {@code BasicStroke(0.5)}. * * @return The label outline stroke (never {@code null}). * * @see #setLabelOutlineStroke(java.awt.Stroke) */ public Stroke getLabelOutlineStroke() { return this.labelOutlineStroke; } /** * Sets the label outline stroke and sends a property change event (with * the name 'labelOutlineStroke') to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getLabelOutlineStroke() */ public void setLabelOutlineStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); Stroke old = this.labelOutlineStroke; this.labelOutlineStroke = stroke; this.pcs.firePropertyChange("labelOutlineStroke", old, stroke); } /** * Tests this crosshair for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof Crosshair)) { return false; } Crosshair that = (Crosshair) obj; if (this.visible != that.visible) { return false; } if (this.value != that.value) { return false; } if (!PaintUtils.equal(this.paint, that.paint)) { return false; } if (!this.stroke.equals(that.stroke)) { return false; } if (this.labelVisible != that.labelVisible) { return false; } if (!this.labelGenerator.equals(that.labelGenerator)) { return false; } if (!this.labelAnchor.equals(that.labelAnchor)) { return false; } if (this.labelXOffset != that.labelXOffset) { return false; } if (this.labelYOffset != that.labelYOffset) { return false; } if (!this.labelFont.equals(that.labelFont)) { return false; } if (!PaintUtils.equal(this.labelPaint, that.labelPaint)) { return false; } if (!PaintUtils.equal(this.labelBackgroundPaint, that.labelBackgroundPaint)) { return false; } if (this.labelOutlineVisible != that.labelOutlineVisible) { return false; } if (!PaintUtils.equal(this.labelOutlinePaint, that.labelOutlinePaint)) { return false; } if (!this.labelOutlineStroke.equals(that.labelOutlineStroke)) { return false; } return true; // can't find any difference } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int hash = 7; hash = HashUtils.hashCode(hash, this.visible); hash = HashUtils.hashCode(hash, this.value); hash = HashUtils.hashCode(hash, this.paint); hash = HashUtils.hashCode(hash, this.stroke); hash = HashUtils.hashCode(hash, this.labelVisible); hash = HashUtils.hashCode(hash, this.labelAnchor); hash = HashUtils.hashCode(hash, this.labelGenerator); hash = HashUtils.hashCode(hash, this.labelXOffset); hash = HashUtils.hashCode(hash, this.labelYOffset); hash = HashUtils.hashCode(hash, this.labelFont); hash = HashUtils.hashCode(hash, this.labelPaint); hash = HashUtils.hashCode(hash, this.labelBackgroundPaint); hash = HashUtils.hashCode(hash, this.labelOutlineVisible); hash = HashUtils.hashCode(hash, this.labelOutlineStroke); hash = HashUtils.hashCode(hash, this.labelOutlinePaint); return hash; } /** * Returns an independent copy of this instance. * * @return An independent copy of this instance. * * @throws java.lang.CloneNotSupportedException if there is a problem with * cloning. */ @Override public Object clone() throws CloneNotSupportedException { // FIXME: clone generator return super.clone(); } /** * Adds a property change listener. * * @param l the listener. * * @see #removePropertyChangeListener(java.beans.PropertyChangeListener) */ public void addPropertyChangeListener(PropertyChangeListener l) { this.pcs.addPropertyChangeListener(l); } /** * Removes a property change listener. * * @param l the listener. * * @see #addPropertyChangeListener(java.beans.PropertyChangeListener) */ public void removePropertyChangeListener(PropertyChangeListener l) { this.pcs.removePropertyChangeListener(l); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.paint, stream); SerialUtils.writeStroke(this.stroke, stream); SerialUtils.writePaint(this.labelPaint, stream); SerialUtils.writePaint(this.labelBackgroundPaint, stream); SerialUtils.writeStroke(this.labelOutlineStroke, stream); SerialUtils.writePaint(this.labelOutlinePaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.paint = SerialUtils.readPaint(stream); this.stroke = SerialUtils.readStroke(stream); this.labelPaint = SerialUtils.readPaint(stream); this.labelBackgroundPaint = SerialUtils.readPaint(stream); this.labelOutlineStroke = SerialUtils.readStroke(stream); this.labelOutlinePaint = SerialUtils.readPaint(stream); this.pcs = new PropertyChangeSupport(this); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/CrosshairState.java000066400000000000000000000260761463604235500276500ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * CrosshairState.java * ------------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import java.awt.geom.Point2D; /** * Maintains state information about crosshairs on a plot between successive * calls to the renderer's draw method. This class is used internally by * JFreeChart - it is not intended for external use. */ public class CrosshairState { /** * A flag that controls whether the distance is calculated in data space * or Java2D space. */ private boolean calculateDistanceInDataSpace = false; /** The x-value (in data space) for the anchor point. */ private double anchorX; /** The y-value (in data space) for the anchor point. */ private double anchorY; /** The anchor point in Java2D space - if null, don't update crosshair. */ private Point2D anchor; /** The x-value for the current crosshair point. */ private double crosshairX; /** The y-value for the current crosshair point. */ private double crosshairY; /** * The dataset index that the crosshair point relates to (this determines * the axes that the crosshairs will be plotted against). */ private int datasetIndex; /** * The smallest distance (so far) between the anchor point and a data * point. */ private double distance; /** * Creates a new {@code crosshairState} instance that calculates * distance in Java2D space. */ public CrosshairState() { this(false); } /** * Creates a new {@code crosshairState} instance. Determination of the * data point nearest the anchor point can be calculated in either * dataspace or Java2D space. The former should only be used for charts * with a single set of axes. * * @param calculateDistanceInDataSpace a flag that controls whether the * distance is calculated in data * space or Java2D space. */ public CrosshairState(boolean calculateDistanceInDataSpace) { this.calculateDistanceInDataSpace = calculateDistanceInDataSpace; } /** * Returns the distance between the anchor point and the current crosshair * point. * * @return The distance. * * @see #setCrosshairDistance(double) */ public double getCrosshairDistance() { return this.distance; } /** * Sets the distance between the anchor point and the current crosshair * point. As each data point is processed, its distance to the anchor * point is compared with this value and, if it is closer, the data point * becomes the new crosshair point. * * @param distance the distance. * * @see #getCrosshairDistance() */ public void setCrosshairDistance(double distance) { this.distance = distance; } /** * Updates the crosshair point. * * @param x the x-value. * @param y the y-value. * @param datasetIndex the dataset index. * @param transX the x-value in Java2D space. * @param transY the y-value in Java2D space. * @param orientation the plot orientation ({@code null} not permitted). */ public void updateCrosshairPoint(double x, double y, int datasetIndex, double transX, double transY, PlotOrientation orientation) { if (this.anchor != null) { double d = 0.0; if (this.calculateDistanceInDataSpace) { d = (x - this.anchorX) * (x - this.anchorX) + (y - this.anchorY) * (y - this.anchorY); } else { // anchor point is in Java2D coordinates double xx = this.anchor.getX(); double yy = this.anchor.getY(); if (orientation == PlotOrientation.HORIZONTAL) { double temp = yy; yy = xx; xx = temp; } d = (transX - xx) * (transX - xx) + (transY - yy) * (transY - yy); } if (d < this.distance) { this.crosshairX = x; this.crosshairY = y; this.datasetIndex = datasetIndex; this.distance = d; } } } /** * Checks to see if the specified data point is the closest to the * anchor point and, if yes, updates the current state. * * @param x the x-value. * @param transX the x-value in Java2D space. * @param datasetIndex the dataset index. */ public void updateCrosshairX(double x, double transX, int datasetIndex) { if (this.anchor == null) { return; } double d = Math.abs(transX - this.anchor.getX()); if (d < this.distance) { this.crosshairX = x; this.datasetIndex = datasetIndex; this.distance = d; } } /** * Evaluates a y-value and if it is the closest to the anchor y-value it * becomes the new crosshair value. *

* Used in cases where only the y-axis is numerical. * * @param candidateY y position of the candidate for the new crosshair * point. * @param transY the y-value in Java2D space. * @param datasetIndex the index of the range axis for this y-value. */ public void updateCrosshairY(double candidateY, double transY, int datasetIndex) { if (this.anchor == null) { return; } double d = Math.abs(transY - this.anchor.getY()); if (d < this.distance) { this.crosshairY = candidateY; this.datasetIndex = datasetIndex; this.distance = d; } } /** * Returns the anchor point. * * @return The anchor point. * * @see #setAnchor(Point2D) */ public Point2D getAnchor() { return this.anchor; } /** * Sets the anchor point. This is usually the mouse click point in a chart * panel, and the crosshair point will often be the data item that is * closest to the anchor point. *

* Note that the x and y coordinates (in data space) are not updated by * this method - the caller is responsible for ensuring that this happens * in sync. * * @param anchor the anchor point ({@code null} permitted). * * @see #getAnchor() */ public void setAnchor(Point2D anchor) { this.anchor = anchor; } /** * Returns the x-coordinate (in data space) for the anchor point. * * @return The x-coordinate of the anchor point. */ public double getAnchorX() { return this.anchorX; } /** * Sets the x-coordinate (in data space) for the anchor point. Note that * this does NOT update the anchor itself - the caller is responsible for * ensuring this is done in sync. * * @param x the x-coordinate. */ public void setAnchorX(double x) { this.anchorX = x; } /** * Returns the y-coordinate (in data space) for the anchor point. * * @return The y-coordinate of teh anchor point. */ public double getAnchorY() { return this.anchorY; } /** * Sets the y-coordinate (in data space) for the anchor point. Note that * this does NOT update the anchor itself - the caller is responsible for * ensuring this is done in sync. * * @param y the y-coordinate. */ public void setAnchorY(double y) { this.anchorY = y; } /** * Get the x-value for the crosshair point. * * @return The x position of the crosshair point. * * @see #setCrosshairX(double) */ public double getCrosshairX() { return this.crosshairX; } /** * Sets the x coordinate for the crosshair. This is the coordinate in data * space measured against the domain axis. * * @param x the coordinate. * * @see #getCrosshairX() * @see #setCrosshairY(double) * @see #updateCrosshairPoint(double, double, int, double, double, * PlotOrientation) */ public void setCrosshairX(double x) { this.crosshairX = x; } /** * Get the y-value for the crosshair point. This is the coordinate in data * space measured against the range axis. * * @return The y position of the crosshair point. * * @see #setCrosshairY(double) */ public double getCrosshairY() { return this.crosshairY; } /** * Sets the y coordinate for the crosshair. * * @param y the y coordinate. * * @see #getCrosshairY() * @see #setCrosshairX(double) * @see #updateCrosshairPoint(double, double, int, double, double, * PlotOrientation) */ public void setCrosshairY(double y) { this.crosshairY = y; } /** * Returns the dataset index that the crosshair values relate to. The * dataset is mapped to specific axes, and this is how the crosshairs are * mapped also. * * @return The dataset index. * * @see #setDatasetIndex(int) */ public int getDatasetIndex() { return this.datasetIndex; } /** * Sets the dataset index that the current crosshair values relate to. * * @param index the dataset index. * * @see #getDatasetIndex() */ public void setDatasetIndex(int index) { this.datasetIndex = index; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/DatasetRenderingOrder.java000066400000000000000000000101771463604235500311240ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * DatasetRenderingOrder.java * -------------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import java.io.ObjectStreamException; import java.io.Serializable; /** * Defines the tokens that indicate the rendering order for datasets in a * {@link org.jfree.chart.plot.CategoryPlot} or an * {@link org.jfree.chart.plot.XYPlot}. */ public final class DatasetRenderingOrder implements Serializable { /** For serialization. */ private static final long serialVersionUID = -600593412366385072L; /** * Render datasets in the order 0, 1, 2, ..., N-1, where N is the number * of datasets. */ public static final DatasetRenderingOrder FORWARD = new DatasetRenderingOrder("DatasetRenderingOrder.FORWARD"); /** * Render datasets in the order N-1, N-2, ..., 2, 1, 0, where N is the * number of datasets. */ public static final DatasetRenderingOrder REVERSE = new DatasetRenderingOrder("DatasetRenderingOrder.REVERSE"); /** The name. */ private final String name; /** * Private constructor. * * @param name the name. */ private DatasetRenderingOrder(String name) { this.name = name; } /** * Returns a string representing the object. * * @return The string (never {@code null}). */ @Override public String toString() { return this.name; } /** * Returns {@code true} if this object is equal to the specified * object, and {@code false} otherwise. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof DatasetRenderingOrder)) { return false; } DatasetRenderingOrder order = (DatasetRenderingOrder) obj; if (!this.name.equals(order.toString())) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { return this.name.hashCode(); } /** * Ensures that serialization returns the unique instances. * * @return The object. * * @throws ObjectStreamException if there is a problem. */ private Object readResolve() throws ObjectStreamException { if (this.equals(DatasetRenderingOrder.FORWARD)) { return DatasetRenderingOrder.FORWARD; } else if (this.equals(DatasetRenderingOrder.REVERSE)) { return DatasetRenderingOrder.REVERSE; } return null; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/DefaultDrawingSupplier.java000066400000000000000000000421041463604235500313240ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * DefaultDrawingSupplier.java * --------------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Jeremy Bowman; * */ package org.jfree.chart.plot; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Paint; import java.awt.Polygon; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Ellipse2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Arrays; import org.jfree.chart.ChartColor; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; import org.jfree.chart.util.ShapeUtils; /** * A default implementation of the {@link DrawingSupplier} interface. All * {@link Plot} instances have a new instance of this class installed by * default. */ public class DefaultDrawingSupplier implements DrawingSupplier, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -7339847061039422538L; /** The default fill paint sequence. */ public static final Paint[] DEFAULT_PAINT_SEQUENCE = ChartColor.createDefaultPaintArray(); /** The default outline paint sequence. */ public static final Paint[] DEFAULT_OUTLINE_PAINT_SEQUENCE = new Paint[] { Color.LIGHT_GRAY}; /** The default fill paint sequence. */ public static final Paint[] DEFAULT_FILL_PAINT_SEQUENCE = new Paint[] { Color.WHITE}; /** The default stroke sequence. */ public static final Stroke[] DEFAULT_STROKE_SEQUENCE = new Stroke[] { new BasicStroke(1.0f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL)}; /** The default outline stroke sequence. */ public static final Stroke[] DEFAULT_OUTLINE_STROKE_SEQUENCE = new Stroke[] {new BasicStroke(1.0f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL)}; /** The default shape sequence. */ public static final Shape[] DEFAULT_SHAPE_SEQUENCE = createStandardSeriesShapes(); /** The paint sequence. */ private transient Paint[] paintSequence; /** The current paint index. */ private int paintIndex; /** The outline paint sequence. */ private transient Paint[] outlinePaintSequence; /** The current outline paint index. */ private int outlinePaintIndex; /** The fill paint sequence. */ private transient Paint[] fillPaintSequence; /** The current fill paint index. */ private int fillPaintIndex; /** The stroke sequence. */ private transient Stroke[] strokeSequence; /** The current stroke index. */ private int strokeIndex; /** The outline stroke sequence. */ private transient Stroke[] outlineStrokeSequence; /** The current outline stroke index. */ private int outlineStrokeIndex; /** The shape sequence. */ private transient Shape[] shapeSequence; /** The current shape index. */ private int shapeIndex; /** * Creates a new supplier, with default sequences for fill paint, outline * paint, stroke and shapes. */ public DefaultDrawingSupplier() { this(DEFAULT_PAINT_SEQUENCE, DEFAULT_FILL_PAINT_SEQUENCE, DEFAULT_OUTLINE_PAINT_SEQUENCE, DEFAULT_STROKE_SEQUENCE, DEFAULT_OUTLINE_STROKE_SEQUENCE, DEFAULT_SHAPE_SEQUENCE); } /** * Creates a new supplier. * * @param paintSequence the fill paint sequence. * @param outlinePaintSequence the outline paint sequence. * @param strokeSequence the stroke sequence. * @param outlineStrokeSequence the outline stroke sequence. * @param shapeSequence the shape sequence. */ public DefaultDrawingSupplier(Paint[] paintSequence, Paint[] outlinePaintSequence, Stroke[] strokeSequence, Stroke[] outlineStrokeSequence, Shape[] shapeSequence) { this.paintSequence = paintSequence; this.fillPaintSequence = DEFAULT_FILL_PAINT_SEQUENCE; this.outlinePaintSequence = outlinePaintSequence; this.strokeSequence = strokeSequence; this.outlineStrokeSequence = outlineStrokeSequence; this.shapeSequence = shapeSequence; } /** * Creates a new supplier. * * @param paintSequence the paint sequence. * @param fillPaintSequence the fill paint sequence. * @param outlinePaintSequence the outline paint sequence. * @param strokeSequence the stroke sequence. * @param outlineStrokeSequence the outline stroke sequence. * @param shapeSequence the shape sequence. */ public DefaultDrawingSupplier(Paint[] paintSequence, Paint[] fillPaintSequence, Paint[] outlinePaintSequence, Stroke[] strokeSequence, Stroke[] outlineStrokeSequence, Shape[] shapeSequence) { this.paintSequence = paintSequence; this.fillPaintSequence = fillPaintSequence; this.outlinePaintSequence = outlinePaintSequence; this.strokeSequence = strokeSequence; this.outlineStrokeSequence = outlineStrokeSequence; this.shapeSequence = shapeSequence; } /** * Returns the next paint in the sequence. * * @return The paint. */ @Override public Paint getNextPaint() { Paint result = this.paintSequence[this.paintIndex % this.paintSequence.length]; this.paintIndex++; return result; } /** * Returns the next outline paint in the sequence. * * @return The paint. */ @Override public Paint getNextOutlinePaint() { Paint result = this.outlinePaintSequence[ this.outlinePaintIndex % this.outlinePaintSequence.length]; this.outlinePaintIndex++; return result; } /** * Returns the next fill paint in the sequence. * * @return The paint. */ @Override public Paint getNextFillPaint() { Paint result = this.fillPaintSequence[this.fillPaintIndex % this.fillPaintSequence.length]; this.fillPaintIndex++; return result; } /** * Returns the next stroke in the sequence. * * @return The stroke. */ @Override public Stroke getNextStroke() { Stroke result = this.strokeSequence[ this.strokeIndex % this.strokeSequence.length]; this.strokeIndex++; return result; } /** * Returns the next outline stroke in the sequence. * * @return The stroke. */ @Override public Stroke getNextOutlineStroke() { Stroke result = this.outlineStrokeSequence[ this.outlineStrokeIndex % this.outlineStrokeSequence.length]; this.outlineStrokeIndex++; return result; } /** * Returns the next shape in the sequence. * * @return The shape. */ @Override public Shape getNextShape() { Shape result = this.shapeSequence[ this.shapeIndex % this.shapeSequence.length]; this.shapeIndex++; return result; } /** * Creates an array of standard shapes to display for the items in series * on charts. * * @return The array of shapes. */ public static Shape[] createStandardSeriesShapes() { Shape[] result = new Shape[10]; double size = 6.0; double delta = size / 2.0; int[] xpoints; int[] ypoints; // square result[0] = new Rectangle2D.Double(-delta, -delta, size, size); // circle result[1] = new Ellipse2D.Double(-delta, -delta, size, size); // up-pointing triangle xpoints = intArray(0.0, delta, -delta); ypoints = intArray(-delta, delta, delta); result[2] = new Polygon(xpoints, ypoints, 3); // diamond xpoints = intArray(0.0, delta, 0.0, -delta); ypoints = intArray(-delta, 0.0, delta, 0.0); result[3] = new Polygon(xpoints, ypoints, 4); // horizontal rectangle result[4] = new Rectangle2D.Double(-delta, -delta / 2, size, size / 2); // down-pointing triangle xpoints = intArray(-delta, +delta, 0.0); ypoints = intArray(-delta, -delta, delta); result[5] = new Polygon(xpoints, ypoints, 3); // horizontal ellipse result[6] = new Ellipse2D.Double(-delta, -delta / 2, size, size / 2); // right-pointing triangle xpoints = intArray(-delta, delta, -delta); ypoints = intArray(-delta, 0.0, delta); result[7] = new Polygon(xpoints, ypoints, 3); // vertical rectangle result[8] = new Rectangle2D.Double(-delta / 2, -delta, size / 2, size); // left-pointing triangle xpoints = intArray(-delta, delta, delta); ypoints = intArray(0.0, -delta, +delta); result[9] = new Polygon(xpoints, ypoints, 3); return result; } /** * Tests this object for equality with another object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof DefaultDrawingSupplier)) { return false; } DefaultDrawingSupplier that = (DefaultDrawingSupplier) obj; if (!Arrays.equals(this.paintSequence, that.paintSequence)) { return false; } if (this.paintIndex != that.paintIndex) { return false; } if (!Arrays.equals(this.outlinePaintSequence, that.outlinePaintSequence)) { return false; } if (this.outlinePaintIndex != that.outlinePaintIndex) { return false; } if (!Arrays.equals(this.strokeSequence, that.strokeSequence)) { return false; } if (this.strokeIndex != that.strokeIndex) { return false; } if (!Arrays.equals(this.outlineStrokeSequence, that.outlineStrokeSequence)) { return false; } if (this.outlineStrokeIndex != that.outlineStrokeIndex) { return false; } if (!equalShapes(this.shapeSequence, that.shapeSequence)) { return false; } if (this.shapeIndex != that.shapeIndex) { return false; } return true; } /** * A utility method for testing the equality of two arrays of shapes. * * @param s1 the first array ({@code null} permitted). * @param s2 the second array ({@code null} permitted). * * @return A boolean. */ private boolean equalShapes(Shape[] s1, Shape[] s2) { if (s1 == null) { return s2 == null; } if (s2 == null) { return false; } if (s1.length != s2.length) { return false; } for (int i = 0; i < s1.length; i++) { if (!ShapeUtils.equal(s1[i], s2[i])) { return false; } } return true; } /** * Handles serialization. * * @param stream the output stream. * * @throws IOException if there is an I/O problem. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); int paintCount = this.paintSequence.length; stream.writeInt(paintCount); for (int i = 0; i < paintCount; i++) { SerialUtils.writePaint(this.paintSequence[i], stream); } int outlinePaintCount = this.outlinePaintSequence.length; stream.writeInt(outlinePaintCount); for (int i = 0; i < outlinePaintCount; i++) { SerialUtils.writePaint(this.outlinePaintSequence[i], stream); } int strokeCount = this.strokeSequence.length; stream.writeInt(strokeCount); for (int i = 0; i < strokeCount; i++) { SerialUtils.writeStroke(this.strokeSequence[i], stream); } int outlineStrokeCount = this.outlineStrokeSequence.length; stream.writeInt(outlineStrokeCount); for (int i = 0; i < outlineStrokeCount; i++) { SerialUtils.writeStroke(this.outlineStrokeSequence[i], stream); } int shapeCount = this.shapeSequence.length; stream.writeInt(shapeCount); for (int i = 0; i < shapeCount; i++) { SerialUtils.writeShape(this.shapeSequence[i], stream); } } /** * Restores a serialized object. * * @param stream the input stream. * * @throws IOException if there is an I/O problem. * @throws ClassNotFoundException if there is a problem loading a class. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); int paintCount = stream.readInt(); this.paintSequence = new Paint[paintCount]; for (int i = 0; i < paintCount; i++) { this.paintSequence[i] = SerialUtils.readPaint(stream); } int outlinePaintCount = stream.readInt(); this.outlinePaintSequence = new Paint[outlinePaintCount]; for (int i = 0; i < outlinePaintCount; i++) { this.outlinePaintSequence[i] = SerialUtils.readPaint(stream); } int strokeCount = stream.readInt(); this.strokeSequence = new Stroke[strokeCount]; for (int i = 0; i < strokeCount; i++) { this.strokeSequence[i] = SerialUtils.readStroke(stream); } int outlineStrokeCount = stream.readInt(); this.outlineStrokeSequence = new Stroke[outlineStrokeCount]; for (int i = 0; i < outlineStrokeCount; i++) { this.outlineStrokeSequence[i] = SerialUtils.readStroke(stream); } int shapeCount = stream.readInt(); this.shapeSequence = new Shape[shapeCount]; for (int i = 0; i < shapeCount; i++) { this.shapeSequence[i] = SerialUtils.readShape(stream); } } /** * Helper method to avoid lots of explicit casts in getShape(). Returns * an array containing the provided doubles cast to ints. * * @param a x * @param b y * @param c z * * @return int[3] with converted params. */ private static int[] intArray(double a, double b, double c) { return new int[] {(int) a, (int) b, (int) c}; } /** * Helper method to avoid lots of explicit casts in getShape(). Returns * an array containing the provided doubles cast to ints. * * @param a x * @param b y * @param c z * @param d t * * @return int[4] with converted params. */ private static int[] intArray(double a, double b, double c, double d) { return new int[] {(int) a, (int) b, (int) c, (int) d}; } /** * Returns a clone. * * @return A clone. * * @throws CloneNotSupportedException if a component of the supplier does * not support cloning. */ @Override public Object clone() throws CloneNotSupportedException { DefaultDrawingSupplier clone = (DefaultDrawingSupplier) super.clone(); return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/DialShape.java000066400000000000000000000073721463604235500265420ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * DialShape.java * -------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import java.io.ObjectStreamException; import java.io.Serializable; /** * Used to indicate the background shape for a * {@link org.jfree.chart.plot.MeterPlot}. */ public final class DialShape implements Serializable { /** For serialization. */ private static final long serialVersionUID = -3471933055190251131L; /** Circle. */ public static final DialShape CIRCLE = new DialShape("DialShape.CIRCLE"); /** Chord. */ public static final DialShape CHORD = new DialShape("DialShape.CHORD"); /** Pie. */ public static final DialShape PIE = new DialShape("DialShape.PIE"); /** The name. */ private String name; /** * Private constructor. * * @param name the name. */ private DialShape(String name) { this.name = name; } /** * Returns a string representing the object. * * @return The string. */ @Override public String toString() { return this.name; } /** * Returns {@code true} if this object is equal to the specified * object, and {@code false} otherwise. * * @param obj the other object. * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof DialShape)) { return false; } DialShape shape = (DialShape) obj; if (!this.name.equals(shape.toString())) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { return this.name.hashCode(); } /** * Ensures that serialization returns the unique instances. * * @return The object. * * @throws ObjectStreamException if there is a problem. */ private Object readResolve() throws ObjectStreamException { if (this.equals(DialShape.CIRCLE)) { return DialShape.CIRCLE; } else if (this.equals(DialShape.CHORD)) { return DialShape.CHORD; } else if (this.equals(DialShape.PIE)) { return DialShape.PIE; } return null; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/DrawingSupplier.java000066400000000000000000000056571463604235500300330ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * DrawingSupplier.java * -------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; /** * A supplier of {@code Paint}, {@code Stroke} and {@code Shape} * objects for use by plots and renderers. By providing a central place for * obtaining these items, we can ensure that duplication is avoided. *

* To support the cloning of charts, classes that implement this interface * should also implement {@code PublicCloneable}. */ public interface DrawingSupplier { /** * Returns the next paint in a sequence maintained by the supplier. * * @return The paint. */ Paint getNextPaint(); /** * Returns the next outline paint in a sequence maintained by the supplier. * * @return The paint. */ Paint getNextOutlinePaint(); /** * Returns the next fill paint in a sequence maintained by the supplier. * * @return The paint. */ Paint getNextFillPaint(); /** * Returns the next {@code Stroke} object in a sequence maintained by * the supplier. * * @return The stroke. */ Stroke getNextStroke(); /** * Returns the next {@code Stroke} object in a sequence maintained by * the supplier. * * @return The stroke. */ Stroke getNextOutlineStroke(); /** * Returns the next {@code Shape} object in a sequence maintained by * the supplier. * * @return The shape. */ Shape getNextShape(); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/FastScatterPlot.java000066400000000000000000001026701463604235500277670ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * FastScatterPlot.java * -------------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Arnaud Lelievre; * Ulrich Voigt (patch #307); * */ package org.jfree.chart.plot; import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Composite; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Iterator; import java.util.List; import java.util.Objects; import java.util.ResourceBundle; import org.jfree.chart.axis.AxisSpace; import org.jfree.chart.axis.AxisState; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.axis.ValueTick; import org.jfree.chart.event.PlotChangeEvent; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.util.ArrayUtils; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.ResourceBundleWrapper; import org.jfree.chart.util.SerialUtils; import org.jfree.data.Range; /** * A fast scatter plot. */ public class FastScatterPlot extends Plot implements ValueAxisPlot, Pannable, Zoomable, Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 7871545897358563521L; /** The default grid line stroke. */ public static final Stroke DEFAULT_GRIDLINE_STROKE = new BasicStroke(0.5f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0.0f, new float[] {2.0f, 2.0f}, 0.0f); /** The default grid line paint. */ public static final Paint DEFAULT_GRIDLINE_PAINT = Color.LIGHT_GRAY; /** The data. */ private float[][] data; /** The x data range. */ private Range xDataRange; /** The y data range. */ private Range yDataRange; /** The domain axis (used for the x-values). */ private ValueAxis domainAxis; /** The range axis (used for the y-values). */ private ValueAxis rangeAxis; /** The paint used to plot data points. */ private transient Paint paint; /** A flag that controls whether the domain grid-lines are visible. */ private boolean domainGridlinesVisible; /** The stroke used to draw the domain grid-lines. */ private transient Stroke domainGridlineStroke; /** The paint used to draw the domain grid-lines. */ private transient Paint domainGridlinePaint; /** A flag that controls whether the range grid-lines are visible. */ private boolean rangeGridlinesVisible; /** The stroke used to draw the range grid-lines. */ private transient Stroke rangeGridlineStroke; /** The paint used to draw the range grid-lines. */ private transient Paint rangeGridlinePaint; /** * A flag that controls whether or not panning is enabled for the domain * axis. */ private boolean domainPannable; /** * A flag that controls whether or not panning is enabled for the range * axis. */ private boolean rangePannable; /** The resourceBundle for the localization. */ protected static ResourceBundle localizationResources = ResourceBundleWrapper.getBundle( "org.jfree.chart.plot.LocalizationBundle"); /** * Creates a new instance of {@code FastScatterPlot} with default * axes. */ public FastScatterPlot() { this(null, new NumberAxis("X"), new NumberAxis("Y")); } /** * Creates a new fast scatter plot. *

* The data is an array of x, y values: data[0][i] = x, data[1][i] = y. * * @param data the data ({@code null} permitted). * @param domainAxis the domain (x) axis ({@code null} not permitted). * @param rangeAxis the range (y) axis ({@code null} not permitted). */ public FastScatterPlot(float[][] data, ValueAxis domainAxis, ValueAxis rangeAxis) { super(); Args.nullNotPermitted(domainAxis, "domainAxis"); Args.nullNotPermitted(rangeAxis, "rangeAxis"); this.data = data; this.xDataRange = calculateXDataRange(data); this.yDataRange = calculateYDataRange(data); this.domainAxis = domainAxis; this.domainAxis.setPlot(this); this.domainAxis.addChangeListener(this); this.rangeAxis = rangeAxis; this.rangeAxis.setPlot(this); this.rangeAxis.addChangeListener(this); this.paint = Color.RED; this.domainGridlinesVisible = true; this.domainGridlinePaint = FastScatterPlot.DEFAULT_GRIDLINE_PAINT; this.domainGridlineStroke = FastScatterPlot.DEFAULT_GRIDLINE_STROKE; this.rangeGridlinesVisible = true; this.rangeGridlinePaint = FastScatterPlot.DEFAULT_GRIDLINE_PAINT; this.rangeGridlineStroke = FastScatterPlot.DEFAULT_GRIDLINE_STROKE; } /** * Returns a short string describing the plot type. * * @return A short string describing the plot type. */ @Override public String getPlotType() { return localizationResources.getString("Fast_Scatter_Plot"); } /** * Returns the data array used by the plot. * * @return The data array (possibly {@code null}). * * @see #setData(float[][]) */ public float[][] getData() { return this.data; } /** * Sets the data array used by the plot and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param data the data array ({@code null} permitted). * * @see #getData() */ public void setData(float[][] data) { this.data = data; fireChangeEvent(); } /** * Returns the orientation of the plot. * * @return The orientation (always {@link PlotOrientation#VERTICAL}). */ @Override public PlotOrientation getOrientation() { return PlotOrientation.VERTICAL; } /** * Returns the domain axis for the plot. * * @return The domain axis (never {@code null}). * * @see #setDomainAxis(ValueAxis) */ public ValueAxis getDomainAxis() { return this.domainAxis; } /** * Sets the domain axis and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param axis the axis ({@code null} not permitted). * * @see #getDomainAxis() */ public void setDomainAxis(ValueAxis axis) { Args.nullNotPermitted(axis, "axis"); this.domainAxis = axis; fireChangeEvent(); } /** * Returns the range axis for the plot. * * @return The range axis (never {@code null}). * * @see #setRangeAxis(ValueAxis) */ public ValueAxis getRangeAxis() { return this.rangeAxis; } /** * Sets the range axis and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param axis the axis ({@code null} not permitted). * * @see #getRangeAxis() */ public void setRangeAxis(ValueAxis axis) { Args.nullNotPermitted(axis, "axis"); this.rangeAxis = axis; fireChangeEvent(); } /** * Returns the paint used to plot data points. The default is * {@code Color.RED}. * * @return The paint. * * @see #setPaint(Paint) */ public Paint getPaint() { return this.paint; } /** * Sets the color for the data points and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getPaint() */ public void setPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.paint = paint; fireChangeEvent(); } /** * Returns {@code true} if the domain gridlines are visible, and * {@code false} otherwise. * * @return {@code true} or {@code false}. * * @see #setDomainGridlinesVisible(boolean) * @see #setDomainGridlinePaint(Paint) */ public boolean isDomainGridlinesVisible() { return this.domainGridlinesVisible; } /** * Sets the flag that controls whether or not the domain grid-lines are * visible. If the flag value is changed, a {@link PlotChangeEvent} is * sent to all registered listeners. * * @param visible the new value of the flag. * * @see #getDomainGridlinePaint() */ public void setDomainGridlinesVisible(boolean visible) { if (this.domainGridlinesVisible != visible) { this.domainGridlinesVisible = visible; fireChangeEvent(); } } /** * Returns the stroke for the grid-lines (if any) plotted against the * domain axis. * * @return The stroke (never {@code null}). * * @see #setDomainGridlineStroke(Stroke) */ public Stroke getDomainGridlineStroke() { return this.domainGridlineStroke; } /** * Sets the stroke for the grid lines plotted against the domain axis and * sends a {@link PlotChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getDomainGridlineStroke() */ public void setDomainGridlineStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.domainGridlineStroke = stroke; fireChangeEvent(); } /** * Returns the paint for the grid lines (if any) plotted against the domain * axis. * * @return The paint (never {@code null}). * * @see #setDomainGridlinePaint(Paint) */ public Paint getDomainGridlinePaint() { return this.domainGridlinePaint; } /** * Sets the paint for the grid lines plotted against the domain axis and * sends a {@link PlotChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getDomainGridlinePaint() */ public void setDomainGridlinePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.domainGridlinePaint = paint; fireChangeEvent(); } /** * Returns {@code true} if the range axis grid is visible, and * {@code false} otherwise. * * @return {@code true} or {@code false}. * * @see #setRangeGridlinesVisible(boolean) */ public boolean isRangeGridlinesVisible() { return this.rangeGridlinesVisible; } /** * Sets the flag that controls whether or not the range axis grid lines are * visible. If the flag value is changed, a {@link PlotChangeEvent} is * sent to all registered listeners. * * @param visible the new value of the flag. * * @see #isRangeGridlinesVisible() */ public void setRangeGridlinesVisible(boolean visible) { if (this.rangeGridlinesVisible != visible) { this.rangeGridlinesVisible = visible; fireChangeEvent(); } } /** * Returns the stroke for the grid lines (if any) plotted against the range * axis. * * @return The stroke (never {@code null}). * * @see #setRangeGridlineStroke(Stroke) */ public Stroke getRangeGridlineStroke() { return this.rangeGridlineStroke; } /** * Sets the stroke for the grid lines plotted against the range axis and * sends a {@link PlotChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} permitted). * * @see #getRangeGridlineStroke() */ public void setRangeGridlineStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.rangeGridlineStroke = stroke; fireChangeEvent(); } /** * Returns the paint for the grid lines (if any) plotted against the range * axis. * * @return The paint (never {@code null}). * * @see #setRangeGridlinePaint(Paint) */ public Paint getRangeGridlinePaint() { return this.rangeGridlinePaint; } /** * Sets the paint for the grid lines plotted against the range axis and * sends a {@link PlotChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getRangeGridlinePaint() */ public void setRangeGridlinePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.rangeGridlinePaint = paint; fireChangeEvent(); } /** * Draws the fast scatter plot on a Java 2D graphics device (such as the * screen or a printer). * * @param g2 the graphics device. * @param area the area within which the plot (including axis labels) * should be drawn. * @param anchor the anchor point ({@code null} permitted). * @param parentState the state from the parent plot (ignored). * @param info collects chart drawing information ({@code null} * permitted). */ @Override public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, PlotState parentState, PlotRenderingInfo info) { // set up info collection... if (info != null) { info.setPlotArea(area); } // adjust the drawing area for plot insets (if any)... RectangleInsets insets = getInsets(); insets.trim(area); AxisSpace space = new AxisSpace(); space = this.domainAxis.reserveSpace(g2, this, area, RectangleEdge.BOTTOM, space); space = this.rangeAxis.reserveSpace(g2, this, area, RectangleEdge.LEFT, space); Rectangle2D dataArea = space.shrink(area, null); if (info != null) { info.setDataArea(dataArea); } // draw the plot background and axes... drawBackground(g2, dataArea); AxisState domainAxisState = this.domainAxis.draw(g2, dataArea.getMaxY(), area, dataArea, RectangleEdge.BOTTOM, info); AxisState rangeAxisState = this.rangeAxis.draw(g2, dataArea.getMinX(), area, dataArea, RectangleEdge.LEFT, info); drawDomainGridlines(g2, dataArea, domainAxisState.getTicks()); drawRangeGridlines(g2, dataArea, rangeAxisState.getTicks()); Shape originalClip = g2.getClip(); Composite originalComposite = g2.getComposite(); g2.clip(dataArea); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, getForegroundAlpha())); render(g2, dataArea, info, null); g2.setClip(originalClip); g2.setComposite(originalComposite); drawOutline(g2, dataArea); } /** * Draws a representation of the data within the dataArea region. The * {@code info} and {@code crosshairState} arguments may be * {@code null}. * * @param g2 the graphics device. * @param dataArea the region in which the data is to be drawn. * @param info an optional object for collection dimension information. * @param crosshairState collects crosshair information ({@code null} * permitted). */ public void render(Graphics2D g2, Rectangle2D dataArea, PlotRenderingInfo info, CrosshairState crosshairState) { g2.setPaint(this.paint); // if the axes use a linear scale, you can uncomment the code below and // switch to the alternative transX/transY calculation inside the loop // that follows - it is a little bit faster then. // // int xx = (int) dataArea.getMinX(); // int ww = (int) dataArea.getWidth(); // int yy = (int) dataArea.getMaxY(); // int hh = (int) dataArea.getHeight(); // double domainMin = this.domainAxis.getLowerBound(); // double domainLength = this.domainAxis.getUpperBound() - domainMin; // double rangeMin = this.rangeAxis.getLowerBound(); // double rangeLength = this.rangeAxis.getUpperBound() - rangeMin; if (this.data != null) { for (int i = 0; i < this.data[0].length; i++) { float x = this.data[0][i]; float y = this.data[1][i]; //int transX = (int) (xx + ww * (x - domainMin) / domainLength); //int transY = (int) (yy - hh * (y - rangeMin) / rangeLength); int transX = (int) this.domainAxis.valueToJava2D(x, dataArea, RectangleEdge.BOTTOM); int transY = (int) this.rangeAxis.valueToJava2D(y, dataArea, RectangleEdge.LEFT); g2.fillRect(transX, transY, 1, 1); } } } /** * Draws the gridlines for the plot, if they are visible. * * @param g2 the graphics device. * @param dataArea the data area. * @param ticks the ticks. */ protected void drawDomainGridlines(Graphics2D g2, Rectangle2D dataArea, List ticks) { if (!isDomainGridlinesVisible()) { return; } Object saved = g2.getRenderingHint(RenderingHints.KEY_STROKE_CONTROL); g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE); Iterator iterator = ticks.iterator(); while (iterator.hasNext()) { ValueTick tick = (ValueTick) iterator.next(); double v = this.domainAxis.valueToJava2D(tick.getValue(), dataArea, RectangleEdge.BOTTOM); Line2D line = new Line2D.Double(v, dataArea.getMinY(), v, dataArea.getMaxY()); g2.setPaint(getDomainGridlinePaint()); g2.setStroke(getDomainGridlineStroke()); g2.draw(line); } g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, saved); } /** * Draws the gridlines for the plot, if they are visible. * * @param g2 the graphics device. * @param dataArea the data area. * @param ticks the ticks. */ protected void drawRangeGridlines(Graphics2D g2, Rectangle2D dataArea, List ticks) { if (!isRangeGridlinesVisible()) { return; } Object saved = g2.getRenderingHint(RenderingHints.KEY_STROKE_CONTROL); g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE); Iterator iterator = ticks.iterator(); while (iterator.hasNext()) { ValueTick tick = (ValueTick) iterator.next(); double v = this.rangeAxis.valueToJava2D(tick.getValue(), dataArea, RectangleEdge.LEFT); Line2D line = new Line2D.Double(dataArea.getMinX(), v, dataArea.getMaxX(), v); g2.setPaint(getRangeGridlinePaint()); g2.setStroke(getRangeGridlineStroke()); g2.draw(line); } g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, saved); } /** * Returns the range of data values to be plotted along the axis, or * {@code null} if the specified axis isn't the domain axis or the * range axis for the plot. * * @param axis the axis ({@code null} permitted). * * @return The range (possibly {@code null}). */ @Override public Range getDataRange(ValueAxis axis) { Range result = null; if (axis == this.domainAxis) { result = this.xDataRange; } else if (axis == this.rangeAxis) { result = this.yDataRange; } return result; } /** * Calculates the X data range. * * @param data the data ({@code null} permitted). * * @return The range. */ private Range calculateXDataRange(float[][] data) { Range result = null; if (data != null) { float lowest = Float.POSITIVE_INFINITY; float highest = Float.NEGATIVE_INFINITY; for (int i = 0; i < data[0].length; i++) { float v = data[0][i]; if (v < lowest) { lowest = v; } if (v > highest) { highest = v; } } if (lowest <= highest) { result = new Range(lowest, highest); } } return result; } /** * Calculates the Y data range. * * @param data the data ({@code null} permitted). * * @return The range. */ private Range calculateYDataRange(float[][] data) { Range result = null; if (data != null) { float lowest = Float.POSITIVE_INFINITY; float highest = Float.NEGATIVE_INFINITY; for (int i = 0; i < data[0].length; i++) { float v = data[1][i]; if (v < lowest) { lowest = v; } if (v > highest) { highest = v; } } if (lowest <= highest) { result = new Range(lowest, highest); } } return result; } /** * Multiplies the range on the domain axis by the specified factor. * * @param factor the zoom factor. * @param info the plot rendering info. * @param source the source point. */ @Override public void zoomDomainAxes(double factor, PlotRenderingInfo info, Point2D source) { this.domainAxis.resizeRange(factor); } /** * Multiplies the range on the domain axis by the specified factor. * * @param factor the zoom factor. * @param info the plot rendering info. * @param source the source point (in Java2D space). * @param useAnchor use source point as zoom anchor? * * @see #zoomRangeAxes(double, PlotRenderingInfo, Point2D, boolean) */ @Override public void zoomDomainAxes(double factor, PlotRenderingInfo info, Point2D source, boolean useAnchor) { if (useAnchor) { // get the source coordinate - this plot has always a VERTICAL // orientation double sourceX = source.getX(); double anchorX = this.domainAxis.java2DToValue(sourceX, info.getDataArea(), RectangleEdge.BOTTOM); this.domainAxis.resizeRange2(factor, anchorX); } else { this.domainAxis.resizeRange(factor); } } /** * Zooms in on the domain axes. * * @param lowerPercent the new lower bound as a percentage of the current * range. * @param upperPercent the new upper bound as a percentage of the current * range. * @param info the plot rendering info. * @param source the source point. */ @Override public void zoomDomainAxes(double lowerPercent, double upperPercent, PlotRenderingInfo info, Point2D source) { this.domainAxis.zoomRange(lowerPercent, upperPercent); } /** * Multiplies the range on the range axis/axes by the specified factor. * * @param factor the zoom factor. * @param info the plot rendering info. * @param source the source point. */ @Override public void zoomRangeAxes(double factor, PlotRenderingInfo info, Point2D source) { this.rangeAxis.resizeRange(factor); } /** * Multiplies the range on the range axis by the specified factor. * * @param factor the zoom factor. * @param info the plot rendering info. * @param source the source point (in Java2D space). * @param useAnchor use source point as zoom anchor? * * @see #zoomDomainAxes(double, PlotRenderingInfo, Point2D, boolean) */ @Override public void zoomRangeAxes(double factor, PlotRenderingInfo info, Point2D source, boolean useAnchor) { if (useAnchor) { // get the source coordinate - this plot has always a VERTICAL // orientation double sourceY = source.getY(); double anchorY = this.rangeAxis.java2DToValue(sourceY, info.getDataArea(), RectangleEdge.LEFT); this.rangeAxis.resizeRange2(factor, anchorY); } else { this.rangeAxis.resizeRange(factor); } } /** * Zooms in on the range axes. * * @param lowerPercent the new lower bound as a percentage of the current * range. * @param upperPercent the new upper bound as a percentage of the current * range. * @param info the plot rendering info. * @param source the source point. */ @Override public void zoomRangeAxes(double lowerPercent, double upperPercent, PlotRenderingInfo info, Point2D source) { this.rangeAxis.zoomRange(lowerPercent, upperPercent); } /** * Returns {@code true}. * * @return A boolean. */ @Override public boolean isDomainZoomable() { return true; } /** * Returns {@code true}. * * @return A boolean. */ @Override public boolean isRangeZoomable() { return true; } /** * Returns {@code true} if panning is enabled for the domain axes, * and {@code false} otherwise. * * @return A boolean. */ @Override public boolean isDomainPannable() { return this.domainPannable; } /** * Sets the flag that enables or disables panning of the plot along the * domain axes. * * @param pannable the new flag value. */ public void setDomainPannable(boolean pannable) { this.domainPannable = pannable; } /** * Returns {@code true} if panning is enabled for the range axes, * and {@code false} otherwise. * * @return A boolean. */ @Override public boolean isRangePannable() { return this.rangePannable; } /** * Sets the flag that enables or disables panning of the plot along * the range axes. * * @param pannable the new flag value. */ public void setRangePannable(boolean pannable) { this.rangePannable = pannable; } /** * Pans the domain axes by the specified percentage. * * @param percent the distance to pan (as a percentage of the axis length). * @param info the plot info * @param source the source point where the pan action started. */ @Override public void panDomainAxes(double percent, PlotRenderingInfo info, Point2D source) { if (!isDomainPannable() || this.domainAxis == null) { return; } double length = this.domainAxis.getRange().getLength(); double adj = percent * length; if (this.domainAxis.isInverted()) { adj = -adj; } this.domainAxis.setRange(this.domainAxis.getLowerBound() + adj, this.domainAxis.getUpperBound() + adj); } /** * Pans the range axes by the specified percentage. * * @param percent the distance to pan (as a percentage of the axis length). * @param info the plot info * @param source the source point where the pan action started. */ @Override public void panRangeAxes(double percent, PlotRenderingInfo info, Point2D source) { if (!isRangePannable() || this.rangeAxis == null) { return; } double length = this.rangeAxis.getRange().getLength(); double adj = percent * length; if (this.rangeAxis.isInverted()) { adj = -adj; } this.rangeAxis.setRange(this.rangeAxis.getLowerBound() + adj, this.rangeAxis.getUpperBound() + adj); } /** * Tests an arbitrary object for equality with this plot. Note that * {@code FastScatterPlot} carries its data around with it (rather * than referencing a dataset), and the data is included in the * equality test. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!super.equals(obj)) { return false; } if (!(obj instanceof FastScatterPlot)) { return false; } FastScatterPlot that = (FastScatterPlot) obj; if (this.domainPannable != that.domainPannable) { return false; } if (this.rangePannable != that.rangePannable) { return false; } if (!ArrayUtils.equal(this.data, that.data)) { return false; } if (!Objects.equals(this.domainAxis, that.domainAxis)) { return false; } if (!Objects.equals(this.rangeAxis, that.rangeAxis)) { return false; } if (!PaintUtils.equal(this.paint, that.paint)) { return false; } if (this.domainGridlinesVisible != that.domainGridlinesVisible) { return false; } if (!PaintUtils.equal(this.domainGridlinePaint, that.domainGridlinePaint)) { return false; } if (!Objects.equals(this.domainGridlineStroke, that.domainGridlineStroke)) { return false; } if (!this.rangeGridlinesVisible == that.rangeGridlinesVisible) { return false; } if (!PaintUtils.equal(this.rangeGridlinePaint, that.rangeGridlinePaint)) { return false; } if (!Objects.equals(this.rangeGridlineStroke, that.rangeGridlineStroke)) { return false; } return true; } /** * Returns a clone of the plot. * * @return A clone. * * @throws CloneNotSupportedException if some component of the plot does * not support cloning. */ @Override public Object clone() throws CloneNotSupportedException { FastScatterPlot clone = (FastScatterPlot) super.clone(); if (this.data != null) { clone.data = ArrayUtils.clone(this.data); } if (this.domainAxis != null) { clone.domainAxis = (ValueAxis) this.domainAxis.clone(); clone.domainAxis.setPlot(clone); clone.domainAxis.addChangeListener(clone); } if (this.rangeAxis != null) { clone.rangeAxis = (ValueAxis) this.rangeAxis.clone(); clone.rangeAxis.setPlot(clone); clone.rangeAxis.addChangeListener(clone); } return clone; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.paint, stream); SerialUtils.writeStroke(this.domainGridlineStroke, stream); SerialUtils.writePaint(this.domainGridlinePaint, stream); SerialUtils.writeStroke(this.rangeGridlineStroke, stream); SerialUtils.writePaint(this.rangeGridlinePaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.paint = SerialUtils.readPaint(stream); this.domainGridlineStroke = SerialUtils.readStroke(stream); this.domainGridlinePaint = SerialUtils.readPaint(stream); this.rangeGridlineStroke = SerialUtils.readStroke(stream); this.rangeGridlinePaint = SerialUtils.readPaint(stream); if (this.domainAxis != null) { this.domainAxis.addChangeListener(this); } if (this.rangeAxis != null) { this.rangeAxis.addChangeListener(this); } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/IntervalMarker.java000066400000000000000000000177751463604235500276460ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * IntervalMarker.java * ------------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.plot; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Paint; import java.awt.Stroke; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.event.MarkerChangeEvent; import org.jfree.chart.ui.GradientPaintTransformer; import org.jfree.chart.ui.LengthAdjustmentType; /** * Represents an interval to be highlighted in some way. */ public class IntervalMarker extends Marker implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -1762344775267627916L; /** The start value. */ private double startValue; /** The end value. */ private double endValue; /** The gradient paint transformer (optional). */ private GradientPaintTransformer gradientPaintTransformer; /** * Constructs an interval marker. * * @param start the start of the interval. * @param end the end of the interval. */ public IntervalMarker(double start, double end) { this(start, end, Color.GRAY, new BasicStroke(0.5f), Color.GRAY, new BasicStroke(0.5f), 0.8f); } /** * Creates a new interval marker with the specified range and fill paint. * The outline paint and stroke default to {@code null}. * * @param start the lower bound of the interval. * @param end the upper bound of the interval. * @param paint the fill paint ({@code null} not permitted). */ public IntervalMarker(double start, double end, Paint paint) { this(start, end, paint, new BasicStroke(0.5f), null, null, 0.8f); } /** * Constructs an interval marker. * * @param start the start of the interval. * @param end the end of the interval. * @param paint the paint ({@code null} not permitted). * @param stroke the stroke ({@code null} not permitted). * @param outlinePaint the outline paint. * @param outlineStroke the outline stroke. * @param alpha the alpha transparency. */ public IntervalMarker(double start, double end, Paint paint, Stroke stroke, Paint outlinePaint, Stroke outlineStroke, float alpha) { super(paint, stroke, outlinePaint, outlineStroke, alpha); this.startValue = start; this.endValue = end; this.gradientPaintTransformer = null; setLabelOffsetType(LengthAdjustmentType.CONTRACT); } /** * Returns the start value for the interval. * * @return The start value. */ public double getStartValue() { return this.startValue; } /** * Sets the start value for the marker and sends a * {@link MarkerChangeEvent} to all registered listeners. * * @param value the value. */ public void setStartValue(double value) { this.startValue = value; notifyListeners(new MarkerChangeEvent(this)); } /** * Returns the end value for the interval. * * @return The end value. */ public double getEndValue() { return this.endValue; } /** * Sets the end value for the marker and sends a * {@link MarkerChangeEvent} to all registered listeners. * * @param value the value. */ public void setEndValue(double value) { this.endValue = value; notifyListeners(new MarkerChangeEvent(this)); } /** * Returns the gradient paint transformer. * * @return The gradient paint transformer (possibly {@code null}). */ public GradientPaintTransformer getGradientPaintTransformer() { return this.gradientPaintTransformer; } /** * Sets the gradient paint transformer and sends a * {@link MarkerChangeEvent} to all registered listeners. * * @param transformer the transformer ({@code null} permitted). */ public void setGradientPaintTransformer( GradientPaintTransformer transformer) { this.gradientPaintTransformer = transformer; notifyListeners(new MarkerChangeEvent(this)); } /** * Tests the marker for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof IntervalMarker)) { return false; } IntervalMarker that = (IntervalMarker) obj; if (!that.canEqual(this)) { return false; } if (Double.doubleToLongBits(this.startValue) != Double.doubleToLongBits(that.startValue)) { return false; } if (Double.doubleToLongBits(this.endValue) != Double.doubleToLongBits(that.endValue)) { return false; } if (!Objects.equals(this.gradientPaintTransformer, that.gradientPaintTransformer)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // Solves Problem: equals not symmetric return (other instanceof IntervalMarker); } @Override public int hashCode() { int hash = super.hashCode(); // equals calls superclass, hashCode must also hash = 47 * hash + (int) (Double.doubleToLongBits(this.startValue) ^ (Double.doubleToLongBits(this.startValue) >>> 32)); hash = 47 * hash + (int) (Double.doubleToLongBits(this.endValue) ^ (Double.doubleToLongBits(this.endValue) >>> 32)); hash = 47 * hash + Objects.hashCode(this.gradientPaintTransformer); return hash; } /** * Returns a clone of the marker. * * @return A clone. * * @throws CloneNotSupportedException Not thrown by this class, but the * exception is declared for the use of subclasses. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/Marker.java000066400000000000000000000522251463604235500261260ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------- * Marker.java * ----------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Nicolas Brodu; * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.plot; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.Paint; import java.awt.Stroke; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.EventListener; import java.util.Objects; import javax.swing.event.EventListenerList; import org.jfree.chart.HashUtils; import org.jfree.chart.event.MarkerChangeEvent; import org.jfree.chart.event.MarkerChangeListener; import org.jfree.chart.ui.LengthAdjustmentType; import org.jfree.chart.ui.RectangleAnchor; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.ui.TextAnchor; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.SerialUtils; /** * The base class for markers that can be added to plots to highlight a value * or range of values. *

* An event notification mechanism was added to this class in JFreeChart * version 1.0.3. */ public abstract class Marker implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -734389651405327166L; /** The paint (null is not allowed). */ private transient Paint paint; /** The stroke (null is not allowed). */ private transient Stroke stroke; /** The outline paint. */ private transient Paint outlinePaint; /** The outline stroke. */ private transient Stroke outlineStroke; /** The alpha transparency. */ private float alpha; /** The label. */ private String label = null; /** The label font. */ private Font labelFont; /** The label paint. */ private transient Paint labelPaint; /** The label background color. */ private Color labelBackgroundColor; /** The label position. */ private RectangleAnchor labelAnchor; /** The text anchor for the label. */ private TextAnchor labelTextAnchor; /** The label offset from the marker rectangle. */ private RectangleInsets labelOffset; /** * The offset type for the domain or range axis (never {@code null}). */ private LengthAdjustmentType labelOffsetType; /** Storage for registered change listeners. */ private transient EventListenerList listenerList; /** * Creates a new marker with default attributes. */ protected Marker() { this(Color.GRAY); } /** * Constructs a new marker. * * @param paint the paint ({@code null} not permitted). */ protected Marker(Paint paint) { this(paint, new BasicStroke(0.5f), Color.GRAY, new BasicStroke(0.5f), 0.80f); } /** * Constructs a new marker. * * @param paint the paint ({@code null} not permitted). * @param stroke the stroke ({@code null} not permitted). * @param outlinePaint the outline paint ({@code null} permitted). * @param outlineStroke the outline stroke ({@code null} permitted). * @param alpha the alpha transparency (must be in the range 0.0f to * 1.0f). * * @throws IllegalArgumentException if {@code paint} or * {@code stroke} is {@code null}, or {@code alpha} is * not in the specified range. */ protected Marker(Paint paint, Stroke stroke, Paint outlinePaint, Stroke outlineStroke, float alpha) { Args.nullNotPermitted(paint, "paint"); Args.nullNotPermitted(stroke, "stroke"); if (alpha < 0.0f || alpha > 1.0f) { throw new IllegalArgumentException( "The 'alpha' value must be in the range 0.0f to 1.0f"); } this.paint = paint; this.stroke = stroke; this.outlinePaint = outlinePaint; this.outlineStroke = outlineStroke; this.alpha = alpha; this.labelFont = new Font("SansSerif", Font.PLAIN, 9); this.labelPaint = Color.BLACK; this.labelBackgroundColor = new Color(100, 100, 100, 100); this.labelAnchor = RectangleAnchor.TOP_LEFT; this.labelOffset = new RectangleInsets(3.0, 3.0, 3.0, 3.0); this.labelOffsetType = LengthAdjustmentType.CONTRACT; this.labelTextAnchor = TextAnchor.CENTER; this.listenerList = new EventListenerList(); } /** * Returns the paint. * * @return The paint (never {@code null}). * * @see #setPaint(Paint) */ public Paint getPaint() { return this.paint; } /** * Sets the paint and sends a {@link MarkerChangeEvent} to all registered * listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getPaint() */ public void setPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.paint = paint; notifyListeners(new MarkerChangeEvent(this)); } /** * Returns the stroke. * * @return The stroke (never {@code null}). * * @see #setStroke(Stroke) */ public Stroke getStroke() { return this.stroke; } /** * Sets the stroke and sends a {@link MarkerChangeEvent} to all registered * listeners. * * @param stroke the stroke ({@code null}not permitted). * * @see #getStroke() */ public void setStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.stroke = stroke; notifyListeners(new MarkerChangeEvent(this)); } /** * Returns the outline paint. * * @return The outline paint (possibly {@code null}). * * @see #setOutlinePaint(Paint) */ public Paint getOutlinePaint() { return this.outlinePaint; } /** * Sets the outline paint and sends a {@link MarkerChangeEvent} to all * registered listeners. * * @param paint the paint ({@code null} permitted). * * @see #getOutlinePaint() */ public void setOutlinePaint(Paint paint) { this.outlinePaint = paint; notifyListeners(new MarkerChangeEvent(this)); } /** * Returns the outline stroke. * * @return The outline stroke (possibly {@code null}). * * @see #setOutlineStroke(Stroke) */ public Stroke getOutlineStroke() { return this.outlineStroke; } /** * Sets the outline stroke and sends a {@link MarkerChangeEvent} to all * registered listeners. * * @param stroke the stroke ({@code null} permitted). * * @see #getOutlineStroke() */ public void setOutlineStroke(Stroke stroke) { this.outlineStroke = stroke; notifyListeners(new MarkerChangeEvent(this)); } /** * Returns the alpha transparency. * * @return The alpha transparency. * * @see #setAlpha(float) */ public float getAlpha() { return this.alpha; } /** * Sets the alpha transparency that should be used when drawing the * marker, and sends a {@link MarkerChangeEvent} to all registered * listeners. The alpha transparency is a value in the range 0.0f * (completely transparent) to 1.0f (completely opaque). * * @param alpha the alpha transparency (must be in the range 0.0f to * 1.0f). * * @throws IllegalArgumentException if {@code alpha} is not in the * specified range. * * @see #getAlpha() */ public void setAlpha(float alpha) { if (alpha < 0.0f || alpha > 1.0f) { throw new IllegalArgumentException( "The 'alpha' value must be in the range 0.0f to 1.0f"); } this.alpha = alpha; notifyListeners(new MarkerChangeEvent(this)); } /** * Returns the label (if {@code null} no label is displayed). * * @return The label (possibly {@code null}). * * @see #setLabel(String) */ public String getLabel() { return this.label; } /** * Sets the label (if {@code null} no label is displayed) and sends a * {@link MarkerChangeEvent} to all registered listeners. * * @param label the label ({@code null} permitted). * * @see #getLabel() */ public void setLabel(String label) { this.label = label; notifyListeners(new MarkerChangeEvent(this)); } /** * Returns the label font. * * @return The label font (never {@code null}). * * @see #setLabelFont(Font) */ public Font getLabelFont() { return this.labelFont; } /** * Sets the label font and sends a {@link MarkerChangeEvent} to all * registered listeners. * * @param font the font ({@code null} not permitted). * * @see #getLabelFont() */ public void setLabelFont(Font font) { Args.nullNotPermitted(font, "font"); this.labelFont = font; notifyListeners(new MarkerChangeEvent(this)); } /** * Returns the label paint. * * @return The label paint (never {@code null}). * * @see #setLabelPaint(Paint) */ public Paint getLabelPaint() { return this.labelPaint; } /** * Sets the label paint and sends a {@link MarkerChangeEvent} to all * registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getLabelPaint() */ public void setLabelPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.labelPaint = paint; notifyListeners(new MarkerChangeEvent(this)); } /** * Returns the label background color. The default value is * {@code Color(100, 100, 100, 100)}.. * * @return The label background color (never {@code null}). */ public Color getLabelBackgroundColor() { return this.labelBackgroundColor; } /** * Sets the label background color. * * @param color the color ({@code null} not permitted). */ public void setLabelBackgroundColor(Color color) { Args.nullNotPermitted(color, "color"); this.labelBackgroundColor = color; } /** * Returns the label anchor. This defines the position of the label * anchor, relative to the bounds of the marker. * * @return The label anchor (never {@code null}). * * @see #setLabelAnchor(RectangleAnchor) */ public RectangleAnchor getLabelAnchor() { return this.labelAnchor; } /** * Sets the label anchor and sends a {@link MarkerChangeEvent} to all * registered listeners. The anchor defines the position of the label * anchor, relative to the bounds of the marker. * * @param anchor the anchor ({@code null} not permitted). * * @see #getLabelAnchor() */ public void setLabelAnchor(RectangleAnchor anchor) { Args.nullNotPermitted(anchor, "anchor"); this.labelAnchor = anchor; notifyListeners(new MarkerChangeEvent(this)); } /** * Returns the label offset. * * @return The label offset (never {@code null}). * * @see #setLabelOffset(RectangleInsets) */ public RectangleInsets getLabelOffset() { return this.labelOffset; } /** * Sets the label offset and sends a {@link MarkerChangeEvent} to all * registered listeners. * * @param offset the label offset ({@code null} not permitted). * * @see #getLabelOffset() */ public void setLabelOffset(RectangleInsets offset) { Args.nullNotPermitted(offset, "offset"); this.labelOffset = offset; notifyListeners(new MarkerChangeEvent(this)); } /** * Returns the label offset type. * * @return The type (never {@code null}). * * @see #setLabelOffsetType(LengthAdjustmentType) */ public LengthAdjustmentType getLabelOffsetType() { return this.labelOffsetType; } /** * Sets the label offset type and sends a {@link MarkerChangeEvent} to all * registered listeners. * * @param adj the type ({@code null} not permitted). * * @see #getLabelOffsetType() */ public void setLabelOffsetType(LengthAdjustmentType adj) { Args.nullNotPermitted(adj, "adj"); this.labelOffsetType = adj; notifyListeners(new MarkerChangeEvent(this)); } /** * Returns the label text anchor. * * @return The label text anchor (never {@code null}). * * @see #setLabelTextAnchor(TextAnchor) */ public TextAnchor getLabelTextAnchor() { return this.labelTextAnchor; } /** * Sets the label text anchor and sends a {@link MarkerChangeEvent} to * all registered listeners. * * @param anchor the label text anchor ({@code null} not permitted). * * @see #getLabelTextAnchor() */ public void setLabelTextAnchor(TextAnchor anchor) { Args.nullNotPermitted(anchor, "anchor"); this.labelTextAnchor = anchor; notifyListeners(new MarkerChangeEvent(this)); } /** * Registers an object for notification of changes to the marker. * * @param listener the object to be registered. * * @see #removeChangeListener(MarkerChangeListener) */ public void addChangeListener(MarkerChangeListener listener) { this.listenerList.add(MarkerChangeListener.class, listener); } /** * Unregisters an object for notification of changes to the marker. * * @param listener the object to be unregistered. * * @see #addChangeListener(MarkerChangeListener) */ public void removeChangeListener(MarkerChangeListener listener) { this.listenerList.remove(MarkerChangeListener.class, listener); } /** * Notifies all registered listeners that the marker has been modified. * * @param event information about the change event. */ public void notifyListeners(MarkerChangeEvent event) { Object[] listeners = this.listenerList.getListenerList(); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == MarkerChangeListener.class) { ((MarkerChangeListener) listeners[i + 1]).markerChanged(event); } } } /** * Returns an array containing all the listeners of the specified type. * * @param listenerType the listener type. * * @return The array of listeners. */ public EventListener[] getListeners(Class listenerType) { return this.listenerList.getListeners(listenerType); } /** * Tests the marker for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof Marker)) { return false; } Marker that = (Marker) obj; if (!that.canEqual(this)) { return false; } if (!PaintUtils.equal(this.paint, that.paint)) { return false; } if (!Objects.equals(this.stroke, that.stroke)) { return false; } if (!PaintUtils.equal(this.outlinePaint, that.outlinePaint)) { return false; } if (!Objects.equals(this.outlineStroke, that.outlineStroke)) { return false; } if (Float.floatToIntBits(this.alpha) != Float.floatToIntBits(that.alpha)) { return false; } if (!Objects.equals(this.label, that.label)) { return false; } if (!Objects.equals(this.labelFont, that.labelFont)) { return false; } if (!PaintUtils.equal(this.labelPaint, that.labelPaint)) { return false; } if (!Objects.equals(this.labelBackgroundColor,that.labelBackgroundColor)) { return false; } if (!Objects.equals(this.labelAnchor, that.labelAnchor)) { return false; } if (!Objects.equals(this.labelTextAnchor, that.labelTextAnchor)) { return false; } if (!Objects.equals(this.labelOffset, that.labelOffset)) { return false; } if (!Objects.equals(this.labelOffsetType,that.labelOffsetType)) { return false; } return true; } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ public boolean canEqual(Object other) { // Solves Problem: equals not symmetric return (other instanceof Marker); } @Override public int hashCode() { int hash = 7; hash = 43 * hash + HashUtils.hashCodeForPaint(this.paint); hash = 43 * hash + Objects.hashCode(this.stroke); hash = 43 * hash + HashUtils.hashCodeForPaint(this.outlinePaint); hash = 43 * hash + Objects.hashCode(this.outlineStroke); hash = 43 * hash + Float.floatToIntBits(this.alpha); hash = 43 * hash + Objects.hashCode(this.label); hash = 43 * hash + Objects.hashCode(this.labelFont); hash = 43 * hash + HashUtils.hashCodeForPaint(this.labelPaint); hash = 43 * hash + Objects.hashCode(this.labelBackgroundColor); hash = 43 * hash + Objects.hashCode(this.labelAnchor); hash = 43 * hash + Objects.hashCode(this.labelTextAnchor); hash = 43 * hash + Objects.hashCode(this.labelOffset); hash = 43 * hash + Objects.hashCode(this.labelOffsetType); return hash; } /** * Creates a clone of the marker. * * @return A clone. * * @throws CloneNotSupportedException never. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.paint, stream); SerialUtils.writeStroke(this.stroke, stream); SerialUtils.writePaint(this.outlinePaint, stream); SerialUtils.writeStroke(this.outlineStroke, stream); SerialUtils.writePaint(this.labelPaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.paint = SerialUtils.readPaint(stream); this.stroke = SerialUtils.readStroke(stream); this.outlinePaint = SerialUtils.readPaint(stream); this.outlineStroke = SerialUtils.readStroke(stream); this.labelPaint = SerialUtils.readPaint(stream); this.listenerList = new EventListenerList(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/MeterInterval.java000066400000000000000000000153751463604235500274730ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * MeterInterval.java * ------------------ * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Paint; import java.awt.Stroke; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.SerialUtils; import org.jfree.data.Range; /** * An interval to be highlighted on a {@link MeterPlot}. Instances of this * class are immutable. */ public class MeterInterval implements Serializable { /** For serialization. */ private static final long serialVersionUID = 1530982090622488257L; /** The interval label. */ private String label; /** The interval range. */ private Range range; /** The outline paint (used for the arc marking the interval). */ private transient Paint outlinePaint; /** The outline stroke (used for the arc marking the interval). */ private transient Stroke outlineStroke; /** The background paint for the interval. */ private transient Paint backgroundPaint; /** * Creates a new interval. * * @param label the label ({@code null} not permitted). * @param range the range ({@code null} not permitted). */ public MeterInterval(String label, Range range) { this(label, range, Color.YELLOW, new BasicStroke(2.0f), null); } /** * Creates a new interval. * * @param label the label ({@code null} not permitted). * @param range the range ({@code null} not permitted). * @param outlinePaint the outline paint ({@code null} permitted). * @param outlineStroke the outline stroke ({@code null} permitted). * @param backgroundPaint the background paint ({@code null} * permitted). */ public MeterInterval(String label, Range range, Paint outlinePaint, Stroke outlineStroke, Paint backgroundPaint) { Args.nullNotPermitted(label, "label"); Args.nullNotPermitted(range, "range"); this.label = label; this.range = range; this.outlinePaint = outlinePaint; this.outlineStroke = outlineStroke; this.backgroundPaint = backgroundPaint; } /** * Returns the label. * * @return The label (never {@code null}). */ public String getLabel() { return this.label; } /** * Returns the range. * * @return The range (never {@code null}). */ public Range getRange() { return this.range; } /** * Returns the background paint. If {@code null}, the background * should remain unfilled. * * @return The background paint (possibly {@code null}). */ public Paint getBackgroundPaint() { return this.backgroundPaint; } /** * Returns the outline paint. * * @return The outline paint (possibly {@code null}). */ public Paint getOutlinePaint() { return this.outlinePaint; } /** * Returns the outline stroke. * * @return The outline stroke (possibly {@code null}). */ public Stroke getOutlineStroke() { return this.outlineStroke; } /** * Checks this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof MeterInterval)) { return false; } MeterInterval that = (MeterInterval) obj; if (!this.label.equals(that.label)) { return false; } if (!this.range.equals(that.range)) { return false; } if (!PaintUtils.equal(this.outlinePaint, that.outlinePaint)) { return false; } if (!Objects.equals(this.outlineStroke, that.outlineStroke)) { return false; } if (!PaintUtils.equal(this.backgroundPaint, that.backgroundPaint)) { return false; } return true; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.outlinePaint, stream); SerialUtils.writeStroke(this.outlineStroke, stream); SerialUtils.writePaint(this.backgroundPaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.outlinePaint = SerialUtils.readPaint(stream); this.outlineStroke = SerialUtils.readStroke(stream); this.backgroundPaint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/MeterPlot.java000066400000000000000000001214671463604235500266250ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * MeterPlot.java * -------------- * (C) Copyright 2000-present, by Hari and Contributors. * * Original Author: Hari (ourhari@hotmail.com); * Contributor(s): David Gilbert; * Bob Orchard; * Arnaud Lelievre; * Nicolas Brodu; * David Bastend; * */ package org.jfree.chart.plot; import org.jfree.chart.LegendItem; import org.jfree.chart.LegendItemCollection; import org.jfree.chart.event.PlotChangeEvent; import org.jfree.chart.text.TextUtils; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.ui.TextAnchor; import org.jfree.chart.util.Args; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.SerialUtils; import org.jfree.data.Range; import org.jfree.data.general.DatasetChangeEvent; import org.jfree.data.general.ValueDataset; import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Composite; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Polygon; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Arc2D; import java.awt.geom.Ellipse2D; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.ResourceBundle; /** * A plot that displays a single value in the form of a needle on a dial. * Defined ranges (for example, 'normal', 'warning' and 'critical') can be * highlighted on the dial. */ public class MeterPlot extends Plot implements Serializable, Cloneable { /** For serialization. */ private static final long serialVersionUID = 2987472457734470962L; /** The default background paint. */ static final Paint DEFAULT_DIAL_BACKGROUND_PAINT = Color.BLACK; /** The default needle paint. */ static final Paint DEFAULT_NEEDLE_PAINT = Color.GREEN; /** The default value font. */ static final Font DEFAULT_VALUE_FONT = new Font("SansSerif", Font.BOLD, 12); /** The default value paint. */ static final Paint DEFAULT_VALUE_PAINT = Color.YELLOW; /** The default meter angle. */ public static final int DEFAULT_METER_ANGLE = 270; /** The default border size. */ public static final float DEFAULT_BORDER_SIZE = 3f; /** The default circle size. */ public static final float DEFAULT_CIRCLE_SIZE = 10f; /** The default label font. */ public static final Font DEFAULT_LABEL_FONT = new Font("SansSerif", Font.BOLD, 10); /** The dataset (contains a single value). */ private ValueDataset dataset; /** The dial shape (background shape). */ private DialShape shape; /** The dial extent (measured in degrees). */ private int meterAngle; /** The overall range of data values on the dial. */ private Range range; /** The tick size. */ private double tickSize; /** The paint used to draw the ticks. */ private transient Paint tickPaint; /** The units displayed on the dial. */ private String units; /** The font for the value displayed in the center of the dial. */ private Font valueFont; /** The paint for the value displayed in the center of the dial. */ private transient Paint valuePaint; /** A flag that indicates whether the value is visible. */ private boolean valueVisible = true; /** A flag that controls whether or not the border is drawn. */ private boolean drawBorder; /** The outline paint. */ private transient Paint dialOutlinePaint; /** The paint for the dial background. */ private transient Paint dialBackgroundPaint; /** The paint for the needle. */ private transient Paint needlePaint; /** A flag that controls whether or not the tick labels are visible. */ private boolean tickLabelsVisible; /** The tick label font. */ private Font tickLabelFont; /** The tick label paint. */ private transient Paint tickLabelPaint; /** The tick label format. */ private NumberFormat tickLabelFormat; /** The resourceBundle for the localization. */ protected static ResourceBundle localizationResources = ResourceBundle.getBundle("org.jfree.chart.plot.LocalizationBundle"); /** * A (possibly empty) list of the {@link MeterInterval}s to be highlighted * on the dial. */ private List intervals; /** * Creates a new plot with a default range of {@code 0} to {@code 100} and * no value to display. */ public MeterPlot() { this(null); } /** * Creates a new plot that displays the value from the supplied dataset. * * @param dataset the dataset ({@code null} permitted). */ public MeterPlot(ValueDataset dataset) { super(); this.shape = DialShape.CIRCLE; this.meterAngle = DEFAULT_METER_ANGLE; this.range = new Range(0.0, 100.0); this.tickSize = 10.0; this.tickPaint = Color.WHITE; this.units = "Units"; this.needlePaint = MeterPlot.DEFAULT_NEEDLE_PAINT; this.tickLabelsVisible = true; this.tickLabelFont = MeterPlot.DEFAULT_LABEL_FONT; this.tickLabelPaint = Color.BLACK; this.tickLabelFormat = NumberFormat.getInstance(); this.valueFont = MeterPlot.DEFAULT_VALUE_FONT; this.valuePaint = MeterPlot.DEFAULT_VALUE_PAINT; this.dialBackgroundPaint = MeterPlot.DEFAULT_DIAL_BACKGROUND_PAINT; this.intervals = new ArrayList<>(); setDataset(dataset); } /** * Returns the dial shape. The default is {@link DialShape#CIRCLE}). * * @return The dial shape (never {@code null}). * * @see #setDialShape(DialShape) */ public DialShape getDialShape() { return this.shape; } /** * Sets the dial shape and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param shape the shape ({@code null} not permitted). * * @see #getDialShape() */ public void setDialShape(DialShape shape) { Args.nullNotPermitted(shape, "shape"); this.shape = shape; fireChangeEvent(); } /** * Returns the meter angle in degrees. This defines, in part, the shape * of the dial. The default is 270 degrees. * * @return The meter angle (in degrees). * * @see #setMeterAngle(int) */ public int getMeterAngle() { return this.meterAngle; } /** * Sets the angle (in degrees) for the whole range of the dial and sends * a {@link PlotChangeEvent} to all registered listeners. * * @param angle the angle (in degrees, in the range 1-360). * * @see #getMeterAngle() */ public void setMeterAngle(int angle) { if (angle < 1 || angle > 360) { throw new IllegalArgumentException("Invalid 'angle' (" + angle + ")"); } this.meterAngle = angle; fireChangeEvent(); } /** * Returns the overall range for the dial. * * @return The overall range (never {@code null}). * * @see #setRange(Range) */ public Range getRange() { return this.range; } /** * Sets the range for the dial and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param range the range ({@code null} not permitted and zero-length * ranges not permitted). * * @see #getRange() */ public void setRange(Range range) { Args.nullNotPermitted(range, "range"); if (!(range.getLength() > 0.0)) { throw new IllegalArgumentException( "Range length must be positive."); } this.range = range; fireChangeEvent(); } /** * Returns the tick size (the interval between ticks on the dial). * * @return The tick size. * * @see #setTickSize(double) */ public double getTickSize() { return this.tickSize; } /** * Sets the tick size and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param size the tick size (must be > 0). * * @see #getTickSize() */ public void setTickSize(double size) { if (size <= 0) { throw new IllegalArgumentException("Requires 'size' > 0."); } this.tickSize = size; fireChangeEvent(); } /** * Returns the paint used to draw the ticks around the dial. * * @return The paint used to draw the ticks around the dial (never * {@code null}). * * @see #setTickPaint(Paint) */ public Paint getTickPaint() { return this.tickPaint; } /** * Sets the paint used to draw the tick labels around the dial and sends * a {@link PlotChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getTickPaint() */ public void setTickPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.tickPaint = paint; fireChangeEvent(); } /** * Returns a string describing the units for the dial. * * @return The units (possibly {@code null}). * * @see #setUnits(String) */ public String getUnits() { return this.units; } /** * Sets the units for the dial and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param units the units ({@code null} permitted). * * @see #getUnits() */ public void setUnits(String units) { this.units = units; fireChangeEvent(); } /** * Returns the paint for the needle. * * @return The paint (never {@code null}). * * @see #setNeedlePaint(Paint) */ public Paint getNeedlePaint() { return this.needlePaint; } /** * Sets the paint used to display the needle and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getNeedlePaint() */ public void setNeedlePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.needlePaint = paint; fireChangeEvent(); } /** * Returns the flag that determines whether or not tick labels are visible. * * @return The flag. * * @see #setTickLabelsVisible(boolean) */ public boolean getTickLabelsVisible() { return this.tickLabelsVisible; } /** * Sets the flag that controls whether or not the tick labels are visible * and sends a {@link PlotChangeEvent} to all registered listeners. * * @param visible the flag. * * @see #getTickLabelsVisible() */ public void setTickLabelsVisible(boolean visible) { if (this.tickLabelsVisible != visible) { this.tickLabelsVisible = visible; fireChangeEvent(); } } /** * Returns the tick label font. * * @return The font (never {@code null}). * * @see #setTickLabelFont(Font) */ public Font getTickLabelFont() { return this.tickLabelFont; } /** * Sets the tick label font and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param font the font ({@code null} not permitted). * * @see #getTickLabelFont() */ public void setTickLabelFont(Font font) { Args.nullNotPermitted(font, "font"); if (!this.tickLabelFont.equals(font)) { this.tickLabelFont = font; fireChangeEvent(); } } /** * Returns the tick label paint. * * @return The paint (never {@code null}). * * @see #setTickLabelPaint(Paint) */ public Paint getTickLabelPaint() { return this.tickLabelPaint; } /** * Sets the tick label paint and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getTickLabelPaint() */ public void setTickLabelPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); if (!this.tickLabelPaint.equals(paint)) { this.tickLabelPaint = paint; fireChangeEvent(); } } /** * Returns the flag that controls whether or not the value is visible. * The default value is {@code true}. * * @return A flag. * * @see #setValueVisible * @since 1.5.4 */ public boolean isValueVisible() { return valueVisible; } /** * Sets the flag that controls whether or not the value is visible * and sends a change event to all registered listeners. * * @param valueVisible the new flag value. * * @see #isValueVisible() * @since 1.5.4 */ public void setValueVisible(boolean valueVisible) { this.valueVisible = valueVisible; fireChangeEvent(); } /** * Returns the tick label format. * * @return The tick label format (never {@code null}). * * @see #setTickLabelFormat(NumberFormat) */ public NumberFormat getTickLabelFormat() { return this.tickLabelFormat; } /** * Sets the format for the tick labels and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param format the format ({@code null} not permitted). * * @see #getTickLabelFormat() */ public void setTickLabelFormat(NumberFormat format) { Args.nullNotPermitted(format, "format"); this.tickLabelFormat = format; fireChangeEvent(); } /** * Returns the font for the value label. * * @return The font (never {@code null}). * * @see #setValueFont(Font) */ public Font getValueFont() { return this.valueFont; } /** * Sets the font used to display the value label and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param font the font ({@code null} not permitted). * * @see #getValueFont() */ public void setValueFont(Font font) { Args.nullNotPermitted(font, "font"); this.valueFont = font; fireChangeEvent(); } /** * Returns the paint for the value label. * * @return The paint (never {@code null}). * * @see #setValuePaint(Paint) */ public Paint getValuePaint() { return this.valuePaint; } /** * Sets the paint used to display the value label and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getValuePaint() */ public void setValuePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.valuePaint = paint; fireChangeEvent(); } /** * Returns the paint for the dial background. * * @return The paint (possibly {@code null}). * * @see #setDialBackgroundPaint(Paint) */ public Paint getDialBackgroundPaint() { return this.dialBackgroundPaint; } /** * Sets the paint used to fill the dial background. Set this to * {@code null} for no background. * * @param paint the paint ({@code null} permitted). * * @see #getDialBackgroundPaint() */ public void setDialBackgroundPaint(Paint paint) { this.dialBackgroundPaint = paint; fireChangeEvent(); } /** * Returns a flag that controls whether or not a rectangular border is * drawn around the plot area. * * @return A flag. * * @see #setDrawBorder(boolean) */ public boolean getDrawBorder() { return this.drawBorder; } /** * Sets the flag that controls whether or not a rectangular border is drawn * around the plot area and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param draw the flag. * * @see #getDrawBorder() */ public void setDrawBorder(boolean draw) { // TODO: fix output when this flag is set to true this.drawBorder = draw; fireChangeEvent(); } /** * Returns the dial outline paint. * * @return The paint. * * @see #setDialOutlinePaint(Paint) */ public Paint getDialOutlinePaint() { return this.dialOutlinePaint; } /** * Sets the dial outline paint and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param paint the paint. * * @see #getDialOutlinePaint() */ public void setDialOutlinePaint(Paint paint) { this.dialOutlinePaint = paint; fireChangeEvent(); } /** * Returns the dataset for the plot. * * @return The dataset (possibly {@code null}). * * @see #setDataset(ValueDataset) */ public ValueDataset getDataset() { return this.dataset; } /** * Sets the dataset for the plot, replacing the existing dataset if there * is one, and triggers a {@link PlotChangeEvent}. * * @param dataset the dataset ({@code null} permitted). * * @see #getDataset() */ public void setDataset(ValueDataset dataset) { // if there is an existing dataset, remove the plot from the list of // change listeners... ValueDataset existing = this.dataset; if (existing != null) { existing.removeChangeListener(this); } // set the new dataset, and register the chart as a change listener... this.dataset = dataset; if (dataset != null) { setDatasetGroup(dataset.getGroup()); dataset.addChangeListener(this); } // send a dataset change event to self... DatasetChangeEvent event = new DatasetChangeEvent(this, dataset); datasetChanged(event); } /** * Returns an unmodifiable list of the intervals for the plot. * * @return A list. * * @see #addInterval(MeterInterval) */ public List getIntervals() { return Collections.unmodifiableList(intervals); } /** * Adds an interval and sends a {@link PlotChangeEvent} to all registered * listeners. * * @param interval the interval ({@code null} not permitted). * * @see #getIntervals() * @see #clearIntervals() */ public void addInterval(MeterInterval interval) { Args.nullNotPermitted(interval, "interval"); this.intervals.add(interval); fireChangeEvent(); } /** * Clears the intervals for the plot and sends a {@link PlotChangeEvent} to * all registered listeners. * * @see #addInterval(MeterInterval) */ public void clearIntervals() { this.intervals.clear(); fireChangeEvent(); } /** * Returns an item for each interval. * * @return A collection of legend items. */ @Override public LegendItemCollection getLegendItems() { LegendItemCollection result = new LegendItemCollection(); for (MeterInterval mi : intervals) { Paint color = mi.getBackgroundPaint(); if (color == null) { color = mi.getOutlinePaint(); } LegendItem item = new LegendItem(mi.getLabel(), mi.getLabel(), null, null, new Rectangle2D.Double(-4.0, -4.0, 8.0, 8.0), color); item.setDataset(getDataset()); result.add(item); } return result; } /** * Draws the plot on a Java 2D graphics device (such as the screen or a * printer). * * @param g2 the graphics device. * @param area the area within which the plot should be drawn. * @param anchor the anchor point ({@code null} permitted). * @param parentState the state from the parent plot, if there is one. * @param info collects info about the drawing. */ @Override public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, PlotState parentState, PlotRenderingInfo info) { if (info != null) { info.setPlotArea(area); } // adjust for insets... RectangleInsets insets = getInsets(); insets.trim(area); area.setRect(area.getX() + 4, area.getY() + 4, area.getWidth() - 8, area.getHeight() - 8); // draw the background if (this.drawBorder) { drawBackground(g2, area); } // adjust the plot area by the interior spacing value double gapHorizontal = (2 * DEFAULT_BORDER_SIZE); double gapVertical = (2 * DEFAULT_BORDER_SIZE); double meterX = area.getX() + gapHorizontal / 2; double meterY = area.getY() + gapVertical / 2; double meterW = area.getWidth() - gapHorizontal; double meterH = area.getHeight() - gapVertical + ((this.meterAngle <= 180) && (this.shape != DialShape.CIRCLE) ? area.getHeight() / 1.25 : 0); double min = Math.min(meterW, meterH) / 2; meterX = (meterX + meterX + meterW) / 2 - min; meterY = (meterY + meterY + meterH) / 2 - min; meterW = 2 * min; meterH = 2 * min; Rectangle2D meterArea = new Rectangle2D.Double(meterX, meterY, meterW, meterH); Rectangle2D.Double originalArea = new Rectangle2D.Double( meterArea.getX() - 4, meterArea.getY() - 4, meterArea.getWidth() + 8, meterArea.getHeight() + 8); double meterMiddleX = meterArea.getCenterX(); double meterMiddleY = meterArea.getCenterY(); // plot the data (unless the dataset is null)... ValueDataset data = getDataset(); if (data != null) { double dataMin = this.range.getLowerBound(); double dataMax = this.range.getUpperBound(); Shape savedClip = g2.getClip(); g2.clip(originalArea); Composite originalComposite = g2.getComposite(); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, getForegroundAlpha())); if (this.dialBackgroundPaint != null) { fillArc(g2, originalArea, dataMin, dataMax, this.dialBackgroundPaint, true); } drawTicks(g2, meterArea, dataMin, dataMax); drawArcForInterval(g2, meterArea, new MeterInterval("", this.range, this.dialOutlinePaint, new BasicStroke(1.0f), null)); for (MeterInterval interval : this.intervals) { drawArcForInterval(g2, meterArea, interval); } Number n = data.getValue(); if (n != null) { double value = n.doubleValue(); drawValueLabel(g2, meterArea); if (this.range.contains(value)) { g2.setPaint(this.needlePaint); g2.setStroke(new BasicStroke(2.0f)); double radius = (meterArea.getWidth() / 2) + DEFAULT_BORDER_SIZE + 15; double valueAngle = valueToAngle(value); double valueP1 = meterMiddleX + (radius * Math.cos(Math.PI * (valueAngle / 180))); double valueP2 = meterMiddleY - (radius * Math.sin(Math.PI * (valueAngle / 180))); Polygon arrow = new Polygon(); if ((valueAngle > 135 && valueAngle < 225) || (valueAngle < 45 && valueAngle > -45)) { double valueP3 = (meterMiddleY - DEFAULT_CIRCLE_SIZE / 4); double valueP4 = (meterMiddleY + DEFAULT_CIRCLE_SIZE / 4); arrow.addPoint((int) meterMiddleX, (int) valueP3); arrow.addPoint((int) meterMiddleX, (int) valueP4); } else { arrow.addPoint((int) (meterMiddleX - DEFAULT_CIRCLE_SIZE / 4), (int) meterMiddleY); arrow.addPoint((int) (meterMiddleX + DEFAULT_CIRCLE_SIZE / 4), (int) meterMiddleY); } arrow.addPoint((int) valueP1, (int) valueP2); g2.fill(arrow); Ellipse2D circle = new Ellipse2D.Double(meterMiddleX - DEFAULT_CIRCLE_SIZE / 2, meterMiddleY - DEFAULT_CIRCLE_SIZE / 2, DEFAULT_CIRCLE_SIZE, DEFAULT_CIRCLE_SIZE); g2.fill(circle); } } g2.setClip(savedClip); g2.setComposite(originalComposite); } if (this.drawBorder) { drawOutline(g2, area); } } /** * Draws the arc to represent an interval. * * @param g2 the graphics device. * @param meterArea the drawing area. * @param interval the interval. */ protected void drawArcForInterval(Graphics2D g2, Rectangle2D meterArea, MeterInterval interval) { double minValue = interval.getRange().getLowerBound(); double maxValue = interval.getRange().getUpperBound(); Paint outlinePaint = interval.getOutlinePaint(); Stroke outlineStroke = interval.getOutlineStroke(); Paint backgroundPaint = interval.getBackgroundPaint(); if (backgroundPaint != null) { fillArc(g2, meterArea, minValue, maxValue, backgroundPaint, false); } if (outlinePaint != null) { if (outlineStroke != null) { drawArc(g2, meterArea, minValue, maxValue, outlinePaint, outlineStroke); } drawTick(g2, meterArea, minValue, true); drawTick(g2, meterArea, maxValue, true); } } /** * Draws an arc. * * @param g2 the graphics device. * @param area the plot area. * @param minValue the minimum value. * @param maxValue the maximum value. * @param paint the paint. * @param stroke the stroke. */ protected void drawArc(Graphics2D g2, Rectangle2D area, double minValue, double maxValue, Paint paint, Stroke stroke) { double startAngle = valueToAngle(maxValue); double endAngle = valueToAngle(minValue); double extent = endAngle - startAngle; double x = area.getX(); double y = area.getY(); double w = area.getWidth(); double h = area.getHeight(); g2.setPaint(paint); g2.setStroke(stroke); if (paint != null && stroke != null) { Arc2D.Double arc = new Arc2D.Double(x, y, w, h, startAngle, extent, Arc2D.OPEN); g2.setPaint(paint); g2.setStroke(stroke); g2.draw(arc); } } /** * Fills an arc on the dial between the given values. * * @param g2 the graphics device. * @param area the plot area. * @param minValue the minimum data value. * @param maxValue the maximum data value. * @param paint the background paint ({@code null} not permitted). * @param dial a flag that indicates whether the arc represents the whole * dial. */ protected void fillArc(Graphics2D g2, Rectangle2D area, double minValue, double maxValue, Paint paint, boolean dial) { Args.nullNotPermitted(paint, "paint"); double startAngle = valueToAngle(maxValue); double endAngle = valueToAngle(minValue); double extent = endAngle - startAngle; double x = area.getX(); double y = area.getY(); double w = area.getWidth(); double h = area.getHeight(); int joinType = Arc2D.OPEN; if (this.shape == DialShape.PIE) { joinType = Arc2D.PIE; } else if (this.shape == DialShape.CHORD) { if (dial && this.meterAngle > 180) { joinType = Arc2D.CHORD; } else { joinType = Arc2D.PIE; } } else if (this.shape == DialShape.CIRCLE) { joinType = Arc2D.PIE; if (dial) { extent = 360; } } else { throw new IllegalStateException("DialShape not recognised."); } g2.setPaint(paint); Arc2D.Double arc = new Arc2D.Double(x, y, w, h, startAngle, extent, joinType); g2.fill(arc); } /** * Translates a data value to an angle on the dial. * * @param value the value. * * @return The angle on the dial. */ public double valueToAngle(double value) { value = value - this.range.getLowerBound(); double baseAngle = 180 + ((this.meterAngle - 180) / 2.0); return baseAngle - ((value / this.range.getLength()) * this.meterAngle); } /** * Draws the ticks that subdivide the overall range. * * @param g2 the graphics device. * @param meterArea the meter area. * @param minValue the minimum value. * @param maxValue the maximum value. */ protected void drawTicks(Graphics2D g2, Rectangle2D meterArea, double minValue, double maxValue) { for (double v = minValue; v <= maxValue; v += this.tickSize) { drawTick(g2, meterArea, v); } } /** * Draws a tick. * * @param g2 the graphics device. * @param meterArea the meter area. * @param value the value. */ protected void drawTick(Graphics2D g2, Rectangle2D meterArea, double value) { drawTick(g2, meterArea, value, false); } /** * Draws a tick on the dial. * * @param g2 the graphics device. * @param meterArea the meter area. * @param value the tick value. * @param label a flag that controls whether or not a value label is drawn. */ protected void drawTick(Graphics2D g2, Rectangle2D meterArea, double value, boolean label) { double valueAngle = valueToAngle(value); double meterMiddleX = meterArea.getCenterX(); double meterMiddleY = meterArea.getCenterY(); g2.setPaint(this.tickPaint); g2.setStroke(new BasicStroke(2.0f)); double valueP2X; double valueP2Y; double radius = (meterArea.getWidth() / 2) + DEFAULT_BORDER_SIZE; double radius1 = radius - 15; double valueP1X = meterMiddleX + (radius * Math.cos(Math.PI * (valueAngle / 180))); double valueP1Y = meterMiddleY - (radius * Math.sin(Math.PI * (valueAngle / 180))); valueP2X = meterMiddleX + (radius1 * Math.cos(Math.PI * (valueAngle / 180))); valueP2Y = meterMiddleY - (radius1 * Math.sin(Math.PI * (valueAngle / 180))); Line2D.Double line = new Line2D.Double(valueP1X, valueP1Y, valueP2X, valueP2Y); g2.draw(line); if (this.tickLabelsVisible && label) { String tickLabel = this.tickLabelFormat.format(value); g2.setFont(this.tickLabelFont); g2.setPaint(this.tickLabelPaint); FontMetrics fm = g2.getFontMetrics(); Rectangle2D tickLabelBounds = TextUtils.getTextBounds(tickLabel, g2, fm); double x = valueP2X; double y = valueP2Y; if (valueAngle == 90 || valueAngle == 270) { x = x - tickLabelBounds.getWidth() / 2; } else if (valueAngle < 90 || valueAngle > 270) { x = x - tickLabelBounds.getWidth(); } if ((valueAngle > 135 && valueAngle < 225) || valueAngle > 315 || valueAngle < 45) { y = y - tickLabelBounds.getHeight() / 2; } else { y = y + tickLabelBounds.getHeight() / 2; } g2.drawString(tickLabel, (float) x, (float) y); } } /** * Draws the value label just below the center of the dial. * * @param g2 the graphics device. * @param area the plot area. */ protected void drawValueLabel(Graphics2D g2, Rectangle2D area) { if (valueVisible) { g2.setFont(this.valueFont); g2.setPaint(this.valuePaint); String valueStr = "No value"; if (this.dataset != null) { Number n = this.dataset.getValue(); if (n != null) { valueStr = this.tickLabelFormat.format(n.doubleValue()) + " " + this.units; } } float x = (float) area.getCenterX(); float y = (float) area.getCenterY() + DEFAULT_CIRCLE_SIZE; TextUtils.drawAlignedString(valueStr, g2, x, y, TextAnchor.TOP_CENTER); } } /** * Returns a short string describing the type of plot. * * @return A string describing the type of plot. */ @Override public String getPlotType() { return localizationResources.getString("Meter_Plot"); } /** * A zoom method that does nothing. Plots are required to support the * zoom operation. In the case of a meter plot, it doesn't make sense to * zoom in or out, so the method is empty. * * @param percent The zoom percentage. */ @Override public void zoom(double percent) { // intentionally blank } /** * Tests the plot for equality with an arbitrary object. Note that the * dataset is ignored for the purposes of testing equality. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof MeterPlot)) { return false; } if (!super.equals(obj)) { return false; } MeterPlot that = (MeterPlot) obj; if (!Objects.equals(this.units, that.units)) { return false; } if (!Objects.equals(this.range, that.range)) { return false; } if (!Objects.equals(this.intervals, that.intervals)) { return false; } if (!PaintUtils.equal(this.dialOutlinePaint, that.dialOutlinePaint)) { return false; } if (this.shape != that.shape) { return false; } if (!PaintUtils.equal(this.dialBackgroundPaint, that.dialBackgroundPaint)) { return false; } if (!PaintUtils.equal(this.needlePaint, that.needlePaint)) { return false; } if (this.valueVisible != that.valueVisible) { return false; } if (!Objects.equals(this.valueFont, that.valueFont)) { return false; } if (!PaintUtils.equal(this.valuePaint, that.valuePaint)) { return false; } if (!PaintUtils.equal(this.tickPaint, that.tickPaint)) { return false; } if (this.tickSize != that.tickSize) { return false; } if (this.tickLabelsVisible != that.tickLabelsVisible) { return false; } if (!Objects.equals(this.tickLabelFont, that.tickLabelFont)) { return false; } if (!PaintUtils.equal(this.tickLabelPaint, that.tickLabelPaint)) { return false; } if (!Objects.equals(this.tickLabelFormat, that.tickLabelFormat)) { return false; } if (this.drawBorder != that.drawBorder) { return false; } if (this.meterAngle != that.meterAngle) { return false; } return true; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.dialBackgroundPaint, stream); SerialUtils.writePaint(this.dialOutlinePaint, stream); SerialUtils.writePaint(this.needlePaint, stream); SerialUtils.writePaint(this.valuePaint, stream); SerialUtils.writePaint(this.tickPaint, stream); SerialUtils.writePaint(this.tickLabelPaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.dialBackgroundPaint = SerialUtils.readPaint(stream); this.dialOutlinePaint = SerialUtils.readPaint(stream); this.needlePaint = SerialUtils.readPaint(stream); this.valuePaint = SerialUtils.readPaint(stream); this.tickPaint = SerialUtils.readPaint(stream); this.tickLabelPaint = SerialUtils.readPaint(stream); if (this.dataset != null) { this.dataset.addChangeListener(this); } } /** * Returns an independent copy (clone) of the plot. The dataset is NOT * cloned - both the original and the clone will have a reference to the * same dataset. * * @return A clone. * * @throws CloneNotSupportedException if some component of the plot cannot * be cloned. */ @Override public Object clone() throws CloneNotSupportedException { MeterPlot clone = (MeterPlot) super.clone(); clone.tickLabelFormat = (NumberFormat) this.tickLabelFormat.clone(); // the following relies on the fact that the intervals are immutable clone.intervals = new ArrayList<>(this.intervals); if (clone.dataset != null) { clone.dataset.addChangeListener(clone); } return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/MultiplePiePlot.java000066400000000000000000000523641463604235500300010ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * MultiplePiePlot.java * -------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Brian Cabana (patch 1943021); * */ package org.jfree.chart.plot; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Rectangle; import java.awt.Shape; import java.awt.geom.Ellipse2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; import org.jfree.chart.ChartRenderingInfo; import org.jfree.chart.JFreeChart; import org.jfree.chart.LegendItem; import org.jfree.chart.LegendItemCollection; import org.jfree.chart.event.PlotChangeEvent; import org.jfree.chart.title.TextTitle; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.SerialUtils; import org.jfree.chart.util.ShapeUtils; import org.jfree.chart.util.TableOrder; import org.jfree.data.category.CategoryDataset; import org.jfree.data.category.CategoryToPieDataset; import org.jfree.data.general.DatasetChangeEvent; import org.jfree.data.general.DatasetUtils; import org.jfree.data.general.PieDataset; /** * A plot that displays multiple pie plots using data from a * {@link CategoryDataset}. */ public class MultiplePiePlot extends Plot implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -355377800470807389L; /** The chart object that draws the individual pie charts. */ private JFreeChart pieChart; /** The dataset. */ private CategoryDataset dataset; /** The data extract order (by row or by column). */ private TableOrder dataExtractOrder; /** The pie section limit percentage. */ private double limit = 0.0; /** * The key for the aggregated items. */ private Comparable aggregatedItemsKey; /** * The paint for the aggregated items. */ private transient Paint aggregatedItemsPaint; /** * The colors to use for each section. */ private transient Map sectionPaints; /** * The legend item shape (never null). */ private transient Shape legendItemShape; /** * Creates a new plot with no data. */ public MultiplePiePlot() { this(null); } /** * Creates a new plot. * * @param dataset the dataset ({@code null} permitted). */ public MultiplePiePlot(CategoryDataset dataset) { super(); setDataset(dataset); PiePlot piePlot = new PiePlot(null); piePlot.setIgnoreNullValues(true); this.pieChart = new JFreeChart(piePlot); this.pieChart.removeLegend(); this.dataExtractOrder = TableOrder.BY_COLUMN; this.pieChart.setBackgroundPaint(null); TextTitle seriesTitle = new TextTitle("Series Title", new Font("SansSerif", Font.BOLD, 12)); seriesTitle.setPosition(RectangleEdge.BOTTOM); this.pieChart.setTitle(seriesTitle); this.aggregatedItemsKey = "Other"; this.aggregatedItemsPaint = Color.LIGHT_GRAY; this.sectionPaints = new HashMap(); this.legendItemShape = new Ellipse2D.Double(-4.0, -4.0, 8.0, 8.0); } /** * Returns the dataset used by the plot. * * @return The dataset (possibly {@code null}). */ public CategoryDataset getDataset() { return this.dataset; } /** * Sets the dataset used by the plot and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param dataset the dataset ({@code null} permitted). */ public void setDataset(CategoryDataset dataset) { // if there is an existing dataset, remove the plot from the list of // change listeners... if (this.dataset != null) { this.dataset.removeChangeListener(this); } // set the new dataset, and register the chart as a change listener... this.dataset = dataset; if (dataset != null) { setDatasetGroup(dataset.getGroup()); dataset.addChangeListener(this); } // send a dataset change event to self to trigger plot change event datasetChanged(new DatasetChangeEvent(this, dataset)); } /** * Returns the pie chart that is used to draw the individual pie plots. * Note that there are some attributes on this chart instance that will * be ignored at rendering time (for example, legend item settings). * * @return The pie chart (never {@code null}). * * @see #setPieChart(JFreeChart) */ public JFreeChart getPieChart() { return this.pieChart; } /** * Sets the chart that is used to draw the individual pie plots. The * chart's plot must be an instance of {@link PiePlot}. * * @param pieChart the pie chart ({@code null} not permitted). * * @see #getPieChart() */ public void setPieChart(JFreeChart pieChart) { Args.nullNotPermitted(pieChart, "pieChart"); if (!(pieChart.getPlot() instanceof PiePlot)) { throw new IllegalArgumentException("The 'pieChart' argument must " + "be a chart based on a PiePlot."); } this.pieChart = pieChart; fireChangeEvent(); } /** * Returns the data extract order (by row or by column). * * @return The data extract order (never {@code null}). */ public TableOrder getDataExtractOrder() { return this.dataExtractOrder; } /** * Sets the data extract order (by row or by column) and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param order the order ({@code null} not permitted). */ public void setDataExtractOrder(TableOrder order) { Args.nullNotPermitted(order, "order"); this.dataExtractOrder = order; fireChangeEvent(); } /** * Returns the limit (as a percentage) below which small pie sections are * aggregated. * * @return The limit percentage. */ public double getLimit() { return this.limit; } /** * Sets the limit below which pie sections are aggregated. * Set this to 0.0 if you don't want any aggregation to occur. * * @param limit the limit percent. */ public void setLimit(double limit) { this.limit = limit; fireChangeEvent(); } /** * Returns the key for aggregated items in the pie plots, if there are any. * The default value is "Other". * * @return The aggregated items key. */ public Comparable getAggregatedItemsKey() { return this.aggregatedItemsKey; } /** * Sets the key for aggregated items in the pie plots. You must ensure * that this doesn't clash with any keys in the dataset. * * @param key the key ({@code null} not permitted). */ public void setAggregatedItemsKey(Comparable key) { Args.nullNotPermitted(key, "key"); this.aggregatedItemsKey = key; fireChangeEvent(); } /** * Returns the paint used to draw the pie section representing the * aggregated items. The default value is {code Color.LIGHT_GRAY}. * * @return The paint. */ public Paint getAggregatedItemsPaint() { return this.aggregatedItemsPaint; } /** * Sets the paint used to draw the pie section representing the aggregated * items and sends a {@link PlotChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). */ public void setAggregatedItemsPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.aggregatedItemsPaint = paint; fireChangeEvent(); } /** * Returns a short string describing the type of plot. * * @return The plot type. */ @Override public String getPlotType() { return "Multiple Pie Plot"; // TODO: need to fetch this from localised resources } /** * Returns the shape used for legend items. * * @return The shape (never {@code null}). * * @see #setLegendItemShape(Shape) */ public Shape getLegendItemShape() { return this.legendItemShape; } /** * Sets the shape used for legend items and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param shape the shape ({@code null} not permitted). * * @see #getLegendItemShape() */ public void setLegendItemShape(Shape shape) { Args.nullNotPermitted(shape, "shape"); this.legendItemShape = shape; fireChangeEvent(); } /** * Draws the plot on a Java 2D graphics device (such as the screen or a * printer). * * @param g2 the graphics device. * @param area the area within which the plot should be drawn. * @param anchor the anchor point ({@code null} permitted). * @param parentState the state from the parent plot, if there is one. * @param info collects info about the drawing. */ @Override public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, PlotState parentState, PlotRenderingInfo info) { // adjust the drawing area for the plot insets (if any)... RectangleInsets insets = getInsets(); insets.trim(area); drawBackground(g2, area); drawOutline(g2, area); // check that there is some data to display... if (DatasetUtils.isEmptyOrNull(this.dataset)) { drawNoDataMessage(g2, area); return; } int pieCount; if (this.dataExtractOrder == TableOrder.BY_ROW) { pieCount = this.dataset.getRowCount(); } else { pieCount = this.dataset.getColumnCount(); } // the columns variable is always >= rows int displayCols = (int) Math.ceil(Math.sqrt(pieCount)); int displayRows = (int) Math.ceil((double) pieCount / (double) displayCols); // swap rows and columns to match plotArea shape if (displayCols > displayRows && area.getWidth() < area.getHeight()) { int temp = displayCols; displayCols = displayRows; displayRows = temp; } prefetchSectionPaints(); int x = (int) area.getX(); int y = (int) area.getY(); int width = ((int) area.getWidth()) / displayCols; int height = ((int) area.getHeight()) / displayRows; int row = 0; int column = 0; int diff = (displayRows * displayCols) - pieCount; int xoffset = 0; Rectangle rect = new Rectangle(); for (int pieIndex = 0; pieIndex < pieCount; pieIndex++) { rect.setBounds(x + xoffset + (width * column), y + (height * row), width, height); String title; if (this.dataExtractOrder == TableOrder.BY_ROW) { title = this.dataset.getRowKey(pieIndex).toString(); } else { title = this.dataset.getColumnKey(pieIndex).toString(); } this.pieChart.setTitle(title); PieDataset piedataset; PieDataset dd = new CategoryToPieDataset(this.dataset, this.dataExtractOrder, pieIndex); if (this.limit > 0.0) { piedataset = DatasetUtils.createConsolidatedPieDataset( dd, this.aggregatedItemsKey, this.limit); } else { piedataset = dd; } PiePlot piePlot = (PiePlot) this.pieChart.getPlot(); piePlot.setDataset(piedataset); piePlot.setPieIndex(pieIndex); // update the section colors to match the global colors... for (int i = 0; i < piedataset.getItemCount(); i++) { Comparable key = piedataset.getKey(i); Paint p; if (key.equals(this.aggregatedItemsKey)) { p = this.aggregatedItemsPaint; } else { p = (Paint) this.sectionPaints.get(key); } piePlot.setSectionPaint(key, p); } ChartRenderingInfo subinfo = null; if (info != null) { subinfo = new ChartRenderingInfo(); } this.pieChart.draw(g2, rect, subinfo); if (info != null) { assert subinfo != null; info.getOwner().getEntityCollection().addAll( subinfo.getEntityCollection()); info.addSubplotInfo(subinfo.getPlotInfo()); } ++column; if (column == displayCols) { column = 0; ++row; if (row == displayRows - 1 && diff != 0) { xoffset = (diff * width) / 2; } } } } /** * For each key in the dataset, check the {@code sectionPaints} * cache to see if a paint is associated with that key and, if not, * fetch one from the drawing supplier. These colors are cached so that * the legend and all the subplots use consistent colors. */ private void prefetchSectionPaints() { // pre-fetch the colors for each key...this is because the subplots // may not display every key, but we need the coloring to be // consistent... PiePlot piePlot = (PiePlot) getPieChart().getPlot(); if (this.dataExtractOrder == TableOrder.BY_ROW) { // column keys provide potential keys for individual pies for (int c = 0; c < this.dataset.getColumnCount(); c++) { Comparable key = this.dataset.getColumnKey(c); Paint p = piePlot.getSectionPaint(key); if (p == null) { p = (Paint) this.sectionPaints.get(key); if (p == null) { p = getDrawingSupplier().getNextPaint(); } } this.sectionPaints.put(key, p); } } else { // row keys provide potential keys for individual pies for (int r = 0; r < this.dataset.getRowCount(); r++) { Comparable key = this.dataset.getRowKey(r); Paint p = piePlot.getSectionPaint(key); if (p == null) { p = (Paint) this.sectionPaints.get(key); if (p == null) { p = getDrawingSupplier().getNextPaint(); } } this.sectionPaints.put(key, p); } } } /** * Returns a collection of legend items for the pie chart. * * @return The legend items. */ @Override public LegendItemCollection getLegendItems() { LegendItemCollection result = new LegendItemCollection(); if (this.dataset == null) { return result; } List keys = null; prefetchSectionPaints(); if (this.dataExtractOrder == TableOrder.BY_ROW) { keys = this.dataset.getColumnKeys(); } else if (this.dataExtractOrder == TableOrder.BY_COLUMN) { keys = this.dataset.getRowKeys(); } if (keys == null) { return result; } int section = 0; Iterator iterator = keys.iterator(); while (iterator.hasNext()) { Comparable key = (Comparable) iterator.next(); String label = key.toString(); // TODO: use a generator here String description = label; Paint paint = (Paint) this.sectionPaints.get(key); LegendItem item = new LegendItem(label, description, null, null, getLegendItemShape(), paint, Plot.DEFAULT_OUTLINE_STROKE, paint); item.setSeriesKey(key); item.setSeriesIndex(section); item.setDataset(getDataset()); result.add(item); section++; } if (this.limit > 0.0) { LegendItem a = new LegendItem(this.aggregatedItemsKey.toString(), this.aggregatedItemsKey.toString(), null, null, getLegendItemShape(), this.aggregatedItemsPaint, Plot.DEFAULT_OUTLINE_STROKE, this.aggregatedItemsPaint); result.add(a); } return result; } /** * Tests this plot for equality with an arbitrary object. Note that the * plot's dataset is not considered in the equality test. * * @param obj the object ({@code null} permitted). * * @return {@code true} if this plot is equal to {@code obj}, and * {@code false} otherwise. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof MultiplePiePlot)) { return false; } MultiplePiePlot that = (MultiplePiePlot) obj; if (this.dataExtractOrder != that.dataExtractOrder) { return false; } if (this.limit != that.limit) { return false; } if (!this.aggregatedItemsKey.equals(that.aggregatedItemsKey)) { return false; } if (!PaintUtils.equal(this.aggregatedItemsPaint, that.aggregatedItemsPaint)) { return false; } if (!Objects.equals(this.pieChart, that.pieChart)) { return false; } if (!ShapeUtils.equal(this.legendItemShape, that.legendItemShape)) { return false; } if (!super.equals(obj)) { return false; } return true; } /** * Returns a clone of the plot. * * @return A clone. * * @throws CloneNotSupportedException if some component of the plot does * not support cloning. */ @Override public Object clone() throws CloneNotSupportedException { MultiplePiePlot clone = (MultiplePiePlot) super.clone(); clone.pieChart = (JFreeChart) this.pieChart.clone(); clone.sectionPaints = new HashMap(this.sectionPaints); clone.legendItemShape = ShapeUtils.clone(this.legendItemShape); return clone; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.aggregatedItemsPaint, stream); SerialUtils.writeShape(this.legendItemShape, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.aggregatedItemsPaint = SerialUtils.readPaint(stream); this.legendItemShape = SerialUtils.readShape(stream); this.sectionPaints = new HashMap(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/Pannable.java000066400000000000000000000056541463604235500264310ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------- * Pannable.java * ------------- * * (C) Copyright 2009-present, by David Gilbert and Contributors. * * Original Author: Ulrich Voigt - patch 2686040; * Contributor(s): David Gilbert; * */ package org.jfree.chart.plot; import java.awt.geom.Point2D; import org.jfree.chart.ChartPanel; /** * An interface that the {@link ChartPanel} class uses to communicate with * plots that support panning. */ public interface Pannable { /** * Returns the orientation of the plot. * * @return The orientation (never {@code null}). */ PlotOrientation getOrientation(); /** * Evaluates if the domain axis can be panned. * * @return {@code true} if the domain axis is pannable. */ boolean isDomainPannable(); /** * Evaluates if the range axis can be panned. * * @return {@code true} if the range axis is pannable. */ boolean isRangePannable(); /** * Pans the domain axes by the specified percentage. * * @param percent the distance to pan (as a percentage of the axis length). * @param info the plot info * @param source the source point where the pan action started. */ void panDomainAxes(double percent, PlotRenderingInfo info, Point2D source); /** * Pans the range axes by the specified percentage. * * @param percent the distance to pan (as a percentage of the axis length). * @param info the plot info * @param source the source point where the pan action started. */ void panRangeAxes(double percent, PlotRenderingInfo info, Point2D source); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/PieLabelDistributor.java000066400000000000000000000166001463604235500306120ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * PieLabelDistributor.java * ------------------------ * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import java.util.Collections; /** * This class distributes the section labels for one side of a pie chart so * that they do not overlap. */ public class PieLabelDistributor extends AbstractPieLabelDistributor { /** The minimum gap. */ private double minGap = 4.0; /** * Creates a new distributor. * * @param labelCount the number of labels (ignored). */ public PieLabelDistributor(int labelCount) { super(); } /** * Distributes the labels. * * @param minY the minimum y-coordinate in Java2D-space. * @param height the available height (in Java2D units). */ @Override public void distributeLabels(double minY, double height) { sort(); // sorts the label records into ascending order by baseY // if (isOverlap()) { // adjustInwards(); // } // if still overlapping, do something else... if (isOverlap()) { adjustDownwards(minY, height); } if (isOverlap()) { adjustUpwards(minY, height); } if (isOverlap()) { spreadEvenly(minY, height); } } /** * Returns {@code true} if there are overlapping labels in the list, * and {@code false} otherwise. * * @return A boolean. */ private boolean isOverlap() { double y = 0.0; for (int i = 0; i < this.labels.size(); i++) { PieLabelRecord plr = getPieLabelRecord(i); if (y > plr.getLowerY()) { return true; } y = plr.getUpperY(); } return false; } /** * Adjusts the y-coordinate for the labels in towards the center in an * attempt to fix overlapping. */ protected void adjustInwards() { int lower = 0; int upper = this.labels.size() - 1; while (upper > lower) { if (lower < upper - 1) { PieLabelRecord r0 = getPieLabelRecord(lower); PieLabelRecord r1 = getPieLabelRecord(lower + 1); if (r1.getLowerY() < r0.getUpperY()) { double adjust = r0.getUpperY() - r1.getLowerY() + this.minGap; r1.setAllocatedY(r1.getAllocatedY() + adjust); } } PieLabelRecord r2 = getPieLabelRecord(upper - 1); PieLabelRecord r3 = getPieLabelRecord(upper); if (r2.getUpperY() > r3.getLowerY()) { double adjust = (r2.getUpperY() - r3.getLowerY()) + this.minGap; r3.setAllocatedY(r3.getAllocatedY() + adjust); } lower++; upper--; } } /** * Any labels that are overlapping are moved down in an attempt to * eliminate the overlaps. * * @param minY the minimum y value (in Java2D coordinate space). * @param height the height available for all labels. */ protected void adjustDownwards(double minY, double height) { for (int i = 0; i < this.labels.size() - 1; i++) { PieLabelRecord record0 = getPieLabelRecord(i); PieLabelRecord record1 = getPieLabelRecord(i + 1); if (record1.getLowerY() < record0.getUpperY()) { record1.setAllocatedY(Math.min(minY + height - record1.getLabelHeight() / 2.0, record0.getUpperY() + this.minGap + record1.getLabelHeight() / 2.0)); } } } /** * Any labels that are overlapping are moved up in an attempt to eliminate * the overlaps. * * @param minY the minimum y value (in Java2D coordinate space). * @param height the height available for all labels. */ protected void adjustUpwards(double minY, double height) { for (int i = this.labels.size() - 1; i > 0; i--) { PieLabelRecord record0 = getPieLabelRecord(i); PieLabelRecord record1 = getPieLabelRecord(i - 1); if (record1.getUpperY() > record0.getLowerY()) { record1.setAllocatedY(Math.max(minY + record1.getLabelHeight() / 2.0, record0.getLowerY() - this.minGap - record1.getLabelHeight() / 2.0)); } } } /** * Labels are spaced evenly in the available space in an attempt to * eliminate the overlaps. * * @param minY the minimum y value (in Java2D coordinate space). * @param height the height available for all labels. */ protected void spreadEvenly(double minY, double height) { double y = minY; double sumOfLabelHeights = 0.0; for (int i = 0; i < this.labels.size(); i++) { sumOfLabelHeights += getPieLabelRecord(i).getLabelHeight(); } double gap = height - sumOfLabelHeights; if (this.labels.size() > 1) { gap = gap / (this.labels.size() - 1); } for (int i = 0; i < this.labels.size(); i++) { PieLabelRecord record = getPieLabelRecord(i); y = y + record.getLabelHeight() / 2.0; record.setAllocatedY(y); y = y + record.getLabelHeight() / 2.0 + gap; } } /** * Sorts the label records into ascending order by y-value. */ public void sort() { Collections.sort(this.labels); } /** * Returns a string containing a description of the object for * debugging purposes. * * @return A string. */ @Override public String toString() { StringBuilder result = new StringBuilder(); for (int i = 0; i < this.labels.size(); i++) { result.append(getPieLabelRecord(i).toString()).append("\n"); } return result.toString(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/PieLabelLinkStyle.java000066400000000000000000000077751463604235500302330ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * PieLabelLinkStyle.java * ---------------------- * (C) Copyright 2008-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import java.io.ObjectStreamException; import java.io.Serializable; /** * Used to indicate the style for the lines linking pie sections to their * corresponding labels. */ public final class PieLabelLinkStyle implements Serializable { /** STANDARD. */ public static final PieLabelLinkStyle STANDARD = new PieLabelLinkStyle("PieLabelLinkStyle.STANDARD"); /** QUAD_CURVE. */ public static final PieLabelLinkStyle QUAD_CURVE = new PieLabelLinkStyle("PieLabelLinkStyle.QUAD_CURVE"); /** CUBIC_CURVE. */ public static final PieLabelLinkStyle CUBIC_CURVE = new PieLabelLinkStyle("PieLabelLinkStyle.CUBIC_CURVE"); /** The name. */ private String name; /** * Private constructor. * * @param name the name. */ private PieLabelLinkStyle(String name) { this.name = name; } /** * Returns a string representing the object. * * @return The string. */ @Override public String toString() { return this.name; } /** * Returns {@code true} if this object is equal to the specified * object, and {@code false} otherwise. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof PieLabelLinkStyle)) { return false; } PieLabelLinkStyle style = (PieLabelLinkStyle) obj; if (!this.name.equals(style.toString())) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { return this.name.hashCode(); } /** * Ensures that serialization returns the unique instances. * * @return The object. * * @throws ObjectStreamException if there is a problem. */ private Object readResolve() throws ObjectStreamException { Object result = null; if (this.equals(PieLabelLinkStyle.STANDARD)) { result = PieLabelLinkStyle.STANDARD; } else if (this.equals(PieLabelLinkStyle.QUAD_CURVE)) { result = PieLabelLinkStyle.QUAD_CURVE; } else if (this.equals(PieLabelLinkStyle.CUBIC_CURVE)) { result = PieLabelLinkStyle.CUBIC_CURVE; } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/PieLabelRecord.java000066400000000000000000000165421463604235500275230ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * PieLabelRecord.java * ------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import java.io.Serializable; import org.jfree.chart.text.TextBox; /** * A structure that retains information about the label for a section in a pie * chart. */ public class PieLabelRecord implements Comparable, Serializable { /** The section key. */ private Comparable key; /** The angle of the centre of the section (in radians). */ private double angle; /** The base y-coordinate. */ private double baseY; /** The allocated y-coordinate. */ private double allocatedY; /** The label. */ private TextBox label; /** The label height. */ private double labelHeight; /** The gap. */ private double gap; /** The link percent. */ private double linkPercent; /** * Creates a new record. * * @param key the section key. * @param angle the angle to the middle of the section (in radians). * @param baseY the base y-coordinate. * @param label the section label. * @param labelHeight the label height (in Java2D units). * @param gap the offset to the left. * @param linkPercent the link percent. */ public PieLabelRecord(Comparable key, double angle, double baseY, TextBox label, double labelHeight, double gap, double linkPercent) { this.key = key; this.angle = angle; this.baseY = baseY; this.allocatedY = baseY; this.label = label; this.labelHeight = labelHeight; this.gap = gap; this.linkPercent = linkPercent; } /** * Returns the base y-coordinate. This is where the label will appear if * there is no overlapping of labels. * * @return The base y-coordinate. */ public double getBaseY() { return this.baseY; } /** * Sets the base y-coordinate. * * @param base the base y-coordinate. */ public void setBaseY(double base) { this.baseY = base; } /** * Returns the lower bound of the label. * * @return The lower bound. */ public double getLowerY() { return this.allocatedY - this.labelHeight / 2.0; } /** * Returns the upper bound of the label. * * @return The upper bound. */ public double getUpperY() { return this.allocatedY + this.labelHeight / 2.0; } /** * Returns the angle of the middle of the section, in radians. * * @return The angle, in radians. */ public double getAngle() { return this.angle; } /** * Returns the key for the section that the label applies to. * * @return The key. */ public Comparable getKey() { return this.key; } /** * Returns the label. * * @return The label. */ public TextBox getLabel() { return this.label; } /** * Returns the label height (you could derive this from the label itself, * but we cache the value so it can be retrieved quickly). * * @return The label height (in Java2D units). */ public double getLabelHeight() { return this.labelHeight; } /** * Returns the allocated y-coordinate. * * @return The allocated y-coordinate. */ public double getAllocatedY() { return this.allocatedY; } /** * Sets the allocated y-coordinate. * * @param y the y-coordinate. */ public void setAllocatedY(double y) { this.allocatedY = y; } /** * Returns the gap. * * @return The gap. */ public double getGap() { return this.gap; } /** * Returns the link percent. * * @return The link percent. */ public double getLinkPercent() { return this.linkPercent; } /** * Compares this object to an arbitrary object. * * @param obj the object to compare against. * * @return An integer that specifies the relative order of the two objects. */ @Override public int compareTo(Object obj) { int result = 0; if (obj instanceof PieLabelRecord) { PieLabelRecord plr = (PieLabelRecord) obj; if (this.baseY < plr.baseY) { result = -1; } else if (this.baseY > plr.baseY) { result = 1; } } return result; } /** * Tests this record for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof PieLabelRecord)) { return false; } PieLabelRecord that = (PieLabelRecord) obj; if (!this.key.equals(that.key)) { return false; } if (this.angle != that.angle) { return false; } if (this.gap != that.gap) { return false; } if (this.allocatedY != that.allocatedY) { return false; } if (this.baseY != that.baseY) { return false; } if (this.labelHeight != that.labelHeight) { return false; } if (this.linkPercent != that.linkPercent) { return false; } if (!this.label.equals(that.label)) { return false; } return true; } /** * Returns a string describing the object. This is used for debugging only. * * @return A string. */ @Override public String toString() { return this.baseY + ", " + this.key.toString(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/PiePlot.java000066400000000000000000003457131463604235500262700ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------ * PiePlot.java * ------------ * (C) Copyright 2000-present, by Andrzej Porebski and Contributors. * * Original Author: Andrzej Porebski; * Contributor(s): David Gilbert; * Martin Cordova (percentages in labels); * Richard Atkinson (URL support for image maps); * Christian W. Zuckschwerdt; * Arnaud Lelievre; * Martin Hilpert (patch 1891849); * Andreas Schroeder (very minor); * Christoph Beck (bug 2121818); * Tracy Hiltbrand (Added generics for bug fix); * */ package org.jfree.chart.plot; import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Composite; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.RadialGradientPaint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Arc2D; import java.awt.geom.CubicCurve2D; import java.awt.geom.Ellipse2D; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.QuadCurve2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.ResourceBundle; import java.util.TreeMap; import org.jfree.chart.JFreeChart; import org.jfree.chart.LegendItem; import org.jfree.chart.LegendItemCollection; import org.jfree.chart.PaintMap; import org.jfree.chart.StrokeMap; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.entity.PieSectionEntity; import org.jfree.chart.event.PlotChangeEvent; import org.jfree.chart.labels.PieSectionLabelGenerator; import org.jfree.chart.labels.PieToolTipGenerator; import org.jfree.chart.labels.StandardPieSectionLabelGenerator; import org.jfree.chart.text.G2TextMeasurer; import org.jfree.chart.text.TextBlock; import org.jfree.chart.text.TextBox; import org.jfree.chart.text.TextUtils; import org.jfree.chart.ui.RectangleAnchor; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.ui.TextAnchor; import org.jfree.chart.urls.PieURLGenerator; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.ResourceBundleWrapper; import org.jfree.chart.util.Rotation; import org.jfree.chart.util.SerialUtils; import org.jfree.chart.util.ShadowGenerator; import org.jfree.chart.util.ShapeUtils; import org.jfree.chart.util.UnitType; import org.jfree.data.DefaultKeyedValues; import org.jfree.data.KeyedValues; import org.jfree.data.general.DatasetChangeEvent; import org.jfree.data.general.DatasetUtils; import org.jfree.data.general.PieDataset; /** * A plot that displays data in the form of a pie chart, using data from any * class that implements the {@link PieDataset} interface. * The example shown here is generated by the {@code PieChartDemo2.java} * program included in the JFreeChart Demo Collection: *

* PieChartDemo2.svg *

* Special notes: *

    *
  1. the default starting point is 12 o'clock and the pie sections proceed * in a clockwise direction, but these settings can be changed;
  2. *
  3. negative values in the dataset are ignored;
  4. *
  5. there are utility methods for creating a {@link PieDataset} from a * {@link org.jfree.data.category.CategoryDataset};
  6. *
* * @param Key type for PieDataset * * @see Plot * @see PieDataset */ public class PiePlot> extends Plot implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -795612466005590431L; /** The default interior gap. */ public static final double DEFAULT_INTERIOR_GAP = 0.08; /** The maximum interior gap (currently 40%). */ public static final double MAX_INTERIOR_GAP = 0.40; /** The default starting angle for the pie chart. */ public static final double DEFAULT_START_ANGLE = 90.0; /** The default section label font. */ public static final Font DEFAULT_LABEL_FONT = new Font("SansSerif", Font.PLAIN, 10); /** The default section label paint. */ public static final Paint DEFAULT_LABEL_PAINT = Color.BLACK; /** The default section label background paint. */ public static final Paint DEFAULT_LABEL_BACKGROUND_PAINT = new Color(255, 255, 192); /** The default section label outline paint. */ public static final Paint DEFAULT_LABEL_OUTLINE_PAINT = Color.BLACK; /** The default section label outline stroke. */ public static final Stroke DEFAULT_LABEL_OUTLINE_STROKE = new BasicStroke( 0.5f); /** The default section label shadow paint. */ public static final Paint DEFAULT_LABEL_SHADOW_PAINT = new Color(151, 151, 151, 128); /** The default minimum arc angle to draw. */ public static final double DEFAULT_MINIMUM_ARC_ANGLE_TO_DRAW = 0.00001; /** The dataset for the pie chart. */ private PieDataset dataset; /** The pie index (used by the {@link MultiplePiePlot} class). */ private int pieIndex; /** * The amount of space left around the outside of the pie plot, expressed * as a percentage of the plot area width and height. */ private double interiorGap; /** Flag determining whether to draw an ellipse or a perfect circle. */ private boolean circular; /** The starting angle. */ private double startAngle; /** The direction for the pie segments. */ private Rotation direction; /** The section paint map. */ private PaintMap sectionPaintMap; /** The default section paint (fallback). */ private transient Paint defaultSectionPaint; /** * A flag that controls whether or not the section paint is auto-populated * from the drawing supplier. */ private boolean autoPopulateSectionPaint; /** * A flag that controls whether or not an outline is drawn for each * section in the plot. */ private boolean sectionOutlinesVisible; /** The section outline paint map. */ private PaintMap sectionOutlinePaintMap; /** The default section outline paint (fallback). */ private transient Paint defaultSectionOutlinePaint; /** * A flag that controls whether or not the section outline paint is * auto-populated from the drawing supplier. */ private boolean autoPopulateSectionOutlinePaint; /** The section outline stroke map. */ private StrokeMap sectionOutlineStrokeMap; /** The default section outline stroke (fallback). */ private transient Stroke defaultSectionOutlineStroke; /** * A flag that controls whether or not the section outline stroke is * auto-populated from the drawing supplier. */ private boolean autoPopulateSectionOutlineStroke; /** The shadow paint. */ private transient Paint shadowPaint = Color.GRAY; /** The x-offset for the shadow effect. */ private double shadowXOffset = 4.0f; /** The y-offset for the shadow effect. */ private double shadowYOffset = 4.0f; /** The percentage amount to explode each pie section. */ private Map explodePercentages; /** The section label generator. */ private PieSectionLabelGenerator labelGenerator; /** The font used to display the section labels. */ private Font labelFont; /** The color used to draw the section labels. */ private transient Paint labelPaint; /** * The color used to draw the background of the section labels. If this * is {@code null}, the background is not filled. */ private transient Paint labelBackgroundPaint; /** * The paint used to draw the outline of the section labels * ({@code null} permitted). */ private transient Paint labelOutlinePaint; /** * The stroke used to draw the outline of the section labels * ({@code null} permitted). */ private transient Stroke labelOutlineStroke; /** * The paint used to draw the shadow for the section labels * ({@code null} permitted). */ private transient Paint labelShadowPaint; /** * A flag that controls whether simple or extended labels are used. */ private boolean simpleLabels = true; /** * The padding between the labels and the label outlines. This is not * allowed to be {@code null}. */ private RectangleInsets labelPadding; /** * The simple label offset. */ private RectangleInsets simpleLabelOffset; /** The maximum label width as a percentage of the plot width. */ private double maximumLabelWidth = 0.14; /** * The gap between the labels and the link corner, as a percentage of the * plot width. */ private double labelGap = 0.025; /** A flag that controls whether or not the label links are drawn. */ private boolean labelLinksVisible; /** * The label link style. */ private PieLabelLinkStyle labelLinkStyle = PieLabelLinkStyle.STANDARD; /** The link margin. */ private double labelLinkMargin = 0.025; /** The paint used for the label linking lines. */ private transient Paint labelLinkPaint = Color.BLACK; /** The stroke used for the label linking lines. */ private transient Stroke labelLinkStroke = new BasicStroke(0.5f); /** * The pie section label distributor. */ private AbstractPieLabelDistributor labelDistributor; /** The tooltip generator. */ private PieToolTipGenerator toolTipGenerator; /** The URL generator. */ private PieURLGenerator urlGenerator; /** The legend label generator. */ private PieSectionLabelGenerator legendLabelGenerator; /** A tool tip generator for the legend. */ private PieSectionLabelGenerator legendLabelToolTipGenerator; /** * A URL generator for the legend items (optional). */ private PieURLGenerator legendLabelURLGenerator; /** * A flag that controls whether {@code null} values are ignored. */ private boolean ignoreNullValues; /** * A flag that controls whether zero values are ignored. */ private boolean ignoreZeroValues; /** The legend item shape. */ private transient Shape legendItemShape; /** * The smallest arc angle that will get drawn (this is to avoid a bug in * various Java implementations that causes the JVM to crash). See this * link for details: * * http://www.jfree.org/phpBB2/viewtopic.php?t=2707 * * ...and this bug report in the Java Bug Parade: * * http://developer.java.sun.com/developer/bugParade/bugs/4836495.html */ private double minimumArcAngleToDraw; /** * The shadow generator for the plot ({@code null} permitted). */ private ShadowGenerator shadowGenerator; /** The resourceBundle for the localization. */ protected static ResourceBundle localizationResources = ResourceBundleWrapper.getBundle( "org.jfree.chart.plot.LocalizationBundle"); /** * This debug flag controls whether or not an outline is drawn showing the * interior of the plot region. This is drawn as a lightGray rectangle * showing the padding provided by the 'interiorGap' setting. */ static final boolean DEBUG_DRAW_INTERIOR = false; /** * This debug flag controls whether or not an outline is drawn showing the * link area (in blue) and link ellipse (in yellow). This controls where * the label links have 'elbow' points. */ static final boolean DEBUG_DRAW_LINK_AREA = false; /** * This debug flag controls whether or not an outline is drawn showing * the pie area (in green). */ static final boolean DEBUG_DRAW_PIE_AREA = false; /** * Creates a new plot. The dataset is initially set to {@code null}. */ public PiePlot() { this(null); } /** * Creates a plot that will draw a pie chart for the specified dataset. * * @param dataset the dataset ({@code null} permitted). */ public PiePlot(PieDataset dataset) { super(); this.dataset = dataset; if (dataset != null) { dataset.addChangeListener(this); } this.pieIndex = 0; this.interiorGap = DEFAULT_INTERIOR_GAP; this.circular = true; this.startAngle = DEFAULT_START_ANGLE; this.direction = Rotation.CLOCKWISE; this.minimumArcAngleToDraw = DEFAULT_MINIMUM_ARC_ANGLE_TO_DRAW; this.sectionPaintMap = new PaintMap(); this.defaultSectionPaint = Color.GRAY; this.autoPopulateSectionPaint = true; this.sectionOutlinesVisible = true; this.sectionOutlinePaintMap = new PaintMap(); this.defaultSectionOutlinePaint = DEFAULT_OUTLINE_PAINT; this.autoPopulateSectionOutlinePaint = false; this.sectionOutlineStrokeMap = new StrokeMap(); this.defaultSectionOutlineStroke = DEFAULT_OUTLINE_STROKE; this.autoPopulateSectionOutlineStroke = false; this.explodePercentages = new TreeMap<>(); this.labelGenerator = new StandardPieSectionLabelGenerator(); this.labelFont = DEFAULT_LABEL_FONT; this.labelPaint = DEFAULT_LABEL_PAINT; this.labelBackgroundPaint = DEFAULT_LABEL_BACKGROUND_PAINT; this.labelOutlinePaint = DEFAULT_LABEL_OUTLINE_PAINT; this.labelOutlineStroke = DEFAULT_LABEL_OUTLINE_STROKE; this.labelShadowPaint = DEFAULT_LABEL_SHADOW_PAINT; this.labelLinksVisible = true; this.labelDistributor = new PieLabelDistributor(0); this.simpleLabels = false; this.simpleLabelOffset = new RectangleInsets(UnitType.RELATIVE, 0.18, 0.18, 0.18, 0.18); this.labelPadding = new RectangleInsets(2, 2, 2, 2); this.toolTipGenerator = null; this.urlGenerator = null; this.legendLabelGenerator = new StandardPieSectionLabelGenerator(); this.legendLabelToolTipGenerator = null; this.legendLabelURLGenerator = null; this.legendItemShape = Plot.DEFAULT_LEGEND_ITEM_CIRCLE; this.ignoreNullValues = false; this.ignoreZeroValues = false; this.shadowGenerator = null; } /** * Returns the dataset. * * @return The dataset (possibly {@code null}). * * @see #setDataset(PieDataset) */ public PieDataset getDataset() { return this.dataset; } /** * Sets the dataset and sends a {@link DatasetChangeEvent} to 'this'. * * @param dataset the dataset ({@code null} permitted). * * @see #getDataset() */ public void setDataset(PieDataset dataset) { // if there is an existing dataset, remove the plot from the list of // change listeners... PieDataset existing = this.dataset; if (existing != null) { existing.removeChangeListener(this); } // set the new dataset, and register the chart as a change listener... this.dataset = dataset; if (dataset != null) { setDatasetGroup(dataset.getGroup()); dataset.addChangeListener(this); } // send a dataset change event to self... DatasetChangeEvent event = new DatasetChangeEvent(this, dataset); datasetChanged(event); } /** * Returns the pie index (this is used by the {@link MultiplePiePlot} class * to track subplots). * * @return The pie index. * * @see #setPieIndex(int) */ public int getPieIndex() { return this.pieIndex; } /** * Sets the pie index (this is used by the {@link MultiplePiePlot} class to * track subplots). * * @param index the index. * * @see #getPieIndex() */ public void setPieIndex(int index) { this.pieIndex = index; } /** * Returns the start angle for the first pie section. This is measured in * degrees starting from 3 o'clock and measuring anti-clockwise. * * @return The start angle. * * @see #setStartAngle(double) */ public double getStartAngle() { return this.startAngle; } /** * Sets the starting angle and sends a {@link PlotChangeEvent} to all * registered listeners. The initial default value is 90 degrees, which * corresponds to 12 o'clock. A value of zero corresponds to 3 o'clock... * this is the encoding used by Java's Arc2D class. * * @param angle the angle (in degrees). * * @see #getStartAngle() */ public void setStartAngle(double angle) { this.startAngle = angle; fireChangeEvent(); } /** * Returns the direction in which the pie sections are drawn (clockwise or * anti-clockwise). * * @return The direction (never {@code null}). * * @see #setDirection(Rotation) */ public Rotation getDirection() { return this.direction; } /** * Sets the direction in which the pie sections are drawn and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param direction the direction ({@code null} not permitted). * * @see #getDirection() */ public void setDirection(Rotation direction) { Args.nullNotPermitted(direction, "direction"); this.direction = direction; fireChangeEvent(); } /** * Returns the interior gap, measured as a percentage of the available * drawing space. * * @return The gap (as a percentage of the available drawing space). * * @see #setInteriorGap(double) */ public double getInteriorGap() { return this.interiorGap; } /** * Sets the interior gap and sends a {@link PlotChangeEvent} to all * registered listeners. This controls the space between the edges of the * pie plot and the plot area itself (the region where the section labels * appear). * * @param percent the gap (as a percentage of the available drawing space). * * @see #getInteriorGap() */ public void setInteriorGap(double percent) { if ((percent < 0.0) || (percent > MAX_INTERIOR_GAP)) { throw new IllegalArgumentException( "Invalid 'percent' (" + percent + ") argument."); } if (this.interiorGap != percent) { this.interiorGap = percent; fireChangeEvent(); } } /** * Returns a flag indicating whether the pie chart is circular, or * stretched into an elliptical shape. * * @return A flag indicating whether the pie chart is circular. * * @see #setCircular(boolean) */ public boolean isCircular() { return this.circular; } /** * A flag indicating whether the pie chart is circular, or stretched into * an elliptical shape. * * @param flag the new value. * * @see #isCircular() */ public void setCircular(boolean flag) { setCircular(flag, true); } /** * Sets the circular attribute and, if requested, sends a * {@link PlotChangeEvent} to all registered listeners. * * @param circular the new value of the flag. * @param notify notify listeners? * * @see #isCircular() */ public void setCircular(boolean circular, boolean notify) { this.circular = circular; if (notify) { fireChangeEvent(); } } /** * Returns the flag that controls whether {@code null} values in the * dataset are ignored. * * @return A boolean. * * @see #setIgnoreNullValues(boolean) */ public boolean getIgnoreNullValues() { return this.ignoreNullValues; } /** * Sets a flag that controls whether {@code null} values are ignored, * and sends a {@link PlotChangeEvent} to all registered listeners. At * present, this only affects whether or not the key is presented in the * legend. * * @param flag the flag. * * @see #getIgnoreNullValues() * @see #setIgnoreZeroValues(boolean) */ public void setIgnoreNullValues(boolean flag) { this.ignoreNullValues = flag; fireChangeEvent(); } /** * Returns the flag that controls whether zero values in the * dataset are ignored. * * @return A boolean. * * @see #setIgnoreZeroValues(boolean) */ public boolean getIgnoreZeroValues() { return this.ignoreZeroValues; } /** * Sets a flag that controls whether zero values are ignored, * and sends a {@link PlotChangeEvent} to all registered listeners. This * only affects whether or not a label appears for the non-visible * pie section. * * @param flag the flag. * * @see #getIgnoreZeroValues() * @see #setIgnoreNullValues(boolean) */ public void setIgnoreZeroValues(boolean flag) { this.ignoreZeroValues = flag; fireChangeEvent(); } //// SECTION PAINT //////////////////////////////////////////////////////// /** * Returns the paint for the specified section. This is equivalent to * {@code lookupSectionPaint(section, getAutoPopulateSectionPaint())}. * * @param key the section key. * * @return The paint for the specified section. * * @see #lookupSectionPaint(Comparable, boolean) */ protected Paint lookupSectionPaint(Comparable key) { return lookupSectionPaint(key, getAutoPopulateSectionPaint()); } /** * Returns the paint for the specified section. The lookup involves these * steps: *
    *
  • if {@link #getSectionPaint(Comparable)} is non-{@code null} return * it;
  • *
  • if {@link #getSectionPaint(Comparable)} is {@code null} but * {@code autoPopulate} is {@code true}, attempt to fetch * a new paint from the drawing supplier * ({@link #getDrawingSupplier()}); *
  • if all else fails, return {@link #getDefaultSectionPaint()}. *
* * @param key the section key. * @param autoPopulate a flag that controls whether the drawing supplier * is used to auto-populate the section paint settings. * * @return The paint. */ protected Paint lookupSectionPaint(Comparable key, boolean autoPopulate) { // if not, check if there is a paint defined for the specified key Paint result = this.sectionPaintMap.getPaint(key); if (result != null) { return result; } // nothing defined - do we autoPopulate? if (autoPopulate) { DrawingSupplier ds = getDrawingSupplier(); if (ds != null) { result = ds.getNextPaint(); this.sectionPaintMap.put(key, result); } else { result = this.defaultSectionPaint; } } else { result = this.defaultSectionPaint; } return result; } /** * Returns a key for the specified section. The preferred way of doing this * now is to link the attributes directly to the section key (there are new * methods for this, starting from version 1.0.3). * * @param section the section index. * * @return The key. */ protected K getSectionKey(int section) { K key = null; if (this.dataset != null) { if (section >= 0 && section < this.dataset.getItemCount()) { key = this.dataset.getKey(section); } } return key; } /** * Returns the paint associated with the specified key, or * {@code null} if there is no paint associated with the key. * * @param key the key ({@code null} not permitted). * * @return The paint associated with the specified key, or * {@code null}. * * @throws IllegalArgumentException if {@code key} is * {@code null}. * * @see #setSectionPaint(Comparable, Paint) */ public Paint getSectionPaint(Comparable key) { // null argument check delegated... return this.sectionPaintMap.getPaint(key); } /** * Sets the paint associated with the specified key, and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param key the key ({@code null} not permitted). * @param paint the paint. * * @throws IllegalArgumentException if {@code key} is * {@code null}. * * @see #getSectionPaint(Comparable) */ public void setSectionPaint(Comparable key, Paint paint) { // null argument check delegated... this.sectionPaintMap.put(key, paint); fireChangeEvent(); } /** * Clears the section paint settings for this plot and, if requested, sends * a {@link PlotChangeEvent} to all registered listeners. Be aware that * if the {@code autoPopulateSectionPaint} flag is set, the section * paints may be repopulated using the same colours as before. * * @param notify notify listeners? * * @see #autoPopulateSectionPaint */ public void clearSectionPaints(boolean notify) { this.sectionPaintMap.clear(); if (notify) { fireChangeEvent(); } } /** * Returns the default section paint. This is used when no other paint is * defined, which is rare. The default value is {@code Color.GRAY}. * * @return The paint (never {@code null}). * * @see #setDefaultSectionPaint(Paint) */ public Paint getDefaultSectionPaint() { return this.defaultSectionPaint; } /** * Sets the default section paint and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getDefaultSectionPaint() */ public void setDefaultSectionPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.defaultSectionPaint = paint; fireChangeEvent(); } /** * Returns the flag that controls whether or not the section paint is * auto-populated by the {@link #lookupSectionPaint(Comparable)} method. * * @return A boolean. */ public boolean getAutoPopulateSectionPaint() { return this.autoPopulateSectionPaint; } /** * Sets the flag that controls whether or not the section paint is * auto-populated by the {@link #lookupSectionPaint(Comparable)} method, * and sends a {@link PlotChangeEvent} to all registered listeners. * * @param auto auto-populate? */ public void setAutoPopulateSectionPaint(boolean auto) { this.autoPopulateSectionPaint = auto; fireChangeEvent(); } //// SECTION OUTLINE PAINT //////////////////////////////////////////////// /** * Returns the flag that controls whether or not the outline is drawn for * each pie section. * * @return The flag that controls whether or not the outline is drawn for * each pie section. * * @see #setSectionOutlinesVisible(boolean) */ public boolean getSectionOutlinesVisible() { return this.sectionOutlinesVisible; } /** * Sets the flag that controls whether or not the outline is drawn for * each pie section, and sends a {@link PlotChangeEvent} to all registered * listeners. * * @param visible the flag. * * @see #getSectionOutlinesVisible() */ public void setSectionOutlinesVisible(boolean visible) { this.sectionOutlinesVisible = visible; fireChangeEvent(); } /** * Returns the outline paint for the specified section. This is equivalent * to {@code lookupSectionPaint(section, * getAutoPopulateSectionOutlinePaint())}. * * @param key the section key. * * @return The paint for the specified section. * * @see #lookupSectionOutlinePaint(Comparable, boolean) */ protected Paint lookupSectionOutlinePaint(Comparable key) { return lookupSectionOutlinePaint(key, getAutoPopulateSectionOutlinePaint()); } /** * Returns the outline paint for the specified section. The lookup * involves these steps: *
    *
  • if {@link #getSectionOutlinePaint(Comparable)} is * non-{@code null} return it;
  • *
  • if {@link #getSectionOutlinePaint(Comparable)} is {@code null} but * {@code autoPopulate} is {@code true}, attempt to fetch * a new outline paint from the drawing supplier * ({@link #getDrawingSupplier()}); *
  • if all else fails, return {@link #getDefaultSectionOutlinePaint()}. *
* * @param key the section key. * @param autoPopulate a flag that controls whether the drawing supplier * is used to auto-populate the section outline paint settings. * * @return The paint. */ protected Paint lookupSectionOutlinePaint(Comparable key, boolean autoPopulate) { // if not, check if there is a paint defined for the specified key Paint result = this.sectionOutlinePaintMap.getPaint(key); if (result != null) { return result; } // nothing defined - do we autoPopulate? if (autoPopulate) { DrawingSupplier ds = getDrawingSupplier(); if (ds != null) { result = ds.getNextOutlinePaint(); this.sectionOutlinePaintMap.put(key, result); } else { result = this.defaultSectionOutlinePaint; } } else { result = this.defaultSectionOutlinePaint; } return result; } /** * Returns the outline paint associated with the specified key, or * {@code null} if there is no paint associated with the key. * * @param key the key ({@code null} not permitted). * * @return The paint associated with the specified key, or * {@code null}. * * @throws IllegalArgumentException if {@code key} is * {@code null}. * * @see #setSectionOutlinePaint(Comparable, Paint) */ public Paint getSectionOutlinePaint(Comparable key) { // null argument check delegated... return this.sectionOutlinePaintMap.getPaint(key); } /** * Sets the outline paint associated with the specified key, and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param key the key ({@code null} not permitted). * @param paint the paint. * * @throws IllegalArgumentException if {@code key} is * {@code null}. * * @see #getSectionOutlinePaint(Comparable) */ public void setSectionOutlinePaint(Comparable key, Paint paint) { // null argument check delegated... this.sectionOutlinePaintMap.put(key, paint); fireChangeEvent(); } /** * Clears the section outline paint settings for this plot and, if * requested, sends a {@link PlotChangeEvent} to all registered listeners. * Be aware that if the {@code autoPopulateSectionPaint} flag is set, * the section paints may be repopulated using the same colours as before. * * @param notify notify listeners? * * @see #autoPopulateSectionOutlinePaint */ public void clearSectionOutlinePaints(boolean notify) { this.sectionOutlinePaintMap.clear(); if (notify) { fireChangeEvent(); } } /** * Returns the default section paint. This is used when no other paint is * available. * * @return The paint (never {@code null}). * * @see #setDefaultSectionOutlinePaint(Paint) */ public Paint getDefaultSectionOutlinePaint() { return this.defaultSectionOutlinePaint; } /** * Sets the default section paint. * * @param paint the paint ({@code null} not permitted). * * @see #getDefaultSectionOutlinePaint() */ public void setDefaultSectionOutlinePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.defaultSectionOutlinePaint = paint; fireChangeEvent(); } /** * Returns the flag that controls whether or not the section outline paint * is auto-populated by the {@link #lookupSectionOutlinePaint(Comparable)} * method. * * @return A boolean. */ public boolean getAutoPopulateSectionOutlinePaint() { return this.autoPopulateSectionOutlinePaint; } /** * Sets the flag that controls whether or not the section outline paint is * auto-populated by the {@link #lookupSectionOutlinePaint(Comparable)} * method, and sends a {@link PlotChangeEvent} to all registered listeners. * * @param auto auto-populate? */ public void setAutoPopulateSectionOutlinePaint(boolean auto) { this.autoPopulateSectionOutlinePaint = auto; fireChangeEvent(); } //// SECTION OUTLINE STROKE /////////////////////////////////////////////// /** * Returns the outline stroke for the specified section. This is * equivalent to {@code lookupSectionOutlineStroke(section, * getAutoPopulateSectionOutlineStroke())}. * * @param key the section key. * * @return The stroke for the specified section. * * @see #lookupSectionOutlineStroke(Comparable, boolean) */ protected Stroke lookupSectionOutlineStroke(Comparable key) { return lookupSectionOutlineStroke(key, getAutoPopulateSectionOutlineStroke()); } /** * Returns the outline stroke for the specified section. The lookup * involves these steps: *
    *
  • if {@link #getSectionOutlineStroke(Comparable)} is * non-{@code null} return it;
  • *
  • if {@link #getSectionOutlineStroke(Comparable)} is {@code null} but * {@code autoPopulate} is {@code true}, attempt to fetch * a new outline stroke from the drawing supplier * ({@link #getDrawingSupplier()}); *
  • if all else fails, return {@link #getDefaultSectionOutlineStroke()}. *
* * @param key the section key. * @param autoPopulate a flag that controls whether the drawing supplier * is used to auto-populate the section outline stroke settings. * * @return The stroke. */ protected Stroke lookupSectionOutlineStroke(Comparable key, boolean autoPopulate) { // if not, check if there is a stroke defined for the specified key Stroke result = this.sectionOutlineStrokeMap.getStroke(key); if (result != null) { return result; } // nothing defined - do we autoPopulate? if (autoPopulate) { DrawingSupplier ds = getDrawingSupplier(); if (ds != null) { result = ds.getNextOutlineStroke(); this.sectionOutlineStrokeMap.put(key, result); } else { result = this.defaultSectionOutlineStroke; } } else { result = this.defaultSectionOutlineStroke; } return result; } /** * Returns the outline stroke associated with the specified key, or * {@code null} if there is no stroke associated with the key. * * @param key the key ({@code null} not permitted). * * @return The stroke associated with the specified key, or * {@code null}. * * @throws IllegalArgumentException if {@code key} is * {@code null}. * * @see #setSectionOutlineStroke(Comparable, Stroke) */ public Stroke getSectionOutlineStroke(Comparable key) { // null argument check delegated... return this.sectionOutlineStrokeMap.getStroke(key); } /** * Sets the outline stroke associated with the specified key, and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param key the key ({@code null} not permitted). * @param stroke the stroke. * * @throws IllegalArgumentException if {@code key} is * {@code null}. * * @see #getSectionOutlineStroke(Comparable) */ public void setSectionOutlineStroke(Comparable key, Stroke stroke) { // null argument check delegated... this.sectionOutlineStrokeMap.put(key, stroke); fireChangeEvent(); } /** * Clears the section outline stroke settings for this plot and, if * requested, sends a {@link PlotChangeEvent} to all registered listeners. * Be aware that if the {@code autoPopulateSectionPaint} flag is set, * the section paints may be repopulated using the same colours as before. * * @param notify notify listeners? * * @see #autoPopulateSectionOutlineStroke */ public void clearSectionOutlineStrokes(boolean notify) { this.sectionOutlineStrokeMap.clear(); if (notify) { fireChangeEvent(); } } /** * Returns the default section stroke. This is used when no other stroke is * available. * * @return The stroke (never {@code null}). * * @see #setDefaultSectionOutlineStroke(Stroke) */ public Stroke getDefaultSectionOutlineStroke() { return this.defaultSectionOutlineStroke; } /** * Sets the default section stroke. * * @param stroke the stroke ({@code null} not permitted). * * @see #getDefaultSectionOutlineStroke() */ public void setDefaultSectionOutlineStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.defaultSectionOutlineStroke = stroke; fireChangeEvent(); } /** * Returns the flag that controls whether or not the section outline stroke * is auto-populated by the {@link #lookupSectionOutlinePaint(Comparable)} * method. * * @return A boolean. */ public boolean getAutoPopulateSectionOutlineStroke() { return this.autoPopulateSectionOutlineStroke; } /** * Sets the flag that controls whether or not the section outline stroke is * auto-populated by the {@link #lookupSectionOutlineStroke(Comparable)} * method, and sends a {@link PlotChangeEvent} to all registered listeners. * * @param auto auto-populate? */ public void setAutoPopulateSectionOutlineStroke(boolean auto) { this.autoPopulateSectionOutlineStroke = auto; fireChangeEvent(); } /** * Returns the shadow paint. * * @return The paint (possibly {@code null}). * * @see #setShadowPaint(Paint) */ public Paint getShadowPaint() { return this.shadowPaint; } /** * Sets the shadow paint and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param paint the paint ({@code null} permitted). * * @see #getShadowPaint() */ public void setShadowPaint(Paint paint) { this.shadowPaint = paint; fireChangeEvent(); } /** * Returns the x-offset for the shadow effect. * * @return The offset (in Java2D units). * * @see #setShadowXOffset(double) */ public double getShadowXOffset() { return this.shadowXOffset; } /** * Sets the x-offset for the shadow effect and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param offset the offset (in Java2D units). * * @see #getShadowXOffset() */ public void setShadowXOffset(double offset) { this.shadowXOffset = offset; fireChangeEvent(); } /** * Returns the y-offset for the shadow effect. * * @return The offset (in Java2D units). * * @see #setShadowYOffset(double) */ public double getShadowYOffset() { return this.shadowYOffset; } /** * Sets the y-offset for the shadow effect and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param offset the offset (in Java2D units). * * @see #getShadowYOffset() */ public void setShadowYOffset(double offset) { this.shadowYOffset = offset; fireChangeEvent(); } /** * Returns the amount that the section with the specified key should be * exploded. * * @param key the key ({@code null} not permitted). * * @return The amount that the section with the specified key should be * exploded. * * @throws IllegalArgumentException if {@code key} is * {@code null}. * * @see #setExplodePercent(Comparable, double) */ public double getExplodePercent(K key) { double result = 0.0; if (this.explodePercentages != null) { Number percent = (Number) this.explodePercentages.get(key); if (percent != null) { result = percent.doubleValue(); } } return result; } /** * Sets the amount that a pie section should be exploded and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param key the section key ({@code null} not permitted). * @param percent the explode percentage (0.30 = 30 percent). * * @see #getExplodePercent(Comparable) */ public void setExplodePercent(K key, double percent) { Args.nullNotPermitted(key, "key"); if (this.explodePercentages == null) { this.explodePercentages = new TreeMap<>(); } this.explodePercentages.put(key, percent); fireChangeEvent(); } /** * Returns the maximum explode percent. * * @return The percent. */ public double getMaximumExplodePercent() { if (this.dataset == null) { return 0.0; } double result = 0.0; for (K key : this.dataset.getKeys()) { Double explode = this.explodePercentages.get(key); if (explode != null) { result = Math.max(result, explode); } } return result; } /** * Returns the section label generator. * * @return The generator (possibly {@code null}). * * @see #setLabelGenerator(PieSectionLabelGenerator) */ public PieSectionLabelGenerator getLabelGenerator() { return this.labelGenerator; } /** * Sets the section label generator and sends a {@link PlotChangeEvent} to * all registered listeners. * * @param generator the generator ({@code null} permitted). * * @see #getLabelGenerator() */ public void setLabelGenerator(PieSectionLabelGenerator generator) { this.labelGenerator = generator; fireChangeEvent(); } /** * Returns the gap between the edge of the pie and the labels, expressed as * a percentage of the plot width. * * @return The gap (a percentage, where 0.05 = five percent). * * @see #setLabelGap(double) */ public double getLabelGap() { return this.labelGap; } /** * Sets the gap between the edge of the pie and the labels (expressed as a * percentage of the plot width) and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param gap the gap (a percentage, where 0.05 = five percent). * * @see #getLabelGap() */ public void setLabelGap(double gap) { this.labelGap = gap; fireChangeEvent(); } /** * Returns the maximum label width as a percentage of the plot width. * * @return The width (a percentage, where 0.20 = 20 percent). * * @see #setMaximumLabelWidth(double) */ public double getMaximumLabelWidth() { return this.maximumLabelWidth; } /** * Sets the maximum label width as a percentage of the plot width and sends * a {@link PlotChangeEvent} to all registered listeners. * * @param width the width (a percentage, where 0.20 = 20 percent). * * @see #getMaximumLabelWidth() */ public void setMaximumLabelWidth(double width) { this.maximumLabelWidth = width; fireChangeEvent(); } /** * Returns the flag that controls whether or not label linking lines are * visible. * * @return A boolean. * * @see #setLabelLinksVisible(boolean) */ public boolean getLabelLinksVisible() { return this.labelLinksVisible; } /** * Sets the flag that controls whether or not label linking lines are * visible and sends a {@link PlotChangeEvent} to all registered listeners. * Please take care when hiding the linking lines - depending on the data * values, the labels can be displayed some distance away from the * corresponding pie section. * * @param visible the flag. * * @see #getLabelLinksVisible() */ public void setLabelLinksVisible(boolean visible) { this.labelLinksVisible = visible; fireChangeEvent(); } /** * Returns the label link style. * * @return The label link style (never {@code null}). * * @see #setLabelLinkStyle(PieLabelLinkStyle) */ public PieLabelLinkStyle getLabelLinkStyle() { return this.labelLinkStyle; } /** * Sets the label link style and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param style the new style ({@code null} not permitted). * * @see #getLabelLinkStyle() */ public void setLabelLinkStyle(PieLabelLinkStyle style) { Args.nullNotPermitted(style, "style"); this.labelLinkStyle = style; fireChangeEvent(); } /** * Returns the margin (expressed as a percentage of the width or height) * between the edge of the pie and the link point. * * @return The link margin (as a percentage, where 0.05 is five percent). * * @see #setLabelLinkMargin(double) */ public double getLabelLinkMargin() { return this.labelLinkMargin; } /** * Sets the link margin and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param margin the margin. * * @see #getLabelLinkMargin() */ public void setLabelLinkMargin(double margin) { this.labelLinkMargin = margin; fireChangeEvent(); } /** * Returns the paint used for the lines that connect pie sections to their * corresponding labels. * * @return The paint (never {@code null}). * * @see #setLabelLinkPaint(Paint) */ public Paint getLabelLinkPaint() { return this.labelLinkPaint; } /** * Sets the paint used for the lines that connect pie sections to their * corresponding labels, and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getLabelLinkPaint() */ public void setLabelLinkPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.labelLinkPaint = paint; fireChangeEvent(); } /** * Returns the stroke used for the label linking lines. * * @return The stroke. * * @see #setLabelLinkStroke(Stroke) */ public Stroke getLabelLinkStroke() { return this.labelLinkStroke; } /** * Sets the link stroke and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param stroke the stroke. * * @see #getLabelLinkStroke() */ public void setLabelLinkStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.labelLinkStroke = stroke; fireChangeEvent(); } /** * Returns the distance that the end of the label link is embedded into * the plot, expressed as a percentage of the plot's radius. *

* This method is overridden in the {@link RingPlot} class to resolve * bug 2121818. * * @return {@code 0.10}. */ protected double getLabelLinkDepth() { return 0.1; } /** * Returns the section label font. * * @return The font (never {@code null}). * * @see #setLabelFont(Font) */ public Font getLabelFont() { return this.labelFont; } /** * Sets the section label font and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param font the font ({@code null} not permitted). * * @see #getLabelFont() */ public void setLabelFont(Font font) { Args.nullNotPermitted(font, "font"); this.labelFont = font; fireChangeEvent(); } /** * Returns the section label paint. * * @return The paint (never {@code null}). * * @see #setLabelPaint(Paint) */ public Paint getLabelPaint() { return this.labelPaint; } /** * Sets the section label paint and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getLabelPaint() */ public void setLabelPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.labelPaint = paint; fireChangeEvent(); } /** * Returns the section label background paint. * * @return The paint (possibly {@code null}). * * @see #setLabelBackgroundPaint(Paint) */ public Paint getLabelBackgroundPaint() { return this.labelBackgroundPaint; } /** * Sets the section label background paint and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} permitted). * * @see #getLabelBackgroundPaint() */ public void setLabelBackgroundPaint(Paint paint) { this.labelBackgroundPaint = paint; fireChangeEvent(); } /** * Returns the section label outline paint. * * @return The paint (possibly {@code null}). * * @see #setLabelOutlinePaint(Paint) */ public Paint getLabelOutlinePaint() { return this.labelOutlinePaint; } /** * Sets the section label outline paint and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} permitted). * * @see #getLabelOutlinePaint() */ public void setLabelOutlinePaint(Paint paint) { this.labelOutlinePaint = paint; fireChangeEvent(); } /** * Returns the section label outline stroke. * * @return The stroke (possibly {@code null}). * * @see #setLabelOutlineStroke(Stroke) */ public Stroke getLabelOutlineStroke() { return this.labelOutlineStroke; } /** * Sets the section label outline stroke and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} permitted). * * @see #getLabelOutlineStroke() */ public void setLabelOutlineStroke(Stroke stroke) { this.labelOutlineStroke = stroke; fireChangeEvent(); } /** * Returns the section label shadow paint. * * @return The paint (possibly {@code null}). * * @see #setLabelShadowPaint(Paint) */ public Paint getLabelShadowPaint() { return this.labelShadowPaint; } /** * Sets the section label shadow paint and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param paint the paint ({@code null} permitted). * * @see #getLabelShadowPaint() */ public void setLabelShadowPaint(Paint paint) { this.labelShadowPaint = paint; fireChangeEvent(); } /** * Returns the label padding. * * @return The label padding (never {@code null}). * * @see #setLabelPadding(RectangleInsets) */ public RectangleInsets getLabelPadding() { return this.labelPadding; } /** * Sets the padding between each label and its outline and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param padding the padding ({@code null} not permitted). * * @see #getLabelPadding() */ public void setLabelPadding(RectangleInsets padding) { Args.nullNotPermitted(padding, "padding"); this.labelPadding = padding; fireChangeEvent(); } /** * Returns the flag that controls whether simple or extended labels are * displayed on the plot. * * @return A boolean. */ public boolean getSimpleLabels() { return this.simpleLabels; } /** * Sets the flag that controls whether simple or extended labels are * displayed on the plot, and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param simple the new flag value. */ public void setSimpleLabels(boolean simple) { this.simpleLabels = simple; fireChangeEvent(); } /** * Returns the offset used for the simple labels, if they are displayed. * * @return The offset (never {@code null}). * * @see #setSimpleLabelOffset(RectangleInsets) */ public RectangleInsets getSimpleLabelOffset() { return this.simpleLabelOffset; } /** * Sets the offset for the simple labels and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param offset the offset ({@code null} not permitted). * * @see #getSimpleLabelOffset() */ public void setSimpleLabelOffset(RectangleInsets offset) { Args.nullNotPermitted(offset, "offset"); this.simpleLabelOffset = offset; fireChangeEvent(); } /** * Returns the object responsible for the vertical layout of the pie * section labels. * * @return The label distributor (never {@code null}). */ public AbstractPieLabelDistributor getLabelDistributor() { return this.labelDistributor; } /** * Sets the label distributor and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param distributor the distributor ({@code null} not permitted). */ public void setLabelDistributor(AbstractPieLabelDistributor distributor) { Args.nullNotPermitted(distributor, "distributor"); this.labelDistributor = distributor; fireChangeEvent(); } /** * Returns the tool tip generator, an object that is responsible for * generating the text items used for tool tips by the plot. If the * generator is {@code null}, no tool tips will be created. * * @return The generator (possibly {@code null}). * * @see #setToolTipGenerator(PieToolTipGenerator) */ public PieToolTipGenerator getToolTipGenerator() { return this.toolTipGenerator; } /** * Sets the tool tip generator and sends a {@link PlotChangeEvent} to all * registered listeners. Set the generator to {@code null} if you * don't want any tool tips. * * @param generator the generator ({@code null} permitted). * * @see #getToolTipGenerator() */ public void setToolTipGenerator(PieToolTipGenerator generator) { this.toolTipGenerator = generator; fireChangeEvent(); } /** * Returns the URL generator. * * @return The generator (possibly {@code null}). * * @see #setURLGenerator(PieURLGenerator) */ public PieURLGenerator getURLGenerator() { return this.urlGenerator; } /** * Sets the URL generator and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param generator the generator ({@code null} permitted). * * @see #getURLGenerator() */ public void setURLGenerator(PieURLGenerator generator) { this.urlGenerator = generator; fireChangeEvent(); } /** * Returns the minimum arc angle that will be drawn. Pie sections for an * angle smaller than this are not drawn, to avoid a JDK bug. * * @return The minimum angle. * * @see #setMinimumArcAngleToDraw(double) */ public double getMinimumArcAngleToDraw() { return this.minimumArcAngleToDraw; } /** * Sets the minimum arc angle that will be drawn. Pie sections for an * angle smaller than this are not drawn, to avoid a JDK bug. See this * link for details: *

* * http://www.jfree.org/phpBB2/viewtopic.php?t=2707 *

* ...and this bug report in the Java Bug Parade: *

* * http://developer.java.sun.com/developer/bugParade/bugs/4836495.html * * @param angle the minimum angle. * * @see #getMinimumArcAngleToDraw() */ public void setMinimumArcAngleToDraw(double angle) { this.minimumArcAngleToDraw = angle; } /** * Returns the shape used for legend items. * * @return The shape (never {@code null}). * * @see #setLegendItemShape(Shape) */ public Shape getLegendItemShape() { return this.legendItemShape; } /** * Sets the shape used for legend items and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param shape the shape ({@code null} not permitted). * * @see #getLegendItemShape() */ public void setLegendItemShape(Shape shape) { Args.nullNotPermitted(shape, "shape"); this.legendItemShape = shape; fireChangeEvent(); } /** * Returns the legend label generator. * * @return The legend label generator (never {@code null}). * * @see #setLegendLabelGenerator(PieSectionLabelGenerator) */ public PieSectionLabelGenerator getLegendLabelGenerator() { return this.legendLabelGenerator; } /** * Sets the legend label generator and sends a {@link PlotChangeEvent} to * all registered listeners. * * @param generator the generator ({@code null} not permitted). * * @see #getLegendLabelGenerator() */ public void setLegendLabelGenerator(PieSectionLabelGenerator generator) { Args.nullNotPermitted(generator, "generator"); this.legendLabelGenerator = generator; fireChangeEvent(); } /** * Returns the legend label tool tip generator. * * @return The legend label tool tip generator (possibly {@code null}). * * @see #setLegendLabelToolTipGenerator(PieSectionLabelGenerator) */ public PieSectionLabelGenerator getLegendLabelToolTipGenerator() { return this.legendLabelToolTipGenerator; } /** * Sets the legend label tool tip generator and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param generator the generator ({@code null} permitted). * * @see #getLegendLabelToolTipGenerator() */ public void setLegendLabelToolTipGenerator( PieSectionLabelGenerator generator) { this.legendLabelToolTipGenerator = generator; fireChangeEvent(); } /** * Returns the legend label URL generator. * * @return The legend label URL generator (possibly {@code null}). * * @see #setLegendLabelURLGenerator(PieURLGenerator) */ public PieURLGenerator getLegendLabelURLGenerator() { return this.legendLabelURLGenerator; } /** * Sets the legend label URL generator and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param generator the generator ({@code null} permitted). * * @see #getLegendLabelURLGenerator() */ public void setLegendLabelURLGenerator(PieURLGenerator generator) { this.legendLabelURLGenerator = generator; fireChangeEvent(); } /** * Returns the shadow generator for the plot, if any. * * @return The shadow generator (possibly {@code null}). */ public ShadowGenerator getShadowGenerator() { return this.shadowGenerator; } /** * Sets the shadow generator for the plot and sends a * {@link PlotChangeEvent} to all registered listeners. Note that this is * a bitmap drop-shadow generation facility and is separate from the * vector based show option that is controlled via the * {@link #setShadowPaint(java.awt.Paint)} method. * * @param generator the generator ({@code null} permitted). */ public void setShadowGenerator(ShadowGenerator generator) { this.shadowGenerator = generator; fireChangeEvent(); } /** * Handles a mouse wheel rotation (this method is intended for use by the * {@code MouseWheelHandler} class). * * @param rotateClicks the number of rotate clicks on the the mouse wheel. */ public void handleMouseWheelRotation(int rotateClicks) { setStartAngle(this.startAngle + rotateClicks * 4.0); } /** * Initialises the drawing procedure. This method will be called before * the first item is rendered, giving the plot an opportunity to initialise * any state information it wants to maintain. * * @param g2 the graphics device. * @param plotArea the plot area ({@code null} not permitted). * @param plot the plot. * @param index the secondary index ({@code null} for primary * renderer). * @param info collects chart rendering information for return to caller. * * @return A state object (maintains state information relevant to one * chart drawing). */ public PiePlotState initialise(Graphics2D g2, Rectangle2D plotArea, PiePlot plot, Integer index, PlotRenderingInfo info) { PiePlotState state = new PiePlotState(info); state.setPassesRequired(2); if (this.dataset != null) { state.setTotal(DatasetUtils.calculatePieDatasetTotal( plot.getDataset())); } state.setLatestAngle(plot.getStartAngle()); return state; } /** * Draws the plot on a Java 2D graphics device (such as the screen or a * printer). * * @param g2 the graphics device. * @param area the area within which the plot should be drawn. * @param anchor the anchor point ({@code null} permitted). * @param parentState the state from the parent plot, if there is one. * @param info collects info about the drawing * ({@code null} permitted). */ @Override public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, PlotState parentState, PlotRenderingInfo info) { // adjust for insets... RectangleInsets insets = getInsets(); insets.trim(area); if (info != null) { info.setPlotArea(area); info.setDataArea(area); } drawBackground(g2, area); drawOutline(g2, area); Shape savedClip = g2.getClip(); g2.clip(area); Composite originalComposite = g2.getComposite(); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, getForegroundAlpha())); if (!DatasetUtils.isEmptyOrNull(this.dataset)) { Graphics2D savedG2 = g2; boolean suppressShadow = Boolean.TRUE.equals(g2.getRenderingHint( JFreeChart.KEY_SUPPRESS_SHADOW_GENERATION)); BufferedImage dataImage = null; if (this.shadowGenerator != null && !suppressShadow) { dataImage = new BufferedImage((int) area.getWidth(), (int) area.getHeight(), BufferedImage.TYPE_INT_ARGB); g2 = dataImage.createGraphics(); g2.translate(-area.getX(), -area.getY()); g2.setRenderingHints(savedG2.getRenderingHints()); } drawPie(g2, area, info); if (this.shadowGenerator != null && !suppressShadow) { BufferedImage shadowImage = this.shadowGenerator.createDropShadow(dataImage); g2 = savedG2; g2.drawImage(shadowImage, (int) area.getX() + this.shadowGenerator.calculateOffsetX(), (int) area.getY() + this.shadowGenerator.calculateOffsetY(), null); g2.drawImage(dataImage, (int) area.getX(), (int) area.getY(), null); } } else { drawNoDataMessage(g2, area); } g2.setClip(savedClip); g2.setComposite(originalComposite); drawOutline(g2, area); } /** * Draws the pie. * * @param g2 the graphics device. * @param plotArea the plot area. * @param info chart rendering info. */ protected void drawPie(Graphics2D g2, Rectangle2D plotArea, PlotRenderingInfo info) { PiePlotState state = initialise(g2, plotArea, this, null, info); // adjust the plot area for interior spacing and labels... double labelReserve = 0.0; if (this.labelGenerator != null && !this.simpleLabels) { labelReserve = this.labelGap + this.maximumLabelWidth; } double gapHorizontal = plotArea.getWidth() * labelReserve * 2.0; double gapVertical = plotArea.getHeight() * this.interiorGap * 2.0; if (DEBUG_DRAW_INTERIOR) { double hGap = plotArea.getWidth() * this.interiorGap; double vGap = plotArea.getHeight() * this.interiorGap; double igx1 = plotArea.getX() + hGap; double igx2 = plotArea.getMaxX() - hGap; double igy1 = plotArea.getY() + vGap; double igy2 = plotArea.getMaxY() - vGap; g2.setPaint(Color.GRAY); g2.draw(new Rectangle2D.Double(igx1, igy1, igx2 - igx1, igy2 - igy1)); } double linkX = plotArea.getX() + gapHorizontal / 2; double linkY = plotArea.getY() + gapVertical / 2; double linkW = plotArea.getWidth() - gapHorizontal; double linkH = plotArea.getHeight() - gapVertical; // make the link area a square if the pie chart is to be circular... if (this.circular) { double min = Math.min(linkW, linkH) / 2; linkX = (linkX + linkX + linkW) / 2 - min; linkY = (linkY + linkY + linkH) / 2 - min; linkW = 2 * min; linkH = 2 * min; } // the link area defines the dog leg points for the linking lines to // the labels Rectangle2D linkArea = new Rectangle2D.Double(linkX, linkY, linkW, linkH); state.setLinkArea(linkArea); if (DEBUG_DRAW_LINK_AREA) { g2.setPaint(Color.BLUE); g2.draw(linkArea); g2.setPaint(Color.YELLOW); g2.draw(new Ellipse2D.Double(linkArea.getX(), linkArea.getY(), linkArea.getWidth(), linkArea.getHeight())); } // the explode area defines the max circle/ellipse for the exploded // pie sections. it is defined by shrinking the linkArea by the // linkMargin factor. double lm = 0.0; if (!this.simpleLabels) { lm = this.labelLinkMargin; } double hh = linkArea.getWidth() * lm * 2.0; double vv = linkArea.getHeight() * lm * 2.0; Rectangle2D explodeArea = new Rectangle2D.Double(linkX + hh / 2.0, linkY + vv / 2.0, linkW - hh, linkH - vv); state.setExplodedPieArea(explodeArea); // the pie area defines the circle/ellipse for regular pie sections. // it is defined by shrinking the explodeArea by the explodeMargin // factor. double maximumExplodePercent = getMaximumExplodePercent(); double percent = maximumExplodePercent / (1.0 + maximumExplodePercent); double h1 = explodeArea.getWidth() * percent; double v1 = explodeArea.getHeight() * percent; Rectangle2D pieArea = new Rectangle2D.Double(explodeArea.getX() + h1 / 2.0, explodeArea.getY() + v1 / 2.0, explodeArea.getWidth() - h1, explodeArea.getHeight() - v1); if (DEBUG_DRAW_PIE_AREA) { g2.setPaint(Color.GREEN); g2.draw(pieArea); } state.setPieArea(pieArea); state.setPieCenterX(pieArea.getCenterX()); state.setPieCenterY(pieArea.getCenterY()); state.setPieWRadius(pieArea.getWidth() / 2.0); state.setPieHRadius(pieArea.getHeight() / 2.0); // plot the data (unless the dataset is null)... if ((this.dataset != null) && (this.dataset.getKeys().size() > 0)) { List keys = this.dataset.getKeys(); double totalValue = DatasetUtils.calculatePieDatasetTotal( this.dataset); int passesRequired = state.getPassesRequired(); for (int pass = 0; pass < passesRequired; pass++) { double runningTotal = 0.0; for (int section = 0; section < keys.size(); section++) { Number n = this.dataset.getValue(section); if (n != null) { double value = n.doubleValue(); if (value > 0.0) { runningTotal += value; drawItem(g2, section, explodeArea, state, pass); } } } } if (this.simpleLabels) { drawSimpleLabels(g2, keys, totalValue, plotArea, linkArea, state); } else { drawLabels(g2, keys, totalValue, plotArea, linkArea, state); } } else { drawNoDataMessage(g2, plotArea); } } /** * Draws a single data item. * * @param g2 the graphics device ({@code null} not permitted). * @param section the section index. * @param dataArea the data plot area. * @param state state information for one chart. * @param currentPass the current pass index. */ protected void drawItem(Graphics2D g2, int section, Rectangle2D dataArea, PiePlotState state, int currentPass) { Number n = this.dataset.getValue(section); if (n == null) { return; } double value = n.doubleValue(); double angle1 = 0.0; double angle2 = 0.0; if (this.direction == Rotation.CLOCKWISE) { angle1 = state.getLatestAngle(); angle2 = angle1 - value / state.getTotal() * 360.0; } else if (this.direction == Rotation.ANTICLOCKWISE) { angle1 = state.getLatestAngle(); angle2 = angle1 + value / state.getTotal() * 360.0; } else { throw new IllegalStateException("Rotation type not recognised."); } double angle = (angle2 - angle1); if (Math.abs(angle) > getMinimumArcAngleToDraw()) { double ep = 0.0; double mep = getMaximumExplodePercent(); if (mep > 0.0) { ep = getExplodePercent(dataset.getKey(section)) / mep; } Rectangle2D arcBounds = getArcBounds(state.getPieArea(), state.getExplodedPieArea(), angle1, angle, ep); Arc2D.Double arc = new Arc2D.Double(arcBounds, angle1, angle, Arc2D.PIE); if (currentPass == 0) { if (this.shadowPaint != null && this.shadowGenerator == null) { Shape shadowArc = ShapeUtils.createTranslatedShape( arc, (float) this.shadowXOffset, (float) this.shadowYOffset); g2.setPaint(this.shadowPaint); g2.fill(shadowArc); } } else if (currentPass == 1) { K key = getSectionKey(section); Paint paint = lookupSectionPaint(key, state); g2.setPaint(paint); g2.fill(arc); Paint outlinePaint = lookupSectionOutlinePaint(key); Stroke outlineStroke = lookupSectionOutlineStroke(key); if (this.sectionOutlinesVisible) { g2.setPaint(outlinePaint); g2.setStroke(outlineStroke); g2.draw(arc); } // update the linking line target for later // add an entity for the pie section if (state.getInfo() != null) { EntityCollection entities = state.getEntityCollection(); if (entities != null) { String tip = null; if (this.toolTipGenerator != null) { tip = this.toolTipGenerator.generateToolTip( this.dataset, key); } String url = null; if (this.urlGenerator != null) { url = this.urlGenerator.generateURL(this.dataset, key, this.pieIndex); } PieSectionEntity entity = new PieSectionEntity( arc, this.dataset, this.pieIndex, section, key, tip, url); entities.add(entity); } } } } state.setLatestAngle(angle2); } /** * Draws the pie section labels in the simple form. * * @param g2 the graphics device. * @param keys the section keys. * @param totalValue the total value for all sections in the pie. * @param plotArea the plot area. * @param pieArea the area containing the pie. * @param state the plot state. */ protected void drawSimpleLabels(Graphics2D g2, List keys, double totalValue, Rectangle2D plotArea, Rectangle2D pieArea, PiePlotState state) { Composite originalComposite = g2.getComposite(); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f)); Rectangle2D labelsArea = this.simpleLabelOffset.createInsetRectangle( pieArea); double runningTotal = 0.0; for (K key : keys) { boolean include; double v = 0.0; Number n = getDataset().getValue(key); if (n == null) { include = !getIgnoreNullValues(); } else { v = n.doubleValue(); include = getIgnoreZeroValues() ? v > 0.0 : v >= 0.0; } if (include) { runningTotal = runningTotal + v; // work out the mid angle (0 - 90 and 270 - 360) = right, // otherwise left double mid = getStartAngle() + (getDirection().getFactor() * ((runningTotal - v / 2.0) * 360) / totalValue); Arc2D arc = new Arc2D.Double(labelsArea, getStartAngle(), mid - getStartAngle(), Arc2D.OPEN); int x = (int) arc.getEndPoint().getX(); int y = (int) arc.getEndPoint().getY(); PieSectionLabelGenerator myLabelGenerator = getLabelGenerator(); if (myLabelGenerator == null) { continue; } String label = myLabelGenerator.generateSectionLabel( this.dataset, key); if (label == null) { continue; } g2.setFont(this.labelFont); FontMetrics fm = g2.getFontMetrics(); Rectangle2D bounds = TextUtils.getTextBounds(label, g2, fm); Rectangle2D out = this.labelPadding.createOutsetRectangle( bounds); Shape bg = ShapeUtils.createTranslatedShape(out, x - bounds.getCenterX(), y - bounds.getCenterY()); if (this.labelShadowPaint != null && this.shadowGenerator == null) { Shape shadow = ShapeUtils.createTranslatedShape(bg, this.shadowXOffset, this.shadowYOffset); g2.setPaint(this.labelShadowPaint); g2.fill(shadow); } if (this.labelBackgroundPaint != null) { g2.setPaint(this.labelBackgroundPaint); g2.fill(bg); } if (this.labelOutlinePaint != null && this.labelOutlineStroke != null) { g2.setPaint(this.labelOutlinePaint); g2.setStroke(this.labelOutlineStroke); g2.draw(bg); } g2.setPaint(this.labelPaint); g2.setFont(this.labelFont); TextUtils.drawAlignedString(label, g2, x, y, TextAnchor.CENTER); } } g2.setComposite(originalComposite); } /** * Draws the labels for the pie sections. * * @param g2 the graphics device. * @param keys the keys. * @param totalValue the total value. * @param plotArea the plot area. * @param linkArea the link area. * @param state the state. */ protected void drawLabels(Graphics2D g2, List keys, double totalValue, Rectangle2D plotArea, Rectangle2D linkArea, PiePlotState state) { Composite originalComposite = g2.getComposite(); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f)); // classify the keys according to which side the label will appear... DefaultKeyedValues leftKeys = new DefaultKeyedValues(); DefaultKeyedValues rightKeys = new DefaultKeyedValues(); double runningTotal = 0.0; for (K key : keys) { boolean include; double v = 0.0; Number n = this.dataset.getValue(key); if (n == null) { include = !this.ignoreNullValues; } else { v = n.doubleValue(); include = this.ignoreZeroValues ? v > 0.0 : v >= 0.0; } if (include) { runningTotal = runningTotal + v; // work out the mid angle (0 - 90 and 270 - 360) = right, // otherwise left double mid = this.startAngle + (this.direction.getFactor() * ((runningTotal - v / 2.0) * 360) / totalValue); if (Math.cos(Math.toRadians(mid)) < 0.0) { leftKeys.addValue(key, mid); } else { rightKeys.addValue(key, mid); } } } g2.setFont(getLabelFont()); // calculate the max label width from the plot dimensions, because // a circular pie can leave a lot more room for labels... double marginX = plotArea.getX(); double gap = plotArea.getWidth() * this.labelGap; double ww = linkArea.getX() - gap - marginX; float labelWidth = (float) this.labelPadding.trimWidth(ww); // draw the labels... if (this.labelGenerator != null) { drawLeftLabels(leftKeys, g2, plotArea, linkArea, labelWidth, state); drawRightLabels(rightKeys, g2, plotArea, linkArea, labelWidth, state); } g2.setComposite(originalComposite); } /** * Draws the left labels. * * @param leftKeys a collection of keys and angles (to the middle of the * section, in degrees) for the sections on the left side of the * plot. * @param g2 the graphics device. * @param plotArea the plot area. * @param linkArea the link area. * @param maxLabelWidth the maximum label width. * @param state the state. */ protected void drawLeftLabels(KeyedValues leftKeys, Graphics2D g2, Rectangle2D plotArea, Rectangle2D linkArea, float maxLabelWidth, PiePlotState state) { this.labelDistributor.clear(); double lGap = plotArea.getWidth() * this.labelGap; double verticalLinkRadius = state.getLinkArea().getHeight() / 2.0; for (int i = 0; i < leftKeys.getItemCount(); i++) { String label = this.labelGenerator.generateSectionLabel( this.dataset, leftKeys.getKey(i)); if (label != null) { TextBlock block = TextUtils.createTextBlock(label, this.labelFont, this.labelPaint, maxLabelWidth, new G2TextMeasurer(g2)); TextBox labelBox = new TextBox(block); labelBox.setBackgroundPaint(this.labelBackgroundPaint); labelBox.setOutlinePaint(this.labelOutlinePaint); labelBox.setOutlineStroke(this.labelOutlineStroke); if (this.shadowGenerator == null) { labelBox.setShadowPaint(this.labelShadowPaint); } else { labelBox.setShadowPaint(null); } labelBox.setInteriorGap(this.labelPadding); double theta = Math.toRadians( leftKeys.getValue(i).doubleValue()); double baseY = state.getPieCenterY() - Math.sin(theta) * verticalLinkRadius; double hh = labelBox.getHeight(g2); this.labelDistributor.addPieLabelRecord(new PieLabelRecord( leftKeys.getKey(i), theta, baseY, labelBox, hh, lGap / 2.0 + lGap / 2.0 * -Math.cos(theta), 1.0 - getLabelLinkDepth() + getExplodePercent(leftKeys.getKey(i)))); } } double hh = plotArea.getHeight(); double gap = hh * getInteriorGap(); this.labelDistributor.distributeLabels(plotArea.getMinY() + gap, hh - 2 * gap); for (int i = 0; i < this.labelDistributor.getItemCount(); i++) { drawLeftLabel(g2, state, this.labelDistributor.getPieLabelRecord(i)); } } /** * Draws the right labels. * * @param keys the keys. * @param g2 the graphics device. * @param plotArea the plot area. * @param linkArea the link area. * @param maxLabelWidth the maximum label width. * @param state the state. */ protected void drawRightLabels(KeyedValues keys, Graphics2D g2, Rectangle2D plotArea, Rectangle2D linkArea, float maxLabelWidth, PiePlotState state) { // draw the right labels... this.labelDistributor.clear(); double lGap = plotArea.getWidth() * this.labelGap; double verticalLinkRadius = state.getLinkArea().getHeight() / 2.0; for (int i = 0; i < keys.getItemCount(); i++) { String label = this.labelGenerator.generateSectionLabel( this.dataset, keys.getKey(i)); if (label != null) { TextBlock block = TextUtils.createTextBlock(label, this.labelFont, this.labelPaint, maxLabelWidth, new G2TextMeasurer(g2)); TextBox labelBox = new TextBox(block); labelBox.setBackgroundPaint(this.labelBackgroundPaint); labelBox.setOutlinePaint(this.labelOutlinePaint); labelBox.setOutlineStroke(this.labelOutlineStroke); if (this.shadowGenerator == null) { labelBox.setShadowPaint(this.labelShadowPaint); } else { labelBox.setShadowPaint(null); } labelBox.setInteriorGap(this.labelPadding); double theta = Math.toRadians(keys.getValue(i).doubleValue()); double baseY = state.getPieCenterY() - Math.sin(theta) * verticalLinkRadius; double hh = labelBox.getHeight(g2); this.labelDistributor.addPieLabelRecord(new PieLabelRecord( keys.getKey(i), theta, baseY, labelBox, hh, lGap / 2.0 + lGap / 2.0 * Math.cos(theta), 1.0 - getLabelLinkDepth() + getExplodePercent(keys.getKey(i)))); } } double hh = plotArea.getHeight(); double gap = 0.00; //hh * getInteriorGap(); this.labelDistributor.distributeLabels(plotArea.getMinY() + gap, hh - 2 * gap); for (int i = 0; i < this.labelDistributor.getItemCount(); i++) { drawRightLabel(g2, state, this.labelDistributor.getPieLabelRecord(i)); } } /** * Returns a collection of legend items for the pie chart. * * @return The legend items (never {@code null}). */ @Override public LegendItemCollection getLegendItems() { LegendItemCollection result = new LegendItemCollection(); if (this.dataset == null) { return result; } List keys = this.dataset.getKeys(); int section = 0; Shape shape = getLegendItemShape(); for (K key : keys) { Number n = this.dataset.getValue(key); boolean include; if (n == null) { include = !this.ignoreNullValues; } else { double v = n.doubleValue(); if (v == 0.0) { include = !this.ignoreZeroValues; } else { include = v > 0.0; } } if (include) { String label = this.legendLabelGenerator.generateSectionLabel( this.dataset, key); if (label != null) { String description = label; String toolTipText = null; if (this.legendLabelToolTipGenerator != null) { toolTipText = this.legendLabelToolTipGenerator .generateSectionLabel(this.dataset, key); } String urlText = null; if (this.legendLabelURLGenerator != null) { urlText = this.legendLabelURLGenerator.generateURL( this.dataset, key, this.pieIndex); } Paint paint = lookupSectionPaint(key); Paint outlinePaint = lookupSectionOutlinePaint(key); Stroke outlineStroke = lookupSectionOutlineStroke(key); LegendItem item = new LegendItem(label, description, toolTipText, urlText, true, shape, true, paint, true, outlinePaint, outlineStroke, false, // line not visible new Line2D.Float(), new BasicStroke(), Color.BLACK); item.setDataset(getDataset()); item.setSeriesIndex(this.dataset.getIndex(key)); item.setSeriesKey(key); result.add(item); } section++; } else { section++; } } return result; } /** * Returns a short string describing the type of plot. * * @return The plot type. */ @Override public String getPlotType() { return localizationResources.getString("Pie_Plot"); } /** * Returns a rectangle that can be used to create a pie section (taking * into account the amount by which the pie section is 'exploded'). * * @param unexploded the area inside which the unexploded pie sections are * drawn. * @param exploded the area inside which the exploded pie sections are * drawn. * @param angle the start angle. * @param extent the extent of the arc. * @param explodePercent the amount by which the pie section is exploded. * * @return A rectangle that can be used to create a pie section. */ protected Rectangle2D getArcBounds(Rectangle2D unexploded, Rectangle2D exploded, double angle, double extent, double explodePercent) { if (explodePercent == 0.0) { return unexploded; } Arc2D arc1 = new Arc2D.Double(unexploded, angle, extent / 2, Arc2D.OPEN); Point2D point1 = arc1.getEndPoint(); Arc2D.Double arc2 = new Arc2D.Double(exploded, angle, extent / 2, Arc2D.OPEN); Point2D point2 = arc2.getEndPoint(); double deltaX = (point1.getX() - point2.getX()) * explodePercent; double deltaY = (point1.getY() - point2.getY()) * explodePercent; return new Rectangle2D.Double(unexploded.getX() - deltaX, unexploded.getY() - deltaY, unexploded.getWidth(), unexploded.getHeight()); } /** * Draws a section label on the left side of the pie chart. * * @param g2 the graphics device. * @param state the state. * @param record the label record. */ protected void drawLeftLabel(Graphics2D g2, PiePlotState state, PieLabelRecord record) { double anchorX = state.getLinkArea().getMinX(); double targetX = anchorX - record.getGap(); double targetY = record.getAllocatedY(); if (this.labelLinksVisible) { double theta = record.getAngle(); double linkX = state.getPieCenterX() + Math.cos(theta) * state.getPieWRadius() * record.getLinkPercent(); double linkY = state.getPieCenterY() - Math.sin(theta) * state.getPieHRadius() * record.getLinkPercent(); double elbowX = state.getPieCenterX() + Math.cos(theta) * state.getLinkArea().getWidth() / 2.0; double elbowY = state.getPieCenterY() - Math.sin(theta) * state.getLinkArea().getHeight() / 2.0; double anchorY = elbowY; g2.setPaint(this.labelLinkPaint); g2.setStroke(this.labelLinkStroke); PieLabelLinkStyle style = getLabelLinkStyle(); if (style.equals(PieLabelLinkStyle.STANDARD)) { g2.draw(new Line2D.Double(linkX, linkY, elbowX, elbowY)); g2.draw(new Line2D.Double(anchorX, anchorY, elbowX, elbowY)); g2.draw(new Line2D.Double(anchorX, anchorY, targetX, targetY)); } else if (style.equals(PieLabelLinkStyle.QUAD_CURVE)) { QuadCurve2D q = new QuadCurve2D.Float(); q.setCurve(targetX, targetY, anchorX, anchorY, elbowX, elbowY); g2.draw(q); g2.draw(new Line2D.Double(elbowX, elbowY, linkX, linkY)); } else if (style.equals(PieLabelLinkStyle.CUBIC_CURVE)) { CubicCurve2D c = new CubicCurve2D .Float(); c.setCurve(targetX, targetY, anchorX, anchorY, elbowX, elbowY, linkX, linkY); g2.draw(c); } } TextBox tb = record.getLabel(); tb.draw(g2, (float) targetX, (float) targetY, RectangleAnchor.RIGHT); } /** * Draws a section label on the right side of the pie chart. * * @param g2 the graphics device. * @param state the state. * @param record the label record. */ protected void drawRightLabel(Graphics2D g2, PiePlotState state, PieLabelRecord record) { double anchorX = state.getLinkArea().getMaxX(); double targetX = anchorX + record.getGap(); double targetY = record.getAllocatedY(); if (this.labelLinksVisible) { double theta = record.getAngle(); double linkX = state.getPieCenterX() + Math.cos(theta) * state.getPieWRadius() * record.getLinkPercent(); double linkY = state.getPieCenterY() - Math.sin(theta) * state.getPieHRadius() * record.getLinkPercent(); double elbowX = state.getPieCenterX() + Math.cos(theta) * state.getLinkArea().getWidth() / 2.0; double elbowY = state.getPieCenterY() - Math.sin(theta) * state.getLinkArea().getHeight() / 2.0; double anchorY = elbowY; g2.setPaint(this.labelLinkPaint); g2.setStroke(this.labelLinkStroke); PieLabelLinkStyle style = getLabelLinkStyle(); if (style.equals(PieLabelLinkStyle.STANDARD)) { g2.draw(new Line2D.Double(linkX, linkY, elbowX, elbowY)); g2.draw(new Line2D.Double(anchorX, anchorY, elbowX, elbowY)); g2.draw(new Line2D.Double(anchorX, anchorY, targetX, targetY)); } else if (style.equals(PieLabelLinkStyle.QUAD_CURVE)) { QuadCurve2D q = new QuadCurve2D.Float(); q.setCurve(targetX, targetY, anchorX, anchorY, elbowX, elbowY); g2.draw(q); g2.draw(new Line2D.Double(elbowX, elbowY, linkX, linkY)); } else if (style.equals(PieLabelLinkStyle.CUBIC_CURVE)) { CubicCurve2D c = new CubicCurve2D .Float(); c.setCurve(targetX, targetY, anchorX, anchorY, elbowX, elbowY, linkX, linkY); g2.draw(c); } } TextBox tb = record.getLabel(); tb.draw(g2, (float) targetX, (float) targetY, RectangleAnchor.LEFT); } /** * Returns the center for the specified section. * Checks to see if the section is exploded and recalculates the * new center if so. * * @param state PiePlotState * @param key section key. * * @return The center for the specified section. */ protected Point2D getArcCenter(PiePlotState state, K key) { Point2D center = new Point2D.Double(state.getPieCenterX(), state .getPieCenterY()); double ep = getExplodePercent(key); double mep = getMaximumExplodePercent(); if (mep > 0.0) { ep = ep / mep; } if (ep != 0) { Rectangle2D pieArea = state.getPieArea(); Rectangle2D expPieArea = state.getExplodedPieArea(); double angle1, angle2; Number n = this.dataset.getValue(key); double value = n.doubleValue(); if (this.direction == Rotation.CLOCKWISE) { angle1 = state.getLatestAngle(); angle2 = angle1 - value / state.getTotal() * 360.0; } else if (this.direction == Rotation.ANTICLOCKWISE) { angle1 = state.getLatestAngle(); angle2 = angle1 + value / state.getTotal() * 360.0; } else { throw new IllegalStateException("Rotation type not recognised."); } double angle = (angle2 - angle1); Arc2D arc1 = new Arc2D.Double(pieArea, angle1, angle / 2, Arc2D.OPEN); Point2D point1 = arc1.getEndPoint(); Arc2D.Double arc2 = new Arc2D.Double(expPieArea, angle1, angle / 2, Arc2D.OPEN); Point2D point2 = arc2.getEndPoint(); double deltaX = (point1.getX() - point2.getX()) * ep; double deltaY = (point1.getY() - point2.getY()) * ep; center = new Point2D.Double(state.getPieCenterX() - deltaX, state.getPieCenterY() - deltaY); } return center; } /** * Returns the paint for the specified section. This is equivalent to * {@code lookupSectionPaint(section)}. Checks to see if the user set the * {@code Paint} to be of type {@code RadialGradientPaint} and if so it * adjusts the center and radius to match the Pie. * * @param key the section key. * @param state PiePlotState. * * @return The paint for the specified section. */ protected Paint lookupSectionPaint(K key, PiePlotState state) { Paint paint = lookupSectionPaint(key, getAutoPopulateSectionPaint()); // for a RadialGradientPaint we adjust the center and radius to match // the current pie segment... if (paint instanceof RadialGradientPaint) { RadialGradientPaint rgp = (RadialGradientPaint) paint; Point2D center = getArcCenter(state, key); float radius = (float) Math.max(state.getPieHRadius(), state.getPieWRadius()); float[] fractions = rgp.getFractions(); Color[] colors = rgp.getColors(); paint = new RadialGradientPaint(center, radius, fractions, colors); } return paint; } /** * Tests this plot for equality with an arbitrary object. Note that the * plot's dataset is NOT included in the test for equality. * * @param obj the object to test against ({@code null} permitted). * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof PiePlot)) { return false; } if (!super.equals(obj)) { return false; } PiePlot that = (PiePlot) obj; if (this.pieIndex != that.pieIndex) { return false; } if (this.interiorGap != that.interiorGap) { return false; } if (this.circular != that.circular) { return false; } if (this.startAngle != that.startAngle) { return false; } if (this.direction != that.direction) { return false; } if (this.ignoreZeroValues != that.ignoreZeroValues) { return false; } if (this.ignoreNullValues != that.ignoreNullValues) { return false; } if (!Objects.equals(this.sectionPaintMap, that.sectionPaintMap)) { return false; } if (!PaintUtils.equal(this.defaultSectionPaint, that.defaultSectionPaint)) { return false; } if (this.sectionOutlinesVisible != that.sectionOutlinesVisible) { return false; } if (!Objects.equals(this.sectionOutlinePaintMap, that.sectionOutlinePaintMap)) { return false; } if (!PaintUtils.equal(this.defaultSectionOutlinePaint, that.defaultSectionOutlinePaint)) { return false; } if (!Objects.equals(this.sectionOutlineStrokeMap, that.sectionOutlineStrokeMap)) { return false; } if (!Objects.equals(this.defaultSectionOutlineStroke, that.defaultSectionOutlineStroke)) { return false; } if (!PaintUtils.equal(this.shadowPaint, that.shadowPaint)) { return false; } if (!(this.shadowXOffset == that.shadowXOffset)) { return false; } if (!(this.shadowYOffset == that.shadowYOffset)) { return false; } if (!Objects.equals(this.explodePercentages, that.explodePercentages)) { return false; } if (!Objects.equals(this.labelGenerator, that.labelGenerator)) { return false; } if (!Objects.equals(this.labelFont, that.labelFont)) { return false; } if (!PaintUtils.equal(this.labelPaint, that.labelPaint)) { return false; } if (!PaintUtils.equal(this.labelBackgroundPaint, that.labelBackgroundPaint)) { return false; } if (!PaintUtils.equal(this.labelOutlinePaint, that.labelOutlinePaint)) { return false; } if (!Objects.equals(this.labelOutlineStroke, that.labelOutlineStroke)) { return false; } if (!PaintUtils.equal(this.labelShadowPaint, that.labelShadowPaint)) { return false; } if (this.simpleLabels != that.simpleLabels) { return false; } if (!this.simpleLabelOffset.equals(that.simpleLabelOffset)) { return false; } if (!this.labelPadding.equals(that.labelPadding)) { return false; } if (!(this.maximumLabelWidth == that.maximumLabelWidth)) { return false; } if (!(this.labelGap == that.labelGap)) { return false; } if (!(this.labelLinkMargin == that.labelLinkMargin)) { return false; } if (this.labelLinksVisible != that.labelLinksVisible) { return false; } if (!this.labelLinkStyle.equals(that.labelLinkStyle)) { return false; } if (!PaintUtils.equal(this.labelLinkPaint, that.labelLinkPaint)) { return false; } if (!Objects.equals(this.labelLinkStroke, that.labelLinkStroke)) { return false; } if (!Objects.equals(this.toolTipGenerator, that.toolTipGenerator)) { return false; } if (!Objects.equals(this.urlGenerator, that.urlGenerator)) { return false; } if (!(this.minimumArcAngleToDraw == that.minimumArcAngleToDraw)) { return false; } if (!ShapeUtils.equal(this.legendItemShape, that.legendItemShape)) { return false; } if (!Objects.equals(this.legendLabelGenerator, that.legendLabelGenerator)) { return false; } if (!Objects.equals(this.legendLabelToolTipGenerator, that.legendLabelToolTipGenerator)) { return false; } if (!Objects.equals(this.legendLabelURLGenerator, that.legendLabelURLGenerator)) { return false; } if (this.autoPopulateSectionPaint != that.autoPopulateSectionPaint) { return false; } if (this.autoPopulateSectionOutlinePaint != that.autoPopulateSectionOutlinePaint) { return false; } if (this.autoPopulateSectionOutlineStroke != that.autoPopulateSectionOutlineStroke) { return false; } if (!Objects.equals(this.shadowGenerator, that.shadowGenerator)) { return false; } // can't find any difference... return true; } /** * Generates a hashcode. Note that, as with the equals method, the dataset * is NOT included in the hashcode. * * @return the hashcode */ @Override public int hashCode() { int hash = 7; hash = 73 * hash + this.pieIndex; hash = 73 * hash + (int) (Double.doubleToLongBits(this.interiorGap) ^ (Double.doubleToLongBits(this.interiorGap) >>> 32)); hash = 73 * hash + (this.circular ? 1 : 0); hash = 73 * hash + (int) (Double.doubleToLongBits(this.startAngle) ^ (Double.doubleToLongBits(this.startAngle) >>> 32)); hash = 73 * hash + Objects.hashCode(this.direction); hash = 73 * hash + Objects.hashCode(this.sectionPaintMap); hash = 73 * hash + Objects.hashCode(this.defaultSectionPaint); hash = 73 * hash + (this.autoPopulateSectionPaint ? 1 : 0); hash = 73 * hash + (this.sectionOutlinesVisible ? 1 : 0); hash = 73 * hash + Objects.hashCode(this.sectionOutlinePaintMap); hash = 73 * hash + Objects.hashCode(this.defaultSectionOutlinePaint); hash = 73 * hash + (this.autoPopulateSectionOutlinePaint ? 1 : 0); hash = 73 * hash + Objects.hashCode(this.sectionOutlineStrokeMap); hash = 73 * hash + Objects.hashCode(this.defaultSectionOutlineStroke); hash = 73 * hash + (this.autoPopulateSectionOutlineStroke ? 1 : 0); hash = 73 * hash + Objects.hashCode(this.shadowPaint); hash = 73 * hash + (int) (Double.doubleToLongBits(this.shadowXOffset) ^ (Double.doubleToLongBits(this.shadowXOffset) >>> 32)); hash = 73 * hash + (int) (Double.doubleToLongBits(this.shadowYOffset) ^ (Double.doubleToLongBits(this.shadowYOffset) >>> 32)); hash = 73 * hash + Objects.hashCode(this.explodePercentages); hash = 73 * hash + Objects.hashCode(this.labelGenerator); hash = 73 * hash + Objects.hashCode(this.labelFont); hash = 73 * hash + Objects.hashCode(this.labelPaint); hash = 73 * hash + Objects.hashCode(this.labelBackgroundPaint); hash = 73 * hash + Objects.hashCode(this.labelOutlinePaint); hash = 73 * hash + Objects.hashCode(this.labelOutlineStroke); hash = 73 * hash + Objects.hashCode(this.labelShadowPaint); hash = 73 * hash + (this.simpleLabels ? 1 : 0); hash = 73 * hash + Objects.hashCode(this.labelPadding); hash = 73 * hash + Objects.hashCode(this.simpleLabelOffset); hash = 73 * hash + (int) (Double.doubleToLongBits(this.maximumLabelWidth) ^ (Double.doubleToLongBits(this.maximumLabelWidth) >>> 32)); hash = 73 * hash + (int) (Double.doubleToLongBits(this.labelGap) ^ (Double.doubleToLongBits(this.labelGap) >>> 32)); hash = 73 * hash + (this.labelLinksVisible ? 1 : 0); hash = 73 * hash + Objects.hashCode(this.labelLinkStyle); hash = 73 * hash + (int) (Double.doubleToLongBits(this.labelLinkMargin) ^ (Double.doubleToLongBits(this.labelLinkMargin) >>> 32)); hash = 73 * hash + Objects.hashCode(this.labelLinkPaint); hash = 73 * hash + Objects.hashCode(this.labelLinkStroke); hash = 73 * hash + Objects.hashCode(this.toolTipGenerator); hash = 73 * hash + Objects.hashCode(this.urlGenerator); hash = 73 * hash + Objects.hashCode(this.legendLabelGenerator); hash = 73 * hash + Objects.hashCode(this.legendLabelToolTipGenerator); hash = 73 * hash + Objects.hashCode(this.legendLabelURLGenerator); hash = 73 * hash + (this.ignoreNullValues ? 1 : 0); hash = 73 * hash + (this.ignoreZeroValues ? 1 : 0); hash = 73 * hash + Objects.hashCode(this.legendItemShape); hash = 73 * hash + (int) (Double.doubleToLongBits(this.minimumArcAngleToDraw) ^ (Double.doubleToLongBits(this.minimumArcAngleToDraw) >>> 32)); hash = 73 * hash + Objects.hashCode(this.shadowGenerator); return hash; } /** * Returns a clone of the plot. * * @return A clone. * * @throws CloneNotSupportedException if some component of the plot does * not support cloning. */ @Override public Object clone() throws CloneNotSupportedException { PiePlot clone = (PiePlot) super.clone(); clone.sectionPaintMap = (PaintMap) this.sectionPaintMap.clone(); clone.sectionOutlinePaintMap = (PaintMap) this.sectionOutlinePaintMap.clone(); clone.sectionOutlineStrokeMap = (StrokeMap) this.sectionOutlineStrokeMap.clone(); clone.explodePercentages = new TreeMap<>(this.explodePercentages); if (this.labelGenerator != null) { clone.labelGenerator = (PieSectionLabelGenerator) ObjectUtils.clone(this.labelGenerator); } if (clone.dataset != null) { clone.dataset.addChangeListener(clone); } if (this.urlGenerator instanceof PublicCloneable) { clone.urlGenerator = (PieURLGenerator) ObjectUtils.clone( this.urlGenerator); } clone.legendItemShape = ShapeUtils.clone(this.legendItemShape); if (this.legendLabelGenerator != null) { clone.legendLabelGenerator = (PieSectionLabelGenerator) ObjectUtils.clone(this.legendLabelGenerator); } if (this.legendLabelToolTipGenerator != null) { clone.legendLabelToolTipGenerator = (PieSectionLabelGenerator) ObjectUtils.clone(this.legendLabelToolTipGenerator); } if (this.legendLabelURLGenerator instanceof PublicCloneable) { clone.legendLabelURLGenerator = (PieURLGenerator) ObjectUtils.clone(this.legendLabelURLGenerator); } return clone; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.defaultSectionPaint, stream); SerialUtils.writePaint(this.defaultSectionOutlinePaint, stream); SerialUtils.writeStroke(this.defaultSectionOutlineStroke, stream); SerialUtils.writePaint(this.shadowPaint, stream); SerialUtils.writePaint(this.labelPaint, stream); SerialUtils.writePaint(this.labelBackgroundPaint, stream); SerialUtils.writePaint(this.labelOutlinePaint, stream); SerialUtils.writeStroke(this.labelOutlineStroke, stream); SerialUtils.writePaint(this.labelShadowPaint, stream); SerialUtils.writePaint(this.labelLinkPaint, stream); SerialUtils.writeStroke(this.labelLinkStroke, stream); SerialUtils.writeShape(this.legendItemShape, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.defaultSectionPaint = SerialUtils.readPaint(stream); this.defaultSectionOutlinePaint = SerialUtils.readPaint(stream); this.defaultSectionOutlineStroke = SerialUtils.readStroke(stream); this.shadowPaint = SerialUtils.readPaint(stream); this.labelPaint = SerialUtils.readPaint(stream); this.labelBackgroundPaint = SerialUtils.readPaint(stream); this.labelOutlinePaint = SerialUtils.readPaint(stream); this.labelOutlineStroke = SerialUtils.readStroke(stream); this.labelShadowPaint = SerialUtils.readPaint(stream); this.labelLinkPaint = SerialUtils.readPaint(stream); this.labelLinkStroke = SerialUtils.readStroke(stream); this.legendItemShape = SerialUtils.readShape(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/PiePlot3D.java000066400000000000000000001154051463604235500264500ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * PiePlot3D.java * -------------- * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Original Author: Tomer Peretz; * Contributor(s): Richard Atkinson; * David Gilbert; * Xun Kang; * Christian W. Zuckschwerdt; * Arnaud Lelievre; * Dave Crane; * Martin Hoeller; * DaveLaw (dave ATT davelaw DOTT de); */ package org.jfree.chart.plot; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Composite; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Polygon; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Arc2D; import java.awt.geom.Area; import java.awt.geom.Ellipse2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.Serializable; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.entity.PieSectionEntity; import org.jfree.chart.event.PlotChangeEvent; import org.jfree.chart.labels.PieToolTipGenerator; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.util.PaintAlpha; import org.jfree.data.general.DatasetUtils; import org.jfree.data.general.PieDataset; /** * A plot that displays data in the form of a 3D pie chart, using data from * any class that implements the {@link PieDataset} interface. *

* Although this class extends {@link PiePlot}, it does not currently support * exploded sections. * * @deprecated For 3D pie charts, use Orson Charts (https://github.com/jfree/orson-charts). */ public class PiePlot3D extends PiePlot implements Serializable { /** For serialization. */ private static final long serialVersionUID = 3408984188945161432L; /** The factor of the depth of the pie from the plot height */ private double depthFactor = 0.12; /** * A flag that controls whether or not the sides of the pie chart * are rendered using a darker colour. */ private boolean darkerSides = false; // default preserves previous behaviour /** * Creates a new instance with no dataset. */ public PiePlot3D() { this(null); } /** * Creates a pie chart with a three dimensional effect using the specified * dataset. * * @param dataset the dataset ({@code null} permitted). */ public PiePlot3D(PieDataset dataset) { super(dataset); setCircular(false, false); } /** * Returns the depth factor for the chart. * * @return The depth factor. * * @see #setDepthFactor(double) */ public double getDepthFactor() { return this.depthFactor; } /** * Sets the pie depth as a percentage of the height of the plot area, and * sends a {@link PlotChangeEvent} to all registered listeners. * * @param factor the depth factor (for example, 0.20 is twenty percent). * * @see #getDepthFactor() */ public void setDepthFactor(double factor) { this.depthFactor = factor; fireChangeEvent(); } /** * Returns a flag that controls whether or not the sides of the pie chart * are rendered using a darker colour. * * @return A boolean. * * @see #setDarkerSides(boolean) */ public boolean getDarkerSides() { return this.darkerSides; } /** * Sets a flag that controls whether or not the sides of the pie chart * are rendered using a darker colour, and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param darker true to darken the sides, false to use the default * behaviour. * * @see #getDarkerSides() */ public void setDarkerSides(boolean darker) { this.darkerSides = darker; fireChangeEvent(); } /** * Draws the plot on a Java 2D graphics device (such as the screen or a * printer). This method is called by the * {@link org.jfree.chart.JFreeChart} class, you don't normally need * to call it yourself. * * @param g2 the graphics device. * @param plotArea the area within which the plot should be drawn. * @param anchor the anchor point. * @param parentState the state from the parent plot, if there is one. * @param info collects info about the drawing * ({@code null} permitted). */ @Override public void draw(Graphics2D g2, Rectangle2D plotArea, Point2D anchor, PlotState parentState, PlotRenderingInfo info) { // adjust for insets... RectangleInsets insets = getInsets(); insets.trim(plotArea); Rectangle2D originalPlotArea = (Rectangle2D) plotArea.clone(); if (info != null) { info.setPlotArea(plotArea); info.setDataArea(plotArea); } drawBackground(g2, plotArea); Shape savedClip = g2.getClip(); g2.clip(plotArea); Graphics2D savedG2 = g2; BufferedImage dataImage = null; if (getShadowGenerator() != null) { dataImage = new BufferedImage((int) plotArea.getWidth(), (int) plotArea.getHeight(), BufferedImage.TYPE_INT_ARGB); g2 = dataImage.createGraphics(); g2.translate(-plotArea.getX(), -plotArea.getY()); g2.setRenderingHints(savedG2.getRenderingHints()); originalPlotArea = (Rectangle2D) plotArea.clone(); } // adjust the plot area by the interior spacing value double gapPercent = getInteriorGap(); double labelPercent = 0.0; if (getLabelGenerator() != null) { labelPercent = getLabelGap() + getMaximumLabelWidth(); } double gapHorizontal = plotArea.getWidth() * (gapPercent + labelPercent) * 2.0; double gapVertical = plotArea.getHeight() * gapPercent * 2.0; if (DEBUG_DRAW_INTERIOR) { double hGap = plotArea.getWidth() * getInteriorGap(); double vGap = plotArea.getHeight() * getInteriorGap(); double igx1 = plotArea.getX() + hGap; double igx2 = plotArea.getMaxX() - hGap; double igy1 = plotArea.getY() + vGap; double igy2 = plotArea.getMaxY() - vGap; g2.setPaint(Color.LIGHT_GRAY); g2.draw(new Rectangle2D.Double(igx1, igy1, igx2 - igx1, igy2 - igy1)); } double linkX = plotArea.getX() + gapHorizontal / 2; double linkY = plotArea.getY() + gapVertical / 2; double linkW = plotArea.getWidth() - gapHorizontal; double linkH = plotArea.getHeight() - gapVertical; // make the link area a square if the pie chart is to be circular... if (isCircular()) { // is circular? double min = Math.min(linkW, linkH) / 2; linkX = (linkX + linkX + linkW) / 2 - min; linkY = (linkY + linkY + linkH) / 2 - min; linkW = 2 * min; linkH = 2 * min; } PiePlotState state = initialise(g2, plotArea, this, null, info); // the link area defines the dog leg points for the linking lines to // the labels Rectangle2D linkAreaXX = new Rectangle2D.Double(linkX, linkY, linkW, linkH * (1 - this.depthFactor)); state.setLinkArea(linkAreaXX); if (DEBUG_DRAW_LINK_AREA) { g2.setPaint(Color.BLUE); g2.draw(linkAreaXX); g2.setPaint(Color.YELLOW); g2.draw(new Ellipse2D.Double(linkAreaXX.getX(), linkAreaXX.getY(), linkAreaXX.getWidth(), linkAreaXX.getHeight())); } // the explode area defines the max circle/ellipse for the exploded pie // sections. // it is defined by shrinking the linkArea by the linkMargin factor. double hh = linkW * getLabelLinkMargin(); double vv = linkH * getLabelLinkMargin(); Rectangle2D explodeArea = new Rectangle2D.Double(linkX + hh / 2.0, linkY + vv / 2.0, linkW - hh, linkH - vv); state.setExplodedPieArea(explodeArea); // the pie area defines the circle/ellipse for regular pie sections. // it is defined by shrinking the explodeArea by the explodeMargin // factor. double maximumExplodePercent = getMaximumExplodePercent(); double percent = maximumExplodePercent / (1.0 + maximumExplodePercent); double h1 = explodeArea.getWidth() * percent; double v1 = explodeArea.getHeight() * percent; Rectangle2D pieArea = new Rectangle2D.Double(explodeArea.getX() + h1 / 2.0, explodeArea.getY() + v1 / 2.0, explodeArea.getWidth() - h1, explodeArea.getHeight() - v1); // the link area defines the dog-leg point for the linking lines to // the labels int depth = (int) (pieArea.getHeight() * this.depthFactor); Rectangle2D linkArea = new Rectangle2D.Double(linkX, linkY, linkW, linkH - depth); state.setLinkArea(linkArea); state.setPieArea(pieArea); state.setPieCenterX(pieArea.getCenterX()); state.setPieCenterY(pieArea.getCenterY() - depth / 2.0); state.setPieWRadius(pieArea.getWidth() / 2.0); state.setPieHRadius((pieArea.getHeight() - depth) / 2.0); // get the data source - return if null; PieDataset dataset = getDataset(); if (DatasetUtils.isEmptyOrNull(getDataset())) { drawNoDataMessage(g2, plotArea); g2.setClip(savedClip); drawOutline(g2, plotArea); return; } // if too any elements if (dataset.getKeys().size() > plotArea.getWidth()) { String text = localizationResources.getString("Too_many_elements"); Font sfont = new Font("dialog", Font.BOLD, 10); g2.setFont(sfont); FontMetrics fm = g2.getFontMetrics(sfont); int stringWidth = fm.stringWidth(text); g2.drawString(text, (int) (plotArea.getX() + (plotArea.getWidth() - stringWidth) / 2), (int) (plotArea.getY() + (plotArea.getHeight() / 2))); return; } // if we are drawing a perfect circle, we need to readjust the top left // coordinates of the drawing area for the arcs to arrive at this // effect. if (isCircular()) { double min = Math.min(plotArea.getWidth(), plotArea.getHeight()) / 2; plotArea = new Rectangle2D.Double(plotArea.getCenterX() - min, plotArea.getCenterY() - min, 2 * min, 2 * min); } // get a list of keys... List sectionKeys = dataset.getKeys(); if (sectionKeys.isEmpty()) { return; } // establish the coordinates of the top left corner of the drawing area double arcX = pieArea.getX(); double arcY = pieArea.getY(); //g2.clip(clipArea); Composite originalComposite = g2.getComposite(); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, getForegroundAlpha())); double totalValue = DatasetUtils.calculatePieDatasetTotal(dataset); double runningTotal = 0; if (depth < 0) { return; // if depth is negative don't draw anything } ArrayList arcList = new ArrayList(); Arc2D.Double arc; Paint paint; Paint outlinePaint; Stroke outlineStroke; Iterator iterator = sectionKeys.iterator(); while (iterator.hasNext()) { Comparable currentKey = (Comparable) iterator.next(); Number dataValue = dataset.getValue(currentKey); if (dataValue == null) { arcList.add(null); continue; } double value = dataValue.doubleValue(); if (value <= 0) { arcList.add(null); continue; } double startAngle = getStartAngle(); double direction = getDirection().getFactor(); double angle1 = startAngle + (direction * (runningTotal * 360)) / totalValue; double angle2 = startAngle + (direction * (runningTotal + value) * 360) / totalValue; if (Math.abs(angle2 - angle1) > getMinimumArcAngleToDraw()) { arcList.add(new Arc2D.Double(arcX, arcY + depth, pieArea.getWidth(), pieArea.getHeight() - depth, angle1, angle2 - angle1, Arc2D.PIE)); } else { arcList.add(null); } runningTotal += value; } Shape oldClip = g2.getClip(); Ellipse2D top = new Ellipse2D.Double(pieArea.getX(), pieArea.getY(), pieArea.getWidth(), pieArea.getHeight() - depth); Ellipse2D bottom = new Ellipse2D.Double(pieArea.getX(), pieArea.getY() + depth, pieArea.getWidth(), pieArea.getHeight() - depth); Rectangle2D lower = new Rectangle2D.Double(top.getX(), top.getCenterY(), pieArea.getWidth(), bottom.getMaxY() - top.getCenterY()); Rectangle2D upper = new Rectangle2D.Double(pieArea.getX(), top.getY(), pieArea.getWidth(), bottom.getCenterY() - top.getY()); Area a = new Area(top); a.add(new Area(lower)); Area b = new Area(bottom); b.add(new Area(upper)); Area pie = new Area(a); pie.intersect(b); Area front = new Area(pie); front.subtract(new Area(top)); Area back = new Area(pie); back.subtract(new Area(bottom)); // draw the bottom circle int[] xs; int[] ys; int categoryCount = arcList.size(); for (int categoryIndex = 0; categoryIndex < categoryCount; categoryIndex++) { arc = (Arc2D.Double) arcList.get(categoryIndex); if (arc == null) { continue; } Comparable key = getSectionKey(categoryIndex); paint = lookupSectionPaint(key); outlinePaint = lookupSectionOutlinePaint(key); outlineStroke = lookupSectionOutlineStroke(key); g2.setPaint(paint); g2.fill(arc); g2.setPaint(outlinePaint); g2.setStroke(outlineStroke); g2.draw(arc); g2.setPaint(paint); Point2D p1 = arc.getStartPoint(); // draw the height xs = new int[] {(int) arc.getCenterX(), (int) arc.getCenterX(), (int) p1.getX(), (int) p1.getX()}; ys = new int[] {(int) arc.getCenterY(), (int) arc.getCenterY() - depth, (int) p1.getY() - depth, (int) p1.getY()}; Polygon polygon = new Polygon(xs, ys, 4); g2.setPaint(java.awt.Color.LIGHT_GRAY); g2.fill(polygon); g2.setPaint(outlinePaint); g2.setStroke(outlineStroke); g2.draw(polygon); g2.setPaint(paint); } g2.setPaint(Color.GRAY); g2.fill(back); g2.fill(front); // cycle through once drawing only the sides at the back... int cat = 0; iterator = arcList.iterator(); while (iterator.hasNext()) { Arc2D segment = (Arc2D) iterator.next(); if (segment != null) { Comparable key = getSectionKey(cat); paint = lookupSectionPaint(key); outlinePaint = lookupSectionOutlinePaint(key); outlineStroke = lookupSectionOutlineStroke(key); drawSide(g2, pieArea, segment, front, back, paint, outlinePaint, outlineStroke, false, true); } cat++; } // cycle through again drawing only the sides at the front... cat = 0; iterator = arcList.iterator(); while (iterator.hasNext()) { Arc2D segment = (Arc2D) iterator.next(); if (segment != null) { Comparable key = getSectionKey(cat); paint = lookupSectionPaint(key); outlinePaint = lookupSectionOutlinePaint(key); outlineStroke = lookupSectionOutlineStroke(key); drawSide(g2, pieArea, segment, front, back, paint, outlinePaint, outlineStroke, true, false); } cat++; } g2.setClip(oldClip); // draw the sections at the top of the pie (and set up tooltips)... Arc2D upperArc; for (int sectionIndex = 0; sectionIndex < categoryCount; sectionIndex++) { arc = (Arc2D.Double) arcList.get(sectionIndex); if (arc == null) { continue; } upperArc = new Arc2D.Double(arcX, arcY, pieArea.getWidth(), pieArea.getHeight() - depth, arc.getAngleStart(), arc.getAngleExtent(), Arc2D.PIE); Comparable currentKey = (Comparable) sectionKeys.get(sectionIndex); paint = lookupSectionPaint(currentKey, true); outlinePaint = lookupSectionOutlinePaint(currentKey); outlineStroke = lookupSectionOutlineStroke(currentKey); g2.setPaint(paint); g2.fill(upperArc); g2.setStroke(outlineStroke); g2.setPaint(outlinePaint); g2.draw(upperArc); // add a tooltip for the section... if (info != null) { EntityCollection entities = info.getOwner().getEntityCollection(); if (entities != null) { String tip = null; PieToolTipGenerator tipster = getToolTipGenerator(); if (tipster != null) { // @mgs: using the method's return value was missing tip = tipster.generateToolTip(dataset, currentKey); } String url = null; if (getURLGenerator() != null) { url = getURLGenerator().generateURL(dataset, currentKey, getPieIndex()); } PieSectionEntity entity = new PieSectionEntity( upperArc, dataset, getPieIndex(), sectionIndex, currentKey, tip, url); entities.add(entity); } } } List keys = dataset.getKeys(); Rectangle2D adjustedPlotArea = new Rectangle2D.Double( originalPlotArea.getX(), originalPlotArea.getY(), originalPlotArea.getWidth(), originalPlotArea.getHeight() - depth); if (getSimpleLabels()) { drawSimpleLabels(g2, keys, totalValue, adjustedPlotArea, linkArea, state); } else { drawLabels(g2, keys, totalValue, adjustedPlotArea, linkArea, state); } if (getShadowGenerator() != null) { BufferedImage shadowImage = getShadowGenerator().createDropShadow(dataImage); g2 = savedG2; g2.drawImage(shadowImage, (int) plotArea.getX() + getShadowGenerator().calculateOffsetX(), (int) plotArea.getY() + getShadowGenerator().calculateOffsetY(), null); g2.drawImage(dataImage, (int) plotArea.getX(), (int) plotArea.getY(), null); } g2.setClip(savedClip); g2.setComposite(originalComposite); drawOutline(g2, originalPlotArea); } /** * Draws the side of a pie section. * * @param g2 the graphics device. * @param plotArea the plot area. * @param arc the arc. * @param front the front of the pie. * @param back the back of the pie. * @param paint the color. * @param outlinePaint the outline paint. * @param outlineStroke the outline stroke. * @param drawFront draw the front? * @param drawBack draw the back? */ protected void drawSide(Graphics2D g2, Rectangle2D plotArea, Arc2D arc, Area front, Area back, Paint paint, Paint outlinePaint, Stroke outlineStroke, boolean drawFront, boolean drawBack) { if (getDarkerSides()) { paint = PaintAlpha.darker(paint); } double start = arc.getAngleStart(); double extent = arc.getAngleExtent(); double end = start + extent; g2.setStroke(outlineStroke); // for CLOCKWISE charts, the extent will be negative... if (extent < 0.0) { if (isAngleAtFront(start)) { // start at front if (!isAngleAtBack(end)) { if (extent > -180.0) { // the segment is entirely at the // front of the chart if (drawFront) { Area side = new Area(new Rectangle2D.Double( arc.getEndPoint().getX(), plotArea.getY(), arc.getStartPoint().getX() - arc.getEndPoint().getX(), plotArea.getHeight())); side.intersect(front); g2.setPaint(paint); g2.fill(side); g2.setPaint(outlinePaint); g2.draw(side); } } else { // the segment starts at the front, and wraps all // the way around // the back and finishes at the front again Area side1 = new Area(new Rectangle2D.Double( plotArea.getX(), plotArea.getY(), arc.getStartPoint().getX() - plotArea.getX(), plotArea.getHeight())); side1.intersect(front); Area side2 = new Area(new Rectangle2D.Double( arc.getEndPoint().getX(), plotArea.getY(), plotArea.getMaxX() - arc.getEndPoint().getX(), plotArea.getHeight())); side2.intersect(front); g2.setPaint(paint); if (drawFront) { g2.fill(side1); g2.fill(side2); } if (drawBack) { g2.fill(back); } g2.setPaint(outlinePaint); if (drawFront) { g2.draw(side1); g2.draw(side2); } if (drawBack) { g2.draw(back); } } } else { // starts at the front, finishes at the back (going // around the left side) if (drawBack) { Area side2 = new Area(new Rectangle2D.Double( plotArea.getX(), plotArea.getY(), arc.getEndPoint().getX() - plotArea.getX(), plotArea.getHeight())); side2.intersect(back); g2.setPaint(paint); g2.fill(side2); g2.setPaint(outlinePaint); g2.draw(side2); } if (drawFront) { Area side1 = new Area(new Rectangle2D.Double( plotArea.getX(), plotArea.getY(), arc.getStartPoint().getX() - plotArea.getX(), plotArea.getHeight())); side1.intersect(front); g2.setPaint(paint); g2.fill(side1); g2.setPaint(outlinePaint); g2.draw(side1); } } } else { // the segment starts at the back (still extending // CLOCKWISE) if (!isAngleAtFront(end)) { if (extent > -180.0) { // whole segment stays at the back if (drawBack) { Area side = new Area(new Rectangle2D.Double( arc.getStartPoint().getX(), plotArea.getY(), arc.getEndPoint().getX() - arc.getStartPoint().getX(), plotArea.getHeight())); side.intersect(back); g2.setPaint(paint); g2.fill(side); g2.setPaint(outlinePaint); g2.draw(side); } } else { // starts at the back, wraps around front, and // finishes at back again Area side1 = new Area(new Rectangle2D.Double( arc.getStartPoint().getX(), plotArea.getY(), plotArea.getMaxX() - arc.getStartPoint().getX(), plotArea.getHeight())); side1.intersect(back); Area side2 = new Area(new Rectangle2D.Double( plotArea.getX(), plotArea.getY(), arc.getEndPoint().getX() - plotArea.getX(), plotArea.getHeight())); side2.intersect(back); g2.setPaint(paint); if (drawBack) { g2.fill(side1); g2.fill(side2); } if (drawFront) { g2.fill(front); } g2.setPaint(outlinePaint); if (drawBack) { g2.draw(side1); g2.draw(side2); } if (drawFront) { g2.draw(front); } } } else { // starts at back, finishes at front (CLOCKWISE) if (drawBack) { Area side1 = new Area(new Rectangle2D.Double( arc.getStartPoint().getX(), plotArea.getY(), plotArea.getMaxX() - arc.getStartPoint().getX(), plotArea.getHeight())); side1.intersect(back); g2.setPaint(paint); g2.fill(side1); g2.setPaint(outlinePaint); g2.draw(side1); } if (drawFront) { Area side2 = new Area(new Rectangle2D.Double( arc.getEndPoint().getX(), plotArea.getY(), plotArea.getMaxX() - arc.getEndPoint().getX(), plotArea.getHeight())); side2.intersect(front); g2.setPaint(paint); g2.fill(side2); g2.setPaint(outlinePaint); g2.draw(side2); } } } } else if (extent > 0.0) { // the pie sections are arranged ANTICLOCKWISE if (isAngleAtFront(start)) { // segment starts at the front if (!isAngleAtBack(end)) { // and finishes at the front if (extent < 180.0) { // segment only occupies the front if (drawFront) { Area side = new Area(new Rectangle2D.Double( arc.getStartPoint().getX(), plotArea.getY(), arc.getEndPoint().getX() - arc.getStartPoint().getX(), plotArea.getHeight())); side.intersect(front); g2.setPaint(paint); g2.fill(side); g2.setPaint(outlinePaint); g2.draw(side); } } else { // segments wraps right around the back... Area side1 = new Area(new Rectangle2D.Double( arc.getStartPoint().getX(), plotArea.getY(), plotArea.getMaxX() - arc.getStartPoint().getX(), plotArea.getHeight())); side1.intersect(front); Area side2 = new Area(new Rectangle2D.Double( plotArea.getX(), plotArea.getY(), arc.getEndPoint().getX() - plotArea.getX(), plotArea.getHeight())); side2.intersect(front); g2.setPaint(paint); if (drawFront) { g2.fill(side1); g2.fill(side2); } if (drawBack) { g2.fill(back); } g2.setPaint(outlinePaint); if (drawFront) { g2.draw(side1); g2.draw(side2); } if (drawBack) { g2.draw(back); } } } else { // segments starts at front and finishes at back... if (drawBack) { Area side2 = new Area(new Rectangle2D.Double( arc.getEndPoint().getX(), plotArea.getY(), plotArea.getMaxX() - arc.getEndPoint().getX(), plotArea.getHeight())); side2.intersect(back); g2.setPaint(paint); g2.fill(side2); g2.setPaint(outlinePaint); g2.draw(side2); } if (drawFront) { Area side1 = new Area(new Rectangle2D.Double( arc.getStartPoint().getX(), plotArea.getY(), plotArea.getMaxX() - arc.getStartPoint().getX(), plotArea.getHeight())); side1.intersect(front); g2.setPaint(paint); g2.fill(side1); g2.setPaint(outlinePaint); g2.draw(side1); } } } else { // segment starts at back if (!isAngleAtFront(end)) { if (extent < 180.0) { // and finishes at back if (drawBack) { Area side = new Area(new Rectangle2D.Double( arc.getEndPoint().getX(), plotArea.getY(), arc.getStartPoint().getX() - arc.getEndPoint().getX(), plotArea.getHeight())); side.intersect(back); g2.setPaint(paint); g2.fill(side); g2.setPaint(outlinePaint); g2.draw(side); } } else { // starts at back and wraps right around to the // back again Area side1 = new Area(new Rectangle2D.Double( arc.getStartPoint().getX(), plotArea.getY(), plotArea.getX() - arc.getStartPoint().getX(), plotArea.getHeight())); side1.intersect(back); Area side2 = new Area(new Rectangle2D.Double( arc.getEndPoint().getX(), plotArea.getY(), plotArea.getMaxX() - arc.getEndPoint().getX(), plotArea.getHeight())); side2.intersect(back); g2.setPaint(paint); if (drawBack) { g2.fill(side1); g2.fill(side2); } if (drawFront) { g2.fill(front); } g2.setPaint(outlinePaint); if (drawBack) { g2.draw(side1); g2.draw(side2); } if (drawFront) { g2.draw(front); } } } else { // starts at the back and finishes at the front // (wrapping the left side) if (drawBack) { Area side1 = new Area(new Rectangle2D.Double( plotArea.getX(), plotArea.getY(), arc.getStartPoint().getX() - plotArea.getX(), plotArea.getHeight())); side1.intersect(back); g2.setPaint(paint); g2.fill(side1); g2.setPaint(outlinePaint); g2.draw(side1); } if (drawFront) { Area side2 = new Area(new Rectangle2D.Double( plotArea.getX(), plotArea.getY(), arc.getEndPoint().getX() - plotArea.getX(), plotArea.getHeight())); side2.intersect(front); g2.setPaint(paint); g2.fill(side2); g2.setPaint(outlinePaint); g2.draw(side2); } } } } } /** * Returns a short string describing the type of plot. * * @return Pie 3D Plot. */ @Override public String getPlotType() { return localizationResources.getString("Pie_3D_Plot"); } /** * A utility method that returns true if the angle represents a point at * the front of the 3D pie chart. 0 - 180 degrees is the back, 180 - 360 * is the front. * * @param angle the angle. * * @return A boolean. */ private boolean isAngleAtFront(double angle) { return (Math.sin(Math.toRadians(angle)) < 0.0); } /** * A utility method that returns true if the angle represents a point at * the back of the 3D pie chart. 0 - 180 degrees is the back, 180 - 360 * is the front. * * @param angle the angle. * * @return {@code true} if the angle is at the back of the pie. */ private boolean isAngleAtBack(double angle) { return (Math.sin(Math.toRadians(angle)) > 0.0); } /** * Tests this plot for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof PiePlot3D)) { return false; } PiePlot3D that = (PiePlot3D) obj; if (this.depthFactor != that.depthFactor) { return false; } if (this.darkerSides != that.darkerSides) { return false; } return super.equals(obj); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/PiePlotState.java000066400000000000000000000152431463604235500272610ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * PiePlotState.java * ----------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import java.awt.geom.Rectangle2D; import org.jfree.chart.renderer.RendererState; /** * A renderer state. */ public class PiePlotState extends RendererState { /** The number of passes required by the renderer. */ private int passesRequired; /** The total of the values in the dataset. */ private double total; /** The latest angle. */ private double latestAngle; /** The exploded pie area. */ private Rectangle2D explodedPieArea; /** The pie area. */ private Rectangle2D pieArea; /** The center of the pie in Java 2D coordinates. */ private double pieCenterX; /** The center of the pie in Java 2D coordinates. */ private double pieCenterY; /** The vertical pie radius. */ private double pieHRadius; /** The horizontal pie radius. */ private double pieWRadius; /** The link area. */ private Rectangle2D linkArea; /** * Creates a new object for recording temporary state information for a * renderer. * * @param info the plot rendering info. */ public PiePlotState(PlotRenderingInfo info) { super(info); this.passesRequired = 1; this.total = 0.0; } /** * Returns the number of passes required by the renderer. * * @return The number of passes. */ public int getPassesRequired() { return this.passesRequired; } /** * Sets the number of passes required by the renderer. * * @param passes the passes. */ public void setPassesRequired(int passes) { this.passesRequired = passes; } /** * Returns the total of the values in the dataset. * * @return The total. */ public double getTotal() { return this.total; } /** * Sets the total. * * @param total the total. */ public void setTotal(double total) { this.total = total; } /** * Returns the latest angle. * * @return The latest angle. */ public double getLatestAngle() { return this.latestAngle; } /** * Sets the latest angle. * * @param angle the angle. */ public void setLatestAngle(double angle) { this.latestAngle = angle; } /** * Returns the pie area. * * @return The pie area. */ public Rectangle2D getPieArea() { return this.pieArea; } /** * Sets the pie area. * * @param area the area. */ public void setPieArea(Rectangle2D area) { this.pieArea = area; } /** * Returns the exploded pie area. * * @return The exploded pie area. */ public Rectangle2D getExplodedPieArea() { return this.explodedPieArea; } /** * Sets the exploded pie area. * * @param area the area. */ public void setExplodedPieArea(Rectangle2D area) { this.explodedPieArea = area; } /** * Returns the x-coordinate of the center of the pie chart. * * @return The x-coordinate (in Java2D space). */ public double getPieCenterX() { return this.pieCenterX; } /** * Sets the x-coordinate of the center of the pie chart. * * @param x the x-coordinate (in Java2D space). */ public void setPieCenterX(double x) { this.pieCenterX = x; } /** * Returns the y-coordinate (in Java2D space) of the center of the pie * chart. * * @return The y-coordinate (in Java2D space). */ public double getPieCenterY() { return this.pieCenterY; } /** * Sets the y-coordinate of the center of the pie chart. This method is * used by the plot and typically is not called directly by applications. * * @param y the y-coordinate (in Java2D space). */ public void setPieCenterY(double y) { this.pieCenterY = y; } /** * Returns the link area. This defines the "dog-leg" point for the label * linking lines. * * @return The link area. */ public Rectangle2D getLinkArea() { return this.linkArea; } /** * Sets the label link area. This defines the "dog-leg" point for the * label linking lines. * * @param area the area. */ public void setLinkArea(Rectangle2D area) { this.linkArea = area; } /** * Returns the vertical pie radius. * * @return The radius. */ public double getPieHRadius() { return this.pieHRadius; } /** * Sets the vertical pie radius. * * @param radius the radius. */ public void setPieHRadius(double radius) { this.pieHRadius = radius; } /** * Returns the horizontal pie radius. * * @return The radius. */ public double getPieWRadius() { return this.pieWRadius; } /** * Sets the horizontal pie radius. * * @param radius the radius. */ public void setPieWRadius(double radius) { this.pieWRadius = radius; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/Plot.java000066400000000000000000001406651463604235500256310ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------- * Plot.java * --------- * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Sylvain Vieujot; * Jeremy Bowman; * Andreas Schneider; * Gideon Krause; * Nicolas Brodu; * Michal Krause; * Richard West, Advanced Micro Devices, Inc.; * Peter Kolb - patches 2603321, 2809117; * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.plot; import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Composite; import java.awt.Font; import java.awt.GradientPaint; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Paint; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Ellipse2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Objects; import javax.swing.event.EventListenerList; import org.jfree.chart.JFreeChart; import org.jfree.chart.LegendItemCollection; import org.jfree.chart.LegendItemSource; import org.jfree.chart.annotations.Annotation; import org.jfree.chart.axis.AxisLocation; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.entity.PlotEntity; import org.jfree.chart.event.AnnotationChangeEvent; import org.jfree.chart.event.AnnotationChangeListener; import org.jfree.chart.event.AxisChangeEvent; import org.jfree.chart.event.AxisChangeListener; import org.jfree.chart.event.ChartChangeEventType; import org.jfree.chart.event.MarkerChangeEvent; import org.jfree.chart.event.MarkerChangeListener; import org.jfree.chart.event.PlotChangeEvent; import org.jfree.chart.event.PlotChangeListener; import org.jfree.chart.text.G2TextMeasurer; import org.jfree.chart.text.TextBlock; import org.jfree.chart.text.TextBlockAnchor; import org.jfree.chart.text.TextUtils; import org.jfree.chart.ui.Align; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; import org.jfree.data.general.DatasetChangeEvent; import org.jfree.data.general.DatasetChangeListener; import org.jfree.data.general.DatasetGroup; /** * The base class for all plots in JFreeChart. The {@link JFreeChart} class * delegates the drawing of axes and data to the plot. This base class * provides facilities common to most plot types. */ public abstract class Plot implements AxisChangeListener, DatasetChangeListener, AnnotationChangeListener, MarkerChangeListener, LegendItemSource, PublicCloneable, Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -8831571430103671324L; /** Useful constant representing zero. */ public static final Number ZERO = 0; /** The default insets. */ public static final RectangleInsets DEFAULT_INSETS = new RectangleInsets(4.0, 8.0, 4.0, 8.0); /** The default outline stroke. */ public static final Stroke DEFAULT_OUTLINE_STROKE = new BasicStroke(0.5f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); /** The default outline color. */ public static final Paint DEFAULT_OUTLINE_PAINT = Color.GRAY; /** The default foreground alpha transparency. */ public static final float DEFAULT_FOREGROUND_ALPHA = 1.0f; /** The default background alpha transparency. */ public static final float DEFAULT_BACKGROUND_ALPHA = 1.0f; /** The default background color. */ public static final Paint DEFAULT_BACKGROUND_PAINT = Color.WHITE; /** The minimum width at which the plot should be drawn. */ public static final int MINIMUM_WIDTH_TO_DRAW = 10; /** The minimum height at which the plot should be drawn. */ public static final int MINIMUM_HEIGHT_TO_DRAW = 10; /** A default box shape for legend items. */ public static final Shape DEFAULT_LEGEND_ITEM_BOX = new Rectangle2D.Double(-4.0, -4.0, 8.0, 8.0); /** A default circle shape for legend items. */ public static final Shape DEFAULT_LEGEND_ITEM_CIRCLE = new Ellipse2D.Double(-4.0, -4.0, 8.0, 8.0); /** * The chart that the plot is assigned to. It can be {@code null} if the * plot is not assigned to a chart yet, or if the plot is a subplot of a * another plot. */ private JFreeChart chart; /** The parent plot ({@code null} if this is the root plot). */ private Plot parent; /** The dataset group (to be used for thread synchronisation). */ private DatasetGroup datasetGroup; /** The message to display if no data is available. */ private String noDataMessage; /** The font used to display the 'no data' message. */ private Font noDataMessageFont; /** The paint used to draw the 'no data' message. */ private transient Paint noDataMessagePaint; /** Amount of blank space around the plot area. */ private RectangleInsets insets; /** * A flag that controls whether or not the plot outline is drawn. */ private boolean outlineVisible; /** The Stroke used to draw an outline around the plot. */ private transient Stroke outlineStroke; /** The Paint used to draw an outline around the plot. */ private transient Paint outlinePaint; /** An optional color used to fill the plot background. */ private transient Paint backgroundPaint; /** An optional image for the plot background. */ private transient Image backgroundImage; // not currently serialized /** The alignment for the background image. */ private int backgroundImageAlignment = Align.FIT; /** The alpha value used to draw the background image. */ private float backgroundImageAlpha = 0.5f; /** The alpha-transparency for the plot. */ private float foregroundAlpha; /** The alpha transparency for the background paint. */ private float backgroundAlpha; /** The drawing supplier. */ private DrawingSupplier drawingSupplier; /** Storage for registered change listeners. */ private transient EventListenerList listenerList; /** * A flag that controls whether or not the plot will notify listeners * of changes (defaults to true, but sometimes it is useful to disable * this). */ private boolean notify; /** * Creates a new plot. */ protected Plot() { this.chart = null; this.parent = null; this.insets = DEFAULT_INSETS; this.backgroundPaint = DEFAULT_BACKGROUND_PAINT; this.backgroundAlpha = DEFAULT_BACKGROUND_ALPHA; this.backgroundImage = null; this.outlineVisible = true; this.outlineStroke = DEFAULT_OUTLINE_STROKE; this.outlinePaint = DEFAULT_OUTLINE_PAINT; this.foregroundAlpha = DEFAULT_FOREGROUND_ALPHA; this.noDataMessage = null; this.noDataMessageFont = new Font("SansSerif", Font.PLAIN, 12); this.noDataMessagePaint = Color.BLACK; this.drawingSupplier = new DefaultDrawingSupplier(); this.notify = true; this.listenerList = new EventListenerList(); } /** * Returns the chart that this plot is assigned to. This method can * return {@code null} if the plot is not yet assigned to a plot, or if the * plot is a subplot of another plot. * * @return The chart (possibly {@code null}). */ public JFreeChart getChart() { return this.chart; } /** * Sets the chart that the plot is assigned to. This method is not * intended for external use. * * @param chart the chart ({@code null} permitted). */ public void setChart(JFreeChart chart) { this.chart = chart; } /** * Fetches the element hinting flag from the chart that this plot is * assigned to. If the plot is not assigned (directly or indirectly) to * a chart instance, this method will return {@code false}. * * @return A boolean. */ public boolean fetchElementHintingFlag() { if (this.parent != null) { return this.parent.fetchElementHintingFlag(); } if (this.chart != null) { return this.chart.getElementHinting(); } return false; } /** * Returns the dataset group for the plot (not currently used). * * @return The dataset group. * * @see #setDatasetGroup(DatasetGroup) */ public DatasetGroup getDatasetGroup() { return this.datasetGroup; } /** * Sets the dataset group (not currently used). * * @param group the dataset group ({@code null} permitted). * * @see #getDatasetGroup() */ protected void setDatasetGroup(DatasetGroup group) { this.datasetGroup = group; } /** * Returns the string that is displayed when the dataset is empty or * {@code null}. * * @return The 'no data' message ({@code null} possible). * * @see #setNoDataMessage(String) * @see #getNoDataMessageFont() * @see #getNoDataMessagePaint() */ public String getNoDataMessage() { return this.noDataMessage; } /** * Sets the message that is displayed when the dataset is empty or * {@code null}, and sends a {@link PlotChangeEvent} to all registered * listeners. * * @param message the message ({@code null} permitted). * * @see #getNoDataMessage() */ public void setNoDataMessage(String message) { this.noDataMessage = message; fireChangeEvent(); } /** * Returns the font used to display the 'no data' message. * * @return The font (never {@code null}). * * @see #setNoDataMessageFont(Font) * @see #getNoDataMessage() */ public Font getNoDataMessageFont() { return this.noDataMessageFont; } /** * Sets the font used to display the 'no data' message and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param font the font ({@code null} not permitted). * * @see #getNoDataMessageFont() */ public void setNoDataMessageFont(Font font) { Args.nullNotPermitted(font, "font"); this.noDataMessageFont = font; fireChangeEvent(); } /** * Returns the paint used to display the 'no data' message. * * @return The paint (never {@code null}). * * @see #setNoDataMessagePaint(Paint) * @see #getNoDataMessage() */ public Paint getNoDataMessagePaint() { return this.noDataMessagePaint; } /** * Sets the paint used to display the 'no data' message and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getNoDataMessagePaint() */ public void setNoDataMessagePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.noDataMessagePaint = paint; fireChangeEvent(); } /** * Returns a short string describing the plot type. *

* Note: this gets used in the chart property editing user interface, * but there needs to be a better mechanism for identifying the plot type. * * @return A short string describing the plot type (never * {@code null}). */ public abstract String getPlotType(); /** * Returns the parent plot (or {@code null} if this plot is not part * of a combined plot). * * @return The parent plot. * * @see #setParent(Plot) * @see #getRootPlot() */ public Plot getParent() { return this.parent; } /** * Sets the parent plot. This method is intended for internal use, you * shouldn't need to call it directly. * * @param parent the parent plot ({@code null} permitted). * * @see #getParent() */ public void setParent(Plot parent) { this.parent = parent; } /** * Returns the root plot. * * @return The root plot. * * @see #getParent() */ public Plot getRootPlot() { Plot p = getParent(); if (p == null) { return this; } return p.getRootPlot(); } /** * Returns {@code true} if this plot is part of a combined plot * structure (that is, {@link #getParent()} returns a non-{@code null} * value), and {@code false} otherwise. * * @return {@code true} if this plot is part of a combined plot * structure. * * @see #getParent() */ public boolean isSubplot() { return (getParent() != null); } /** * Returns the insets for the plot area. * * @return The insets (never {@code null}). * * @see #setInsets(RectangleInsets) */ public RectangleInsets getInsets() { return this.insets; } /** * Sets the insets for the plot and sends a {@link PlotChangeEvent} to * all registered listeners. * * @param insets the new insets ({@code null} not permitted). * * @see #getInsets() * @see #setInsets(RectangleInsets, boolean) */ public void setInsets(RectangleInsets insets) { setInsets(insets, true); } /** * Sets the insets for the plot and, if requested, and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param insets the new insets ({@code null} not permitted). * @param notify a flag that controls whether the registered listeners are * notified. * * @see #getInsets() * @see #setInsets(RectangleInsets) */ public void setInsets(RectangleInsets insets, boolean notify) { Args.nullNotPermitted(insets, "insets"); if (!this.insets.equals(insets)) { this.insets = insets; if (notify) { fireChangeEvent(); } } } /** * Returns the background color of the plot area. * * @return The paint (possibly {@code null}). * * @see #setBackgroundPaint(Paint) */ public Paint getBackgroundPaint() { return this.backgroundPaint; } /** * Sets the background color of the plot area and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} permitted). * * @see #getBackgroundPaint() */ public void setBackgroundPaint(Paint paint) { if (paint == null) { if (this.backgroundPaint != null) { this.backgroundPaint = null; fireChangeEvent(); } } else { if (this.backgroundPaint != null) { if (this.backgroundPaint.equals(paint)) { return; // nothing to do } } this.backgroundPaint = paint; fireChangeEvent(); } } /** * Returns the alpha transparency of the plot area background. * * @return The alpha transparency. * * @see #setBackgroundAlpha(float) */ public float getBackgroundAlpha() { return this.backgroundAlpha; } /** * Sets the alpha transparency of the plot area background, and notifies * registered listeners that the plot has been modified. * * @param alpha the new alpha value (in the range 0.0f to 1.0f). * * @see #getBackgroundAlpha() */ public void setBackgroundAlpha(float alpha) { if (this.backgroundAlpha != alpha) { this.backgroundAlpha = alpha; fireChangeEvent(); } } /** * Returns the drawing supplier for the plot. * * @return The drawing supplier (possibly {@code null}). * * @see #setDrawingSupplier(DrawingSupplier) */ public DrawingSupplier getDrawingSupplier() { DrawingSupplier result; Plot p = getParent(); if (p != null) { result = p.getDrawingSupplier(); } else { result = this.drawingSupplier; } return result; } /** * Sets the drawing supplier for the plot and sends a * {@link PlotChangeEvent} to all registered listeners. The drawing * supplier is responsible for supplying a limitless (possibly repeating) * sequence of {@code Paint}, {@code Stroke} and * {@code Shape} objects that the plot's renderer(s) can use to * populate its (their) tables. * * @param supplier the new supplier. * * @see #getDrawingSupplier() */ public void setDrawingSupplier(DrawingSupplier supplier) { this.drawingSupplier = supplier; fireChangeEvent(); } /** * Sets the drawing supplier for the plot and, if requested, sends a * {@link PlotChangeEvent} to all registered listeners. The drawing * supplier is responsible for supplying a limitless (possibly repeating) * sequence of {@code Paint}, {@code Stroke} and * {@code Shape} objects that the plot's renderer(s) can use to * populate its (their) tables. * * @param supplier the new supplier. * @param notify notify listeners? * * @see #getDrawingSupplier() */ public void setDrawingSupplier(DrawingSupplier supplier, boolean notify) { this.drawingSupplier = supplier; if (notify) { fireChangeEvent(); } } /** * Returns the background image that is used to fill the plot's background * area. * * @return The image (possibly {@code null}). * * @see #setBackgroundImage(Image) */ public Image getBackgroundImage() { return this.backgroundImage; } /** * Sets the background image for the plot and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param image the image ({@code null} permitted). * * @see #getBackgroundImage() */ public void setBackgroundImage(Image image) { this.backgroundImage = image; fireChangeEvent(); } /** * Returns the background image alignment. Alignment constants are defined * in the {@code Align} class. * * @return The alignment. * * @see #setBackgroundImageAlignment(int) */ public int getBackgroundImageAlignment() { return this.backgroundImageAlignment; } /** * Sets the alignment for the background image and sends a * {@link PlotChangeEvent} to all registered listeners. Alignment options * are defined by the {@link org.jfree.chart.ui.Align} class. * * @param alignment the alignment. * * @see #getBackgroundImageAlignment() */ public void setBackgroundImageAlignment(int alignment) { if (this.backgroundImageAlignment != alignment) { this.backgroundImageAlignment = alignment; fireChangeEvent(); } } /** * Returns the alpha transparency used to draw the background image. This * is a value in the range 0.0f to 1.0f, where 0.0f is fully transparent * and 1.0f is fully opaque. * * @return The alpha transparency. * * @see #setBackgroundImageAlpha(float) */ public float getBackgroundImageAlpha() { return this.backgroundImageAlpha; } /** * Sets the alpha transparency used when drawing the background image. * * @param alpha the alpha transparency (in the range 0.0f to 1.0f, where * 0.0f is fully transparent, and 1.0f is fully opaque). * * @throws IllegalArgumentException if {@code alpha} is not within * the specified range. * * @see #getBackgroundImageAlpha() */ public void setBackgroundImageAlpha(float alpha) { if (alpha < 0.0f || alpha > 1.0f) { throw new IllegalArgumentException( "The 'alpha' value must be in the range 0.0f to 1.0f."); } if (this.backgroundImageAlpha != alpha) { this.backgroundImageAlpha = alpha; fireChangeEvent(); } } /** * Returns the flag that controls whether or not the plot outline is * drawn. The default value is {@code true}. Note that for * historical reasons, the plot's outline paint and stroke can take on * {@code null} values, in which case the outline will not be drawn * even if this flag is set to {@code true}. * * @return The outline visibility flag. * * @see #setOutlineVisible(boolean) */ public boolean isOutlineVisible() { return this.outlineVisible; } /** * Sets the flag that controls whether or not the plot's outline is * drawn, and sends a {@link PlotChangeEvent} to all registered listeners. * * @param visible the new flag value. * * @see #isOutlineVisible() */ public void setOutlineVisible(boolean visible) { this.outlineVisible = visible; fireChangeEvent(); } /** * Returns the stroke used to outline the plot area. * * @return The stroke (possibly {@code null}). * * @see #setOutlineStroke(Stroke) */ public Stroke getOutlineStroke() { return this.outlineStroke; } /** * Sets the stroke used to outline the plot area and sends a * {@link PlotChangeEvent} to all registered listeners. If you set this * attribute to {@code null}, no outline will be drawn. * * @param stroke the stroke ({@code null} permitted). * * @see #getOutlineStroke() */ public void setOutlineStroke(Stroke stroke) { if (stroke == null) { if (this.outlineStroke != null) { this.outlineStroke = null; fireChangeEvent(); } } else { if (this.outlineStroke != null) { if (this.outlineStroke.equals(stroke)) { return; // nothing to do } } this.outlineStroke = stroke; fireChangeEvent(); } } /** * Returns the color used to draw the outline of the plot area. * * @return The color (possibly {@code null}). * * @see #setOutlinePaint(Paint) */ public Paint getOutlinePaint() { return this.outlinePaint; } /** * Sets the paint used to draw the outline of the plot area and sends a * {@link PlotChangeEvent} to all registered listeners. If you set this * attribute to {@code null}, no outline will be drawn. * * @param paint the paint ({@code null} permitted). * * @see #getOutlinePaint() */ public void setOutlinePaint(Paint paint) { if (paint == null) { if (this.outlinePaint != null) { this.outlinePaint = null; fireChangeEvent(); } } else { if (this.outlinePaint != null) { if (this.outlinePaint.equals(paint)) { return; // nothing to do } } this.outlinePaint = paint; fireChangeEvent(); } } /** * Returns the alpha-transparency for the plot foreground. * * @return The alpha-transparency. * * @see #setForegroundAlpha(float) */ public float getForegroundAlpha() { return this.foregroundAlpha; } /** * Sets the alpha-transparency for the plot and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param alpha the new alpha transparency. * * @see #getForegroundAlpha() */ public void setForegroundAlpha(float alpha) { if (this.foregroundAlpha != alpha) { this.foregroundAlpha = alpha; fireChangeEvent(); } } /** * Returns the legend items for the plot. By default, this method returns * {@code null}. Subclasses should override to return a * {@link LegendItemCollection}. * * @return The legend items for the plot (possibly {@code null}). */ @Override public LegendItemCollection getLegendItems() { return null; } /** * Returns a flag that controls whether or not change events are sent to * registered listeners. * * @return A boolean. * * @see #setNotify(boolean) */ public boolean isNotify() { return this.notify; } /** * Sets a flag that controls whether or not listeners receive * {@link PlotChangeEvent} notifications. * * @param notify a boolean. * * @see #isNotify() */ public void setNotify(boolean notify) { this.notify = notify; // if the flag is being set to true, there may be queued up changes... if (notify) { notifyListeners(new PlotChangeEvent(this)); } } /** * Registers an object for notification of changes to the plot. * * @param listener the object to be registered. * * @see #removeChangeListener(PlotChangeListener) */ public void addChangeListener(PlotChangeListener listener) { this.listenerList.add(PlotChangeListener.class, listener); } /** * Unregisters an object for notification of changes to the plot. * * @param listener the object to be unregistered. * * @see #addChangeListener(PlotChangeListener) */ public void removeChangeListener(PlotChangeListener listener) { this.listenerList.remove(PlotChangeListener.class, listener); } /** * Notifies all registered listeners that the plot has been modified. * * @param event information about the change event. */ public void notifyListeners(PlotChangeEvent event) { // if the 'notify' flag has been switched to false, we don't notify // the listeners if (!this.notify) { return; } Object[] listeners = this.listenerList.getListenerList(); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == PlotChangeListener.class) { ((PlotChangeListener) listeners[i + 1]).plotChanged(event); } } } /** * Sends a {@link PlotChangeEvent} to all registered listeners. */ protected void fireChangeEvent() { notifyListeners(new PlotChangeEvent(this)); } /** * Draws the plot within the specified area. The anchor is a point on the * chart that is specified externally (for instance, it may be the last * point of the last mouse click performed by the user) - plots can use or * ignore this value as they see fit. *

* Subclasses need to provide an implementation of this method, obviously. * * @param g2 the graphics device. * @param area the plot area. * @param anchor the anchor point ({@code null} permitted). * @param parentState the parent state (if any, {@code null} permitted). * @param info carries back plot rendering info. */ public abstract void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, PlotState parentState, PlotRenderingInfo info); /** * Draws the plot background (the background color and/or image). *

* This method will be called during the chart drawing process and is * declared public so that it can be accessed by the renderers used by * certain subclasses. You shouldn't need to call this method directly. * * @param g2 the graphics device. * @param area the area within which the plot should be drawn. */ public void drawBackground(Graphics2D g2, Rectangle2D area) { // some subclasses override this method completely, so don't put // anything here that *must* be done fillBackground(g2, area); drawBackgroundImage(g2, area); } /** * Fills the specified area with the background paint. * * @param g2 the graphics device. * @param area the area. * * @see #getBackgroundPaint() * @see #getBackgroundAlpha() * @see #fillBackground(Graphics2D, Rectangle2D, PlotOrientation) */ protected void fillBackground(Graphics2D g2, Rectangle2D area) { fillBackground(g2, area, PlotOrientation.VERTICAL); } /** * Fills the specified area with the background paint. If the background * paint is an instance of {@code GradientPaint}, the gradient will * run in the direction suggested by the plot's orientation. * * @param g2 the graphics target. * @param area the plot area. * @param orientation the plot orientation ({@code null} not * permitted). */ protected void fillBackground(Graphics2D g2, Rectangle2D area, PlotOrientation orientation) { Args.nullNotPermitted(orientation, "orientation"); if (this.backgroundPaint == null) { return; } Paint p = this.backgroundPaint; if (p instanceof GradientPaint) { GradientPaint gp = (GradientPaint) p; if (orientation == PlotOrientation.VERTICAL) { p = new GradientPaint((float) area.getCenterX(), (float) area.getMaxY(), gp.getColor1(), (float) area.getCenterX(), (float) area.getMinY(), gp.getColor2()); } else if (orientation == PlotOrientation.HORIZONTAL) { p = new GradientPaint((float) area.getMinX(), (float) area.getCenterY(), gp.getColor1(), (float) area.getMaxX(), (float) area.getCenterY(), gp.getColor2()); } } Composite originalComposite = g2.getComposite(); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, this.backgroundAlpha)); g2.setPaint(p); g2.fill(area); g2.setComposite(originalComposite); } /** * Draws the background image (if there is one) aligned within the * specified area. * * @param g2 the graphics device. * @param area the area. * * @see #getBackgroundImage() * @see #getBackgroundImageAlignment() * @see #getBackgroundImageAlpha() */ public void drawBackgroundImage(Graphics2D g2, Rectangle2D area) { if (this.backgroundImage == null) { return; // nothing to do } Composite savedComposite = g2.getComposite(); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, this.backgroundImageAlpha)); Rectangle2D dest = new Rectangle2D.Double(0.0, 0.0, this.backgroundImage.getWidth(null), this.backgroundImage.getHeight(null)); Align.align(dest, area, this.backgroundImageAlignment); Shape savedClip = g2.getClip(); g2.clip(area); g2.drawImage(this.backgroundImage, (int) dest.getX(), (int) dest.getY(), (int) dest.getWidth() + 1, (int) dest.getHeight() + 1, null); g2.setClip(savedClip); g2.setComposite(savedComposite); } /** * Draws the plot outline. This method will be called during the chart * drawing process and is declared public so that it can be accessed by the * renderers used by certain subclasses. You shouldn't need to call this * method directly. * * @param g2 the graphics device. * @param area the area within which the plot should be drawn. */ public void drawOutline(Graphics2D g2, Rectangle2D area) { if (!this.outlineVisible) { return; } if ((this.outlineStroke != null) && (this.outlinePaint != null)) { g2.setStroke(this.outlineStroke); g2.setPaint(this.outlinePaint); Object saved = g2.getRenderingHint(RenderingHints.KEY_STROKE_CONTROL); g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE); g2.draw(area); g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, saved); } } /** * Draws a message to state that there is no data to plot. * * @param g2 the graphics device. * @param area the area within which the plot should be drawn. */ protected void drawNoDataMessage(Graphics2D g2, Rectangle2D area) { Shape savedClip = g2.getClip(); g2.clip(area); String message = this.noDataMessage; if (message != null) { g2.setFont(this.noDataMessageFont); g2.setPaint(this.noDataMessagePaint); TextBlock block = TextUtils.createTextBlock( this.noDataMessage, this.noDataMessageFont, this.noDataMessagePaint, 0.9f * (float) area.getWidth(), new G2TextMeasurer(g2)); block.draw(g2, (float) area.getCenterX(), (float) area.getCenterY(), TextBlockAnchor.CENTER); } g2.setClip(savedClip); } /** * Creates a plot entity that contains a reference to the plot and the * data area as shape. * * @param dataArea the data area used as hot spot for the entity. * @param plotState the plot rendering info containing a reference to the * EntityCollection. * @param toolTip the tool tip (defined in the respective Plot * subclass) ({@code null} permitted). * @param urlText the url (defined in the respective Plot subclass) * ({@code null} permitted). */ protected void createAndAddEntity(Rectangle2D dataArea, PlotRenderingInfo plotState, String toolTip, String urlText) { if (plotState != null && plotState.getOwner() != null) { EntityCollection e = plotState.getOwner().getEntityCollection(); if (e != null) { e.add(new PlotEntity(dataArea, this, toolTip, urlText)); } } } /** * Handles a 'click' on the plot. Since the plot does not maintain any * information about where it has been drawn, the plot rendering info is * supplied as an argument so that the plot dimensions can be determined. * * @param x the x coordinate (in Java2D space). * @param y the y coordinate (in Java2D space). * @param info an object containing information about the dimensions of * the plot. */ public void handleClick(int x, int y, PlotRenderingInfo info) { // provides a 'no action' default } /** * Performs a zoom on the plot. Subclasses should override if zooming is * appropriate for the type of plot. * * @param percent the zoom percentage. */ public void zoom(double percent) { // do nothing by default. } /** * Receives notification of a change to an {@link Annotation} added to * this plot. * * @param event information about the event (not used here). */ @Override public void annotationChanged(AnnotationChangeEvent event) { fireChangeEvent(); } /** * Receives notification of a change to one of the plot's axes. * * @param event information about the event (not used here). */ @Override public void axisChanged(AxisChangeEvent event) { fireChangeEvent(); } /** * Receives notification of a change to the plot's dataset. *

* The plot reacts by passing on a plot change event to all registered * listeners. * * @param event information about the event (not used here). */ @Override public void datasetChanged(DatasetChangeEvent event) { PlotChangeEvent newEvent = new PlotChangeEvent(this); newEvent.setType(ChartChangeEventType.DATASET_UPDATED); notifyListeners(newEvent); } /** * Receives notification of a change to a marker that is assigned to the * plot. * * @param event the event. */ @Override public void markerChanged(MarkerChangeEvent event) { fireChangeEvent(); } /** * Adjusts the supplied x-value. * * @param x the x-value. * @param w1 width 1. * @param w2 width 2. * @param edge the edge (left or right). * * @return The adjusted x-value. */ protected double getRectX(double x, double w1, double w2, RectangleEdge edge) { double result = x; if (edge == RectangleEdge.LEFT) { result = result + w1; } else if (edge == RectangleEdge.RIGHT) { result = result + w2; } return result; } /** * Adjusts the supplied y-value. * * @param y the x-value. * @param h1 height 1. * @param h2 height 2. * @param edge the edge (top or bottom). * * @return The adjusted y-value. */ protected double getRectY(double y, double h1, double h2, RectangleEdge edge) { double result = y; if (edge == RectangleEdge.TOP) { result = result + h1; } else if (edge == RectangleEdge.BOTTOM) { result = result + h2; } return result; } /** * Tests this plot for equality with another object. * * @param obj the object ({@code null} permitted). * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof Plot)) { return false; } Plot that = (Plot) obj; // fix the "equals not symmetric" problem if (!that.canEqual(this)) { return false; } if (!Objects.equals(this.noDataMessage, that.noDataMessage)) { return false; } if (!Objects.equals( this.noDataMessageFont, that.noDataMessageFont )) { return false; } if (!PaintUtils.equal(this.noDataMessagePaint, that.noDataMessagePaint)) { return false; } if (!Objects.equals(this.insets, that.insets)) { return false; } // There's a reason chart is not included in equals/hashCode - doing so // causes a StackOverflow error during EqualsVerifier's test! // if (!Objects.equals(this.chart, that.chart)) { // return false; // } if (this.outlineVisible != that.outlineVisible) { return false; } if (!Objects.equals(this.outlineStroke, that.outlineStroke)) { return false; } if (!PaintUtils.equal(this.outlinePaint, that.outlinePaint)) { return false; } if (!PaintUtils.equal(this.backgroundPaint, that.backgroundPaint)) { return false; } if (!Objects.equals(this.backgroundImage, that.backgroundImage)) { return false; } if (this.backgroundImageAlignment != that.backgroundImageAlignment) { return false; } if (Float.compare(this.backgroundImageAlpha, that.backgroundImageAlpha) != 0 ){ return false; } if (Float.compare(this.foregroundAlpha, that.foregroundAlpha) != 0 ) { return false; } if (Float.compare(this.backgroundAlpha, that.backgroundAlpha) != 0 ) { return false; } if (!Objects.equals(this.drawingSupplier, that.drawingSupplier)) { return false; } if (this.notify != that.notify) { return false; } if (!Objects.equals(this.datasetGroup, that.datasetGroup)) { return false; } return true; } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ public boolean canEqual(Object other) { // Solves Problem: equals not symmetric return (other instanceof Plot); } @Override public int hashCode() { int hash = 7; hash = 41 * hash + Objects.hashCode(this.noDataMessage); hash = 41 * hash + Objects.hashCode(this.noDataMessageFont); hash = 41 * hash + Objects.hashCode(this.noDataMessagePaint); hash = 41 * hash + Objects.hashCode(this.insets); // hash = 41 * hash + Objects.hashCode(this.chart); hash = 41 * hash + (this.outlineVisible ? 1 : 0); hash = 41 * hash + Objects.hashCode(this.outlineStroke); hash = 41 * hash + Objects.hashCode(this.outlinePaint); hash = 41 * hash + Objects.hashCode(this.backgroundPaint); hash = 41 * hash + Objects.hashCode(this.backgroundImage); hash = 41 * hash + this.backgroundImageAlignment; hash = 41 * hash + Float.floatToIntBits(this.backgroundImageAlpha); hash = 41 * hash + Float.floatToIntBits(this.foregroundAlpha); hash = 41 * hash + Float.floatToIntBits(this.backgroundAlpha); hash = 41 * hash + Objects.hashCode(this.drawingSupplier); hash = 41 * hash + (this.notify ? 1 : 0); hash = 41 * hash + Objects.hashCode(this.datasetGroup); return hash; } /** * Creates a clone of the plot. * * @return A clone. * * @throws CloneNotSupportedException if some component of the plot does not * support cloning. */ @Override public Object clone() throws CloneNotSupportedException { Plot clone = (Plot) super.clone(); // private Plot parent <-- don't clone the parent plot, but take care // childs in combined plots instead if (this.datasetGroup != null) { clone.datasetGroup = (DatasetGroup) ObjectUtils.clone(this.datasetGroup); } clone.drawingSupplier = (DrawingSupplier) ObjectUtils.clone(this.drawingSupplier); clone.listenerList = new EventListenerList(); return clone; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.noDataMessagePaint, stream); SerialUtils.writeStroke(this.outlineStroke, stream); SerialUtils.writePaint(this.outlinePaint, stream); // backgroundImage SerialUtils.writePaint(this.backgroundPaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.noDataMessagePaint = SerialUtils.readPaint(stream); this.outlineStroke = SerialUtils.readStroke(stream); this.outlinePaint = SerialUtils.readPaint(stream); // backgroundImage this.backgroundPaint = SerialUtils.readPaint(stream); this.listenerList = new EventListenerList(); } /** * Resolves a domain axis location for a given plot orientation. * * @param location the location ({@code null} not permitted). * @param orientation the orientation ({@code null} not permitted). * * @return The edge (never {@code null}). */ public static RectangleEdge resolveDomainAxisLocation( AxisLocation location, PlotOrientation orientation) { Args.nullNotPermitted(location, "location"); Args.nullNotPermitted(orientation, "orientation"); RectangleEdge result = null; if (location == AxisLocation.TOP_OR_RIGHT) { if (orientation == PlotOrientation.HORIZONTAL) { result = RectangleEdge.RIGHT; } else if (orientation == PlotOrientation.VERTICAL) { result = RectangleEdge.TOP; } } else if (location == AxisLocation.TOP_OR_LEFT) { if (orientation == PlotOrientation.HORIZONTAL) { result = RectangleEdge.LEFT; } else if (orientation == PlotOrientation.VERTICAL) { result = RectangleEdge.TOP; } } else if (location == AxisLocation.BOTTOM_OR_RIGHT) { if (orientation == PlotOrientation.HORIZONTAL) { result = RectangleEdge.RIGHT; } else if (orientation == PlotOrientation.VERTICAL) { result = RectangleEdge.BOTTOM; } } else if (location == AxisLocation.BOTTOM_OR_LEFT) { if (orientation == PlotOrientation.HORIZONTAL) { result = RectangleEdge.LEFT; } else if (orientation == PlotOrientation.VERTICAL) { result = RectangleEdge.BOTTOM; } } // the above should cover all the options... if (result == null) { throw new IllegalStateException("resolveDomainAxisLocation()"); } return result; } /** * Resolves a range axis location for a given plot orientation. * * @param location the location ({@code null} not permitted). * @param orientation the orientation ({@code null} not permitted). * * @return The edge (never {@code null}). */ public static RectangleEdge resolveRangeAxisLocation( AxisLocation location, PlotOrientation orientation) { Args.nullNotPermitted(location, "location"); Args.nullNotPermitted(orientation, "orientation"); RectangleEdge result = null; if (location == AxisLocation.TOP_OR_RIGHT) { if (orientation == PlotOrientation.HORIZONTAL) { result = RectangleEdge.TOP; } else if (orientation == PlotOrientation.VERTICAL) { result = RectangleEdge.RIGHT; } } else if (location == AxisLocation.TOP_OR_LEFT) { if (orientation == PlotOrientation.HORIZONTAL) { result = RectangleEdge.TOP; } else if (orientation == PlotOrientation.VERTICAL) { result = RectangleEdge.LEFT; } } else if (location == AxisLocation.BOTTOM_OR_RIGHT) { if (orientation == PlotOrientation.HORIZONTAL) { result = RectangleEdge.BOTTOM; } else if (orientation == PlotOrientation.VERTICAL) { result = RectangleEdge.RIGHT; } } else if (location == AxisLocation.BOTTOM_OR_LEFT) { if (orientation == PlotOrientation.HORIZONTAL) { result = RectangleEdge.BOTTOM; } else if (orientation == PlotOrientation.VERTICAL) { result = RectangleEdge.LEFT; } } // the above should cover all the options... if (result == null) { throw new IllegalStateException("resolveRangeAxisLocation()"); } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/PlotOrientation.java000066400000000000000000000107521463604235500300360ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * PlotOrientation.java * -------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import java.io.ObjectStreamException; import java.io.Serializable; /** * Used to indicate the orientation (horizontal or vertical) of a 2D plot. * It is the direction of the y-axis that is the determinant (a conventional * plot has a vertical y-axis). */ public final class PlotOrientation implements Serializable { /** For serialization. */ private static final long serialVersionUID = -2508771828190337782L; /** For a plot where the range axis is horizontal. */ public static final PlotOrientation HORIZONTAL = new PlotOrientation("PlotOrientation.HORIZONTAL"); /** For a plot where the range axis is vertical. */ public static final PlotOrientation VERTICAL = new PlotOrientation("PlotOrientation.VERTICAL"); /** The name. */ private String name; /** * Private constructor. * * @param name the name. */ private PlotOrientation(String name) { this.name = name; } /** * Returns {@code true} if this orientation is {@code HORIZONTAL}, * and {@code false} otherwise. * * @return A boolean. */ public boolean isHorizontal() { return this.equals(PlotOrientation.HORIZONTAL); } /** * Returns {@code true} if this orientation is {@code VERTICAL}, * and {@code false} otherwise. * * @return A boolean. */ public boolean isVertical() { return this.equals(PlotOrientation.VERTICAL); } /** * Returns a string representing the object. * * @return The string. */ @Override public String toString() { return this.name; } /** * Returns {@code true} if this object is equal to the specified * object, and {@code false} otherwise. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof PlotOrientation)) { return false; } PlotOrientation orientation = (PlotOrientation) obj; if (!this.name.equals(orientation.toString())) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { return this.name.hashCode(); } /** * Ensures that serialization returns the unique instances. * * @return The object. * * @throws ObjectStreamException if there is a problem. */ private Object readResolve() throws ObjectStreamException { Object result = null; if (this.equals(PlotOrientation.HORIZONTAL)) { result = PlotOrientation.HORIZONTAL; } else if (this.equals(PlotOrientation.VERTICAL)) { result = PlotOrientation.VERTICAL; } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/PlotRenderingInfo.java000066400000000000000000000211531463604235500302710ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * PlotRenderingInfo.java * ---------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.plot; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.List; import java.util.Objects; import org.jfree.chart.ChartRenderingInfo; import org.jfree.chart.util.Args; import org.jfree.chart.util.SerialUtils; /** * Stores information about the dimensions of a plot and its subplots. */ public class PlotRenderingInfo implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 8446720134379617220L; /** The owner of this info. */ private ChartRenderingInfo owner; /** The plot area. */ private transient Rectangle2D plotArea; /** The data area. */ private transient Rectangle2D dataArea; /** * Storage for the plot rendering info objects belonging to the subplots. */ private List subplotInfo; /** * Creates a new instance. * * @param owner the owner ({@code null} permitted). */ public PlotRenderingInfo(ChartRenderingInfo owner) { this.owner = owner; this.dataArea = new Rectangle2D.Double(); this.subplotInfo = new java.util.ArrayList(); } /** * Returns the owner (as specified in the constructor). * * @return The owner (possibly {@code null}). */ public ChartRenderingInfo getOwner() { return this.owner; } /** * Returns the plot area (in Java2D space). * * @return The plot area (possibly {@code null}). * * @see #setPlotArea(Rectangle2D) */ public Rectangle2D getPlotArea() { return this.plotArea; } /** * Sets the plot area. * * @param area the plot area (in Java2D space, {@code null} * permitted but discouraged) * * @see #getPlotArea() */ public void setPlotArea(Rectangle2D area) { this.plotArea = area; } /** * Returns the plot's data area (in Java2D space). * * @return The data area (possibly {@code null}). * * @see #setDataArea(Rectangle2D) */ public Rectangle2D getDataArea() { return this.dataArea; } /** * Sets the data area. * * @param area the data area (in Java2D space, {@code null} permitted * but discouraged). * * @see #getDataArea() */ public void setDataArea(Rectangle2D area) { this.dataArea = area; } /** * Returns the number of subplots (possibly zero). * * @return The subplot count. */ public int getSubplotCount() { return this.subplotInfo.size(); } /** * Adds the info for a subplot. * * @param info the subplot info. * * @see #getSubplotInfo(int) */ public void addSubplotInfo(PlotRenderingInfo info) { this.subplotInfo.add(info); } /** * Returns the info for a subplot. * * @param index the subplot index. * * @return The info. * * @see #addSubplotInfo(PlotRenderingInfo) */ public PlotRenderingInfo getSubplotInfo(int index) { return (PlotRenderingInfo) this.subplotInfo.get(index); } /** * Returns the index of the subplot that contains the specified * (x, y) point (the "source" point). The source point will usually * come from a mouse click on a {@link org.jfree.chart.ChartPanel}, * and this method is then used to determine the subplot that * contains the source point. * * @param source the source point (in Java2D space, {@code null} not * permitted). * * @return The subplot index (or -1 if no subplot contains {@code source}). */ public int getSubplotIndex(Point2D source) { Args.nullNotPermitted(source, "source"); int subplotCount = getSubplotCount(); for (int i = 0; i < subplotCount; i++) { PlotRenderingInfo info = getSubplotInfo(i); Rectangle2D area = info.getDataArea(); if (area.contains(source)) { return i; } } return -1; } /** * Tests this instance for equality against an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof PlotRenderingInfo)) { return false; } PlotRenderingInfo that = (PlotRenderingInfo) obj; if (!Objects.equals(this.dataArea, that.dataArea)) { return false; } if (!Objects.equals(this.plotArea, that.plotArea)) { return false; } if (!Objects.equals(this.subplotInfo, that.subplotInfo)) { return false; } return true; } @Override public int hashCode() { int hash = 3; hash = 29 * hash + Objects.hashCode(this.plotArea); hash = 29 * hash + Objects.hashCode(this.dataArea); hash = 29 * hash + Objects.hashCode(this.subplotInfo); return hash; } /** * Returns a clone of this object. * * @return A clone. * * @throws CloneNotSupportedException if there is a problem cloning. */ @Override public Object clone() throws CloneNotSupportedException { PlotRenderingInfo clone = (PlotRenderingInfo) super.clone(); if (this.plotArea != null) { clone.plotArea = (Rectangle2D) this.plotArea.clone(); } if (this.dataArea != null) { clone.dataArea = (Rectangle2D) this.dataArea.clone(); } clone.subplotInfo = new java.util.ArrayList(this.subplotInfo.size()); for (int i = 0; i < this.subplotInfo.size(); i++) { PlotRenderingInfo info = (PlotRenderingInfo) this.subplotInfo.get(i); clone.subplotInfo.add(info.clone()); } return clone; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeShape(this.dataArea, stream); SerialUtils.writeShape(this.plotArea, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.dataArea = (Rectangle2D) SerialUtils.readShape(stream); this.plotArea = (Rectangle2D) SerialUtils.readShape(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/PlotState.java000066400000000000000000000037631463604235500266270ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * PlotState.java * -------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import java.util.HashMap; import java.util.Map; /** * Records information about the state of a plot during the drawing process. */ public class PlotState { /** The shared axis states. */ private Map sharedAxisStates; /** * Creates a new state object. */ public PlotState() { this.sharedAxisStates = new HashMap(); } /** * Returns a map containing the shared axis states. * * @return A map. */ public Map getSharedAxisStates() { return this.sharedAxisStates; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/PolarAxisLocation.java000066400000000000000000000121641463604235500302760ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * PolarAxisLocation.java * ---------------------- * (C) Copyright 2009-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; */ package org.jfree.chart.plot; import java.io.ObjectStreamException; import java.io.Serializable; /** * Used to indicate the location of an axis on a {@link PolarPlot}. */ public final class PolarAxisLocation implements Serializable { /** For serialization. */ private static final long serialVersionUID = -3276922179323563410L; /** Axis left of north. */ public static final PolarAxisLocation NORTH_LEFT = new PolarAxisLocation("PolarAxisLocation.NORTH_LEFT"); /** Axis right of north. */ public static final PolarAxisLocation NORTH_RIGHT = new PolarAxisLocation("PolarAxisLocation.NORTH_RIGHT"); /** Axis left of south. */ public static final PolarAxisLocation SOUTH_LEFT = new PolarAxisLocation("PolarAxisLocation.SOUTH_LEFT"); /** Axis right of south. */ public static final PolarAxisLocation SOUTH_RIGHT = new PolarAxisLocation("PolarAxisLocation.SOUTH_RIGHT"); /** Axis above east. */ public static final PolarAxisLocation EAST_ABOVE = new PolarAxisLocation("PolarAxisLocation.EAST_ABOVE"); /** Axis below east. */ public static final PolarAxisLocation EAST_BELOW = new PolarAxisLocation("PolarAxisLocation.EAST_BELOW"); /** Axis above west. */ public static final PolarAxisLocation WEST_ABOVE = new PolarAxisLocation("PolarAxisLocation.WEST_ABOVE"); /** Axis below west. */ public static final PolarAxisLocation WEST_BELOW = new PolarAxisLocation("PolarAxisLocation.WEST_BELOW"); /** The name. */ private String name; /** * Private constructor. * * @param name the name. */ private PolarAxisLocation(String name) { this.name = name; } /** * Returns a string representing the object. * * @return The string. */ @Override public String toString() { return this.name; } /** * Returns {@code true} if this object is equal to the specified * object, and {@code false} otherwise. * * @param obj the other object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof PolarAxisLocation)) { return false; } PolarAxisLocation location = (PolarAxisLocation) obj; if (!this.name.equals(location.toString())) { return false; } return true; } /** * Ensures that serialization returns the unique instances. * * @return The object. * * @throws ObjectStreamException if there is a problem. */ private Object readResolve() throws ObjectStreamException { if (this.equals(PolarAxisLocation.NORTH_RIGHT)) { return PolarAxisLocation.NORTH_RIGHT; } else if (this.equals(PolarAxisLocation.NORTH_LEFT)) { return PolarAxisLocation.NORTH_LEFT; } else if (this.equals(PolarAxisLocation.SOUTH_RIGHT)) { return PolarAxisLocation.SOUTH_RIGHT; } else if (this.equals(PolarAxisLocation.SOUTH_LEFT)) { return PolarAxisLocation.SOUTH_LEFT; } else if (this.equals(PolarAxisLocation.EAST_ABOVE)) { return PolarAxisLocation.EAST_ABOVE; } else if (this.equals(PolarAxisLocation.EAST_BELOW)) { return PolarAxisLocation.EAST_BELOW; } else if (this.equals(PolarAxisLocation.WEST_ABOVE)) { return PolarAxisLocation.WEST_ABOVE; } else if (this.equals(PolarAxisLocation.WEST_BELOW)) { return PolarAxisLocation.WEST_BELOW; } return null; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/PolarPlot.java000066400000000000000000002001121463604235500266070ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * PolarPlot.java * -------------- * (C) Copyright 2004-present, by Solution Engineering, Inc. and Contributors. * * Original Author: Daniel Bridenbecker, Solution Engineering, Inc.; * Contributor(s): David Gilbert; * Martin Hoeller (patches 1871902 and 2850344); * */ package org.jfree.chart.plot; import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Composite; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Point; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.ResourceBundle; import java.util.TreeMap; import org.jfree.chart.LegendItem; import org.jfree.chart.LegendItemCollection; import org.jfree.chart.axis.Axis; import org.jfree.chart.axis.AxisState; import org.jfree.chart.axis.NumberTick; import org.jfree.chart.axis.NumberTickUnit; import org.jfree.chart.axis.TickType; import org.jfree.chart.axis.TickUnit; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.axis.ValueTick; import org.jfree.chart.event.PlotChangeEvent; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.event.RendererChangeListener; import org.jfree.chart.renderer.PolarItemRenderer; import org.jfree.chart.text.TextUtils; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.ui.TextAnchor; import org.jfree.chart.util.ObjectList; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.ResourceBundleWrapper; import org.jfree.chart.util.SerialUtils; import org.jfree.data.Range; import org.jfree.data.general.Dataset; import org.jfree.data.general.DatasetChangeEvent; import org.jfree.data.general.DatasetUtils; import org.jfree.data.xy.XYDataset; /** * Plots data that is in (theta, radius) pairs where * theta equal to zero is due north and increases clockwise. */ public class PolarPlot extends Plot implements ValueAxisPlot, Zoomable, RendererChangeListener, Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 3794383185924179525L; /** The default margin. */ private static final int DEFAULT_MARGIN = 20; /** The annotation margin. */ private static final double ANNOTATION_MARGIN = 7.0; /** * The default angle tick unit size. */ public static final double DEFAULT_ANGLE_TICK_UNIT_SIZE = 45.0; /** * The default angle offset. */ public static final double DEFAULT_ANGLE_OFFSET = -90.0; /** The default grid line stroke. */ public static final Stroke DEFAULT_GRIDLINE_STROKE = new BasicStroke( 0.5f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0.0f, new float[]{2.0f, 2.0f}, 0.0f); /** The default grid line paint. */ public static final Paint DEFAULT_GRIDLINE_PAINT = Color.GRAY; /** The resourceBundle for the localization. */ protected static ResourceBundle localizationResources = ResourceBundleWrapper.getBundle( "org.jfree.chart.plot.LocalizationBundle"); /** The angles that are marked with gridlines. */ private List angleTicks; /** The range axis (used for the y-values). */ private ObjectList axes; /** The axis locations. */ private ObjectList axisLocations; /** Storage for the datasets. */ private ObjectList datasets; /** Storage for the renderers. */ private ObjectList renderers; /** * The tick unit that controls the spacing between the angular grid lines. */ private TickUnit angleTickUnit; /** * An offset for the angles, to start with 0 degrees at north, east, south * or west. */ private double angleOffset; /** * A flag indicating if the angles increase counterclockwise or clockwise. */ private boolean counterClockwise; /** A flag that controls whether or not the angle labels are visible. */ private boolean angleLabelsVisible = true; /** The font used to display the angle labels - never null. */ private Font angleLabelFont = new Font("SansSerif", Font.PLAIN, 12); /** The paint used to display the angle labels. */ private transient Paint angleLabelPaint = Color.BLACK; /** A flag that controls whether the angular grid-lines are visible. */ private boolean angleGridlinesVisible; /** The stroke used to draw the angular grid-lines. */ private transient Stroke angleGridlineStroke; /** The paint used to draw the angular grid-lines. */ private transient Paint angleGridlinePaint; /** A flag that controls whether the radius grid-lines are visible. */ private boolean radiusGridlinesVisible; /** The stroke used to draw the radius grid-lines. */ private transient Stroke radiusGridlineStroke; /** The paint used to draw the radius grid-lines. */ private transient Paint radiusGridlinePaint; /** * A flag that controls whether the radial minor grid-lines are visible. */ private boolean radiusMinorGridlinesVisible; /** The annotations for the plot. */ private List cornerTextItems = new ArrayList(); /** * The actual margin in pixels. */ private int margin; /** * An optional collection of legend items that can be returned by the * getLegendItems() method. */ private LegendItemCollection fixedLegendItems; /** * Storage for the mapping between datasets/renderers and range axes. The * keys in the map are Integer objects, corresponding to the dataset * index. The values in the map are List objects containing Integer * objects (corresponding to the axis indices). If the map contains no * entry for a dataset, it is assumed to map to the primary domain axis * (index = 0). */ private Map datasetToAxesMap; /** * Default constructor. */ public PolarPlot() { this(null, null, null); } /** * Creates a new plot. * * @param dataset the dataset ({@code null} permitted). * @param radiusAxis the radius axis ({@code null} permitted). * @param renderer the renderer ({@code null} permitted). */ public PolarPlot(XYDataset dataset, ValueAxis radiusAxis, PolarItemRenderer renderer) { super(); this.datasets = new ObjectList(); this.datasets.set(0, dataset); if (dataset != null) { dataset.addChangeListener(this); } this.angleTickUnit = new NumberTickUnit(DEFAULT_ANGLE_TICK_UNIT_SIZE); this.axes = new ObjectList(); this.datasetToAxesMap = new TreeMap(); this.axes.set(0, radiusAxis); if (radiusAxis != null) { radiusAxis.setPlot(this); radiusAxis.addChangeListener(this); } // define the default locations for up to 8 axes... this.axisLocations = new ObjectList(); this.axisLocations.set(0, PolarAxisLocation.EAST_ABOVE); this.axisLocations.set(1, PolarAxisLocation.NORTH_LEFT); this.axisLocations.set(2, PolarAxisLocation.WEST_BELOW); this.axisLocations.set(3, PolarAxisLocation.SOUTH_RIGHT); this.axisLocations.set(4, PolarAxisLocation.EAST_BELOW); this.axisLocations.set(5, PolarAxisLocation.NORTH_RIGHT); this.axisLocations.set(6, PolarAxisLocation.WEST_ABOVE); this.axisLocations.set(7, PolarAxisLocation.SOUTH_LEFT); this.renderers = new ObjectList(); this.renderers.set(0, renderer); if (renderer != null) { renderer.setPlot(this); renderer.addChangeListener(this); } this.angleOffset = DEFAULT_ANGLE_OFFSET; this.counterClockwise = false; this.angleGridlinesVisible = true; this.angleGridlineStroke = DEFAULT_GRIDLINE_STROKE; this.angleGridlinePaint = DEFAULT_GRIDLINE_PAINT; this.radiusGridlinesVisible = true; this.radiusMinorGridlinesVisible = true; this.radiusGridlineStroke = DEFAULT_GRIDLINE_STROKE; this.radiusGridlinePaint = DEFAULT_GRIDLINE_PAINT; this.margin = DEFAULT_MARGIN; } /** * Returns the plot type as a string. * * @return A short string describing the type of plot. */ @Override public String getPlotType() { return PolarPlot.localizationResources.getString("Polar_Plot"); } /** * Returns the primary axis for the plot. * * @return The primary axis (possibly {@code null}). * * @see #setAxis(ValueAxis) */ public ValueAxis getAxis() { return getAxis(0); } /** * Returns an axis for the plot. * * @param index the axis index. * * @return The axis ({@code null} possible). * * @see #setAxis(int, ValueAxis) */ public ValueAxis getAxis(int index) { ValueAxis result = null; if (index < this.axes.size()) { result = (ValueAxis) this.axes.get(index); } return result; } /** * Sets the primary axis for the plot and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param axis the new primary axis ({@code null} permitted). */ public void setAxis(ValueAxis axis) { setAxis(0, axis); } /** * Sets an axis for the plot and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param index the axis index. * @param axis the axis ({@code null} permitted). * * @see #getAxis(int) */ public void setAxis(int index, ValueAxis axis) { setAxis(index, axis, true); } /** * Sets an axis for the plot and, if requested, sends a * {@link PlotChangeEvent} to all registered listeners. * * @param index the axis index. * @param axis the axis ({@code null} permitted). * @param notify notify listeners? * * @see #getAxis(int) */ public void setAxis(int index, ValueAxis axis, boolean notify) { ValueAxis existing = getAxis(index); if (existing != null) { existing.removeChangeListener(this); } if (axis != null) { axis.setPlot(this); } this.axes.set(index, axis); if (axis != null) { axis.configure(); axis.addChangeListener(this); } if (notify) { fireChangeEvent(); } } /** * Returns the location of the primary axis. * * @return The location (never {@code null}). * * @see #setAxisLocation(PolarAxisLocation) */ public PolarAxisLocation getAxisLocation() { return getAxisLocation(0); } /** * Returns the location for an axis. * * @param index the axis index. * * @return The location (never {@code null}). * * @see #setAxisLocation(int, PolarAxisLocation) */ public PolarAxisLocation getAxisLocation(int index) { PolarAxisLocation result = null; if (index < this.axisLocations.size()) { result = (PolarAxisLocation) this.axisLocations.get(index); } return result; } /** * Sets the location of the primary axis and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param location the location ({@code null} not permitted). * * @see #getAxisLocation() */ public void setAxisLocation(PolarAxisLocation location) { // delegate... setAxisLocation(0, location, true); } /** * Sets the location of the primary axis and, if requested, sends a * {@link PlotChangeEvent} to all registered listeners. * * @param location the location ({@code null} not permitted). * @param notify notify listeners? * * @see #getAxisLocation() */ public void setAxisLocation(PolarAxisLocation location, boolean notify) { // delegate... setAxisLocation(0, location, notify); } /** * Sets the location for an axis and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param index the axis index. * @param location the location ({@code null} not permitted). * * @see #getAxisLocation(int) */ public void setAxisLocation(int index, PolarAxisLocation location) { // delegate... setAxisLocation(index, location, true); } /** * Sets the axis location for an axis and, if requested, sends a * {@link PlotChangeEvent} to all registered listeners. * * @param index the axis index. * @param location the location ({@code null} not permitted). * @param notify notify listeners? */ public void setAxisLocation(int index, PolarAxisLocation location, boolean notify) { Args.nullNotPermitted(location, "location"); this.axisLocations.set(index, location); if (notify) { fireChangeEvent(); } } /** * Returns the number of domain axes. * * @return The axis count. **/ public int getAxisCount() { return this.axes.size(); } /** * Returns the primary dataset for the plot. * * @return The primary dataset (possibly {@code null}). * * @see #setDataset(XYDataset) */ public XYDataset getDataset() { return getDataset(0); } /** * Returns the dataset with the specified index, if any. * * @param index the dataset index. * * @return The dataset (possibly {@code null}). * * @see #setDataset(int, XYDataset) */ public XYDataset getDataset(int index) { XYDataset result = null; if (index < this.datasets.size()) { result = (XYDataset) this.datasets.get(index); } return result; } /** * Sets the primary dataset for the plot, replacing the existing dataset * if there is one, and sends a {@code link PlotChangeEvent} to all * registered listeners. * * @param dataset the dataset ({@code null} permitted). * * @see #getDataset() */ public void setDataset(XYDataset dataset) { setDataset(0, dataset); } /** * Sets a dataset for the plot, replacing the existing dataset at the same * index if there is one, and sends a {@code link PlotChangeEvent} to all * registered listeners. * * @param index the dataset index. * @param dataset the dataset ({@code null} permitted). * * @see #getDataset(int) */ public void setDataset(int index, XYDataset dataset) { XYDataset existing = getDataset(index); if (existing != null) { existing.removeChangeListener(this); } this.datasets.set(index, dataset); if (dataset != null) { dataset.addChangeListener(this); } // send a dataset change event to self... DatasetChangeEvent event = new DatasetChangeEvent(this, dataset); datasetChanged(event); } /** * Returns the number of datasets. * * @return The number of datasets. */ public int getDatasetCount() { return this.datasets.size(); } /** * Returns the index of the specified dataset, or {@code -1} if the * dataset does not belong to the plot. * * @param dataset the dataset ({@code null} not permitted). * * @return The index. */ public int indexOf(XYDataset dataset) { int result = -1; for (int i = 0; i < this.datasets.size(); i++) { if (dataset == this.datasets.get(i)) { result = i; break; } } return result; } /** * Returns the primary renderer. * * @return The renderer (possibly {@code null}). * * @see #setRenderer(PolarItemRenderer) */ public PolarItemRenderer getRenderer() { return getRenderer(0); } /** * Returns the renderer at the specified index, if there is one. * * @param index the renderer index. * * @return The renderer (possibly {@code null}). * * @see #setRenderer(int, PolarItemRenderer) */ public PolarItemRenderer getRenderer(int index) { PolarItemRenderer result = null; if (index < this.renderers.size()) { result = (PolarItemRenderer) this.renderers.get(index); } return result; } /** * Sets the primary renderer, and notifies all listeners of a change to the * plot. If the renderer is set to {@code null}, no data items will * be drawn for the corresponding dataset. * * @param renderer the new renderer ({@code null} permitted). * * @see #getRenderer() */ public void setRenderer(PolarItemRenderer renderer) { setRenderer(0, renderer); } /** * Sets a renderer and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param index the index. * @param renderer the renderer. * * @see #getRenderer(int) */ public void setRenderer(int index, PolarItemRenderer renderer) { setRenderer(index, renderer, true); } /** * Sets a renderer and, if requested, sends a {@link PlotChangeEvent} to * all registered listeners. * * @param index the index. * @param renderer the renderer. * @param notify notify listeners? * * @see #getRenderer(int) */ public void setRenderer(int index, PolarItemRenderer renderer, boolean notify) { PolarItemRenderer existing = getRenderer(index); if (existing != null) { existing.removeChangeListener(this); } this.renderers.set(index, renderer); if (renderer != null) { renderer.setPlot(this); renderer.addChangeListener(this); } if (notify) { fireChangeEvent(); } } /** * Returns the tick unit that controls the spacing of the angular grid * lines. * * @return The tick unit (never {@code null}). */ public TickUnit getAngleTickUnit() { return this.angleTickUnit; } /** * Sets the tick unit that controls the spacing of the angular grid * lines, and sends a {@link PlotChangeEvent} to all registered listeners. * * @param unit the tick unit ({@code null} not permitted). */ public void setAngleTickUnit(TickUnit unit) { Args.nullNotPermitted(unit, "unit"); this.angleTickUnit = unit; fireChangeEvent(); } /** * Returns the offset that is used for all angles. * * @return The offset for the angles. */ public double getAngleOffset() { return this.angleOffset; } /** * Sets the offset that is used for all angles and sends a * {@link PlotChangeEvent} to all registered listeners. * * This is useful to let 0 degrees be at the north, east, south or west * side of the chart. * * @param offset The offset */ public void setAngleOffset(double offset) { this.angleOffset = offset; fireChangeEvent(); } /** * Get the direction for growing angle degrees. * * @return {@code true} if angle increases counterclockwise, * {@code false} otherwise. */ public boolean isCounterClockwise() { return this.counterClockwise; } /** * Sets the flag for increasing angle degrees direction. * * {@code true} for counterclockwise, {@code false} for * clockwise. * * @param counterClockwise The flag. */ public void setCounterClockwise(boolean counterClockwise) { this.counterClockwise = counterClockwise; } /** * Returns a flag that controls whether or not the angle labels are visible. * * @return A boolean. * * @see #setAngleLabelsVisible(boolean) */ public boolean isAngleLabelsVisible() { return this.angleLabelsVisible; } /** * Sets the flag that controls whether or not the angle labels are visible, * and sends a {@link PlotChangeEvent} to all registered listeners. * * @param visible the flag. * * @see #isAngleLabelsVisible() */ public void setAngleLabelsVisible(boolean visible) { if (this.angleLabelsVisible != visible) { this.angleLabelsVisible = visible; fireChangeEvent(); } } /** * Returns the font used to display the angle labels. * * @return A font (never {@code null}). * * @see #setAngleLabelFont(Font) */ public Font getAngleLabelFont() { return this.angleLabelFont; } /** * Sets the font used to display the angle labels and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param font the font ({@code null} not permitted). * * @see #getAngleLabelFont() */ public void setAngleLabelFont(Font font) { Args.nullNotPermitted(font, "font"); this.angleLabelFont = font; fireChangeEvent(); } /** * Returns the paint used to display the angle labels. * * @return A paint (never {@code null}). * * @see #setAngleLabelPaint(Paint) */ public Paint getAngleLabelPaint() { return this.angleLabelPaint; } /** * Sets the paint used to display the angle labels and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). */ public void setAngleLabelPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.angleLabelPaint = paint; fireChangeEvent(); } /** * Returns {@code true} if the angular gridlines are visible, and * {@code false} otherwise. * * @return {@code true} or {@code false}. * * @see #setAngleGridlinesVisible(boolean) */ public boolean isAngleGridlinesVisible() { return this.angleGridlinesVisible; } /** * Sets the flag that controls whether or not the angular grid-lines are * visible. *

* If the flag value is changed, a {@link PlotChangeEvent} is sent to all * registered listeners. * * @param visible the new value of the flag. * * @see #isAngleGridlinesVisible() */ public void setAngleGridlinesVisible(boolean visible) { if (this.angleGridlinesVisible != visible) { this.angleGridlinesVisible = visible; fireChangeEvent(); } } /** * Returns the stroke for the grid-lines (if any) plotted against the * angular axis. * * @return The stroke (possibly {@code null}). * * @see #setAngleGridlineStroke(Stroke) */ public Stroke getAngleGridlineStroke() { return this.angleGridlineStroke; } /** * Sets the stroke for the grid lines plotted against the angular axis and * sends a {@link PlotChangeEvent} to all registered listeners. *

* If you set this to {@code null}, no grid lines will be drawn. * * @param stroke the stroke ({@code null} permitted). * * @see #getAngleGridlineStroke() */ public void setAngleGridlineStroke(Stroke stroke) { this.angleGridlineStroke = stroke; fireChangeEvent(); } /** * Returns the paint for the grid lines (if any) plotted against the * angular axis. * * @return The paint (possibly {@code null}). * * @see #setAngleGridlinePaint(Paint) */ public Paint getAngleGridlinePaint() { return this.angleGridlinePaint; } /** * Sets the paint for the grid lines plotted against the angular axis. *

* If you set this to {@code null}, no grid lines will be drawn. * * @param paint the paint ({@code null} permitted). * * @see #getAngleGridlinePaint() */ public void setAngleGridlinePaint(Paint paint) { this.angleGridlinePaint = paint; fireChangeEvent(); } /** * Returns {@code true} if the radius axis grid is visible, and * {@code false} otherwise. * * @return {@code true} or {@code false}. * * @see #setRadiusGridlinesVisible(boolean) */ public boolean isRadiusGridlinesVisible() { return this.radiusGridlinesVisible; } /** * Sets the flag that controls whether or not the radius axis grid lines * are visible. *

* If the flag value is changed, a {@link PlotChangeEvent} is sent to all * registered listeners. * * @param visible the new value of the flag. * * @see #isRadiusGridlinesVisible() */ public void setRadiusGridlinesVisible(boolean visible) { if (this.radiusGridlinesVisible != visible) { this.radiusGridlinesVisible = visible; fireChangeEvent(); } } /** * Returns the stroke for the grid lines (if any) plotted against the * radius axis. * * @return The stroke (possibly {@code null}). * * @see #setRadiusGridlineStroke(Stroke) */ public Stroke getRadiusGridlineStroke() { return this.radiusGridlineStroke; } /** * Sets the stroke for the grid lines plotted against the radius axis and * sends a {@link PlotChangeEvent} to all registered listeners. *

* If you set this to {@code null}, no grid lines will be drawn. * * @param stroke the stroke ({@code null} permitted). * * @see #getRadiusGridlineStroke() */ public void setRadiusGridlineStroke(Stroke stroke) { this.radiusGridlineStroke = stroke; fireChangeEvent(); } /** * Returns the paint for the grid lines (if any) plotted against the radius * axis. * * @return The paint (possibly {@code null}). * * @see #setRadiusGridlinePaint(Paint) */ public Paint getRadiusGridlinePaint() { return this.radiusGridlinePaint; } /** * Sets the paint for the grid lines plotted against the radius axis and * sends a {@link PlotChangeEvent} to all registered listeners. *

* If you set this to {@code null}, no grid lines will be drawn. * * @param paint the paint ({@code null} permitted). * * @see #getRadiusGridlinePaint() */ public void setRadiusGridlinePaint(Paint paint) { this.radiusGridlinePaint = paint; fireChangeEvent(); } /** * Return the current value of the flag indicating if radial minor * grid-lines will be drawn or not. * * @return Returns {@code true} if radial minor grid-lines are drawn. */ public boolean isRadiusMinorGridlinesVisible() { return this.radiusMinorGridlinesVisible; } /** * Set the flag that determines if radial minor grid-lines will be drawn, * and sends a {@link PlotChangeEvent} to all registered listeners. * * @param flag {@code true} to draw the radial minor grid-lines, * {@code false} to hide them. */ public void setRadiusMinorGridlinesVisible(boolean flag) { this.radiusMinorGridlinesVisible = flag; fireChangeEvent(); } /** * Returns the margin around the plot area. * * @return The actual margin in pixels. */ public int getMargin() { return this.margin; } /** * Set the margin around the plot area and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param margin The new margin in pixels. */ public void setMargin(int margin) { this.margin = margin; fireChangeEvent(); } /** * Returns the fixed legend items, if any. * * @return The legend items (possibly {@code null}). * * @see #setFixedLegendItems(LegendItemCollection) */ public LegendItemCollection getFixedLegendItems() { return this.fixedLegendItems; } /** * Sets the fixed legend items for the plot. Leave this set to * {@code null} if you prefer the legend items to be created * automatically. * * @param items the legend items ({@code null} permitted). * * @see #getFixedLegendItems() */ public void setFixedLegendItems(LegendItemCollection items) { this.fixedLegendItems = items; fireChangeEvent(); } /** * Add text to be displayed in the lower right hand corner and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param text the text to display ({@code null} not permitted). * * @see #removeCornerTextItem(String) */ public void addCornerTextItem(String text) { Args.nullNotPermitted(text, "text"); this.cornerTextItems.add(text); fireChangeEvent(); } /** * Remove the given text from the list of corner text items and * sends a {@link PlotChangeEvent} to all registered listeners. * * @param text the text to remove ({@code null} ignored). * * @see #addCornerTextItem(String) */ public void removeCornerTextItem(String text) { boolean removed = this.cornerTextItems.remove(text); if (removed) { fireChangeEvent(); } } /** * Clear the list of corner text items and sends a {@link PlotChangeEvent} * to all registered listeners. * * @see #addCornerTextItem(String) * @see #removeCornerTextItem(String) */ public void clearCornerTextItems() { if (this.cornerTextItems.size() > 0) { this.cornerTextItems.clear(); fireChangeEvent(); } } /** * Generates a list of tick values for the angular tick marks. * * @return A list of {@link NumberTick} instances. */ protected List refreshAngleTicks() { List ticks = new ArrayList(); for (double currentTickVal = 0.0; currentTickVal < 360.0; currentTickVal += this.angleTickUnit.getSize()) { TextAnchor ta = calculateTextAnchor(currentTickVal); NumberTick tick = new NumberTick(currentTickVal, this.angleTickUnit.valueToString(currentTickVal), ta, TextAnchor.CENTER, 0.0); ticks.add(tick); } return ticks; } /** * Calculate the text position for the given degrees. * * @param angleDegrees the angle in degrees. * * @return The optimal text anchor. */ protected TextAnchor calculateTextAnchor(double angleDegrees) { TextAnchor ta = TextAnchor.CENTER; // normalize angle double offset = this.angleOffset; while (offset < 0.0) { offset += 360.0; } double normalizedAngle = (((this.counterClockwise ? -1 : 1) * angleDegrees) + offset) % 360; while (this.counterClockwise && (normalizedAngle < 0.0)) { normalizedAngle += 360.0; } if (normalizedAngle == 0.0) { ta = TextAnchor.CENTER_LEFT; } else if (normalizedAngle > 0.0 && normalizedAngle < 90.0) { ta = TextAnchor.TOP_LEFT; } else if (normalizedAngle == 90.0) { ta = TextAnchor.TOP_CENTER; } else if (normalizedAngle > 90.0 && normalizedAngle < 180.0) { ta = TextAnchor.TOP_RIGHT; } else if (normalizedAngle == 180) { ta = TextAnchor.CENTER_RIGHT; } else if (normalizedAngle > 180.0 && normalizedAngle < 270.0) { ta = TextAnchor.BOTTOM_RIGHT; } else if (normalizedAngle == 270) { ta = TextAnchor.BOTTOM_CENTER; } else if (normalizedAngle > 270.0 && normalizedAngle < 360.0) { ta = TextAnchor.BOTTOM_LEFT; } return ta; } /** * Maps a dataset to a particular axis. All data will be plotted * against axis zero by default, no mapping is required for this case. * * @param index the dataset index (zero-based). * @param axisIndex the axis index. */ public void mapDatasetToAxis(int index, int axisIndex) { List axisIndices = new ArrayList<>(1); axisIndices.add(axisIndex); mapDatasetToAxes(index, axisIndices); } /** * Maps the specified dataset to the axes in the list. Note that the * conversion of data values into Java2D space is always performed using * the first axis in the list. * * @param index the dataset index (zero-based). * @param axisIndices the axis indices ({@code null} permitted). */ public void mapDatasetToAxes(int index, List axisIndices) { if (index < 0) { throw new IllegalArgumentException("Requires 'index' >= 0."); } checkAxisIndices(axisIndices); Integer key = index; this.datasetToAxesMap.put(key, new ArrayList(axisIndices)); // fake a dataset change event to update axes... datasetChanged(new DatasetChangeEvent(this, getDataset(index))); } /** * This method is used to perform argument checking on the list of * axis indices passed to mapDatasetToAxes(). * * @param indices the list of indices ({@code null} permitted). */ private void checkAxisIndices(List indices) { // axisIndices can be: // 1. null; // 2. non-empty, containing only Integer objects that are unique. if (indices == null) { return; // OK } int count = indices.size(); if (count == 0) { throw new IllegalArgumentException("Empty list not permitted."); } HashSet set = new HashSet(); for (int i = 0; i < count; i++) { Object item = indices.get(i); if (!(item instanceof Integer)) { throw new IllegalArgumentException( "Indices must be Integer instances."); } if (set.contains(item)) { throw new IllegalArgumentException("Indices must be unique."); } set.add(item); } } /** * Returns the axis for a dataset. * * @param index the dataset index. * * @return The axis. */ public ValueAxis getAxisForDataset(int index) { ValueAxis valueAxis; List axisIndices = (List) this.datasetToAxesMap.get(index); if (axisIndices != null) { // the first axis in the list is used for data <--> Java2D Integer axisIndex = (Integer) axisIndices.get(0); valueAxis = getAxis(axisIndex); } else { valueAxis = getAxis(0); } return valueAxis; } /** * Returns the index of the given axis. * * @param axis the axis. * * @return The axis index or -1 if axis is not used in this plot. */ public int getAxisIndex(ValueAxis axis) { int result = this.axes.indexOf(axis); if (result < 0) { // try the parent plot Plot parent = getParent(); if (parent instanceof PolarPlot) { PolarPlot p = (PolarPlot) parent; result = p.getAxisIndex(axis); } } return result; } /** * Returns the index of the specified renderer, or {@code -1} if the * renderer is not assigned to this plot. * * @param renderer the renderer ({@code null} permitted). * * @return The renderer index. */ public int getIndexOf(PolarItemRenderer renderer) { return this.renderers.indexOf(renderer); } /** * Draws the plot on a Java 2D graphics device (such as the screen or a * printer). *

* This plot relies on a {@link PolarItemRenderer} to draw each * item in the plot. This allows the visual representation of the data to * be changed easily. *

* The optional info argument collects information about the rendering of * the plot (dimensions, tooltip information etc). Just pass in * {@code null} if you do not need this information. * * @param g2 the graphics device. * @param area the area within which the plot (including axes and * labels) should be drawn. * @param anchor the anchor point ({@code null} permitted). * @param parentState ignored. * @param info collects chart drawing information ({@code null} * permitted). */ @Override public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, PlotState parentState, PlotRenderingInfo info) { // if the plot area is too small, just return... boolean b1 = (area.getWidth() <= MINIMUM_WIDTH_TO_DRAW); boolean b2 = (area.getHeight() <= MINIMUM_HEIGHT_TO_DRAW); if (b1 || b2) { return; } // record the plot area... if (info != null) { info.setPlotArea(area); } // adjust the drawing area for the plot insets (if any)... RectangleInsets insets = getInsets(); insets.trim(area); Rectangle2D dataArea = area; if (info != null) { info.setDataArea(dataArea); } // draw the plot background and axes... drawBackground(g2, dataArea); int axisCount = this.axes.size(); AxisState state = null; for (int i = 0; i < axisCount; i++) { ValueAxis axis = getAxis(i); if (axis != null) { PolarAxisLocation location = (PolarAxisLocation) this.axisLocations.get(i); AxisState s = this.drawAxis(axis, location, g2, dataArea); if (i == 0) { state = s; } } } // now for each dataset, get the renderer and the appropriate axis // and render the dataset... Shape originalClip = g2.getClip(); Composite originalComposite = g2.getComposite(); g2.clip(dataArea); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, getForegroundAlpha())); this.angleTicks = refreshAngleTicks(); drawGridlines(g2, dataArea, this.angleTicks, state.getTicks()); render(g2, dataArea, info); g2.setClip(originalClip); g2.setComposite(originalComposite); drawOutline(g2, dataArea); drawCornerTextItems(g2, dataArea); } /** * Draws the corner text items. * * @param g2 the drawing surface. * @param area the area. */ protected void drawCornerTextItems(Graphics2D g2, Rectangle2D area) { if (this.cornerTextItems.isEmpty()) { return; } g2.setColor(Color.BLACK); double width = 0.0; double height = 0.0; for (Iterator it = this.cornerTextItems.iterator(); it.hasNext();) { String msg = (String) it.next(); FontMetrics fm = g2.getFontMetrics(); Rectangle2D bounds = TextUtils.getTextBounds(msg, g2, fm); width = Math.max(width, bounds.getWidth()); height += bounds.getHeight(); } double xadj = ANNOTATION_MARGIN * 2.0; double yadj = ANNOTATION_MARGIN; width += xadj; height += yadj; double x = area.getMaxX() - width; double y = area.getMaxY() - height; g2.drawRect((int) x, (int) y, (int) width, (int) height); x += ANNOTATION_MARGIN; for (Iterator it = this.cornerTextItems.iterator(); it.hasNext();) { String msg = (String) it.next(); Rectangle2D bounds = TextUtils.getTextBounds(msg, g2, g2.getFontMetrics()); y += bounds.getHeight(); g2.drawString(msg, (int) x, (int) y); } } /** * Draws the axis with the specified index. * * @param axis the axis. * @param location the axis location. * @param g2 the graphics target. * @param plotArea the plot area. * * @return The axis state. */ protected AxisState drawAxis(ValueAxis axis, PolarAxisLocation location, Graphics2D g2, Rectangle2D plotArea) { double centerX = plotArea.getCenterX(); double centerY = plotArea.getCenterY(); double r = Math.min(plotArea.getWidth() / 2.0, plotArea.getHeight() / 2.0) - this.margin; double x = centerX - r; double y = centerY - r; Rectangle2D dataArea; AxisState result = null; if (location == PolarAxisLocation.NORTH_RIGHT) { dataArea = new Rectangle2D.Double(x, y, r, r); result = axis.draw(g2, centerX, plotArea, dataArea, RectangleEdge.RIGHT, null); } else if (location == PolarAxisLocation.NORTH_LEFT) { dataArea = new Rectangle2D.Double(centerX, y, r, r); result = axis.draw(g2, centerX, plotArea, dataArea, RectangleEdge.LEFT, null); } else if (location == PolarAxisLocation.SOUTH_LEFT) { dataArea = new Rectangle2D.Double(centerX, centerY, r, r); result = axis.draw(g2, centerX, plotArea, dataArea, RectangleEdge.LEFT, null); } else if (location == PolarAxisLocation.SOUTH_RIGHT) { dataArea = new Rectangle2D.Double(x, centerY, r, r); result = axis.draw(g2, centerX, plotArea, dataArea, RectangleEdge.RIGHT, null); } else if (location == PolarAxisLocation.EAST_ABOVE) { dataArea = new Rectangle2D.Double(centerX, centerY, r, r); result = axis.draw(g2, centerY, plotArea, dataArea, RectangleEdge.TOP, null); } else if (location == PolarAxisLocation.EAST_BELOW) { dataArea = new Rectangle2D.Double(centerX, y, r, r); result = axis.draw(g2, centerY, plotArea, dataArea, RectangleEdge.BOTTOM, null); } else if (location == PolarAxisLocation.WEST_ABOVE) { dataArea = new Rectangle2D.Double(x, centerY, r, r); result = axis.draw(g2, centerY, plotArea, dataArea, RectangleEdge.TOP, null); } else if (location == PolarAxisLocation.WEST_BELOW) { dataArea = new Rectangle2D.Double(x, y, r, r); result = axis.draw(g2, centerY, plotArea, dataArea, RectangleEdge.BOTTOM, null); } return result; } /** * Draws a representation of the data within the dataArea region, using the * current m_Renderer. * * @param g2 the graphics device. * @param dataArea the region in which the data is to be drawn. * @param info an optional object for collection dimension * information ({@code null} permitted). */ protected void render(Graphics2D g2, Rectangle2D dataArea, PlotRenderingInfo info) { // now get the data and plot it (the visual representation will depend // on the m_Renderer that has been set)... boolean hasData = false; int datasetCount = this.datasets.size(); for (int i = datasetCount - 1; i >= 0; i--) { XYDataset dataset = getDataset(i); if (dataset == null) { continue; } PolarItemRenderer renderer = getRenderer(i); if (renderer == null) { continue; } if (!DatasetUtils.isEmptyOrNull(dataset)) { hasData = true; int seriesCount = dataset.getSeriesCount(); for (int series = 0; series < seriesCount; series++) { renderer.drawSeries(g2, dataArea, info, this, dataset, series); } } } if (!hasData) { drawNoDataMessage(g2, dataArea); } } /** * Draws the gridlines for the plot, if they are visible. * * @param g2 the graphics device. * @param dataArea the data area. * @param angularTicks the ticks for the angular axis. * @param radialTicks the ticks for the radial axis. */ protected void drawGridlines(Graphics2D g2, Rectangle2D dataArea, List angularTicks, List radialTicks) { PolarItemRenderer renderer = getRenderer(); // no renderer, no gridlines... if (renderer == null) { return; } // draw the domain grid lines, if any... if (isAngleGridlinesVisible()) { Stroke gridStroke = getAngleGridlineStroke(); Paint gridPaint = getAngleGridlinePaint(); if ((gridStroke != null) && (gridPaint != null)) { renderer.drawAngularGridLines(g2, this, angularTicks, dataArea); } } // draw the radius grid lines, if any... if (isRadiusGridlinesVisible()) { Stroke gridStroke = getRadiusGridlineStroke(); Paint gridPaint = getRadiusGridlinePaint(); if ((gridStroke != null) && (gridPaint != null)) { List ticks = buildRadialTicks(radialTicks); renderer.drawRadialGridLines(g2, this, getAxis(), ticks, dataArea); } } } /** * Create a list of ticks based on the given list and plot properties. * Only ticks of a specific type may be in the result list. * * @param allTicks A list of all available ticks for the primary axis. * {@code null} not permitted. * @return Ticks to use for radial gridlines. */ protected List buildRadialTicks(List allTicks) { List ticks = new ArrayList(); Iterator it = allTicks.iterator(); while (it.hasNext()) { ValueTick tick = (ValueTick) it.next(); if (isRadiusMinorGridlinesVisible() || TickType.MAJOR.equals(tick.getTickType())) { ticks.add(tick); } } return ticks; } /** * Zooms the axis ranges by the specified percentage about the anchor point. * * @param percent the amount of the zoom. */ @Override public void zoom(double percent) { for (int axisIdx = 0; axisIdx < getAxisCount(); axisIdx++) { final ValueAxis axis = getAxis(axisIdx); if (axis != null) { if (percent > 0.0) { double radius = axis.getUpperBound(); double scaledRadius = radius * percent; axis.setUpperBound(scaledRadius); axis.setAutoRange(false); } else { axis.setAutoRange(true); } } } } /** * A utility method that returns a list of datasets that are mapped to a * particular axis. * * @param axisIndex the axis index ({@code null} not permitted). * * @return A list of datasets. */ private List getDatasetsMappedToAxis(Integer axisIndex) { Args.nullNotPermitted(axisIndex, "axisIndex"); List result = new ArrayList(); for (int i = 0; i < this.datasets.size(); i++) { List mappedAxes = (List) this.datasetToAxesMap.get(i); if (mappedAxes == null) { if (axisIndex.equals(ZERO)) { result.add(this.datasets.get(i)); } } else { if (mappedAxes.contains(axisIndex)) { result.add(this.datasets.get(i)); } } } return result; } /** * Returns the range for the specified axis. * * @param axis the axis. * * @return The range. */ @Override public Range getDataRange(ValueAxis axis) { Range result = null; int axisIdx = getAxisIndex(axis); List mappedDatasets = new ArrayList(); if (axisIdx >= 0) { mappedDatasets = getDatasetsMappedToAxis(axisIdx); } // iterate through the datasets that map to the axis and get the union // of the ranges. Iterator iterator = mappedDatasets.iterator(); int datasetIdx = -1; while (iterator.hasNext()) { datasetIdx++; XYDataset d = (XYDataset) iterator.next(); if (d != null) { // FIXME better ask the renderer instead of DatasetUtilities result = Range.combine(result, DatasetUtils.findRangeBounds(d)); } } return result; } /** * Receives notification of a change to the plot's m_Dataset. *

* The axis ranges are updated if necessary. * * @param event information about the event (not used here). */ @Override public void datasetChanged(DatasetChangeEvent event) { for (int i = 0; i < this.axes.size(); i++) { final ValueAxis axis = (ValueAxis) this.axes.get(i); if (axis != null) { axis.configure(); } } if (getParent() != null) { getParent().datasetChanged(event); } else { super.datasetChanged(event); } } /** * Notifies all registered listeners of a property change. *

* One source of property change events is the plot's m_Renderer. * * @param event information about the property change. */ @Override public void rendererChanged(RendererChangeEvent event) { fireChangeEvent(); } /** * Returns the legend items for the plot. Each legend item is generated by * the plot's m_Renderer, since the m_Renderer is responsible for the visual * representation of the data. * * @return The legend items. */ @Override public LegendItemCollection getLegendItems() { if (this.fixedLegendItems != null) { return this.fixedLegendItems; } LegendItemCollection result = new LegendItemCollection(); int count = this.datasets.size(); for (int datasetIndex = 0; datasetIndex < count; datasetIndex++) { XYDataset dataset = getDataset(datasetIndex); PolarItemRenderer renderer = getRenderer(datasetIndex); if (dataset != null && renderer != null) { int seriesCount = dataset.getSeriesCount(); for (int i = 0; i < seriesCount; i++) { LegendItem item = renderer.getLegendItem(i); result.add(item); } } } return result; } /** * Tests this plot for equality with another object. * * @param obj the object ({@code null} permitted). * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof PolarPlot)) { return false; } PolarPlot that = (PolarPlot) obj; if (!this.axes.equals(that.axes)) { return false; } if (!this.axisLocations.equals(that.axisLocations)) { return false; } if (!this.renderers.equals(that.renderers)) { return false; } if (!this.angleTickUnit.equals(that.angleTickUnit)) { return false; } if (this.angleGridlinesVisible != that.angleGridlinesVisible) { return false; } if (this.angleOffset != that.angleOffset) { return false; } if (this.counterClockwise != that.counterClockwise) { return false; } if (this.angleLabelsVisible != that.angleLabelsVisible) { return false; } if (!this.angleLabelFont.equals(that.angleLabelFont)) { return false; } if (!PaintUtils.equal(this.angleLabelPaint, that.angleLabelPaint)) { return false; } if (!Objects.equals(this.angleGridlineStroke, that.angleGridlineStroke)) { return false; } if (!PaintUtils.equal( this.angleGridlinePaint, that.angleGridlinePaint )) { return false; } if (this.radiusGridlinesVisible != that.radiusGridlinesVisible) { return false; } if (!Objects.equals(this.radiusGridlineStroke, that.radiusGridlineStroke)) { return false; } if (!PaintUtils.equal(this.radiusGridlinePaint, that.radiusGridlinePaint)) { return false; } if (this.radiusMinorGridlinesVisible != that.radiusMinorGridlinesVisible) { return false; } if (!this.cornerTextItems.equals(that.cornerTextItems)) { return false; } if (this.margin != that.margin) { return false; } if (!Objects.equals(this.fixedLegendItems, that.fixedLegendItems)) { return false; } return super.equals(obj); } /** * Returns a clone of the plot. * * @return A clone. * * @throws CloneNotSupportedException this can occur if some component of * the plot cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { PolarPlot clone = (PolarPlot) super.clone(); clone.axes = (ObjectList) ObjectUtils.clone(this.axes); for (int i = 0; i < this.axes.size(); i++) { ValueAxis axis = (ValueAxis) this.axes.get(i); if (axis != null) { ValueAxis clonedAxis = (ValueAxis) axis.clone(); clone.axes.set(i, clonedAxis); clonedAxis.setPlot(clone); clonedAxis.addChangeListener(clone); } } // the datasets are not cloned, but listeners need to be added... clone.datasets = (ObjectList) ObjectUtils.clone(this.datasets); for (int i = 0; i < clone.datasets.size(); ++i) { XYDataset d = getDataset(i); if (d != null) { d.addChangeListener(clone); } } clone.renderers = (ObjectList) ObjectUtils.clone(this.renderers); for (int i = 0; i < this.renderers.size(); i++) { PolarItemRenderer renderer2 = (PolarItemRenderer) this.renderers.get(i); if (renderer2 instanceof PublicCloneable) { PublicCloneable pc = (PublicCloneable) renderer2; PolarItemRenderer rc = (PolarItemRenderer) pc.clone(); clone.renderers.set(i, rc); rc.setPlot(clone); rc.addChangeListener(clone); } } clone.cornerTextItems = new ArrayList(this.cornerTextItems); return clone; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeStroke(this.angleGridlineStroke, stream); SerialUtils.writePaint(this.angleGridlinePaint, stream); SerialUtils.writeStroke(this.radiusGridlineStroke, stream); SerialUtils.writePaint(this.radiusGridlinePaint, stream); SerialUtils.writePaint(this.angleLabelPaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.angleGridlineStroke = SerialUtils.readStroke(stream); this.angleGridlinePaint = SerialUtils.readPaint(stream); this.radiusGridlineStroke = SerialUtils.readStroke(stream); this.radiusGridlinePaint = SerialUtils.readPaint(stream); this.angleLabelPaint = SerialUtils.readPaint(stream); int rangeAxisCount = this.axes.size(); for (int i = 0; i < rangeAxisCount; i++) { Axis axis = (Axis) this.axes.get(i); if (axis != null) { axis.setPlot(this); axis.addChangeListener(this); } } int datasetCount = this.datasets.size(); for (int i = 0; i < datasetCount; i++) { Dataset dataset = (Dataset) this.datasets.get(i); if (dataset != null) { dataset.addChangeListener(this); } } int rendererCount = this.renderers.size(); for (int i = 0; i < rendererCount; i++) { PolarItemRenderer renderer = (PolarItemRenderer) this.renderers.get(i); if (renderer != null) { renderer.addChangeListener(this); } } } /** * This method is required by the {@link Zoomable} interface, but since * the plot does not have any domain axes, it does nothing. * * @param factor the zoom factor. * @param state the plot state. * @param source the source point (in Java2D coordinates). */ @Override public void zoomDomainAxes(double factor, PlotRenderingInfo state, Point2D source) { // do nothing } /** * This method is required by the {@link Zoomable} interface, but since * the plot does not have any domain axes, it does nothing. * * @param factor the zoom factor. * @param state the plot state. * @param source the source point (in Java2D coordinates). * @param useAnchor use source point as zoom anchor? */ @Override public void zoomDomainAxes(double factor, PlotRenderingInfo state, Point2D source, boolean useAnchor) { // do nothing } /** * This method is required by the {@link Zoomable} interface, but since * the plot does not have any domain axes, it does nothing. * * @param lowerPercent the new lower bound. * @param upperPercent the new upper bound. * @param state the plot state. * @param source the source point (in Java2D coordinates). */ @Override public void zoomDomainAxes(double lowerPercent, double upperPercent, PlotRenderingInfo state, Point2D source) { // do nothing } /** * Multiplies the range on the range axis/axes by the specified factor. * * @param factor the zoom factor. * @param state the plot state. * @param source the source point (in Java2D coordinates). */ @Override public void zoomRangeAxes(double factor, PlotRenderingInfo state, Point2D source) { zoom(factor); } /** * Multiplies the range on the range axis by the specified factor. * * @param factor the zoom factor. * @param info the plot rendering info. * @param source the source point (in Java2D space). * @param useAnchor use source point as zoom anchor? * * @see #zoomDomainAxes(double, PlotRenderingInfo, Point2D, boolean) */ @Override public void zoomRangeAxes(double factor, PlotRenderingInfo info, Point2D source, boolean useAnchor) { // get the source coordinate - this plot has always a VERTICAL // orientation final double sourceX = source.getX(); for (int axisIdx = 0; axisIdx < getAxisCount(); axisIdx++) { final ValueAxis axis = getAxis(axisIdx); if (axis != null) { if (useAnchor) { double anchorX = axis.java2DToValue(sourceX, info.getDataArea(), RectangleEdge.BOTTOM); axis.resizeRange(factor, anchorX); } else { axis.resizeRange(factor); } } } } /** * Zooms in on the range axes. * * @param lowerPercent the new lower bound. * @param upperPercent the new upper bound. * @param state the plot state. * @param source the source point (in Java2D coordinates). */ @Override public void zoomRangeAxes(double lowerPercent, double upperPercent, PlotRenderingInfo state, Point2D source) { zoom((upperPercent + lowerPercent) / 2.0); } /** * Returns {@code false} always. * * @return {@code false} always. */ @Override public boolean isDomainZoomable() { return false; } /** * Returns {@code true} to indicate that the range axis is zoomable. * * @return {@code true}. */ @Override public boolean isRangeZoomable() { return true; } /** * Returns the orientation of the plot. * * @return The orientation. */ @Override public PlotOrientation getOrientation() { return PlotOrientation.HORIZONTAL; } /** * Translates a (theta, radius) pair into Java2D coordinates. If * {@code radius} is less than the lower bound of the axis, then * this method returns the centre point. * * @param angleDegrees the angle in degrees. * @param radius the radius. * @param axis the axis. * @param dataArea the data area. * * @return A point in Java2D space. */ public Point translateToJava2D(double angleDegrees, double radius, ValueAxis axis, Rectangle2D dataArea) { if (counterClockwise) { angleDegrees = -angleDegrees; } double radians = Math.toRadians(angleDegrees + this.angleOffset); double minx = dataArea.getMinX() + this.margin; double maxx = dataArea.getMaxX() - this.margin; double miny = dataArea.getMinY() + this.margin; double maxy = dataArea.getMaxY() - this.margin; double halfWidth = (maxx - minx) / 2.0; double halfHeight = (maxy - miny) / 2.0; double midX = minx + halfWidth; double midY = miny + halfHeight; double l = Math.min(halfWidth, halfHeight); Rectangle2D quadrant = new Rectangle2D.Double(midX, midY, l, l); double axisMin = axis.getLowerBound(); double adjustedRadius = Math.max(radius, axisMin); double length = axis.valueToJava2D(adjustedRadius, quadrant, RectangleEdge.BOTTOM) - midX; float x = (float) (midX + Math.cos(radians) * length); float y = (float) (midY + Math.sin(radians) * length); int ix = Math.round(x); int iy = Math.round(y); Point p = new Point(ix, iy); return p; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/RingPlot.java000066400000000000000000000560361463604235500264470ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------- * RingPlot.java * ------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Christoph Beck (bug 2121818); * */ package org.jfree.chart.plot; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Arc2D; import java.awt.geom.GeneralPath; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.text.DecimalFormat; import java.text.Format; import java.util.Objects; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.entity.PieSectionEntity; import org.jfree.chart.labels.PieToolTipGenerator; import org.jfree.chart.text.TextUtils; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.ui.TextAnchor; import org.jfree.chart.urls.PieURLGenerator; import org.jfree.chart.util.LineUtils; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.Rotation; import org.jfree.chart.util.SerialUtils; import org.jfree.chart.util.ShapeUtils; import org.jfree.chart.util.UnitType; import org.jfree.data.general.PieDataset; /** * A customised pie plot that leaves a hole in the middle. */ public class RingPlot extends PiePlot implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 1556064784129676620L; /** The center text mode. */ private CenterTextMode centerTextMode = CenterTextMode.NONE; /** * Text to display in the middle of the chart (used for * CenterTextMode.FIXED). */ private String centerText; /** * The formatter used when displaying the first data value from the * dataset (CenterTextMode.VALUE). */ private Format centerTextFormatter = new DecimalFormat("0.00"); /** The font used to display the center text. */ private Font centerTextFont; /** The color used to display the center text. */ private Color centerTextColor; /** * A flag that controls whether or not separators are drawn between the * sections of the chart. */ private boolean separatorsVisible; /** The stroke used to draw separators. */ private transient Stroke separatorStroke; /** The paint used to draw separators. */ private transient Paint separatorPaint; /** * The length of the inner separator extension (as a percentage of the * depth of the sections). */ private double innerSeparatorExtension; /** * The length of the outer separator extension (as a percentage of the * depth of the sections). */ private double outerSeparatorExtension; /** * The depth of the section as a percentage of the diameter. */ private double sectionDepth; /** * Creates a new plot with a {@code null} dataset. */ public RingPlot() { this(null); } /** * Creates a new plot for the specified dataset. * * @param dataset the dataset ({@code null} permitted). */ public RingPlot(PieDataset dataset) { super(dataset); this.centerTextMode = CenterTextMode.NONE; this.centerText = null; this.centerTextFormatter = new DecimalFormat("0.00"); this.centerTextFont = DEFAULT_LABEL_FONT; this.centerTextColor = Color.BLACK; this.separatorsVisible = true; this.separatorStroke = new BasicStroke(0.5f); this.separatorPaint = Color.GRAY; this.innerSeparatorExtension = 0.20; // 20% this.outerSeparatorExtension = 0.20; // 20% this.sectionDepth = 0.20; // 20% } /** * Returns the mode for displaying text in the center of the plot. The * default value is {@link CenterTextMode#NONE} therefore no text * will be displayed by default. * * @return The mode (never {@code null}). */ public CenterTextMode getCenterTextMode() { return this.centerTextMode; } /** * Sets the mode for displaying text in the center of the plot and sends * a change event to all registered listeners. For * {@link CenterTextMode#FIXED}, the display text will come from the * {@code centerText} attribute (see {@link #getCenterText()}). * For {@link CenterTextMode#VALUE}, the center text will be the value from * the first section in the dataset. * * @param mode the mode ({@code null} not permitted). */ public void setCenterTextMode(CenterTextMode mode) { Args.nullNotPermitted(mode, "mode"); this.centerTextMode = mode; fireChangeEvent(); } /** * Returns the text to display in the center of the plot when the mode * is {@link CenterTextMode#FIXED}. * * @return The text (possibly {@code null}). */ public String getCenterText() { return this.centerText; } /** * Sets the text to display in the center of the plot and sends a * change event to all registered listeners. If the text is set to * {@code null}, no text will be displayed. * * @param text the text ({@code null} permitted). */ public void setCenterText(String text) { this.centerText = text; fireChangeEvent(); } /** * Returns the formatter used to format the center text value for the mode * {@link CenterTextMode#VALUE}. The default value is * {@code DecimalFormat("0.00")}. * * @return The formatter (never {@code null}). */ public Format getCenterTextFormatter() { return this.centerTextFormatter; } /** * Sets the formatter used to format the center text value and sends a * change event to all registered listeners. * * @param formatter the formatter ({@code null} not permitted). */ public void setCenterTextFormatter(Format formatter) { Args.nullNotPermitted(formatter, "formatter"); this.centerTextFormatter = formatter; } /** * Returns the font used to display the center text. The default value * is {@link PiePlot#DEFAULT_LABEL_FONT}. * * @return The font (never {@code null}). */ public Font getCenterTextFont() { return this.centerTextFont; } /** * Sets the font used to display the center text and sends a change event * to all registered listeners. * * @param font the font ({@code null} not permitted). */ public void setCenterTextFont(Font font) { Args.nullNotPermitted(font, "font"); this.centerTextFont = font; fireChangeEvent(); } /** * Returns the color for the center text. The default value is * {@code Color.BLACK}. * * @return The color (never {@code null}). */ public Color getCenterTextColor() { return this.centerTextColor; } /** * Sets the color for the center text and sends a change event to all * registered listeners. * * @param color the color ({@code null} not permitted). */ public void setCenterTextColor(Color color) { Args.nullNotPermitted(color, "color"); this.centerTextColor = color; fireChangeEvent(); } /** * Returns a flag that indicates whether or not separators are drawn between * the sections in the chart. * * @return A boolean. * * @see #setSeparatorsVisible(boolean) */ public boolean getSeparatorsVisible() { return this.separatorsVisible; } /** * Sets the flag that controls whether or not separators are drawn between * the sections in the chart, and sends a change event to all registered * listeners. * * @param visible the flag. * * @see #getSeparatorsVisible() */ public void setSeparatorsVisible(boolean visible) { this.separatorsVisible = visible; fireChangeEvent(); } /** * Returns the separator stroke. * * @return The stroke (never {@code null}). * * @see #setSeparatorStroke(Stroke) */ public Stroke getSeparatorStroke() { return this.separatorStroke; } /** * Sets the stroke used to draw the separator between sections and sends * a change event to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getSeparatorStroke() */ public void setSeparatorStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.separatorStroke = stroke; fireChangeEvent(); } /** * Returns the separator paint. * * @return The paint (never {@code null}). * * @see #setSeparatorPaint(Paint) */ public Paint getSeparatorPaint() { return this.separatorPaint; } /** * Sets the paint used to draw the separator between sections and sends a * change event to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getSeparatorPaint() */ public void setSeparatorPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.separatorPaint = paint; fireChangeEvent(); } /** * Returns the length of the inner extension of the separator line that * is drawn between sections, expressed as a percentage of the depth of * the section. * * @return The inner separator extension (as a percentage). * * @see #setInnerSeparatorExtension(double) */ public double getInnerSeparatorExtension() { return this.innerSeparatorExtension; } /** * Sets the length of the inner extension of the separator line that is * drawn between sections, as a percentage of the depth of the * sections, and sends a change event to all registered listeners. * * @param percent the percentage. * * @see #getInnerSeparatorExtension() * @see #setOuterSeparatorExtension(double) */ public void setInnerSeparatorExtension(double percent) { this.innerSeparatorExtension = percent; fireChangeEvent(); } /** * Returns the length of the outer extension of the separator line that * is drawn between sections, expressed as a percentage of the depth of * the section. * * @return The outer separator extension (as a percentage). * * @see #setOuterSeparatorExtension(double) */ public double getOuterSeparatorExtension() { return this.outerSeparatorExtension; } /** * Sets the length of the outer extension of the separator line that is * drawn between sections, as a percentage of the depth of the * sections, and sends a change event to all registered listeners. * * @param percent the percentage. * * @see #getOuterSeparatorExtension() */ public void setOuterSeparatorExtension(double percent) { this.outerSeparatorExtension = percent; fireChangeEvent(); } /** * Returns the depth of each section, expressed as a percentage of the * plot radius. * * @return The depth of each section. * * @see #setSectionDepth(double) */ public double getSectionDepth() { return this.sectionDepth; } /** * The section depth is given as percentage of the plot radius. * Specifying 1.0 results in a straightforward pie chart. * * @param sectionDepth the section depth. * * @see #getSectionDepth() */ public void setSectionDepth(double sectionDepth) { this.sectionDepth = sectionDepth; fireChangeEvent(); } /** * Initialises the plot state (which will store the total of all dataset * values, among other things). This method is called once at the * beginning of each drawing. * * @param g2 the graphics device. * @param plotArea the plot area ({@code null} not permitted). * @param plot the plot. * @param index the secondary index ({@code null} for primary * renderer). * @param info collects chart rendering information for return to caller. * * @return A state object (maintains state information relevant to one * chart drawing). */ @Override public PiePlotState initialise(Graphics2D g2, Rectangle2D plotArea, PiePlot plot, Integer index, PlotRenderingInfo info) { PiePlotState state = super.initialise(g2, plotArea, plot, index, info); state.setPassesRequired(3); return state; } /** * Draws a single data item. * * @param g2 the graphics device ({@code null} not permitted). * @param section the section index. * @param dataArea the data plot area. * @param state state information for one chart. * @param currentPass the current pass index. */ @Override protected void drawItem(Graphics2D g2, int section, Rectangle2D dataArea, PiePlotState state, int currentPass) { PieDataset dataset = getDataset(); Number n = dataset.getValue(section); if (n == null) { return; } double value = n.doubleValue(); double angle1 = 0.0; double angle2 = 0.0; Rotation direction = getDirection(); if (direction == Rotation.CLOCKWISE) { angle1 = state.getLatestAngle(); angle2 = angle1 - value / state.getTotal() * 360.0; } else if (direction == Rotation.ANTICLOCKWISE) { angle1 = state.getLatestAngle(); angle2 = angle1 + value / state.getTotal() * 360.0; } else { throw new IllegalStateException("Rotation type not recognised."); } double angle = (angle2 - angle1); if (Math.abs(angle) > getMinimumArcAngleToDraw()) { Comparable key = getSectionKey(section); double ep = 0.0; double mep = getMaximumExplodePercent(); if (mep > 0.0) { ep = getExplodePercent(key) / mep; } Rectangle2D arcBounds = getArcBounds(state.getPieArea(), state.getExplodedPieArea(), angle1, angle, ep); Arc2D.Double arc = new Arc2D.Double(arcBounds, angle1, angle, Arc2D.OPEN); // create the bounds for the inner arc double depth = this.sectionDepth / 2.0; RectangleInsets s = new RectangleInsets(UnitType.RELATIVE, depth, depth, depth, depth); Rectangle2D innerArcBounds = new Rectangle2D.Double(); innerArcBounds.setRect(arcBounds); s.trim(innerArcBounds); // calculate inner arc in reverse direction, for later // GeneralPath construction Arc2D.Double arc2 = new Arc2D.Double(innerArcBounds, angle1 + angle, -angle, Arc2D.OPEN); GeneralPath path = new GeneralPath(); path.moveTo((float) arc.getStartPoint().getX(), (float) arc.getStartPoint().getY()); path.append(arc.getPathIterator(null), false); path.append(arc2.getPathIterator(null), true); path.closePath(); Line2D separator = new Line2D.Double(arc2.getEndPoint(), arc.getStartPoint()); if (currentPass == 0) { Paint shadowPaint = getShadowPaint(); double shadowXOffset = getShadowXOffset(); double shadowYOffset = getShadowYOffset(); if (shadowPaint != null && getShadowGenerator() == null) { Shape shadowArc = ShapeUtils.createTranslatedShape( path, (float) shadowXOffset, (float) shadowYOffset); g2.setPaint(shadowPaint); g2.fill(shadowArc); } } else if (currentPass == 1) { Paint paint = lookupSectionPaint(key); g2.setPaint(paint); g2.fill(path); Paint outlinePaint = lookupSectionOutlinePaint(key); Stroke outlineStroke = lookupSectionOutlineStroke(key); if (getSectionOutlinesVisible() && outlinePaint != null && outlineStroke != null) { g2.setPaint(outlinePaint); g2.setStroke(outlineStroke); g2.draw(path); } if (section == 0) { String nstr = null; if (this.centerTextMode.equals(CenterTextMode.VALUE)) { nstr = this.centerTextFormatter.format(n); } else if (this.centerTextMode.equals(CenterTextMode.FIXED)) { nstr = this.centerText; } if (nstr != null) { g2.setFont(this.centerTextFont); g2.setPaint(this.centerTextColor); TextUtils.drawAlignedString(nstr, g2, (float) dataArea.getCenterX(), (float) dataArea.getCenterY(), TextAnchor.CENTER); } } // add an entity for the pie section if (state.getInfo() != null) { EntityCollection entities = state.getEntityCollection(); if (entities != null) { String tip = null; PieToolTipGenerator toolTipGenerator = getToolTipGenerator(); if (toolTipGenerator != null) { tip = toolTipGenerator.generateToolTip(dataset, key); } String url = null; PieURLGenerator urlGenerator = getURLGenerator(); if (urlGenerator != null) { url = urlGenerator.generateURL(dataset, key, getPieIndex()); } PieSectionEntity entity = new PieSectionEntity(path, dataset, getPieIndex(), section, key, tip, url); entities.add(entity); } } } else if (currentPass == 2) { if (this.separatorsVisible) { Line2D extendedSeparator = LineUtils.extendLine( separator, this.innerSeparatorExtension, this.outerSeparatorExtension); g2.setStroke(this.separatorStroke); g2.setPaint(this.separatorPaint); g2.draw(extendedSeparator); } } } state.setLatestAngle(angle2); } /** * This method overrides the default value for cases where the ring plot * is very thin. This fixes bug 2121818. * * @return The label link depth, as a percentage of the plot's radius. */ @Override protected double getLabelLinkDepth() { return Math.min(super.getLabelLinkDepth(), getSectionDepth() / 2); } /** * Tests this plot for equality with an arbitrary object. * * @param obj the object to test against ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof RingPlot)) { return false; } RingPlot that = (RingPlot) obj; if (!this.centerTextMode.equals(that.centerTextMode)) { return false; } if (!Objects.equals(this.centerText, that.centerText)) { return false; } if (!this.centerTextFormatter.equals(that.centerTextFormatter)) { return false; } if (!this.centerTextFont.equals(that.centerTextFont)) { return false; } if (!this.centerTextColor.equals(that.centerTextColor)) { return false; } if (this.separatorsVisible != that.separatorsVisible) { return false; } if (!Objects.equals(this.separatorStroke, that.separatorStroke)) { return false; } if (!PaintUtils.equal(this.separatorPaint, that.separatorPaint)) { return false; } if (this.innerSeparatorExtension != that.innerSeparatorExtension) { return false; } if (this.outerSeparatorExtension != that.outerSeparatorExtension) { return false; } if (this.sectionDepth != that.sectionDepth) { return false; } return super.equals(obj); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeStroke(this.separatorStroke, stream); SerialUtils.writePaint(this.separatorPaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.separatorStroke = SerialUtils.readStroke(stream); this.separatorPaint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/SeriesRenderingOrder.java000066400000000000000000000101031463604235500307560ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * SeriesRenderingOrder.java * -------------------------- * (C) Copyright 2005-present, by David Gilbert. * * Original Author: Eric Thomas (www.isti.com); * Contributor(s): David Gilbert; * */ package org.jfree.chart.plot; import java.io.ObjectStreamException; import java.io.Serializable; /** * Defines the tokens that indicate the rendering order for series in a * {@link org.jfree.chart.plot.XYPlot}. */ public final class SeriesRenderingOrder implements Serializable { /** For serialization. */ private static final long serialVersionUID = 209336477448807735L; /** * Render series in the order 0, 1, 2, ..., N-1, where N is the number * of series. */ public static final SeriesRenderingOrder FORWARD = new SeriesRenderingOrder("SeriesRenderingOrder.FORWARD"); /** * Render series in the order N-1, N-2, ..., 2, 1, 0, where N is the * number of series. */ public static final SeriesRenderingOrder REVERSE = new SeriesRenderingOrder("SeriesRenderingOrder.REVERSE"); /** The name. */ private String name; /** * Private constructor. * * @param name the name. */ private SeriesRenderingOrder(String name) { this.name = name; } /** * Returns a string representing the object. * * @return The string (never {@code null}). */ @Override public String toString() { return this.name; } /** * Returns {@code true} if this object is equal to the specified * object, and {@code false} otherwise. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof SeriesRenderingOrder)) { return false; } SeriesRenderingOrder order = (SeriesRenderingOrder) obj; if (!this.name.equals(order.toString())) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { return this.name.hashCode(); } /** * Ensures that serialization returns the unique instances. * * @return The object. * * @throws ObjectStreamException if there is a problem. */ private Object readResolve() throws ObjectStreamException { if (this.equals(SeriesRenderingOrder.FORWARD)) { return SeriesRenderingOrder.FORWARD; } else if (this.equals(SeriesRenderingOrder.REVERSE)) { return SeriesRenderingOrder.REVERSE; } return null; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/SpiderWebPlot.java000066400000000000000000001520071463604235500274270ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * SpiderWebPlot.java * ------------------ * (C) Copyright 2005-present, by Heaps of Flavour Pty Ltd and Contributors. * * Company Info: http://www.i4-talent.com * * Original Author: Don Elliott; * Contributor(s): David Gilbert; * Nina Jeliazkova; * */ package org.jfree.chart.plot; import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Composite; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Polygon; import java.awt.Rectangle; import java.awt.Shape; import java.awt.Stroke; import java.awt.font.FontRenderContext; import java.awt.font.LineMetrics; import java.awt.geom.Arc2D; import java.awt.geom.Ellipse2D; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Iterator; import java.util.List; import java.util.Objects; import org.jfree.chart.LegendItem; import org.jfree.chart.LegendItemCollection; import org.jfree.chart.entity.CategoryItemEntity; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.PlotChangeEvent; import org.jfree.chart.labels.CategoryItemLabelGenerator; import org.jfree.chart.labels.CategoryToolTipGenerator; import org.jfree.chart.labels.StandardCategoryItemLabelGenerator; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.urls.CategoryURLGenerator; import org.jfree.chart.util.PaintList; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.Rotation; import org.jfree.chart.util.SerialUtils; import org.jfree.chart.util.ShapeUtils; import org.jfree.chart.util.StrokeList; import org.jfree.chart.util.TableOrder; import org.jfree.data.category.CategoryDataset; import org.jfree.data.general.DatasetChangeEvent; import org.jfree.data.general.DatasetUtils; /** * A plot that displays data from a {@link CategoryDataset} in the form of a * "spider web". Multiple series can be plotted on the same axis to allow * easy comparison. This plot doesn't support negative values at present. */ public class SpiderWebPlot extends Plot implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -5376340422031599463L; /** The default head radius percent (currently 1%). */ public static final double DEFAULT_HEAD = 0.01; /** The default axis label gap (currently 10%). */ public static final double DEFAULT_AXIS_LABEL_GAP = 0.10; /** The default interior gap. */ public static final double DEFAULT_INTERIOR_GAP = 0.25; /** The maximum interior gap (currently 40%). */ public static final double MAX_INTERIOR_GAP = 0.40; /** The default starting angle for the radar chart axes. */ public static final double DEFAULT_START_ANGLE = 90.0; /** The default series label font. */ public static final Font DEFAULT_LABEL_FONT = new Font("SansSerif", Font.PLAIN, 10); /** The default series label paint. */ public static final Paint DEFAULT_LABEL_PAINT = Color.BLACK; /** The default series label background paint. */ public static final Paint DEFAULT_LABEL_BACKGROUND_PAINT = new Color(255, 255, 192); /** The default series label outline paint. */ public static final Paint DEFAULT_LABEL_OUTLINE_PAINT = Color.BLACK; /** The default series label outline stroke. */ public static final Stroke DEFAULT_LABEL_OUTLINE_STROKE = new BasicStroke(0.5f); /** The default series label shadow paint. */ public static final Paint DEFAULT_LABEL_SHADOW_PAINT = Color.LIGHT_GRAY; /** * The default maximum value plotted - forces the plot to evaluate * the maximum from the data passed in */ public static final double DEFAULT_MAX_VALUE = -1.0; /** The head radius as a percentage of the available drawing area. */ protected double headPercent; /** The space left around the outside of the plot as a percentage. */ private double interiorGap; /** The gap between the labels and the axes as a %age of the radius. */ private double axisLabelGap; /** * The paint used to draw the axis lines. */ private transient Paint axisLinePaint; /** * The stroke used to draw the axis lines. */ private transient Stroke axisLineStroke; /** The dataset. */ private CategoryDataset dataset; /** The maximum value we are plotting against on each category axis */ private double maxValue; /** * The data extract order (BY_ROW or BY_COLUMN). This denotes whether * the data series are stored in rows (in which case the category names are * derived from the column keys) or in columns (in which case the category * names are derived from the row keys). */ private TableOrder dataExtractOrder; /** The starting angle. */ private double startAngle; /** The direction for drawing the radar axis and plots. */ private Rotation direction; /** The legend item shape. */ private transient Shape legendItemShape; /** The paint for ALL series (overrides list). */ private transient Paint seriesPaint; /** The series paint list. */ private PaintList seriesPaintList; /** The base series paint (fallback). */ private transient Paint baseSeriesPaint; /** The outline paint for ALL series (overrides list). */ private transient Paint seriesOutlinePaint; /** The series outline paint list. */ private PaintList seriesOutlinePaintList; /** The base series outline paint (fallback). */ private transient Paint baseSeriesOutlinePaint; /** The outline stroke for ALL series (overrides list). */ private transient Stroke seriesOutlineStroke; /** The series outline stroke list. */ private StrokeList seriesOutlineStrokeList; /** The base series outline stroke (fallback). */ private transient Stroke baseSeriesOutlineStroke; /** The font used to display the category labels. */ private Font labelFont; /** The color used to draw the category labels. */ private transient Paint labelPaint; /** The label generator. */ private CategoryItemLabelGenerator labelGenerator; /** controls if the web polygons are filled or not */ private boolean webFilled = true; /** The alpha value of the fill portion of a polygon. */ private float webFillAlpha = 0.1F; /** A tooltip generator for the plot ({@code null} permitted). */ private CategoryToolTipGenerator toolTipGenerator; /** A URL generator for the plot ({@code null} permitted). */ private CategoryURLGenerator urlGenerator; /** * Creates a default plot with no dataset. */ public SpiderWebPlot() { this(null); } /** * Creates a new spider web plot with the given dataset, with each row * representing a series. * * @param dataset the dataset ({@code null} permitted). */ public SpiderWebPlot(CategoryDataset dataset) { this(dataset, TableOrder.BY_ROW); } /** * Creates a new spider web plot with the given dataset. * * @param dataset the dataset. * @param extract controls how data is extracted ({@link TableOrder#BY_ROW} * or {@link TableOrder#BY_COLUMN}). */ public SpiderWebPlot(CategoryDataset dataset, TableOrder extract) { super(); Args.nullNotPermitted(extract, "extract"); this.dataset = dataset; if (dataset != null) { dataset.addChangeListener(this); } this.dataExtractOrder = extract; this.headPercent = DEFAULT_HEAD; this.axisLabelGap = DEFAULT_AXIS_LABEL_GAP; this.axisLinePaint = Color.BLACK; this.axisLineStroke = new BasicStroke(1.0f); this.interiorGap = DEFAULT_INTERIOR_GAP; this.startAngle = DEFAULT_START_ANGLE; this.direction = Rotation.CLOCKWISE; this.maxValue = DEFAULT_MAX_VALUE; this.seriesPaint = null; this.seriesPaintList = new PaintList(); this.baseSeriesPaint = null; this.seriesOutlinePaint = null; this.seriesOutlinePaintList = new PaintList(); this.baseSeriesOutlinePaint = DEFAULT_OUTLINE_PAINT; this.seriesOutlineStroke = null; this.seriesOutlineStrokeList = new StrokeList(); this.baseSeriesOutlineStroke = DEFAULT_OUTLINE_STROKE; this.labelFont = DEFAULT_LABEL_FONT; this.labelPaint = DEFAULT_LABEL_PAINT; this.labelGenerator = new StandardCategoryItemLabelGenerator(); this.legendItemShape = DEFAULT_LEGEND_ITEM_CIRCLE; } /** * Returns a short string describing the type of plot. * * @return The plot type. */ @Override public String getPlotType() { // return localizationResources.getString("Radar_Plot"); return ("Spider Web Plot"); } /** * Returns the dataset. * * @return The dataset (possibly {@code null}). * * @see #setDataset(CategoryDataset) */ public CategoryDataset getDataset() { return this.dataset; } /** * Sets the dataset used by the plot and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param dataset the dataset ({@code null} permitted). * * @see #getDataset() */ public void setDataset(CategoryDataset dataset) { // if there is an existing dataset, remove the plot from the list of // change listeners... if (this.dataset != null) { this.dataset.removeChangeListener(this); } // set the new dataset, and register the chart as a change listener... this.dataset = dataset; if (dataset != null) { setDatasetGroup(dataset.getGroup()); dataset.addChangeListener(this); } // send a dataset change event to self to trigger plot change event datasetChanged(new DatasetChangeEvent(this, dataset)); } /** * Method to determine if the web chart is to be filled. * * @return A boolean. * * @see #setWebFilled(boolean) */ public boolean isWebFilled() { return this.webFilled; } /** * Sets the webFilled flag and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param flag the flag. * * @see #isWebFilled() */ public void setWebFilled(boolean flag) { this.webFilled = flag; fireChangeEvent(); } /** * Returns the alpha value for filling a graph (in the range 0.0 to 1.0). * * @return The alpha value for filling a spider plot polygon. * * @see #setWebFillAlpha(float) */ public float getWebFillAlpha() { return webFillAlpha; } /** * Sets the alpha value for the fill of a plot polygon and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param alpha the new alpha value. If it is outside [0,1] it will be corrected to fit the range. * @see #getWebFillAlpha() */ public void setWebFillAlpha(float alpha) { this.webFillAlpha = alpha; if (webFillAlpha < 0f) { webFillAlpha = 0f; } else if (webFillAlpha > 1f) { webFillAlpha = 1f; } fireChangeEvent(); } /** * Returns the data extract order (by row or by column). * * @return The data extract order (never {@code null}). * * @see #setDataExtractOrder(TableOrder) */ public TableOrder getDataExtractOrder() { return this.dataExtractOrder; } /** * Sets the data extract order (by row or by column) and sends a * {@link PlotChangeEvent}to all registered listeners. * * @param order the order ({@code null} not permitted). * * @throws IllegalArgumentException if {@code order} is * {@code null}. * * @see #getDataExtractOrder() */ public void setDataExtractOrder(TableOrder order) { Args.nullNotPermitted(order, "order"); this.dataExtractOrder = order; fireChangeEvent(); } /** * Returns the head percent (the default value is 0.01). * * @return The head percent (always > 0). * * @see #setHeadPercent(double) */ public double getHeadPercent() { return this.headPercent; } /** * Sets the head percent and sends a {@link PlotChangeEvent} to all * registered listeners. Note that 0.10 is 10 percent. * * @param percent the percent (must be greater than zero). * * @see #getHeadPercent() */ public void setHeadPercent(double percent) { Args.requireNonNegative(percent, "percent"); this.headPercent = percent; fireChangeEvent(); } /** * Returns the start angle for the first radar axis. *
* This is measured in degrees starting from 3 o'clock (Java Arc2D default) * and measuring anti-clockwise. * * @return The start angle. * * @see #setStartAngle(double) */ public double getStartAngle() { return this.startAngle; } /** * Sets the starting angle and sends a {@link PlotChangeEvent} to all * registered listeners. *

* The initial default value is 90 degrees, which corresponds to 12 o'clock. * A value of zero corresponds to 3 o'clock... this is the encoding used by * Java's Arc2D class. * * @param angle the angle (in degrees). * * @see #getStartAngle() */ public void setStartAngle(double angle) { this.startAngle = angle; fireChangeEvent(); } /** * Returns the maximum value any category axis can take. * * @return The maximum value. * * @see #setMaxValue(double) */ public double getMaxValue() { return this.maxValue; } /** * Sets the maximum value any category axis can take and sends * a {@link PlotChangeEvent} to all registered listeners. * * @param value the maximum value. * * @see #getMaxValue() */ public void setMaxValue(double value) { this.maxValue = value; fireChangeEvent(); } /** * Returns the direction in which the radar axes are drawn * (clockwise or anti-clockwise). * * @return The direction (never {@code null}). * * @see #setDirection(Rotation) */ public Rotation getDirection() { return this.direction; } /** * Sets the direction in which the radar axes are drawn and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param direction the direction ({@code null} not permitted). * * @see #getDirection() */ public void setDirection(Rotation direction) { Args.nullNotPermitted(direction, "direction"); this.direction = direction; fireChangeEvent(); } /** * Returns the interior gap, measured as a percentage of the available * drawing space. * * @return The gap (as a percentage of the available drawing space). * * @see #setInteriorGap(double) */ public double getInteriorGap() { return this.interiorGap; } /** * Sets the interior gap and sends a {@link PlotChangeEvent} to all * registered listeners. This controls the space between the edges of the * plot and the plot area itself (the region where the axis labels appear). * * @param percent the gap (as a percentage of the available drawing space). * * @see #getInteriorGap() */ public void setInteriorGap(double percent) { if ((percent < 0.0) || (percent > MAX_INTERIOR_GAP)) { throw new IllegalArgumentException( "Percentage outside valid range."); } if (this.interiorGap != percent) { this.interiorGap = percent; fireChangeEvent(); } } /** * Returns the axis label gap. * * @return The axis label gap. * * @see #setAxisLabelGap(double) */ public double getAxisLabelGap() { return this.axisLabelGap; } /** * Sets the axis label gap and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param gap the gap. * * @see #getAxisLabelGap() */ public void setAxisLabelGap(double gap) { this.axisLabelGap = gap; fireChangeEvent(); } /** * Returns the paint used to draw the axis lines. * * @return The paint used to draw the axis lines (never {@code null}). * * @see #setAxisLinePaint(Paint) * @see #getAxisLineStroke() */ public Paint getAxisLinePaint() { return this.axisLinePaint; } /** * Sets the paint used to draw the axis lines and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getAxisLinePaint() */ public void setAxisLinePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.axisLinePaint = paint; fireChangeEvent(); } /** * Returns the stroke used to draw the axis lines. * * @return The stroke used to draw the axis lines (never {@code null}). * * @see #setAxisLineStroke(Stroke) * @see #getAxisLinePaint() */ public Stroke getAxisLineStroke() { return this.axisLineStroke; } /** * Sets the stroke used to draw the axis lines and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getAxisLineStroke() */ public void setAxisLineStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.axisLineStroke = stroke; fireChangeEvent(); } //// SERIES PAINT ///////////////////////// /** * Returns the paint for ALL series in the plot. * * @return The paint (possibly {@code null}). * * @see #setSeriesPaint(Paint) */ public Paint getSeriesPaint() { return this.seriesPaint; } /** * Sets the paint for ALL series in the plot. If this is set to * {@code null}, then a list of paints is used instead (to allow different * colors to be used for each series of the radar group). * * @param paint the paint ({@code null} permitted). * * @see #getSeriesPaint() */ public void setSeriesPaint(Paint paint) { this.seriesPaint = paint; fireChangeEvent(); } /** * Returns the paint for the specified series. * * @param series the series index (zero-based). * * @return The paint (never {@code null}). * * @see #setSeriesPaint(int, Paint) */ public Paint getSeriesPaint(int series) { // return the override, if there is one... if (this.seriesPaint != null) { return this.seriesPaint; } // otherwise look up the paint list Paint result = this.seriesPaintList.getPaint(series); if (result == null) { DrawingSupplier supplier = getDrawingSupplier(); if (supplier != null) { Paint p = supplier.getNextPaint(); this.seriesPaintList.setPaint(series, p); result = p; } else { result = this.baseSeriesPaint; } } return result; } /** * Sets the paint used to fill a series of the radar and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param paint the paint ({@code null} permitted). * * @see #getSeriesPaint(int) */ public void setSeriesPaint(int series, Paint paint) { this.seriesPaintList.setPaint(series, paint); fireChangeEvent(); } /** * Returns the base series paint. This is used when no other paint is * available. * * @return The paint (never {@code null}). * * @see #setBaseSeriesPaint(Paint) */ public Paint getBaseSeriesPaint() { return this.baseSeriesPaint; } /** * Sets the base series paint. * * @param paint the paint ({@code null} not permitted). * * @see #getBaseSeriesPaint() */ public void setBaseSeriesPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.baseSeriesPaint = paint; fireChangeEvent(); } //// SERIES OUTLINE PAINT //////////////////////////// /** * Returns the outline paint for ALL series in the plot. * * @return The paint (possibly {@code null}). */ public Paint getSeriesOutlinePaint() { return this.seriesOutlinePaint; } /** * Sets the outline paint for ALL series in the plot. If this is set to * {@code null}, then a list of paints is used instead (to allow * different colors to be used for each series). * * @param paint the paint ({@code null} permitted). */ public void setSeriesOutlinePaint(Paint paint) { this.seriesOutlinePaint = paint; fireChangeEvent(); } /** * Returns the paint for the specified series. * * @param series the series index (zero-based). * * @return The paint (never {@code null}). */ public Paint getSeriesOutlinePaint(int series) { // return the override, if there is one... if (this.seriesOutlinePaint != null) { return this.seriesOutlinePaint; } // otherwise look up the paint list Paint result = this.seriesOutlinePaintList.getPaint(series); if (result == null) { result = this.baseSeriesOutlinePaint; } return result; } /** * Sets the paint used to fill a series of the radar and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param paint the paint ({@code null} permitted). */ public void setSeriesOutlinePaint(int series, Paint paint) { this.seriesOutlinePaintList.setPaint(series, paint); fireChangeEvent(); } /** * Returns the base series paint. This is used when no other paint is * available. * * @return The paint (never {@code null}). */ public Paint getBaseSeriesOutlinePaint() { return this.baseSeriesOutlinePaint; } /** * Sets the base series paint. * * @param paint the paint ({@code null} not permitted). */ public void setBaseSeriesOutlinePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.baseSeriesOutlinePaint = paint; fireChangeEvent(); } //// SERIES OUTLINE STROKE ///////////////////// /** * Returns the outline stroke for ALL series in the plot. * * @return The stroke (possibly {@code null}). */ public Stroke getSeriesOutlineStroke() { return this.seriesOutlineStroke; } /** * Sets the outline stroke for ALL series in the plot. If this is set to * {@code null}, then a list of paints is used instead (to allow * different colors to be used for each series). * * @param stroke the stroke ({@code null} permitted). */ public void setSeriesOutlineStroke(Stroke stroke) { this.seriesOutlineStroke = stroke; fireChangeEvent(); } /** * Returns the stroke for the specified series. * * @param series the series index (zero-based). * * @return The stroke (never {@code null}). */ public Stroke getSeriesOutlineStroke(int series) { // return the override, if there is one... if (this.seriesOutlineStroke != null) { return this.seriesOutlineStroke; } // otherwise look up the paint list Stroke result = this.seriesOutlineStrokeList.getStroke(series); if (result == null) { result = this.baseSeriesOutlineStroke; } return result; } /** * Sets the stroke used to fill a series of the radar and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param stroke the stroke ({@code null} permitted). */ public void setSeriesOutlineStroke(int series, Stroke stroke) { this.seriesOutlineStrokeList.setStroke(series, stroke); fireChangeEvent(); } /** * Returns the base series stroke. This is used when no other stroke is * available. * * @return The stroke (never {@code null}). */ public Stroke getBaseSeriesOutlineStroke() { return this.baseSeriesOutlineStroke; } /** * Sets the base series stroke. * * @param stroke the stroke ({@code null} not permitted). */ public void setBaseSeriesOutlineStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.baseSeriesOutlineStroke = stroke; fireChangeEvent(); } /** * Returns the shape used for legend items. * * @return The shape (never {@code null}). * * @see #setLegendItemShape(Shape) */ public Shape getLegendItemShape() { return this.legendItemShape; } /** * Sets the shape used for legend items and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param shape the shape ({@code null} not permitted). * * @see #getLegendItemShape() */ public void setLegendItemShape(Shape shape) { Args.nullNotPermitted(shape, "shape"); this.legendItemShape = shape; fireChangeEvent(); } /** * Returns the series label font. * * @return The font (never {@code null}). * * @see #setLabelFont(Font) */ public Font getLabelFont() { return this.labelFont; } /** * Sets the series label font and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param font the font ({@code null} not permitted). * * @see #getLabelFont() */ public void setLabelFont(Font font) { Args.nullNotPermitted(font, "font"); this.labelFont = font; fireChangeEvent(); } /** * Returns the series label paint. * * @return The paint (never {@code null}). * * @see #setLabelPaint(Paint) */ public Paint getLabelPaint() { return this.labelPaint; } /** * Sets the series label paint and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getLabelPaint() */ public void setLabelPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.labelPaint = paint; fireChangeEvent(); } /** * Returns the label generator. * * @return The label generator (never {@code null}). * * @see #setLabelGenerator(CategoryItemLabelGenerator) */ public CategoryItemLabelGenerator getLabelGenerator() { return this.labelGenerator; } /** * Sets the label generator and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param generator the generator ({@code null} not permitted). * * @see #getLabelGenerator() */ public void setLabelGenerator(CategoryItemLabelGenerator generator) { Args.nullNotPermitted(generator, "generator"); this.labelGenerator = generator; } /** * Returns the tool tip generator for the plot. * * @return The tool tip generator (possibly {@code null}). * * @see #setToolTipGenerator(CategoryToolTipGenerator) */ public CategoryToolTipGenerator getToolTipGenerator() { return this.toolTipGenerator; } /** * Sets the tool tip generator for the plot and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param generator the generator ({@code null} permitted). * * @see #getToolTipGenerator() */ public void setToolTipGenerator(CategoryToolTipGenerator generator) { this.toolTipGenerator = generator; fireChangeEvent(); } /** * Returns the URL generator for the plot. * * @return The URL generator (possibly {@code null}). * * @see #setURLGenerator(CategoryURLGenerator) */ public CategoryURLGenerator getURLGenerator() { return this.urlGenerator; } /** * Sets the URL generator for the plot and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param generator the generator ({@code null} permitted). * * @see #getURLGenerator() */ public void setURLGenerator(CategoryURLGenerator generator) { this.urlGenerator = generator; fireChangeEvent(); } /** * Returns a collection of legend items for the spider web chart. * * @return The legend items (never {@code null}). */ @Override public LegendItemCollection getLegendItems() { LegendItemCollection result = new LegendItemCollection(); if (getDataset() == null) { return result; } List keys = null; if (this.dataExtractOrder == TableOrder.BY_ROW) { keys = this.dataset.getRowKeys(); } else if (this.dataExtractOrder == TableOrder.BY_COLUMN) { keys = this.dataset.getColumnKeys(); } if (keys == null) { return result; } int series = 0; Iterator iterator = keys.iterator(); Shape shape = getLegendItemShape(); while (iterator.hasNext()) { Comparable key = (Comparable) iterator.next(); String label = key.toString(); String description = label; Paint paint = getSeriesPaint(series); Paint outlinePaint = getSeriesOutlinePaint(series); Stroke stroke = getSeriesOutlineStroke(series); LegendItem item = new LegendItem(label, description, null, null, shape, paint, stroke, outlinePaint); item.setDataset(getDataset()); item.setSeriesKey(key); item.setSeriesIndex(series); result.add(item); series++; } return result; } /** * Returns a cartesian point from a polar angle, length and bounding box * * @param bounds the area inside which the point needs to be. * @param angle the polar angle, in degrees. * @param length the relative length. Given in percent of maximum extend. * * @return The cartesian point. */ protected Point2D getWebPoint(Rectangle2D bounds, double angle, double length) { double angrad = Math.toRadians(angle); double x = Math.cos(angrad) * length * bounds.getWidth() / 2; double y = -Math.sin(angrad) * length * bounds.getHeight() / 2; return new Point2D.Double(bounds.getX() + x + bounds.getWidth() / 2, bounds.getY() + y + bounds.getHeight() / 2); } /** * Draws the plot on a Java 2D graphics device (such as the screen or a * printer). * * @param g2 the graphics device. * @param area the area within which the plot should be drawn. * @param anchor the anchor point ({@code null} permitted). * @param parentState the state from the parent plot, if there is one. * @param info collects info about the drawing. */ @Override public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, PlotState parentState, PlotRenderingInfo info) { // adjust for insets... RectangleInsets insets = getInsets(); insets.trim(area); if (info != null) { info.setPlotArea(area); info.setDataArea(area); } drawBackground(g2, area); drawOutline(g2, area); Shape savedClip = g2.getClip(); g2.clip(area); Composite originalComposite = g2.getComposite(); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, getForegroundAlpha())); if (!DatasetUtils.isEmptyOrNull(this.dataset)) { int seriesCount, catCount; if (this.dataExtractOrder == TableOrder.BY_ROW) { seriesCount = this.dataset.getRowCount(); catCount = this.dataset.getColumnCount(); } else { seriesCount = this.dataset.getColumnCount(); catCount = this.dataset.getRowCount(); } // ensure we have a maximum value to use on the axes if (this.maxValue == DEFAULT_MAX_VALUE) { calculateMaxValue(seriesCount, catCount); } // Next, setup the plot area // adjust the plot area by the interior spacing value double gapHorizontal = area.getWidth() * getInteriorGap(); double gapVertical = area.getHeight() * getInteriorGap(); double X = area.getX() + gapHorizontal / 2; double Y = area.getY() + gapVertical / 2; double W = area.getWidth() - gapHorizontal; double H = area.getHeight() - gapVertical; double headW = area.getWidth() * this.headPercent; double headH = area.getHeight() * this.headPercent; // make the chart area a square double min = Math.min(W, H) / 2; X = (X + X + W) / 2 - min; Y = (Y + Y + H) / 2 - min; W = 2 * min; H = 2 * min; Point2D centre = new Point2D.Double(X + W / 2, Y + H / 2); Rectangle2D radarArea = new Rectangle2D.Double(X, Y, W, H); // draw the axis and category label for (int cat = 0; cat < catCount; cat++) { double angle = getStartAngle() + (getDirection().getFactor() * cat * 360 / catCount); Point2D endPoint = getWebPoint(radarArea, angle, 1); // 1 = end of axis Line2D line = new Line2D.Double(centre, endPoint); g2.setPaint(this.axisLinePaint); g2.setStroke(this.axisLineStroke); g2.draw(line); drawLabel(g2, radarArea, 0.0, cat, angle, 360.0 / catCount); } // Now actually plot each of the series polygons.. for (int series = 0; series < seriesCount; series++) { drawRadarPoly(g2, radarArea, centre, info, series, catCount, headH, headW); } } else { drawNoDataMessage(g2, area); } g2.setClip(savedClip); g2.setComposite(originalComposite); drawOutline(g2, area); } /** * loop through each of the series to get the maximum value * on each category axis * * @param seriesCount the number of series * @param catCount the number of categories */ private void calculateMaxValue(int seriesCount, int catCount) { double v; Number nV; for (int seriesIndex = 0; seriesIndex < seriesCount; seriesIndex++) { for (int catIndex = 0; catIndex < catCount; catIndex++) { nV = getPlotValue(seriesIndex, catIndex); if (nV != null) { v = nV.doubleValue(); if (v > this.maxValue) { this.maxValue = v; } } } } } /** * Draws a radar plot polygon. * * @param g2 the graphics device. * @param plotArea the area we are plotting in (already adjusted). * @param centre the centre point of the radar axes * @param info chart rendering info. * @param series the series within the dataset we are plotting * @param catCount the number of categories per radar plot * @param headH the data point height * @param headW the data point width */ protected void drawRadarPoly(Graphics2D g2, Rectangle2D plotArea, Point2D centre, PlotRenderingInfo info, int series, int catCount, double headH, double headW) { Polygon polygon = new Polygon(); EntityCollection entities = null; if (info != null) { entities = info.getOwner().getEntityCollection(); } // plot the data... for (int cat = 0; cat < catCount; cat++) { Number dataValue = getPlotValue(series, cat); if (dataValue != null) { double value = dataValue.doubleValue(); if (value >= 0) { // draw the polygon series... // Finds our starting angle from the centre for this axis double angle = getStartAngle() + (getDirection().getFactor() * cat * 360 / catCount); // The following angle calc will ensure there isn't a top // vertical axis - this may be useful if you don't want any // given criteria to 'appear' move important than the // others.. // + (getDirection().getFactor() // * (cat + 0.5) * 360 / catCount); // find the point at the appropriate distance end point // along the axis/angle identified above and add it to the // polygon Point2D point = getWebPoint(plotArea, angle, value / this.maxValue); polygon.addPoint((int) point.getX(), (int) point.getY()); // put an elipse at the point being plotted.. Paint paint = getSeriesPaint(series); Paint outlinePaint = getSeriesOutlinePaint(series); Stroke outlineStroke = getSeriesOutlineStroke(series); Ellipse2D head = new Ellipse2D.Double(point.getX() - headW / 2, point.getY() - headH / 2, headW, headH); g2.setPaint(paint); g2.fill(head); g2.setStroke(outlineStroke); g2.setPaint(outlinePaint); g2.draw(head); if (entities != null) { int row, col; if (this.dataExtractOrder == TableOrder.BY_ROW) { row = series; col = cat; } else { row = cat; col = series; } String tip = null; if (this.toolTipGenerator != null) { tip = this.toolTipGenerator.generateToolTip( this.dataset, row, col); } String url = null; if (this.urlGenerator != null) { url = this.urlGenerator.generateURL(this.dataset, row, col); } Shape area = new Rectangle( (int) (point.getX() - headW), (int) (point.getY() - headH), (int) (headW * 2), (int) (headH * 2)); CategoryItemEntity entity = new CategoryItemEntity( area, tip, url, this.dataset, this.dataset.getRowKey(row), this.dataset.getColumnKey(col)); entities.add(entity); } } } } // Plot the polygon Paint paint = getSeriesPaint(series); g2.setPaint(paint); g2.setStroke(getSeriesOutlineStroke(series)); g2.draw(polygon); // Lastly, fill the web polygon if this is required if (this.webFilled) { g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, webFillAlpha)); g2.fill(polygon); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, getForegroundAlpha())); } } /** * Returns the value to be plotted at the intersection of the * series and the category. This allows us to plot * {@code BY_ROW} or {@code BY_COLUMN} which basically is just * reversing the definition of the categories and data series being * plotted. * * @param series the series to be plotted. * @param cat the category within the series to be plotted. * * @return The value to be plotted (possibly {@code null}). * * @see #getDataExtractOrder() */ protected Number getPlotValue(int series, int cat) { Number value = null; if (this.dataExtractOrder == TableOrder.BY_ROW) { value = this.dataset.getValue(series, cat); } else if (this.dataExtractOrder == TableOrder.BY_COLUMN) { value = this.dataset.getValue(cat, series); } return value; } /** * Draws the label for one axis. * * @param g2 the graphics device. * @param plotArea the plot area * @param value the value of the label (ignored). * @param cat the category (zero-based index). * @param startAngle the starting angle. * @param extent the extent of the arc. */ protected void drawLabel(Graphics2D g2, Rectangle2D plotArea, double value, int cat, double startAngle, double extent) { FontRenderContext frc = g2.getFontRenderContext(); String label; if (this.dataExtractOrder == TableOrder.BY_ROW) { // if series are in rows, then the categories are the column keys label = this.labelGenerator.generateColumnLabel(this.dataset, cat); } else { // if series are in columns, then the categories are the row keys label = this.labelGenerator.generateRowLabel(this.dataset, cat); } Rectangle2D labelBounds = getLabelFont().getStringBounds(label, frc); LineMetrics lm = getLabelFont().getLineMetrics(label, frc); double ascent = lm.getAscent(); Point2D labelLocation = calculateLabelLocation(labelBounds, ascent, plotArea, startAngle); Composite saveComposite = g2.getComposite(); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f)); g2.setPaint(getLabelPaint()); g2.setFont(getLabelFont()); g2.drawString(label, (float) labelLocation.getX(), (float) labelLocation.getY()); g2.setComposite(saveComposite); } /** * Returns the location for a label * * @param labelBounds the label bounds. * @param ascent the ascent (height of font). * @param plotArea the plot area * @param startAngle the start angle for the pie series. * * @return The location for a label. */ protected Point2D calculateLabelLocation(Rectangle2D labelBounds, double ascent, Rectangle2D plotArea, double startAngle) { Arc2D arc1 = new Arc2D.Double(plotArea, startAngle, 0, Arc2D.OPEN); Point2D point1 = arc1.getEndPoint(); double deltaX = -(point1.getX() - plotArea.getCenterX()) * this.axisLabelGap; double deltaY = -(point1.getY() - plotArea.getCenterY()) * this.axisLabelGap; double labelX = point1.getX() - deltaX; double labelY = point1.getY() - deltaY; if (labelX < plotArea.getCenterX()) { labelX -= labelBounds.getWidth(); } if (labelX == plotArea.getCenterX()) { labelX -= labelBounds.getWidth() / 2; } if (labelY > plotArea.getCenterY()) { labelY += ascent; } return new Point2D.Double(labelX, labelY); } /** * Tests this plot for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof SpiderWebPlot)) { return false; } if (!super.equals(obj)) { return false; } SpiderWebPlot that = (SpiderWebPlot) obj; if (!this.dataExtractOrder.equals(that.dataExtractOrder)) { return false; } if (this.headPercent != that.headPercent) { return false; } if (this.interiorGap != that.interiorGap) { return false; } if (this.startAngle != that.startAngle) { return false; } if (!this.direction.equals(that.direction)) { return false; } if (this.maxValue != that.maxValue) { return false; } if (this.webFilled != that.webFilled) { return false; } if (this.webFillAlpha != that.webFillAlpha) { return false; } if (this.axisLabelGap != that.axisLabelGap) { return false; } if (!PaintUtils.equal(this.axisLinePaint, that.axisLinePaint)) { return false; } if (!this.axisLineStroke.equals(that.axisLineStroke)) { return false; } if (!ShapeUtils.equal(this.legendItemShape, that.legendItemShape)) { return false; } if (!PaintUtils.equal(this.seriesPaint, that.seriesPaint)) { return false; } if (!this.seriesPaintList.equals(that.seriesPaintList)) { return false; } if (!PaintUtils.equal(this.baseSeriesPaint, that.baseSeriesPaint)) { return false; } if (!PaintUtils.equal(this.seriesOutlinePaint, that.seriesOutlinePaint)) { return false; } if (!this.seriesOutlinePaintList.equals(that.seriesOutlinePaintList)) { return false; } if (!PaintUtils.equal(this.baseSeriesOutlinePaint, that.baseSeriesOutlinePaint)) { return false; } if (!Objects.equals(this.seriesOutlineStroke, that.seriesOutlineStroke)) { return false; } if (!this.seriesOutlineStrokeList.equals( that.seriesOutlineStrokeList)) { return false; } if (!this.baseSeriesOutlineStroke.equals( that.baseSeriesOutlineStroke)) { return false; } if (!this.labelFont.equals(that.labelFont)) { return false; } if (!PaintUtils.equal(this.labelPaint, that.labelPaint)) { return false; } if (!this.labelGenerator.equals(that.labelGenerator)) { return false; } if (!Objects.equals(this.toolTipGenerator, that.toolTipGenerator)) { return false; } if (!Objects.equals(this.urlGenerator, that.urlGenerator)) { return false; } return true; } /** * Returns a clone of this plot. * * @return A clone of this plot. * * @throws CloneNotSupportedException if the plot cannot be cloned for * any reason. */ @Override public Object clone() throws CloneNotSupportedException { SpiderWebPlot clone = (SpiderWebPlot) super.clone(); clone.legendItemShape = ShapeUtils.clone(this.legendItemShape); clone.seriesPaintList = (PaintList) this.seriesPaintList.clone(); clone.seriesOutlinePaintList = (PaintList) this.seriesOutlinePaintList.clone(); clone.seriesOutlineStrokeList = (StrokeList) this.seriesOutlineStrokeList.clone(); return clone; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeShape(this.legendItemShape, stream); SerialUtils.writePaint(this.seriesPaint, stream); SerialUtils.writePaint(this.baseSeriesPaint, stream); SerialUtils.writePaint(this.seriesOutlinePaint, stream); SerialUtils.writePaint(this.baseSeriesOutlinePaint, stream); SerialUtils.writeStroke(this.seriesOutlineStroke, stream); SerialUtils.writeStroke(this.baseSeriesOutlineStroke, stream); SerialUtils.writePaint(this.labelPaint, stream); SerialUtils.writePaint(this.axisLinePaint, stream); SerialUtils.writeStroke(this.axisLineStroke, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.legendItemShape = SerialUtils.readShape(stream); this.seriesPaint = SerialUtils.readPaint(stream); this.baseSeriesPaint = SerialUtils.readPaint(stream); this.seriesOutlinePaint = SerialUtils.readPaint(stream); this.baseSeriesOutlinePaint = SerialUtils.readPaint(stream); this.seriesOutlineStroke = SerialUtils.readStroke(stream); this.baseSeriesOutlineStroke = SerialUtils.readStroke(stream); this.labelPaint = SerialUtils.readPaint(stream); this.axisLinePaint = SerialUtils.readPaint(stream); this.axisLineStroke = SerialUtils.readStroke(stream); if (this.dataset != null) { this.dataset.addChangeListener(this); } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/ThermometerPlot.java000066400000000000000000001442171463604235500300420ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * ThermometerPlot.java * -------------------- * * (C) Copyright 2000-present, by Bryan Scott and Contributors. * * Original Author: Bryan Scott (based on MeterPlot by Hari). * Contributor(s): David Gilbert. * Arnaud Lelievre; * Julien Henry (see patch 1769088) (DG); */ package org.jfree.chart.plot; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Stroke; import java.awt.geom.Area; import java.awt.geom.Ellipse2D; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.geom.RoundRectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.Arrays; import java.util.Objects; import java.util.ResourceBundle; import org.jfree.chart.LegendItemCollection; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.PlotChangeEvent; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.ResourceBundleWrapper; import org.jfree.chart.util.SerialUtils; import org.jfree.chart.util.UnitType; import org.jfree.data.Range; import org.jfree.data.general.DatasetChangeEvent; import org.jfree.data.general.DefaultValueDataset; import org.jfree.data.general.ValueDataset; /** * A plot that displays a single value (from a {@link ValueDataset}) in a * thermometer type display. *

* This plot supports a number of options: *

    *
  1. three sub-ranges which could be viewed as 'Normal', 'Warning' * and 'Critical' ranges.
  2. *
  3. the thermometer can be run in two modes: *
      *
    • fixed range, or
    • *
    • range adjusts to current sub-range.
    • *
    *
  4. *
  5. settable units to be displayed.
  6. *
  7. settable display location for the value text.
  8. *
*/ public class ThermometerPlot extends Plot implements ValueAxisPlot, Zoomable, Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 4087093313147984390L; /** A constant for unit type 'None'. */ public static final int UNITS_NONE = 0; /** A constant for unit type 'Fahrenheit'. */ public static final int UNITS_FAHRENHEIT = 1; /** A constant for unit type 'Celcius'. */ public static final int UNITS_CELCIUS = 2; /** A constant for unit type 'Kelvin'. */ public static final int UNITS_KELVIN = 3; /** A constant for the value label position (no label). */ public static final int NONE = 0; /** A constant for the value label position (right of the thermometer). */ public static final int RIGHT = 1; /** A constant for the value label position (left of the thermometer). */ public static final int LEFT = 2; /** A constant for the value label position (in the thermometer bulb). */ public static final int BULB = 3; /** A constant for the 'normal' range. */ public static final int NORMAL = 0; /** A constant for the 'warning' range. */ public static final int WARNING = 1; /** A constant for the 'critical' range. */ public static final int CRITICAL = 2; /** The axis gap. */ protected static final int AXIS_GAP = 10; /** The unit strings. */ protected static final String[] UNITS = {"", "\u00B0F", "\u00B0C", "\u00B0K"}; /** Index for low value in subrangeInfo matrix. */ protected static final int RANGE_LOW = 0; /** Index for high value in subrangeInfo matrix. */ protected static final int RANGE_HIGH = 1; /** Index for display low value in subrangeInfo matrix. */ protected static final int DISPLAY_LOW = 2; /** Index for display high value in subrangeInfo matrix. */ protected static final int DISPLAY_HIGH = 3; /** The default lower bound. */ protected static final double DEFAULT_LOWER_BOUND = 0.0; /** The default upper bound. */ protected static final double DEFAULT_UPPER_BOUND = 100.0; /** * The default bulb radius. */ protected static final int DEFAULT_BULB_RADIUS = 40; /** * The default column radius. */ protected static final int DEFAULT_COLUMN_RADIUS = 20; /** * The default gap between the outlines representing the thermometer. */ protected static final int DEFAULT_GAP = 5; /** The dataset for the plot. */ private ValueDataset dataset; /** The range axis. */ private ValueAxis rangeAxis; /** The lower bound for the thermometer. */ private double lowerBound = DEFAULT_LOWER_BOUND; /** The upper bound for the thermometer. */ private double upperBound = DEFAULT_UPPER_BOUND; /** * The value label position. */ private int bulbRadius = DEFAULT_BULB_RADIUS; /** * The column radius. */ private int columnRadius = DEFAULT_COLUMN_RADIUS; /** * The gap between the two outlines the represent the thermometer. */ private int gap = DEFAULT_GAP; /** * Blank space inside the plot area around the outside of the thermometer. */ private RectangleInsets padding; /** Stroke for drawing the thermometer */ private transient Stroke thermometerStroke = new BasicStroke(1.0f); /** Paint for drawing the thermometer */ private transient Paint thermometerPaint = Color.BLACK; /** The display units */ private int units = UNITS_CELCIUS; /** The value label position. */ private int valueLocation = BULB; /** The position of the axis **/ private int axisLocation = LEFT; /** The font to write the value in */ private Font valueFont = new Font("SansSerif", Font.BOLD, 16); /** Colour that the value is written in */ private transient Paint valuePaint = Color.WHITE; /** Number format for the value */ private NumberFormat valueFormat = new DecimalFormat(); /** The default paint for the mercury in the thermometer. */ private transient Paint mercuryPaint = Color.LIGHT_GRAY; /** A flag that controls whether value lines are drawn. */ private boolean showValueLines = false; /** The display sub-range. */ private int subrange = -1; /** The start and end values for the subranges. */ private double[][] subrangeInfo = { {0.0, 50.0, 0.0, 50.0}, {50.0, 75.0, 50.0, 75.0}, {75.0, 100.0, 75.0, 100.0} }; /** * A flag that controls whether or not the axis range adjusts to the * sub-ranges. */ private boolean followDataInSubranges = false; /** * A flag that controls whether or not the mercury paint changes with * the subranges. */ private boolean useSubrangePaint = true; /** Paint for each range */ private transient Paint[] subrangePaint = {Color.GREEN, Color.ORANGE, Color.RED}; /** A flag that controls whether the sub-range indicators are visible. */ private boolean subrangeIndicatorsVisible = true; /** The stroke for the sub-range indicators. */ private transient Stroke subrangeIndicatorStroke = new BasicStroke(2.0f); /** The range indicator stroke. */ private transient Stroke rangeIndicatorStroke = new BasicStroke(3.0f); /** The resourceBundle for the localization. */ protected static ResourceBundle localizationResources = ResourceBundleWrapper.getBundle( "org.jfree.chart.plot.LocalizationBundle"); /** * Creates a new thermometer plot. */ public ThermometerPlot() { this(new DefaultValueDataset()); } /** * Creates a new thermometer plot, using default attributes where necessary. * * @param dataset the data set. */ public ThermometerPlot(ValueDataset dataset) { super(); this.padding = new RectangleInsets(UnitType.RELATIVE, 0.05, 0.05, 0.05, 0.05); this.dataset = dataset; if (dataset != null) { dataset.addChangeListener(this); } NumberAxis axis = new NumberAxis(null); axis.setStandardTickUnits(NumberAxis.createIntegerTickUnits()); axis.setAxisLineVisible(false); axis.setPlot(this); axis.addChangeListener(this); this.rangeAxis = axis; setAxisRange(); } /** * Returns the dataset for the plot. * * @return The dataset (possibly {@code null}). * * @see #setDataset(ValueDataset) */ public ValueDataset getDataset() { return this.dataset; } /** * Sets the dataset for the plot, replacing the existing dataset if there * is one, and sends a {@link PlotChangeEvent} to all registered listeners. * * @param dataset the dataset ({@code null} permitted). * * @see #getDataset() */ public void setDataset(ValueDataset dataset) { // if there is an existing dataset, remove the plot from the list // of change listeners... ValueDataset existing = this.dataset; if (existing != null) { existing.removeChangeListener(this); } // set the new dataset, and register the chart as a change listener... this.dataset = dataset; if (dataset != null) { setDatasetGroup(dataset.getGroup()); dataset.addChangeListener(this); } // send a dataset change event to self... DatasetChangeEvent event = new DatasetChangeEvent(this, dataset); datasetChanged(event); } /** * Returns the range axis. * * @return The range axis (never {@code null}). * * @see #setRangeAxis(ValueAxis) */ public ValueAxis getRangeAxis() { return this.rangeAxis; } /** * Sets the range axis for the plot and sends a {@link PlotChangeEvent} to * all registered listeners. * * @param axis the new axis ({@code null} not permitted). * * @see #getRangeAxis() */ public void setRangeAxis(ValueAxis axis) { Args.nullNotPermitted(axis, "axis"); // plot is registered as a listener with the existing axis... this.rangeAxis.removeChangeListener(this); axis.setPlot(this); axis.addChangeListener(this); this.rangeAxis = axis; fireChangeEvent(); } /** * Returns the lower bound for the thermometer. The data value can be set * lower than this, but it will not be shown in the thermometer. * * @return The lower bound. * * @see #setLowerBound(double) */ public double getLowerBound() { return this.lowerBound; } /** * Sets the lower bound for the thermometer. * * @param lower the lower bound. * * @see #getLowerBound() */ public void setLowerBound(double lower) { this.lowerBound = lower; setAxisRange(); } /** * Returns the upper bound for the thermometer. The data value can be set * higher than this, but it will not be shown in the thermometer. * * @return The upper bound. * * @see #setUpperBound(double) */ public double getUpperBound() { return this.upperBound; } /** * Sets the upper bound for the thermometer. * * @param upper the upper bound. * * @see #getUpperBound() */ public void setUpperBound(double upper) { this.upperBound = upper; setAxisRange(); } /** * Sets the lower and upper bounds for the thermometer. * * @param lower the lower bound. * @param upper the upper bound. */ public void setRange(double lower, double upper) { this.lowerBound = lower; this.upperBound = upper; setAxisRange(); } /** * Returns the padding for the thermometer. This is the space inside the * plot area. * * @return The padding (never {@code null}). * * @see #setPadding(RectangleInsets) */ public RectangleInsets getPadding() { return this.padding; } /** * Sets the padding for the thermometer and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param padding the padding ({@code null} not permitted). * * @see #getPadding() */ public void setPadding(RectangleInsets padding) { Args.nullNotPermitted(padding, "padding"); this.padding = padding; fireChangeEvent(); } /** * Returns the stroke used to draw the thermometer outline. * * @return The stroke (never {@code null}). * * @see #setThermometerStroke(Stroke) * @see #getThermometerPaint() */ public Stroke getThermometerStroke() { return this.thermometerStroke; } /** * Sets the stroke used to draw the thermometer outline and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param s the new stroke ({@code null} ignored). * * @see #getThermometerStroke() */ public void setThermometerStroke(Stroke s) { if (s != null) { this.thermometerStroke = s; fireChangeEvent(); } } /** * Returns the paint used to draw the thermometer outline. * * @return The paint (never {@code null}). * * @see #setThermometerPaint(Paint) * @see #getThermometerStroke() */ public Paint getThermometerPaint() { return this.thermometerPaint; } /** * Sets the paint used to draw the thermometer outline and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param paint the new paint ({@code null} ignored). * * @see #getThermometerPaint() */ public void setThermometerPaint(Paint paint) { if (paint != null) { this.thermometerPaint = paint; fireChangeEvent(); } } /** * Returns a code indicating the unit display type. This is one of * {@link #UNITS_NONE}, {@link #UNITS_FAHRENHEIT}, {@link #UNITS_CELCIUS} * and {@link #UNITS_KELVIN}. * * @return The units type. * * @see #setUnits(int) */ public int getUnits() { return this.units; } /** * Sets the units to be displayed in the thermometer. Use one of the * following constants: * *
    *
  • UNITS_NONE : no units displayed.
  • *
  • UNITS_FAHRENHEIT : units displayed in Fahrenheit.
  • *
  • UNITS_CELCIUS : units displayed in Celcius.
  • *
  • UNITS_KELVIN : units displayed in Kelvin.
  • *
* * @param u the new unit type. * * @see #getUnits() */ public void setUnits(int u) { if ((u >= 0) && (u < UNITS.length)) { if (this.units != u) { this.units = u; fireChangeEvent(); } } } /** * Returns a code indicating the location at which the value label is * displayed. * * @return The location (one of {@link #NONE}, {@link #RIGHT}, * {@link #LEFT} and {@link #BULB}.). */ public int getValueLocation() { return this.valueLocation; } /** * Sets the location at which the current value is displayed and sends a * {@link PlotChangeEvent} to all registered listeners. *

* The location can be one of the constants: {@code NONE}, {@code RIGHT}, * {@code LEFT} and {@code BULB}. * * @param location the location. */ public void setValueLocation(int location) { if ((location >= 0) && (location < 4)) { this.valueLocation = location; fireChangeEvent(); } else { throw new IllegalArgumentException("Location not recognised."); } } /** * Returns the axis location. * * @return The location (one of {@link #NONE}, {@link #LEFT} and * {@link #RIGHT}). * * @see #setAxisLocation(int) */ public int getAxisLocation() { return this.axisLocation; } /** * Sets the location at which the axis is displayed relative to the * thermometer, and sends a {@link PlotChangeEvent} to all registered * listeners. * * @param location the location (one of {@link #NONE}, {@link #LEFT} and * {@link #RIGHT}). * * @see #getAxisLocation() */ public void setAxisLocation(int location) { if ((location >= 0) && (location < 3)) { this.axisLocation = location; fireChangeEvent(); } else { throw new IllegalArgumentException("Location not recognised."); } } /** * Gets the font used to display the current value. * * @return The font. * * @see #setValueFont(Font) */ public Font getValueFont() { return this.valueFont; } /** * Sets the font used to display the current value. * * @param f the new font ({@code null} not permitted). * * @see #getValueFont() */ public void setValueFont(Font f) { Args.nullNotPermitted(f, "f"); if (!this.valueFont.equals(f)) { this.valueFont = f; fireChangeEvent(); } } /** * Gets the paint used to display the current value. * * @return The paint. * * @see #setValuePaint(Paint) */ public Paint getValuePaint() { return this.valuePaint; } /** * Sets the paint used to display the current value and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param paint the new paint ({@code null} not permitted). * * @see #getValuePaint() */ public void setValuePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); if (!this.valuePaint.equals(paint)) { this.valuePaint = paint; fireChangeEvent(); } } // FIXME: No getValueFormat() method? /** * Sets the formatter for the value label and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param formatter the new formatter ({@code null} not permitted). */ public void setValueFormat(NumberFormat formatter) { Args.nullNotPermitted(formatter, "formatter"); this.valueFormat = formatter; fireChangeEvent(); } /** * Returns the default mercury paint. * * @return The paint (never {@code null}). * * @see #setMercuryPaint(Paint) */ public Paint getMercuryPaint() { return this.mercuryPaint; } /** * Sets the default mercury paint and sends a {@link PlotChangeEvent} to * all registered listeners. * * @param paint the new paint ({@code null} not permitted). * * @see #getMercuryPaint() */ public void setMercuryPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.mercuryPaint = paint; fireChangeEvent(); } /** * Sets information for a particular range. * * @param range the range to specify information about. * @param low the low value for the range * @param hi the high value for the range */ public void setSubrangeInfo(int range, double low, double hi) { setSubrangeInfo(range, low, hi, low, hi); } /** * Sets the subrangeInfo attribute of the ThermometerPlot object * * @param range the new rangeInfo value. * @param rangeLow the new rangeInfo value * @param rangeHigh the new rangeInfo value * @param displayLow the new rangeInfo value * @param displayHigh the new rangeInfo value */ public void setSubrangeInfo(int range, double rangeLow, double rangeHigh, double displayLow, double displayHigh) { if ((range >= 0) && (range < 3)) { setSubrange(range, rangeLow, rangeHigh); setDisplayRange(range, displayLow, displayHigh); setAxisRange(); fireChangeEvent(); } } /** * Sets the bounds for a subrange. * * @param range the range type. * @param low the low value. * @param high the high value. */ public void setSubrange(int range, double low, double high) { if ((range >= 0) && (range < 3)) { this.subrangeInfo[range][RANGE_HIGH] = high; this.subrangeInfo[range][RANGE_LOW] = low; } } /** * Sets the displayed bounds for a sub range. * * @param range the range type. * @param low the low value. * @param high the high value. */ public void setDisplayRange(int range, double low, double high) { if ((range >= 0) && (range < this.subrangeInfo.length) && isValidNumber(high) && isValidNumber(low)) { if (high > low) { this.subrangeInfo[range][DISPLAY_HIGH] = high; this.subrangeInfo[range][DISPLAY_LOW] = low; } else { this.subrangeInfo[range][DISPLAY_HIGH] = low; this.subrangeInfo[range][DISPLAY_LOW] = high; } } } /** * Gets the paint used for a particular subrange. * * @param range the range (. * * @return The paint. * * @see #setSubrangePaint(int, Paint) */ public Paint getSubrangePaint(int range) { if ((range >= 0) && (range < this.subrangePaint.length)) { return this.subrangePaint[range]; } else { return this.mercuryPaint; } } /** * Sets the paint to be used for a subrange and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param range the range (0, 1 or 2). * @param paint the paint to be applied ({@code null} not permitted). * * @see #getSubrangePaint(int) */ public void setSubrangePaint(int range, Paint paint) { if ((range >= 0) && (range < this.subrangePaint.length) && (paint != null)) { this.subrangePaint[range] = paint; fireChangeEvent(); } } /** * Returns a flag that controls whether or not the thermometer axis zooms * to display the subrange within which the data value falls. * * @return The flag. */ public boolean getFollowDataInSubranges() { return this.followDataInSubranges; } /** * Sets the flag that controls whether or not the thermometer axis zooms * to display the subrange within which the data value falls. * * @param flag the flag. */ public void setFollowDataInSubranges(boolean flag) { this.followDataInSubranges = flag; fireChangeEvent(); } /** * Returns a flag that controls whether or not the mercury color changes * for each subrange. * * @return The flag. * * @see #setUseSubrangePaint(boolean) */ public boolean getUseSubrangePaint() { return this.useSubrangePaint; } /** * Sets the range colour change option. * * @param flag the new range colour change option * * @see #getUseSubrangePaint() */ public void setUseSubrangePaint(boolean flag) { this.useSubrangePaint = flag; fireChangeEvent(); } /** * Returns the bulb radius, in Java2D units. * @return The bulb radius. */ public int getBulbRadius() { return this.bulbRadius; } /** * Sets the bulb radius (in Java2D units) and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param r the new radius (in Java2D units). * * @see #getBulbRadius() */ public void setBulbRadius(int r) { this.bulbRadius = r; fireChangeEvent(); } /** * Returns the bulb diameter, which is always twice the value returned * by {@link #getBulbRadius()}. * * @return The bulb diameter. */ public int getBulbDiameter() { return getBulbRadius() * 2; } /** * Returns the column radius, in Java2D units. * * @return The column radius. * * @see #setColumnRadius(int) */ public int getColumnRadius() { return this.columnRadius; } /** * Sets the column radius (in Java2D units) and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param r the new radius. * * @see #getColumnRadius() */ public void setColumnRadius(int r) { this.columnRadius = r; fireChangeEvent(); } /** * Returns the column diameter, which is always twice the value returned * by {@link #getColumnRadius()}. * * @return The column diameter. */ public int getColumnDiameter() { return getColumnRadius() * 2; } /** * Returns the gap, in Java2D units, between the two outlines that * represent the thermometer. * * @return The gap. * * @see #setGap(int) */ public int getGap() { return this.gap; } /** * Sets the gap (in Java2D units) between the two outlines that represent * the thermometer, and sends a {@link PlotChangeEvent} to all registered * listeners. * * @param gap the new gap. * * @see #getGap() */ public void setGap(int gap) { this.gap = gap; fireChangeEvent(); } /** * Draws the plot on a Java 2D graphics device (such as the screen or a * printer). * * @param g2 the graphics device. * @param area the area within which the plot should be drawn. * @param anchor the anchor point ({@code null} permitted). * @param parentState the state from the parent plot, if there is one. * @param info collects info about the drawing. */ @Override public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, PlotState parentState, PlotRenderingInfo info) { RoundRectangle2D outerStem = new RoundRectangle2D.Double(); RoundRectangle2D innerStem = new RoundRectangle2D.Double(); RoundRectangle2D mercuryStem = new RoundRectangle2D.Double(); Ellipse2D outerBulb = new Ellipse2D.Double(); Ellipse2D innerBulb = new Ellipse2D.Double(); String temp; FontMetrics metrics; if (info != null) { info.setPlotArea(area); } // adjust for insets... RectangleInsets insets = getInsets(); insets.trim(area); drawBackground(g2, area); // adjust for padding... Rectangle2D interior = (Rectangle2D) area.clone(); this.padding.trim(interior); int midX = (int) (interior.getX() + (interior.getWidth() / 2)); int midY = (int) (interior.getY() + (interior.getHeight() / 2)); int stemTop = (int) (interior.getMinY() + getBulbRadius()); int stemBottom = (int) (interior.getMaxY() - getBulbDiameter()); Rectangle2D dataArea = new Rectangle2D.Double(midX - getColumnRadius(), stemTop, getColumnRadius(), stemBottom - stemTop); outerBulb.setFrame(midX - getBulbRadius(), stemBottom, getBulbDiameter(), getBulbDiameter()); outerStem.setRoundRect(midX - getColumnRadius(), interior.getMinY(), getColumnDiameter(), stemBottom + getBulbDiameter() - stemTop, getColumnDiameter(), getColumnDiameter()); Area outerThermometer = new Area(outerBulb); Area tempArea = new Area(outerStem); outerThermometer.add(tempArea); innerBulb.setFrame(midX - getBulbRadius() + getGap(), stemBottom + getGap(), getBulbDiameter() - getGap() * 2, getBulbDiameter() - getGap() * 2); innerStem.setRoundRect(midX - getColumnRadius() + getGap(), interior.getMinY() + getGap(), getColumnDiameter() - getGap() * 2, stemBottom + getBulbDiameter() - getGap() * 2 - stemTop, getColumnDiameter() - getGap() * 2, getColumnDiameter() - getGap() * 2); Area innerThermometer = new Area(innerBulb); tempArea = new Area(innerStem); innerThermometer.add(tempArea); if ((this.dataset != null) && (this.dataset.getValue() != null)) { double current = this.dataset.getValue().doubleValue(); double ds = this.rangeAxis.valueToJava2D(current, dataArea, RectangleEdge.LEFT); int i = getColumnDiameter() - getGap() * 2; // already calculated int j = getColumnRadius() - getGap(); // already calculated int l = (i / 2); int k = (int) Math.round(ds); if (k < (getGap() + interior.getMinY())) { k = (int) (getGap() + interior.getMinY()); l = getBulbRadius(); } Area mercury = new Area(innerBulb); if (k < (stemBottom + getBulbRadius())) { mercuryStem.setRoundRect(midX - j, k, i, (stemBottom + getBulbRadius()) - k, l, l); tempArea = new Area(mercuryStem); mercury.add(tempArea); } g2.setPaint(getCurrentPaint()); g2.fill(mercury); // draw range indicators... if (this.subrangeIndicatorsVisible) { g2.setStroke(this.subrangeIndicatorStroke); Range range = this.rangeAxis.getRange(); // draw start of normal range double value = this.subrangeInfo[NORMAL][RANGE_LOW]; if (range.contains(value)) { double x = midX + getColumnRadius() + 2; double y = this.rangeAxis.valueToJava2D(value, dataArea, RectangleEdge.LEFT); Line2D line = new Line2D.Double(x, y, x + 10, y); g2.setPaint(this.subrangePaint[NORMAL]); g2.draw(line); } // draw start of warning range value = this.subrangeInfo[WARNING][RANGE_LOW]; if (range.contains(value)) { double x = midX + getColumnRadius() + 2; double y = this.rangeAxis.valueToJava2D(value, dataArea, RectangleEdge.LEFT); Line2D line = new Line2D.Double(x, y, x + 10, y); g2.setPaint(this.subrangePaint[WARNING]); g2.draw(line); } // draw start of critical range value = this.subrangeInfo[CRITICAL][RANGE_LOW]; if (range.contains(value)) { double x = midX + getColumnRadius() + 2; double y = this.rangeAxis.valueToJava2D(value, dataArea, RectangleEdge.LEFT); Line2D line = new Line2D.Double(x, y, x + 10, y); g2.setPaint(this.subrangePaint[CRITICAL]); g2.draw(line); } } // draw the axis... if ((this.rangeAxis != null) && (this.axisLocation != NONE)) { int drawWidth = AXIS_GAP; if (this.showValueLines) { drawWidth += getColumnDiameter(); } Rectangle2D drawArea; double cursor; switch (this.axisLocation) { case RIGHT: cursor = midX + getColumnRadius(); drawArea = new Rectangle2D.Double(cursor, stemTop, drawWidth, (stemBottom - stemTop + 1)); this.rangeAxis.draw(g2, cursor, area, drawArea, RectangleEdge.RIGHT, null); break; case LEFT: default: //cursor = midX - COLUMN_RADIUS - AXIS_GAP; cursor = midX - getColumnRadius(); drawArea = new Rectangle2D.Double(cursor, stemTop, drawWidth, (stemBottom - stemTop + 1)); this.rangeAxis.draw(g2, cursor, area, drawArea, RectangleEdge.LEFT, null); break; } } // draw text value on screen g2.setFont(this.valueFont); g2.setPaint(this.valuePaint); metrics = g2.getFontMetrics(); switch (this.valueLocation) { case RIGHT: g2.drawString(this.valueFormat.format(current), midX + getColumnRadius() + getGap(), midY); break; case LEFT: String valueString = this.valueFormat.format(current); int stringWidth = metrics.stringWidth(valueString); g2.drawString(valueString, midX - getColumnRadius() - getGap() - stringWidth, midY); break; case BULB: temp = this.valueFormat.format(current); i = metrics.stringWidth(temp) / 2; g2.drawString(temp, midX - i, stemBottom + getBulbRadius() + getGap()); break; default: } } g2.setPaint(this.thermometerPaint); g2.setFont(this.valueFont); // draw units indicator metrics = g2.getFontMetrics(); int tickX1 = midX - getColumnRadius() - getGap() * 2 - metrics.stringWidth(UNITS[this.units]); if (tickX1 > area.getMinX()) { g2.drawString(UNITS[this.units], tickX1, (int) (area.getMinY() + 20)); } // draw thermometer outline g2.setStroke(this.thermometerStroke); g2.draw(outerThermometer); g2.draw(innerThermometer); drawOutline(g2, area); } /** * A zoom method that does nothing. Plots are required to support the * zoom operation. In the case of a thermometer chart, it doesn't make * sense to zoom in or out, so the method is empty. * * @param percent the zoom percentage. */ @Override public void zoom(double percent) { // intentionally blank } /** * Returns a short string describing the type of plot. * * @return A short string describing the type of plot. */ @Override public String getPlotType() { return localizationResources.getString("Thermometer_Plot"); } /** * Checks to see if a new value means the axis range needs adjusting. * * @param event the dataset change event. */ @Override public void datasetChanged(DatasetChangeEvent event) { if (this.dataset != null) { Number vn = this.dataset.getValue(); if (vn != null) { double value = vn.doubleValue(); if (inSubrange(NORMAL, value)) { this.subrange = NORMAL; } else if (inSubrange(WARNING, value)) { this.subrange = WARNING; } else if (inSubrange(CRITICAL, value)) { this.subrange = CRITICAL; } else { this.subrange = -1; } setAxisRange(); } } super.datasetChanged(event); } /** * Returns the data range. * * @param axis the axis. * * @return The range of data displayed. */ @Override public Range getDataRange(ValueAxis axis) { return new Range(this.lowerBound, this.upperBound); } /** * Sets the axis range to the current values in the rangeInfo array. */ protected void setAxisRange() { if ((this.subrange >= 0) && (this.followDataInSubranges)) { this.rangeAxis.setRange( new Range(this.subrangeInfo[this.subrange][DISPLAY_LOW], this.subrangeInfo[this.subrange][DISPLAY_HIGH])); } else { this.rangeAxis.setRange(this.lowerBound, this.upperBound); } } /** * Returns the legend items for the plot. * * @return {@code null}. */ @Override public LegendItemCollection getLegendItems() { return null; } /** * Returns the orientation of the plot. * * @return The orientation (always {@link PlotOrientation#VERTICAL}). */ @Override public PlotOrientation getOrientation() { return PlotOrientation.VERTICAL; } /** * Determine whether a number is valid and finite. * * @param d the number to be tested. * * @return {@code true} if the number is valid and finite, and * {@code false} otherwise. */ protected static boolean isValidNumber(double d) { return (!(Double.isNaN(d) || Double.isInfinite(d))); } /** * Returns true if the value is in the specified range, and false otherwise. * * @param subrange the subrange. * @param value the value to check. * * @return A boolean. */ private boolean inSubrange(int subrange, double value) { return (value > this.subrangeInfo[subrange][RANGE_LOW] && value <= this.subrangeInfo[subrange][RANGE_HIGH]); } /** * Returns the mercury paint corresponding to the current data value. * Called from the {@link #draw(Graphics2D, Rectangle2D, Point2D, * PlotState, PlotRenderingInfo)} method. * * @return The paint (never {@code null}). */ private Paint getCurrentPaint() { Paint result = this.mercuryPaint; if (this.useSubrangePaint) { double value = this.dataset.getValue().doubleValue(); if (inSubrange(NORMAL, value)) { result = this.subrangePaint[NORMAL]; } else if (inSubrange(WARNING, value)) { result = this.subrangePaint[WARNING]; } else if (inSubrange(CRITICAL, value)) { result = this.subrangePaint[CRITICAL]; } } return result; } /** * Tests this plot for equality with another object. The plot's dataset * is not considered in the test. * * @param obj the object ({@code null} permitted). * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof ThermometerPlot)) { return false; } ThermometerPlot that = (ThermometerPlot) obj; if (!super.equals(obj)) { return false; } if (!Objects.equals(this.rangeAxis, that.rangeAxis)) { return false; } if (this.axisLocation != that.axisLocation) { return false; } if (this.lowerBound != that.lowerBound) { return false; } if (this.upperBound != that.upperBound) { return false; } if (!Objects.equals(this.padding, that.padding)) { return false; } if (!Objects.equals(this.thermometerStroke, that.thermometerStroke)) { return false; } if (!PaintUtils.equal(this.thermometerPaint, that.thermometerPaint)) { return false; } if (this.units != that.units) { return false; } if (this.valueLocation != that.valueLocation) { return false; } if (!Objects.equals(this.valueFont, that.valueFont)) { return false; } if (!PaintUtils.equal(this.valuePaint, that.valuePaint)) { return false; } if (!Objects.equals(this.valueFormat, that.valueFormat)) { return false; } if (!PaintUtils.equal(this.mercuryPaint, that.mercuryPaint)) { return false; } if (this.showValueLines != that.showValueLines) { return false; } if (this.subrange != that.subrange) { return false; } if (this.followDataInSubranges != that.followDataInSubranges) { return false; } if (!equal(this.subrangeInfo, that.subrangeInfo)) { return false; } if (this.useSubrangePaint != that.useSubrangePaint) { return false; } if (this.bulbRadius != that.bulbRadius) { return false; } if (this.columnRadius != that.columnRadius) { return false; } if (this.gap != that.gap) { return false; } for (int i = 0; i < this.subrangePaint.length; i++) { if (!PaintUtils.equal(this.subrangePaint[i], that.subrangePaint[i])) { return false; } } return true; } /** * Tests two double[][] arrays for equality. * * @param array1 the first array ({@code null} permitted). * @param array2 the second arrray ({@code null} permitted). * * @return A boolean. */ private static boolean equal(double[][] array1, double[][] array2) { if (array1 == null) { return (array2 == null); } if (array2 == null) { return false; } if (array1.length != array2.length) { return false; } for (int i = 0; i < array1.length; i++) { if (!Arrays.equals(array1[i], array2[i])) { return false; } } return true; } /** * Returns a clone of the plot. * * @return A clone. * * @throws CloneNotSupportedException if the plot cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { ThermometerPlot clone = (ThermometerPlot) super.clone(); if (clone.dataset != null) { clone.dataset.addChangeListener(clone); } clone.rangeAxis = (ValueAxis) ObjectUtils.clone(this.rangeAxis); if (clone.rangeAxis != null) { clone.rangeAxis.setPlot(clone); clone.rangeAxis.addChangeListener(clone); } clone.valueFormat = (NumberFormat) this.valueFormat.clone(); clone.subrangePaint = (Paint[]) this.subrangePaint.clone(); return clone; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeStroke(this.thermometerStroke, stream); SerialUtils.writePaint(this.thermometerPaint, stream); SerialUtils.writePaint(this.valuePaint, stream); SerialUtils.writePaint(this.mercuryPaint, stream); SerialUtils.writeStroke(this.subrangeIndicatorStroke, stream); SerialUtils.writeStroke(this.rangeIndicatorStroke, stream); for (int i = 0; i < 3; i++) { SerialUtils.writePaint(this.subrangePaint[i], stream); } } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.thermometerStroke = SerialUtils.readStroke(stream); this.thermometerPaint = SerialUtils.readPaint(stream); this.valuePaint = SerialUtils.readPaint(stream); this.mercuryPaint = SerialUtils.readPaint(stream); this.subrangeIndicatorStroke = SerialUtils.readStroke(stream); this.rangeIndicatorStroke = SerialUtils.readStroke(stream); this.subrangePaint = new Paint[3]; for (int i = 0; i < 3; i++) { this.subrangePaint[i] = SerialUtils.readPaint(stream); } if (this.rangeAxis != null) { this.rangeAxis.addChangeListener(this); } } /** * Multiplies the range on the domain axis/axes by the specified factor. * * @param factor the zoom factor. * @param state the plot state. * @param source the source point. */ @Override public void zoomDomainAxes(double factor, PlotRenderingInfo state, Point2D source) { // no domain axis to zoom } /** * Multiplies the range on the domain axis/axes by the specified factor. * * @param factor the zoom factor. * @param state the plot state. * @param source the source point. * @param useAnchor a flag that controls whether or not the source point * is used for the zoom anchor. */ @Override public void zoomDomainAxes(double factor, PlotRenderingInfo state, Point2D source, boolean useAnchor) { // no domain axis to zoom } /** * Multiplies the range on the range axis/axes by the specified factor. * * @param factor the zoom factor. * @param state the plot state. * @param source the source point. */ @Override public void zoomRangeAxes(double factor, PlotRenderingInfo state, Point2D source) { this.rangeAxis.resizeRange(factor); } /** * Multiplies the range on the range axis/axes by the specified factor. * * @param factor the zoom factor. * @param state the plot state. * @param source the source point. * @param useAnchor a flag that controls whether or not the source point * is used for the zoom anchor. */ @Override public void zoomRangeAxes(double factor, PlotRenderingInfo state, Point2D source, boolean useAnchor) { double anchorY = this.getRangeAxis().java2DToValue(source.getY(), state.getDataArea(), RectangleEdge.LEFT); this.rangeAxis.resizeRange(factor, anchorY); } /** * This method does nothing. * * @param lowerPercent the lower percent. * @param upperPercent the upper percent. * @param state the plot state. * @param source the source point. */ @Override public void zoomDomainAxes(double lowerPercent, double upperPercent, PlotRenderingInfo state, Point2D source) { // no domain axis to zoom } /** * Zooms the range axes. * * @param lowerPercent the lower percent. * @param upperPercent the upper percent. * @param state the plot state. * @param source the source point. */ @Override public void zoomRangeAxes(double lowerPercent, double upperPercent, PlotRenderingInfo state, Point2D source) { this.rangeAxis.zoomRange(lowerPercent, upperPercent); } /** * Returns {@code false}. * * @return A boolean. */ @Override public boolean isDomainZoomable() { return false; } /** * Returns {@code true}. * * @return A boolean. */ @Override public boolean isRangeZoomable() { return true; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/ValueAxisPlot.java000066400000000000000000000036571463604235500274520ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * ValueAxisPlot.java * ------------------ * * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import org.jfree.chart.axis.ValueAxis; import org.jfree.data.Range; /** * An interface that is implemented by plots that use a {@link ValueAxis}, * providing a standard method to find the current data range. */ public interface ValueAxisPlot { /** * Returns the data range that should apply for the specified axis. * * @param axis the axis. * * @return The data range. */ Range getDataRange(ValueAxis axis); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/ValueMarker.java000066400000000000000000000124241463604235500271200ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * ValueMarker.java * ---------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.plot; import java.awt.Paint; import java.awt.Stroke; import org.jfree.chart.event.MarkerChangeEvent; /** * A marker that represents a single value. Markers can be added to plots to * highlight specific values. */ public class ValueMarker extends Marker { /** The value. */ private double value; /** * Creates a new marker. * * @param value the value. */ public ValueMarker(double value) { super(); this.value = value; } /** * Creates a new marker. * * @param value the value. * @param paint the paint ({@code null} not permitted). * @param stroke the stroke ({@code null} not permitted). */ public ValueMarker(double value, Paint paint, Stroke stroke) { this(value, paint, stroke, paint, stroke, 1.0f); } /** * Creates a new value marker. * * @param value the value. * @param paint the paint ({@code null} not permitted). * @param stroke the stroke ({@code null} not permitted). * @param outlinePaint the outline paint ({@code null} permitted). * @param outlineStroke the outline stroke ({@code null} permitted). * @param alpha the alpha transparency (in the range 0.0f to 1.0f). */ public ValueMarker(double value, Paint paint, Stroke stroke, Paint outlinePaint, Stroke outlineStroke, float alpha) { super(paint, stroke, outlinePaint, outlineStroke, alpha); this.value = value; } /** * Returns the value. * * @return The value. * * @see #setValue(double) */ public double getValue() { return this.value; } /** * Sets the value for the marker and sends a {@link MarkerChangeEvent} to * all registered listeners. * * @param value the value. * * @see #getValue() */ public void setValue(double value) { this.value = value; notifyListeners(new MarkerChangeEvent(this)); } /** * Tests this marker for equality with an arbitrary object. This method * returns {@code true} if: * *

    *
  • {@code obj} is not {@code null};
  • *
  • {@code obj} is an instance of {@code ValueMarker};
  • *
  • {@code obj} has the same value as this marker;
  • *
  • {@code super.equals(obj)} returns {@code true}.
  • *
* * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof ValueMarker)) { return false; } ValueMarker that = (ValueMarker) obj; if (!that.canEqual(this)) { return false; } if (Double.doubleToLongBits(this.value) != Double.doubleToLongBits(that.value)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // Solves Problem: equals not symmetric return (other instanceof ValueMarker); } @Override public int hashCode() { int hash = super.hashCode(); // equals calls superclass, hashCode must also hash = 47 * hash + (int) (Double.doubleToLongBits(this.value) ^ (Double.doubleToLongBits(this.value) >>> 32)); return hash; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/WaferMapPlot.java000066400000000000000000000335261463604235500272510ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * WaferMapPlot.java * ----------------- * * (C) Copyright 2003-present, by Robert Redburn and Contributors. * * Original Author: Robert Redburn; * Contributor(s): David Gilbert; * */ package org.jfree.chart.plot; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Arc2D; import java.awt.geom.Ellipse2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import java.util.ResourceBundle; import org.jfree.chart.LegendItemCollection; import org.jfree.chart.event.PlotChangeEvent; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.event.RendererChangeListener; import org.jfree.chart.renderer.WaferMapRenderer; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.util.ResourceBundleWrapper; import org.jfree.data.general.DatasetChangeEvent; import org.jfree.data.general.WaferMapDataset; /** * A wafer map plot. */ public class WaferMapPlot extends Plot implements RendererChangeListener, Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 4668320403707308155L; /** The default grid line stroke. */ public static final Stroke DEFAULT_GRIDLINE_STROKE = new BasicStroke(0.5f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0.0f, new float[] {2.0f, 2.0f}, 0.0f); /** The default grid line paint. */ public static final Paint DEFAULT_GRIDLINE_PAINT = Color.LIGHT_GRAY; /** The default crosshair visibility. */ public static final boolean DEFAULT_CROSSHAIR_VISIBLE = false; /** The default crosshair stroke. */ public static final Stroke DEFAULT_CROSSHAIR_STROKE = DEFAULT_GRIDLINE_STROKE; /** The default crosshair paint. */ public static final Paint DEFAULT_CROSSHAIR_PAINT = Color.BLUE; /** The resourceBundle for the localization. */ protected static ResourceBundle localizationResources = ResourceBundleWrapper.getBundle( "org.jfree.chart.plot.LocalizationBundle"); /** The plot orientation. * vertical = notch down * horizontal = notch right */ private PlotOrientation orientation; /** The dataset. */ private WaferMapDataset dataset; /** * Object responsible for drawing the visual representation of each point * on the plot. */ private WaferMapRenderer renderer; /** * Creates a new plot with no dataset. */ public WaferMapPlot() { this(null); } /** * Creates a new plot. * * @param dataset the dataset ({@code null} permitted). */ public WaferMapPlot(WaferMapDataset dataset) { this(dataset, null); } /** * Creates a new plot. * * @param dataset the dataset ({@code null} permitted). * @param renderer the renderer ({@code null} permitted). */ public WaferMapPlot(WaferMapDataset dataset, WaferMapRenderer renderer) { super(); this.orientation = PlotOrientation.VERTICAL; this.dataset = dataset; if (dataset != null) { dataset.addChangeListener(this); } this.renderer = renderer; if (renderer != null) { renderer.setPlot(this); renderer.addChangeListener(this); } } /** * Returns the plot type as a string. * * @return A short string describing the type of plot. */ @Override public String getPlotType() { return ("WMAP_Plot"); } /** * Returns the dataset * * @return The dataset (possibly {@code null}). */ public WaferMapDataset getDataset() { return this.dataset; } /** * Sets the dataset used by the plot and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param dataset the dataset ({@code null} permitted). */ public void setDataset(WaferMapDataset dataset) { // if there is an existing dataset, remove the plot from the list of // change listeners... if (this.dataset != null) { this.dataset.removeChangeListener(this); } // set the new dataset, and register the chart as a change listener... this.dataset = dataset; if (dataset != null) { setDatasetGroup(dataset.getGroup()); dataset.addChangeListener(this); } // send a dataset change event to self to trigger plot change event datasetChanged(new DatasetChangeEvent(this, dataset)); } /** * Sets the item renderer, and notifies all listeners of a change to the * plot. If the renderer is set to {@code null}, no chart will be * drawn. * * @param renderer the new renderer ({@code null} permitted). */ public void setRenderer(WaferMapRenderer renderer) { if (this.renderer != null) { this.renderer.removeChangeListener(this); } this.renderer = renderer; if (renderer != null) { renderer.setPlot(this); } fireChangeEvent(); } /** * Draws the wafermap view. * * @param g2 the graphics device. * @param area the plot area. * @param anchor the anchor point ({@code null} permitted). * @param state the plot state. * @param info the plot rendering info. */ @Override public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, PlotState state, PlotRenderingInfo info) { // if the plot area is too small, just return... boolean b1 = (area.getWidth() <= MINIMUM_WIDTH_TO_DRAW); boolean b2 = (area.getHeight() <= MINIMUM_HEIGHT_TO_DRAW); if (b1 || b2) { return; } // record the plot area... if (info != null) { info.setPlotArea(area); } // adjust the drawing area for the plot insets (if any)... RectangleInsets insets = getInsets(); insets.trim(area); drawChipGrid(g2, area); drawWaferEdge(g2, area); } /** * Calculates and draws the chip locations on the wafer. * * @param g2 the graphics device. * @param plotArea the plot area. */ protected void drawChipGrid(Graphics2D g2, Rectangle2D plotArea) { Shape savedClip = g2.getClip(); g2.setClip(getWaferEdge(plotArea)); Rectangle2D chip = new Rectangle2D.Double(); int xchips = 35; int ychips = 20; double space = 1d; if (this.dataset != null) { xchips = this.dataset.getMaxChipX() + 2; ychips = this.dataset.getMaxChipY() + 2; space = this.dataset.getChipSpace(); } double startX = plotArea.getX(); double startY = plotArea.getY(); double chipWidth = 1d; double chipHeight = 1d; if (plotArea.getWidth() != plotArea.getHeight()) { double major, minor; if (plotArea.getWidth() > plotArea.getHeight()) { major = plotArea.getWidth(); minor = plotArea.getHeight(); } else { major = plotArea.getHeight(); minor = plotArea.getWidth(); } //set upperLeft point if (plotArea.getWidth() == minor) { // x is minor startY += (major - minor) / 2; chipWidth = (plotArea.getWidth() - (space * xchips - 1)) / xchips; chipHeight = (plotArea.getWidth() - (space * ychips - 1)) / ychips; } else { // y is minor startX += (major - minor) / 2; chipWidth = (plotArea.getHeight() - (space * xchips - 1)) / xchips; chipHeight = (plotArea.getHeight() - (space * ychips - 1)) / ychips; } } for (int x = 1; x <= xchips; x++) { double upperLeftX = (startX - chipWidth) + (chipWidth * x) + (space * (x - 1)); for (int y = 1; y <= ychips; y++) { double upperLeftY = (startY - chipHeight) + (chipHeight * y) + (space * (y - 1)); chip.setFrame(upperLeftX, upperLeftY, chipWidth, chipHeight); g2.setColor(Color.WHITE); if (this.dataset.getChipValue(x - 1, ychips - y - 1) != null) { g2.setPaint( this.renderer.getChipColor( this.dataset.getChipValue(x - 1, ychips - y - 1) ) ); } g2.fill(chip); g2.setColor(Color.LIGHT_GRAY); g2.draw(chip); } } g2.setClip(savedClip); } /** * Calculates the location of the waferedge. * * @param plotArea the plot area. * * @return The wafer edge. */ protected Ellipse2D getWaferEdge(Rectangle2D plotArea) { Ellipse2D edge = new Ellipse2D.Double(); double diameter = plotArea.getWidth(); double upperLeftX = plotArea.getX(); double upperLeftY = plotArea.getY(); //get major dimension if (plotArea.getWidth() != plotArea.getHeight()) { double major, minor; if (plotArea.getWidth() > plotArea.getHeight()) { major = plotArea.getWidth(); minor = plotArea.getHeight(); } else { major = plotArea.getHeight(); minor = plotArea.getWidth(); } //ellipse diameter is the minor dimension diameter = minor; //set upperLeft point if (plotArea.getWidth() == minor) { // x is minor upperLeftY = plotArea.getY() + (major - minor) / 2; } else { // y is minor upperLeftX = plotArea.getX() + (major - minor) / 2; } } edge.setFrame(upperLeftX, upperLeftY, diameter, diameter); return edge; } /** * Draws the waferedge, including the notch. * * @param g2 the graphics device. * @param plotArea the plot area. */ protected void drawWaferEdge(Graphics2D g2, Rectangle2D plotArea) { // draw the wafer Ellipse2D waferEdge = getWaferEdge(plotArea); g2.setColor(Color.BLACK); g2.draw(waferEdge); // calculate and draw the notch // horizontal orientation is considered notch right // vertical orientation is considered notch down Arc2D notch; Rectangle2D waferFrame = waferEdge.getFrame(); double notchDiameter = waferFrame.getWidth() * 0.04; if (this.orientation == PlotOrientation.HORIZONTAL) { Rectangle2D notchFrame = new Rectangle2D.Double( waferFrame.getX() + waferFrame.getWidth() - (notchDiameter / 2), waferFrame.getY() + (waferFrame.getHeight() / 2) - (notchDiameter / 2), notchDiameter, notchDiameter ); notch = new Arc2D.Double(notchFrame, 90d, 180d, Arc2D.OPEN); } else { Rectangle2D notchFrame = new Rectangle2D.Double( waferFrame.getX() + (waferFrame.getWidth() / 2) - (notchDiameter / 2), waferFrame.getY() + waferFrame.getHeight() - (notchDiameter / 2), notchDiameter, notchDiameter ); notch = new Arc2D.Double(notchFrame, 0d, 180d, Arc2D.OPEN); } g2.setColor(Color.WHITE); g2.fill(notch); g2.setColor(Color.BLACK); g2.draw(notch); } /** * Return the legend items from the renderer. * * @return The legend items. */ @Override public LegendItemCollection getLegendItems() { return this.renderer.getLegendCollection(); } /** * Notifies all registered listeners of a renderer change. * * @param event the event. */ @Override public void rendererChanged(RendererChangeEvent event) { fireChangeEvent(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/XYCrosshairState.java000066400000000000000000000034031463604235500301160ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * XYCrosshairState.java * --------------------- * (C) Copyright 2008-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import org.jfree.chart.renderer.xy.XYItemRenderer; /** * Crosshair state information for the {@link XYPlot} and {@link XYItemRenderer} * classes. */ public class XYCrosshairState extends CrosshairState { /** * Creates a new instance. */ public XYCrosshairState() { } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/XYPlot.java000066400000000000000000005423251463604235500261110ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------- * XYPlot.java * ----------- * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Craig MacFarlane; * Mark Watson (www.markwatson.com); * Jonathan Nash; * Gideon Krause; * Klaus Rheinwald; * Xavier Poinsard; * Richard Atkinson; * Arnaud Lelievre; * Nicolas Brodu; * Eduardo Ramalho; * Sergei Ivanov; * Richard West, Advanced Micro Devices, Inc.; * Ulrich Voigt - patches 1997549 and 2686040; * Peter Kolb - patches 1934255, 2603321 and 2809117; * Andrew Mickish - patch 1868749; * */ package org.jfree.chart.plot; import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Composite; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.ResourceBundle; import java.util.Set; import java.util.TreeMap; import org.jfree.chart.JFreeChart; import org.jfree.chart.LegendItem; import org.jfree.chart.LegendItemCollection; import org.jfree.chart.annotations.Annotation; import org.jfree.chart.annotations.XYAnnotation; import org.jfree.chart.annotations.XYAnnotationBoundsInfo; import org.jfree.chart.axis.Axis; import org.jfree.chart.axis.AxisCollection; import org.jfree.chart.axis.AxisLocation; import org.jfree.chart.axis.AxisSpace; import org.jfree.chart.axis.AxisState; import org.jfree.chart.axis.TickType; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.axis.ValueTick; import org.jfree.chart.event.AnnotationChangeEvent; import org.jfree.chart.event.ChartChangeEventType; import org.jfree.chart.event.PlotChangeEvent; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.event.RendererChangeListener; import org.jfree.chart.renderer.RendererUtils; import org.jfree.chart.renderer.xy.AbstractXYItemRenderer; import org.jfree.chart.renderer.xy.XYItemRenderer; import org.jfree.chart.renderer.xy.XYItemRendererState; import org.jfree.chart.ui.Layer; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.util.CloneUtils; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.ResourceBundleWrapper; import org.jfree.chart.util.SerialUtils; import org.jfree.chart.util.ShadowGenerator; import org.jfree.data.Range; import org.jfree.data.general.DatasetChangeEvent; import org.jfree.data.general.DatasetUtils; import org.jfree.data.xy.XYDataset; /** * A general class for plotting data in the form of (x, y) pairs. This plot can * use data from any class that implements the {@link XYDataset} interface. *

* {@code XYPlot} makes use of an {@link XYItemRenderer} to draw each point * on the plot. By using different renderers, various chart types can be * produced. *

* The {@link org.jfree.chart.ChartFactory} class contains static methods for * creating pre-configured charts. */ public class XYPlot extends Plot implements ValueAxisPlot, Pannable, Zoomable, RendererChangeListener, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 7044148245716569264L; /** The default grid line stroke. */ public static final Stroke DEFAULT_GRIDLINE_STROKE = new BasicStroke(0.5f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0.0f, new float[] {2.0f, 2.0f}, 0.0f); /** The default grid line paint. */ public static final Paint DEFAULT_GRIDLINE_PAINT = Color.LIGHT_GRAY; /** The default crosshair visibility. */ public static final boolean DEFAULT_CROSSHAIR_VISIBLE = false; /** The default crosshair stroke. */ public static final Stroke DEFAULT_CROSSHAIR_STROKE = DEFAULT_GRIDLINE_STROKE; /** The default crosshair paint. */ public static final Paint DEFAULT_CROSSHAIR_PAINT = Color.BLUE; /** The resourceBundle for the localization. */ protected static ResourceBundle localizationResources = ResourceBundleWrapper.getBundle( "org.jfree.chart.plot.LocalizationBundle"); /** The plot orientation. */ private PlotOrientation orientation; /** The offset between the data area and the axes. */ private RectangleInsets axisOffset; /** The domain axis / axes (used for the x-values). */ private Map domainAxes; /** The domain axis locations. */ private Map domainAxisLocations; /** The range axis (used for the y-values). */ private Map rangeAxes; /** The range axis location. */ private Map rangeAxisLocations; /** Storage for the datasets. */ private Map datasets; /** Storage for the renderers. */ private Map renderers; /** * Storage for the mapping between datasets/renderers and domain axes. The * keys in the map are Integer objects, corresponding to the dataset * index. The values in the map are List objects containing Integer * objects (corresponding to the axis indices). If the map contains no * entry for a dataset, it is assumed to map to the primary domain axis * (index = 0). */ private Map> datasetToDomainAxesMap; /** * Storage for the mapping between datasets/renderers and range axes. The * keys in the map are Integer objects, corresponding to the dataset * index. The values in the map are List objects containing Integer * objects (corresponding to the axis indices). If the map contains no * entry for a dataset, it is assumed to map to the primary domain axis * (index = 0). */ private Map> datasetToRangeAxesMap; /** The origin point for the quadrants (if drawn). */ private transient Point2D quadrantOrigin = new Point2D.Double(0.0, 0.0); /** The paint used for each quadrant. */ private transient Paint[] quadrantPaint = new Paint[] {null, null, null, null}; /** A flag that controls whether the domain grid-lines are visible. */ private boolean domainGridlinesVisible; /** The stroke used to draw the domain grid-lines. */ private transient Stroke domainGridlineStroke; /** The paint used to draw the domain grid-lines. */ private transient Paint domainGridlinePaint; /** A flag that controls whether the range grid-lines are visible. */ private boolean rangeGridlinesVisible; /** The stroke used to draw the range grid-lines. */ private transient Stroke rangeGridlineStroke; /** The paint used to draw the range grid-lines. */ private transient Paint rangeGridlinePaint; /** * A flag that controls whether the domain minor grid-lines are visible. */ private boolean domainMinorGridlinesVisible; /** * The stroke used to draw the domain minor grid-lines. */ private transient Stroke domainMinorGridlineStroke; /** * The paint used to draw the domain minor grid-lines. */ private transient Paint domainMinorGridlinePaint; /** * A flag that controls whether the range minor grid-lines are visible. */ private boolean rangeMinorGridlinesVisible; /** * The stroke used to draw the range minor grid-lines. */ private transient Stroke rangeMinorGridlineStroke; /** * The paint used to draw the range minor grid-lines. */ private transient Paint rangeMinorGridlinePaint; /** * A flag that controls whether or not the zero baseline against the domain * axis is visible. */ private boolean domainZeroBaselineVisible; /** * The stroke used for the zero baseline against the domain axis. */ private transient Stroke domainZeroBaselineStroke; /** * The paint used for the zero baseline against the domain axis. */ private transient Paint domainZeroBaselinePaint; /** * A flag that controls whether or not the zero baseline against the range * axis is visible. */ private boolean rangeZeroBaselineVisible; /** The stroke used for the zero baseline against the range axis. */ private transient Stroke rangeZeroBaselineStroke; /** The paint used for the zero baseline against the range axis. */ private transient Paint rangeZeroBaselinePaint; /** A flag that controls whether or not a domain crosshair is drawn..*/ private boolean domainCrosshairVisible; /** The domain crosshair value. */ private double domainCrosshairValue; /** The pen/brush used to draw the crosshair (if any). */ private transient Stroke domainCrosshairStroke; /** The color used to draw the crosshair (if any). */ private transient Paint domainCrosshairPaint; /** * A flag that controls whether or not the crosshair locks onto actual * data points. */ private boolean domainCrosshairLockedOnData = true; /** A flag that controls whether or not a range crosshair is drawn..*/ private boolean rangeCrosshairVisible; /** The range crosshair value. */ private double rangeCrosshairValue; /** The pen/brush used to draw the crosshair (if any). */ private transient Stroke rangeCrosshairStroke; /** The color used to draw the crosshair (if any). */ private transient Paint rangeCrosshairPaint; /** * A flag that controls whether or not the crosshair locks onto actual * data points. */ private boolean rangeCrosshairLockedOnData = true; /** A map of lists of foreground markers (optional) for the domain axes. */ private Map> foregroundDomainMarkers; /** A map of lists of background markers (optional) for the domain axes. */ private Map> backgroundDomainMarkers; /** A map of lists of foreground markers (optional) for the range axes. */ private Map> foregroundRangeMarkers; /** A map of lists of background markers (optional) for the range axes. */ private Map> backgroundRangeMarkers; /** * A (possibly empty) list of annotations for the plot. The list should * be initialised in the constructor and never allowed to be * {@code null}. */ private List annotations; /** The paint used for the domain tick bands (if any). */ private transient Paint domainTickBandPaint; /** The paint used for the range tick bands (if any). */ private transient Paint rangeTickBandPaint; /** The fixed domain axis space. */ private AxisSpace fixedDomainAxisSpace; /** The fixed range axis space. */ private AxisSpace fixedRangeAxisSpace; /** * The order of the dataset rendering (REVERSE draws the primary dataset * last so that it appears to be on top). */ private DatasetRenderingOrder datasetRenderingOrder = DatasetRenderingOrder.REVERSE; /** * The order of the series rendering (REVERSE draws the primary series * last so that it appears to be on top). */ private SeriesRenderingOrder seriesRenderingOrder = SeriesRenderingOrder.REVERSE; /** * The weight for this plot (only relevant if this is a subplot in a * combined plot). */ private int weight; /** * An optional collection of legend items that can be returned by the * getLegendItems() method. */ private LegendItemCollection fixedLegendItems; /** * A flag that controls whether or not panning is enabled for the domain * axis/axes. */ private boolean domainPannable; /** * A flag that controls whether or not panning is enabled for the range * axis/axes. */ private boolean rangePannable; /** * The shadow generator ({@code null} permitted). */ private ShadowGenerator shadowGenerator; /** * Creates a new {@code XYPlot} instance with no dataset, no axes and * no renderer. You should specify these items before using the plot. */ public XYPlot() { this(null, null, null, null); } /** * Creates a new plot with the specified dataset, axes and renderer. Any * of the arguments can be {@code null}, but in that case you should * take care to specify the value before using the plot (otherwise a * {@code NullPointerException} may be thrown). * * @param dataset the dataset ({@code null} permitted). * @param domainAxis the domain axis ({@code null} permitted). * @param rangeAxis the range axis ({@code null} permitted). * @param renderer the renderer ({@code null} permitted). */ public XYPlot(XYDataset dataset, ValueAxis domainAxis, ValueAxis rangeAxis, XYItemRenderer renderer) { super(); this.orientation = PlotOrientation.VERTICAL; this.weight = 1; // only relevant when this is a subplot this.axisOffset = RectangleInsets.ZERO_INSETS; // allocate storage for datasets, axes and renderers (all optional) this.domainAxes = new HashMap<>(); this.domainAxisLocations = new HashMap<>(); this.foregroundDomainMarkers = new HashMap<>(); this.backgroundDomainMarkers = new HashMap<>(); this.rangeAxes = new HashMap<>(); this.rangeAxisLocations = new HashMap<>(); this.foregroundRangeMarkers = new HashMap<>(); this.backgroundRangeMarkers = new HashMap<>(); this.datasets = new HashMap<>(); this.renderers = new HashMap<>(); this.datasetToDomainAxesMap = new TreeMap<>(); this.datasetToRangeAxesMap = new TreeMap<>(); this.annotations = new ArrayList<>(); this.datasets.put(0, dataset); if (dataset != null) { dataset.addChangeListener(this); } this.renderers.put(0, renderer); if (renderer != null) { renderer.setPlot(this); renderer.addChangeListener(this); } this.domainAxes.put(0, domainAxis); mapDatasetToDomainAxis(0, 0); if (domainAxis != null) { domainAxis.setPlot(this); domainAxis.addChangeListener(this); } this.domainAxisLocations.put(0, AxisLocation.BOTTOM_OR_LEFT); this.rangeAxes.put(0, rangeAxis); mapDatasetToRangeAxis(0, 0); if (rangeAxis != null) { rangeAxis.setPlot(this); rangeAxis.addChangeListener(this); } this.rangeAxisLocations.put(0, AxisLocation.BOTTOM_OR_LEFT); configureDomainAxes(); configureRangeAxes(); this.domainGridlinesVisible = true; this.domainGridlineStroke = DEFAULT_GRIDLINE_STROKE; this.domainGridlinePaint = DEFAULT_GRIDLINE_PAINT; this.domainMinorGridlinesVisible = false; this.domainMinorGridlineStroke = DEFAULT_GRIDLINE_STROKE; this.domainMinorGridlinePaint = Color.WHITE; this.domainZeroBaselineVisible = false; this.domainZeroBaselinePaint = Color.BLACK; this.domainZeroBaselineStroke = new BasicStroke(0.5f); this.rangeGridlinesVisible = true; this.rangeGridlineStroke = DEFAULT_GRIDLINE_STROKE; this.rangeGridlinePaint = DEFAULT_GRIDLINE_PAINT; this.rangeMinorGridlinesVisible = false; this.rangeMinorGridlineStroke = DEFAULT_GRIDLINE_STROKE; this.rangeMinorGridlinePaint = Color.WHITE; this.rangeZeroBaselineVisible = false; this.rangeZeroBaselinePaint = Color.BLACK; this.rangeZeroBaselineStroke = new BasicStroke(0.5f); this.domainCrosshairVisible = false; this.domainCrosshairValue = 0.0; this.domainCrosshairStroke = DEFAULT_CROSSHAIR_STROKE; this.domainCrosshairPaint = DEFAULT_CROSSHAIR_PAINT; this.rangeCrosshairVisible = false; this.rangeCrosshairValue = 0.0; this.rangeCrosshairStroke = DEFAULT_CROSSHAIR_STROKE; this.rangeCrosshairPaint = DEFAULT_CROSSHAIR_PAINT; this.shadowGenerator = null; } /** * Returns the plot type as a string. * * @return A short string describing the type of plot. */ @Override public String getPlotType() { return localizationResources.getString("XY_Plot"); } /** * Returns the orientation of the plot. * * @return The orientation (never {@code null}). * * @see #setOrientation(PlotOrientation) */ @Override public PlotOrientation getOrientation() { return this.orientation; } /** * Sets the orientation for the plot and sends a {@link PlotChangeEvent} to * all registered listeners. * * @param orientation the orientation ({@code null} not allowed). * * @see #getOrientation() */ public void setOrientation(PlotOrientation orientation) { Args.nullNotPermitted(orientation, "orientation"); if (orientation != this.orientation) { this.orientation = orientation; fireChangeEvent(); } } /** * Returns the axis offset. * * @return The axis offset (never {@code null}). * * @see #setAxisOffset(RectangleInsets) */ public RectangleInsets getAxisOffset() { return this.axisOffset; } /** * Sets the axis offsets (gap between the data area and the axes) and sends * a {@link PlotChangeEvent} to all registered listeners. * * @param offset the offset ({@code null} not permitted). * * @see #getAxisOffset() */ public void setAxisOffset(RectangleInsets offset) { Args.nullNotPermitted(offset, "offset"); this.axisOffset = offset; fireChangeEvent(); } /** * Returns the domain axis with index 0. If the domain axis for this plot * is {@code null}, then the method will return the parent plot's * domain axis (if there is a parent plot). * * @return The domain axis (possibly {@code null}). * * @see #getDomainAxis(int) * @see #setDomainAxis(ValueAxis) */ public ValueAxis getDomainAxis() { return getDomainAxis(0); } /** * Returns the domain axis with the specified index, or {@code null} if * there is no axis with that index. * * @param index the axis index. * * @return The axis ({@code null} possible). * * @see #setDomainAxis(int, ValueAxis) */ public ValueAxis getDomainAxis(int index) { ValueAxis result = this.domainAxes.get(index); if (result == null) { Plot parent = getParent(); if (parent instanceof XYPlot) { XYPlot xy = (XYPlot) parent; result = xy.getDomainAxis(index); } } return result; } /** * Returns a map containing the domain axes that are assigned to this plot. * The map is unmodifiable. * * @return A map containing the domain axes that are assigned to the plot * (never {@code null}). * * @since 1.5.4 */ public Map getDomainAxes() { return Collections.unmodifiableMap(this.domainAxes); } /** * Sets the domain axis for the plot and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param axis the new axis ({@code null} permitted). * * @see #getDomainAxis() * @see #setDomainAxis(int, ValueAxis) */ public void setDomainAxis(ValueAxis axis) { setDomainAxis(0, axis); } /** * Sets a domain axis and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param index the axis index. * @param axis the axis ({@code null} permitted). * * @see #getDomainAxis(int) * @see #setRangeAxis(int, ValueAxis) */ public void setDomainAxis(int index, ValueAxis axis) { setDomainAxis(index, axis, true); } /** * Sets a domain axis and, if requested, sends a {@link PlotChangeEvent} to * all registered listeners. * * @param index the axis index. * @param axis the axis. * @param notify notify listeners? * * @see #getDomainAxis(int) */ public void setDomainAxis(int index, ValueAxis axis, boolean notify) { ValueAxis existing = getDomainAxis(index); if (existing != null) { existing.removeChangeListener(this); } if (axis != null) { axis.setPlot(this); } this.domainAxes.put(index, axis); if (axis != null) { axis.configure(); axis.addChangeListener(this); } if (notify) { fireChangeEvent(); } } /** * Sets the domain axes for this plot and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param axes the axes ({@code null} not permitted). * * @see #setRangeAxes(ValueAxis[]) */ public void setDomainAxes(ValueAxis[] axes) { for (int i = 0; i < axes.length; i++) { setDomainAxis(i, axes[i], false); } fireChangeEvent(); } /** * Returns the location of the primary domain axis. * * @return The location (never {@code null}). * * @see #setDomainAxisLocation(AxisLocation) */ public AxisLocation getDomainAxisLocation() { return this.domainAxisLocations.get(0); } /** * Sets the location of the primary domain axis and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param location the location ({@code null} not permitted). * * @see #getDomainAxisLocation() */ public void setDomainAxisLocation(AxisLocation location) { // delegate... setDomainAxisLocation(0, location, true); } /** * Sets the location of the domain axis and, if requested, sends a * {@link PlotChangeEvent} to all registered listeners. * * @param location the location ({@code null} not permitted). * @param notify notify listeners? * * @see #getDomainAxisLocation() */ public void setDomainAxisLocation(AxisLocation location, boolean notify) { // delegate... setDomainAxisLocation(0, location, notify); } /** * Returns the edge for the primary domain axis (taking into account the * plot's orientation). * * @return The edge. * * @see #getDomainAxisLocation() * @see #getOrientation() */ public RectangleEdge getDomainAxisEdge() { return Plot.resolveDomainAxisLocation(getDomainAxisLocation(), this.orientation); } /** * Returns the number of domain axes. * * @return The axis count. * * @see #getRangeAxisCount() */ public int getDomainAxisCount() { return this.domainAxes.size(); } /** * Clears the domain axes from the plot and sends a {@link PlotChangeEvent} * to all registered listeners. * * @see #clearRangeAxes() */ public void clearDomainAxes() { for (ValueAxis axis: this.domainAxes.values()) { if (axis != null) { axis.removeChangeListener(this); } } this.domainAxes.clear(); fireChangeEvent(); } /** * Configures the domain axes. */ public void configureDomainAxes() { for (ValueAxis axis: this.domainAxes.values()) { if (axis != null) { axis.configure(); } } } /** * Returns the location for a domain axis. If this hasn't been set * explicitly, the method returns the location that is opposite to the * primary domain axis location. * * @param index the axis index (must be >= 0). * * @return The location (never {@code null}). * * @see #setDomainAxisLocation(int, AxisLocation) */ public AxisLocation getDomainAxisLocation(int index) { AxisLocation result = this.domainAxisLocations.get(index); if (result == null) { result = AxisLocation.getOpposite(getDomainAxisLocation()); } return result; } /** * Sets the location for a domain axis and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param index the axis index. * @param location the location ({@code null} not permitted for index * 0). * * @see #getDomainAxisLocation(int) */ public void setDomainAxisLocation(int index, AxisLocation location) { // delegate... setDomainAxisLocation(index, location, true); } /** * Sets the axis location for a domain axis and, if requested, sends a * {@link PlotChangeEvent} to all registered listeners. * * @param index the axis index (must be >= 0). * @param location the location ({@code null} not permitted for * index 0). * @param notify notify listeners? * * @see #getDomainAxisLocation(int) * @see #setRangeAxisLocation(int, AxisLocation, boolean) */ public void setDomainAxisLocation(int index, AxisLocation location, boolean notify) { if (index == 0 && location == null) { throw new IllegalArgumentException( "Null 'location' for index 0 not permitted."); } this.domainAxisLocations.put(index, location); if (notify) { fireChangeEvent(); } } /** * Returns the edge for a domain axis. * * @param index the axis index. * * @return The edge. * * @see #getRangeAxisEdge(int) */ public RectangleEdge getDomainAxisEdge(int index) { AxisLocation location = getDomainAxisLocation(index); return Plot.resolveDomainAxisLocation(location, this.orientation); } /** * Returns the range axis for the plot. If the range axis for this plot is * {@code null}, then the method will return the parent plot's range * axis (if there is a parent plot). * * @return The range axis. * * @see #getRangeAxis(int) * @see #setRangeAxis(ValueAxis) */ public ValueAxis getRangeAxis() { return getRangeAxis(0); } /** * Sets the range axis for the plot and sends a {@link PlotChangeEvent} to * all registered listeners. * * @param axis the axis ({@code null} permitted). * * @see #getRangeAxis() * @see #setRangeAxis(int, ValueAxis) */ public void setRangeAxis(ValueAxis axis) { if (axis != null) { axis.setPlot(this); } // plot is likely registered as a listener with the existing axis... ValueAxis existing = getRangeAxis(); if (existing != null) { existing.removeChangeListener(this); } this.rangeAxes.put(0, axis); if (axis != null) { axis.configure(); axis.addChangeListener(this); } fireChangeEvent(); } /** * Returns the location of the primary range axis. * * @return The location (never {@code null}). * * @see #setRangeAxisLocation(AxisLocation) */ public AxisLocation getRangeAxisLocation() { return (AxisLocation) this.rangeAxisLocations.get(0); } /** * Sets the location of the primary range axis and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param location the location ({@code null} not permitted). * * @see #getRangeAxisLocation() */ public void setRangeAxisLocation(AxisLocation location) { // delegate... setRangeAxisLocation(0, location, true); } /** * Sets the location of the primary range axis and, if requested, sends a * {@link PlotChangeEvent} to all registered listeners. * * @param location the location ({@code null} not permitted). * @param notify notify listeners? * * @see #getRangeAxisLocation() */ public void setRangeAxisLocation(AxisLocation location, boolean notify) { // delegate... setRangeAxisLocation(0, location, notify); } /** * Returns the edge for the primary range axis. * * @return The range axis edge. * * @see #getRangeAxisLocation() * @see #getOrientation() */ public RectangleEdge getRangeAxisEdge() { return Plot.resolveRangeAxisLocation(getRangeAxisLocation(), this.orientation); } /** * Returns the range axis with the specified index, or {@code null} if * there is no axis with that index. * * @param index the axis index (must be >= 0). * * @return The axis ({@code null} possible). * * @see #setRangeAxis(int, ValueAxis) */ public ValueAxis getRangeAxis(int index) { ValueAxis result = this.rangeAxes.get(index); if (result == null) { Plot parent = getParent(); if (parent instanceof XYPlot) { XYPlot xy = (XYPlot) parent; result = xy.getRangeAxis(index); } } return result; } /** * Returns a map containing the range axes that are assigned to this plot. * The map is unmodifiable. * * @return A map containing the range axes that are assigned to the plot * (never {@code null}). * * @since 1.5.4 */ public Map getRangeAxes() { return Collections.unmodifiableMap(this.rangeAxes); } /** * Sets a range axis and sends a {@link PlotChangeEvent} to all registered * listeners. * * @param index the axis index. * @param axis the axis ({@code null} permitted). * * @see #getRangeAxis(int) */ public void setRangeAxis(int index, ValueAxis axis) { setRangeAxis(index, axis, true); } /** * Sets a range axis and, if requested, sends a {@link PlotChangeEvent} to * all registered listeners. * * @param index the axis index. * @param axis the axis ({@code null} permitted). * @param notify notify listeners? * * @see #getRangeAxis(int) */ public void setRangeAxis(int index, ValueAxis axis, boolean notify) { ValueAxis existing = getRangeAxis(index); if (existing != null) { existing.removeChangeListener(this); } if (axis != null) { axis.setPlot(this); } this.rangeAxes.put(index, axis); if (axis != null) { axis.configure(); axis.addChangeListener(this); } if (notify) { fireChangeEvent(); } } /** * Sets the range axes for this plot and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param axes the axes ({@code null} not permitted). * * @see #setDomainAxes(ValueAxis[]) */ public void setRangeAxes(ValueAxis[] axes) { for (int i = 0; i < axes.length; i++) { setRangeAxis(i, axes[i], false); } fireChangeEvent(); } /** * Returns the number of range axes. * * @return The axis count. * * @see #getDomainAxisCount() */ public int getRangeAxisCount() { return this.rangeAxes.size(); } /** * Clears the range axes from the plot and sends a {@link PlotChangeEvent} * to all registered listeners. * * @see #clearDomainAxes() */ public void clearRangeAxes() { for (ValueAxis axis: this.rangeAxes.values()) { if (axis != null) { axis.removeChangeListener(this); } } this.rangeAxes.clear(); fireChangeEvent(); } /** * Configures the range axes. * * @see #configureDomainAxes() */ public void configureRangeAxes() { for (ValueAxis axis: this.rangeAxes.values()) { if (axis != null) { axis.configure(); } } } /** * Returns the location for a range axis. If this hasn't been set * explicitly, the method returns the location that is opposite to the * primary range axis location. * * @param index the axis index (must be >= 0). * * @return The location (never {@code null}). * * @see #setRangeAxisLocation(int, AxisLocation) */ public AxisLocation getRangeAxisLocation(int index) { AxisLocation result = this.rangeAxisLocations.get(index); if (result == null) { result = AxisLocation.getOpposite(getRangeAxisLocation()); } return result; } /** * Sets the location for a range axis and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param index the axis index. * @param location the location ({@code null} permitted). * * @see #getRangeAxisLocation(int) */ public void setRangeAxisLocation(int index, AxisLocation location) { // delegate... setRangeAxisLocation(index, location, true); } /** * Sets the axis location for a domain axis and, if requested, sends a * {@link PlotChangeEvent} to all registered listeners. * * @param index the axis index. * @param location the location ({@code null} not permitted for index 0). * @param notify notify listeners? * * @see #getRangeAxisLocation(int) * @see #setDomainAxisLocation(int, AxisLocation, boolean) */ public void setRangeAxisLocation(int index, AxisLocation location, boolean notify) { if (index == 0 && location == null) { throw new IllegalArgumentException( "Null 'location' for index 0 not permitted."); } this.rangeAxisLocations.put(index, location); if (notify) { fireChangeEvent(); } } /** * Returns the edge for a range axis. * * @param index the axis index. * * @return The edge. * * @see #getRangeAxisLocation(int) * @see #getOrientation() */ public RectangleEdge getRangeAxisEdge(int index) { AxisLocation location = getRangeAxisLocation(index); return Plot.resolveRangeAxisLocation(location, this.orientation); } /** * Returns the primary dataset for the plot. * * @return The primary dataset (possibly {@code null}). * * @see #getDataset(int) * @see #setDataset(XYDataset) */ public XYDataset getDataset() { return getDataset(0); } /** * Returns the dataset with the specified index, or {@code null} if there * is no dataset with that index. * * @param index the dataset index (must be >= 0). * * @return The dataset (possibly {@code null}). * * @see #setDataset(int, XYDataset) */ public XYDataset getDataset(int index) { return this.datasets.get(index); } /** * Returns a map containing the datasets that are assigned to this plot. * The map is unmodifiable. * * @return A map containing the datasets that are assigned to the plot * (never {@code null}). * * @since 1.5.4 */ public Map getDatasets() { return Collections.unmodifiableMap(this.datasets); } /** * Sets the primary dataset for the plot, replacing the existing dataset if * there is one. * * @param dataset the dataset ({@code null} permitted). * * @see #getDataset() * @see #setDataset(int, XYDataset) */ public void setDataset(XYDataset dataset) { setDataset(0, dataset); } /** * Sets a dataset for the plot and sends a change event to all registered * listeners. * * @param index the dataset index (must be >= 0). * @param dataset the dataset ({@code null} permitted). * * @see #getDataset(int) */ public void setDataset(int index, XYDataset dataset) { XYDataset existing = getDataset(index); if (existing != null) { existing.removeChangeListener(this); } this.datasets.put(index, dataset); if (dataset != null) { dataset.addChangeListener(this); } // send a dataset change event to self... DatasetChangeEvent event = new DatasetChangeEvent(this, dataset); datasetChanged(event); } /** * Returns the number of datasets. * * @return The number of datasets. */ public int getDatasetCount() { return (int) this.datasets.values().stream().filter(Objects::nonNull).count(); } /** * Returns the index of the specified dataset, or {@code -1} if the * dataset does not belong to the plot. * * @param dataset the dataset ({@code null} not permitted). * * @return The index or -1. */ public int indexOf(XYDataset dataset) { for (Map.Entry entry: this.datasets.entrySet()) { if (dataset == entry.getValue()) { return entry.getKey(); } } return -1; } /** * Maps a dataset to a particular domain axis. All data will be plotted * against axis zero by default, no mapping is required for this case. * * @param index the dataset index (zero-based). * @param axisIndex the axis index. * * @see #mapDatasetToRangeAxis(int, int) */ public void mapDatasetToDomainAxis(int index, int axisIndex) { List axisIndices = new ArrayList<>(1); axisIndices.add(axisIndex); mapDatasetToDomainAxes(index, axisIndices); } /** * Maps the specified dataset to the axes in the list. Note that the * conversion of data values into Java2D space is always performed using * the first axis in the list. * * @param index the dataset index (zero-based). * @param axisIndices the axis indices ({@code null} permitted). */ public void mapDatasetToDomainAxes(int index, List axisIndices) { Args.requireNonNegative(index, "index"); checkAxisIndices(axisIndices); this.datasetToDomainAxesMap.put(index, new ArrayList<>(axisIndices)); // fake a dataset change event to update axes... datasetChanged(new DatasetChangeEvent(this, getDataset(index))); } /** * Maps a dataset to a particular range axis. All data will be plotted * against axis zero by default, no mapping is required for this case. * * @param index the dataset index (zero-based). * @param axisIndex the axis index. * * @see #mapDatasetToDomainAxis(int, int) */ public void mapDatasetToRangeAxis(int index, int axisIndex) { List axisIndices = new ArrayList<>(1); axisIndices.add(axisIndex); mapDatasetToRangeAxes(index, axisIndices); } /** * Maps the specified dataset to the axes in the list. Note that the * conversion of data values into Java2D space is always performed using * the first axis in the list. * * @param index the dataset index (zero-based). * @param axisIndices the axis indices ({@code null} permitted). */ public void mapDatasetToRangeAxes(int index, List axisIndices) { Args.requireNonNegative(index, "index"); checkAxisIndices(axisIndices); this.datasetToRangeAxesMap.put(index, new ArrayList<>(axisIndices)); // fake a dataset change event to update axes... datasetChanged(new DatasetChangeEvent(this, getDataset(index))); } /** * This method is used to perform argument checking on the list of * axis indices passed to mapDatasetToDomainAxes() and * mapDatasetToRangeAxes(). * * @param indices the list of indices ({@code null} permitted). */ private void checkAxisIndices(List indices) { // axisIndices can be: // 1. null; // 2. non-empty, containing only Integer objects that are unique. if (indices == null) { return; // OK } int count = indices.size(); if (count == 0) { throw new IllegalArgumentException("Empty list not permitted."); } Set set = new HashSet<>(); for (Integer item : indices) { if (set.contains(item)) { throw new IllegalArgumentException("Indices must be unique."); } set.add(item); } } /** * Returns the number of renderer slots for this plot. * * @return The number of renderer slots. */ public int getRendererCount() { return this.renderers.size(); } /** * Returns the renderer for the primary dataset. * * @return The item renderer (possibly {@code null}). * * @see #setRenderer(XYItemRenderer) */ public XYItemRenderer getRenderer() { return getRenderer(0); } /** * Returns the renderer with the specified index, or {@code null}. * * @param index the renderer index (must be >= 0). * * @return The renderer (possibly {@code null}). * * @see #setRenderer(int, XYItemRenderer) */ public XYItemRenderer getRenderer(int index) { return this.renderers.get(index); } /** * Returns a map containing the renderers that are assigned to this plot. * The map is unmodifiable. * * @return A map containing the renderers that are assigned to the plot * (never {@code null}). * * @since 1.5.4 */ public Map getRenderers() { return Collections.unmodifiableMap(this.renderers); } /** * Sets the renderer for the primary dataset and sends a change event to * all registered listeners. If the renderer is set to {@code null}, * no data will be displayed. * * @param renderer the renderer ({@code null} permitted). * * @see #getRenderer() */ public void setRenderer(XYItemRenderer renderer) { setRenderer(0, renderer); } /** * Sets the renderer for the dataset with the specified index and sends a * change event to all registered listeners. Note that each dataset should * have its own renderer, you should not use one renderer for multiple * datasets. * * @param index the index (must be >= 0). * @param renderer the renderer. * * @see #getRenderer(int) */ public void setRenderer(int index, XYItemRenderer renderer) { setRenderer(index, renderer, true); } /** * Sets the renderer for the dataset with the specified index and, if * requested, sends a change event to all registered listeners. Note that * each dataset should have its own renderer, you should not use one * renderer for multiple datasets. * * @param index the index (must be >= 0). * @param renderer the renderer. * @param notify notify listeners? * * @see #getRenderer(int) */ public void setRenderer(int index, XYItemRenderer renderer, boolean notify) { XYItemRenderer existing = getRenderer(index); if (existing != null) { existing.removeChangeListener(this); } this.renderers.put(index, renderer); if (renderer != null) { renderer.setPlot(this); renderer.addChangeListener(this); } configureDomainAxes(); configureRangeAxes(); if (notify) { fireChangeEvent(); } } /** * Sets the renderers for this plot and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param renderers the renderers ({@code null} not permitted). */ public void setRenderers(XYItemRenderer[] renderers) { for (int i = 0; i < renderers.length; i++) { setRenderer(i, renderers[i], false); } fireChangeEvent(); } /** * Returns the dataset rendering order. * * @return The order (never {@code null}). * * @see #setDatasetRenderingOrder(DatasetRenderingOrder) */ public DatasetRenderingOrder getDatasetRenderingOrder() { return this.datasetRenderingOrder; } /** * Sets the rendering order and sends a {@link PlotChangeEvent} to all * registered listeners. By default, the plot renders the primary dataset * last (so that the primary dataset overlays the secondary datasets). * You can reverse this if you want to. * * @param order the rendering order ({@code null} not permitted). * * @see #getDatasetRenderingOrder() */ public void setDatasetRenderingOrder(DatasetRenderingOrder order) { Args.nullNotPermitted(order, "order"); this.datasetRenderingOrder = order; fireChangeEvent(); } /** * Returns the series rendering order. * * @return the order (never {@code null}). * * @see #setSeriesRenderingOrder(SeriesRenderingOrder) */ public SeriesRenderingOrder getSeriesRenderingOrder() { return this.seriesRenderingOrder; } /** * Sets the series order and sends a {@link PlotChangeEvent} to all * registered listeners. By default, the plot renders the primary series * last (so that the primary series appears to be on top). * You can reverse this if you want to. * * @param order the rendering order ({@code null} not permitted). * * @see #getSeriesRenderingOrder() */ public void setSeriesRenderingOrder(SeriesRenderingOrder order) { Args.nullNotPermitted(order, "order"); this.seriesRenderingOrder = order; fireChangeEvent(); } /** * Returns the index of the specified renderer, or {@code -1} if the * renderer is not assigned to this plot. * * @param renderer the renderer ({@code null} permitted). * * @return The renderer index. */ public int getIndexOf(XYItemRenderer renderer) { for (Map.Entry entry : this.renderers.entrySet()) { if (entry.getValue() == renderer) { return entry.getKey(); } } return -1; } /** * Returns the renderer for the specified dataset (this is either the * renderer with the same index as the dataset or, if there isn't a * renderer with the same index, the default renderer). If the dataset * does not belong to the plot, this method will return {@code null}. * * @param dataset the dataset ({@code null} permitted). * * @return The renderer (possibly {@code null}). */ public XYItemRenderer getRendererForDataset(XYDataset dataset) { int datasetIndex = indexOf(dataset); if (datasetIndex < 0) { return null; } XYItemRenderer result = this.renderers.get(datasetIndex); if (result == null) { result = getRenderer(); } return result; } /** * Returns the weight for this plot when it is used as a subplot within a * combined plot. * * @return The weight. * * @see #setWeight(int) */ public int getWeight() { return this.weight; } /** * Sets the weight for the plot and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param weight the weight. * * @see #getWeight() */ public void setWeight(int weight) { this.weight = weight; fireChangeEvent(); } /** * Returns {@code true} if the domain gridlines are visible, and * {@code false} otherwise. * * @return {@code true} or {@code false}. * * @see #setDomainGridlinesVisible(boolean) */ public boolean isDomainGridlinesVisible() { return this.domainGridlinesVisible; } /** * Sets the flag that controls whether or not the domain grid-lines are * visible. *

* If the flag value is changed, a {@link PlotChangeEvent} is sent to all * registered listeners. * * @param visible the new value of the flag. * * @see #isDomainGridlinesVisible() */ public void setDomainGridlinesVisible(boolean visible) { if (this.domainGridlinesVisible != visible) { this.domainGridlinesVisible = visible; fireChangeEvent(); } } /** * Returns {@code true} if the domain minor gridlines are visible, and * {@code false} otherwise. * * @return {@code true} or {@code false}. * * @see #setDomainMinorGridlinesVisible(boolean) */ public boolean isDomainMinorGridlinesVisible() { return this.domainMinorGridlinesVisible; } /** * Sets the flag that controls whether or not the domain minor grid-lines * are visible. *

* If the flag value is changed, a {@link PlotChangeEvent} is sent to all * registered listeners. * * @param visible the new value of the flag. * * @see #isDomainMinorGridlinesVisible() */ public void setDomainMinorGridlinesVisible(boolean visible) { if (this.domainMinorGridlinesVisible != visible) { this.domainMinorGridlinesVisible = visible; fireChangeEvent(); } } /** * Returns the stroke for the grid-lines (if any) plotted against the * domain axis. * * @return The stroke (never {@code null}). * * @see #setDomainGridlineStroke(Stroke) */ public Stroke getDomainGridlineStroke() { return this.domainGridlineStroke; } /** * Sets the stroke for the grid lines plotted against the domain axis, and * sends a {@link PlotChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getDomainGridlineStroke() */ public void setDomainGridlineStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.domainGridlineStroke = stroke; fireChangeEvent(); } /** * Returns the stroke for the minor grid-lines (if any) plotted against the * domain axis. * * @return The stroke (never {@code null}). * * @see #setDomainMinorGridlineStroke(Stroke) */ public Stroke getDomainMinorGridlineStroke() { return this.domainMinorGridlineStroke; } /** * Sets the stroke for the minor grid lines plotted against the domain * axis, and sends a {@link PlotChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getDomainMinorGridlineStroke() */ public void setDomainMinorGridlineStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.domainMinorGridlineStroke = stroke; fireChangeEvent(); } /** * Returns the paint for the grid lines (if any) plotted against the domain * axis. * * @return The paint (never {@code null}). * * @see #setDomainGridlinePaint(Paint) */ public Paint getDomainGridlinePaint() { return this.domainGridlinePaint; } /** * Sets the paint for the grid lines plotted against the domain axis, and * sends a {@link PlotChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getDomainGridlinePaint() */ public void setDomainGridlinePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.domainGridlinePaint = paint; fireChangeEvent(); } /** * Returns the paint for the minor grid lines (if any) plotted against the * domain axis. * * @return The paint (never {@code null}). * * @see #setDomainMinorGridlinePaint(Paint) */ public Paint getDomainMinorGridlinePaint() { return this.domainMinorGridlinePaint; } /** * Sets the paint for the minor grid lines plotted against the domain axis, * and sends a {@link PlotChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @throws IllegalArgumentException if {@code Paint} is * {@code null}. * * @see #getDomainMinorGridlinePaint() */ public void setDomainMinorGridlinePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.domainMinorGridlinePaint = paint; fireChangeEvent(); } /** * Returns {@code true} if the range axis grid is visible, and * {@code false} otherwise. * * @return A boolean. * * @see #setRangeGridlinesVisible(boolean) */ public boolean isRangeGridlinesVisible() { return this.rangeGridlinesVisible; } /** * Sets the flag that controls whether or not the range axis grid lines * are visible. *

* If the flag value is changed, a {@link PlotChangeEvent} is sent to all * registered listeners. * * @param visible the new value of the flag. * * @see #isRangeGridlinesVisible() */ public void setRangeGridlinesVisible(boolean visible) { if (this.rangeGridlinesVisible != visible) { this.rangeGridlinesVisible = visible; fireChangeEvent(); } } /** * Returns the stroke for the grid lines (if any) plotted against the * range axis. * * @return The stroke (never {@code null}). * * @see #setRangeGridlineStroke(Stroke) */ public Stroke getRangeGridlineStroke() { return this.rangeGridlineStroke; } /** * Sets the stroke for the grid lines plotted against the range axis, * and sends a {@link PlotChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getRangeGridlineStroke() */ public void setRangeGridlineStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.rangeGridlineStroke = stroke; fireChangeEvent(); } /** * Returns the paint for the grid lines (if any) plotted against the range * axis. * * @return The paint (never {@code null}). * * @see #setRangeGridlinePaint(Paint) */ public Paint getRangeGridlinePaint() { return this.rangeGridlinePaint; } /** * Sets the paint for the grid lines plotted against the range axis and * sends a {@link PlotChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getRangeGridlinePaint() */ public void setRangeGridlinePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.rangeGridlinePaint = paint; fireChangeEvent(); } /** * Returns {@code true} if the range axis minor grid is visible, and * {@code false} otherwise. * * @return A boolean. * * @see #setRangeMinorGridlinesVisible(boolean) */ public boolean isRangeMinorGridlinesVisible() { return this.rangeMinorGridlinesVisible; } /** * Sets the flag that controls whether or not the range axis minor grid * lines are visible. *

* If the flag value is changed, a {@link PlotChangeEvent} is sent to all * registered listeners. * * @param visible the new value of the flag. * * @see #isRangeMinorGridlinesVisible() */ public void setRangeMinorGridlinesVisible(boolean visible) { if (this.rangeMinorGridlinesVisible != visible) { this.rangeMinorGridlinesVisible = visible; fireChangeEvent(); } } /** * Returns the stroke for the minor grid lines (if any) plotted against the * range axis. * * @return The stroke (never {@code null}). * * @see #setRangeMinorGridlineStroke(Stroke) */ public Stroke getRangeMinorGridlineStroke() { return this.rangeMinorGridlineStroke; } /** * Sets the stroke for the minor grid lines plotted against the range axis, * and sends a {@link PlotChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getRangeMinorGridlineStroke() */ public void setRangeMinorGridlineStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.rangeMinorGridlineStroke = stroke; fireChangeEvent(); } /** * Returns the paint for the minor grid lines (if any) plotted against the * range axis. * * @return The paint (never {@code null}). * * @see #setRangeMinorGridlinePaint(Paint) */ public Paint getRangeMinorGridlinePaint() { return this.rangeMinorGridlinePaint; } /** * Sets the paint for the minor grid lines plotted against the range axis * and sends a {@link PlotChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getRangeMinorGridlinePaint() */ public void setRangeMinorGridlinePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.rangeMinorGridlinePaint = paint; fireChangeEvent(); } /** * Returns a flag that controls whether or not a zero baseline is * displayed for the domain axis. * * @return A boolean. * * @see #setDomainZeroBaselineVisible(boolean) */ public boolean isDomainZeroBaselineVisible() { return this.domainZeroBaselineVisible; } /** * Sets the flag that controls whether or not the zero baseline is * displayed for the domain axis, and sends a {@link PlotChangeEvent} to * all registered listeners. * * @param visible the flag. * * @see #isDomainZeroBaselineVisible() */ public void setDomainZeroBaselineVisible(boolean visible) { this.domainZeroBaselineVisible = visible; fireChangeEvent(); } /** * Returns the stroke used for the zero baseline against the domain axis. * * @return The stroke (never {@code null}). * * @see #setDomainZeroBaselineStroke(Stroke) */ public Stroke getDomainZeroBaselineStroke() { return this.domainZeroBaselineStroke; } /** * Sets the stroke for the zero baseline for the domain axis, * and sends a {@link PlotChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getRangeZeroBaselineStroke() */ public void setDomainZeroBaselineStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.domainZeroBaselineStroke = stroke; fireChangeEvent(); } /** * Returns the paint for the zero baseline (if any) plotted against the * domain axis. * * @return The paint (never {@code null}). * * @see #setDomainZeroBaselinePaint(Paint) */ public Paint getDomainZeroBaselinePaint() { return this.domainZeroBaselinePaint; } /** * Sets the paint for the zero baseline plotted against the domain axis and * sends a {@link PlotChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getDomainZeroBaselinePaint() */ public void setDomainZeroBaselinePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.domainZeroBaselinePaint = paint; fireChangeEvent(); } /** * Returns a flag that controls whether or not a zero baseline is * displayed for the range axis. * * @return A boolean. * * @see #setRangeZeroBaselineVisible(boolean) */ public boolean isRangeZeroBaselineVisible() { return this.rangeZeroBaselineVisible; } /** * Sets the flag that controls whether or not the zero baseline is * displayed for the range axis, and sends a {@link PlotChangeEvent} to * all registered listeners. * * @param visible the flag. * * @see #isRangeZeroBaselineVisible() */ public void setRangeZeroBaselineVisible(boolean visible) { this.rangeZeroBaselineVisible = visible; fireChangeEvent(); } /** * Returns the stroke used for the zero baseline against the range axis. * * @return The stroke (never {@code null}). * * @see #setRangeZeroBaselineStroke(Stroke) */ public Stroke getRangeZeroBaselineStroke() { return this.rangeZeroBaselineStroke; } /** * Sets the stroke for the zero baseline for the range axis, * and sends a {@link PlotChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getRangeZeroBaselineStroke() */ public void setRangeZeroBaselineStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.rangeZeroBaselineStroke = stroke; fireChangeEvent(); } /** * Returns the paint for the zero baseline (if any) plotted against the * range axis. * * @return The paint (never {@code null}). * * @see #setRangeZeroBaselinePaint(Paint) */ public Paint getRangeZeroBaselinePaint() { return this.rangeZeroBaselinePaint; } /** * Sets the paint for the zero baseline plotted against the range axis and * sends a {@link PlotChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getRangeZeroBaselinePaint() */ public void setRangeZeroBaselinePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.rangeZeroBaselinePaint = paint; fireChangeEvent(); } /** * Returns the paint used for the domain tick bands. If this is * {@code null}, no tick bands will be drawn. * * @return The paint (possibly {@code null}). * * @see #setDomainTickBandPaint(Paint) */ public Paint getDomainTickBandPaint() { return this.domainTickBandPaint; } /** * Sets the paint for the domain tick bands. * * @param paint the paint ({@code null} permitted). * * @see #getDomainTickBandPaint() */ public void setDomainTickBandPaint(Paint paint) { this.domainTickBandPaint = paint; fireChangeEvent(); } /** * Returns the paint used for the range tick bands. If this is * {@code null}, no tick bands will be drawn. * * @return The paint (possibly {@code null}). * * @see #setRangeTickBandPaint(Paint) */ public Paint getRangeTickBandPaint() { return this.rangeTickBandPaint; } /** * Sets the paint for the range tick bands. * * @param paint the paint ({@code null} permitted). * * @see #getRangeTickBandPaint() */ public void setRangeTickBandPaint(Paint paint) { this.rangeTickBandPaint = paint; fireChangeEvent(); } /** * Returns the origin for the quadrants that can be displayed on the plot. * This defaults to (0, 0). * * @return The origin point (never {@code null}). * * @see #setQuadrantOrigin(Point2D) */ public Point2D getQuadrantOrigin() { return this.quadrantOrigin; } /** * Sets the quadrant origin and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param origin the origin ({@code null} not permitted). * * @see #getQuadrantOrigin() */ public void setQuadrantOrigin(Point2D origin) { Args.nullNotPermitted(origin, "origin"); this.quadrantOrigin = origin; fireChangeEvent(); } /** * Returns the paint used for the specified quadrant. * * @param index the quadrant index (0-3). * * @return The paint (possibly {@code null}). * * @see #setQuadrantPaint(int, Paint) */ public Paint getQuadrantPaint(int index) { if (index < 0 || index > 3) { throw new IllegalArgumentException("The index value (" + index + ") should be in the range 0 to 3."); } return this.quadrantPaint[index]; } /** * Sets the paint used for the specified quadrant and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param index the quadrant index (0-3). * @param paint the paint ({@code null} permitted). * * @see #getQuadrantPaint(int) */ public void setQuadrantPaint(int index, Paint paint) { if (index < 0 || index > 3) { throw new IllegalArgumentException("The index value (" + index + ") should be in the range 0 to 3."); } this.quadrantPaint[index] = paint; fireChangeEvent(); } /** * Adds a marker for the domain axis and sends a {@link PlotChangeEvent} * to all registered listeners. *

* Typically a marker will be drawn by the renderer as a line perpendicular * to the domain axis, however this is entirely up to the renderer. * * @param marker the marker ({@code null} not permitted). * * @see #addDomainMarker(Marker, Layer) * @see #clearDomainMarkers() */ public void addDomainMarker(Marker marker) { // defer argument checking... addDomainMarker(marker, Layer.FOREGROUND); } /** * Adds a marker for the domain axis in the specified layer and sends a * {@link PlotChangeEvent} to all registered listeners. *

* Typically a marker will be drawn by the renderer as a line perpendicular * to the domain axis, however this is entirely up to the renderer. * * @param marker the marker ({@code null} not permitted). * @param layer the layer (foreground or background). * * @see #addDomainMarker(int, Marker, Layer) */ public void addDomainMarker(Marker marker, Layer layer) { addDomainMarker(0, marker, layer); } /** * Clears all the (foreground and background) domain markers and sends a * {@link PlotChangeEvent} to all registered listeners. * * @see #addDomainMarker(int, Marker, Layer) */ public void clearDomainMarkers() { if (this.backgroundDomainMarkers != null) { Set keys = this.backgroundDomainMarkers.keySet(); for (Integer key : keys) { clearDomainMarkers(key); } this.backgroundDomainMarkers.clear(); } if (this.foregroundDomainMarkers != null) { Set keys = this.foregroundDomainMarkers.keySet(); for (Integer key : keys) { clearDomainMarkers(key); } this.foregroundDomainMarkers.clear(); } fireChangeEvent(); } /** * Clears the (foreground and background) domain markers for a particular * renderer and sends a {@link PlotChangeEvent} to all registered listeners. * * @param index the renderer index. * * @see #clearRangeMarkers(int) */ public void clearDomainMarkers(int index) { if (this.backgroundDomainMarkers != null) { List markers = this.backgroundDomainMarkers.get(index); if (markers != null) { for (Marker m : markers) { m.removeChangeListener(this); } markers.clear(); } } if (this.foregroundRangeMarkers != null) { List markers = this.foregroundDomainMarkers.get(index); if (markers != null) { for (Marker m : markers) { m.removeChangeListener(this); } markers.clear(); } } fireChangeEvent(); } /** * Adds a marker for a specific dataset/renderer and sends a * {@link PlotChangeEvent} to all registered listeners. *

* Typically a marker will be drawn by the renderer as a line perpendicular * to the domain axis (that the renderer is mapped to), however this is * entirely up to the renderer. * * @param index the dataset/renderer index. * @param marker the marker. * @param layer the layer (foreground or background). * * @see #clearDomainMarkers(int) * @see #addRangeMarker(int, Marker, Layer) */ public void addDomainMarker(int index, Marker marker, Layer layer) { addDomainMarker(index, marker, layer, true); } /** * Adds a marker for a specific dataset/renderer and, if requested, sends a * {@link PlotChangeEvent} to all registered listeners. *

* Typically a marker will be drawn by the renderer as a line perpendicular * to the domain axis (that the renderer is mapped to), however this is * entirely up to the renderer. * * @param index the dataset/renderer index. * @param marker the marker. * @param layer the layer (foreground or background). * @param notify notify listeners? */ public void addDomainMarker(int index, Marker marker, Layer layer, boolean notify) { Args.nullNotPermitted(marker, "marker"); Args.nullNotPermitted(layer, "layer"); List markers; if (layer == Layer.FOREGROUND) { markers = this.foregroundDomainMarkers.computeIfAbsent(index, k -> new ArrayList<>()); markers.add(marker); } else if (layer == Layer.BACKGROUND) { markers = this.backgroundDomainMarkers.computeIfAbsent(index, k -> new ArrayList<>()); markers.add(marker); } marker.addChangeListener(this); if (notify) { fireChangeEvent(); } } /** * Removes a marker for the domain axis and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param marker the marker. * * @return A boolean indicating whether or not the marker was actually * removed. */ public boolean removeDomainMarker(Marker marker) { return removeDomainMarker(marker, Layer.FOREGROUND); } /** * Removes a marker for the domain axis in the specified layer and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param marker the marker ({@code null} not permitted). * @param layer the layer (foreground or background). * * @return A boolean indicating whether or not the marker was actually * removed. */ public boolean removeDomainMarker(Marker marker, Layer layer) { return removeDomainMarker(0, marker, layer); } /** * Removes a marker for a specific dataset/renderer and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param index the dataset/renderer index. * @param marker the marker. * @param layer the layer (foreground or background). * * @return A boolean indicating whether or not the marker was actually * removed. */ public boolean removeDomainMarker(int index, Marker marker, Layer layer) { return removeDomainMarker(index, marker, layer, true); } /** * Removes a marker for a specific dataset/renderer and, if requested, * sends a {@link PlotChangeEvent} to all registered listeners. * * @param index the dataset/renderer index. * @param marker the marker. * @param layer the layer (foreground or background). * @param notify notify listeners? * * @return A boolean indicating whether or not the marker was actually * removed. */ public boolean removeDomainMarker(int index, Marker marker, Layer layer, boolean notify) { List markers; if (layer == Layer.FOREGROUND) { markers = this.foregroundDomainMarkers.get(index); } else { markers = this.backgroundDomainMarkers.get(index); } if (markers == null) { return false; } boolean removed = markers.remove(marker); if (removed && notify) { fireChangeEvent(); } return removed; } /** * Adds a marker for the range axis and sends a {@link PlotChangeEvent} to * all registered listeners. *

* Typically a marker will be drawn by the renderer as a line perpendicular * to the range axis, however this is entirely up to the renderer. * * @param marker the marker ({@code null} not permitted). * * @see #addRangeMarker(Marker, Layer) */ public void addRangeMarker(Marker marker) { addRangeMarker(marker, Layer.FOREGROUND); } /** * Adds a marker for the range axis in the specified layer and sends a * {@link PlotChangeEvent} to all registered listeners. *

* Typically a marker will be drawn by the renderer as a line perpendicular * to the range axis, however this is entirely up to the renderer. * * @param marker the marker ({@code null} not permitted). * @param layer the layer (foreground or background). * * @see #addRangeMarker(int, Marker, Layer) */ public void addRangeMarker(Marker marker, Layer layer) { addRangeMarker(0, marker, layer); } /** * Clears all the range markers and sends a {@link PlotChangeEvent} to all * registered listeners. * * @see #clearRangeMarkers() */ public void clearRangeMarkers() { if (this.backgroundRangeMarkers != null) { Set keys = this.backgroundRangeMarkers.keySet(); for (Integer key : keys) { clearRangeMarkers(key); } this.backgroundRangeMarkers.clear(); } if (this.foregroundRangeMarkers != null) { Set keys = this.foregroundRangeMarkers.keySet(); for (Integer key : keys) { clearRangeMarkers(key); } this.foregroundRangeMarkers.clear(); } fireChangeEvent(); } /** * Adds a marker for a specific dataset/renderer and sends a * {@link PlotChangeEvent} to all registered listeners. *

* Typically a marker will be drawn by the renderer as a line perpendicular * to the range axis, however this is entirely up to the renderer. * * @param index the dataset/renderer index. * @param marker the marker. * @param layer the layer (foreground or background). * * @see #clearRangeMarkers(int) * @see #addDomainMarker(int, Marker, Layer) */ public void addRangeMarker(int index, Marker marker, Layer layer) { addRangeMarker(index, marker, layer, true); } /** * Adds a marker for a specific dataset/renderer and, if requested, sends a * {@link PlotChangeEvent} to all registered listeners. *

* Typically a marker will be drawn by the renderer as a line perpendicular * to the range axis, however this is entirely up to the renderer. * * @param index the dataset/renderer index. * @param marker the marker. * @param layer the layer (foreground or background). * @param notify notify listeners? */ public void addRangeMarker(int index, Marker marker, Layer layer, boolean notify) { List markers; if (layer == Layer.FOREGROUND) { markers = this.foregroundRangeMarkers.computeIfAbsent(index, k -> new ArrayList<>()); markers.add(marker); } else if (layer == Layer.BACKGROUND) { markers = this.backgroundRangeMarkers.computeIfAbsent(index, k -> new ArrayList<>()); markers.add(marker); } marker.addChangeListener(this); if (notify) { fireChangeEvent(); } } /** * Clears the (foreground and background) range markers for a particular * renderer. * * @param index the renderer index. */ public void clearRangeMarkers(int index) { if (this.backgroundRangeMarkers != null) { List markers = this.backgroundRangeMarkers.get(index); if (markers != null) { for (Marker m : markers) { m.removeChangeListener(this); } markers.clear(); } } if (this.foregroundRangeMarkers != null) { List markers = this.foregroundRangeMarkers.get(index); if (markers != null) { for (Marker m : markers) { m.removeChangeListener(this); } markers.clear(); } } fireChangeEvent(); } /** * Removes a marker for the range axis and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param marker the marker. * * @return A boolean indicating whether or not the marker was actually * removed. */ public boolean removeRangeMarker(Marker marker) { return removeRangeMarker(marker, Layer.FOREGROUND); } /** * Removes a marker for the range axis in the specified layer and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param marker the marker ({@code null} not permitted). * @param layer the layer (foreground or background). * * @return A boolean indicating whether or not the marker was actually * removed. */ public boolean removeRangeMarker(Marker marker, Layer layer) { return removeRangeMarker(0, marker, layer); } /** * Removes a marker for a specific dataset/renderer and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param index the dataset/renderer index. * @param marker the marker ({@code null} not permitted). * @param layer the layer (foreground or background). * * @return A boolean indicating whether or not the marker was actually * removed. */ public boolean removeRangeMarker(int index, Marker marker, Layer layer) { return removeRangeMarker(index, marker, layer, true); } /** * Removes a marker for a specific dataset/renderer and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param index the dataset/renderer index. * @param marker the marker ({@code null} not permitted). * @param layer the layer (foreground or background) ({@code null} not permitted). * @param notify notify listeners? * * @return A boolean indicating whether or not the marker was actually * removed. */ public boolean removeRangeMarker(int index, Marker marker, Layer layer, boolean notify) { Args.nullNotPermitted(marker, "marker"); Args.nullNotPermitted(layer, "layer"); List markers; if (layer == Layer.FOREGROUND) { markers = this.foregroundRangeMarkers.get(index); } else { markers = this.backgroundRangeMarkers.get(index); } if (markers == null) { return false; } boolean removed = markers.remove(marker); if (removed && notify) { fireChangeEvent(); } return removed; } /** * Adds an annotation to the plot and sends a {@link PlotChangeEvent} to * all registered listeners. * * @param annotation the annotation ({@code null} not permitted). * * @see #getAnnotations() * @see #removeAnnotation(XYAnnotation) */ public void addAnnotation(XYAnnotation annotation) { addAnnotation(annotation, true); } /** * Adds an annotation to the plot and, if requested, sends a * {@link PlotChangeEvent} to all registered listeners. * * @param annotation the annotation ({@code null} not permitted). * @param notify notify listeners? */ public void addAnnotation(XYAnnotation annotation, boolean notify) { Args.nullNotPermitted(annotation, "annotation"); this.annotations.add(annotation); annotation.addChangeListener(this); if (notify) { fireChangeEvent(); } } /** * Removes an annotation from the plot and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param annotation the annotation ({@code null} not permitted). * * @return A boolean (indicates whether or not the annotation was removed). * * @see #addAnnotation(XYAnnotation) * @see #getAnnotations() */ public boolean removeAnnotation(XYAnnotation annotation) { return removeAnnotation(annotation, true); } /** * Removes an annotation from the plot and sends a {@link PlotChangeEvent} * to all registered listeners. * * @param annotation the annotation ({@code null} not permitted). * @param notify notify listeners? * * @return A boolean (indicates whether or not the annotation was removed). */ public boolean removeAnnotation(XYAnnotation annotation, boolean notify) { Args.nullNotPermitted(annotation, "annotation"); boolean removed = this.annotations.remove(annotation); annotation.removeChangeListener(this); if (removed && notify) { fireChangeEvent(); } return removed; } /** * Returns the list of annotations. * * @return The list of annotations. * * @see #addAnnotation(XYAnnotation) */ public List getAnnotations() { return new ArrayList<>(this.annotations); } /** * Clears all the annotations and sends a {@link PlotChangeEvent} to all * registered listeners. * * @see #addAnnotation(XYAnnotation) */ public void clearAnnotations() { for (XYAnnotation annotation : this.annotations) { annotation.removeChangeListener(this); } this.annotations.clear(); fireChangeEvent(); } /** * Returns the shadow generator for the plot, if any. * * @return The shadow generator (possibly {@code null}). */ public ShadowGenerator getShadowGenerator() { return this.shadowGenerator; } /** * Sets the shadow generator for the plot and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param generator the generator ({@code null} permitted). */ public void setShadowGenerator(ShadowGenerator generator) { this.shadowGenerator = generator; fireChangeEvent(); } /** * Calculates the space required for all the axes in the plot. * * @param g2 the graphics device. * @param plotArea the plot area. * * @return The required space. */ protected AxisSpace calculateAxisSpace(Graphics2D g2, Rectangle2D plotArea) { AxisSpace space = new AxisSpace(); space = calculateRangeAxisSpace(g2, plotArea, space); Rectangle2D revPlotArea = space.shrink(plotArea, null); space = calculateDomainAxisSpace(g2, revPlotArea, space); return space; } /** * Calculates the space required for the domain axis/axes. * * @param g2 the graphics device. * @param plotArea the plot area. * @param space a carrier for the result ({@code null} permitted). * * @return The required space. */ protected AxisSpace calculateDomainAxisSpace(Graphics2D g2, Rectangle2D plotArea, AxisSpace space) { if (space == null) { space = new AxisSpace(); } // reserve some space for the domain axis... if (this.fixedDomainAxisSpace != null) { if (this.orientation == PlotOrientation.HORIZONTAL) { space.ensureAtLeast(this.fixedDomainAxisSpace.getLeft(), RectangleEdge.LEFT); space.ensureAtLeast(this.fixedDomainAxisSpace.getRight(), RectangleEdge.RIGHT); } else if (this.orientation == PlotOrientation.VERTICAL) { space.ensureAtLeast(this.fixedDomainAxisSpace.getTop(), RectangleEdge.TOP); space.ensureAtLeast(this.fixedDomainAxisSpace.getBottom(), RectangleEdge.BOTTOM); } } else { // reserve space for the domain axes... for (ValueAxis axis: this.domainAxes.values()) { if (axis != null) { RectangleEdge edge = getDomainAxisEdge( findDomainAxisIndex(axis)); space = axis.reserveSpace(g2, this, plotArea, edge, space); } } } return space; } /** * Calculates the space required for the range axis/axes. * * @param g2 the graphics device. * @param plotArea the plot area. * @param space a carrier for the result ({@code null} permitted). * * @return The required space. */ protected AxisSpace calculateRangeAxisSpace(Graphics2D g2, Rectangle2D plotArea, AxisSpace space) { if (space == null) { space = new AxisSpace(); } // reserve some space for the range axis... if (this.fixedRangeAxisSpace != null) { if (this.orientation == PlotOrientation.HORIZONTAL) { space.ensureAtLeast(this.fixedRangeAxisSpace.getTop(), RectangleEdge.TOP); space.ensureAtLeast(this.fixedRangeAxisSpace.getBottom(), RectangleEdge.BOTTOM); } else if (this.orientation == PlotOrientation.VERTICAL) { space.ensureAtLeast(this.fixedRangeAxisSpace.getLeft(), RectangleEdge.LEFT); space.ensureAtLeast(this.fixedRangeAxisSpace.getRight(), RectangleEdge.RIGHT); } } else { // reserve space for the range axes... for (ValueAxis axis: this.rangeAxes.values()) { if (axis != null) { RectangleEdge edge = getRangeAxisEdge( findRangeAxisIndex(axis)); space = axis.reserveSpace(g2, this, plotArea, edge, space); } } } return space; } /** * Trims a rectangle to integer coordinates. * * @param rect the incoming rectangle. * * @return A rectangle with integer coordinates. */ private Rectangle integerise(Rectangle2D rect) { int x0 = (int) Math.ceil(rect.getMinX()); int y0 = (int) Math.ceil(rect.getMinY()); int x1 = (int) Math.floor(rect.getMaxX()); int y1 = (int) Math.floor(rect.getMaxY()); return new Rectangle(x0, y0, (x1 - x0), (y1 - y0)); } /** * Draws the plot within the specified area on a graphics device. * * @param g2 the graphics device. * @param area the plot area (in Java2D space). * @param anchor an anchor point in Java2D space ({@code null} * permitted). * @param parentState the state from the parent plot, if there is one * ({@code null} permitted). * @param info collects chart drawing information ({@code null} * permitted). */ @Override public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, PlotState parentState, PlotRenderingInfo info) { // if the plot area is too small, just return... boolean b1 = (area.getWidth() <= MINIMUM_WIDTH_TO_DRAW); boolean b2 = (area.getHeight() <= MINIMUM_HEIGHT_TO_DRAW); if (b1 || b2) { return; } // record the plot area... if (info != null) { info.setPlotArea(area); } // adjust the drawing area for the plot insets (if any)... RectangleInsets insets = getInsets(); insets.trim(area); AxisSpace space = calculateAxisSpace(g2, area); Rectangle2D dataArea = space.shrink(area, null); this.axisOffset.trim(dataArea); dataArea = integerise(dataArea); if (dataArea.isEmpty()) { return; } createAndAddEntity((Rectangle2D) dataArea.clone(), info, null, null); if (info != null) { info.setDataArea(dataArea); } // draw the plot background and axes... drawBackground(g2, dataArea); Map axisStateMap = drawAxes(g2, area, dataArea, info); PlotOrientation orient = getOrientation(); // the anchor point is typically the point where the mouse last // clicked - the crosshairs will be driven off this point... if (anchor != null && !dataArea.contains(anchor)) { anchor = null; } CrosshairState crosshairState = new CrosshairState(); crosshairState.setCrosshairDistance(Double.POSITIVE_INFINITY); crosshairState.setAnchor(anchor); crosshairState.setAnchorX(Double.NaN); crosshairState.setAnchorY(Double.NaN); if (anchor != null) { ValueAxis domainAxis = getDomainAxis(); if (domainAxis != null) { double x; if (orient == PlotOrientation.VERTICAL) { x = domainAxis.java2DToValue(anchor.getX(), dataArea, getDomainAxisEdge()); } else { x = domainAxis.java2DToValue(anchor.getY(), dataArea, getDomainAxisEdge()); } crosshairState.setAnchorX(x); } ValueAxis rangeAxis = getRangeAxis(); if (rangeAxis != null) { double y; if (orient == PlotOrientation.VERTICAL) { y = rangeAxis.java2DToValue(anchor.getY(), dataArea, getRangeAxisEdge()); } else { y = rangeAxis.java2DToValue(anchor.getX(), dataArea, getRangeAxisEdge()); } crosshairState.setAnchorY(y); } } crosshairState.setCrosshairX(getDomainCrosshairValue()); crosshairState.setCrosshairY(getRangeCrosshairValue()); Shape originalClip = g2.getClip(); Composite originalComposite = g2.getComposite(); g2.clip(dataArea); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, getForegroundAlpha())); AxisState domainAxisState = axisStateMap.get(getDomainAxis()); if (domainAxisState == null) { if (parentState != null) { domainAxisState = (AxisState) parentState.getSharedAxisStates() .get(getDomainAxis()); } } AxisState rangeAxisState = axisStateMap.get(getRangeAxis()); if (rangeAxisState == null) { if (parentState != null) { rangeAxisState = (AxisState) parentState.getSharedAxisStates() .get(getRangeAxis()); } } if (domainAxisState != null) { drawDomainTickBands(g2, dataArea, domainAxisState.getTicks()); } if (rangeAxisState != null) { drawRangeTickBands(g2, dataArea, rangeAxisState.getTicks()); } if (domainAxisState != null) { drawDomainGridlines(g2, dataArea, domainAxisState.getTicks()); drawZeroDomainBaseline(g2, dataArea); } if (rangeAxisState != null) { drawRangeGridlines(g2, dataArea, rangeAxisState.getTicks()); drawZeroRangeBaseline(g2, dataArea); } Graphics2D savedG2 = g2; BufferedImage dataImage = null; boolean suppressShadow = Boolean.TRUE.equals(g2.getRenderingHint( JFreeChart.KEY_SUPPRESS_SHADOW_GENERATION)); if (this.shadowGenerator != null && !suppressShadow) { dataImage = new BufferedImage((int) dataArea.getWidth(), (int)dataArea.getHeight(), BufferedImage.TYPE_INT_ARGB); g2 = dataImage.createGraphics(); g2.translate(-dataArea.getX(), -dataArea.getY()); g2.setRenderingHints(savedG2.getRenderingHints()); } // draw the markers that are associated with a specific dataset... for (XYDataset dataset: this.datasets.values()) { int datasetIndex = indexOf(dataset); drawDomainMarkers(g2, dataArea, datasetIndex, Layer.BACKGROUND); } for (XYDataset dataset: this.datasets.values()) { int datasetIndex = indexOf(dataset); drawRangeMarkers(g2, dataArea, datasetIndex, Layer.BACKGROUND); } // now draw annotations and render data items... boolean foundData = false; DatasetRenderingOrder order = getDatasetRenderingOrder(); List rendererIndices = getRendererIndices(order); List datasetIndices = getDatasetIndices(order); // draw background annotations for (int i : rendererIndices) { XYItemRenderer renderer = getRenderer(i); if (renderer != null) { ValueAxis domainAxis = getDomainAxisForDataset(i); ValueAxis rangeAxis = getRangeAxisForDataset(i); renderer.drawAnnotations(g2, dataArea, domainAxis, rangeAxis, Layer.BACKGROUND, info); } } // render data items... for (int datasetIndex : datasetIndices) { XYDataset dataset = this.getDataset(datasetIndex); foundData = render(g2, dataArea, datasetIndex, info, crosshairState) || foundData; } // draw foreground annotations for (int i : rendererIndices) { XYItemRenderer renderer = getRenderer(i); if (renderer != null) { ValueAxis domainAxis = getDomainAxisForDataset(i); ValueAxis rangeAxis = getRangeAxisForDataset(i); renderer.drawAnnotations(g2, dataArea, domainAxis, rangeAxis, Layer.FOREGROUND, info); } } // draw domain crosshair if required... int datasetIndex = crosshairState.getDatasetIndex(); ValueAxis xAxis = getDomainAxisForDataset(datasetIndex); RectangleEdge xAxisEdge = getDomainAxisEdge(getDomainAxisIndex(xAxis)); if (!this.domainCrosshairLockedOnData && anchor != null) { double xx; if (orient == PlotOrientation.VERTICAL) { xx = xAxis.java2DToValue(anchor.getX(), dataArea, xAxisEdge); } else { xx = xAxis.java2DToValue(anchor.getY(), dataArea, xAxisEdge); } crosshairState.setCrosshairX(xx); } setDomainCrosshairValue(crosshairState.getCrosshairX(), false); if (isDomainCrosshairVisible()) { double x = getDomainCrosshairValue(); Paint paint = getDomainCrosshairPaint(); Stroke stroke = getDomainCrosshairStroke(); drawDomainCrosshair(g2, dataArea, orient, x, xAxis, stroke, paint); } // draw range crosshair if required... ValueAxis yAxis = getRangeAxisForDataset(datasetIndex); RectangleEdge yAxisEdge = getRangeAxisEdge(getRangeAxisIndex(yAxis)); if (!this.rangeCrosshairLockedOnData && anchor != null) { double yy; if (orient == PlotOrientation.VERTICAL) { yy = yAxis.java2DToValue(anchor.getY(), dataArea, yAxisEdge); } else { yy = yAxis.java2DToValue(anchor.getX(), dataArea, yAxisEdge); } crosshairState.setCrosshairY(yy); } setRangeCrosshairValue(crosshairState.getCrosshairY(), false); if (isRangeCrosshairVisible()) { double y = getRangeCrosshairValue(); Paint paint = getRangeCrosshairPaint(); Stroke stroke = getRangeCrosshairStroke(); drawRangeCrosshair(g2, dataArea, orient, y, yAxis, stroke, paint); } if (!foundData) { drawNoDataMessage(g2, dataArea); } for (int i : rendererIndices) { drawDomainMarkers(g2, dataArea, i, Layer.FOREGROUND); } for (int i : rendererIndices) { drawRangeMarkers(g2, dataArea, i, Layer.FOREGROUND); } drawAnnotations(g2, dataArea, info); if (this.shadowGenerator != null && !suppressShadow) { BufferedImage shadowImage = this.shadowGenerator.createDropShadow(dataImage); g2 = savedG2; g2.drawImage(shadowImage, (int) dataArea.getX() + this.shadowGenerator.calculateOffsetX(), (int) dataArea.getY() + this.shadowGenerator.calculateOffsetY(), null); g2.drawImage(dataImage, (int) dataArea.getX(), (int) dataArea.getY(), null); } g2.setClip(originalClip); g2.setComposite(originalComposite); drawOutline(g2, dataArea); } /** * Returns the indices of the non-null datasets in the specified order. * * @param order the order ({@code null} not permitted). * * @return The list of indices. */ private List getDatasetIndices(DatasetRenderingOrder order) { List result = new ArrayList<>(); for (Entry entry : this.datasets.entrySet()) { if (entry.getValue() != null) { result.add(entry.getKey()); } } Collections.sort(result); if (order == DatasetRenderingOrder.REVERSE) { Collections.reverse(result); } return result; } private List getRendererIndices(DatasetRenderingOrder order) { List result = new ArrayList<>(); for (Entry entry : this.renderers.entrySet()) { if (entry.getValue() != null) { result.add(entry.getKey()); } } Collections.sort(result); if (order == DatasetRenderingOrder.REVERSE) { Collections.reverse(result); } return result; } /** * Draws the background for the plot. * * @param g2 the graphics device. * @param area the area. */ @Override public void drawBackground(Graphics2D g2, Rectangle2D area) { fillBackground(g2, area, this.orientation); drawQuadrants(g2, area); drawBackgroundImage(g2, area); } /** * Draws the quadrants. * * @param g2 the graphics device. * @param area the area. * * @see #setQuadrantOrigin(Point2D) * @see #setQuadrantPaint(int, Paint) */ protected void drawQuadrants(Graphics2D g2, Rectangle2D area) { // 0 | 1 // --+-- // 2 | 3 boolean somethingToDraw = false; ValueAxis xAxis = getDomainAxis(); if (xAxis == null) { // we can't draw quadrants without a valid x-axis return; } double x = xAxis.getRange().constrain(this.quadrantOrigin.getX()); double xx = xAxis.valueToJava2D(x, area, getDomainAxisEdge()); ValueAxis yAxis = getRangeAxis(); if (yAxis == null) { // we can't draw quadrants without a valid y-axis return; } double y = yAxis.getRange().constrain(this.quadrantOrigin.getY()); double yy = yAxis.valueToJava2D(y, area, getRangeAxisEdge()); double xmin = xAxis.getLowerBound(); double xxmin = xAxis.valueToJava2D(xmin, area, getDomainAxisEdge()); double xmax = xAxis.getUpperBound(); double xxmax = xAxis.valueToJava2D(xmax, area, getDomainAxisEdge()); double ymin = yAxis.getLowerBound(); double yymin = yAxis.valueToJava2D(ymin, area, getRangeAxisEdge()); double ymax = yAxis.getUpperBound(); double yymax = yAxis.valueToJava2D(ymax, area, getRangeAxisEdge()); Rectangle2D[] r = new Rectangle2D[] {null, null, null, null}; if (this.quadrantPaint[0] != null) { if (x > xmin && y < ymax) { if (this.orientation == PlotOrientation.HORIZONTAL) { r[0] = new Rectangle2D.Double(Math.min(yymax, yy), Math.min(xxmin, xx), Math.abs(yy - yymax), Math.abs(xx - xxmin)); } else { // PlotOrientation.VERTICAL r[0] = new Rectangle2D.Double(Math.min(xxmin, xx), Math.min(yymax, yy), Math.abs(xx - xxmin), Math.abs(yy - yymax)); } somethingToDraw = true; } } if (this.quadrantPaint[1] != null) { if (x < xmax && y < ymax) { if (this.orientation == PlotOrientation.HORIZONTAL) { r[1] = new Rectangle2D.Double(Math.min(yymax, yy), Math.min(xxmax, xx), Math.abs(yy - yymax), Math.abs(xx - xxmax)); } else { // PlotOrientation.VERTICAL r[1] = new Rectangle2D.Double(Math.min(xx, xxmax), Math.min(yymax, yy), Math.abs(xx - xxmax), Math.abs(yy - yymax)); } somethingToDraw = true; } } if (this.quadrantPaint[2] != null) { if (x > xmin && y > ymin) { if (this.orientation == PlotOrientation.HORIZONTAL) { r[2] = new Rectangle2D.Double(Math.min(yymin, yy), Math.min(xxmin, xx), Math.abs(yy - yymin), Math.abs(xx - xxmin)); } else { // PlotOrientation.VERTICAL r[2] = new Rectangle2D.Double(Math.min(xxmin, xx), Math.min(yymin, yy), Math.abs(xx - xxmin), Math.abs(yy - yymin)); } somethingToDraw = true; } } if (this.quadrantPaint[3] != null) { if (x < xmax && y > ymin) { if (this.orientation == PlotOrientation.HORIZONTAL) { r[3] = new Rectangle2D.Double(Math.min(yymin, yy), Math.min(xxmax, xx), Math.abs(yy - yymin), Math.abs(xx - xxmax)); } else { // PlotOrientation.VERTICAL r[3] = new Rectangle2D.Double(Math.min(xx, xxmax), Math.min(yymin, yy), Math.abs(xx - xxmax), Math.abs(yy - yymin)); } somethingToDraw = true; } } if (somethingToDraw) { Composite originalComposite = g2.getComposite(); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, getBackgroundAlpha())); for (int i = 0; i < 4; i++) { if (this.quadrantPaint[i] != null && r[i] != null) { g2.setPaint(this.quadrantPaint[i]); g2.fill(r[i]); } } g2.setComposite(originalComposite); } } /** * Draws the domain tick bands, if any. * * @param g2 the graphics device. * @param dataArea the data area. * @param ticks the ticks. * * @see #setDomainTickBandPaint(Paint) */ public void drawDomainTickBands(Graphics2D g2, Rectangle2D dataArea, List ticks) { Paint bandPaint = getDomainTickBandPaint(); if (bandPaint != null) { boolean fillBand = false; ValueAxis xAxis = getDomainAxis(); double previous = xAxis.getLowerBound(); for (ValueTick tick : ticks) { double current = tick.getValue(); if (fillBand) { getRenderer().fillDomainGridBand(g2, this, xAxis, dataArea, previous, current); } previous = current; fillBand = !fillBand; } double end = xAxis.getUpperBound(); if (fillBand) { getRenderer().fillDomainGridBand(g2, this, xAxis, dataArea, previous, end); } } } /** * Draws the range tick bands, if any. * * @param g2 the graphics device. * @param dataArea the data area. * @param ticks the ticks. * * @see #setRangeTickBandPaint(Paint) */ public void drawRangeTickBands(Graphics2D g2, Rectangle2D dataArea, List ticks) { Paint bandPaint = getRangeTickBandPaint(); if (bandPaint != null) { boolean fillBand = false; ValueAxis axis = getRangeAxis(); double previous = axis.getLowerBound(); for (ValueTick tick : ticks) { double current = tick.getValue(); if (fillBand) { getRenderer().fillRangeGridBand(g2, this, axis, dataArea, previous, current); } previous = current; fillBand = !fillBand; } double end = axis.getUpperBound(); if (fillBand) { getRenderer().fillRangeGridBand(g2, this, axis, dataArea, previous, end); } } } /** * A utility method for drawing the axes. * * @param g2 the graphics device ({@code null} not permitted). * @param plotArea the plot area ({@code null} not permitted). * @param dataArea the data area ({@code null} not permitted). * @param plotState collects information about the plot ({@code null} * permitted). * * @return A map containing the state for each axis drawn. */ protected Map drawAxes(Graphics2D g2, Rectangle2D plotArea, Rectangle2D dataArea, PlotRenderingInfo plotState) { AxisCollection axisCollection = new AxisCollection(); // add domain axes to lists... for (ValueAxis axis : this.domainAxes.values()) { if (axis != null) { int axisIndex = findDomainAxisIndex(axis); axisCollection.add(axis, getDomainAxisEdge(axisIndex)); } } // add range axes to lists... for (ValueAxis axis : this.rangeAxes.values()) { if (axis != null) { int axisIndex = findRangeAxisIndex(axis); axisCollection.add(axis, getRangeAxisEdge(axisIndex)); } } Map axisStateMap = new HashMap<>(); // draw the top axes double cursor = dataArea.getMinY() - this.axisOffset.calculateTopOutset( dataArea.getHeight()); Iterator iterator = axisCollection.getAxesAtTop().iterator(); while (iterator.hasNext()) { ValueAxis axis = (ValueAxis) iterator.next(); AxisState info = axis.draw(g2, cursor, plotArea, dataArea, RectangleEdge.TOP, plotState); cursor = info.getCursor(); axisStateMap.put(axis, info); } // draw the bottom axes cursor = dataArea.getMaxY() + this.axisOffset.calculateBottomOutset(dataArea.getHeight()); iterator = axisCollection.getAxesAtBottom().iterator(); while (iterator.hasNext()) { ValueAxis axis = (ValueAxis) iterator.next(); AxisState info = axis.draw(g2, cursor, plotArea, dataArea, RectangleEdge.BOTTOM, plotState); cursor = info.getCursor(); axisStateMap.put(axis, info); } // draw the left axes cursor = dataArea.getMinX() - this.axisOffset.calculateLeftOutset(dataArea.getWidth()); iterator = axisCollection.getAxesAtLeft().iterator(); while (iterator.hasNext()) { ValueAxis axis = (ValueAxis) iterator.next(); AxisState info = axis.draw(g2, cursor, plotArea, dataArea, RectangleEdge.LEFT, plotState); cursor = info.getCursor(); axisStateMap.put(axis, info); } // draw the right axes cursor = dataArea.getMaxX() + this.axisOffset.calculateRightOutset(dataArea.getWidth()); iterator = axisCollection.getAxesAtRight().iterator(); while (iterator.hasNext()) { ValueAxis axis = (ValueAxis) iterator.next(); AxisState info = axis.draw(g2, cursor, plotArea, dataArea, RectangleEdge.RIGHT, plotState); cursor = info.getCursor(); axisStateMap.put(axis, info); } return axisStateMap; } /** * Draws a representation of the data within the dataArea region, using the * current renderer. *

* The {@code info} and {@code crosshairState} arguments may be * {@code null}. * * @param g2 the graphics device. * @param dataArea the region in which the data is to be drawn. * @param index the dataset index. * @param info an optional object for collection dimension information. * @param crosshairState collects crosshair information * ({@code null} permitted). * * @return A flag that indicates whether any data was actually rendered. */ public boolean render(Graphics2D g2, Rectangle2D dataArea, int index, PlotRenderingInfo info, CrosshairState crosshairState) { boolean foundData = false; XYDataset dataset = getDataset(index); if (!DatasetUtils.isEmptyOrNull(dataset)) { foundData = true; ValueAxis xAxis = getDomainAxisForDataset(index); ValueAxis yAxis = getRangeAxisForDataset(index); if (xAxis == null || yAxis == null) { return foundData; // can't render anything without axes } XYItemRenderer renderer = getRenderer(index); if (renderer == null) { renderer = getRenderer(); if (renderer == null) { // no default renderer available return foundData; } } XYItemRendererState state = renderer.initialise(g2, dataArea, this, dataset, info); int passCount = renderer.getPassCount(); SeriesRenderingOrder seriesOrder = getSeriesRenderingOrder(); if (seriesOrder == SeriesRenderingOrder.REVERSE) { //render series in reverse order for (int pass = 0; pass < passCount; pass++) { int seriesCount = dataset.getSeriesCount(); for (int series = seriesCount - 1; series >= 0; series--) { int firstItem = 0; int lastItem = dataset.getItemCount(series) - 1; if (lastItem == -1) { continue; } if (state.getProcessVisibleItemsOnly()) { int[] itemBounds = RendererUtils.findLiveItems( dataset, series, xAxis.getLowerBound(), xAxis.getUpperBound()); firstItem = Math.max(itemBounds[0] - 1, 0); lastItem = Math.min(itemBounds[1] + 1, lastItem); } state.startSeriesPass(dataset, series, firstItem, lastItem, pass, passCount); for (int item = firstItem; item <= lastItem; item++) { renderer.drawItem(g2, state, dataArea, info, this, xAxis, yAxis, dataset, series, item, crosshairState, pass); } state.endSeriesPass(dataset, series, firstItem, lastItem, pass, passCount); } } } else { //render series in forward order for (int pass = 0; pass < passCount; pass++) { int seriesCount = dataset.getSeriesCount(); for (int series = 0; series < seriesCount; series++) { int firstItem = 0; int lastItem = dataset.getItemCount(series) - 1; if (state.getProcessVisibleItemsOnly()) { int[] itemBounds = RendererUtils.findLiveItems( dataset, series, xAxis.getLowerBound(), xAxis.getUpperBound()); firstItem = Math.max(itemBounds[0] - 1, 0); lastItem = Math.min(itemBounds[1] + 1, lastItem); } state.startSeriesPass(dataset, series, firstItem, lastItem, pass, passCount); for (int item = firstItem; item <= lastItem; item++) { renderer.drawItem(g2, state, dataArea, info, this, xAxis, yAxis, dataset, series, item, crosshairState, pass); } state.endSeriesPass(dataset, series, firstItem, lastItem, pass, passCount); } } } } return foundData; } /** * Returns the domain axis for a dataset. * * @param index the dataset index (must be >= 0). * * @return The axis. */ public ValueAxis getDomainAxisForDataset(int index) { Args.requireNonNegative(index, "index"); ValueAxis valueAxis; List axisIndices = this.datasetToDomainAxesMap.get(index); if (axisIndices != null) { // the first axis in the list is used for data <--> Java2D Integer axisIndex = axisIndices.get(0); valueAxis = getDomainAxis(axisIndex); } else { valueAxis = getDomainAxis(0); } return valueAxis; } /** * Returns the range axis for a dataset. * * @param index the dataset index (must be >= 0). * * @return The axis. */ public ValueAxis getRangeAxisForDataset(int index) { Args.requireNonNegative(index, "index"); ValueAxis valueAxis; List axisIndices = this.datasetToRangeAxesMap.get(index); if (axisIndices != null) { // the first axis in the list is used for data <--> Java2D Integer axisIndex = axisIndices.get(0); valueAxis = getRangeAxis(axisIndex); } else { valueAxis = getRangeAxis(0); } return valueAxis; } /** * Draws the gridlines for the plot, if they are visible. * * @param g2 the graphics device. * @param dataArea the data area. * @param ticks the ticks. * * @see #drawRangeGridlines(Graphics2D, Rectangle2D, List) */ protected void drawDomainGridlines(Graphics2D g2, Rectangle2D dataArea, List ticks) { // no renderer, no gridlines... if (getRenderer() == null) { return; } // draw the domain grid lines, if any... if (isDomainGridlinesVisible() || isDomainMinorGridlinesVisible()) { Stroke gridStroke = null; Paint gridPaint = null; Iterator iterator = ticks.iterator(); boolean paintLine; while (iterator.hasNext()) { paintLine = false; ValueTick tick = (ValueTick) iterator.next(); if ((tick.getTickType() == TickType.MINOR) && isDomainMinorGridlinesVisible()) { gridStroke = getDomainMinorGridlineStroke(); gridPaint = getDomainMinorGridlinePaint(); paintLine = true; } else if ((tick.getTickType() == TickType.MAJOR) && isDomainGridlinesVisible()) { gridStroke = getDomainGridlineStroke(); gridPaint = getDomainGridlinePaint(); paintLine = true; } XYItemRenderer r = getRenderer(); if ((r instanceof AbstractXYItemRenderer) && paintLine) { ((AbstractXYItemRenderer) r).drawDomainLine(g2, this, getDomainAxis(), dataArea, tick.getValue(), gridPaint, gridStroke); } } } } /** * Draws the gridlines for the plot's primary range axis, if they are * visible. * * @param g2 the graphics device. * @param area the data area. * @param ticks the ticks. * * @see #drawDomainGridlines(Graphics2D, Rectangle2D, List) */ protected void drawRangeGridlines(Graphics2D g2, Rectangle2D area, List ticks) { // no renderer, no gridlines... if (getRenderer() == null) { return; } // draw the range grid lines, if any... if (isRangeGridlinesVisible() || isRangeMinorGridlinesVisible()) { Stroke gridStroke = null; Paint gridPaint = null; ValueAxis axis = getRangeAxis(); if (axis != null) { for (ValueTick tick : ticks) { boolean paintLine = false; if ((tick.getTickType() == TickType.MINOR) && isRangeMinorGridlinesVisible()) { gridStroke = getRangeMinorGridlineStroke(); gridPaint = getRangeMinorGridlinePaint(); paintLine = true; } else if ((tick.getTickType() == TickType.MAJOR) && isRangeGridlinesVisible()) { gridStroke = getRangeGridlineStroke(); gridPaint = getRangeGridlinePaint(); paintLine = true; } if ((tick.getValue() != 0.0 || !isRangeZeroBaselineVisible()) && paintLine) { getRenderer().drawRangeLine(g2, this, getRangeAxis(), area, tick.getValue(), gridPaint, gridStroke); } } } } } /** * Draws a base line across the chart at value zero on the domain axis. * * @param g2 the graphics device. * @param area the data area. * * @see #setDomainZeroBaselineVisible(boolean) */ protected void drawZeroDomainBaseline(Graphics2D g2, Rectangle2D area) { if (isDomainZeroBaselineVisible() && getRenderer() != null) { getRenderer().drawDomainLine(g2, this, getDomainAxis(), area, 0.0, this.domainZeroBaselinePaint, this.domainZeroBaselineStroke); } } /** * Draws a base line across the chart at value zero on the range axis. * * @param g2 the graphics device. * @param area the data area. * * @see #setRangeZeroBaselineVisible(boolean) */ protected void drawZeroRangeBaseline(Graphics2D g2, Rectangle2D area) { if (isRangeZeroBaselineVisible()) { getRenderer().drawRangeLine(g2, this, getRangeAxis(), area, 0.0, this.rangeZeroBaselinePaint, this.rangeZeroBaselineStroke); } } /** * Draws the annotations for the plot. * * @param g2 the graphics device. * @param dataArea the data area. * @param info the chart rendering info. */ public void drawAnnotations(Graphics2D g2, Rectangle2D dataArea, PlotRenderingInfo info) { for (XYAnnotation annotation : this.annotations) { ValueAxis xAxis = getDomainAxis(); ValueAxis yAxis = getRangeAxis(); annotation.draw(g2, this, dataArea, xAxis, yAxis, 0, info); } } /** * Draws the domain markers (if any) for an axis and layer. This method is * typically called from within the draw() method. * * @param g2 the graphics device. * @param dataArea the data area. * @param index the dataset/renderer index. * @param layer the layer (foreground or background). */ protected void drawDomainMarkers(Graphics2D g2, Rectangle2D dataArea, int index, Layer layer) { XYItemRenderer r = getRenderer(index); if (r == null) { return; } // check that the renderer has a corresponding dataset (it doesn't // matter if the dataset is null) if (!this.datasets.containsKey(index)) { return; } Collection markers = getDomainMarkers(index, layer); ValueAxis axis = getDomainAxisForDataset(index); if (markers != null && axis != null) { for (Marker marker : markers) { r.drawDomainMarker(g2, this, axis, marker, dataArea); } } } /** * Draws the range markers (if any) for a renderer and layer. This method * is typically called from within the draw() method. * * @param g2 the graphics device. * @param dataArea the data area. * @param index the renderer index. * @param layer the layer (foreground or background). */ protected void drawRangeMarkers(Graphics2D g2, Rectangle2D dataArea, int index, Layer layer) { XYItemRenderer r = getRenderer(index); if (r == null) { return; } // check that the renderer has a corresponding dataset (it doesn't // matter if the dataset is null) if (!this.datasets.containsKey(index)) { return; } Collection markers = getRangeMarkers(index, layer); ValueAxis axis = getRangeAxisForDataset(index); if (markers != null && axis != null) { for (Marker marker : markers) { r.drawRangeMarker(g2, this, axis, marker, dataArea); } } } /** * Returns the list of domain markers (read only) for the specified layer. * * @param layer the layer (foreground or background). * * @return The list of domain markers. * * @see #getRangeMarkers(Layer) */ public Collection getDomainMarkers(Layer layer) { return getDomainMarkers(0, layer); } /** * Returns the list of range markers (read only) for the specified layer. * * @param layer the layer (foreground or background). * * @return The list of range markers. * * @see #getDomainMarkers(Layer) */ public Collection getRangeMarkers(Layer layer) { return getRangeMarkers(0, layer); } /** * Returns a collection of domain markers for a particular renderer and * layer. * * @param index the renderer index. * @param layer the layer. * * @return A collection of markers (possibly {@code null}). * * @see #getRangeMarkers(int, Layer) */ public Collection getDomainMarkers(int index, Layer layer) { Collection result = null; if (layer == Layer.FOREGROUND) { result = this.foregroundDomainMarkers.get(index); } else if (layer == Layer.BACKGROUND) { result = this.backgroundDomainMarkers.get(index); } if (result != null) { result = Collections.unmodifiableCollection(result); } return result; } /** * Returns a collection of range markers for a particular renderer and * layer. * * @param index the renderer index. * @param layer the layer. * * @return A collection of markers (possibly {@code null}). * * @see #getDomainMarkers(int, Layer) */ public Collection getRangeMarkers(int index, Layer layer) { Collection result = null; if (layer == Layer.FOREGROUND) { result = this.foregroundRangeMarkers.get(index); } else if (layer == Layer.BACKGROUND) { result = this.backgroundRangeMarkers.get(index); } if (result != null) { result = Collections.unmodifiableCollection(result); } return result; } /** * Utility method for drawing a horizontal line across the data area of the * plot. * * @param g2 the graphics device. * @param dataArea the data area. * @param value the coordinate, where to draw the line. * @param stroke the stroke to use. * @param paint the paint to use. */ protected void drawHorizontalLine(Graphics2D g2, Rectangle2D dataArea, double value, Stroke stroke, Paint paint) { ValueAxis axis = getRangeAxis(); if (getOrientation() == PlotOrientation.HORIZONTAL) { axis = getDomainAxis(); } if (axis.getRange().contains(value)) { double yy = axis.valueToJava2D(value, dataArea, RectangleEdge.LEFT); Line2D line = new Line2D.Double(dataArea.getMinX(), yy, dataArea.getMaxX(), yy); g2.setStroke(stroke); g2.setPaint(paint); g2.draw(line); } } /** * Draws a domain crosshair. * * @param g2 the graphics target. * @param dataArea the data area. * @param orientation the plot orientation. * @param value the crosshair value. * @param axis the axis against which the value is measured. * @param stroke the stroke used to draw the crosshair line. * @param paint the paint used to draw the crosshair line. */ protected void drawDomainCrosshair(Graphics2D g2, Rectangle2D dataArea, PlotOrientation orientation, double value, ValueAxis axis, Stroke stroke, Paint paint) { if (!axis.getRange().contains(value)) { return; } Line2D line; if (orientation == PlotOrientation.VERTICAL) { double xx = axis.valueToJava2D(value, dataArea, RectangleEdge.BOTTOM); line = new Line2D.Double(xx, dataArea.getMinY(), xx, dataArea.getMaxY()); } else { double yy = axis.valueToJava2D(value, dataArea, RectangleEdge.LEFT); line = new Line2D.Double(dataArea.getMinX(), yy, dataArea.getMaxX(), yy); } Object saved = g2.getRenderingHint(RenderingHints.KEY_STROKE_CONTROL); g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE); g2.setStroke(stroke); g2.setPaint(paint); g2.draw(line); g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, saved); } /** * Utility method for drawing a vertical line on the data area of the plot. * * @param g2 the graphics device. * @param dataArea the data area. * @param value the coordinate, where to draw the line. * @param stroke the stroke to use. * @param paint the paint to use. */ protected void drawVerticalLine(Graphics2D g2, Rectangle2D dataArea, double value, Stroke stroke, Paint paint) { ValueAxis axis = getDomainAxis(); if (getOrientation() == PlotOrientation.HORIZONTAL) { axis = getRangeAxis(); } if (axis.getRange().contains(value)) { double xx = axis.valueToJava2D(value, dataArea, RectangleEdge.BOTTOM); Line2D line = new Line2D.Double(xx, dataArea.getMinY(), xx, dataArea.getMaxY()); g2.setStroke(stroke); g2.setPaint(paint); g2.draw(line); } } /** * Draws a range crosshair. * * @param g2 the graphics target. * @param dataArea the data area. * @param orientation the plot orientation. * @param value the crosshair value. * @param axis the axis against which the value is measured. * @param stroke the stroke used to draw the crosshair line. * @param paint the paint used to draw the crosshair line. */ protected void drawRangeCrosshair(Graphics2D g2, Rectangle2D dataArea, PlotOrientation orientation, double value, ValueAxis axis, Stroke stroke, Paint paint) { if (!axis.getRange().contains(value)) { return; } Object saved = g2.getRenderingHint(RenderingHints.KEY_STROKE_CONTROL); g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE); Line2D line; if (orientation == PlotOrientation.HORIZONTAL) { double xx = axis.valueToJava2D(value, dataArea, RectangleEdge.BOTTOM); line = new Line2D.Double(xx, dataArea.getMinY(), xx, dataArea.getMaxY()); } else { double yy = axis.valueToJava2D(value, dataArea, RectangleEdge.LEFT); line = new Line2D.Double(dataArea.getMinX(), yy, dataArea.getMaxX(), yy); } g2.setStroke(stroke); g2.setPaint(paint); g2.draw(line); g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, saved); } /** * Handles a 'click' on the plot by updating the anchor values. * * @param x the x-coordinate, where the click occurred, in Java2D space. * @param y the y-coordinate, where the click occurred, in Java2D space. * @param info object containing information about the plot dimensions. */ @Override public void handleClick(int x, int y, PlotRenderingInfo info) { Rectangle2D dataArea = info.getDataArea(); if (dataArea.contains(x, y)) { // set the anchor value for the horizontal axis... ValueAxis xaxis = getDomainAxis(); if (xaxis != null) { double hvalue = xaxis.java2DToValue(x, info.getDataArea(), getDomainAxisEdge()); setDomainCrosshairValue(hvalue); } // set the anchor value for the vertical axis... ValueAxis yaxis = getRangeAxis(); if (yaxis != null) { double vvalue = yaxis.java2DToValue(y, info.getDataArea(), getRangeAxisEdge()); setRangeCrosshairValue(vvalue); } } } /** * A utility method that returns a list of datasets that are mapped to a * particular axis. * * @param axisIndex the axis index ({@code null} not permitted). * * @return A list of datasets. */ private List getDatasetsMappedToDomainAxis(Integer axisIndex) { Args.nullNotPermitted(axisIndex, "axisIndex"); List result = new ArrayList<>(); for (Entry entry : this.datasets.entrySet()) { int index = entry.getKey(); List mappedAxes = this.datasetToDomainAxesMap.get(index); if (mappedAxes == null) { if (axisIndex.equals(ZERO)) { result.add(entry.getValue()); } } else { if (mappedAxes.contains(axisIndex)) { result.add(entry.getValue()); } } } return result; } /** * A utility method that returns a list of datasets that are mapped to a * particular axis. * * @param axisIndex the axis index ({@code null} not permitted). * * @return A list of datasets. */ private List getDatasetsMappedToRangeAxis(Integer axisIndex) { Args.nullNotPermitted(axisIndex, "axisIndex"); List result = new ArrayList<>(); for (Entry entry : this.datasets.entrySet()) { int index = entry.getKey(); List mappedAxes = this.datasetToRangeAxesMap.get(index); if (mappedAxes == null) { if (axisIndex.equals(ZERO)) { result.add(entry.getValue()); } } else { if (mappedAxes.contains(axisIndex)) { result.add(entry.getValue()); } } } return result; } /** * Returns the index of the given domain axis. * * @param axis the axis. * * @return The axis index. * * @see #getRangeAxisIndex(ValueAxis) */ public int getDomainAxisIndex(ValueAxis axis) { int result = findDomainAxisIndex(axis); if (result < 0) { // try the parent plot Plot parent = getParent(); if (parent instanceof XYPlot) { XYPlot p = (XYPlot) parent; result = p.getDomainAxisIndex(axis); } } return result; } private int findDomainAxisIndex(ValueAxis axis) { for (Map.Entry entry : this.domainAxes.entrySet()) { if (entry.getValue() == axis) { return entry.getKey(); } } return -1; } /** * Returns the index of the given range axis. * * @param axis the axis. * * @return The axis index. * * @see #getDomainAxisIndex(ValueAxis) */ public int getRangeAxisIndex(ValueAxis axis) { int result = findRangeAxisIndex(axis); if (result < 0) { // try the parent plot Plot parent = getParent(); if (parent instanceof XYPlot) { XYPlot p = (XYPlot) parent; result = p.getRangeAxisIndex(axis); } } return result; } private int findRangeAxisIndex(ValueAxis axis) { for (Map.Entry entry : this.rangeAxes.entrySet()) { if (entry.getValue() == axis) { return entry.getKey(); } } return -1; } /** * Returns the range for the specified axis. * * @param axis the axis. * * @return The range. */ @Override public Range getDataRange(ValueAxis axis) { Range result = null; List mappedDatasets = new ArrayList<>(); List includedAnnotations = new ArrayList<>(); boolean isDomainAxis = true; // is it a domain axis? int domainIndex = getDomainAxisIndex(axis); if (domainIndex >= 0) { isDomainAxis = true; mappedDatasets.addAll(getDatasetsMappedToDomainAxis(domainIndex)); if (domainIndex == 0) { // grab the plot's annotations for (XYAnnotation annotation : this.annotations) { if (annotation instanceof XYAnnotationBoundsInfo) { includedAnnotations.add(annotation); } } } } // or is it a range axis? int rangeIndex = getRangeAxisIndex(axis); if (rangeIndex >= 0) { isDomainAxis = false; mappedDatasets.addAll(getDatasetsMappedToRangeAxis(rangeIndex)); if (rangeIndex == 0) { Iterator iterator = this.annotations.iterator(); while (iterator.hasNext()) { XYAnnotation annotation = (XYAnnotation) iterator.next(); if (annotation instanceof XYAnnotationBoundsInfo) { includedAnnotations.add(annotation); } } } } // iterate through the datasets that map to the axis and get the union // of the ranges. for (XYDataset d : mappedDatasets) { if (d != null) { XYItemRenderer r = getRendererForDataset(d); if (isDomainAxis) { if (r != null) { result = Range.combine(result, r.findDomainBounds(d)); } else { result = Range.combine(result, DatasetUtils.findDomainBounds(d)); } } else { if (r != null) { result = Range.combine(result, r.findRangeBounds(d)); } else { result = Range.combine(result, DatasetUtils.findRangeBounds(d)); } } // FIXME: the XYItemRenderer interface doesn't specify the // getAnnotations() method but it should if (r instanceof AbstractXYItemRenderer) { AbstractXYItemRenderer rr = (AbstractXYItemRenderer) r; Collection c = rr.getAnnotations(); Iterator i = c.iterator(); while (i.hasNext()) { XYAnnotation a = (XYAnnotation) i.next(); if (a instanceof XYAnnotationBoundsInfo) { includedAnnotations.add(a); } } } } } Iterator it = includedAnnotations.iterator(); while (it.hasNext()) { XYAnnotationBoundsInfo xyabi = (XYAnnotationBoundsInfo) it.next(); if (xyabi.getIncludeInDataBounds()) { if (isDomainAxis) { result = Range.combine(result, xyabi.getXRange()); } else { result = Range.combine(result, xyabi.getYRange()); } } } return result; } /** * Receives notification of a change to an {@link Annotation} added to * this plot. * * @param event information about the event (not used here). */ @Override public void annotationChanged(AnnotationChangeEvent event) { if (getParent() != null) { getParent().annotationChanged(event); } else { PlotChangeEvent e = new PlotChangeEvent(this); notifyListeners(e); } } /** * Receives notification of a change to the plot's dataset. *

* The axis ranges are updated if necessary. * * @param event information about the event (not used here). */ @Override public void datasetChanged(DatasetChangeEvent event) { configureDomainAxes(); configureRangeAxes(); if (getParent() != null) { getParent().datasetChanged(event); } else { PlotChangeEvent e = new PlotChangeEvent(this); e.setType(ChartChangeEventType.DATASET_UPDATED); notifyListeners(e); } } /** * Receives notification of a renderer change event. * * @param event the event. */ @Override public void rendererChanged(RendererChangeEvent event) { // if the event was caused by a change to series visibility, then // the axis ranges might need updating... if (event.getSeriesVisibilityChanged()) { configureDomainAxes(); configureRangeAxes(); } fireChangeEvent(); } /** * Returns a flag indicating whether or not the domain crosshair is visible. * * @return The flag. * * @see #setDomainCrosshairVisible(boolean) */ public boolean isDomainCrosshairVisible() { return this.domainCrosshairVisible; } /** * Sets the flag indicating whether or not the domain crosshair is visible * and, if the flag changes, sends a {@link PlotChangeEvent} to all * registered listeners. * * @param flag the new value of the flag. * * @see #isDomainCrosshairVisible() */ public void setDomainCrosshairVisible(boolean flag) { if (this.domainCrosshairVisible != flag) { this.domainCrosshairVisible = flag; fireChangeEvent(); } } /** * Returns a flag indicating whether or not the crosshair should "lock-on" * to actual data values. * * @return The flag. * * @see #setDomainCrosshairLockedOnData(boolean) */ public boolean isDomainCrosshairLockedOnData() { return this.domainCrosshairLockedOnData; } /** * Sets the flag indicating whether or not the domain crosshair should * "lock-on" to actual data values. If the flag value changes, this * method sends a {@link PlotChangeEvent} to all registered listeners. * * @param flag the flag. * * @see #isDomainCrosshairLockedOnData() */ public void setDomainCrosshairLockedOnData(boolean flag) { if (this.domainCrosshairLockedOnData != flag) { this.domainCrosshairLockedOnData = flag; fireChangeEvent(); } } /** * Returns the domain crosshair value. * * @return The value. * * @see #setDomainCrosshairValue(double) */ public double getDomainCrosshairValue() { return this.domainCrosshairValue; } /** * Sets the domain crosshair value and sends a {@link PlotChangeEvent} to * all registered listeners (provided that the domain crosshair is visible). * * @param value the value. * * @see #getDomainCrosshairValue() */ public void setDomainCrosshairValue(double value) { setDomainCrosshairValue(value, true); } /** * Sets the domain crosshair value and, if requested, sends a * {@link PlotChangeEvent} to all registered listeners (provided that the * domain crosshair is visible). * * @param value the new value. * @param notify notify listeners? * * @see #getDomainCrosshairValue() */ public void setDomainCrosshairValue(double value, boolean notify) { this.domainCrosshairValue = value; if (isDomainCrosshairVisible() && notify) { fireChangeEvent(); } } /** * Returns the {@link Stroke} used to draw the crosshair (if visible). * * @return The crosshair stroke (never {@code null}). * * @see #setDomainCrosshairStroke(Stroke) * @see #isDomainCrosshairVisible() * @see #getDomainCrosshairPaint() */ public Stroke getDomainCrosshairStroke() { return this.domainCrosshairStroke; } /** * Sets the Stroke used to draw the crosshairs (if visible) and notifies * registered listeners that the axis has been modified. * * @param stroke the new crosshair stroke ({@code null} not * permitted). * * @see #getDomainCrosshairStroke() */ public void setDomainCrosshairStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.domainCrosshairStroke = stroke; fireChangeEvent(); } /** * Returns the domain crosshair paint. * * @return The crosshair paint (never {@code null}). * * @see #setDomainCrosshairPaint(Paint) * @see #isDomainCrosshairVisible() * @see #getDomainCrosshairStroke() */ public Paint getDomainCrosshairPaint() { return this.domainCrosshairPaint; } /** * Sets the paint used to draw the crosshairs (if visible) and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param paint the new crosshair paint ({@code null} not permitted). * * @see #getDomainCrosshairPaint() */ public void setDomainCrosshairPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.domainCrosshairPaint = paint; fireChangeEvent(); } /** * Returns a flag indicating whether or not the range crosshair is visible. * * @return The flag. * * @see #setRangeCrosshairVisible(boolean) * @see #isDomainCrosshairVisible() */ public boolean isRangeCrosshairVisible() { return this.rangeCrosshairVisible; } /** * Sets the flag indicating whether or not the range crosshair is visible. * If the flag value changes, this method sends a {@link PlotChangeEvent} * to all registered listeners. * * @param flag the new value of the flag. * * @see #isRangeCrosshairVisible() */ public void setRangeCrosshairVisible(boolean flag) { if (this.rangeCrosshairVisible != flag) { this.rangeCrosshairVisible = flag; fireChangeEvent(); } } /** * Returns a flag indicating whether or not the crosshair should "lock-on" * to actual data values. * * @return The flag. * * @see #setRangeCrosshairLockedOnData(boolean) */ public boolean isRangeCrosshairLockedOnData() { return this.rangeCrosshairLockedOnData; } /** * Sets the flag indicating whether or not the range crosshair should * "lock-on" to actual data values. If the flag value changes, this method * sends a {@link PlotChangeEvent} to all registered listeners. * * @param flag the flag. * * @see #isRangeCrosshairLockedOnData() */ public void setRangeCrosshairLockedOnData(boolean flag) { if (this.rangeCrosshairLockedOnData != flag) { this.rangeCrosshairLockedOnData = flag; fireChangeEvent(); } } /** * Returns the range crosshair value. * * @return The value. * * @see #setRangeCrosshairValue(double) */ public double getRangeCrosshairValue() { return this.rangeCrosshairValue; } /** * Sets the range crosshair value. *

* Registered listeners are notified that the plot has been modified, but * only if the crosshair is visible. * * @param value the new value. * * @see #getRangeCrosshairValue() */ public void setRangeCrosshairValue(double value) { setRangeCrosshairValue(value, true); } /** * Sets the range crosshair value and sends a {@link PlotChangeEvent} to * all registered listeners, but only if the crosshair is visible. * * @param value the new value. * @param notify a flag that controls whether or not listeners are * notified. * * @see #getRangeCrosshairValue() */ public void setRangeCrosshairValue(double value, boolean notify) { this.rangeCrosshairValue = value; if (isRangeCrosshairVisible() && notify) { fireChangeEvent(); } } /** * Returns the stroke used to draw the crosshair (if visible). * * @return The crosshair stroke (never {@code null}). * * @see #setRangeCrosshairStroke(Stroke) * @see #isRangeCrosshairVisible() * @see #getRangeCrosshairPaint() */ public Stroke getRangeCrosshairStroke() { return this.rangeCrosshairStroke; } /** * Sets the stroke used to draw the crosshairs (if visible) and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param stroke the new crosshair stroke ({@code null} not * permitted). * * @see #getRangeCrosshairStroke() */ public void setRangeCrosshairStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.rangeCrosshairStroke = stroke; fireChangeEvent(); } /** * Returns the range crosshair paint. * * @return The crosshair paint (never {@code null}). * * @see #setRangeCrosshairPaint(Paint) * @see #isRangeCrosshairVisible() * @see #getRangeCrosshairStroke() */ public Paint getRangeCrosshairPaint() { return this.rangeCrosshairPaint; } /** * Sets the paint used to color the crosshairs (if visible) and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param paint the new crosshair paint ({@code null} not permitted). * * @see #getRangeCrosshairPaint() */ public void setRangeCrosshairPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.rangeCrosshairPaint = paint; fireChangeEvent(); } /** * Returns the fixed domain axis space. * * @return The fixed domain axis space (possibly {@code null}). * * @see #setFixedDomainAxisSpace(AxisSpace) */ public AxisSpace getFixedDomainAxisSpace() { return this.fixedDomainAxisSpace; } /** * Sets the fixed domain axis space and sends a {@link PlotChangeEvent} to * all registered listeners. * * @param space the space ({@code null} permitted). * * @see #getFixedDomainAxisSpace() */ public void setFixedDomainAxisSpace(AxisSpace space) { setFixedDomainAxisSpace(space, true); } /** * Sets the fixed domain axis space and, if requested, sends a * {@link PlotChangeEvent} to all registered listeners. * * @param space the space ({@code null} permitted). * @param notify notify listeners? * * @see #getFixedDomainAxisSpace() */ public void setFixedDomainAxisSpace(AxisSpace space, boolean notify) { this.fixedDomainAxisSpace = space; if (notify) { fireChangeEvent(); } } /** * Returns the fixed range axis space. * * @return The fixed range axis space (possibly {@code null}). * * @see #setFixedRangeAxisSpace(AxisSpace) */ public AxisSpace getFixedRangeAxisSpace() { return this.fixedRangeAxisSpace; } /** * Sets the fixed range axis space and sends a {@link PlotChangeEvent} to * all registered listeners. * * @param space the space ({@code null} permitted). * * @see #getFixedRangeAxisSpace() */ public void setFixedRangeAxisSpace(AxisSpace space) { setFixedRangeAxisSpace(space, true); } /** * Sets the fixed range axis space and, if requested, sends a * {@link PlotChangeEvent} to all registered listeners. * * @param space the space ({@code null} permitted). * @param notify notify listeners? * * @see #getFixedRangeAxisSpace() */ public void setFixedRangeAxisSpace(AxisSpace space, boolean notify) { this.fixedRangeAxisSpace = space; if (notify) { fireChangeEvent(); } } /** * Returns {@code true} if panning is enabled for the domain axes, * and {@code false} otherwise. * * @return A boolean. */ @Override public boolean isDomainPannable() { return this.domainPannable; } /** * Sets the flag that enables or disables panning of the plot along the * domain axes. * * @param pannable the new flag value. */ public void setDomainPannable(boolean pannable) { this.domainPannable = pannable; } /** * Returns {@code true} if panning is enabled for the range axis/axes, * and {@code false} otherwise. The default value is {@code false}. * * @return A boolean. */ @Override public boolean isRangePannable() { return this.rangePannable; } /** * Sets the flag that enables or disables panning of the plot along * the range axis/axes. * * @param pannable the new flag value. */ public void setRangePannable(boolean pannable) { this.rangePannable = pannable; } /** * Pans the domain axes by the specified percentage. * * @param percent the distance to pan (as a percentage of the axis length). * @param info the plot info * @param source the source point where the pan action started. */ @Override public void panDomainAxes(double percent, PlotRenderingInfo info, Point2D source) { if (!isDomainPannable()) { return; } int domainAxisCount = getDomainAxisCount(); for (int i = 0; i < domainAxisCount; i++) { ValueAxis axis = getDomainAxis(i); if (axis == null) { continue; } axis.pan(axis.isInverted() ? -percent : percent); } } /** * Pans the range axes by the specified percentage. * * @param percent the distance to pan (as a percentage of the axis length). * @param info the plot info * @param source the source point where the pan action started. */ @Override public void panRangeAxes(double percent, PlotRenderingInfo info, Point2D source) { if (!isRangePannable()) { return; } int rangeAxisCount = getRangeAxisCount(); for (int i = 0; i < rangeAxisCount; i++) { ValueAxis axis = getRangeAxis(i); if (axis == null) { continue; } axis.pan(axis.isInverted() ? -percent : percent); } } /** * Multiplies the range on the domain axis/axes by the specified factor. * * @param factor the zoom factor. * @param info the plot rendering info. * @param source the source point (in Java2D space). * * @see #zoomRangeAxes(double, PlotRenderingInfo, Point2D) */ @Override public void zoomDomainAxes(double factor, PlotRenderingInfo info, Point2D source) { // delegate to other method zoomDomainAxes(factor, info, source, false); } /** * Multiplies the range on the domain axis/axes by the specified factor. * * @param factor the zoom factor. * @param info the plot rendering info. * @param source the source point (in Java2D space). * @param useAnchor use source point as zoom anchor? * * @see #zoomRangeAxes(double, PlotRenderingInfo, Point2D, boolean) */ @Override public void zoomDomainAxes(double factor, PlotRenderingInfo info, Point2D source, boolean useAnchor) { // perform the zoom on each domain axis for (ValueAxis xAxis : this.domainAxes.values()) { if (xAxis == null) { continue; } if (useAnchor) { // get the relevant source coordinate given the plot orientation double sourceX = source.getX(); if (this.orientation == PlotOrientation.HORIZONTAL) { sourceX = source.getY(); } double anchorX = xAxis.java2DToValue(sourceX, info.getDataArea(), getDomainAxisEdge()); xAxis.resizeRange2(factor, anchorX); } else { xAxis.resizeRange(factor); } } } /** * Zooms in on the domain axis/axes. The new lower and upper bounds are * specified as percentages of the current axis range, where 0 percent is * the current lower bound and 100 percent is the current upper bound. * * @param lowerPercent a percentage that determines the new lower bound * for the axis (e.g. 0.20 is twenty percent). * @param upperPercent a percentage that determines the new upper bound * for the axis (e.g. 0.80 is eighty percent). * @param info the plot rendering info. * @param source the source point (ignored). * * @see #zoomRangeAxes(double, double, PlotRenderingInfo, Point2D) */ @Override public void zoomDomainAxes(double lowerPercent, double upperPercent, PlotRenderingInfo info, Point2D source) { for (ValueAxis xAxis : this.domainAxes.values()) { if (xAxis != null) { xAxis.zoomRange(lowerPercent, upperPercent); } } } /** * Multiplies the range on the range axis/axes by the specified factor. * * @param factor the zoom factor. * @param info the plot rendering info. * @param source the source point. * * @see #zoomDomainAxes(double, PlotRenderingInfo, Point2D, boolean) */ @Override public void zoomRangeAxes(double factor, PlotRenderingInfo info, Point2D source) { // delegate to other method zoomRangeAxes(factor, info, source, false); } /** * Multiplies the range on the range axis/axes by the specified factor. * * @param factor the zoom factor. * @param info the plot rendering info. * @param source the source point. * @param useAnchor a flag that controls whether or not the source point * is used for the zoom anchor. * * @see #zoomDomainAxes(double, PlotRenderingInfo, Point2D, boolean) */ @Override public void zoomRangeAxes(double factor, PlotRenderingInfo info, Point2D source, boolean useAnchor) { // perform the zoom on each range axis for (ValueAxis yAxis : this.rangeAxes.values()) { if (yAxis == null) { continue; } if (useAnchor) { // get the relevant source coordinate given the plot orientation double sourceY = source.getY(); if (this.orientation == PlotOrientation.HORIZONTAL) { sourceY = source.getX(); } double anchorY = yAxis.java2DToValue(sourceY, info.getDataArea(), getRangeAxisEdge()); yAxis.resizeRange2(factor, anchorY); } else { yAxis.resizeRange(factor); } } } /** * Zooms in on the range axes. * * @param lowerPercent the lower bound. * @param upperPercent the upper bound. * @param info the plot rendering info. * @param source the source point. * * @see #zoomDomainAxes(double, double, PlotRenderingInfo, Point2D) */ @Override public void zoomRangeAxes(double lowerPercent, double upperPercent, PlotRenderingInfo info, Point2D source) { for (ValueAxis yAxis : this.rangeAxes.values()) { if (yAxis != null) { yAxis.zoomRange(lowerPercent, upperPercent); } } } /** * Returns {@code true}, indicating that the domain axis/axes for this * plot are zoomable. * * @return A boolean. * * @see #isRangeZoomable() */ @Override public boolean isDomainZoomable() { return true; } /** * Returns {@code true}, indicating that the range axis/axes for this * plot are zoomable. * * @return A boolean. * * @see #isDomainZoomable() */ @Override public boolean isRangeZoomable() { return true; } /** * Returns the number of series in the primary dataset for this plot. If * the dataset is {@code null}, the method returns 0. * * @return The series count. */ public int getSeriesCount() { int result = 0; XYDataset dataset = getDataset(); if (dataset != null) { result = dataset.getSeriesCount(); } return result; } /** * Returns the fixed legend items, if any. * * @return The legend items (possibly {@code null}). * * @see #setFixedLegendItems(LegendItemCollection) */ public LegendItemCollection getFixedLegendItems() { return this.fixedLegendItems; } /** * Sets the fixed legend items for the plot. Leave this set to * {@code null} if you prefer the legend items to be created * automatically. * * @param items the legend items ({@code null} permitted). * * @see #getFixedLegendItems() */ public void setFixedLegendItems(LegendItemCollection items) { this.fixedLegendItems = items; fireChangeEvent(); } /** * Returns the legend items for the plot. Each legend item is generated by * the plot's renderer, since the renderer is responsible for the visual * representation of the data. * * @return The legend items. */ @Override public LegendItemCollection getLegendItems() { if (this.fixedLegendItems != null) { return this.fixedLegendItems; } LegendItemCollection result = new LegendItemCollection(); for (XYDataset dataset : this.datasets.values()) { if (dataset == null) { continue; } int datasetIndex = indexOf(dataset); XYItemRenderer renderer = getRenderer(datasetIndex); if (renderer == null) { renderer = getRenderer(0); } if (renderer != null) { int seriesCount = dataset.getSeriesCount(); for (int i = 0; i < seriesCount; i++) { if (renderer.isSeriesVisible(i) && renderer.isSeriesVisibleInLegend(i)) { LegendItem item = renderer.getLegendItem( datasetIndex, i); if (item != null) { result.add(item); } } } } } return result; } /** * Tests this plot for equality with another object. * * @param obj the object ({@code null} permitted). * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYPlot)) { return false; } XYPlot that = (XYPlot) obj; if (this.weight != that.weight) { return false; } if (this.orientation != that.orientation) { return false; } if (!this.domainAxes.equals(that.domainAxes)) { return false; } if (!this.domainAxisLocations.equals(that.domainAxisLocations)) { return false; } if (this.rangeCrosshairLockedOnData != that.rangeCrosshairLockedOnData) { return false; } if (this.domainGridlinesVisible != that.domainGridlinesVisible) { return false; } if (this.rangeGridlinesVisible != that.rangeGridlinesVisible) { return false; } if (this.domainMinorGridlinesVisible != that.domainMinorGridlinesVisible) { return false; } if (this.rangeMinorGridlinesVisible != that.rangeMinorGridlinesVisible) { return false; } if (this.domainZeroBaselineVisible != that.domainZeroBaselineVisible) { return false; } if (this.rangeZeroBaselineVisible != that.rangeZeroBaselineVisible) { return false; } if (this.domainCrosshairVisible != that.domainCrosshairVisible) { return false; } if (this.domainCrosshairValue != that.domainCrosshairValue) { return false; } if (this.domainCrosshairLockedOnData != that.domainCrosshairLockedOnData) { return false; } if (this.rangeCrosshairVisible != that.rangeCrosshairVisible) { return false; } if (this.rangeCrosshairValue != that.rangeCrosshairValue) { return false; } if (!Objects.equals(this.axisOffset, that.axisOffset)) { return false; } if (!Objects.equals(this.renderers, that.renderers)) { return false; } if (!Objects.equals(this.rangeAxes, that.rangeAxes)) { return false; } if (!this.rangeAxisLocations.equals(that.rangeAxisLocations)) { return false; } if (!Objects.equals(this.datasetToDomainAxesMap, that.datasetToDomainAxesMap)) { return false; } if (!Objects.equals(this.datasetToRangeAxesMap, that.datasetToRangeAxesMap)) { return false; } if (!Objects.equals(this.domainGridlineStroke, that.domainGridlineStroke)) { return false; } if (!PaintUtils.equal(this.domainGridlinePaint, that.domainGridlinePaint)) { return false; } if (!Objects.equals(this.rangeGridlineStroke, that.rangeGridlineStroke)) { return false; } if (!PaintUtils.equal(this.rangeGridlinePaint, that.rangeGridlinePaint)) { return false; } if (!Objects.equals(this.domainMinorGridlineStroke, that.domainMinorGridlineStroke)) { return false; } if (!PaintUtils.equal(this.domainMinorGridlinePaint, that.domainMinorGridlinePaint)) { return false; } if (!Objects.equals(this.rangeMinorGridlineStroke, that.rangeMinorGridlineStroke)) { return false; } if (!PaintUtils.equal(this.rangeMinorGridlinePaint, that.rangeMinorGridlinePaint)) { return false; } if (!PaintUtils.equal(this.domainZeroBaselinePaint, that.domainZeroBaselinePaint)) { return false; } if (!Objects.equals(this.domainZeroBaselineStroke, that.domainZeroBaselineStroke)) { return false; } if (!PaintUtils.equal(this.rangeZeroBaselinePaint, that.rangeZeroBaselinePaint)) { return false; } if (!Objects.equals(this.rangeZeroBaselineStroke, that.rangeZeroBaselineStroke)) { return false; } if (!Objects.equals(this.domainCrosshairStroke, that.domainCrosshairStroke)) { return false; } if (!PaintUtils.equal(this.domainCrosshairPaint, that.domainCrosshairPaint)) { return false; } if (!Objects.equals(this.rangeCrosshairStroke, that.rangeCrosshairStroke)) { return false; } if (!PaintUtils.equal(this.rangeCrosshairPaint, that.rangeCrosshairPaint)) { return false; } if (!Objects.equals(this.foregroundDomainMarkers, that.foregroundDomainMarkers)) { return false; } if (!Objects.equals(this.backgroundDomainMarkers, that.backgroundDomainMarkers)) { return false; } if (!Objects.equals(this.foregroundRangeMarkers, that.foregroundRangeMarkers)) { return false; } if (!Objects.equals(this.backgroundRangeMarkers, that.backgroundRangeMarkers)) { return false; } if (!Objects.equals(this.foregroundDomainMarkers, that.foregroundDomainMarkers)) { return false; } if (!Objects.equals(this.backgroundDomainMarkers, that.backgroundDomainMarkers)) { return false; } if (!Objects.equals(this.foregroundRangeMarkers, that.foregroundRangeMarkers)) { return false; } if (!Objects.equals(this.backgroundRangeMarkers, that.backgroundRangeMarkers)) { return false; } if (!Objects.equals(this.annotations, that.annotations)) { return false; } if (!Objects.equals(this.fixedLegendItems, that.fixedLegendItems)) { return false; } if (!PaintUtils.equal(this.domainTickBandPaint, that.domainTickBandPaint)) { return false; } if (!PaintUtils.equal(this.rangeTickBandPaint, that.rangeTickBandPaint)) { return false; } if (!this.quadrantOrigin.equals(that.quadrantOrigin)) { return false; } for (int i = 0; i < 4; i++) { if (!PaintUtils.equal(this.quadrantPaint[i], that.quadrantPaint[i])) { return false; } } if (!Objects.equals(this.shadowGenerator, that.shadowGenerator)) { return false; } return super.equals(obj); } /** * Returns a clone of the plot. * * @return A clone. * * @throws CloneNotSupportedException this can occur if some component of * the plot cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { XYPlot clone = (XYPlot) super.clone(); clone.domainAxes = CloneUtils.cloneMapValues(this.domainAxes); for (ValueAxis axis : clone.domainAxes.values()) { if (axis != null) { axis.setPlot(clone); axis.addChangeListener(clone); } } clone.rangeAxes = CloneUtils.cloneMapValues(this.rangeAxes); for (ValueAxis axis : clone.rangeAxes.values()) { if (axis != null) { axis.setPlot(clone); axis.addChangeListener(clone); } } clone.domainAxisLocations = new HashMap<>(this.domainAxisLocations); clone.rangeAxisLocations = new HashMap<>(this.rangeAxisLocations); // the datasets are not cloned, but listeners need to be added... clone.datasets = new HashMap<>(this.datasets); for (XYDataset dataset : clone.datasets.values()) { if (dataset != null) { dataset.addChangeListener(clone); } } clone.datasetToDomainAxesMap = new TreeMap(); clone.datasetToDomainAxesMap.putAll(this.datasetToDomainAxesMap); clone.datasetToRangeAxesMap = new TreeMap(); clone.datasetToRangeAxesMap.putAll(this.datasetToRangeAxesMap); clone.renderers = CloneUtils.cloneMapValues(this.renderers); for (XYItemRenderer renderer : clone.renderers.values()) { if (renderer != null) { renderer.setPlot(clone); renderer.addChangeListener(clone); } } clone.foregroundDomainMarkers = (Map) ObjectUtils.clone( this.foregroundDomainMarkers); clone.backgroundDomainMarkers = (Map) ObjectUtils.clone( this.backgroundDomainMarkers); clone.foregroundRangeMarkers = (Map) ObjectUtils.clone( this.foregroundRangeMarkers); clone.backgroundRangeMarkers = (Map) ObjectUtils.clone( this.backgroundRangeMarkers); clone.annotations = (List) ObjectUtils.deepClone(this.annotations); if (this.fixedDomainAxisSpace != null) { clone.fixedDomainAxisSpace = (AxisSpace) ObjectUtils.clone( this.fixedDomainAxisSpace); } if (this.fixedRangeAxisSpace != null) { clone.fixedRangeAxisSpace = (AxisSpace) ObjectUtils.clone( this.fixedRangeAxisSpace); } if (this.fixedLegendItems != null) { clone.fixedLegendItems = (LegendItemCollection) this.fixedLegendItems.clone(); } clone.quadrantOrigin = (Point2D) ObjectUtils.clone( this.quadrantOrigin); clone.quadrantPaint = this.quadrantPaint.clone(); return clone; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeStroke(this.domainGridlineStroke, stream); SerialUtils.writePaint(this.domainGridlinePaint, stream); SerialUtils.writeStroke(this.rangeGridlineStroke, stream); SerialUtils.writePaint(this.rangeGridlinePaint, stream); SerialUtils.writeStroke(this.domainMinorGridlineStroke, stream); SerialUtils.writePaint(this.domainMinorGridlinePaint, stream); SerialUtils.writeStroke(this.rangeMinorGridlineStroke, stream); SerialUtils.writePaint(this.rangeMinorGridlinePaint, stream); SerialUtils.writeStroke(this.rangeZeroBaselineStroke, stream); SerialUtils.writePaint(this.rangeZeroBaselinePaint, stream); SerialUtils.writeStroke(this.domainCrosshairStroke, stream); SerialUtils.writePaint(this.domainCrosshairPaint, stream); SerialUtils.writeStroke(this.rangeCrosshairStroke, stream); SerialUtils.writePaint(this.rangeCrosshairPaint, stream); SerialUtils.writePaint(this.domainTickBandPaint, stream); SerialUtils.writePaint(this.rangeTickBandPaint, stream); SerialUtils.writePoint2D(this.quadrantOrigin, stream); for (int i = 0; i < 4; i++) { SerialUtils.writePaint(this.quadrantPaint[i], stream); } SerialUtils.writeStroke(this.domainZeroBaselineStroke, stream); SerialUtils.writePaint(this.domainZeroBaselinePaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.domainGridlineStroke = SerialUtils.readStroke(stream); this.domainGridlinePaint = SerialUtils.readPaint(stream); this.rangeGridlineStroke = SerialUtils.readStroke(stream); this.rangeGridlinePaint = SerialUtils.readPaint(stream); this.domainMinorGridlineStroke = SerialUtils.readStroke(stream); this.domainMinorGridlinePaint = SerialUtils.readPaint(stream); this.rangeMinorGridlineStroke = SerialUtils.readStroke(stream); this.rangeMinorGridlinePaint = SerialUtils.readPaint(stream); this.rangeZeroBaselineStroke = SerialUtils.readStroke(stream); this.rangeZeroBaselinePaint = SerialUtils.readPaint(stream); this.domainCrosshairStroke = SerialUtils.readStroke(stream); this.domainCrosshairPaint = SerialUtils.readPaint(stream); this.rangeCrosshairStroke = SerialUtils.readStroke(stream); this.rangeCrosshairPaint = SerialUtils.readPaint(stream); this.domainTickBandPaint = SerialUtils.readPaint(stream); this.rangeTickBandPaint = SerialUtils.readPaint(stream); this.quadrantOrigin = SerialUtils.readPoint2D(stream); this.quadrantPaint = new Paint[4]; for (int i = 0; i < 4; i++) { this.quadrantPaint[i] = SerialUtils.readPaint(stream); } this.domainZeroBaselineStroke = SerialUtils.readStroke(stream); this.domainZeroBaselinePaint = SerialUtils.readPaint(stream); // register the plot as a listener with its axes, datasets, and // renderers... for (ValueAxis axis : this.domainAxes.values()) { if (axis != null) { axis.setPlot(this); axis.addChangeListener(this); } } for (ValueAxis axis : this.rangeAxes.values()) { if (axis != null) { axis.setPlot(this); axis.addChangeListener(this); } } for (XYDataset dataset : this.datasets.values()) { if (dataset != null) { dataset.addChangeListener(this); } } for (XYItemRenderer renderer : this.renderers.values()) { if (renderer != null) { renderer.addChangeListener(this); } } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/Zoomable.java000066400000000000000000000140721463604235500264530ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------- * Zoomable.java * ------------- * * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Rune Fauske; * */ package org.jfree.chart.plot; import java.awt.geom.Point2D; import org.jfree.chart.ChartPanel; /** * A plot that is zoomable must implement this interface to provide a * mechanism for the {@link ChartPanel} to control the zooming. */ public interface Zoomable { /** * Returns {@code true} if the plot's domain is zoomable, and {@code false} * otherwise. * * @return A boolean. * * @see #isRangeZoomable() */ boolean isDomainZoomable(); /** * Returns {@code true} if the plot's range is zoomable, and {@code false} * otherwise. * * @return A boolean. * * @see #isDomainZoomable() */ boolean isRangeZoomable(); /** * Returns the orientation of the plot. * * @return The orientation (never {@code null}). */ PlotOrientation getOrientation(); /** * Multiplies the range on the domain axis/axes by the specified factor. * The {@code source} point can be used in some cases to identify a * subplot, or to determine the center of zooming (refer to the * documentation of the implementing class for details). * * @param factor the zoom factor. * @param state the plot state. * @param source the source point (in Java2D coordinates). * * @see #zoomRangeAxes(double, PlotRenderingInfo, Point2D) */ void zoomDomainAxes(double factor, PlotRenderingInfo state, Point2D source); /** * Multiplies the range on the domain axis/axes by the specified factor. * The {@code source} point can be used in some cases to identify a * subplot, or to determine the center of zooming (refer to the * documentation of the implementing class for details). * * @param factor the zoom factor. * @param state the plot state. * @param source the source point (in Java2D coordinates). * @param useAnchor use source point as zoom anchor? * * @see #zoomRangeAxes(double, PlotRenderingInfo, Point2D, boolean) */ void zoomDomainAxes(double factor, PlotRenderingInfo state, Point2D source, boolean useAnchor); /** * Zooms in on the domain axes. The {@code source} point can be used * in some cases to identify a subplot for zooming. * * @param lowerPercent the new lower bound. * @param upperPercent the new upper bound. * @param state the plot state. * @param source the source point (in Java2D coordinates). * * @see #zoomRangeAxes(double, double, PlotRenderingInfo, Point2D) */ void zoomDomainAxes(double lowerPercent, double upperPercent, PlotRenderingInfo state, Point2D source); /** * Multiplies the range on the range axis/axes by the specified factor. * The {@code source} point can be used in some cases to identify a * subplot, or to determine the center of zooming (refer to the * documentation of the implementing class for details). * * @param factor the zoom factor. * @param state the plot state. * @param source the source point (in Java2D coordinates). * * @see #zoomDomainAxes(double, PlotRenderingInfo, Point2D) */ void zoomRangeAxes(double factor, PlotRenderingInfo state, Point2D source); /** * Multiplies the range on the range axis/axes by the specified factor. * The {@code source} point can be used in some cases to identify a * subplot, or to determine the center of zooming (refer to the * documentation of the implementing class for details). * * @param factor the zoom factor. * @param state the plot state. * @param source the source point (in Java2D coordinates). * @param useAnchor use source point as zoom anchor? * * @see #zoomDomainAxes(double, PlotRenderingInfo, Point2D) */ void zoomRangeAxes(double factor, PlotRenderingInfo state, Point2D source, boolean useAnchor); /** * Zooms in on the range axes. The {@code source} point can be used * in some cases to identify a subplot for zooming. * * @param lowerPercent the new lower bound. * @param upperPercent the new upper bound. * @param state the plot state. * @param source the source point (in Java2D coordinates). * * @see #zoomDomainAxes(double, double, PlotRenderingInfo, Point2D) */ void zoomRangeAxes(double lowerPercent, double upperPercent, PlotRenderingInfo state, Point2D source); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/dial/000077500000000000000000000000001463604235500247455ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/dial/AbstractDialLayer.java000066400000000000000000000144751463604235500311550ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * AbstractDialLayer.java * ---------------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot.dial; import java.io.IOException; import java.io.ObjectInputStream; import java.util.Arrays; import java.util.EventListener; import java.util.List; import javax.swing.event.EventListenerList; import org.jfree.chart.HashUtils; /** * A base class that can be used to implement a {@link DialLayer}. It includes * an event notification mechanism. */ public abstract class AbstractDialLayer implements DialLayer { /** A flag that controls whether or not the layer is visible. */ private boolean visible; /** Storage for registered listeners. */ private transient EventListenerList listenerList; /** * Creates a new instance. */ protected AbstractDialLayer() { this.visible = true; this.listenerList = new EventListenerList(); } /** * Returns {@code true} if this layer is visible (should be displayed), * and {@code false} otherwise. * * @return A boolean. * * @see #setVisible(boolean) */ @Override public boolean isVisible() { return this.visible; } /** * Sets the flag that determines whether or not this layer is drawn by * the plot, and sends a {@link DialLayerChangeEvent} to all registered * listeners. * * @param visible the flag. * * @see #isVisible() */ public void setVisible(boolean visible) { this.visible = visible; notifyListeners(new DialLayerChangeEvent(this)); } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof AbstractDialLayer)) { return false; } AbstractDialLayer that = (AbstractDialLayer) obj; return this.visible == that.visible; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = 23; result = HashUtils.hashCode(result, this.visible); return result; } /** * Returns a clone of this instance. * * @return A clone. * * @throws CloneNotSupportedException if there is a problem cloning this * instance. */ @Override public Object clone() throws CloneNotSupportedException { AbstractDialLayer clone = (AbstractDialLayer) super.clone(); // we don't clone the listeners clone.listenerList = new EventListenerList(); return clone; } /** * Registers an object for notification of changes to the dial layer. * * @param listener the object that is being registered. * * @see #removeChangeListener(DialLayerChangeListener) */ @Override public void addChangeListener(DialLayerChangeListener listener) { this.listenerList.add(DialLayerChangeListener.class, listener); } /** * Deregisters an object for notification of changes to the dial layer. * * @param listener the object to deregister. * * @see #addChangeListener(DialLayerChangeListener) */ @Override public void removeChangeListener(DialLayerChangeListener listener) { this.listenerList.remove(DialLayerChangeListener.class, listener); } /** * Returns {@code true} if the specified object is registered with * the dataset as a listener. Most applications won't need to call this * method, it exists mainly for use by unit testing code. * * @param listener the listener. * * @return A boolean. */ @Override public boolean hasListener(EventListener listener) { List list = Arrays.asList(this.listenerList.getListenerList()); return list.contains(listener); } /** * Notifies all registered listeners that the dial layer has changed. * The {@link DialLayerChangeEvent} provides information about the change. * * @param event information about the change to the axis. */ protected void notifyListeners(DialLayerChangeEvent event) { Object[] listeners = this.listenerList.getListenerList(); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == DialLayerChangeListener.class) { ((DialLayerChangeListener) listeners[i + 1]).dialLayerChanged( event); } } } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.listenerList = new EventListenerList(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/dial/ArcDialFrame.java000066400000000000000000000353701463604235500300720ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * ArcDialFrame.java * ----------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot.dial; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Arc2D; import java.awt.geom.Area; import java.awt.geom.GeneralPath; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import org.jfree.chart.HashUtils; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; /** * A standard frame for the {@link DialPlot} class. */ public class ArcDialFrame extends AbstractDialLayer implements DialFrame, Cloneable, PublicCloneable, Serializable { /** For serialization. */ static final long serialVersionUID = -4089176959553523499L; /** * The color used for the front of the panel. This field is transient * because it requires special handling for serialization. */ private transient Paint backgroundPaint; /** * The color used for the border around the window. This field is transient * because it requires special handling for serialization. */ private transient Paint foregroundPaint; /** * The stroke for drawing the frame outline. This field is transient * because it requires special handling for serialization. */ private transient Stroke stroke; /** * The start angle. */ private double startAngle; /** * The end angle. */ private double extent; /** The inner radius, relative to the framing rectangle. */ private double innerRadius; /** The outer radius, relative to the framing rectangle. */ private double outerRadius; /** * Creates a new instance of {@code ArcDialFrame} that spans * 180 degrees. */ public ArcDialFrame() { this(0, 180); } /** * Creates a new instance of {@code ArcDialFrame} that spans * the arc specified. * * @param startAngle the startAngle (in degrees). * @param extent the extent of the arc (in degrees, counter-clockwise). */ public ArcDialFrame(double startAngle, double extent) { this.backgroundPaint = Color.GRAY; this.foregroundPaint = new Color(100, 100, 150); this.stroke = new BasicStroke(2.0f); this.innerRadius = 0.25; this.outerRadius = 0.75; this.startAngle = startAngle; this.extent = extent; } /** * Returns the background paint (never {@code null}). * * @return The background paint. * * @see #setBackgroundPaint(Paint) */ public Paint getBackgroundPaint() { return this.backgroundPaint; } /** * Sets the background paint and sends a {@link DialLayerChangeEvent} to * all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getBackgroundPaint() */ public void setBackgroundPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.backgroundPaint = paint; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the foreground paint. * * @return The foreground paint (never {@code null}). * * @see #setForegroundPaint(Paint) */ public Paint getForegroundPaint() { return this.foregroundPaint; } /** * Sets the foreground paint and sends a {@link DialLayerChangeEvent} to * all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getForegroundPaint() */ public void setForegroundPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.foregroundPaint = paint; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the stroke. * * @return The stroke (never {@code null}). * * @see #setStroke(Stroke) */ public Stroke getStroke() { return this.stroke; } /** * Sets the stroke and sends a {@link DialLayerChangeEvent} to * all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getStroke() */ public void setStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.stroke = stroke; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the inner radius, relative to the framing rectangle. * * @return The inner radius. * * @see #setInnerRadius(double) */ public double getInnerRadius() { return this.innerRadius; } /** * Sets the inner radius and sends a {@link DialLayerChangeEvent} to * all registered listeners. * * @param radius the inner radius. * * @see #getInnerRadius() */ public void setInnerRadius(double radius) { if (radius < 0.0) { throw new IllegalArgumentException("Negative 'radius' argument."); } this.innerRadius = radius; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the outer radius, relative to the framing rectangle. * * @return The outer radius. * * @see #setOuterRadius(double) */ public double getOuterRadius() { return this.outerRadius; } /** * Sets the outer radius and sends a {@link DialLayerChangeEvent} to * all registered listeners. * * @param radius the outer radius. * * @see #getOuterRadius() */ public void setOuterRadius(double radius) { if (radius < 0.0) { throw new IllegalArgumentException("Negative 'radius' argument."); } this.outerRadius = radius; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the start angle. * * @return The start angle. * * @see #setStartAngle(double) */ public double getStartAngle() { return this.startAngle; } /** * Sets the start angle and sends a {@link DialLayerChangeEvent} to * all registered listeners. * * @param angle the angle. * * @see #getStartAngle() */ public void setStartAngle(double angle) { this.startAngle = angle; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the extent. * * @return The extent. * * @see #setExtent(double) */ public double getExtent() { return this.extent; } /** * Sets the extent and sends a {@link DialLayerChangeEvent} to * all registered listeners. * * @param extent the extent. * * @see #getExtent() */ public void setExtent(double extent) { this.extent = extent; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the shape for the window for this dial. Some dial layers will * request that their drawing be clipped within this window. * * @param frame the reference frame ({@code null} not permitted). * * @return The shape of the dial's window. */ @Override public Shape getWindow(Rectangle2D frame) { Rectangle2D innerFrame = DialPlot.rectangleByRadius(frame, this.innerRadius, this.innerRadius); Rectangle2D outerFrame = DialPlot.rectangleByRadius(frame, this.outerRadius, this.outerRadius); Arc2D inner = new Arc2D.Double(innerFrame, this.startAngle, this.extent, Arc2D.OPEN); Arc2D outer = new Arc2D.Double(outerFrame, this.startAngle + this.extent, -this.extent, Arc2D.OPEN); GeneralPath p = new GeneralPath(); Point2D point1 = inner.getStartPoint(); p.moveTo((float) point1.getX(), (float) point1.getY()); p.append(inner, true); p.append(outer, true); p.closePath(); return p; } /** * Returns the outer window. * * @param frame the frame. * * @return The outer window. */ protected Shape getOuterWindow(Rectangle2D frame) { double radiusMargin = 0.02; double angleMargin = 1.5; Rectangle2D innerFrame = DialPlot.rectangleByRadius(frame, this.innerRadius - radiusMargin, this.innerRadius - radiusMargin); Rectangle2D outerFrame = DialPlot.rectangleByRadius(frame, this.outerRadius + radiusMargin, this.outerRadius + radiusMargin); Arc2D inner = new Arc2D.Double(innerFrame, this.startAngle - angleMargin, this.extent + 2 * angleMargin, Arc2D.OPEN); Arc2D outer = new Arc2D.Double(outerFrame, this.startAngle + angleMargin + this.extent, -this.extent - 2 * angleMargin, Arc2D.OPEN); GeneralPath p = new GeneralPath(); Point2D point1 = inner.getStartPoint(); p.moveTo((float) point1.getX(), (float) point1.getY()); p.append(inner, true); p.append(outer, true); p.closePath(); return p; } /** * Draws the frame. * * @param g2 the graphics target. * @param plot the plot. * @param frame the dial's reference frame. * @param view the dial's view rectangle. */ @Override public void draw(Graphics2D g2, DialPlot plot, Rectangle2D frame, Rectangle2D view) { Shape window = getWindow(frame); Shape outerWindow = getOuterWindow(frame); Area area1 = new Area(outerWindow); Area area2 = new Area(window); area1.subtract(area2); g2.setPaint(Color.LIGHT_GRAY); g2.fill(area1); g2.setStroke(this.stroke); g2.setPaint(this.foregroundPaint); g2.draw(window); g2.draw(outerWindow); } /** * Returns {@code false} to indicate that this dial layer is not * clipped to the dial window. * * @return {@code false}. */ @Override public boolean isClippedToWindow() { return false; } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof ArcDialFrame)) { return false; } ArcDialFrame that = (ArcDialFrame) obj; if (!PaintUtils.equal(this.backgroundPaint, that.backgroundPaint)) { return false; } if (!PaintUtils.equal(this.foregroundPaint, that.foregroundPaint)) { return false; } if (this.startAngle != that.startAngle) { return false; } if (this.extent != that.extent) { return false; } if (this.innerRadius != that.innerRadius) { return false; } if (this.outerRadius != that.outerRadius) { return false; } if (!this.stroke.equals(that.stroke)) { return false; } return super.equals(obj); } /** * Returns a hash code for this instance. * * @return The hash code. */ @Override public int hashCode() { int result = 193; long temp = Double.doubleToLongBits(this.startAngle); result = 37 * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(this.extent); result = 37 * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(this.innerRadius); result = 37 * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(this.outerRadius); result = 37 * result + (int) (temp ^ (temp >>> 32)); result = 37 * result + HashUtils.hashCodeForPaint( this.backgroundPaint); result = 37 * result + HashUtils.hashCodeForPaint( this.foregroundPaint); result = 37 * result + this.stroke.hashCode(); return result; } /** * Returns a clone of this instance. * * @return A clone. * * @throws CloneNotSupportedException if any attribute of this instance * cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.backgroundPaint, stream); SerialUtils.writePaint(this.foregroundPaint, stream); SerialUtils.writeStroke(this.stroke, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.backgroundPaint = SerialUtils.readPaint(stream); this.foregroundPaint = SerialUtils.readPaint(stream); this.stroke = SerialUtils.readStroke(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/dial/DialBackground.java000066400000000000000000000176631463604235500304760ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * DialBackground.java * ------------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot.dial; import java.awt.Color; import java.awt.GradientPaint; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import org.jfree.chart.HashUtils; import org.jfree.chart.ui.GradientPaintTransformer; import org.jfree.chart.ui.StandardGradientPaintTransformer; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; /** * A regular dial layer that can be used to draw the background for a dial. */ public class DialBackground extends AbstractDialLayer implements DialLayer, Cloneable, PublicCloneable, Serializable { /** For serialization. */ static final long serialVersionUID = -9019069533317612375L; /** * The background paint. This field is transient because serialization * requires special handling. */ private transient Paint paint; /** * The transformer used when the background paint is an instance of * {@code GradientPaint}. */ private GradientPaintTransformer gradientPaintTransformer; /** * Creates a new instance of {@code DialBackground}. The * default background paint is {@code Color.WHITE}. */ public DialBackground() { this(Color.WHITE); } /** * Creates a new instance of {@code DialBackground}. * * @param paint the paint ({@code null} not permitted). * * @throws IllegalArgumentException if {@code Paint} is * {@code null}. */ public DialBackground(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.paint = paint; this.gradientPaintTransformer = new StandardGradientPaintTransformer(); } /** * Returns the paint used to fill the background. * * @return The paint (never {@code null}). * * @see #setPaint(Paint) */ public Paint getPaint() { return this.paint; } /** * Sets the paint for the dial background and sends a * {@link DialLayerChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getPaint() */ public void setPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.paint = paint; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the transformer used to adjust the coordinates of any * {@code GradientPaint} instance used for the background paint. * * @return The transformer (never {@code null}). * * @see #setGradientPaintTransformer(GradientPaintTransformer) */ public GradientPaintTransformer getGradientPaintTransformer() { return this.gradientPaintTransformer; } /** * Sets the transformer used to adjust the coordinates of any * {@code GradientPaint} instance used for the background paint, and * sends a {@link DialLayerChangeEvent} to all registered listeners. * * @param t the transformer ({@code null} not permitted). * * @see #getGradientPaintTransformer() */ public void setGradientPaintTransformer(GradientPaintTransformer t) { Args.nullNotPermitted(t, "t"); this.gradientPaintTransformer = t; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns {@code true} to indicate that this layer should be * clipped within the dial window. * * @return {@code true}. */ @Override public boolean isClippedToWindow() { return true; } /** * Draws the background to the specified graphics device. If the dial * frame specifies a window, the clipping region will already have been * set to this window before this method is called. * * @param g2 the graphics device ({@code null} not permitted). * @param plot the plot (ignored here). * @param frame the dial frame (ignored here). * @param view the view rectangle ({@code null} not permitted). */ @Override public void draw(Graphics2D g2, DialPlot plot, Rectangle2D frame, Rectangle2D view) { Paint p = this.paint; if (p instanceof GradientPaint) { p = this.gradientPaintTransformer.transform((GradientPaint) p, view); } g2.setPaint(p); g2.fill(view); } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof DialBackground)) { return false; } DialBackground that = (DialBackground) obj; if (!PaintUtils.equal(this.paint, that.paint)) { return false; } if (!this.gradientPaintTransformer.equals( that.gradientPaintTransformer)) { return false; } return super.equals(obj); } /** * Returns a hash code for this instance. * * @return The hash code. */ @Override public int hashCode() { int result = 193; result = 37 * result + HashUtils.hashCodeForPaint(this.paint); result = 37 * result + this.gradientPaintTransformer.hashCode(); return result; } /** * Returns a clone of this instance. * * @return The clone. * * @throws CloneNotSupportedException if some attribute of this instance * cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.paint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.paint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/dial/DialCap.java000066400000000000000000000234651463604235500271170ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------ * DialCap.java * ------------ * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot.dial; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Stroke; import java.awt.geom.Ellipse2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import org.jfree.chart.HashUtils; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; /** * A regular dial layer that can be used to draw a cap over the center of * the dial (the base of the dial pointer(s)). */ public class DialCap extends AbstractDialLayer implements DialLayer, Cloneable, PublicCloneable, Serializable { /** For serialization. */ static final long serialVersionUID = -2929484264982524463L; /** * The radius of the cap, as a percentage of the framing rectangle. */ private double radius; /** * The fill paint. This field is transient because it requires special * handling for serialization. */ private transient Paint fillPaint; /** * The paint used to draw the cap outline (this should never be * {@code null}). This field is transient because it requires * special handling for serialization. */ private transient Paint outlinePaint; /** * The stroke used to draw the cap outline (this should never be * {@code null}). This field is transient because it requires * special handling for serialization. */ private transient Stroke outlineStroke; /** * Creates a new instance of {@code StandardDialBackground}. The * default background paint is {@code Color.WHITE}. */ public DialCap() { this.radius = 0.05; this.fillPaint = Color.WHITE; this.outlinePaint = Color.BLACK; this.outlineStroke = new BasicStroke(2.0f); } /** * Returns the radius of the cap, as a percentage of the dial's framing * rectangle. * * @return The radius. * * @see #setRadius(double) */ public double getRadius() { return this.radius; } /** * Sets the radius of the cap, as a percentage of the dial's framing * rectangle, and sends a {@link DialLayerChangeEvent} to all registered * listeners. * * @param radius the radius (must be greater than zero). * * @see #getRadius() */ public void setRadius(double radius) { if (radius <= 0.0) { throw new IllegalArgumentException("Requires radius > 0.0."); } this.radius = radius; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the paint used to fill the cap. * * @return The paint (never {@code null}). * * @see #setFillPaint(Paint) */ public Paint getFillPaint() { return this.fillPaint; } /** * Sets the paint for the cap background and sends a * {@link DialLayerChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getFillPaint() */ public void setFillPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.fillPaint = paint; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the paint used to draw the outline of the cap. * * @return The paint (never {@code null}). * * @see #setOutlinePaint(Paint) */ public Paint getOutlinePaint() { return this.outlinePaint; } /** * Sets the paint used to draw the outline of the cap and sends a * {@link DialLayerChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getOutlinePaint() */ public void setOutlinePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.outlinePaint = paint; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the stroke used to draw the outline of the cap. * * @return The stroke (never {@code null}). * * @see #setOutlineStroke(Stroke) */ public Stroke getOutlineStroke() { return this.outlineStroke; } /** * Sets the stroke used to draw the outline of the cap and sends a * {@link DialLayerChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getOutlineStroke() */ public void setOutlineStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.outlineStroke = stroke; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns {@code true} to indicate that this layer should be * clipped within the dial window. * * @return {@code true}. */ @Override public boolean isClippedToWindow() { return true; } /** * Draws the background to the specified graphics device. If the dial * frame specifies a window, the clipping region will already have been * set to this window before this method is called. * * @param g2 the graphics device ({@code null} not permitted). * @param plot the plot (ignored here). * @param frame the dial frame (ignored here). * @param view the view rectangle ({@code null} not permitted). */ @Override public void draw(Graphics2D g2, DialPlot plot, Rectangle2D frame, Rectangle2D view) { g2.setPaint(this.fillPaint); Rectangle2D f = DialPlot.rectangleByRadius(frame, this.radius, this.radius); Ellipse2D e = new Ellipse2D.Double(f.getX(), f.getY(), f.getWidth(), f.getHeight()); g2.fill(e); g2.setPaint(this.outlinePaint); g2.setStroke(this.outlineStroke); g2.draw(e); } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof DialCap)) { return false; } DialCap that = (DialCap) obj; if (this.radius != that.radius) { return false; } if (!PaintUtils.equal(this.fillPaint, that.fillPaint)) { return false; } if (!PaintUtils.equal(this.outlinePaint, that.outlinePaint)) { return false; } if (!this.outlineStroke.equals(that.outlineStroke)) { return false; } return super.equals(obj); } /** * Returns a hash code for this instance. * * @return The hash code. */ @Override public int hashCode() { int result = 193; result = 37 * result + HashUtils.hashCodeForPaint(this.fillPaint); result = 37 * result + HashUtils.hashCodeForPaint( this.outlinePaint); result = 37 * result + this.outlineStroke.hashCode(); return result; } /** * Returns a clone of this instance. * * @return A clone. * * @throws CloneNotSupportedException if some attribute of the cap cannot * be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.fillPaint, stream); SerialUtils.writePaint(this.outlinePaint, stream); SerialUtils.writeStroke(this.outlineStroke, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.fillPaint = SerialUtils.readPaint(stream); this.outlinePaint = SerialUtils.readPaint(stream); this.outlineStroke = SerialUtils.readStroke(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/dial/DialFrame.java000066400000000000000000000043321463604235500274360ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * DialFrame.java * -------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot.dial; import java.awt.Shape; import java.awt.geom.Rectangle2D; import java.io.Serializable; /** * A dial frame is the face plate for a dial plot - it is always drawn last. * JFreeChart includes a couple of implementations of this interface * ({@link StandardDialFrame} and {@link ArcDialFrame}). *

* Classes that implement this interface should be {@link Serializable}, * otherwise chart serialization may fail. */ public interface DialFrame extends DialLayer { /** * Returns the shape of the viewing window for the dial, or * {@code null} if the dial is completely open. Other layers in the * plot will rely on their drawing to be clipped within this window. * * @param frame the reference frame for the dial. * * @return The window. */ Shape getWindow(Rectangle2D frame); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/dial/DialLayer.java000066400000000000000000000071561463604235500274670ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * DialLayer.java * -------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot.dial; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import java.util.EventListener; /** * A dial layer draws itself within a reference frame. The view frame is a * subset of the reference frame, and defines the area that is actually * visible. *

* Classes that implement this interface should be {@link Serializable}, * otherwise chart serialization may fail. */ public interface DialLayer { /** * Returns a flag that indicates whether or not the layer is visible. * * @return A boolean. */ boolean isVisible(); /** * Registers a listener with this layer, so that it receives notification * of changes to this layer. * * @param listener the listener. */ void addChangeListener(DialLayerChangeListener listener); /** * Deregisters a listener, so that it no longer receives notification of * changes to this layer. * * @param listener the listener. */ void removeChangeListener(DialLayerChangeListener listener); /** * Returns {@code true} if the specified listener is currently * registered with the this layer. * * @param listener the listener. * * @return A boolean. */ boolean hasListener(EventListener listener); /** * Returns {@code true} if the drawing should be clipped to the * dial window (which is defined by the {@link DialFrame}), and * {@code false} otherwise. * * @return A boolean. */ boolean isClippedToWindow(); /** * Draws the content of this layer. * * @param g2 the graphics target ({@code null} not permitted). * @param plot the plot (typically this should not be {@code null}, * but for a layer that doesn't need to reference the plot, it may * be permitted). * @param frame the reference frame for the dial's geometry * ({@code null} not permitted). This is typically larger than * the visible area of the dial (see the next parameter). * @param view the visible area for the dial ({@code null} not * permitted). */ void draw(Graphics2D g2, DialPlot plot, Rectangle2D frame, Rectangle2D view); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/dial/DialLayerChangeEvent.java000066400000000000000000000042321463604235500315670ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * DialLayerChangeEvent.java * ------------------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot.dial; import org.jfree.chart.event.ChartChangeEvent; /** * An event that can be forwarded to any {@link DialLayerChangeListener} to * signal a change to a {@link DialLayer}. */ public class DialLayerChangeEvent extends ChartChangeEvent { /** The dial layer that generated the event. */ private DialLayer layer; /** * Creates a new instance. * * @param layer the dial layer that generated the event. */ public DialLayerChangeEvent(DialLayer layer) { super(layer); this.layer = layer; } /** * Returns the layer that generated the event. * * @return The layer that generated the event. */ public DialLayer getDialLayer() { return this.layer; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/dial/DialLayerChangeListener.java000066400000000000000000000036361463604235500323020ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------- * DialLayerChangeListener.java * ---------------------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot.dial; import java.util.EventListener; /** * The interface via which an object is notified of changes to a * {@link DialLayer}. The {@link DialPlot} class listens for changes to its * layers in this way. */ public interface DialLayerChangeListener extends EventListener { /** * A call-back method for receiving notification of a change to a * {@link DialLayer}. * * @param event the event. */ void dialLayerChanged(DialLayerChangeEvent event); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/dial/DialPlot.java000066400000000000000000000570051463604235500273270ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------- * DialPlot.java * ------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot.dial; import java.awt.Graphics2D; import java.awt.Shape; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Iterator; import java.util.List; import java.util.Objects; import org.jfree.chart.JFreeChart; import org.jfree.chart.event.PlotChangeEvent; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.PlotState; import org.jfree.chart.util.ObjectList; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.Args; import org.jfree.data.general.DatasetChangeEvent; import org.jfree.data.general.ValueDataset; /** * A dial plot composed of user-definable layers. * The example shown here is generated by the {@code DialDemo2.java} * program included in the JFreeChart Demo Collection: *

* DialPlotSample.png */ public class DialPlot extends Plot implements DialLayerChangeListener { /** * The background layer (optional). */ private DialLayer background; /** * The needle cap (optional). */ private DialLayer cap; /** * The dial frame. */ private DialFrame dialFrame; /** * The dataset(s) for the dial plot. */ private ObjectList datasets; /** * The scale(s) for the dial plot. */ private ObjectList scales; /** Storage for keys that map datasets to scales. */ private ObjectList datasetToScaleMap; /** * The drawing layers for the dial plot. */ private List layers; /** * The pointer(s) for the dial. */ private List pointers; /** * The x-coordinate for the view window. */ private double viewX; /** * The y-coordinate for the view window. */ private double viewY; /** * The width of the view window, expressed as a percentage. */ private double viewW; /** * The height of the view window, expressed as a percentage. */ private double viewH; /** * Creates a new instance of {@code DialPlot}. */ public DialPlot() { this(null); } /** * Creates a new instance of {@code DialPlot}. * * @param dataset the dataset ({@code null} permitted). */ public DialPlot(ValueDataset dataset) { this.background = null; this.cap = null; this.dialFrame = new ArcDialFrame(); this.datasets = new ObjectList(); if (dataset != null) { setDataset(dataset); } this.scales = new ObjectList(); this.datasetToScaleMap = new ObjectList(); this.layers = new java.util.ArrayList(); this.pointers = new java.util.ArrayList(); this.viewX = 0.0; this.viewY = 0.0; this.viewW = 1.0; this.viewH = 1.0; } /** * Returns the background. * * @return The background (possibly {@code null}). * * @see #setBackground(DialLayer) */ public DialLayer getBackground() { return this.background; } /** * Sets the background layer and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param background the background layer ({@code null} permitted). * * @see #getBackground() */ public void setBackground(DialLayer background) { if (this.background != null) { this.background.removeChangeListener(this); } this.background = background; if (background != null) { background.addChangeListener(this); } fireChangeEvent(); } /** * Returns the cap. * * @return The cap (possibly {@code null}). * * @see #setCap(DialLayer) */ public DialLayer getCap() { return this.cap; } /** * Sets the cap and sends a {@link PlotChangeEvent} to all registered * listeners. * * @param cap the cap ({@code null} permitted). * * @see #getCap() */ public void setCap(DialLayer cap) { if (this.cap != null) { this.cap.removeChangeListener(this); } this.cap = cap; if (cap != null) { cap.addChangeListener(this); } fireChangeEvent(); } /** * Returns the dial's frame. * * @return The dial's frame (never {@code null}). * * @see #setDialFrame(DialFrame) */ public DialFrame getDialFrame() { return this.dialFrame; } /** * Sets the dial's frame and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param frame the frame ({@code null} not permitted). * * @see #getDialFrame() */ public void setDialFrame(DialFrame frame) { Args.nullNotPermitted(frame, "frame"); this.dialFrame.removeChangeListener(this); this.dialFrame = frame; frame.addChangeListener(this); fireChangeEvent(); } /** * Returns the x-coordinate of the viewing rectangle. This is specified * in the range 0.0 to 1.0, relative to the dial's framing rectangle. * * @return The x-coordinate of the viewing rectangle. * * @see #setView(double, double, double, double) */ public double getViewX() { return this.viewX; } /** * Returns the y-coordinate of the viewing rectangle. This is specified * in the range 0.0 to 1.0, relative to the dial's framing rectangle. * * @return The y-coordinate of the viewing rectangle. * * @see #setView(double, double, double, double) */ public double getViewY() { return this.viewY; } /** * Returns the width of the viewing rectangle. This is specified * in the range 0.0 to 1.0, relative to the dial's framing rectangle. * * @return The width of the viewing rectangle. * * @see #setView(double, double, double, double) */ public double getViewWidth() { return this.viewW; } /** * Returns the height of the viewing rectangle. This is specified * in the range 0.0 to 1.0, relative to the dial's framing rectangle. * * @return The height of the viewing rectangle. * * @see #setView(double, double, double, double) */ public double getViewHeight() { return this.viewH; } /** * Sets the viewing rectangle, relative to the dial's framing rectangle, * and sends a {@link PlotChangeEvent} to all registered listeners. * * @param x the x-coordinate (in the range 0.0 to 1.0). * @param y the y-coordinate (in the range 0.0 to 1.0). * @param w the width (in the range 0.0 to 1.0). * @param h the height (in the range 0.0 to 1.0). * * @see #getViewX() * @see #getViewY() * @see #getViewWidth() * @see #getViewHeight() */ public void setView(double x, double y, double w, double h) { this.viewX = x; this.viewY = y; this.viewW = w; this.viewH = h; fireChangeEvent(); } /** * Adds a layer to the plot and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param layer the layer ({@code null} not permitted). */ public void addLayer(DialLayer layer) { Args.nullNotPermitted(layer, "layer"); this.layers.add(layer); layer.addChangeListener(this); fireChangeEvent(); } /** * Returns the index for the specified layer. * * @param layer the layer ({@code null} not permitted). * * @return The layer index. */ public int getLayerIndex(DialLayer layer) { Args.nullNotPermitted(layer, "layer"); return this.layers.indexOf(layer); } /** * Removes the layer at the specified index and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param index the index. */ public void removeLayer(int index) { DialLayer layer = (DialLayer) this.layers.get(index); if (layer != null) { layer.removeChangeListener(this); } this.layers.remove(index); fireChangeEvent(); } /** * Removes the specified layer and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param layer the layer ({@code null} not permitted). */ public void removeLayer(DialLayer layer) { // defer argument checking removeLayer(getLayerIndex(layer)); } /** * Adds a pointer to the plot and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param pointer the pointer ({@code null} not permitted). */ public void addPointer(DialPointer pointer) { Args.nullNotPermitted(pointer, "pointer"); this.pointers.add(pointer); pointer.addChangeListener(this); fireChangeEvent(); } /** * Returns the index for the specified pointer. * * @param pointer the pointer ({@code null} not permitted). * * @return The pointer index. */ public int getPointerIndex(DialPointer pointer) { Args.nullNotPermitted(pointer, "pointer"); return this.pointers.indexOf(pointer); } /** * Removes the pointer at the specified index and sends a * {@link PlotChangeEvent} to all registered listeners. * * @param index the index. */ public void removePointer(int index) { DialPointer pointer = (DialPointer) this.pointers.get(index); if (pointer != null) { pointer.removeChangeListener(this); } this.pointers.remove(index); fireChangeEvent(); } /** * Removes the specified pointer and sends a {@link PlotChangeEvent} to all * registered listeners. * * @param pointer the pointer ({@code null} not permitted). */ public void removePointer(DialPointer pointer) { // defer argument checking removeLayer(getPointerIndex(pointer)); } /** * Returns the dial pointer that is associated with the specified * dataset, or {@code null}. * * @param datasetIndex the dataset index. * * @return The pointer. */ public DialPointer getPointerForDataset(int datasetIndex) { DialPointer result = null; Iterator iterator = this.pointers.iterator(); while (iterator.hasNext()) { DialPointer p = (DialPointer) iterator.next(); if (p.getDatasetIndex() == datasetIndex) { return p; } } return result; } /** * Returns the primary dataset for the plot. * * @return The primary dataset (possibly {@code null}). */ public ValueDataset getDataset() { return getDataset(0); } /** * Returns the dataset at the given index. * * @param index the dataset index. * * @return The dataset (possibly {@code null}). */ public ValueDataset getDataset(int index) { ValueDataset result = null; if (this.datasets.size() > index) { result = (ValueDataset) this.datasets.get(index); } return result; } /** * Sets the dataset for the plot, replacing the existing dataset, if there * is one, and sends a {@link PlotChangeEvent} to all registered * listeners. * * @param dataset the dataset ({@code null} permitted). */ public void setDataset(ValueDataset dataset) { setDataset(0, dataset); } /** * Sets a dataset for the plot. * * @param index the dataset index. * @param dataset the dataset ({@code null} permitted). */ public void setDataset(int index, ValueDataset dataset) { ValueDataset existing = (ValueDataset) this.datasets.get(index); if (existing != null) { existing.removeChangeListener(this); } this.datasets.set(index, dataset); if (dataset != null) { dataset.addChangeListener(this); } // send a dataset change event to self... DatasetChangeEvent event = new DatasetChangeEvent(this, dataset); datasetChanged(event); } /** * Returns the number of datasets. * * @return The number of datasets. */ public int getDatasetCount() { return this.datasets.size(); } /** * Draws the plot. This method is usually called by the {@link JFreeChart} * instance that manages the plot. * * @param g2 the graphics target. * @param area the area in which the plot should be drawn. * @param anchor the anchor point (typically the last point that the * mouse clicked on, {@code null} is permitted). * @param parentState the state for the parent plot (if any). * @param info used to collect plot rendering info ({@code null} * permitted). */ @Override public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, PlotState parentState, PlotRenderingInfo info) { Shape origClip = g2.getClip(); g2.setClip(area); // first, expand the viewing area into a drawing frame Rectangle2D frame = viewToFrame(area); // draw the background if there is one... if (this.background != null && this.background.isVisible()) { if (this.background.isClippedToWindow()) { Shape savedClip = g2.getClip(); g2.clip(this.dialFrame.getWindow(frame)); this.background.draw(g2, this, frame, area); g2.setClip(savedClip); } else { this.background.draw(g2, this, frame, area); } } Iterator iterator = this.layers.iterator(); while (iterator.hasNext()) { DialLayer current = (DialLayer) iterator.next(); if (current.isVisible()) { if (current.isClippedToWindow()) { Shape savedClip = g2.getClip(); g2.clip(this.dialFrame.getWindow(frame)); current.draw(g2, this, frame, area); g2.setClip(savedClip); } else { current.draw(g2, this, frame, area); } } } // draw the pointers iterator = this.pointers.iterator(); while (iterator.hasNext()) { DialPointer current = (DialPointer) iterator.next(); if (current.isVisible()) { if (current.isClippedToWindow()) { Shape savedClip = g2.getClip(); g2.clip(this.dialFrame.getWindow(frame)); current.draw(g2, this, frame, area); g2.setClip(savedClip); } else { current.draw(g2, this, frame, area); } } } // draw the cap if there is one... if (this.cap != null && this.cap.isVisible()) { if (this.cap.isClippedToWindow()) { Shape savedClip = g2.getClip(); g2.clip(this.dialFrame.getWindow(frame)); this.cap.draw(g2, this, frame, area); g2.setClip(savedClip); } else { this.cap.draw(g2, this, frame, area); } } if (this.dialFrame.isVisible()) { this.dialFrame.draw(g2, this, frame, area); } g2.setClip(origClip); } /** * Returns the frame surrounding the specified view rectangle. * * @param view the view rectangle ({@code null} not permitted). * * @return The frame rectangle. */ private Rectangle2D viewToFrame(Rectangle2D view) { double width = view.getWidth() / this.viewW; double height = view.getHeight() / this.viewH; double x = view.getX() - (width * this.viewX); double y = view.getY() - (height * this.viewY); return new Rectangle2D.Double(x, y, width, height); } /** * Returns the value from the specified dataset. * * @param datasetIndex the dataset index. * * @return The data value. */ public double getValue(int datasetIndex) { double result = Double.NaN; ValueDataset dataset = getDataset(datasetIndex); if (dataset != null) { Number n = dataset.getValue(); if (n != null) { result = n.doubleValue(); } } return result; } /** * Adds a dial scale to the plot and sends a {@link PlotChangeEvent} to * all registered listeners. * * @param index the scale index. * @param scale the scale ({@code null} not permitted). */ public void addScale(int index, DialScale scale) { Args.nullNotPermitted(scale, "scale"); DialScale existing = (DialScale) this.scales.get(index); if (existing != null) { removeLayer(existing); } this.layers.add(scale); this.scales.set(index, scale); scale.addChangeListener(this); fireChangeEvent(); } /** * Returns the scale at the given index. * * @param index the scale index. * * @return The scale (possibly {@code null}). */ public DialScale getScale(int index) { DialScale result = null; if (this.scales.size() > index) { result = (DialScale) this.scales.get(index); } return result; } /** * Maps a dataset to a particular scale. * * @param index the dataset index (zero-based). * @param scaleIndex the scale index (zero-based). */ public void mapDatasetToScale(int index, int scaleIndex) { this.datasetToScaleMap.set(index, scaleIndex); fireChangeEvent(); } /** * Returns the dial scale for a specific dataset. * * @param datasetIndex the dataset index. * * @return The dial scale. */ public DialScale getScaleForDataset(int datasetIndex) { DialScale result = (DialScale) this.scales.get(0); Integer scaleIndex = (Integer) this.datasetToScaleMap.get(datasetIndex); if (scaleIndex != null) { result = getScale(scaleIndex); } return result; } /** * A utility method that computes a rectangle using relative radius values. * * @param rect the reference rectangle ({@code null} not permitted). * @param radiusW the width radius (must be > 0.0) * @param radiusH the height radius. * * @return A new rectangle. */ public static Rectangle2D rectangleByRadius(Rectangle2D rect, double radiusW, double radiusH) { Args.nullNotPermitted(rect, "rect"); double x = rect.getCenterX(); double y = rect.getCenterY(); double w = rect.getWidth() * radiusW; double h = rect.getHeight() * radiusH; return new Rectangle2D.Double(x - w / 2.0, y - h / 2.0, w, h); } /** * Receives notification when a layer has changed, and responds by * forwarding a {@link PlotChangeEvent} to all registered listeners. * * @param event the event. */ @Override public void dialLayerChanged(DialLayerChangeEvent event) { fireChangeEvent(); } /** * Tests this {@code DialPlot} instance for equality with an * arbitrary object. The plot's dataset(s) is (are) not included in * the test. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof DialPlot)) { return false; } DialPlot that = (DialPlot) obj; if (!Objects.equals(this.background, that.background)) { return false; } if (!Objects.equals(this.cap, that.cap)) { return false; } if (!this.dialFrame.equals(that.dialFrame)) { return false; } if (this.viewX != that.viewX) { return false; } if (this.viewY != that.viewY) { return false; } if (this.viewW != that.viewW) { return false; } if (this.viewH != that.viewH) { return false; } if (!this.layers.equals(that.layers)) { return false; } if (!this.pointers.equals(that.pointers)) { return false; } return super.equals(obj); } /** * Returns a hash code for this instance. * * @return The hash code. */ @Override public int hashCode() { int result = 193; result = 37 * result + ObjectUtils.hashCode(this.background); result = 37 * result + ObjectUtils.hashCode(this.cap); result = 37 * result + this.dialFrame.hashCode(); long temp = Double.doubleToLongBits(this.viewX); result = 37 * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(this.viewY); result = 37 * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(this.viewW); result = 37 * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(this.viewH); result = 37 * result + (int) (temp ^ (temp >>> 32)); return result; } /** * Returns the plot type. * * @return {@code "DialPlot"} */ @Override public String getPlotType() { return "DialPlot"; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/dial/DialPointer.java000066400000000000000000000437401463604235500300320ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * DialPointer.java * ---------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot.dial; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Stroke; import java.awt.geom.Arc2D; import java.awt.geom.GeneralPath; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import org.jfree.chart.HashUtils; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; /** * A base class for the pointer in a {@link DialPlot}. */ public abstract class DialPointer extends AbstractDialLayer implements DialLayer, Cloneable, PublicCloneable, Serializable { /** The needle radius. */ double radius; /** * The dataset index for the needle. */ int datasetIndex; /** * Creates a new {@code DialPointer} instance. */ protected DialPointer() { this(0); } /** * Creates a new pointer for the specified dataset. * * @param datasetIndex the dataset index. */ protected DialPointer(int datasetIndex) { this.radius = 0.9; this.datasetIndex = datasetIndex; } /** * Returns the dataset index that the pointer maps to. * * @return The dataset index. * * @see #getDatasetIndex() */ public int getDatasetIndex() { return this.datasetIndex; } /** * Sets the dataset index for the pointer and sends a * {@link DialLayerChangeEvent} to all registered listeners. * * @param index the index. * * @see #getDatasetIndex() */ public void setDatasetIndex(int index) { this.datasetIndex = index; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the radius of the pointer, as a percentage of the dial's * framing rectangle. * * @return The radius. * * @see #setRadius(double) */ public double getRadius() { return this.radius; } /** * Sets the radius of the pointer and sends a * {@link DialLayerChangeEvent} to all registered listeners. * * @param radius the radius. * * @see #getRadius() */ public void setRadius(double radius) { this.radius = radius; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns {@code true} to indicate that this layer should be * clipped within the dial window. * * @return {@code true}. */ @Override public boolean isClippedToWindow() { return true; } /** * Checks this instance for equality with an arbitrary object. * * @param obj the object ({@code null} not permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof DialPointer)) { return false; } DialPointer that = (DialPointer) obj; if (this.datasetIndex != that.datasetIndex) { return false; } if (this.radius != that.radius) { return false; } return super.equals(obj); } /** * Returns a hash code. * * @return A hash code. */ @Override public int hashCode() { int result = 23; result = HashUtils.hashCode(result, this.radius); return result; } /** * Returns a clone of the pointer. * * @return a clone. * * @throws CloneNotSupportedException if one of the attributes cannot * be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * A dial pointer that draws a thin line (like a pin). */ public static class Pin extends DialPointer { /** For serialization. */ static final long serialVersionUID = -8445860485367689750L; /** The paint. */ private transient Paint paint; /** The stroke. */ private transient Stroke stroke; /** * Creates a new instance. */ public Pin() { this(0); } /** * Creates a new instance. * * @param datasetIndex the dataset index. */ public Pin(int datasetIndex) { super(datasetIndex); this.paint = Color.RED; this.stroke = new BasicStroke(3.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL); } /** * Returns the paint. * * @return The paint (never {@code null}). * * @see #setPaint(Paint) */ public Paint getPaint() { return this.paint; } /** * Sets the paint and sends a {@link DialLayerChangeEvent} to all * registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getPaint() */ public void setPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.paint = paint; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the stroke. * * @return The stroke (never {@code null}). * * @see #setStroke(Stroke) */ public Stroke getStroke() { return this.stroke; } /** * Sets the stroke and sends a {@link DialLayerChangeEvent} to all * registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getStroke() */ public void setStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.stroke = stroke; notifyListeners(new DialLayerChangeEvent(this)); } /** * Draws the pointer. * * @param g2 the graphics target. * @param plot the plot. * @param frame the dial's reference frame. * @param view the dial's view. */ @Override public void draw(Graphics2D g2, DialPlot plot, Rectangle2D frame, Rectangle2D view) { g2.setPaint(this.paint); g2.setStroke(this.stroke); Rectangle2D arcRect = DialPlot.rectangleByRadius(frame, this.radius, this.radius); double value = plot.getValue(this.datasetIndex); DialScale scale = plot.getScaleForDataset(this.datasetIndex); double angle = scale.valueToAngle(value); Arc2D arc = new Arc2D.Double(arcRect, angle, 0, Arc2D.OPEN); Point2D pt = arc.getEndPoint(); Line2D line = new Line2D.Double(frame.getCenterX(), frame.getCenterY(), pt.getX(), pt.getY()); g2.draw(line); } /** * Tests this pointer for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof DialPointer.Pin)) { return false; } DialPointer.Pin that = (DialPointer.Pin) obj; if (!PaintUtils.equal(this.paint, that.paint)) { return false; } if (!this.stroke.equals(that.stroke)) { return false; } return super.equals(obj); } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = super.hashCode(); result = HashUtils.hashCode(result, this.paint); result = HashUtils.hashCode(result, this.stroke); return result; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.paint, stream); SerialUtils.writeStroke(this.stroke, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.paint = SerialUtils.readPaint(stream); this.stroke = SerialUtils.readStroke(stream); } } /** * A dial pointer. */ public static class Pointer extends DialPointer { /** For serialization. */ static final long serialVersionUID = -4180500011963176960L; /** * The radius that defines the width of the pointer at the base. */ private double widthRadius; /** * The fill paint. */ private transient Paint fillPaint; /** * The outline paint. */ private transient Paint outlinePaint; /** * Creates a new instance. */ public Pointer() { this(0); } /** * Creates a new instance. * * @param datasetIndex the dataset index. */ public Pointer(int datasetIndex) { super(datasetIndex); this.widthRadius = 0.05; this.fillPaint = Color.GRAY; this.outlinePaint = Color.BLACK; } /** * Returns the width radius. * * @return The width radius. * * @see #setWidthRadius(double) */ public double getWidthRadius() { return this.widthRadius; } /** * Sets the width radius and sends a {@link DialLayerChangeEvent} to * all registered listeners. * * @param radius the radius * * @see #getWidthRadius() */ public void setWidthRadius(double radius) { this.widthRadius = radius; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the fill paint. * * @return The paint (never {@code null}). * * @see #setFillPaint(Paint) */ public Paint getFillPaint() { return this.fillPaint; } /** * Sets the fill paint and sends a {@link DialLayerChangeEvent} to all * registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getFillPaint() */ public void setFillPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.fillPaint = paint; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the outline paint. * * @return The paint (never {@code null}). * * @see #setOutlinePaint(Paint) */ public Paint getOutlinePaint() { return this.outlinePaint; } /** * Sets the outline paint and sends a {@link DialLayerChangeEvent} to * all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getOutlinePaint() */ public void setOutlinePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.outlinePaint = paint; notifyListeners(new DialLayerChangeEvent(this)); } /** * Draws the pointer. * * @param g2 the graphics target. * @param plot the plot. * @param frame the dial's reference frame. * @param view the dial's view. */ @Override public void draw(Graphics2D g2, DialPlot plot, Rectangle2D frame, Rectangle2D view) { g2.setPaint(Color.BLUE); g2.setStroke(new BasicStroke(1.0f)); Rectangle2D lengthRect = DialPlot.rectangleByRadius(frame, this.radius, this.radius); Rectangle2D widthRect = DialPlot.rectangleByRadius(frame, this.widthRadius, this.widthRadius); double value = plot.getValue(this.datasetIndex); DialScale scale = plot.getScaleForDataset(this.datasetIndex); double angle = scale.valueToAngle(value); Arc2D arc1 = new Arc2D.Double(lengthRect, angle, 0, Arc2D.OPEN); Point2D pt1 = arc1.getEndPoint(); Arc2D arc2 = new Arc2D.Double(widthRect, angle - 90.0, 180.0, Arc2D.OPEN); Point2D pt2 = arc2.getStartPoint(); Point2D pt3 = arc2.getEndPoint(); Arc2D arc3 = new Arc2D.Double(widthRect, angle - 180.0, 0.0, Arc2D.OPEN); Point2D pt4 = arc3.getStartPoint(); GeneralPath gp = new GeneralPath(); gp.moveTo((float) pt1.getX(), (float) pt1.getY()); gp.lineTo((float) pt2.getX(), (float) pt2.getY()); gp.lineTo((float) pt4.getX(), (float) pt4.getY()); gp.lineTo((float) pt3.getX(), (float) pt3.getY()); gp.closePath(); g2.setPaint(this.fillPaint); g2.fill(gp); g2.setPaint(this.outlinePaint); Line2D line = new Line2D.Double(frame.getCenterX(), frame.getCenterY(), pt1.getX(), pt1.getY()); g2.draw(line); line.setLine(pt2, pt3); g2.draw(line); line.setLine(pt3, pt1); g2.draw(line); line.setLine(pt2, pt1); g2.draw(line); line.setLine(pt2, pt4); g2.draw(line); line.setLine(pt3, pt4); g2.draw(line); } /** * Tests this pointer for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof DialPointer.Pointer)) { return false; } DialPointer.Pointer that = (DialPointer.Pointer) obj; if (this.widthRadius != that.widthRadius) { return false; } if (!PaintUtils.equal(this.fillPaint, that.fillPaint)) { return false; } if (!PaintUtils.equal(this.outlinePaint, that.outlinePaint)) { return false; } return super.equals(obj); } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = super.hashCode(); result = HashUtils.hashCode(result, this.widthRadius); result = HashUtils.hashCode(result, this.fillPaint); result = HashUtils.hashCode(result, this.outlinePaint); return result; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.fillPaint, stream); SerialUtils.writePaint(this.outlinePaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.fillPaint = SerialUtils.readPaint(stream); this.outlinePaint = SerialUtils.readPaint(stream); } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/dial/DialScale.java000066400000000000000000000041151463604235500274320ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * DialScale.java * -------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot.dial; /** * A dial scale is a specialised layer that has the ability to convert data * values into angles. */ public interface DialScale extends DialLayer { /** * Converts a data value to an angle (in degrees, using the same * specification as Java's Arc2D class). * * @param value the data value. * * @return The angle in degrees. * * @see #angleToValue(double) */ double valueToAngle(double value); /** * Converts an angle (in degrees) to a data value. * * @param angle the angle (in degrees). * * @return The data value. * * @see #valueToAngle(double) */ double angleToValue(double angle); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/dial/DialTextAnnotation.java000066400000000000000000000264151463604235500313710ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * DialTextAnnotation.java * ----------------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot.dial; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.geom.Arc2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import org.jfree.chart.HashUtils; import org.jfree.chart.text.TextUtils; import org.jfree.chart.ui.TextAnchor; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; /** * A text annotation for a {@link DialPlot}. */ public class DialTextAnnotation extends AbstractDialLayer implements DialLayer, Cloneable, PublicCloneable, Serializable { /** For serialization. */ static final long serialVersionUID = 3065267524054428071L; /** The label text. */ private String label; /** The font. */ private Font font; /** * The paint for the label. This field is transient because it requires * special handling for serialization. */ private transient Paint paint; /** The angle that defines the anchor point for the annotation. */ private double angle; /** The radius that defines the anchor point for the annotation. */ private double radius; /** The text anchor to be aligned to the annotation's anchor point. */ private TextAnchor anchor; /** * Creates a new instance of {@code DialTextAnnotation}. * * @param label the label ({@code null} not permitted). */ public DialTextAnnotation(String label) { Args.nullNotPermitted(label, "label"); this.angle = -90.0; this.radius = 0.3; this.font = new Font("Dialog", Font.BOLD, 14); this.paint = Color.BLACK; this.label = label; this.anchor = TextAnchor.TOP_CENTER; } /** * Returns the label text. * * @return The label text (never {@code null}). * * @see #setLabel(String) */ public String getLabel() { return this.label; } /** * Sets the label and sends a {@link DialLayerChangeEvent} to all * registered listeners. * * @param label the label ({@code null} not permitted). * * @see #getLabel() */ public void setLabel(String label) { Args.nullNotPermitted(label, "label"); this.label = label; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the font used to display the label. * * @return The font (never {@code null}). * * @see #setFont(Font) */ public Font getFont() { return this.font; } /** * Sets the font used to display the label and sends a * {@link DialLayerChangeEvent} to all registered listeners. * * @param font the font ({@code null} not permitted). * * @see #getFont() */ public void setFont(Font font) { Args.nullNotPermitted(font, "font"); this.font = font; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the paint used to display the label. * * @return The paint (never {@code null}). * * @see #setPaint(Paint) */ public Paint getPaint() { return this.paint; } /** * Sets the paint used to display the label and sends a * {@link DialLayerChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getPaint() */ public void setPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.paint = paint; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the angle used to calculate the anchor point. * * @return The angle (in degrees). * * @see #setAngle(double) * @see #getRadius() */ public double getAngle() { return this.angle; } /** * Sets the angle used to calculate the anchor point and sends a * {@link DialLayerChangeEvent} to all registered listeners. * * @param angle the angle (in degrees). * * @see #getAngle() * @see #setRadius(double) */ public void setAngle(double angle) { this.angle = angle; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the radius used to calculate the anchor point. This is * specified as a percentage relative to the dial's framing rectangle. * * @return The radius. * * @see #setRadius(double) * @see #getAngle() */ public double getRadius() { return this.radius; } /** * Sets the radius used to calculate the anchor point and sends a * {@link DialLayerChangeEvent} to all registered listeners. * * @param radius the radius (as a percentage of the dial's framing * rectangle). * * @see #getRadius() * @see #setAngle(double) */ public void setRadius(double radius) { if (radius < 0.0) { throw new IllegalArgumentException( "The 'radius' cannot be negative."); } this.radius = radius; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the text anchor point that will be aligned to the position * specified by {@link #getAngle()} and {@link #getRadius()}. * * @return The anchor point. * * @see #setAnchor(TextAnchor) */ public TextAnchor getAnchor() { return this.anchor; } /** * Sets the text anchor point and sends a {@link DialLayerChangeEvent} to * all registered listeners. * * @param anchor the anchor point ({@code null} not permitted). * * @see #getAnchor() */ public void setAnchor(TextAnchor anchor) { Args.nullNotPermitted(anchor, "anchor"); this.anchor = anchor; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns {@code true} to indicate that this layer should be * clipped within the dial window. * * @return {@code true}. */ @Override public boolean isClippedToWindow() { return true; } /** * Draws the background to the specified graphics device. If the dial * frame specifies a window, the clipping region will already have been * set to this window before this method is called. * * @param g2 the graphics device ({@code null} not permitted). * @param plot the plot (ignored here). * @param frame the dial frame (ignored here). * @param view the view rectangle ({@code null} not permitted). */ @Override public void draw(Graphics2D g2, DialPlot plot, Rectangle2D frame, Rectangle2D view) { // work out the anchor point Rectangle2D f = DialPlot.rectangleByRadius(frame, this.radius, this.radius); Arc2D arc = new Arc2D.Double(f, this.angle, 0.0, Arc2D.OPEN); Point2D pt = arc.getStartPoint(); g2.setPaint(this.paint); g2.setFont(this.font); TextUtils.drawAlignedString(this.label, g2, (float) pt.getX(), (float) pt.getY(), this.anchor); } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof DialTextAnnotation)) { return false; } DialTextAnnotation that = (DialTextAnnotation) obj; if (!this.label.equals(that.label)) { return false; } if (!this.font.equals(that.font)) { return false; } if (!PaintUtils.equal(this.paint, that.paint)) { return false; } if (this.radius != that.radius) { return false; } if (this.angle != that.angle) { return false; } if (!this.anchor.equals(that.anchor)) { return false; } return super.equals(obj); } /** * Returns a hash code for this instance. * * @return The hash code. */ @Override public int hashCode() { int result = 193; result = 37 * result + HashUtils.hashCodeForPaint(this.paint); result = 37 * result + this.font.hashCode(); result = 37 * result + this.label.hashCode(); result = 37 * result + this.anchor.hashCode(); long temp = Double.doubleToLongBits(this.angle); result = 37 * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(this.radius); result = 37 * result + (int) (temp ^ (temp >>> 32)); return result; } /** * Returns a clone of this instance. * * @return The clone. * * @throws CloneNotSupportedException if some attribute of this instance * cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.paint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.paint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/dial/DialValueIndicator.java000066400000000000000000000532321463604235500313200ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * DialValueIndicator.java * ----------------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot.dial; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Arc2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.Objects; import org.jfree.chart.HashUtils; import org.jfree.chart.text.TextUtils; import org.jfree.chart.ui.RectangleAnchor; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.ui.Size2D; import org.jfree.chart.ui.TextAnchor; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; /** * A value indicator for a {@link DialPlot}. */ public class DialValueIndicator extends AbstractDialLayer implements DialLayer, Cloneable, PublicCloneable, Serializable { /** For serialization. */ static final long serialVersionUID = 803094354130942585L; /** The dataset index. */ private int datasetIndex; /** The angle that defines the anchor point. */ private double angle; /** The radius that defines the anchor point. */ private double radius; /** The frame anchor. */ private RectangleAnchor frameAnchor; /** The template value. */ private Number templateValue; /** * A data value that will be formatted to determine the maximum size of * the indicator bounds. If this is null, the indicator bounds can grow * as large as necessary to contain the actual data value. */ private Number maxTemplateValue; /** The formatter. */ private NumberFormat formatter; /** The font. */ private Font font; /** The paint. */ private transient Paint paint; /** The background paint. */ private transient Paint backgroundPaint; /** The outline stroke. */ private transient Stroke outlineStroke; /** The outline paint. */ private transient Paint outlinePaint; /** The insets. */ private RectangleInsets insets; /** The value anchor. */ private RectangleAnchor valueAnchor; /** The text anchor for displaying the value. */ private TextAnchor textAnchor; /** * Creates a new instance of {@code DialValueIndicator}. */ public DialValueIndicator() { this(0); } /** * Creates a new instance of {@code DialValueIndicator}. * * @param datasetIndex the dataset index. */ public DialValueIndicator(int datasetIndex) { this.datasetIndex = datasetIndex; this.angle = -90.0; this.radius = 0.3; this.frameAnchor = RectangleAnchor.CENTER; this.templateValue = 100.0; this.maxTemplateValue = null; this.formatter = new DecimalFormat("0.0"); this.font = new Font("Dialog", Font.BOLD, 14); this.paint = Color.BLACK; this.backgroundPaint = Color.WHITE; this.outlineStroke = new BasicStroke(1.0f); this.outlinePaint = Color.BLUE; this.insets = new RectangleInsets(4, 4, 4, 4); this.valueAnchor = RectangleAnchor.RIGHT; this.textAnchor = TextAnchor.CENTER_RIGHT; } /** * Returns the index of the dataset from which this indicator fetches its * current value. * * @return The dataset index. * * @see #setDatasetIndex(int) */ public int getDatasetIndex() { return this.datasetIndex; } /** * Sets the dataset index and sends a {@link DialLayerChangeEvent} to all * registered listeners. * * @param index the index. * * @see #getDatasetIndex() */ public void setDatasetIndex(int index) { this.datasetIndex = index; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the angle for the anchor point. The angle is specified in * degrees using the same orientation as Java's {@code Arc2D} class. * * @return The angle (in degrees). * * @see #setAngle(double) */ public double getAngle() { return this.angle; } /** * Sets the angle for the anchor point and sends a * {@link DialLayerChangeEvent} to all registered listeners. * * @param angle the angle (in degrees). * * @see #getAngle() */ public void setAngle(double angle) { this.angle = angle; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the radius. * * @return The radius. * * @see #setRadius(double) */ public double getRadius() { return this.radius; } /** * Sets the radius and sends a {@link DialLayerChangeEvent} to all * registered listeners. * * @param radius the radius. * * @see #getRadius() */ public void setRadius(double radius) { this.radius = radius; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the frame anchor. * * @return The frame anchor. * * @see #setFrameAnchor(RectangleAnchor) */ public RectangleAnchor getFrameAnchor() { return this.frameAnchor; } /** * Sets the frame anchor and sends a {@link DialLayerChangeEvent} to all * registered listeners. * * @param anchor the anchor ({@code null} not permitted). * * @see #getFrameAnchor() */ public void setFrameAnchor(RectangleAnchor anchor) { Args.nullNotPermitted(anchor, "anchor"); this.frameAnchor = anchor; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the template value. * * @return The template value (never {@code null}). * * @see #setTemplateValue(Number) */ public Number getTemplateValue() { return this.templateValue; } /** * Sets the template value and sends a {@link DialLayerChangeEvent} to * all registered listeners. * * @param value the value ({@code null} not permitted). * * @see #setTemplateValue(Number) */ public void setTemplateValue(Number value) { Args.nullNotPermitted(value, "value"); this.templateValue = value; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the template value for the maximum size of the indicator * bounds. * * @return The template value (possibly {@code null}). * * @see #setMaxTemplateValue(java.lang.Number) */ public Number getMaxTemplateValue() { return this.maxTemplateValue; } /** * Sets the template value for the maximum size of the indicator bounds * and sends a {@link DialLayerChangeEvent} to all registered listeners. * * @param value the value ({@code null} permitted). * * @see #getMaxTemplateValue() */ public void setMaxTemplateValue(Number value) { this.maxTemplateValue = value; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the formatter used to format the value. * * @return The formatter (never {@code null}). * * @see #setNumberFormat(NumberFormat) */ public NumberFormat getNumberFormat() { return this.formatter; } /** * Sets the formatter used to format the value and sends a * {@link DialLayerChangeEvent} to all registered listeners. * * @param formatter the formatter ({@code null} not permitted). * * @see #getNumberFormat() */ public void setNumberFormat(NumberFormat formatter) { Args.nullNotPermitted(formatter, "formatter"); this.formatter = formatter; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the font. * * @return The font (never {@code null}). * * @see #getFont() */ public Font getFont() { return this.font; } /** * Sets the font and sends a {@link DialLayerChangeEvent} to all registered * listeners. * * @param font the font ({@code null} not permitted). */ public void setFont(Font font) { Args.nullNotPermitted(font, "font"); this.font = font; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the paint. * * @return The paint (never {@code null}). * * @see #setPaint(Paint) */ public Paint getPaint() { return this.paint; } /** * Sets the paint and sends a {@link DialLayerChangeEvent} to all * registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getPaint() */ public void setPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.paint = paint; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the background paint. * * @return The background paint. * * @see #setBackgroundPaint(Paint) */ public Paint getBackgroundPaint() { return this.backgroundPaint; } /** * Sets the background paint and sends a {@link DialLayerChangeEvent} to * all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getBackgroundPaint() */ public void setBackgroundPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.backgroundPaint = paint; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the outline stroke. * * @return The outline stroke (never {@code null}). * * @see #setOutlineStroke(Stroke) */ public Stroke getOutlineStroke() { return this.outlineStroke; } /** * Sets the outline stroke and sends a {@link DialLayerChangeEvent} to * all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getOutlineStroke() */ public void setOutlineStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.outlineStroke = stroke; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the outline paint. * * @return The outline paint (never {@code null}). * * @see #setOutlinePaint(Paint) */ public Paint getOutlinePaint() { return this.outlinePaint; } /** * Sets the outline paint and sends a {@link DialLayerChangeEvent} to all * registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getOutlinePaint() */ public void setOutlinePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.outlinePaint = paint; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the insets. * * @return The insets (never {@code null}). * * @see #setInsets(RectangleInsets) */ public RectangleInsets getInsets() { return this.insets; } /** * Sets the insets and sends a {@link DialLayerChangeEvent} to all * registered listeners. * * @param insets the insets ({@code null} not permitted). * * @see #getInsets() */ public void setInsets(RectangleInsets insets) { Args.nullNotPermitted(insets, "insets"); this.insets = insets; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the value anchor. * * @return The value anchor (never {@code null}). * * @see #setValueAnchor(RectangleAnchor) */ public RectangleAnchor getValueAnchor() { return this.valueAnchor; } /** * Sets the value anchor and sends a {@link DialLayerChangeEvent} to all * registered listeners. * * @param anchor the anchor ({@code null} not permitted). * * @see #getValueAnchor() */ public void setValueAnchor(RectangleAnchor anchor) { Args.nullNotPermitted(anchor, "anchor"); this.valueAnchor = anchor; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the text anchor. * * @return The text anchor (never {@code null}). * * @see #setTextAnchor(TextAnchor) */ public TextAnchor getTextAnchor() { return this.textAnchor; } /** * Sets the text anchor and sends a {@link DialLayerChangeEvent} to all * registered listeners. * * @param anchor the anchor ({@code null} not permitted). * * @see #getTextAnchor() */ public void setTextAnchor(TextAnchor anchor) { Args.nullNotPermitted(anchor, "anchor"); this.textAnchor = anchor; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns {@code true} to indicate that this layer should be * clipped within the dial window. * * @return {@code true}. */ @Override public boolean isClippedToWindow() { return true; } /** * Draws the background to the specified graphics device. If the dial * frame specifies a window, the clipping region will already have been * set to this window before this method is called. * * @param g2 the graphics device ({@code null} not permitted). * @param plot the plot (ignored here). * @param frame the dial frame (ignored here). * @param view the view rectangle ({@code null} not permitted). */ @Override public void draw(Graphics2D g2, DialPlot plot, Rectangle2D frame, Rectangle2D view) { // work out the anchor point Rectangle2D f = DialPlot.rectangleByRadius(frame, this.radius, this.radius); Arc2D arc = new Arc2D.Double(f, this.angle, 0.0, Arc2D.OPEN); Point2D pt = arc.getStartPoint(); // the indicator bounds is calculated from the templateValue (which // determines the minimum size), the maxTemplateValue (which, if // specified, provides a maximum size) and the actual value FontMetrics fm = g2.getFontMetrics(this.font); double value = plot.getValue(this.datasetIndex); String valueStr = this.formatter.format(value); Rectangle2D valueBounds = TextUtils.getTextBounds(valueStr, g2, fm); // calculate the bounds of the template value String s = this.formatter.format(this.templateValue); Rectangle2D tb = TextUtils.getTextBounds(s, g2, fm); double minW = tb.getWidth(); double minH = tb.getHeight(); double maxW = Double.MAX_VALUE; double maxH = Double.MAX_VALUE; if (this.maxTemplateValue != null) { s = this.formatter.format(this.maxTemplateValue); tb = TextUtils.getTextBounds(s, g2, fm); maxW = Math.max(tb.getWidth(), minW); maxH = Math.max(tb.getHeight(), minH); } double w = fixToRange(valueBounds.getWidth(), minW, maxW); double h = fixToRange(valueBounds.getHeight(), minH, maxH); // align this rectangle to the frameAnchor Rectangle2D bounds = RectangleAnchor.createRectangle(new Size2D(w, h), pt.getX(), pt.getY(), this.frameAnchor); // add the insets Rectangle2D fb = this.insets.createOutsetRectangle(bounds); // draw the background g2.setPaint(this.backgroundPaint); g2.fill(fb); // draw the border g2.setStroke(this.outlineStroke); g2.setPaint(this.outlinePaint); g2.draw(fb); // now find the text anchor point Shape savedClip = g2.getClip(); g2.clip(fb); Point2D pt2 = this.valueAnchor.getAnchorPoint(bounds); g2.setPaint(this.paint); g2.setFont(this.font); TextUtils.drawAlignedString(valueStr, g2, (float) pt2.getX(), (float) pt2.getY(), this.textAnchor); g2.setClip(savedClip); } /** * A utility method that adjusts a value, if necessary, to be within a * specified range. * * @param x the value. * @param minX the minimum value in the range. * @param maxX the maximum value in the range. * * @return The adjusted value. */ private double fixToRange(double x, double minX, double maxX) { if (minX > maxX) { throw new IllegalArgumentException("Requires 'minX' <= 'maxX'."); } if (x < minX) { return minX; } else if (x > maxX) { return maxX; } else { return x; } } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof DialValueIndicator)) { return false; } DialValueIndicator that = (DialValueIndicator) obj; if (this.datasetIndex != that.datasetIndex) { return false; } if (this.angle != that.angle) { return false; } if (this.radius != that.radius) { return false; } if (!this.frameAnchor.equals(that.frameAnchor)) { return false; } if (!this.templateValue.equals(that.templateValue)) { return false; } if (!Objects.equals(this.maxTemplateValue, that.maxTemplateValue)) { return false; } if (!this.font.equals(that.font)) { return false; } if (!PaintUtils.equal(this.paint, that.paint)) { return false; } if (!PaintUtils.equal(this.backgroundPaint, that.backgroundPaint)) { return false; } if (!this.outlineStroke.equals(that.outlineStroke)) { return false; } if (!PaintUtils.equal(this.outlinePaint, that.outlinePaint)) { return false; } if (!this.insets.equals(that.insets)) { return false; } if (!this.valueAnchor.equals(that.valueAnchor)) { return false; } if (!this.textAnchor.equals(that.textAnchor)) { return false; } return super.equals(obj); } /** * Returns a hash code for this instance. * * @return The hash code. */ @Override public int hashCode() { int result = 193; result = 37 * result + HashUtils.hashCodeForPaint(this.paint); result = 37 * result + HashUtils.hashCodeForPaint( this.backgroundPaint); result = 37 * result + HashUtils.hashCodeForPaint( this.outlinePaint); result = 37 * result + this.outlineStroke.hashCode(); return result; } /** * Returns a clone of this instance. * * @return The clone. * * @throws CloneNotSupportedException if some attribute of this instance * cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.paint, stream); SerialUtils.writePaint(this.backgroundPaint, stream); SerialUtils.writePaint(this.outlinePaint, stream); SerialUtils.writeStroke(this.outlineStroke, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.paint = SerialUtils.readPaint(stream); this.backgroundPaint = SerialUtils.readPaint(stream); this.outlinePaint = SerialUtils.readPaint(stream); this.outlineStroke = SerialUtils.readStroke(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/dial/StandardDialFrame.java000066400000000000000000000245701463604235500311250ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * StandardDialFrame.java * ---------------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot.dial; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Area; import java.awt.geom.Ellipse2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import org.jfree.chart.HashUtils; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; /** * A simple circular frame for the {@link DialPlot} class. */ public class StandardDialFrame extends AbstractDialLayer implements DialFrame, Cloneable, PublicCloneable, Serializable { /** For serialization. */ static final long serialVersionUID = 1016585407507121596L; /** The outer radius, relative to the framing rectangle. */ private double radius; /** * The color used for the front of the panel. This field is transient * because it requires special handling for serialization. */ private transient Paint backgroundPaint; /** * The color used for the border around the window. This field is transient * because it requires special handling for serialization. */ private transient Paint foregroundPaint; /** * The stroke for drawing the frame outline. This field is transient * because it requires special handling for serialization. */ private transient Stroke stroke; /** * Creates a new instance of {@code StandardDialFrame}. */ public StandardDialFrame() { this.backgroundPaint = Color.GRAY; this.foregroundPaint = Color.BLACK; this.stroke = new BasicStroke(2.0f); this.radius = 0.95; } /** * Returns the radius, relative to the framing rectangle. * * @return The radius. * * @see #setRadius(double) */ public double getRadius() { return this.radius; } /** * Sets the radius and sends a {@link DialLayerChangeEvent} to all * registered listeners. * * @param radius the radius (must be positive). * * @see #getRadius() */ public void setRadius(double radius) { if (radius <= 0) { throw new IllegalArgumentException( "The 'radius' must be positive."); } this.radius = radius; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the background paint. * * @return The background paint (never {@code null}). * * @see #setBackgroundPaint(Paint) */ public Paint getBackgroundPaint() { return this.backgroundPaint; } /** * Sets the background paint and sends a {@link DialLayerChangeEvent} to * all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getBackgroundPaint() */ public void setBackgroundPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.backgroundPaint = paint; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the foreground paint. * * @return The foreground paint (never {@code null}). * * @see #setForegroundPaint(Paint) */ public Paint getForegroundPaint() { return this.foregroundPaint; } /** * Sets the foreground paint and sends a {@link DialLayerChangeEvent} to * all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getForegroundPaint() */ public void setForegroundPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.foregroundPaint = paint; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the stroke for the frame. * * @return The stroke (never {@code null}). * * @see #setStroke(Stroke) */ public Stroke getStroke() { return this.stroke; } /** * Sets the stroke and sends a {@link DialLayerChangeEvent} to all * registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getStroke() */ public void setStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.stroke = stroke; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the shape for the window for this dial. Some dial layers will * request that their drawing be clipped within this window. * * @param frame the reference frame ({@code null} not permitted). * * @return The shape of the dial's window. */ @Override public Shape getWindow(Rectangle2D frame) { Rectangle2D f = DialPlot.rectangleByRadius(frame, this.radius, this.radius); return new Ellipse2D.Double(f.getX(), f.getY(), f.getWidth(), f.getHeight()); } /** * Returns {@code false} to indicate that this dial layer is not * clipped to the dial window. * * @return A boolean. */ @Override public boolean isClippedToWindow() { return false; } /** * Draws the frame. This method is called by the {@link DialPlot} class, * you shouldn't need to call it directly. * * @param g2 the graphics target ({@code null} not permitted). * @param plot the plot ({@code null} not permitted). * @param frame the frame ({@code null} not permitted). * @param view the view ({@code null} not permitted). */ @Override public void draw(Graphics2D g2, DialPlot plot, Rectangle2D frame, Rectangle2D view) { Shape window = getWindow(frame); Rectangle2D f = DialPlot.rectangleByRadius(frame, this.radius + 0.02, this.radius + 0.02); Ellipse2D e = new Ellipse2D.Double(f.getX(), f.getY(), f.getWidth(), f.getHeight()); Area area = new Area(e); Area area2 = new Area(window); area.subtract(area2); g2.setPaint(this.backgroundPaint); g2.fill(area); g2.setStroke(this.stroke); g2.setPaint(this.foregroundPaint); g2.draw(window); g2.draw(e); } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof StandardDialFrame)) { return false; } StandardDialFrame that = (StandardDialFrame) obj; if (!PaintUtils.equal(this.backgroundPaint, that.backgroundPaint)) { return false; } if (!PaintUtils.equal(this.foregroundPaint, that.foregroundPaint)) { return false; } if (this.radius != that.radius) { return false; } if (!this.stroke.equals(that.stroke)) { return false; } return super.equals(obj); } /** * Returns a hash code for this instance. * * @return The hash code. */ @Override public int hashCode() { int result = 193; long temp = Double.doubleToLongBits(this.radius); result = 37 * result + (int) (temp ^ (temp >>> 32)); result = 37 * result + HashUtils.hashCodeForPaint( this.backgroundPaint); result = 37 * result + HashUtils.hashCodeForPaint( this.foregroundPaint); result = 37 * result + this.stroke.hashCode(); return result; } /** * Returns a clone of this instance. * * @return A clone. * * @throws CloneNotSupportedException if any of the frame's attributes * cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.backgroundPaint, stream); SerialUtils.writePaint(this.foregroundPaint, stream); SerialUtils.writeStroke(this.stroke, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.backgroundPaint = SerialUtils.readPaint(stream); this.foregroundPaint = SerialUtils.readPaint(stream); this.stroke = SerialUtils.readStroke(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/dial/StandardDialRange.java000066400000000000000000000302001463604235500311120ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * StandardDialRange.java * ---------------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot.dial; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.geom.Arc2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import org.jfree.chart.HashUtils; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; /** * A layer that draws a range highlight on a dial plot. */ public class StandardDialRange extends AbstractDialLayer implements DialLayer, Cloneable, PublicCloneable, Serializable { /** For serialization. */ static final long serialVersionUID = 345515648249364904L; /** The scale index. */ private int scaleIndex; /** The minimum data value for the scale. */ private double lowerBound; /** The maximum data value for the scale. */ private double upperBound; /** * The paint used to draw the range highlight. This field is transient * because it requires special handling for serialization. */ private transient Paint paint; /** * The factor (in the range 0.0 to 1.0) that determines the inside limit * of the range highlight. */ private double innerRadius; /** * The factor (in the range 0.0 to 1.0) that determines the outside limit * of the range highlight. */ private double outerRadius; /** * Creates a new {@code StandardDialRange} instance. */ public StandardDialRange() { this(0.0, 100.0, Color.WHITE); } /** * Creates a new {@code StandardDialRange} instance. * * @param lower the lower bound. * @param upper the upper bound. * @param paint the paint ({@code null} not permitted). */ public StandardDialRange(double lower, double upper, Paint paint) { Args.nullNotPermitted(paint, "paint"); this.scaleIndex = 0; this.lowerBound = lower; this.upperBound = upper; this.innerRadius = 0.48; this.outerRadius = 0.52; this.paint = paint; } /** * Returns the scale index. * * @return The scale index. * * @see #setScaleIndex(int) */ public int getScaleIndex() { return this.scaleIndex; } /** * Sets the scale index and sends a {@link DialLayerChangeEvent} to all * registered listeners. * * @param index the scale index. * * @see #getScaleIndex() */ public void setScaleIndex(int index) { this.scaleIndex = index; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the lower bound (a data value) of the dial range. * * @return The lower bound of the dial range. * * @see #setLowerBound(double) */ public double getLowerBound() { return this.lowerBound; } /** * Sets the lower bound of the dial range and sends a * {@link DialLayerChangeEvent} to all registered listeners. * * @param bound the lower bound. * * @see #getLowerBound() */ public void setLowerBound(double bound) { if (bound >= this.upperBound) { throw new IllegalArgumentException( "Lower bound must be less than upper bound."); } this.lowerBound = bound; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the upper bound of the dial range. * * @return The upper bound. * * @see #setUpperBound(double) */ public double getUpperBound() { return this.upperBound; } /** * Sets the upper bound of the dial range and sends a * {@link DialLayerChangeEvent} to all registered listeners. * * @param bound the upper bound. * * @see #getUpperBound() */ public void setUpperBound(double bound) { if (bound <= this.lowerBound) { throw new IllegalArgumentException( "Lower bound must be less than upper bound."); } this.upperBound = bound; notifyListeners(new DialLayerChangeEvent(this)); } /** * Sets the bounds for the range and sends a {@link DialLayerChangeEvent} * to all registered listeners. * * @param lower the lower bound. * @param upper the upper bound. */ public void setBounds(double lower, double upper) { if (lower >= upper) { throw new IllegalArgumentException( "Lower must be less than upper."); } this.lowerBound = lower; this.upperBound = upper; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the paint used to highlight the range. * * @return The paint (never {@code null}). * * @see #setPaint(Paint) */ public Paint getPaint() { return this.paint; } /** * Sets the paint used to highlight the range and sends a * {@link DialLayerChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getPaint() */ public void setPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.paint = paint; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the inner radius. * * @return The inner radius. * * @see #setInnerRadius(double) */ public double getInnerRadius() { return this.innerRadius; } /** * Sets the inner radius and sends a {@link DialLayerChangeEvent} to all * registered listeners. * * @param radius the radius. * * @see #getInnerRadius() */ public void setInnerRadius(double radius) { this.innerRadius = radius; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the outer radius. * * @return The outer radius. * * @see #setOuterRadius(double) */ public double getOuterRadius() { return this.outerRadius; } /** * Sets the outer radius and sends a {@link DialLayerChangeEvent} to all * registered listeners. * * @param radius the radius. * * @see #getOuterRadius() */ public void setOuterRadius(double radius) { this.outerRadius = radius; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns {@code true} to indicate that this layer should be * clipped within the dial window. * * @return {@code true}. */ @Override public boolean isClippedToWindow() { return true; } /** * Draws the range. * * @param g2 the graphics target. * @param plot the plot. * @param frame the dial's reference frame (in Java2D space). * @param view the dial's view rectangle (in Java2D space). */ @Override public void draw(Graphics2D g2, DialPlot plot, Rectangle2D frame, Rectangle2D view) { Rectangle2D arcRectInner = DialPlot.rectangleByRadius(frame, this.innerRadius, this.innerRadius); Rectangle2D arcRectOuter = DialPlot.rectangleByRadius(frame, this.outerRadius, this.outerRadius); DialScale scale = plot.getScale(this.scaleIndex); if (scale == null) { throw new RuntimeException("No scale for scaleIndex = " + this.scaleIndex); } double angleMin = scale.valueToAngle(this.lowerBound); double angleMax = scale.valueToAngle(this.upperBound); Arc2D arcInner = new Arc2D.Double(arcRectInner, angleMin, angleMax - angleMin, Arc2D.OPEN); Arc2D arcOuter = new Arc2D.Double(arcRectOuter, angleMax, angleMin - angleMax, Arc2D.OPEN); g2.setPaint(this.paint); g2.setStroke(new BasicStroke(2.0f)); g2.draw(arcInner); g2.draw(arcOuter); } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof StandardDialRange)) { return false; } StandardDialRange that = (StandardDialRange) obj; if (this.scaleIndex != that.scaleIndex) { return false; } if (this.lowerBound != that.lowerBound) { return false; } if (this.upperBound != that.upperBound) { return false; } if (!PaintUtils.equal(this.paint, that.paint)) { return false; } if (this.innerRadius != that.innerRadius) { return false; } if (this.outerRadius != that.outerRadius) { return false; } return super.equals(obj); } /** * Returns a hash code for this instance. * * @return The hash code. */ @Override public int hashCode() { int result = 193; long temp = Double.doubleToLongBits(this.lowerBound); result = 37 * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(this.upperBound); result = 37 * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(this.innerRadius); result = 37 * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(this.outerRadius); result = 37 * result + (int) (temp ^ (temp >>> 32)); result = 37 * result + HashUtils.hashCodeForPaint(this.paint); return result; } /** * Returns a clone of this instance. * * @return A clone. * * @throws CloneNotSupportedException if any of the attributes of this * instance cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.paint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.paint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/dial/StandardDialScale.java000066400000000000000000000736041463604235500311240ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * StandardDialScale.java * ---------------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot.dial; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Stroke; import java.awt.geom.Arc2D; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.text.DecimalFormat; import java.text.NumberFormat; import org.jfree.chart.text.TextUtils; import org.jfree.chart.ui.TextAnchor; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; /** * A scale for a {@link DialPlot}. */ public class StandardDialScale extends AbstractDialLayer implements DialScale, Cloneable, PublicCloneable, Serializable { /** For serialization. */ static final long serialVersionUID = 3715644629665918516L; /** The minimum data value for the scale. */ private double lowerBound; /** The maximum data value for the scale. */ private double upperBound; /** * The start angle for the scale display, in degrees (using the same * encoding as Arc2D). */ private double startAngle; /** The extent of the scale display. */ private double extent; /** * The factor (in the range 0.0 to 1.0) that determines the outside limit * of the tick marks. */ private double tickRadius; /** * The increment (in data units) between major tick marks. */ private double majorTickIncrement; /** * The factor that is subtracted from the tickRadius to determine the * inner point of the major ticks. */ private double majorTickLength; /** * The paint to use for major tick marks. This field is transient because * it requires special handling for serialization. */ private transient Paint majorTickPaint; /** * The stroke to use for major tick marks. This field is transient because * it requires special handling for serialization. */ private transient Stroke majorTickStroke; /** * The number of minor ticks between each major tick. */ private int minorTickCount; /** * The factor that is subtracted from the tickRadius to determine the * inner point of the minor ticks. */ private double minorTickLength; /** * The paint to use for minor tick marks. This field is transient because * it requires special handling for serialization. */ private transient Paint minorTickPaint; /** * The stroke to use for minor tick marks. This field is transient because * it requires special handling for serialization. */ private transient Stroke minorTickStroke; /** * The tick label offset. */ private double tickLabelOffset; /** * The tick label font. */ private Font tickLabelFont; /** * A flag that controls whether or not the tick labels are * displayed. */ private boolean tickLabelsVisible; /** * The number formatter for the tick labels. */ private NumberFormat tickLabelFormatter; /** * A flag that controls whether or not the first tick label is * displayed. */ private boolean firstTickLabelVisible; /** * The tick label paint. This field is transient because it requires * special handling for serialization. */ private transient Paint tickLabelPaint; /** * Creates a new instance of DialScale. */ public StandardDialScale() { this(0.0, 100.0, 175, -170, 10.0, 4); } /** * Creates a new instance. * * @param lowerBound the lower bound of the scale. * @param upperBound the upper bound of the scale. * @param startAngle the start angle (in degrees, using the same * orientation as Java's {@code Arc2D} class). * @param extent the extent (in degrees, counter-clockwise). * @param majorTickIncrement the interval between major tick marks (must * be > 0). * @param minorTickCount the number of minor ticks between major tick * marks. */ public StandardDialScale(double lowerBound, double upperBound, double startAngle, double extent, double majorTickIncrement, int minorTickCount) { if (majorTickIncrement <= 0.0) { throw new IllegalArgumentException( "Requires 'majorTickIncrement' > 0."); } this.startAngle = startAngle; this.extent = extent; this.lowerBound = lowerBound; this.upperBound = upperBound; this.tickRadius = 0.70; this.tickLabelsVisible = true; this.tickLabelFormatter = new DecimalFormat("0.0"); this.firstTickLabelVisible = true; this.tickLabelFont = new Font("Dialog", Font.BOLD, 16); this.tickLabelPaint = Color.BLUE; this.tickLabelOffset = 0.10; this.majorTickIncrement = majorTickIncrement; this.majorTickLength = 0.04; this.majorTickPaint = Color.BLACK; this.majorTickStroke = new BasicStroke(3.0f); this.minorTickCount = minorTickCount; this.minorTickLength = 0.02; this.minorTickPaint = Color.BLACK; this.minorTickStroke = new BasicStroke(1.0f); } /** * Returns the lower bound for the scale. * * @return The lower bound for the scale. * * @see #setLowerBound(double) */ public double getLowerBound() { return this.lowerBound; } /** * Sets the lower bound for the scale and sends a * {@link DialLayerChangeEvent} to all registered listeners. * * @param lower the lower bound. * * @see #getLowerBound() */ public void setLowerBound(double lower) { this.lowerBound = lower; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the upper bound for the scale. * * @return The upper bound for the scale. * * @see #setUpperBound(double) */ public double getUpperBound() { return this.upperBound; } /** * Sets the upper bound for the scale and sends a * {@link DialLayerChangeEvent} to all registered listeners. * * @param upper the upper bound. * * @see #getUpperBound() */ public void setUpperBound(double upper) { this.upperBound = upper; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the start angle for the scale (in degrees using the same * orientation as Java's {@code Arc2D} class). * * @return The start angle. * * @see #setStartAngle(double) */ public double getStartAngle() { return this.startAngle; } /** * Sets the start angle for the scale and sends a * {@link DialLayerChangeEvent} to all registered listeners. * * @param angle the angle (in degrees). * * @see #getStartAngle() */ public void setStartAngle(double angle) { this.startAngle = angle; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the extent. * * @return The extent. * * @see #setExtent(double) */ public double getExtent() { return this.extent; } /** * Sets the extent and sends a {@link DialLayerChangeEvent} to all * registered listeners. * * @param extent the extent. * * @see #getExtent() */ public void setExtent(double extent) { this.extent = extent; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the radius (as a percentage of the maximum space available) of * the outer limit of the tick marks. * * @return The tick radius. * * @see #setTickRadius(double) */ public double getTickRadius() { return this.tickRadius; } /** * Sets the tick radius and sends a {@link DialLayerChangeEvent} to all * registered listeners. * * @param radius the radius. * * @see #getTickRadius() */ public void setTickRadius(double radius) { if (radius <= 0.0) { throw new IllegalArgumentException( "The 'radius' must be positive."); } this.tickRadius = radius; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the increment (in data units) between major tick labels. * * @return The increment between major tick labels. * * @see #setMajorTickIncrement(double) */ public double getMajorTickIncrement() { return this.majorTickIncrement; } /** * Sets the increment (in data units) between major tick labels and sends a * {@link DialLayerChangeEvent} to all registered listeners. * * @param increment the increment (must be > 0). * * @see #getMajorTickIncrement() */ public void setMajorTickIncrement(double increment) { if (increment <= 0.0) { throw new IllegalArgumentException( "The 'increment' must be positive."); } this.majorTickIncrement = increment; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the length factor for the major tick marks. The value is * subtracted from the tick radius to determine the inner starting point * for the tick marks. * * @return The length factor. * * @see #setMajorTickLength(double) */ public double getMajorTickLength() { return this.majorTickLength; } /** * Sets the length factor for the major tick marks and sends a * {@link DialLayerChangeEvent} to all registered listeners. * * @param length the length. * * @see #getMajorTickLength() */ public void setMajorTickLength(double length) { if (length < 0.0) { throw new IllegalArgumentException("Negative 'length' argument."); } this.majorTickLength = length; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the major tick paint. * * @return The major tick paint (never {@code null}). * * @see #setMajorTickPaint(Paint) */ public Paint getMajorTickPaint() { return this.majorTickPaint; } /** * Sets the major tick paint and sends a {@link DialLayerChangeEvent} to * all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getMajorTickPaint() */ public void setMajorTickPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.majorTickPaint = paint; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the stroke used to draw the major tick marks. * * @return The stroke (never {@code null}). * * @see #setMajorTickStroke(Stroke) */ public Stroke getMajorTickStroke() { return this.majorTickStroke; } /** * Sets the stroke used to draw the major tick marks and sends a * {@link DialLayerChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getMajorTickStroke() */ public void setMajorTickStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.majorTickStroke = stroke; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the number of minor tick marks between major tick marks. * * @return The number of minor tick marks between major tick marks. * * @see #setMinorTickCount(int) */ public int getMinorTickCount() { return this.minorTickCount; } /** * Sets the number of minor tick marks between major tick marks and sends * a {@link DialLayerChangeEvent} to all registered listeners. * * @param count the count. * * @see #getMinorTickCount() */ public void setMinorTickCount(int count) { if (count < 0) { throw new IllegalArgumentException( "The 'count' cannot be negative."); } this.minorTickCount = count; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the length factor for the minor tick marks. The value is * subtracted from the tick radius to determine the inner starting point * for the tick marks. * * @return The length factor. * * @see #setMinorTickLength(double) */ public double getMinorTickLength() { return this.minorTickLength; } /** * Sets the length factor for the minor tick marks and sends * a {@link DialLayerChangeEvent} to all registered listeners. * * @param length the length. * * @see #getMinorTickLength() */ public void setMinorTickLength(double length) { if (length < 0.0) { throw new IllegalArgumentException("Negative 'length' argument."); } this.minorTickLength = length; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the paint used to draw the minor tick marks. * * @return The paint (never {@code null}). * * @see #setMinorTickPaint(Paint) */ public Paint getMinorTickPaint() { return this.minorTickPaint; } /** * Sets the paint used to draw the minor tick marks and sends a * {@link DialLayerChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getMinorTickPaint() */ public void setMinorTickPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.minorTickPaint = paint; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the stroke used to draw the minor tick marks. * * @return The paint (never {@code null}). * * @see #setMinorTickStroke(Stroke) */ public Stroke getMinorTickStroke() { return this.minorTickStroke; } /** * Sets the stroke used to draw the minor tick marks and sends a * {@link DialLayerChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getMinorTickStroke() */ public void setMinorTickStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.minorTickStroke = stroke; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the tick label offset. * * @return The tick label offset. * * @see #setTickLabelOffset(double) */ public double getTickLabelOffset() { return this.tickLabelOffset; } /** * Sets the tick label offset and sends a {@link DialLayerChangeEvent} to * all registered listeners. * * @param offset the offset. * * @see #getTickLabelOffset() */ public void setTickLabelOffset(double offset) { this.tickLabelOffset = offset; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the font used to draw the tick labels. * * @return The font (never {@code null}). * * @see #setTickLabelFont(Font) */ public Font getTickLabelFont() { return this.tickLabelFont; } /** * Sets the font used to display the tick labels and sends a * {@link DialLayerChangeEvent} to all registered listeners. * * @param font the font ({@code null} not permitted). * * @see #getTickLabelFont() */ public void setTickLabelFont(Font font) { Args.nullNotPermitted(font, "font"); this.tickLabelFont = font; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the paint used to draw the tick labels. * * @return The paint ({@code null} not permitted). * * @see #setTickLabelPaint(Paint) */ public Paint getTickLabelPaint() { return this.tickLabelPaint; } /** * Sets the paint used to draw the tick labels and sends a * {@link DialLayerChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). */ public void setTickLabelPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.tickLabelPaint = paint; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns {@code true} if the tick labels should be displayed, * and {@code false} otherwise. * * @return A boolean. * * @see #setTickLabelsVisible(boolean) */ public boolean getTickLabelsVisible() { return this.tickLabelsVisible; } /** * Sets the flag that controls whether or not the tick labels are * displayed, and sends a {@link DialLayerChangeEvent} to all registered * listeners. * * @param visible the new flag value. * * @see #getTickLabelsVisible() */ public void setTickLabelsVisible(boolean visible) { this.tickLabelsVisible = visible; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns the number formatter used to convert the tick label values to * strings. * * @return The formatter (never {@code null}). * * @see #setTickLabelFormatter(NumberFormat) */ public NumberFormat getTickLabelFormatter() { return this.tickLabelFormatter; } /** * Sets the number formatter used to convert the tick label values to * strings, and sends a {@link DialLayerChangeEvent} to all registered * listeners. * * @param formatter the formatter ({@code null} not permitted). * * @see #getTickLabelFormatter() */ public void setTickLabelFormatter(NumberFormat formatter) { Args.nullNotPermitted(formatter, "formatter"); this.tickLabelFormatter = formatter; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns a flag that controls whether or not the first tick label is * visible. * * @return A boolean. * * @see #setFirstTickLabelVisible(boolean) */ public boolean getFirstTickLabelVisible() { return this.firstTickLabelVisible; } /** * Sets a flag that controls whether or not the first tick label is * visible, and sends a {@link DialLayerChangeEvent} to all registered * listeners. * * @param visible the new flag value. * * @see #getFirstTickLabelVisible() */ public void setFirstTickLabelVisible(boolean visible) { this.firstTickLabelVisible = visible; notifyListeners(new DialLayerChangeEvent(this)); } /** * Returns {@code true} to indicate that this layer should be * clipped within the dial window. * * @return {@code true}. */ @Override public boolean isClippedToWindow() { return true; } /** * Draws the scale on the dial plot. * * @param g2 the graphics target ({@code null} not permitted). * @param plot the dial plot ({@code null} not permitted). * @param frame the reference frame that is used to construct the * geometry of the plot ({@code null} not permitted). * @param view the visible part of the plot ({@code null} not * permitted). */ @Override public void draw(Graphics2D g2, DialPlot plot, Rectangle2D frame, Rectangle2D view) { Rectangle2D arcRect = DialPlot.rectangleByRadius(frame, this.tickRadius, this.tickRadius); Rectangle2D arcRectMajor = DialPlot.rectangleByRadius(frame, this.tickRadius - this.majorTickLength, this.tickRadius - this.majorTickLength); Rectangle2D arcRectMinor = arcRect; if (this.minorTickCount > 0 && this.minorTickLength > 0.0) { arcRectMinor = DialPlot.rectangleByRadius(frame, this.tickRadius - this.minorTickLength, this.tickRadius - this.minorTickLength); } Rectangle2D arcRectForLabels = DialPlot.rectangleByRadius(frame, this.tickRadius - this.tickLabelOffset, this.tickRadius - this.tickLabelOffset); boolean firstLabel = true; Arc2D arc = new Arc2D.Double(); Line2D workingLine = new Line2D.Double(); for (double v = this.lowerBound; v <= this.upperBound; v += this.majorTickIncrement) { arc.setArc(arcRect, this.startAngle, valueToAngle(v) - this.startAngle, Arc2D.OPEN); Point2D pt0 = arc.getEndPoint(); arc.setArc(arcRectMajor, this.startAngle, valueToAngle(v) - this.startAngle, Arc2D.OPEN); Point2D pt1 = arc.getEndPoint(); g2.setPaint(this.majorTickPaint); g2.setStroke(this.majorTickStroke); workingLine.setLine(pt0, pt1); g2.draw(workingLine); arc.setArc(arcRectForLabels, this.startAngle, valueToAngle(v) - this.startAngle, Arc2D.OPEN); Point2D pt2 = arc.getEndPoint(); if (this.tickLabelsVisible) { if (!firstLabel || this.firstTickLabelVisible) { g2.setFont(this.tickLabelFont); g2.setPaint(this.tickLabelPaint); TextUtils.drawAlignedString( this.tickLabelFormatter.format(v), g2, (float) pt2.getX(), (float) pt2.getY(), TextAnchor.CENTER); } } firstLabel = false; // now do the minor tick marks if (this.minorTickCount > 0 && this.minorTickLength > 0.0) { double minorTickIncrement = this.majorTickIncrement / (this.minorTickCount + 1); for (int i = 0; i < this.minorTickCount; i++) { double vv = v + ((i + 1) * minorTickIncrement); if (vv >= this.upperBound) { break; } double angle = valueToAngle(vv); arc.setArc(arcRect, this.startAngle, angle - this.startAngle, Arc2D.OPEN); pt0 = arc.getEndPoint(); arc.setArc(arcRectMinor, this.startAngle, angle - this.startAngle, Arc2D.OPEN); Point2D pt3 = arc.getEndPoint(); g2.setStroke(this.minorTickStroke); g2.setPaint(this.minorTickPaint); workingLine.setLine(pt0, pt3); g2.draw(workingLine); } } } } /** * Converts a data value to an angle against this scale. * * @param value the data value. * * @return The angle (in degrees, using the same specification as Java's * Arc2D class). * * @see #angleToValue(double) */ @Override public double valueToAngle(double value) { double range = this.upperBound - this.lowerBound; double unit = this.extent / range; return this.startAngle + unit * (value - this.lowerBound); } /** * Converts the given angle to a data value, based on this scale. * * @param angle the angle (in degrees). * * @return The data value. * * @see #valueToAngle(double) */ @Override public double angleToValue(double angle) { double range = this.upperBound - this.lowerBound; double unit = range / this.extent; return (angle - this.startAngle) * unit; } /** * Tests this {@code StandardDialScale} for equality with an arbitrary * object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof StandardDialScale)) { return false; } StandardDialScale that = (StandardDialScale) obj; if (this.lowerBound != that.lowerBound) { return false; } if (this.upperBound != that.upperBound) { return false; } if (this.startAngle != that.startAngle) { return false; } if (this.extent != that.extent) { return false; } if (this.tickRadius != that.tickRadius) { return false; } if (this.majorTickIncrement != that.majorTickIncrement) { return false; } if (this.majorTickLength != that.majorTickLength) { return false; } if (!PaintUtils.equal(this.majorTickPaint, that.majorTickPaint)) { return false; } if (!this.majorTickStroke.equals(that.majorTickStroke)) { return false; } if (this.minorTickCount != that.minorTickCount) { return false; } if (this.minorTickLength != that.minorTickLength) { return false; } if (!PaintUtils.equal(this.minorTickPaint, that.minorTickPaint)) { return false; } if (!this.minorTickStroke.equals(that.minorTickStroke)) { return false; } if (this.tickLabelsVisible != that.tickLabelsVisible) { return false; } if (this.tickLabelOffset != that.tickLabelOffset) { return false; } if (!this.tickLabelFont.equals(that.tickLabelFont)) { return false; } if (!PaintUtils.equal(this.tickLabelPaint, that.tickLabelPaint)) { return false; } return super.equals(obj); } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = 193; // lowerBound long temp = Double.doubleToLongBits(this.lowerBound); result = 37 * result + (int) (temp ^ (temp >>> 32)); // upperBound temp = Double.doubleToLongBits(this.upperBound); result = 37 * result + (int) (temp ^ (temp >>> 32)); // startAngle temp = Double.doubleToLongBits(this.startAngle); result = 37 * result + (int) (temp ^ (temp >>> 32)); // extent temp = Double.doubleToLongBits(this.extent); result = 37 * result + (int) (temp ^ (temp >>> 32)); // tickRadius temp = Double.doubleToLongBits(this.tickRadius); result = 37 * result + (int) (temp ^ (temp >>> 32)); // majorTickIncrement // majorTickLength // majorTickPaint // majorTickStroke // minorTickCount // minorTickLength // minorTickPaint // minorTickStroke // tickLabelOffset // tickLabelFont // tickLabelsVisible // tickLabelFormatter // firstTickLabelsVisible return result; } /** * Returns a clone of this instance. * * @return A clone. * * @throws CloneNotSupportedException if this instance is not cloneable. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.majorTickPaint, stream); SerialUtils.writeStroke(this.majorTickStroke, stream); SerialUtils.writePaint(this.minorTickPaint, stream); SerialUtils.writeStroke(this.minorTickStroke, stream); SerialUtils.writePaint(this.tickLabelPaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.majorTickPaint = SerialUtils.readPaint(stream); this.majorTickStroke = SerialUtils.readStroke(stream); this.minorTickPaint = SerialUtils.readPaint(stream); this.minorTickStroke = SerialUtils.readStroke(stream); this.tickLabelPaint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/dial/package.html000066400000000000000000000002071463604235500272250ustar00rootroot00000000000000 Classes for creating dial plots. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/flow/000077500000000000000000000000001463604235500250035ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/flow/FlowPlot.java000066400000000000000000000773111463604235500274250ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------- * FlowPlot.java * ------------- * (C) Copyright 2021-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot.flow; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Composite; import java.awt.Font; import java.awt.GradientPaint; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.geom.Path2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.entity.FlowEntity; import org.jfree.chart.entity.NodeEntity; import org.jfree.chart.labels.FlowLabelGenerator; import org.jfree.chart.labels.StandardFlowLabelGenerator; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.PlotState; import org.jfree.chart.text.TextUtils; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.ui.TextAnchor; import org.jfree.chart.ui.VerticalAlignment; import org.jfree.chart.util.Args; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.flow.FlowDataset; import org.jfree.data.flow.FlowDatasetUtils; import org.jfree.data.flow.FlowKey; import org.jfree.data.flow.NodeKey; /** * A plot for visualising flows defined in a {@link FlowDataset}. This enables * the production of a type of Sankey chart. The example shown here is * produced by the {@code FlowPlotDemo1.java} program included in the JFreeChart * Demo Collection: * FlowPlotDemo1.svg * * @since 1.5.3 */ public class FlowPlot extends Plot implements Cloneable, PublicCloneable, Serializable { /** The source of data. */ private FlowDataset dataset; /** * The node width in Java 2D user-space units. */ private double nodeWidth = 20.0; /** The gap between nodes (expressed as a percentage of the plot height). */ private double nodeMargin = 0.01; /** * The percentage of the plot width to assign to a gap between the nodes * and the flow representation. */ private double flowMargin = 0.005; /** * Stores colors for specific nodes - if there isn't a color in here for * the node, the default node color will be used (unless the color swatch * is active). */ private Map nodeColorMap; private List nodeColorSwatch; /** A pointer into the color swatch. */ private int nodeColorSwatchPointer = 0; /** The default node color if nothing is defined in the nodeColorMap. */ private Color defaultNodeColor; private Font defaultNodeLabelFont; private Paint defaultNodeLabelPaint; private VerticalAlignment nodeLabelAlignment; /** The x-offset for node labels. */ private double nodeLabelOffsetX; /** The y-offset for node labels. */ private double nodeLabelOffsetY; /** The tool tip generator - if null, no tool tips will be displayed. */ private FlowLabelGenerator toolTipGenerator; /** * Creates a new instance that will source data from the specified dataset. * * @param dataset the dataset. */ public FlowPlot(FlowDataset dataset) { this.dataset = dataset; if (dataset != null) { dataset.addChangeListener(this); } this.nodeColorMap = new HashMap<>(); this.nodeColorSwatch = new ArrayList<>(); this.defaultNodeColor = Color.GRAY; this.defaultNodeLabelFont = new Font(Font.DIALOG, Font.BOLD, 12); this.defaultNodeLabelPaint = Color.BLACK; this.nodeLabelAlignment = VerticalAlignment.CENTER; this.nodeLabelOffsetX = 2.0; this.nodeLabelOffsetY = 2.0; this.toolTipGenerator = new StandardFlowLabelGenerator(); } /** * Returns a string identifying the plot type. * * @return A string identifying the plot type. */ @Override public String getPlotType() { return "FlowPlot"; } /** * Returns a reference to the dataset. * * @return A reference to the dataset (possibly {@code null}). */ public FlowDataset getDataset() { return this.dataset; } /** * Sets the dataset for the plot and sends a change notification to all * registered listeners. * * @param dataset the dataset ({@code null} permitted). */ public void setDataset(FlowDataset dataset) { this.dataset = dataset; fireChangeEvent(); } /** * Returns the node margin (expressed as a percentage of the available * plotting space) which is the gap between nodes (sources or destinations). * The initial (default) value is {@code 0.01} (1 percent). * * @return The node margin. */ public double getNodeMargin() { return this.nodeMargin; } /** * Sets the node margin and sends a change notification to all registered * listeners. * * @param margin the margin (expressed as a percentage). */ public void setNodeMargin(double margin) { Args.requireNonNegative(margin, "margin"); this.nodeMargin = margin; fireChangeEvent(); } /** * Returns the flow margin. This determines the gap between the graphic * representation of the nodes (sources and destinations) and the curved * flow representation. This is expressed as a percentage of the plot * width so that it remains proportional as the plot is resized. The * initial (default) value is {@code 0.005} (0.5 percent). * * @return The flow margin. */ public double getFlowMargin() { return this.flowMargin; } /** * Sets the flow margin and sends a change notification to all registered * listeners. * * @param margin the margin (must be 0.0 or higher). */ public void setFlowMargin(double margin) { Args.requireNonNegative(margin, "margin"); this.flowMargin = margin; fireChangeEvent(); } /** * Returns the width of the source and destination nodes, expressed in * Java2D user-space units. The initial (default) value is {@code 20.0}. * * @return The width. */ public double getNodeWidth() { return this.nodeWidth; } /** * Sets the width for the source and destination nodes and sends a change * notification to all registered listeners. * * @param width the width. */ public void setNodeWidth(double width) { this.nodeWidth = width; fireChangeEvent(); } /** * Returns the list of colors that will be used to auto-populate the node * colors when they are first rendered. If the list is empty, no color * will be assigned to the node so, unless it is manually set, the default * color will apply. This method returns a copy of the list, modifying * the returned list will not affect the plot. * * @return The list of colors (possibly empty, but never {@code null}). */ public List getNodeColorSwatch() { return new ArrayList<>(this.nodeColorSwatch); } /** * Sets the color swatch for the plot. * * @param colors the list of colors ({@code null} not permitted). */ public void setNodeColorSwatch(List colors) { Args.nullNotPermitted(colors, "colors"); this.nodeColorSwatch = colors; } /** * Returns the fill color for the specified node. * * @param nodeKey the node key ({@code null} not permitted). * * @return The fill color (possibly {@code null}). */ public Color getNodeFillColor(NodeKey nodeKey) { return this.nodeColorMap.get(nodeKey); } /** * Sets the fill color for the specified node and sends a change * notification to all registered listeners. * * @param nodeKey the node key ({@code null} not permitted). * @param color the fill color ({@code null} permitted). */ public void setNodeFillColor(NodeKey nodeKey, Color color) { this.nodeColorMap.put(nodeKey, color); fireChangeEvent(); } /** * Returns the default node color. This is used when no specific node color * has been specified. The initial (default) value is {@code Color.GRAY}. * * @return The default node color (never {@code null}). */ public Color getDefaultNodeColor() { return this.defaultNodeColor; } /** * Sets the default node color and sends a change event to registered * listeners. * * @param color the color ({@code null} not permitted). */ public void setDefaultNodeColor(Color color) { Args.nullNotPermitted(color, "color"); this.defaultNodeColor = color; fireChangeEvent(); } /** * Returns the default font used to display labels for the source and * destination nodes. The initial (default) value is * {@code Font(Font.DIALOG, Font.BOLD, 12)}. * * @return The default font (never {@code null}). */ public Font getDefaultNodeLabelFont() { return this.defaultNodeLabelFont; } /** * Sets the default font used to display labels for the source and * destination nodes and sends a change notification to all registered * listeners. * * @param font the font ({@code null} not permitted). */ public void setDefaultNodeLabelFont(Font font) { Args.nullNotPermitted(font, "font"); this.defaultNodeLabelFont = font; fireChangeEvent(); } /** * Returns the default paint used to display labels for the source and * destination nodes. The initial (default) value is {@code Color.BLACK}. * * @return The default paint (never {@code null}). */ public Paint getDefaultNodeLabelPaint() { return this.defaultNodeLabelPaint; } /** * Sets the default paint used to display labels for the source and * destination nodes and sends a change notification to all registered * listeners. * * @param paint the paint ({@code null} not permitted). */ public void setDefaultNodeLabelPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.defaultNodeLabelPaint = paint; fireChangeEvent(); } /** * Returns the vertical alignment of the node labels relative to the node. * The initial (default) value is {@link VerticalAlignment#CENTER}. * * @return The alignment (never {@code null}). */ public VerticalAlignment getNodeLabelAlignment() { return this.nodeLabelAlignment; } /** * Sets the vertical alignment of the node labels and sends a change * notification to all registered listeners. * * @param alignment the new alignment ({@code null} not permitted). */ public void setNodeLabelAlignment(VerticalAlignment alignment) { Args.nullNotPermitted(alignment, "alignment"); this.nodeLabelAlignment = alignment; fireChangeEvent(); } /** * Returns the x-offset for the node labels. * * @return The x-offset for the node labels. */ public double getNodeLabelOffsetX() { return this.nodeLabelOffsetX; } /** * Sets the x-offset for the node labels and sends a change notification * to all registered listeners. * * @param offsetX the node label x-offset in Java2D units. */ public void setNodeLabelOffsetX(double offsetX) { this.nodeLabelOffsetX = offsetX; fireChangeEvent(); } /** * Returns the y-offset for the node labels. * * @return The y-offset for the node labels. */ public double getNodeLabelOffsetY() { return nodeLabelOffsetY; } /** * Sets the y-offset for the node labels and sends a change notification * to all registered listeners. * * @param offsetY the node label y-offset in Java2D units. */ public void setNodeLabelOffsetY(double offsetY) { this.nodeLabelOffsetY = offsetY; fireChangeEvent(); } /** * Returns the tool tip generator that creates the strings that are * displayed as tool tips for the flows displayed in the plot. * * @return The tool tip generator (possibly {@code null}). */ public FlowLabelGenerator getToolTipGenerator() { return this.toolTipGenerator; } /** * Sets the tool tip generator and sends a change notification to all * registered listeners. If the generator is set to {@code null}, no tool * tips will be displayed for the flows. * * @param generator the new generator ({@code null} permitted). */ public void setToolTipGenerator(FlowLabelGenerator generator) { this.toolTipGenerator = generator; fireChangeEvent(); } /** * Draws the flow plot within the specified area of the supplied graphics * target {@code g2}. * * @param g2 the graphics target ({@code null} not permitted). * @param area the plot area ({@code null} not permitted). * @param anchor the anchor point (ignored). * @param parentState the parent state (ignored). * @param info the plot rendering info. */ @Override public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, PlotState parentState, PlotRenderingInfo info) { Args.nullNotPermitted(g2, "g2"); Args.nullNotPermitted(area, "area"); EntityCollection entities = null; if (info != null) { info.setPlotArea(area); entities = info.getOwner().getEntityCollection(); } RectangleInsets insets = getInsets(); insets.trim(area); if (info != null) { info.setDataArea(area); } // use default JFreeChart background handling drawBackground(g2, area); // we need to ensure there is space to show all the inflows and all // the outflows at each node group, so first we calculate the max // flow space required - for each node in the group, consider the // maximum of the inflow and the outflow double flow2d = Double.POSITIVE_INFINITY; double nodeMargin2d = this.nodeMargin * area.getHeight(); int stageCount = this.dataset.getStageCount(); for (int stage = 0; stage < this.dataset.getStageCount(); stage++) { List sources = this.dataset.getSources(stage); int nodeCount = sources.size(); double flowTotal = 0.0; for (Comparable source : sources) { double inflow = FlowDatasetUtils.calculateInflow(this.dataset, source, stage); double outflow = FlowDatasetUtils.calculateOutflow(this.dataset, source, stage); flowTotal = flowTotal + Math.max(inflow, outflow); } if (flowTotal > 0.0) { double availableH = area.getHeight() - (nodeCount - 1) * nodeMargin2d; flow2d = Math.min(availableH / flowTotal, flow2d); } if (stage == this.dataset.getStageCount() - 1) { // check inflows to the final destination nodes... List destinations = this.dataset.getDestinations(stage); int destinationCount = destinations.size(); flowTotal = 0.0; for (Comparable destination : destinations) { double inflow = FlowDatasetUtils.calculateInflow(this.dataset, destination, stage + 1); flowTotal = flowTotal + inflow; } if (flowTotal > 0.0) { double availableH = area.getHeight() - (destinationCount - 1) * nodeMargin2d; flow2d = Math.min(availableH / flowTotal, flow2d); } } } double stageWidth = (area.getWidth() - ((stageCount + 1) * this.nodeWidth)) / stageCount; double flowOffset = area.getWidth() * this.flowMargin; Map nodeRects = new HashMap<>(); boolean hasNodeSelections = FlowDatasetUtils.hasNodeSelections(this.dataset); boolean hasFlowSelections = FlowDatasetUtils.hasFlowSelections(this.dataset); // iterate over all the stages, we can render the source node rects and // the flows ... we should add the destination node rects last, then // in a final pass add the labels for (int stage = 0; stage < this.dataset.getStageCount(); stage++) { double stageLeft = area.getX() + (stage + 1) * this.nodeWidth + (stage * stageWidth); double stageRight = stageLeft + stageWidth; // calculate the source node and flow rectangles Map sourceFlowRects = new HashMap<>(); double nodeY = area.getY(); for (Object s : this.dataset.getSources(stage)) { Comparable source = (Comparable) s; double inflow = FlowDatasetUtils.calculateInflow(dataset, source, stage); double outflow = FlowDatasetUtils.calculateOutflow(dataset, source, stage); double nodeHeight = (Math.max(inflow, outflow) * flow2d); Rectangle2D nodeRect = new Rectangle2D.Double(stageLeft - nodeWidth, nodeY, nodeWidth, nodeHeight); if (entities != null) { entities.add(new NodeEntity(new NodeKey<>(stage, source), nodeRect, source.toString())); } nodeRects.put(new NodeKey<>(stage, source), nodeRect); double y = nodeY; for (Object d : this.dataset.getDestinations(stage)) { Comparable destination = (Comparable) d; Number flow = this.dataset.getFlow(stage, source, destination); if (flow != null) { double height = flow.doubleValue() * flow2d; Rectangle2D rect = new Rectangle2D.Double(stageLeft - nodeWidth, y, nodeWidth, height); sourceFlowRects.put(new FlowKey<>(stage, source, destination), rect); y = y + height; } } nodeY = nodeY + nodeHeight + nodeMargin2d; } // calculate the destination rectangles Map destFlowRects = new HashMap<>(); nodeY = area.getY(); for (Object d : this.dataset.getDestinations(stage)) { Comparable destination = (Comparable) d; double inflow = FlowDatasetUtils.calculateInflow(dataset, destination, stage + 1); double outflow = FlowDatasetUtils.calculateOutflow(dataset, destination, stage + 1); double nodeHeight = Math.max(inflow, outflow) * flow2d; nodeRects.put(new NodeKey<>(stage + 1, destination), new Rectangle2D.Double(stageRight, nodeY, nodeWidth, nodeHeight)); double y = nodeY; for (Object s : this.dataset.getSources(stage)) { Comparable source = (Comparable) s; Number flow = this.dataset.getFlow(stage, source, destination); if (flow != null) { double height = flow.doubleValue() * flow2d; Rectangle2D rect = new Rectangle2D.Double(stageRight, y, nodeWidth, height); y = y + height; destFlowRects.put(new FlowKey<>(stage, source, destination), rect); } } nodeY = nodeY + nodeHeight + nodeMargin2d; } for (Object s : this.dataset.getSources(stage)) { Comparable source = (Comparable) s; NodeKey nodeKey = new NodeKey<>(stage, source); Rectangle2D nodeRect = nodeRects.get(nodeKey); Color ncol = lookupNodeColor(nodeKey); if (hasNodeSelections) { if (!Boolean.TRUE.equals(dataset.getNodeProperty(nodeKey, NodeKey.SELECTED_PROPERTY_KEY))) { int g = (ncol.getRed() + ncol.getGreen() + ncol.getBlue()) / 3; ncol = new Color(g, g, g, ncol.getAlpha()); } } g2.setPaint(ncol); g2.fill(nodeRect); for (Object d : this.dataset.getDestinations(stage)) { Comparable destination = (Comparable) d; FlowKey flowKey = new FlowKey<>(stage, source, destination); Rectangle2D sourceRect = sourceFlowRects.get(flowKey); if (sourceRect == null) { continue; } Rectangle2D destRect = destFlowRects.get(flowKey); Path2D connect = new Path2D.Double(); connect.moveTo(sourceRect.getMaxX() + flowOffset, sourceRect.getMinY()); connect.curveTo(stageLeft + stageWidth / 2.0, sourceRect.getMinY(), stageLeft + stageWidth / 2.0, destRect.getMinY(), destRect.getX() - flowOffset, destRect.getMinY()); connect.lineTo(destRect.getX() - flowOffset, destRect.getMaxY()); connect.curveTo(stageLeft + stageWidth / 2.0, destRect.getMaxY(), stageLeft + stageWidth / 2.0, sourceRect.getMaxY(), sourceRect.getMaxX() + flowOffset, sourceRect.getMaxY()); connect.closePath(); Color nc = lookupNodeColor(nodeKey); if (hasFlowSelections) { if (!Boolean.TRUE.equals(dataset.getFlowProperty(flowKey, FlowKey.SELECTED_PROPERTY_KEY))) { int g = (ncol.getRed() + ncol.getGreen() + ncol.getBlue()) / 3; nc = new Color(g, g, g, ncol.getAlpha()); } } GradientPaint gp = new GradientPaint((float) sourceRect.getMaxX(), 0, nc, (float) destRect.getMinX(), 0, new Color(nc.getRed(), nc.getGreen(), nc.getBlue(), 128)); Composite saved = g2.getComposite(); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.75f)); g2.setPaint(gp); g2.fill(connect); if (entities != null) { String toolTip = null; if (this.toolTipGenerator != null) { toolTip = this.toolTipGenerator.generateLabel(this.dataset, flowKey); } entities.add(new FlowEntity(flowKey, connect, toolTip, "")); } g2.setComposite(saved); } } } // now draw the destination nodes int lastStage = this.dataset.getStageCount() - 1; for (Object d : this.dataset.getDestinations(lastStage)) { Comparable destination = (Comparable) d; NodeKey nodeKey = new NodeKey<>(lastStage + 1, destination); Rectangle2D nodeRect = nodeRects.get(nodeKey); if (nodeRect != null) { Color ncol = lookupNodeColor(nodeKey); if (hasNodeSelections) { if (!Boolean.TRUE.equals(dataset.getNodeProperty(nodeKey, NodeKey.SELECTED_PROPERTY_KEY))) { int g = (ncol.getRed() + ncol.getGreen() + ncol.getBlue()) / 3; ncol = new Color(g, g, g, ncol.getAlpha()); } } g2.setPaint(ncol); g2.fill(nodeRect); if (entities != null) { entities.add(new NodeEntity(new NodeKey<>(lastStage + 1, destination), nodeRect, destination.toString())); } } } // now draw all the labels over top of everything else g2.setFont(this.defaultNodeLabelFont); g2.setPaint(this.defaultNodeLabelPaint); for (NodeKey key : nodeRects.keySet()) { Rectangle2D r = nodeRects.get(key); if (key.getStage() < this.dataset.getStageCount()) { TextUtils.drawAlignedString(key.getNode().toString(), g2, (float) (r.getMaxX() + flowOffset + this.nodeLabelOffsetX), (float) labelY(r), TextAnchor.CENTER_LEFT); } else { TextUtils.drawAlignedString(key.getNode().toString(), g2, (float) (r.getX() - flowOffset - this.nodeLabelOffsetX), (float) labelY(r), TextAnchor.CENTER_RIGHT); } } } /** * Performs a lookup on the color for the specified node. * * @param nodeKey the node key ({@code null} not permitted). * * @return The node color. */ protected Color lookupNodeColor(NodeKey nodeKey) { Color result = this.nodeColorMap.get(nodeKey); if (result == null) { // if the color swatch is non-empty, we use it to autopopulate // the node colors... if (!this.nodeColorSwatch.isEmpty()) { // look through previous stages to see if this source key is already seen for (int s = 0; s < nodeKey.getStage(); s++) { for (Object key : dataset.getSources(s)) { if (nodeKey.getNode().equals(key)) { Color color = this.nodeColorMap.get(new NodeKey<>(s, (Comparable) key)); setNodeFillColor(nodeKey, color); return color; } } } result = this.nodeColorSwatch.get(Math.min(this.nodeColorSwatchPointer, this.nodeColorSwatch.size() - 1)); this.nodeColorSwatchPointer++; if (this.nodeColorSwatchPointer > this.nodeColorSwatch.size() - 1) { this.nodeColorSwatchPointer = 0; } setNodeFillColor(nodeKey, result); return result; } else { result = this.defaultNodeColor; } } return result; } /** * Computes the y-coordinate for a node label taking into account the * current alignment settings. * * @param r the node rectangle. * * @return The y-coordinate for the label. */ private double labelY(Rectangle2D r) { if (this.nodeLabelAlignment == VerticalAlignment.TOP) { return r.getY() + this.nodeLabelOffsetY; } else if (this.nodeLabelAlignment == VerticalAlignment.BOTTOM) { return r.getMaxY() - this.nodeLabelOffsetY; } else { return r.getCenterY(); } } /** * Tests this plot for equality with an arbitrary object. Note that, for * the purposes of this equality test, the dataset is ignored. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (!(obj instanceof FlowPlot)) { return false; } FlowPlot that = (FlowPlot) obj; if (!this.defaultNodeColor.equals(that.defaultNodeColor)) { return false; } if (!this.nodeColorMap.equals(that.nodeColorMap)) { return false; } if (!this.nodeColorSwatch.equals(that.nodeColorSwatch)) { return false; } if (!this.defaultNodeLabelFont.equals(that.defaultNodeLabelFont)) { return false; } if (!PaintUtils.equal(this.defaultNodeLabelPaint, that.defaultNodeLabelPaint)) { return false; } if (this.flowMargin != that.flowMargin) { return false; } if (this.nodeMargin != that.nodeMargin) { return false; } if (this.nodeWidth != that.nodeWidth) { return false; } if (this.nodeLabelOffsetX != that.nodeLabelOffsetX) { return false; } if (this.nodeLabelOffsetY != that.nodeLabelOffsetY) { return false; } if (this.nodeLabelAlignment != that.nodeLabelAlignment) { return false; } if (!Objects.equals(this.toolTipGenerator, that.toolTipGenerator)) { return false; } return super.equals(obj); } /** * Returns a hashcode for this instance. * * @return A hashcode. */ @Override public int hashCode() { int hash = 3; hash = 83 * hash + (int) (Double.doubleToLongBits(this.nodeWidth) ^ (Double.doubleToLongBits(this.nodeWidth) >>> 32)); hash = 83 * hash + (int) (Double.doubleToLongBits(this.nodeMargin) ^ (Double.doubleToLongBits(this.nodeMargin) >>> 32)); hash = 83 * hash + (int) (Double.doubleToLongBits(this.flowMargin) ^ (Double.doubleToLongBits(this.flowMargin) >>> 32)); hash = 83 * hash + Objects.hashCode(this.nodeColorMap); hash = 83 * hash + Objects.hashCode(this.nodeColorSwatch); hash = 83 * hash + Objects.hashCode(this.defaultNodeColor); hash = 83 * hash + Objects.hashCode(this.defaultNodeLabelFont); hash = 83 * hash + Objects.hashCode(this.defaultNodeLabelPaint); hash = 83 * hash + Objects.hashCode(this.nodeLabelAlignment); hash = 83 * hash + (int) (Double.doubleToLongBits(this.nodeLabelOffsetX) ^ (Double.doubleToLongBits(this.nodeLabelOffsetX) >>> 32)); hash = 83 * hash + (int) (Double.doubleToLongBits(this.nodeLabelOffsetY) ^ (Double.doubleToLongBits(this.nodeLabelOffsetY) >>> 32)); hash = 83 * hash + Objects.hashCode(this.toolTipGenerator); return hash; } /** * Returns an independent copy of this {@code FlowPlot} instance (note, * however, that the dataset is NOT cloned). * * @return A close of this instance. * * @throws CloneNotSupportedException */ @Override public Object clone() throws CloneNotSupportedException { FlowPlot clone = (FlowPlot) super.clone(); clone.nodeColorMap = new HashMap<>(this.nodeColorMap); return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/flow/package-info.java000066400000000000000000000001501463604235500301660ustar00rootroot00000000000000/** * Classes for creating flow plots (a type of Sankey chart). */ package org.jfree.chart.plot.flow; jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/plot/package.html000066400000000000000000000002211463604235500263100ustar00rootroot00000000000000 Plot classes and related interfaces. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/000077500000000000000000000000001463604235500246645ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/AbstractRenderer.java000066400000000000000000003152401463604235500307660ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * AbstractRenderer.java * --------------------- * (C) Copyright 2002-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Nicolas Brodu; * Yuri Blankenstein; * */ package org.jfree.chart.renderer; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Arrays; import java.util.EventListener; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import javax.swing.event.EventListenerList; import org.jfree.chart.ChartColor; import org.jfree.chart.ChartHints; import org.jfree.chart.HashUtils; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.event.RendererChangeListener; import org.jfree.chart.labels.ItemLabelAnchor; import org.jfree.chart.labels.ItemLabelPosition; import org.jfree.chart.plot.DrawingSupplier; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.title.LegendTitle; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.ui.TextAnchor; import org.jfree.chart.util.BooleanList; import org.jfree.chart.util.PaintList; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.SerialUtils; import org.jfree.chart.util.ShapeList; import org.jfree.chart.util.ShapeUtils; import org.jfree.chart.util.StrokeList; import org.jfree.data.ItemKey; /** * Base class providing common services for renderers. Most methods that update * attributes of the renderer will fire a {@link RendererChangeEvent}, which * normally means the plot that owns the renderer will receive notification that * the renderer has been changed (the plot will, in turn, notify the chart). */ public abstract class AbstractRenderer implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -828267569428206075L; /** Zero represented as a {@code double}. */ public static final Double ZERO = 0.0; /** The default paint. */ public static final Paint DEFAULT_PAINT = Color.BLUE; /** The default outline paint. */ public static final Paint DEFAULT_OUTLINE_PAINT = Color.GRAY; /** The default stroke. */ public static final Stroke DEFAULT_STROKE = new BasicStroke(1.0f); /** The default outline stroke. */ public static final Stroke DEFAULT_OUTLINE_STROKE = new BasicStroke(1.0f); /** The default shape. */ public static final Shape DEFAULT_SHAPE = new Rectangle2D.Double(-3.0, -3.0, 6.0, 6.0); /** The default value label font. */ public static final Font DEFAULT_VALUE_LABEL_FONT = new Font("SansSerif", Font.PLAIN, 10); /** The default value label paint. */ public static final Paint DEFAULT_VALUE_LABEL_PAINT = Color.BLACK; /** The default item label insets. */ public static final RectangleInsets DEFAULT_ITEM_LABEL_INSETS = new RectangleInsets( 2.0, 2.0, 2.0, 2.0); /** A list of flags that controls whether or not each series is visible. */ private BooleanList seriesVisibleList; /** The default visibility for all series. */ private boolean defaultSeriesVisible; /** * A list of flags that controls whether or not each series is visible in * the legend. */ private BooleanList seriesVisibleInLegendList; /** The default visibility for each series in the legend. */ private boolean defaultSeriesVisibleInLegend; /** The paint list. */ private PaintList paintList; /** * A flag that controls whether or not the paintList is auto-populated * in the {@link #lookupSeriesPaint(int)} method. */ private boolean autoPopulateSeriesPaint; /** The default paint, used when there is no paint assigned for a series. */ private transient Paint defaultPaint; /** The fill paint list. */ private PaintList fillPaintList; /** * A flag that controls whether or not the fillPaintList is auto-populated * in the {@link #lookupSeriesFillPaint(int)} method. */ private boolean autoPopulateSeriesFillPaint; /** The base fill paint. */ private transient Paint defaultFillPaint; /** The outline paint list. */ private PaintList outlinePaintList; /** * A flag that controls whether or not the outlinePaintList is * auto-populated in the {@link #lookupSeriesOutlinePaint(int)} method. */ private boolean autoPopulateSeriesOutlinePaint; /** The base outline paint. */ private transient Paint defaultOutlinePaint; /** The stroke list. */ private StrokeList strokeList; /** * A flag that controls whether or not the strokeList is auto-populated * in the {@link #lookupSeriesStroke(int)} method. */ private boolean autoPopulateSeriesStroke; /** The base stroke. */ private transient Stroke defaultStroke; /** The outline stroke list. */ private StrokeList outlineStrokeList; /** The base outline stroke. */ private transient Stroke defaultOutlineStroke; /** * A flag that controls whether or not the outlineStrokeList is * auto-populated in the {@link #lookupSeriesOutlineStroke(int)} method. */ private boolean autoPopulateSeriesOutlineStroke; /** A shape list. */ private ShapeList shapeList; /** * A flag that controls whether or not the shapeList is auto-populated * in the {@link #lookupSeriesShape(int)} method. */ private boolean autoPopulateSeriesShape; /** The base shape. */ private transient Shape defaultShape; /** Visibility of the item labels PER series. */ private BooleanList itemLabelsVisibleList; /** The base item labels visible. */ private boolean defaultItemLabelsVisible; /** The item label font list (one font per series). */ private Map itemLabelFontMap; /** The base item label font. */ private Font defaultItemLabelFont; /** The item label paint list (one paint per series). */ private PaintList itemLabelPaintList; /** The base item label paint. */ private transient Paint defaultItemLabelPaint; /** Option to use contrast colors for item labels */ private boolean computeItemLabelContrastColor; /** The positive item label position (per series). */ private Map positiveItemLabelPositionMap; /** The fallback positive item label position. */ private ItemLabelPosition defaultPositiveItemLabelPosition; /** The negative item label position (per series). */ private Map negativeItemLabelPositionMap; /** The fallback negative item label position. */ private ItemLabelPosition defaultNegativeItemLabelPosition; /** The item label insets. */ private RectangleInsets itemLabelInsets; /** * Flags that control whether or not entities are generated for each * series. This will be overridden by 'createEntities'. */ private BooleanList createEntitiesList; /** * The default flag that controls whether or not entities are generated. * This flag is used when both the above flags return null. */ private boolean defaultCreateEntities; /** * The per-series legend shape settings. */ private ShapeList legendShapeList; /** * The base shape for legend items. If this is {@code null}, the * series shape will be used. */ private transient Shape defaultLegendShape; /** * A special flag that, if true, will cause the getLegendItem() method * to configure the legend shape as if it were a line. */ private boolean treatLegendShapeAsLine; /** * The per-series legend text font. */ private Map legendTextFontMap; /** * The base legend font. */ private Font defaultLegendTextFont; /** * The per series legend text paint settings. */ private PaintList legendTextPaint; /** * The default paint for the legend text items (if this is * {@code null}, the {@link LegendTitle} class will determine the * text paint to use. */ private transient Paint defaultLegendTextPaint; /** * A flag that controls whether or not the renderer will include the * non-visible series when calculating the data bounds. */ private boolean dataBoundsIncludesVisibleSeriesOnly = true; /** The default radius for the entity 'hotspot' */ private int defaultEntityRadius; /** Storage for registered change listeners. */ private transient EventListenerList listenerList; /** An event for re-use. */ private transient RendererChangeEvent event; /** * Default constructor. */ public AbstractRenderer() { this.seriesVisibleList = new BooleanList(); this.defaultSeriesVisible = true; this.seriesVisibleInLegendList = new BooleanList(); this.defaultSeriesVisibleInLegend = true; this.paintList = new PaintList(); this.defaultPaint = DEFAULT_PAINT; this.autoPopulateSeriesPaint = true; this.fillPaintList = new PaintList(); this.defaultFillPaint = Color.WHITE; this.autoPopulateSeriesFillPaint = false; this.outlinePaintList = new PaintList(); this.defaultOutlinePaint = DEFAULT_OUTLINE_PAINT; this.autoPopulateSeriesOutlinePaint = false; this.strokeList = new StrokeList(); this.defaultStroke = DEFAULT_STROKE; this.autoPopulateSeriesStroke = true; this.outlineStrokeList = new StrokeList(); this.defaultOutlineStroke = DEFAULT_OUTLINE_STROKE; this.autoPopulateSeriesOutlineStroke = false; this.shapeList = new ShapeList(); this.defaultShape = DEFAULT_SHAPE; this.autoPopulateSeriesShape = true; this.itemLabelsVisibleList = new BooleanList(); this.defaultItemLabelsVisible = false; this.itemLabelInsets = DEFAULT_ITEM_LABEL_INSETS; this.itemLabelFontMap = new HashMap<>(); this.defaultItemLabelFont = new Font("SansSerif", Font.PLAIN, 10); this.itemLabelPaintList = new PaintList(); this.defaultItemLabelPaint = Color.BLACK; this.computeItemLabelContrastColor = false; this.positiveItemLabelPositionMap = new HashMap<>(); this.defaultPositiveItemLabelPosition = new ItemLabelPosition( ItemLabelAnchor.OUTSIDE12, TextAnchor.BOTTOM_CENTER); this.negativeItemLabelPositionMap = new HashMap<>(); this.defaultNegativeItemLabelPosition = new ItemLabelPosition( ItemLabelAnchor.OUTSIDE6, TextAnchor.TOP_CENTER); this.createEntitiesList = new BooleanList(); this.defaultCreateEntities = true; this.defaultEntityRadius = 3; this.legendShapeList = new ShapeList(); this.defaultLegendShape = null; this.treatLegendShapeAsLine = false; this.legendTextFontMap = new HashMap<>(); this.defaultLegendTextFont = null; this.legendTextPaint = new PaintList(); this.defaultLegendTextPaint = null; this.listenerList = new EventListenerList(); } /** * Returns the drawing supplier from the plot. * * @return The drawing supplier. */ public abstract DrawingSupplier getDrawingSupplier(); /** * Adds a {@code KEY_BEGIN_ELEMENT} hint to the graphics target. This * hint is recognised by JFreeSVG (in theory it could be used by * other {@code Graphics2D} implementations also). * * @param g2 the graphics target ({@code null} not permitted). * @param key the key ({@code null} not permitted). * * @see #endElementGroup(java.awt.Graphics2D) */ protected void beginElementGroup(Graphics2D g2, ItemKey key) { Args.nullNotPermitted(key, "key"); Map m = new HashMap<>(1); m.put("ref", key.toJSONString()); g2.setRenderingHint(ChartHints.KEY_BEGIN_ELEMENT, m); } /** * Adds a {@code KEY_END_ELEMENT} hint to the graphics target. * * @param g2 the graphics target ({@code null} not permitted). * * @see #beginElementGroup(java.awt.Graphics2D, org.jfree.data.ItemKey) */ protected void endElementGroup(Graphics2D g2) { g2.setRenderingHint(ChartHints.KEY_END_ELEMENT, Boolean.TRUE); } // SERIES VISIBLE (not yet respected by all renderers) /** * Returns a boolean that indicates whether or not the specified item * should be drawn. * * @param series the series index. * @param item the item index. * * @return A boolean. */ public boolean getItemVisible(int series, int item) { return isSeriesVisible(series); } /** * Returns a boolean that indicates whether or not the specified series * should be drawn. In fact this method should be named * lookupSeriesVisible() to be consistent with the other series * attributes and avoid confusion with the getSeriesVisible() method. * * @param series the series index. * * @return A boolean. */ public boolean isSeriesVisible(int series) { boolean result = this.defaultSeriesVisible; Boolean b = this.seriesVisibleList.getBoolean(series); if (b != null) { result = b; } return result; } /** * Returns the flag that controls whether a series is visible. * * @param series the series index (zero-based). * * @return The flag (possibly {@code null}). * * @see #setSeriesVisible(int, Boolean) */ public Boolean getSeriesVisible(int series) { return this.seriesVisibleList.getBoolean(series); } /** * Sets the flag that controls whether a series is visible and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param visible the flag ({@code null} permitted). * * @see #getSeriesVisible(int) */ public void setSeriesVisible(int series, Boolean visible) { setSeriesVisible(series, visible, true); } /** * Sets the flag that controls whether a series is visible and, if * requested, sends a {@link RendererChangeEvent} to all registered * listeners. * * @param series the series index. * @param visible the flag ({@code null} permitted). * @param notify notify listeners? * * @see #getSeriesVisible(int) */ public void setSeriesVisible(int series, Boolean visible, boolean notify) { this.seriesVisibleList.setBoolean(series, visible); if (notify) { // we create an event with a special flag set...the purpose of // this is to communicate to the plot (the default receiver of // the event) that series visibility has changed so the axis // ranges might need updating... RendererChangeEvent e = new RendererChangeEvent(this, true); notifyListeners(e); } } /** * Returns the default visibility for all series. * * @return The default visibility. * * @see #setDefaultSeriesVisible(boolean) */ public boolean getDefaultSeriesVisible() { return this.defaultSeriesVisible; } /** * Sets the default series visibility and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param visible the flag. * * @see #getDefaultSeriesVisible() */ public void setDefaultSeriesVisible(boolean visible) { // defer argument checking... setDefaultSeriesVisible(visible, true); } /** * Sets the default series visibility and, if requested, sends * a {@link RendererChangeEvent} to all registered listeners. * * @param visible the visibility. * @param notify notify listeners? * * @see #getDefaultSeriesVisible() */ public void setDefaultSeriesVisible(boolean visible, boolean notify) { this.defaultSeriesVisible = visible; if (notify) { // we create an event with a special flag set...the purpose of // this is to communicate to the plot (the default receiver of // the event) that series visibility has changed so the axis // ranges might need updating... RendererChangeEvent e = new RendererChangeEvent(this, true); notifyListeners(e); } } // SERIES VISIBLE IN LEGEND (not yet respected by all renderers) /** * Returns {@code true} if the series should be shown in the legend, * and {@code false} otherwise. * * @param series the series index. * * @return A boolean. */ public boolean isSeriesVisibleInLegend(int series) { boolean result = this.defaultSeriesVisibleInLegend; Boolean b = this.seriesVisibleInLegendList.getBoolean(series); if (b != null) { result = b; } return result; } /** * Returns the flag that controls whether a series is visible in the * legend. This method returns only the "per series" settings - to * incorporate the default settings as well, you need to use the * {@link #isSeriesVisibleInLegend(int)} method. * * @param series the series index (zero-based). * * @return The flag (possibly {@code null}). * * @see #setSeriesVisibleInLegend(int, Boolean) */ public Boolean getSeriesVisibleInLegend(int series) { return this.seriesVisibleInLegendList.getBoolean(series); } /** * Sets the flag that controls whether a series is visible in the legend * and sends a {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param visible the flag ({@code null} permitted). * * @see #getSeriesVisibleInLegend(int) */ public void setSeriesVisibleInLegend(int series, Boolean visible) { setSeriesVisibleInLegend(series, visible, true); } /** * Sets the flag that controls whether a series is visible in the legend * and, if requested, sends a {@link RendererChangeEvent} to all registered * listeners. * * @param series the series index. * @param visible the flag ({@code null} permitted). * @param notify notify listeners? * * @see #getSeriesVisibleInLegend(int) */ public void setSeriesVisibleInLegend(int series, Boolean visible, boolean notify) { this.seriesVisibleInLegendList.setBoolean(series, visible); if (notify) { fireChangeEvent(); } } /** * Returns the default visibility in the legend for all series. * * @return The default visibility. * * @see #setDefaultSeriesVisibleInLegend(boolean) */ public boolean getDefaultSeriesVisibleInLegend() { return this.defaultSeriesVisibleInLegend; } /** * Sets the default visibility in the legend and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param visible the flag. * * @see #getDefaultSeriesVisibleInLegend() */ public void setDefaultSeriesVisibleInLegend(boolean visible) { // defer argument checking... setDefaultSeriesVisibleInLegend(visible, true); } /** * Sets the default visibility in the legend and, if requested, sends * a {@link RendererChangeEvent} to all registered listeners. * * @param visible the visibility. * @param notify notify listeners? * * @see #getDefaultSeriesVisibleInLegend() */ public void setDefaultSeriesVisibleInLegend(boolean visible, boolean notify) { this.defaultSeriesVisibleInLegend = visible; if (notify) { fireChangeEvent(); } } // PAINT /** * Returns the paint used to fill data items as they are drawn. * (this is typically the same for an entire series). *

* The default implementation passes control to the * {@code lookupSeriesPaint()} method. You can override this method * if you require different behaviour. * * @param row the row (or series) index (zero-based). * @param column the column (or category) index (zero-based). * * @return The paint (never {@code null}). */ public Paint getItemPaint(int row, int column) { return lookupSeriesPaint(row); } /** * Returns the paint used to fill an item drawn by the renderer. * * @param series the series index (zero-based). * * @return The paint (never {@code null}). */ public Paint lookupSeriesPaint(int series) { Paint seriesPaint = getSeriesPaint(series); if (seriesPaint == null && this.autoPopulateSeriesPaint) { DrawingSupplier supplier = getDrawingSupplier(); if (supplier != null) { seriesPaint = supplier.getNextPaint(); setSeriesPaint(series, seriesPaint, false); } } if (seriesPaint == null) { seriesPaint = this.defaultPaint; } return seriesPaint; } /** * Returns the paint used to fill an item drawn by the renderer. * * @param series the series index (zero-based). * * @return The paint (possibly {@code null}). * * @see #setSeriesPaint(int, Paint) */ public Paint getSeriesPaint(int series) { return this.paintList.getPaint(series); } /** * Sets the paint used for a series and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param series the series index (zero-based). * @param paint the paint ({@code null} permitted). * * @see #getSeriesPaint(int) */ public void setSeriesPaint(int series, Paint paint) { setSeriesPaint(series, paint, true); } /** * Sets the paint used for a series and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index. * @param paint the paint ({@code null} permitted). * @param notify notify listeners? * * @see #getSeriesPaint(int) */ public void setSeriesPaint(int series, Paint paint, boolean notify) { this.paintList.setPaint(series, paint); if (notify) { fireChangeEvent(); } } /** * Clears the series paint settings for this renderer and, if requested, * sends a {@link RendererChangeEvent} to all registered listeners. * * @param notify notify listeners? */ public void clearSeriesPaints(boolean notify) { this.paintList.clear(); if (notify) { fireChangeEvent(); } } /** * Returns the default paint. * * @return The default paint (never {@code null}). * * @see #setDefaultPaint(Paint) */ public Paint getDefaultPaint() { return this.defaultPaint; } /** * Sets the default paint and sends a {@link RendererChangeEvent} to all * registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getDefaultPaint() */ public void setDefaultPaint(Paint paint) { // defer argument checking... setDefaultPaint(paint, true); } /** * Sets the default paint and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * @param notify notify listeners? * * @see #getDefaultPaint() */ public void setDefaultPaint(Paint paint, boolean notify) { this.defaultPaint = paint; if (notify) { fireChangeEvent(); } } /** * Returns the flag that controls whether or not the series paint list is * automatically populated when {@link #lookupSeriesPaint(int)} is called. * * @return A boolean. * * @see #setAutoPopulateSeriesPaint(boolean) */ public boolean getAutoPopulateSeriesPaint() { return this.autoPopulateSeriesPaint; } /** * Sets the flag that controls whether or not the series paint list is * automatically populated when {@link #lookupSeriesPaint(int)} is called. * * @param auto the new flag value. * * @see #getAutoPopulateSeriesPaint() */ public void setAutoPopulateSeriesPaint(boolean auto) { this.autoPopulateSeriesPaint = auto; } //// FILL PAINT ////////////////////////////////////////////////////////// /** * Returns the paint used to fill data items as they are drawn. The * default implementation passes control to the * {@link #lookupSeriesFillPaint(int)} method - you can override this * method if you require different behaviour. * * @param row the row (or series) index (zero-based). * @param column the column (or category) index (zero-based). * * @return The paint (never {@code null}). */ public Paint getItemFillPaint(int row, int column) { return lookupSeriesFillPaint(row); } /** * Returns the paint used to fill an item drawn by the renderer. * * @param series the series (zero-based index). * * @return The paint (never {@code null}). */ public Paint lookupSeriesFillPaint(int series) { Paint seriesFillPaint = getSeriesFillPaint(series); if (seriesFillPaint == null && this.autoPopulateSeriesFillPaint) { DrawingSupplier supplier = getDrawingSupplier(); if (supplier != null) { seriesFillPaint = supplier.getNextFillPaint(); setSeriesFillPaint(series, seriesFillPaint, false); } } if (seriesFillPaint == null) { seriesFillPaint = this.defaultFillPaint; } return seriesFillPaint; } /** * Returns the paint used to fill an item drawn by the renderer. * * @param series the series (zero-based index). * * @return The paint (never {@code null}). * * @see #setSeriesFillPaint(int, Paint) */ public Paint getSeriesFillPaint(int series) { return this.fillPaintList.getPaint(series); } /** * Sets the paint used for a series fill and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param paint the paint ({@code null} permitted). * * @see #getSeriesFillPaint(int) */ public void setSeriesFillPaint(int series, Paint paint) { setSeriesFillPaint(series, paint, true); } /** * Sets the paint used to fill a series and, if requested, * sends a {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param paint the paint ({@code null} permitted). * @param notify notify listeners? * * @see #getSeriesFillPaint(int) */ public void setSeriesFillPaint(int series, Paint paint, boolean notify) { this.fillPaintList.setPaint(series, paint); if (notify) { fireChangeEvent(); } } /** * Returns the default fill paint. * * @return The paint (never {@code null}). * * @see #setDefaultFillPaint(Paint) */ public Paint getDefaultFillPaint() { return this.defaultFillPaint; } /** * Sets the default fill paint and sends a {@link RendererChangeEvent} to * all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getDefaultFillPaint() */ public void setDefaultFillPaint(Paint paint) { // defer argument checking... setDefaultFillPaint(paint, true); } /** * Sets the default fill paint and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * @param notify notify listeners? * * @see #getDefaultFillPaint() */ public void setDefaultFillPaint(Paint paint, boolean notify) { Args.nullNotPermitted(paint, "paint"); this.defaultFillPaint = paint; if (notify) { fireChangeEvent(); } } /** * Returns the flag that controls whether or not the series fill paint list * is automatically populated when {@link #lookupSeriesFillPaint(int)} is * called. * * @return A boolean. * * @see #setAutoPopulateSeriesFillPaint(boolean) */ public boolean getAutoPopulateSeriesFillPaint() { return this.autoPopulateSeriesFillPaint; } /** * Sets the flag that controls whether or not the series fill paint list is * automatically populated when {@link #lookupSeriesFillPaint(int)} is * called. * * @param auto the new flag value. * * @see #getAutoPopulateSeriesFillPaint() */ public void setAutoPopulateSeriesFillPaint(boolean auto) { this.autoPopulateSeriesFillPaint = auto; } // OUTLINE PAINT ////////////////////////////////////////////////////////// /** * Returns the paint used to outline data items as they are drawn. * (this is typically the same for an entire series). *

* The default implementation passes control to the * {@link #lookupSeriesOutlinePaint} method. You can override this method * if you require different behaviour. * * @param row the row (or series) index (zero-based). * @param column the column (or category) index (zero-based). * * @return The paint (never {@code null}). */ public Paint getItemOutlinePaint(int row, int column) { return lookupSeriesOutlinePaint(row); } /** * Returns the paint used to outline an item drawn by the renderer. * * @param series the series (zero-based index). * * @return The paint (never {@code null}). */ public Paint lookupSeriesOutlinePaint(int series) { Paint seriesOutlinePaint = getSeriesOutlinePaint(series); if (seriesOutlinePaint == null && this.autoPopulateSeriesOutlinePaint) { DrawingSupplier supplier = getDrawingSupplier(); if (supplier != null) { seriesOutlinePaint = supplier.getNextOutlinePaint(); setSeriesOutlinePaint(series, seriesOutlinePaint, false); } } if (seriesOutlinePaint == null) { seriesOutlinePaint = this.defaultOutlinePaint; } return seriesOutlinePaint; } /** * Returns the paint used to outline an item drawn by the renderer. * * @param series the series (zero-based index). * * @return The paint (possibly {@code null}). * * @see #setSeriesOutlinePaint(int, Paint) */ public Paint getSeriesOutlinePaint(int series) { return this.outlinePaintList.getPaint(series); } /** * Sets the paint used for a series outline and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param paint the paint ({@code null} permitted). * * @see #getSeriesOutlinePaint(int) */ public void setSeriesOutlinePaint(int series, Paint paint) { setSeriesOutlinePaint(series, paint, true); } /** * Sets the paint used to draw the outline for a series and, if requested, * sends a {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param paint the paint ({@code null} permitted). * @param notify notify listeners? * * @see #getSeriesOutlinePaint(int) */ public void setSeriesOutlinePaint(int series, Paint paint, boolean notify) { this.outlinePaintList.setPaint(series, paint); if (notify) { fireChangeEvent(); } } /** * Returns the default outline paint. * * @return The paint (never {@code null}). * * @see #setDefaultOutlinePaint(Paint) */ public Paint getDefaultOutlinePaint() { return this.defaultOutlinePaint; } /** * Sets the default outline paint and sends a {@link RendererChangeEvent} to * all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getDefaultOutlinePaint() */ public void setDefaultOutlinePaint(Paint paint) { // defer argument checking... setDefaultOutlinePaint(paint, true); } /** * Sets the default outline paint and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * @param notify notify listeners? * * @see #getDefaultOutlinePaint() */ public void setDefaultOutlinePaint(Paint paint, boolean notify) { Args.nullNotPermitted(paint, "paint"); this.defaultOutlinePaint = paint; if (notify) { fireChangeEvent(); } } /** * Returns the flag that controls whether or not the series outline paint * list is automatically populated when * {@link #lookupSeriesOutlinePaint(int)} is called. * * @return A boolean. * * @see #setAutoPopulateSeriesOutlinePaint(boolean) */ public boolean getAutoPopulateSeriesOutlinePaint() { return this.autoPopulateSeriesOutlinePaint; } /** * Sets the flag that controls whether or not the series outline paint list * is automatically populated when {@link #lookupSeriesOutlinePaint(int)} * is called. * * @param auto the new flag value. * * @see #getAutoPopulateSeriesOutlinePaint() */ public void setAutoPopulateSeriesOutlinePaint(boolean auto) { this.autoPopulateSeriesOutlinePaint = auto; } // STROKE /** * Returns the stroke used to draw data items. *

* The default implementation passes control to the getSeriesStroke method. * You can override this method if you require different behaviour. * * @param row the row (or series) index (zero-based). * @param column the column (or category) index (zero-based). * * @return The stroke (never {@code null}). */ public Stroke getItemStroke(int row, int column) { return lookupSeriesStroke(row); } /** * Returns the stroke used to draw the items in a series. * * @param series the series (zero-based index). * * @return The stroke (never {@code null}). */ public Stroke lookupSeriesStroke(int series) { Stroke result = getSeriesStroke(series); if (result == null && this.autoPopulateSeriesStroke) { DrawingSupplier supplier = getDrawingSupplier(); if (supplier != null) { result = supplier.getNextStroke(); setSeriesStroke(series, result, false); } } if (result == null) { result = this.defaultStroke; } return result; } /** * Returns the stroke used to draw the items in a series. * * @param series the series (zero-based index). * * @return The stroke (possibly {@code null}). * * @see #setSeriesStroke(int, Stroke) */ public Stroke getSeriesStroke(int series) { return this.strokeList.getStroke(series); } /** * Sets the stroke used for a series and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param series the series index (zero-based). * @param stroke the stroke ({@code null} permitted). * * @see #getSeriesStroke(int) */ public void setSeriesStroke(int series, Stroke stroke) { setSeriesStroke(series, stroke, true); } /** * Sets the stroke for a series and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param stroke the stroke ({@code null} permitted). * @param notify notify listeners? * * @see #getSeriesStroke(int) */ public void setSeriesStroke(int series, Stroke stroke, boolean notify) { this.strokeList.setStroke(series, stroke); if (notify) { fireChangeEvent(); } } /** * Clears the series stroke settings for this renderer and, if requested, * sends a {@link RendererChangeEvent} to all registered listeners. * * @param notify notify listeners? */ public void clearSeriesStrokes(boolean notify) { this.strokeList.clear(); if (notify) { fireChangeEvent(); } } /** * Returns the default stroke. * * @return The default stroke (never {@code null}). * * @see #setDefaultStroke(Stroke) */ public Stroke getDefaultStroke() { return this.defaultStroke; } /** * Sets the default stroke and sends a {@link RendererChangeEvent} to all * registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getDefaultStroke() */ public void setDefaultStroke(Stroke stroke) { // defer argument checking... setDefaultStroke(stroke, true); } /** * Sets the base stroke and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * @param notify notify listeners? * * @see #getDefaultStroke() */ public void setDefaultStroke(Stroke stroke, boolean notify) { Args.nullNotPermitted(stroke, "stroke"); this.defaultStroke = stroke; if (notify) { fireChangeEvent(); } } /** * Returns the flag that controls whether or not the series stroke list is * automatically populated when {@link #lookupSeriesStroke(int)} is called. * * @return A boolean. * * @see #setAutoPopulateSeriesStroke(boolean) */ public boolean getAutoPopulateSeriesStroke() { return this.autoPopulateSeriesStroke; } /** * Sets the flag that controls whether or not the series stroke list is * automatically populated when {@link #lookupSeriesStroke(int)} is called. * * @param auto the new flag value. * * @see #getAutoPopulateSeriesStroke() */ public void setAutoPopulateSeriesStroke(boolean auto) { this.autoPopulateSeriesStroke = auto; } // OUTLINE STROKE /** * Returns the stroke used to outline data items. The default * implementation passes control to the * {@link #lookupSeriesOutlineStroke(int)} method. You can override this * method if you require different behaviour. * * @param row the row (or series) index (zero-based). * @param column the column (or category) index (zero-based). * * @return The stroke (never {@code null}). */ public Stroke getItemOutlineStroke(int row, int column) { return lookupSeriesOutlineStroke(row); } /** * Returns the stroke used to outline the items in a series. * * @param series the series (zero-based index). * * @return The stroke (never {@code null}). */ public Stroke lookupSeriesOutlineStroke(int series) { Stroke result = getSeriesOutlineStroke(series); if (result == null && this.autoPopulateSeriesOutlineStroke) { DrawingSupplier supplier = getDrawingSupplier(); if (supplier != null) { result = supplier.getNextOutlineStroke(); setSeriesOutlineStroke(series, result, false); } } if (result == null) { result = this.defaultOutlineStroke; } return result; } /** * Returns the stroke used to outline the items in a series. * * @param series the series (zero-based index). * * @return The stroke (possibly {@code null}). * * @see #setSeriesOutlineStroke(int, Stroke) */ public Stroke getSeriesOutlineStroke(int series) { return this.outlineStrokeList.getStroke(series); } /** * Sets the outline stroke used for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param stroke the stroke ({@code null} permitted). * * @see #getSeriesOutlineStroke(int) */ public void setSeriesOutlineStroke(int series, Stroke stroke) { setSeriesOutlineStroke(series, stroke, true); } /** * Sets the outline stroke for a series and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index. * @param stroke the stroke ({@code null} permitted). * @param notify notify listeners? * * @see #getSeriesOutlineStroke(int) */ public void setSeriesOutlineStroke(int series, Stroke stroke, boolean notify) { this.outlineStrokeList.setStroke(series, stroke); if (notify) { fireChangeEvent(); } } /** * Returns the default outline stroke. * * @return The stroke (never {@code null}). * * @see #setDefaultOutlineStroke(Stroke) */ public Stroke getDefaultOutlineStroke() { return this.defaultOutlineStroke; } /** * Sets the default outline stroke and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getDefaultOutlineStroke() */ public void setDefaultOutlineStroke(Stroke stroke) { setDefaultOutlineStroke(stroke, true); } /** * Sets the default outline stroke and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * @param notify a flag that controls whether or not listeners are * notified. * * @see #getDefaultOutlineStroke() */ public void setDefaultOutlineStroke(Stroke stroke, boolean notify) { Args.nullNotPermitted(stroke, "stroke"); this.defaultOutlineStroke = stroke; if (notify) { fireChangeEvent(); } } /** * Returns the flag that controls whether or not the series outline stroke * list is automatically populated when * {@link #lookupSeriesOutlineStroke(int)} is called. * * @return A boolean. * * @see #setAutoPopulateSeriesOutlineStroke(boolean) */ public boolean getAutoPopulateSeriesOutlineStroke() { return this.autoPopulateSeriesOutlineStroke; } /** * Sets the flag that controls whether or not the series outline stroke list * is automatically populated when {@link #lookupSeriesOutlineStroke(int)} * is called. * * @param auto the new flag value. * * @see #getAutoPopulateSeriesOutlineStroke() */ public void setAutoPopulateSeriesOutlineStroke(boolean auto) { this.autoPopulateSeriesOutlineStroke = auto; } // SHAPE /** * Returns a shape used to represent a data item. *

* The default implementation passes control to the * {@link #lookupSeriesShape(int)} method. You can override this method if * you require different behaviour. * * @param row the row (or series) index (zero-based). * @param column the column (or category) index (zero-based). * * @return The shape (never {@code null}). */ public Shape getItemShape(int row, int column) { return lookupSeriesShape(row); } /** * Returns a shape used to represent the items in a series. * * @param series the series (zero-based index). * * @return The shape (never {@code null}). */ public Shape lookupSeriesShape(int series) { Shape result = getSeriesShape(series); if (result == null && this.autoPopulateSeriesShape) { DrawingSupplier supplier = getDrawingSupplier(); if (supplier != null) { result = supplier.getNextShape(); setSeriesShape(series, result, false); } } if (result == null) { result = this.defaultShape; } return result; } /** * Returns a shape used to represent the items in a series. * * @param series the series (zero-based index). * * @return The shape (possibly {@code null}). * * @see #setSeriesShape(int, Shape) */ public Shape getSeriesShape(int series) { return this.shapeList.getShape(series); } /** * Sets the shape used for a series and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param series the series index (zero-based). * @param shape the shape ({@code null} permitted). * * @see #getSeriesShape(int) */ public void setSeriesShape(int series, Shape shape) { setSeriesShape(series, shape, true); } /** * Sets the shape for a series and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero based). * @param shape the shape ({@code null} permitted). * @param notify notify listeners? * * @see #getSeriesShape(int) */ public void setSeriesShape(int series, Shape shape, boolean notify) { this.shapeList.setShape(series, shape); if (notify) { fireChangeEvent(); } } /** * Returns the default shape. * * @return The shape (never {@code null}). * * @see #setDefaultShape(Shape) */ public Shape getDefaultShape() { return this.defaultShape; } /** * Sets the default shape and sends a {@link RendererChangeEvent} to all * registered listeners. * * @param shape the shape ({@code null} not permitted). * * @see #getDefaultShape() */ public void setDefaultShape(Shape shape) { // defer argument checking... setDefaultShape(shape, true); } /** * Sets the default shape and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param shape the shape ({@code null} not permitted). * @param notify notify listeners? * * @see #getDefaultShape() */ public void setDefaultShape(Shape shape, boolean notify) { Args.nullNotPermitted(shape, "shape"); this.defaultShape = shape; if (notify) { fireChangeEvent(); } } /** * Returns the flag that controls whether or not the series shape list is * automatically populated when {@link #lookupSeriesShape(int)} is called. * * @return A boolean. * * @see #setAutoPopulateSeriesShape(boolean) */ public boolean getAutoPopulateSeriesShape() { return this.autoPopulateSeriesShape; } /** * Sets the flag that controls whether or not the series shape list is * automatically populated when {@link #lookupSeriesShape(int)} is called. * * @param auto the new flag value. * * @see #getAutoPopulateSeriesShape() */ public void setAutoPopulateSeriesShape(boolean auto) { this.autoPopulateSeriesShape = auto; } // ITEM LABEL VISIBILITY... /** * Returns {@code true} if an item label is visible, and * {@code false} otherwise. * * @param row the row (or series) index (zero-based). * @param column the column (or category) index (zero-based). * * @return A boolean. */ public boolean isItemLabelVisible(int row, int column) { return isSeriesItemLabelsVisible(row); } /** * Returns {@code true} if the item labels for a series are visible, * and {@code false} otherwise. * * @param series the series index (zero-based). * * @return A boolean. */ public boolean isSeriesItemLabelsVisible(int series) { Boolean b = this.itemLabelsVisibleList.getBoolean(series); if (b == null) { return this.defaultItemLabelsVisible; } return b; } /** * Sets a flag that controls the visibility of the item labels for a series, * and sends a {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param visible the flag. */ public void setSeriesItemLabelsVisible(int series, boolean visible) { setSeriesItemLabelsVisible(series, Boolean.valueOf(visible)); } /** * Sets the visibility of the item labels for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param visible the flag ({@code null} permitted). */ public void setSeriesItemLabelsVisible(int series, Boolean visible) { setSeriesItemLabelsVisible(series, visible, true); } /** * Sets the visibility of item labels for a series and, if requested, sends * a {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param visible the visible flag. * @param notify a flag that controls whether or not listeners are * notified. */ public void setSeriesItemLabelsVisible(int series, Boolean visible, boolean notify) { this.itemLabelsVisibleList.setBoolean(series, visible); if (notify) { fireChangeEvent(); } } /** * Returns the base setting for item label visibility. A {@code null} * result should be interpreted as equivalent to {@code Boolean.FALSE}. * * @return A flag (possibly {@code null}). * * @see #setDefaultItemLabelsVisible(boolean) */ public boolean getDefaultItemLabelsVisible() { return this.defaultItemLabelsVisible; } /** * Sets the base flag that controls whether or not item labels are visible, * and sends a {@link RendererChangeEvent} to all registered listeners. * * @param visible the flag. * * @see #getDefaultItemLabelsVisible() */ public void setDefaultItemLabelsVisible(boolean visible) { setDefaultItemLabelsVisible(visible, true); } /** * Sets the base visibility for item labels and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param visible the flag ({@code null} is permitted, and viewed * as equivalent to {@code Boolean.FALSE}). * @param notify a flag that controls whether or not listeners are * notified. * * @see #getDefaultItemLabelsVisible() */ public void setDefaultItemLabelsVisible(boolean visible, boolean notify) { this.defaultItemLabelsVisible = visible; if (notify) { fireChangeEvent(); } } //// ITEM LABEL FONT ////////////////////////////////////////////////////// /** * Returns the font for an item label. * * @param row the row (or series) index (zero-based). * @param column the column (or category) index (zero-based). * * @return The font (never {@code null}). */ public Font getItemLabelFont(int row, int column) { Font result = getSeriesItemLabelFont(row); if (result == null) { result = this.defaultItemLabelFont; } return result; } /** * Returns the font for all the item labels in a series. * * @param series the series index (zero-based). * * @return The font (possibly {@code null}). * * @see #setSeriesItemLabelFont(int, Font) */ public Font getSeriesItemLabelFont(int series) { return this.itemLabelFontMap.get(series); } /** * Sets the item label font for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param font the font ({@code null} permitted). * * @see #getSeriesItemLabelFont(int) */ public void setSeriesItemLabelFont(int series, Font font) { setSeriesItemLabelFont(series, font, true); } /** * Sets the item label font for a series and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero based). * @param font the font ({@code null} permitted). * @param notify a flag that controls whether or not listeners are * notified. * * @see #getSeriesItemLabelFont(int) */ public void setSeriesItemLabelFont(int series, Font font, boolean notify) { this.itemLabelFontMap.put(series, font); if (notify) { fireChangeEvent(); } } /** * Returns the default item label font (this is used when no other font * setting is available). * * @return The font (never {@code null}). * * @see #setDefaultItemLabelFont(Font) */ public Font getDefaultItemLabelFont() { return this.defaultItemLabelFont; } /** * Sets the default item label font and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param font the font ({@code null} not permitted). * * @see #getDefaultItemLabelFont() */ public void setDefaultItemLabelFont(Font font) { Args.nullNotPermitted(font, "font"); setDefaultItemLabelFont(font, true); } /** * Sets the base item label font and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param font the font ({@code null} not permitted). * @param notify a flag that controls whether or not listeners are * notified. * * @see #getDefaultItemLabelFont() */ public void setDefaultItemLabelFont(Font font, boolean notify) { this.defaultItemLabelFont = font; if (notify) { fireChangeEvent(); } } //// ITEM LABEL PAINT //////////////////////////////////////////////////// /** * Returns {@code true} if contrast colors are automatically computed for * item labels. * * @return {@code true} if contrast colors are automatically computed for * item labels. */ public boolean isComputeItemLabelContrastColor() { return computeItemLabelContrastColor; } /** * If {@code auto} is set to {@code true} and * {@link #getItemPaint(int, int)} returns an instance of {@link Color}, a * {@link ChartColor#getContrastColor(Color) contrast color} is computed and * used for the item label. * * @param auto {@code true} if contrast colors should be computed for item * labels. * @see #getItemLabelPaint(int, int) */ public void setComputeItemLabelContrastColor(boolean auto) { this.computeItemLabelContrastColor = auto; } /** * Returns the paint used to draw an item label. * * @param row the row index (zero based). * @param column the column index (zero based). * * @return The paint (never {@code null}). */ public Paint getItemLabelPaint(int row, int column) { Paint result = null; if (this.computeItemLabelContrastColor) { Paint itemPaint = getItemPaint(row, column); if (itemPaint instanceof Color) { result = ChartColor.getContrastColor((Color) itemPaint); } } if (result == null) { result = getSeriesItemLabelPaint(row); } if (result == null) { result = this.defaultItemLabelPaint; } return result; } /** * Returns the paint used to draw the item labels for a series. * * @param series the series index (zero based). * * @return The paint (possibly {@code null}). * * @see #setSeriesItemLabelPaint(int, Paint) */ public Paint getSeriesItemLabelPaint(int series) { return this.itemLabelPaintList.getPaint(series); } /** * Sets the item label paint for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series (zero based index). * @param paint the paint ({@code null} permitted). * * @see #getSeriesItemLabelPaint(int) */ public void setSeriesItemLabelPaint(int series, Paint paint) { setSeriesItemLabelPaint(series, paint, true); } /** * Sets the item label paint for a series and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero based). * @param paint the paint ({@code null} permitted). * @param notify a flag that controls whether or not listeners are * notified. * * @see #getSeriesItemLabelPaint(int) */ public void setSeriesItemLabelPaint(int series, Paint paint, boolean notify) { this.itemLabelPaintList.setPaint(series, paint); if (notify) { fireChangeEvent(); } } /** * Returns the default item label paint. * * @return The paint (never {@code null}). * * @see #setDefaultItemLabelPaint(Paint) */ public Paint getDefaultItemLabelPaint() { return this.defaultItemLabelPaint; } /** * Sets the default item label paint and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getDefaultItemLabelPaint() */ public void setDefaultItemLabelPaint(Paint paint) { // defer argument checking... setDefaultItemLabelPaint(paint, true); } /** * Sets the default item label paint and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners.. * * @param paint the paint ({@code null} not permitted). * @param notify a flag that controls whether or not listeners are * notified. * * @see #getDefaultItemLabelPaint() */ public void setDefaultItemLabelPaint(Paint paint, boolean notify) { Args.nullNotPermitted(paint, "paint"); this.defaultItemLabelPaint = paint; if (notify) { fireChangeEvent(); } } // POSITIVE ITEM LABEL POSITION... /** * Returns the item label position for positive values. * * @param row the row (or series) index (zero-based). * @param column the column (or category) index (zero-based). * * @return The item label position (never {@code null}). * * @see #getNegativeItemLabelPosition(int, int) */ public ItemLabelPosition getPositiveItemLabelPosition(int row, int column) { return getSeriesPositiveItemLabelPosition(row); } /** * Returns the item label position for all positive values in a series. * * @param series the series index (zero-based). * * @return The item label position (never {@code null}). * * @see #setSeriesPositiveItemLabelPosition(int, ItemLabelPosition) */ public ItemLabelPosition getSeriesPositiveItemLabelPosition(int series) { // otherwise look up the position table ItemLabelPosition position = this.positiveItemLabelPositionMap.get(series); if (position == null) { position = this.defaultPositiveItemLabelPosition; } return position; } /** * Sets the item label position for all positive values in a series and * sends a {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param position the position ({@code null} permitted). * * @see #getSeriesPositiveItemLabelPosition(int) */ public void setSeriesPositiveItemLabelPosition(int series, ItemLabelPosition position) { setSeriesPositiveItemLabelPosition(series, position, true); } /** * Sets the item label position for all positive values in a series and (if * requested) sends a {@link RendererChangeEvent} to all registered * listeners. * * @param series the series index (zero-based). * @param position the position ({@code null} permitted). * @param notify notify registered listeners? * * @see #getSeriesPositiveItemLabelPosition(int) */ public void setSeriesPositiveItemLabelPosition(int series, ItemLabelPosition position, boolean notify) { this.positiveItemLabelPositionMap.put(series, position); if (notify) { fireChangeEvent(); } } /** * Returns the default positive item label position. * * @return The position (never {@code null}). * * @see #setDefaultPositiveItemLabelPosition(ItemLabelPosition) */ public ItemLabelPosition getDefaultPositiveItemLabelPosition() { return this.defaultPositiveItemLabelPosition; } /** * Sets the default positive item label position. * * @param position the position ({@code null} not permitted). * * @see #getDefaultPositiveItemLabelPosition() */ public void setDefaultPositiveItemLabelPosition( ItemLabelPosition position) { // defer argument checking... setDefaultPositiveItemLabelPosition(position, true); } /** * Sets the default positive item label position and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param position the position ({@code null} not permitted). * @param notify notify registered listeners? * * @see #getDefaultPositiveItemLabelPosition() */ public void setDefaultPositiveItemLabelPosition(ItemLabelPosition position, boolean notify) { Args.nullNotPermitted(position, "position"); this.defaultPositiveItemLabelPosition = position; if (notify) { fireChangeEvent(); } } // NEGATIVE ITEM LABEL POSITION... /** * Returns the item label position for negative values. This method can be * overridden to provide customisation of the item label position for * individual data items. * * @param row the row (or series) index (zero-based). * @param column the column (or category) index (zero-based). * * @return The item label position (never {@code null}). * * @see #getPositiveItemLabelPosition(int, int) */ public ItemLabelPosition getNegativeItemLabelPosition(int row, int column) { return getSeriesNegativeItemLabelPosition(row); } /** * Returns the item label position for all negative values in a series. * * @param series the series index (zero-based). * * @return The item label position (never {@code null}). * * @see #setSeriesNegativeItemLabelPosition(int, ItemLabelPosition) */ public ItemLabelPosition getSeriesNegativeItemLabelPosition(int series) { // otherwise look up the position list ItemLabelPosition position = this.negativeItemLabelPositionMap.get(series); if (position == null) { position = this.defaultNegativeItemLabelPosition; } return position; } /** * Sets the item label position for negative values in a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param position the position ({@code null} permitted). * * @see #getSeriesNegativeItemLabelPosition(int) */ public void setSeriesNegativeItemLabelPosition(int series, ItemLabelPosition position) { setSeriesNegativeItemLabelPosition(series, position, true); } /** * Sets the item label position for negative values in a series and (if * requested) sends a {@link RendererChangeEvent} to all registered * listeners. * * @param series the series index (zero-based). * @param position the position ({@code null} permitted). * @param notify notify registered listeners? * * @see #getSeriesNegativeItemLabelPosition(int) */ public void setSeriesNegativeItemLabelPosition(int series, ItemLabelPosition position, boolean notify) { this.negativeItemLabelPositionMap.put(series, position); if (notify) { fireChangeEvent(); } } /** * Returns the base item label position for negative values. * * @return The position (never {@code null}). * * @see #setDefaultNegativeItemLabelPosition(ItemLabelPosition) */ public ItemLabelPosition getDefaultNegativeItemLabelPosition() { return this.defaultNegativeItemLabelPosition; } /** * Sets the default item label position for negative values and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param position the position ({@code null} not permitted). * * @see #getDefaultNegativeItemLabelPosition() */ public void setDefaultNegativeItemLabelPosition( ItemLabelPosition position) { setDefaultNegativeItemLabelPosition(position, true); } /** * Sets the default negative item label position and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param position the position ({@code null} not permitted). * @param notify notify registered listeners? * * @see #getDefaultNegativeItemLabelPosition() */ public void setDefaultNegativeItemLabelPosition(ItemLabelPosition position, boolean notify) { Args.nullNotPermitted(position, "position"); this.defaultNegativeItemLabelPosition = position; if (notify) { fireChangeEvent(); } } /** * Returns the item label anchor offset. * * @return The offset. * * @see #setItemLabelAnchorOffset(double) * @deprecated use {@link #getItemLabelInsets()} */ public double getItemLabelAnchorOffset() { return Math.max( Math.max(itemLabelInsets.getTop(), itemLabelInsets.getBottom()), Math.max(itemLabelInsets.getLeft(), itemLabelInsets.getRight())); } /** * Sets the item label anchor offset. * * @param offset the offset. * * @see #getItemLabelAnchorOffset() * @deprecated use {@link #setItemLabelInsets(RectangleInsets)} */ public void setItemLabelAnchorOffset(double offset) { setItemLabelInsets(new RectangleInsets(offset, offset, offset, offset)); } /** * Returns the item label insets. * * @return The item label insets. */ public RectangleInsets getItemLabelInsets() { return itemLabelInsets; } /** * Sets the item label insets. * * @param itemLabelInsets the insets */ public void setItemLabelInsets(RectangleInsets itemLabelInsets) { Args.nullNotPermitted(itemLabelInsets, "itemLabelInsets"); this.itemLabelInsets = itemLabelInsets; fireChangeEvent(); } /** * Returns a boolean that indicates whether or not the specified item * should have a chart entity created for it. * * @param series the series index. * @param item the item index. * * @return A boolean. */ public boolean getItemCreateEntity(int series, int item) { Boolean b = getSeriesCreateEntities(series); if (b != null) { return b; } // otherwise... return this.defaultCreateEntities; } /** * Returns the flag that controls whether entities are created for a * series. * * @param series the series index (zero-based). * * @return The flag (possibly {@code null}). * * @see #setSeriesCreateEntities(int, Boolean) */ public Boolean getSeriesCreateEntities(int series) { return this.createEntitiesList.getBoolean(series); } /** * Sets the flag that controls whether entities are created for a series, * and sends a {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param create the flag ({@code null} permitted). * * @see #getSeriesCreateEntities(int) */ public void setSeriesCreateEntities(int series, Boolean create) { setSeriesCreateEntities(series, create, true); } /** * Sets the flag that controls whether entities are created for a series * and, if requested, sends a {@link RendererChangeEvent} to all registered * listeners. * * @param series the series index. * @param create the flag ({@code null} permitted). * @param notify notify listeners? * * @see #getSeriesCreateEntities(int) */ public void setSeriesCreateEntities(int series, Boolean create, boolean notify) { this.createEntitiesList.setBoolean(series, create); if (notify) { fireChangeEvent(); } } /** * Returns the default flag for creating entities. * * @return The default flag for creating entities. * * @see #setDefaultCreateEntities(boolean) */ public boolean getDefaultCreateEntities() { return this.defaultCreateEntities; } /** * Sets the default flag that controls whether entities are created * for a series, and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param create the flag. * * @see #getDefaultCreateEntities() */ public void setDefaultCreateEntities(boolean create) { // defer argument checking... setDefaultCreateEntities(create, true); } /** * Sets the default flag that controls whether entities are created and, * if requested, sends a {@link RendererChangeEvent} to all registered * listeners. * * @param create the visibility. * @param notify notify listeners? * * @see #getDefaultCreateEntities() */ public void setDefaultCreateEntities(boolean create, boolean notify) { this.defaultCreateEntities = create; if (notify) { fireChangeEvent(); } } /** * Returns the radius of the circle used for the default entity area * when no area is specified. * * @return A radius. * * @see #setDefaultEntityRadius(int) */ public int getDefaultEntityRadius() { return this.defaultEntityRadius; } /** * Sets the radius of the circle used for the default entity area * when no area is specified. * * @param radius the radius. * * @see #getDefaultEntityRadius() */ public void setDefaultEntityRadius(int radius) { this.defaultEntityRadius = radius; } /** * Performs a lookup for the legend shape. * * @param series the series index. * * @return The shape (possibly {@code null}). */ public Shape lookupLegendShape(int series) { Shape result = getLegendShape(series); if (result == null) { result = this.defaultLegendShape; } if (result == null) { result = lookupSeriesShape(series); } return result; } /** * Returns the legend shape defined for the specified series (possibly * {@code null}). * * @param series the series index. * * @return The shape (possibly {@code null}). * * @see #lookupLegendShape(int) */ public Shape getLegendShape(int series) { return this.legendShapeList.getShape(series); } /** * Sets the shape used for the legend item for the specified series, and * sends a {@link RendererChangeEvent} to all registered listeners. * * @param series the series index. * @param shape the shape ({@code null} permitted). */ public void setLegendShape(int series, Shape shape) { this.legendShapeList.setShape(series, shape); fireChangeEvent(); } /** * Returns the default legend shape, which may be {@code null}. * * @return The default legend shape. */ public Shape getDefaultLegendShape() { return this.defaultLegendShape; } /** * Sets the default legend shape and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param shape the shape ({@code null} permitted). */ public void setDefaultLegendShape(Shape shape) { this.defaultLegendShape = shape; fireChangeEvent(); } /** * Returns the flag that controls whether or not the legend shape is * treated as a line when creating legend items. * * @return A boolean. */ protected boolean getTreatLegendShapeAsLine() { return this.treatLegendShapeAsLine; } /** * Sets the flag that controls whether or not the legend shape is * treated as a line when creating legend items. * * @param treatAsLine the new flag value. */ protected void setTreatLegendShapeAsLine(boolean treatAsLine) { if (this.treatLegendShapeAsLine != treatAsLine) { this.treatLegendShapeAsLine = treatAsLine; fireChangeEvent(); } } /** * Performs a lookup for the legend text font. * * @param series the series index. * * @return The font (possibly {@code null}). */ public Font lookupLegendTextFont(int series) { Font result = getLegendTextFont(series); if (result == null) { result = this.defaultLegendTextFont; } return result; } /** * Returns the legend text font defined for the specified series (possibly * {@code null}). * * @param series the series index. * * @return The font (possibly {@code null}). * * @see #lookupLegendTextFont(int) */ public Font getLegendTextFont(int series) { return this.legendTextFontMap.get(series); } /** * Sets the font used for the legend text for the specified series, and * sends a {@link RendererChangeEvent} to all registered listeners. * * @param series the series index. * @param font the font ({@code null} permitted). */ public void setLegendTextFont(int series, Font font) { this.legendTextFontMap.put(series, font); fireChangeEvent(); } /** * Returns the default legend text font, which may be {@code null}. * * @return The default legend text font. */ public Font getDefaultLegendTextFont() { return this.defaultLegendTextFont; } /** * Sets the default legend text font and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param font the font ({@code null} permitted). */ public void setDefaultLegendTextFont(Font font) { Args.nullNotPermitted(font, "font"); this.defaultLegendTextFont = font; fireChangeEvent(); } /** * Performs a lookup for the legend text paint. * * @param series the series index. * * @return The paint (possibly {@code null}). */ public Paint lookupLegendTextPaint(int series) { Paint result = getLegendTextPaint(series); if (result == null) { result = this.defaultLegendTextPaint; } return result; } /** * Returns the legend text paint defined for the specified series (possibly * {@code null}). * * @param series the series index. * * @return The paint (possibly {@code null}). * * @see #lookupLegendTextPaint(int) */ public Paint getLegendTextPaint(int series) { return this.legendTextPaint.getPaint(series); } /** * Sets the paint used for the legend text for the specified series, and * sends a {@link RendererChangeEvent} to all registered listeners. * * @param series the series index. * @param paint the paint ({@code null} permitted). */ public void setLegendTextPaint(int series, Paint paint) { this.legendTextPaint.setPaint(series, paint); fireChangeEvent(); } /** * Returns the default legend text paint, which may be {@code null}. * * @return The default legend text paint. */ public Paint getDefaultLegendTextPaint() { return this.defaultLegendTextPaint; } /** * Sets the default legend text paint and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} permitted). */ public void setDefaultLegendTextPaint(Paint paint) { this.defaultLegendTextPaint = paint; fireChangeEvent(); } /** * Returns the flag that controls whether or not the data bounds reported * by this renderer will exclude non-visible series. * * @return A boolean. */ public boolean getDataBoundsIncludesVisibleSeriesOnly() { return this.dataBoundsIncludesVisibleSeriesOnly; } /** * Sets the flag that controls whether or not the data bounds reported * by this renderer will exclude non-visible series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param visibleOnly include only visible series. */ public void setDataBoundsIncludesVisibleSeriesOnly(boolean visibleOnly) { this.dataBoundsIncludesVisibleSeriesOnly = visibleOnly; notifyListeners(new RendererChangeEvent(this, true)); } /** The adjacent offset. */ private static final double ADJ = Math.cos(Math.PI / 6.0); /** The opposite offset. */ private static final double OPP = Math.sin(Math.PI / 6.0); /** * Calculates the item label anchor point. * * @param anchor the anchor. * @param x the x coordinate. * @param y the y coordinate. * @param orientation the plot orientation. * * @return The anchor point (never {@code null}). */ protected Point2D calculateLabelAnchorPoint(ItemLabelAnchor anchor, double x, double y, PlotOrientation orientation) { Point2D result = null; if (anchor == ItemLabelAnchor.CENTER) { result = new Point2D.Double(x, y); } else if (anchor == ItemLabelAnchor.INSIDE1) { result = new Point2D.Double(x + OPP * this.itemLabelInsets.getLeft(), y - ADJ * this.itemLabelInsets.getTop()); } else if (anchor == ItemLabelAnchor.INSIDE2) { result = new Point2D.Double(x + ADJ * this.itemLabelInsets.getLeft(), y - OPP * this.itemLabelInsets.getTop()); } else if (anchor == ItemLabelAnchor.INSIDE3) { result = new Point2D.Double(x + this.itemLabelInsets.getLeft(), y); } else if (anchor == ItemLabelAnchor.INSIDE4) { result = new Point2D.Double(x + ADJ * this.itemLabelInsets.getLeft(), y + OPP * this.itemLabelInsets.getTop()); } else if (anchor == ItemLabelAnchor.INSIDE5) { result = new Point2D.Double(x + OPP * this.itemLabelInsets.getLeft(), y + ADJ * this.itemLabelInsets.getTop()); } else if (anchor == ItemLabelAnchor.INSIDE6) { result = new Point2D.Double(x, y + this.itemLabelInsets.getTop()); } else if (anchor == ItemLabelAnchor.INSIDE7) { result = new Point2D.Double(x - OPP * this.itemLabelInsets.getLeft(), y + ADJ * this.itemLabelInsets.getTop()); } else if (anchor == ItemLabelAnchor.INSIDE8) { result = new Point2D.Double(x - ADJ * this.itemLabelInsets.getLeft(), y + OPP * this.itemLabelInsets.getTop()); } else if (anchor == ItemLabelAnchor.INSIDE9) { result = new Point2D.Double(x - this.itemLabelInsets.getLeft(), y); } else if (anchor == ItemLabelAnchor.INSIDE10) { result = new Point2D.Double(x - ADJ * this.itemLabelInsets.getLeft(), y - OPP * this.itemLabelInsets.getTop()); } else if (anchor == ItemLabelAnchor.INSIDE11) { result = new Point2D.Double(x - OPP * this.itemLabelInsets.getLeft(), y - ADJ * this.itemLabelInsets.getTop()); } else if (anchor == ItemLabelAnchor.INSIDE12) { result = new Point2D.Double(x, y - this.itemLabelInsets.getTop()); } else if (anchor == ItemLabelAnchor.OUTSIDE1) { result = new Point2D.Double( x + 2.0 * OPP * this.itemLabelInsets.getLeft(), y - 2.0 * ADJ * this.itemLabelInsets.getTop()); } else if (anchor == ItemLabelAnchor.OUTSIDE2) { result = new Point2D.Double( x + 2.0 * ADJ * this.itemLabelInsets.getLeft(), y - 2.0 * OPP * this.itemLabelInsets.getTop()); } else if (anchor == ItemLabelAnchor.OUTSIDE3) { result = new Point2D.Double(x + 2.0 * this.itemLabelInsets.getLeft(), y); } else if (anchor == ItemLabelAnchor.OUTSIDE4) { result = new Point2D.Double( x + 2.0 * ADJ * this.itemLabelInsets.getLeft(), y + 2.0 * OPP * this.itemLabelInsets.getTop()); } else if (anchor == ItemLabelAnchor.OUTSIDE5) { result = new Point2D.Double( x + 2.0 * OPP * this.itemLabelInsets.getLeft(), y + 2.0 * ADJ * this.itemLabelInsets.getTop()); } else if (anchor == ItemLabelAnchor.OUTSIDE6) { result = new Point2D.Double(x, y + 2.0 * this.itemLabelInsets.getTop()); } else if (anchor == ItemLabelAnchor.OUTSIDE7) { result = new Point2D.Double( x - 2.0 * OPP * this.itemLabelInsets.getLeft(), y + 2.0 * ADJ * this.itemLabelInsets.getTop()); } else if (anchor == ItemLabelAnchor.OUTSIDE8) { result = new Point2D.Double( x - 2.0 * ADJ * this.itemLabelInsets.getLeft(), y + 2.0 * OPP * this.itemLabelInsets.getTop()); } else if (anchor == ItemLabelAnchor.OUTSIDE9) { result = new Point2D.Double(x - 2.0 * this.itemLabelInsets.getLeft(), y); } else if (anchor == ItemLabelAnchor.OUTSIDE10) { result = new Point2D.Double( x - 2.0 * ADJ * this.itemLabelInsets.getLeft(), y - 2.0 * OPP * this.itemLabelInsets.getTop()); } else if (anchor == ItemLabelAnchor.OUTSIDE11) { result = new Point2D.Double( x - 2.0 * OPP * this.itemLabelInsets.getLeft(), y - 2.0 * ADJ * this.itemLabelInsets.getTop()); } else if (anchor == ItemLabelAnchor.OUTSIDE12) { result = new Point2D.Double(x, y - 2.0 * this.itemLabelInsets.getTop()); } return result; } /** * Registers an object to receive notification of changes to the renderer. * * @param listener the listener ({@code null} not permitted). * * @see #removeChangeListener(RendererChangeListener) */ public void addChangeListener(RendererChangeListener listener) { Args.nullNotPermitted(listener, "listener"); this.listenerList.add(RendererChangeListener.class, listener); } /** * Deregisters an object so that it no longer receives * notification of changes to the renderer. * * @param listener the object ({@code null} not permitted). * * @see #addChangeListener(RendererChangeListener) */ public void removeChangeListener(RendererChangeListener listener) { Args.nullNotPermitted(listener, "listener"); this.listenerList.remove(RendererChangeListener.class, listener); } /** * Returns {@code true} if the specified object is registered with * the dataset as a listener. Most applications won't need to call this * method, it exists mainly for use by unit testing code. * * @param listener the listener. * * @return A boolean. */ public boolean hasListener(EventListener listener) { List list = Arrays.asList(this.listenerList.getListenerList()); return list.contains(listener); } /** * Sends a {@link RendererChangeEvent} to all registered listeners. */ protected void fireChangeEvent() { notifyListeners(new RendererChangeEvent(this)); } /** * Notifies all registered listeners that the renderer has been modified. * * @param event information about the change event. */ public void notifyListeners(RendererChangeEvent event) { Object[] ls = this.listenerList.getListenerList(); for (int i = ls.length - 2; i >= 0; i -= 2) { if (ls[i] == RendererChangeListener.class) { ((RendererChangeListener) ls[i + 1]).rendererChanged(event); } } } /** * Tests this renderer for equality with another object. * * @param obj the object ({@code null} permitted). * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof AbstractRenderer)) { return false; } AbstractRenderer that = (AbstractRenderer) obj; if (this.dataBoundsIncludesVisibleSeriesOnly != that.dataBoundsIncludesVisibleSeriesOnly) { return false; } if (this.treatLegendShapeAsLine != that.treatLegendShapeAsLine) { return false; } if (this.defaultEntityRadius != that.defaultEntityRadius) { return false; } if (!this.seriesVisibleList.equals(that.seriesVisibleList)) { return false; } if (this.defaultSeriesVisible != that.defaultSeriesVisible) { return false; } if (!this.seriesVisibleInLegendList.equals( that.seriesVisibleInLegendList)) { return false; } if (this.defaultSeriesVisibleInLegend != that.defaultSeriesVisibleInLegend) { return false; } if (!Objects.equals(this.paintList, that.paintList)) { return false; } if (!PaintUtils.equal(this.defaultPaint, that.defaultPaint)) { return false; } if (!Objects.equals(this.fillPaintList, that.fillPaintList)) { return false; } if (!PaintUtils.equal(this.defaultFillPaint, that.defaultFillPaint)) { return false; } if (!Objects.equals(this.outlinePaintList, that.outlinePaintList)) { return false; } if (!PaintUtils.equal(this.defaultOutlinePaint, that.defaultOutlinePaint)) { return false; } if (!Objects.equals(this.strokeList, that.strokeList)) { return false; } if (!Objects.equals(this.defaultStroke, that.defaultStroke)) { return false; } if (!Objects.equals(this.outlineStrokeList, that.outlineStrokeList)) { return false; } if (!Objects.equals(this.defaultOutlineStroke, that.defaultOutlineStroke)) { return false; } if (!Objects.equals(this.shapeList, that.shapeList)) { return false; } if (!ShapeUtils.equal(this.defaultShape, that.defaultShape)) { return false; } if (!Objects.equals(this.itemLabelsVisibleList, that.itemLabelsVisibleList)) { return false; } if (!Objects.equals(this.defaultItemLabelsVisible, that.defaultItemLabelsVisible)) { return false; } if (!Objects.equals(this.itemLabelFontMap, that.itemLabelFontMap)) { return false; } if (!Objects.equals(this.defaultItemLabelFont, that.defaultItemLabelFont)) { return false; } if (!Objects.equals(this.itemLabelPaintList, that.itemLabelPaintList)) { return false; } if (!PaintUtils.equal(this.defaultItemLabelPaint, that.defaultItemLabelPaint)) { return false; } if (!Objects.equals(this.positiveItemLabelPositionMap, that.positiveItemLabelPositionMap)) { return false; } if (!Objects.equals(this.defaultPositiveItemLabelPosition, that.defaultPositiveItemLabelPosition)) { return false; } if (!Objects.equals(this.negativeItemLabelPositionMap, that.negativeItemLabelPositionMap)) { return false; } if (!Objects.equals(this.defaultNegativeItemLabelPosition, that.defaultNegativeItemLabelPosition)) { return false; } if (!Objects.equals(this.itemLabelInsets, that.itemLabelInsets)) { return false; } if (!Objects.equals(this.createEntitiesList, that.createEntitiesList)) { return false; } if (this.defaultCreateEntities != that.defaultCreateEntities) { return false; } if (!Objects.equals(this.legendShapeList, that.legendShapeList)) { return false; } if (!ShapeUtils.equal(this.defaultLegendShape, that.defaultLegendShape)) { return false; } if (!Objects.equals(this.legendTextFontMap, that.legendTextFontMap)) { return false; } if (!Objects.equals(this.defaultLegendTextFont, that.defaultLegendTextFont)) { return false; } if (!Objects.equals(this.legendTextPaint, that.legendTextPaint)) { return false; } if (!PaintUtils.equal(this.defaultLegendTextPaint, that.defaultLegendTextPaint)) { return false; } return true; } /** * Returns a hashcode for the renderer. * * @return The hashcode. */ @Override public int hashCode() { int result = 193; result = HashUtils.hashCode(result, this.seriesVisibleList); result = HashUtils.hashCode(result, this.defaultSeriesVisible); result = HashUtils.hashCode(result, this.seriesVisibleInLegendList); result = HashUtils.hashCode(result, this.defaultSeriesVisibleInLegend); result = HashUtils.hashCode(result, this.paintList); result = HashUtils.hashCode(result, this.defaultPaint); result = HashUtils.hashCode(result, this.fillPaintList); result = HashUtils.hashCode(result, this.defaultFillPaint); result = HashUtils.hashCode(result, this.outlinePaintList); result = HashUtils.hashCode(result, this.defaultOutlinePaint); result = HashUtils.hashCode(result, this.strokeList); result = HashUtils.hashCode(result, this.defaultStroke); result = HashUtils.hashCode(result, this.outlineStrokeList); result = HashUtils.hashCode(result, this.defaultOutlineStroke); // shapeList // baseShape result = HashUtils.hashCode(result, this.itemLabelsVisibleList); result = HashUtils.hashCode(result, this.defaultItemLabelsVisible); // itemLabelFontList // baseItemLabelFont // itemLabelPaintList // baseItemLabelPaint // positiveItemLabelPositionList // basePositiveItemLabelPosition // negativeItemLabelPositionList // baseNegativeItemLabelPosition // itemLabelAnchorOffset // createEntityList // baseCreateEntities return result; } /** * Returns an independent copy of the renderer. * * @return A clone. * * @throws CloneNotSupportedException if some component of the renderer * does not support cloning. */ @Override protected Object clone() throws CloneNotSupportedException { AbstractRenderer clone = (AbstractRenderer) super.clone(); if (this.seriesVisibleList != null) { clone.seriesVisibleList = (BooleanList) this.seriesVisibleList.clone(); } if (this.seriesVisibleInLegendList != null) { clone.seriesVisibleInLegendList = (BooleanList) this.seriesVisibleInLegendList.clone(); } // 'paint' : immutable, no need to clone reference if (this.paintList != null) { clone.paintList = (PaintList) this.paintList.clone(); } // 'basePaint' : immutable, no need to clone reference if (this.fillPaintList != null) { clone.fillPaintList = (PaintList) this.fillPaintList.clone(); } // 'outlinePaint' : immutable, no need to clone reference if (this.outlinePaintList != null) { clone.outlinePaintList = (PaintList) this.outlinePaintList.clone(); } // 'baseOutlinePaint' : immutable, no need to clone reference // 'stroke' : immutable, no need to clone reference if (this.strokeList != null) { clone.strokeList = (StrokeList) this.strokeList.clone(); } // 'baseStroke' : immutable, no need to clone reference // 'outlineStroke' : immutable, no need to clone reference if (this.outlineStrokeList != null) { clone.outlineStrokeList = (StrokeList) this.outlineStrokeList.clone(); } // 'baseOutlineStroke' : immutable, no need to clone reference if (this.shapeList != null) { clone.shapeList = (ShapeList) this.shapeList.clone(); } if (this.defaultShape != null) { clone.defaultShape = ShapeUtils.clone(this.defaultShape); } // 'itemLabelsVisible' : immutable, no need to clone reference if (this.itemLabelsVisibleList != null) { clone.itemLabelsVisibleList = (BooleanList) this.itemLabelsVisibleList.clone(); } // 'basePaint' : immutable, no need to clone reference // 'itemLabelFont' : immutable, no need to clone reference if (this.itemLabelFontMap != null) { clone.itemLabelFontMap = new HashMap<>(this.itemLabelFontMap); } // 'baseItemLabelFont' : immutable, no need to clone reference // 'itemLabelPaint' : immutable, no need to clone reference if (this.itemLabelPaintList != null) { clone.itemLabelPaintList = (PaintList) this.itemLabelPaintList.clone(); } // 'baseItemLabelPaint' : immutable, no need to clone reference if (this.positiveItemLabelPositionMap != null) { clone.positiveItemLabelPositionMap = new HashMap<>(this.positiveItemLabelPositionMap); } if (this.negativeItemLabelPositionMap != null) { clone.negativeItemLabelPositionMap = new HashMap<>(this.negativeItemLabelPositionMap); } if (this.createEntitiesList != null) { clone.createEntitiesList = (BooleanList) this.createEntitiesList.clone(); } if (this.legendShapeList != null) { clone.legendShapeList = (ShapeList) this.legendShapeList.clone(); } if (this.legendTextFontMap != null) { // Font objects are immutable so just shallow copy the map clone.legendTextFontMap = new HashMap<>(this.legendTextFontMap); } if (this.legendTextPaint != null) { clone.legendTextPaint = (PaintList) this.legendTextPaint.clone(); } clone.listenerList = new EventListenerList(); clone.event = null; return clone; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.defaultPaint, stream); SerialUtils.writePaint(this.defaultFillPaint, stream); SerialUtils.writePaint(this.defaultOutlinePaint, stream); SerialUtils.writeStroke(this.defaultStroke, stream); SerialUtils.writeStroke(this.defaultOutlineStroke, stream); SerialUtils.writeShape(this.defaultShape, stream); SerialUtils.writePaint(this.defaultItemLabelPaint, stream); SerialUtils.writeShape(this.defaultLegendShape, stream); SerialUtils.writePaint(this.defaultLegendTextPaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.defaultPaint = SerialUtils.readPaint(stream); this.defaultFillPaint = SerialUtils.readPaint(stream); this.defaultOutlinePaint = SerialUtils.readPaint(stream); this.defaultStroke = SerialUtils.readStroke(stream); this.defaultOutlineStroke = SerialUtils.readStroke(stream); this.defaultShape = SerialUtils.readShape(stream); this.defaultItemLabelPaint = SerialUtils.readPaint(stream); this.defaultLegendShape = SerialUtils.readShape(stream); this.defaultLegendTextPaint = SerialUtils.readPaint(stream); // listeners are not restored automatically, but storage must be // provided... this.listenerList = new EventListenerList(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/AreaRendererEndType.java000066400000000000000000000100571463604235500313620ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * AreaRendererEndType.java * ------------------------ * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer; import java.io.ObjectStreamException; import java.io.Serializable; /** * An enumeration of the 'end types' for an area renderer. */ public final class AreaRendererEndType implements Serializable { /** For serialization. */ private static final long serialVersionUID = -1774146392916359839L; /** * The area tapers from the first or last value down to zero. */ public static final AreaRendererEndType TAPER = new AreaRendererEndType( "AreaRendererEndType.TAPER"); /** * The area is truncated at the first or last value. */ public static final AreaRendererEndType TRUNCATE = new AreaRendererEndType( "AreaRendererEndType.TRUNCATE"); /** * The area is levelled at the first or last value. */ public static final AreaRendererEndType LEVEL = new AreaRendererEndType( "AreaRendererEndType.LEVEL"); /** The name. */ private final String name; /** * Private constructor. * * @param name the name. */ private AreaRendererEndType(String name) { this.name = name; } /** * Returns a string representing the object. * * @return The string. */ @Override public String toString() { return this.name; } /** * Returns {@code true} if this object is equal to the specified * object, and {@code false} otherwise. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof AreaRendererEndType)) { return false; } AreaRendererEndType that = (AreaRendererEndType) obj; if (!this.name.equals(that.toString())) { return false; } return true; } /** * Ensures that serialization returns the unique instances. * * @return The object. * * @throws ObjectStreamException if there is a problem. */ private Object readResolve() throws ObjectStreamException { Object result = null; if (this.equals(AreaRendererEndType.LEVEL)) { result = AreaRendererEndType.LEVEL; } else if (this.equals(AreaRendererEndType.TAPER)) { result = AreaRendererEndType.TAPER; } else if (this.equals(AreaRendererEndType.TRUNCATE)) { result = AreaRendererEndType.TRUNCATE; } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/DefaultPolarItemRenderer.java000066400000000000000000000734201463604235500324250ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------- * DefaultPolarItemRenderer.java * ----------------------------- * (C) Copyright 2004-present, by Solution Engineering, Inc. and * Contributors. * * Original Author: Daniel Bridenbecker, Solution Engineering, Inc.; * Contributor(s): David Gilbert; * Martin Hoeller (patch 2850344); * */ package org.jfree.chart.renderer; import java.awt.AlphaComposite; import java.awt.Composite; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Point; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Ellipse2D; import java.awt.geom.GeneralPath; import java.awt.geom.Line2D; import java.awt.geom.PathIterator; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Iterator; import java.util.List; import java.util.Objects; import org.jfree.chart.LegendItem; import org.jfree.chart.axis.NumberTick; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.entity.XYItemEntity; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.labels.XYSeriesLabelGenerator; import org.jfree.chart.labels.XYToolTipGenerator; import org.jfree.chart.plot.DrawingSupplier; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.PolarPlot; import org.jfree.chart.text.TextUtils; import org.jfree.chart.urls.XYURLGenerator; import org.jfree.chart.util.BooleanList; import org.jfree.chart.util.ObjectList; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; import org.jfree.chart.util.ShapeUtils; import org.jfree.data.xy.XYDataset; /** * A renderer that can be used with the {@link PolarPlot} class. */ public class DefaultPolarItemRenderer extends AbstractRenderer implements PolarItemRenderer { /** The plot that the renderer is assigned to. */ private PolarPlot plot; /** Flags that control whether the renderer fills each series or not. */ private BooleanList seriesFilled; /** * Flag that controls whether an outline is drawn for filled series or * not. */ private boolean drawOutlineWhenFilled; /** * The composite to use when filling series. */ private transient Composite fillComposite; /** * A flag that controls whether the fill paint is used for filling * shapes. */ private boolean useFillPaint; /** * The shape that is used to represent a line in the legend. */ private transient Shape legendLine; /** * Flag that controls whether item shapes are visible or not. */ private boolean shapesVisible; /** * Flag that controls if the first and last point of the dataset should be * connected or not. */ private boolean connectFirstAndLastPoint; /** * A list of tool tip generators (one per series). */ private ObjectList toolTipGeneratorList; /** * The base tool tip generator. */ private XYToolTipGenerator baseToolTipGenerator; /** * The URL text generator. */ private XYURLGenerator urlGenerator; /** * The legend item tool tip generator. */ private XYSeriesLabelGenerator legendItemToolTipGenerator; /** * The legend item URL generator. */ private XYSeriesLabelGenerator legendItemURLGenerator; /** * Creates a new instance of DefaultPolarItemRenderer */ public DefaultPolarItemRenderer() { this.seriesFilled = new BooleanList(); this.drawOutlineWhenFilled = true; this.fillComposite = AlphaComposite.getInstance( AlphaComposite.SRC_OVER, 0.3f); this.useFillPaint = false; // use item paint for fills by default this.legendLine = new Line2D.Double(-7.0, 0.0, 7.0, 0.0); this.shapesVisible = true; this.connectFirstAndLastPoint = true; this.toolTipGeneratorList = new ObjectList(); this.urlGenerator = null; this.legendItemToolTipGenerator = null; this.legendItemURLGenerator = null; } /** * Set the plot associated with this renderer. * * @param plot the plot. * * @see #getPlot() */ @Override public void setPlot(PolarPlot plot) { this.plot = plot; } /** * Return the plot associated with this renderer. * * @return The plot. * * @see #setPlot(PolarPlot) */ @Override public PolarPlot getPlot() { return this.plot; } /** * Returns {@code true} if the renderer will draw an outline around * a filled polygon, {@code false} otherwise. * * @return A boolean. */ public boolean getDrawOutlineWhenFilled() { return this.drawOutlineWhenFilled; } /** * Set the flag that controls whether the outline around a filled * polygon will be drawn or not and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param drawOutlineWhenFilled the flag. */ public void setDrawOutlineWhenFilled(boolean drawOutlineWhenFilled) { this.drawOutlineWhenFilled = drawOutlineWhenFilled; fireChangeEvent(); } /** * Get the composite that is used for filling. * * @return The composite (never {@code null}). */ public Composite getFillComposite() { return this.fillComposite; } /** * Sets the composite which will be used for filling polygons and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param composite the composite to use ({@code null} not * permitted). */ public void setFillComposite(Composite composite) { Args.nullNotPermitted(composite, "composite"); this.fillComposite = composite; fireChangeEvent(); } /** * Returns {@code true} if a shape will be drawn for every item, or * {@code false} if not. * * @return A boolean. */ public boolean getShapesVisible() { return this.shapesVisible; } /** * Set the flag that controls whether a shape will be drawn for every * item, or not and sends a {@link RendererChangeEvent} to all registered * listeners. * * @param visible the flag. */ public void setShapesVisible(boolean visible) { this.shapesVisible = visible; fireChangeEvent(); } /** * Returns {@code true} if first and last point of a series will be * connected, {@code false} otherwise. * * @return The current status of the flag. */ public boolean getConnectFirstAndLastPoint() { return this.connectFirstAndLastPoint; } /** * Set the flag that controls whether the first and last point of a series * will be connected or not and sends a {@link RendererChangeEvent} to all * registered listeners. * * @param connect the flag. */ public void setConnectFirstAndLastPoint(boolean connect) { this.connectFirstAndLastPoint = connect; fireChangeEvent(); } /** * Returns the drawing supplier from the plot. * * @return The drawing supplier. */ @Override public DrawingSupplier getDrawingSupplier() { DrawingSupplier result = null; PolarPlot p = getPlot(); if (p != null) { result = p.getDrawingSupplier(); } return result; } /** * Returns {@code true} if the renderer should fill the specified * series, and {@code false} otherwise. * * @param series the series index (zero-based). * * @return A boolean. */ public boolean isSeriesFilled(int series) { boolean result = false; Boolean b = this.seriesFilled.getBoolean(series); if (b != null) { result = b; } return result; } /** * Sets a flag that controls whether or not a series is filled. * * @param series the series index. * @param filled the flag. */ public void setSeriesFilled(int series, boolean filled) { this.seriesFilled.setBoolean(series, filled); } /** * Returns {@code true} if the renderer should use the fill paint * setting to fill shapes, and {@code false} if it should just * use the regular paint. * * @return A boolean. * * @see #setUseFillPaint(boolean) */ public boolean getUseFillPaint() { return this.useFillPaint; } /** * Sets the flag that controls whether the fill paint is used to fill * shapes, and sends a {@link RendererChangeEvent} to all * registered listeners. * * @param flag the flag. * * @see #getUseFillPaint() */ public void setUseFillPaint(boolean flag) { this.useFillPaint = flag; fireChangeEvent(); } /** * Returns the shape used to represent a line in the legend. * * @return The legend line (never {@code null}). * * @see #setLegendLine(Shape) */ public Shape getLegendLine() { return this.legendLine; } /** * Sets the shape used as a line in each legend item and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param line the line ({@code null} not permitted). * * @see #getLegendLine() */ public void setLegendLine(Shape line) { Args.nullNotPermitted(line, "line"); this.legendLine = line; fireChangeEvent(); } /** * Adds an entity to the collection. * * @param entities the entity collection being populated. * @param area the entity area (if {@code null} a default will be * used). * @param dataset the dataset. * @param series the series. * @param item the item. * @param entityX the entity's center x-coordinate in user space (only * used if {@code area} is {@code null}). * @param entityY the entity's center y-coordinate in user space (only * used if {@code area} is {@code null}). */ protected void addEntity(EntityCollection entities, Shape area, XYDataset dataset, int series, int item, double entityX, double entityY) { if (!getItemCreateEntity(series, item)) { return; } Shape hotspot = area; if (hotspot == null) { double r = getDefaultEntityRadius(); double w = r * 2; if (getPlot().getOrientation() == PlotOrientation.VERTICAL) { hotspot = new Ellipse2D.Double(entityX - r, entityY - r, w, w); } else { hotspot = new Ellipse2D.Double(entityY - r, entityX - r, w, w); } } String tip = null; XYToolTipGenerator generator = getToolTipGenerator(series, item); if (generator != null) { tip = generator.generateToolTip(dataset, series, item); } String url = null; if (getURLGenerator() != null) { url = getURLGenerator().generateURL(dataset, series, item); } XYItemEntity entity = new XYItemEntity(hotspot, dataset, series, item, tip, url); entities.add(entity); } /** * Plots the data for a given series. * * @param g2 the drawing surface. * @param dataArea the data area. * @param info collects plot rendering info. * @param plot the plot. * @param dataset the dataset. * @param seriesIndex the series index. */ @Override public void drawSeries(Graphics2D g2, Rectangle2D dataArea, PlotRenderingInfo info, PolarPlot plot, XYDataset dataset, int seriesIndex) { final int numPoints = dataset.getItemCount(seriesIndex); if (numPoints == 0) { return; } GeneralPath poly = null; ValueAxis axis = plot.getAxisForDataset(plot.indexOf(dataset)); for (int i = 0; i < numPoints; i++) { double theta = dataset.getXValue(seriesIndex, i); double radius = dataset.getYValue(seriesIndex, i); Point p = plot.translateToJava2D(theta, radius, axis, dataArea); if (poly == null) { poly = new GeneralPath(); poly.moveTo(p.x, p.y); } else { poly.lineTo(p.x, p.y); } } assert poly != null; if (getConnectFirstAndLastPoint()) { poly.closePath(); } g2.setPaint(lookupSeriesPaint(seriesIndex)); g2.setStroke(lookupSeriesStroke(seriesIndex)); if (isSeriesFilled(seriesIndex)) { Composite savedComposite = g2.getComposite(); g2.setComposite(this.fillComposite); g2.fill(poly); g2.setComposite(savedComposite); if (this.drawOutlineWhenFilled) { // draw the outline of the filled polygon g2.setPaint(lookupSeriesOutlinePaint(seriesIndex)); g2.draw(poly); } } else { // just the lines, no filling g2.draw(poly); } // draw the item shapes if (this.shapesVisible) { // setup for collecting optional entity info... EntityCollection entities = null; if (info != null) { entities = info.getOwner().getEntityCollection(); } PathIterator pi = poly.getPathIterator(null); int i = 0; while (!pi.isDone()) { final float[] coords = new float[6]; final int segType = pi.currentSegment(coords); pi.next(); if (segType != PathIterator.SEG_LINETO && segType != PathIterator.SEG_MOVETO) { continue; } final int x = Math.round(coords[0]); final int y = Math.round(coords[1]); final Shape shape = ShapeUtils.createTranslatedShape( getItemShape(seriesIndex, i++), x, y); Paint paint; if (useFillPaint) { paint = lookupSeriesFillPaint(seriesIndex); } else { paint = lookupSeriesPaint(seriesIndex); } g2.setPaint(paint); g2.fill(shape); if (isSeriesFilled(seriesIndex) && this.drawOutlineWhenFilled) { g2.setPaint(lookupSeriesOutlinePaint(seriesIndex)); g2.setStroke(lookupSeriesOutlineStroke(seriesIndex)); g2.draw(shape); } // add an entity for the item, but only if it falls within the // data area... if (entities != null && ShapeUtils.isPointInRect(dataArea, x, y)) { addEntity(entities, shape, dataset, seriesIndex, i-1, x, y); } } } } /** * Draw the angular gridlines - the spokes. * * @param g2 the drawing surface. * @param plot the plot ({@code null} not permitted). * @param ticks the ticks ({@code null} not permitted). * @param dataArea the data area. */ @Override public void drawAngularGridLines(Graphics2D g2, PolarPlot plot, List ticks, Rectangle2D dataArea) { g2.setFont(plot.getAngleLabelFont()); g2.setStroke(plot.getAngleGridlineStroke()); g2.setPaint(plot.getAngleGridlinePaint()); ValueAxis axis = plot.getAxis(); double centerValue, outerValue; if (axis.isInverted()) { outerValue = axis.getLowerBound(); centerValue = axis.getUpperBound(); } else { outerValue = axis.getUpperBound(); centerValue = axis.getLowerBound(); } Point center = plot.translateToJava2D(0, centerValue, axis, dataArea); Iterator iterator = ticks.iterator(); while (iterator.hasNext()) { NumberTick tick = (NumberTick) iterator.next(); double tickVal = tick.getNumber().doubleValue(); Point p = plot.translateToJava2D(tickVal, outerValue, axis, dataArea); g2.setPaint(plot.getAngleGridlinePaint()); g2.drawLine(center.x, center.y, p.x, p.y); if (plot.isAngleLabelsVisible()) { int x = p.x; int y = p.y; g2.setPaint(plot.getAngleLabelPaint()); TextUtils.drawAlignedString(tick.getText(), g2, x, y, tick.getTextAnchor()); } } } /** * Draw the radial gridlines - the rings. * * @param g2 the drawing surface ({@code null} not permitted). * @param plot the plot ({@code null} not permitted). * @param radialAxis the radial axis ({@code null} not permitted). * @param ticks the ticks ({@code null} not permitted). * @param dataArea the data area. */ @Override public void drawRadialGridLines(Graphics2D g2, PolarPlot plot, ValueAxis radialAxis, List ticks, Rectangle2D dataArea) { Args.nullNotPermitted(radialAxis, "radialAxis"); g2.setFont(radialAxis.getTickLabelFont()); g2.setPaint(plot.getRadiusGridlinePaint()); g2.setStroke(plot.getRadiusGridlineStroke()); double centerValue; if (radialAxis.isInverted()) { centerValue = radialAxis.getUpperBound(); } else { centerValue = radialAxis.getLowerBound(); } Point center = plot.translateToJava2D(0, centerValue, radialAxis, dataArea); Iterator iterator = ticks.iterator(); while (iterator.hasNext()) { NumberTick tick = (NumberTick) iterator.next(); double angleDegrees = plot.isCounterClockwise() ? plot.getAngleOffset() : -plot.getAngleOffset(); Point p = plot.translateToJava2D(angleDegrees, tick.getNumber().doubleValue(), radialAxis, dataArea); int r = p.x - center.x; int upperLeftX = center.x - r; int upperLeftY = center.y - r; int d = 2 * r; Ellipse2D ring = new Ellipse2D.Double(upperLeftX, upperLeftY, d, d); g2.setPaint(plot.getRadiusGridlinePaint()); g2.draw(ring); } } /** * Return the legend for the given series. * * @param series the series index. * * @return The legend item. */ @Override public LegendItem getLegendItem(int series) { LegendItem result; PolarPlot p = getPlot(); if (p == null) { return null; } XYDataset dataset = p.getDataset(p.getIndexOf(this)); if (dataset == null) { return null; } String toolTipText = null; if (getLegendItemToolTipGenerator() != null) { toolTipText = getLegendItemToolTipGenerator().generateLabel( dataset, series); } String urlText = null; if (getLegendItemURLGenerator() != null) { urlText = getLegendItemURLGenerator().generateLabel(dataset, series); } Comparable seriesKey = dataset.getSeriesKey(series); String label = seriesKey.toString(); String description = label; Shape shape = lookupSeriesShape(series); Paint paint; if (this.useFillPaint) { paint = lookupSeriesFillPaint(series); } else { paint = lookupSeriesPaint(series); } Stroke stroke = lookupSeriesStroke(series); Paint outlinePaint = lookupSeriesOutlinePaint(series); Stroke outlineStroke = lookupSeriesOutlineStroke(series); boolean shapeOutlined = isSeriesFilled(series) && this.drawOutlineWhenFilled; result = new LegendItem(label, description, toolTipText, urlText, getShapesVisible(), shape, /* shapeFilled=*/ true, paint, shapeOutlined, outlinePaint, outlineStroke, /* lineVisible= */ true, this.legendLine, stroke, paint); result.setToolTipText(toolTipText); result.setURLText(urlText); result.setDataset(dataset); result.setSeriesKey(seriesKey); result.setSeriesIndex(series); return result; } /** * Returns the tooltip generator for the specified series and item. * * @param series the series index. * @param item the item index. * * @return The tooltip generator (possibly {@code null}). */ @Override public XYToolTipGenerator getToolTipGenerator(int series, int item) { XYToolTipGenerator generator = (XYToolTipGenerator) this.toolTipGeneratorList.get(series); if (generator == null) { generator = this.baseToolTipGenerator; } return generator; } /** * Returns the tool tip generator for the specified series. * * @return The tooltip generator (possibly {@code null}). */ @Override public XYToolTipGenerator getSeriesToolTipGenerator(int series) { return (XYToolTipGenerator) this.toolTipGeneratorList.get(series); } /** * Sets the tooltip generator for the specified series. * * @param series the series index. * @param generator the tool tip generator ({@code null} permitted). */ @Override public void setSeriesToolTipGenerator(int series, XYToolTipGenerator generator) { this.toolTipGeneratorList.set(series, generator); fireChangeEvent(); } /** * Returns the default tool tip generator. * * @return The default tool tip generator (possibly {@code null}). */ @Override public XYToolTipGenerator getBaseToolTipGenerator() { return this.baseToolTipGenerator; } /** * Sets the default tool tip generator and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param generator the generator ({@code null} permitted). */ @Override public void setBaseToolTipGenerator(XYToolTipGenerator generator) { this.baseToolTipGenerator = generator; fireChangeEvent(); } /** * Returns the URL generator. * * @return The URL generator (possibly {@code null}). */ @Override public XYURLGenerator getURLGenerator() { return this.urlGenerator; } /** * Sets the URL generator. * * @param urlGenerator the generator ({@code null} permitted) */ @Override public void setURLGenerator(XYURLGenerator urlGenerator) { this.urlGenerator = urlGenerator; fireChangeEvent(); } /** * Returns the legend item tool tip generator. * * @return The tool tip generator (possibly {@code null}). * * @see #setLegendItemToolTipGenerator(XYSeriesLabelGenerator) */ public XYSeriesLabelGenerator getLegendItemToolTipGenerator() { return this.legendItemToolTipGenerator; } /** * Sets the legend item tool tip generator and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param generator the generator ({@code null} permitted). * * @see #getLegendItemToolTipGenerator() */ public void setLegendItemToolTipGenerator( XYSeriesLabelGenerator generator) { this.legendItemToolTipGenerator = generator; fireChangeEvent(); } /** * Returns the legend item URL generator. * * @return The URL generator (possibly {@code null}). * * @see #setLegendItemURLGenerator(XYSeriesLabelGenerator) */ public XYSeriesLabelGenerator getLegendItemURLGenerator() { return this.legendItemURLGenerator; } /** * Sets the legend item URL generator and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param generator the generator ({@code null} permitted). * * @see #getLegendItemURLGenerator() */ public void setLegendItemURLGenerator(XYSeriesLabelGenerator generator) { this.legendItemURLGenerator = generator; fireChangeEvent(); } /** * Tests this renderer for equality with an arbitrary object. * * @param obj the object ({@code null} not permitted). * * @return {@code true} if this renderer is equal to {@code obj}, * and {@code false} otherwise. */ @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (!(obj instanceof DefaultPolarItemRenderer)) { return false; } DefaultPolarItemRenderer that = (DefaultPolarItemRenderer) obj; if (!this.seriesFilled.equals(that.seriesFilled)) { return false; } if (this.drawOutlineWhenFilled != that.drawOutlineWhenFilled) { return false; } if (!Objects.equals(this.fillComposite, that.fillComposite)) { return false; } if (this.useFillPaint != that.useFillPaint) { return false; } if (!ShapeUtils.equal(this.legendLine, that.legendLine)) { return false; } if (this.shapesVisible != that.shapesVisible) { return false; } if (this.connectFirstAndLastPoint != that.connectFirstAndLastPoint) { return false; } if (!this.toolTipGeneratorList.equals(that.toolTipGeneratorList)) { return false; } if (!Objects.equals(this.baseToolTipGenerator, that.baseToolTipGenerator)) { return false; } if (!Objects.equals(this.urlGenerator, that.urlGenerator)) { return false; } if (!Objects.equals(this.legendItemToolTipGenerator, that.legendItemToolTipGenerator)) { return false; } if (!Objects.equals(this.legendItemURLGenerator, that.legendItemURLGenerator)) { return false; } return super.equals(obj); } /** * Returns a clone of the renderer. * * @return A clone. * * @throws CloneNotSupportedException if the renderer cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { DefaultPolarItemRenderer clone = (DefaultPolarItemRenderer) super.clone(); if (this.legendLine != null) { clone.legendLine = ShapeUtils.clone(this.legendLine); } clone.seriesFilled = (BooleanList) this.seriesFilled.clone(); clone.toolTipGeneratorList = (ObjectList) this.toolTipGeneratorList.clone(); if (clone.baseToolTipGenerator instanceof PublicCloneable) { clone.baseToolTipGenerator = (XYToolTipGenerator) ObjectUtils.clone(this.baseToolTipGenerator); } if (clone.urlGenerator instanceof PublicCloneable) { clone.urlGenerator = (XYURLGenerator) ObjectUtils.clone(this.urlGenerator); } if (clone.legendItemToolTipGenerator instanceof PublicCloneable) { clone.legendItemToolTipGenerator = (XYSeriesLabelGenerator) ObjectUtils.clone(this.legendItemToolTipGenerator); } if (clone.legendItemURLGenerator instanceof PublicCloneable) { clone.legendItemURLGenerator = (XYSeriesLabelGenerator) ObjectUtils.clone(this.legendItemURLGenerator); } return clone; } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.legendLine = SerialUtils.readShape(stream); this.fillComposite = SerialUtils.readComposite(stream); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeShape(this.legendLine, stream); SerialUtils.writeComposite(this.fillComposite, stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/GrayPaintScale.java000066400000000000000000000147021463604235500304010ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * GrayPaintScale.java * ------------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer; import java.awt.Color; import java.awt.Paint; import java.io.Serializable; import org.jfree.chart.HashUtils; import org.jfree.chart.util.PublicCloneable; /** * A paint scale that returns shades of gray. */ public class GrayPaintScale implements PaintScale, PublicCloneable, Serializable { /** The lower bound. */ private double lowerBound; /** The upper bound. */ private double upperBound; /** * The alpha transparency (0-255). */ private int alpha; /** * Creates a new {@code GrayPaintScale} instance with default values. */ public GrayPaintScale() { this(0.0, 1.0); } /** * Creates a new paint scale for values in the specified range. * * @param lowerBound the lower bound. * @param upperBound the upper bound. * * @throws IllegalArgumentException if {@code lowerBound} is not * less than {@code upperBound}. */ public GrayPaintScale(double lowerBound, double upperBound) { this(lowerBound, upperBound, 255); } /** * Creates a new paint scale for values in the specified range. * * @param lowerBound the lower bound. * @param upperBound the upper bound. * @param alpha the alpha transparency (0-255). * * @throws IllegalArgumentException if {@code lowerBound} is not * less than {@code upperBound}, or {@code alpha} is not in * the range 0 to 255. */ public GrayPaintScale(double lowerBound, double upperBound, int alpha) { if (lowerBound >= upperBound) { throw new IllegalArgumentException( "Requires lowerBound < upperBound."); } if (alpha < 0 || alpha > 255) { throw new IllegalArgumentException( "Requires alpha in the range 0 to 255."); } this.lowerBound = lowerBound; this.upperBound = upperBound; this.alpha = alpha; } /** * Returns the lower bound. * * @return The lower bound. * * @see #getUpperBound() */ @Override public double getLowerBound() { return this.lowerBound; } /** * Returns the upper bound. * * @return The upper bound. * * @see #getLowerBound() */ @Override public double getUpperBound() { return this.upperBound; } /** * Returns the alpha transparency that was specified in the constructor. * * @return The alpha transparency (in the range 0 to 255). */ public int getAlpha() { return this.alpha; } /** * Returns a paint for the specified value. * * @param value the value (must be within the range specified by the * lower and upper bounds for the scale). * * @return A paint for the specified value. */ @Override public Paint getPaint(double value) { double v = Math.max(value, this.lowerBound); v = Math.min(v, this.upperBound); int g = (int) ((v - this.lowerBound) / (this.upperBound - this.lowerBound) * 255.0); // FIXME: it probably makes sense to allocate an array of 256 Colors // and lazily populate this array... return new Color(g, g, g, this.alpha); } /** * Tests this {@code GrayPaintScale} instance for equality with an * arbitrary object. This method returns {@code true} if and only * if: *
    *
  • {@code obj} is not {@code null};
  • *
  • {@code obj} is an instance of {@code GrayPaintScale};
  • *
* * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof GrayPaintScale)) { return false; } GrayPaintScale that = (GrayPaintScale) obj; if (this.lowerBound != that.lowerBound) { return false; } if (this.upperBound != that.upperBound) { return false; } if (this.alpha != that.alpha) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int hash = 7; hash = HashUtils.hashCode(hash, this.lowerBound); hash = HashUtils.hashCode(hash, this.upperBound); hash = 43 * hash + this.alpha; return hash; } /** * Returns a clone of this {@code GrayPaintScale} instance. * * @return A clone. * * @throws CloneNotSupportedException if there is a problem cloning this * instance. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/LookupPaintScale.java000066400000000000000000000256301463604235500307520ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * LookupPaintScale.java * --------------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer; import java.awt.Color; import java.awt.Paint; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Collections; import java.util.List; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; /** * A paint scale that uses a lookup table to associate paint instances * with data value ranges. */ public class LookupPaintScale implements PaintScale, PublicCloneable, Serializable { /** * Stores the paint for a value. */ static class PaintItem implements Comparable, Serializable { /** For serialization. */ static final long serialVersionUID = 698920578512361570L; /** The value. */ double value; /** The paint. */ transient Paint paint; /** * Creates a new instance. * * @param value the value. * @param paint the paint. */ public PaintItem(double value, Paint paint) { this.value = value; this.paint = paint; } /** * Compares this item to an arbitrary object. * * @param obj the object. * * @return An int defining the relative order of the objects. */ @Override public int compareTo(Object obj) { PaintItem that = (PaintItem) obj; double d1 = this.value; double d2 = that.value; if (d1 > d2) { return 1; } if (d1 < d2) { return -1; } return 0; } /** * Tests this item for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof PaintItem)) { return false; } PaintItem that = (PaintItem) obj; if (this.value != that.value) { return false; } if (!PaintUtils.equal(this.paint, that.paint)) { return false; } return true; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.paint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.paint = SerialUtils.readPaint(stream); } } /** For serialization. */ static final long serialVersionUID = -5239384246251042006L; /** The lower bound. */ private double lowerBound; /** The upper bound. */ private double upperBound; /** The default paint. */ private transient Paint defaultPaint; /** The lookup table. */ private List lookupTable; /** * Creates a new paint scale. */ public LookupPaintScale() { this(0.0, 1.0, Color.LIGHT_GRAY); } /** * Creates a new paint scale with the specified default paint. * * @param lowerBound the lower bound. * @param upperBound the upper bound. * @param defaultPaint the default paint ({@code null} not * permitted). */ public LookupPaintScale(double lowerBound, double upperBound, Paint defaultPaint) { if (lowerBound >= upperBound) { throw new IllegalArgumentException( "Requires lowerBound < upperBound."); } Args.nullNotPermitted(defaultPaint, "defaultPaint"); this.lowerBound = lowerBound; this.upperBound = upperBound; this.defaultPaint = defaultPaint; this.lookupTable = new java.util.ArrayList(); } /** * Returns the default paint (never {@code null}). * * @return The default paint. */ public Paint getDefaultPaint() { return this.defaultPaint; } /** * Returns the lower bound. * * @return The lower bound. * * @see #getUpperBound() */ @Override public double getLowerBound() { return this.lowerBound; } /** * Returns the upper bound. * * @return The upper bound. * * @see #getLowerBound() */ @Override public double getUpperBound() { return this.upperBound; } /** * Adds an entry to the lookup table. Any values from {@code n} up * to but not including the next value in the table take on the specified * {@code Paint}. * * @param value the data value. * @param paint the paint. */ public void add(double value, Paint paint) { PaintItem item = new PaintItem(value, paint); int index = Collections.binarySearch(this.lookupTable, item); if (index >= 0) { this.lookupTable.set(index, item); } else { this.lookupTable.add(-(index + 1), item); } } /** * Returns the paint associated with the specified value. * * @param value the value. * * @return The paint. * * @see #getDefaultPaint() */ @Override public Paint getPaint(double value) { // handle value outside bounds... if (value < this.lowerBound) { return this.defaultPaint; } if (value > this.upperBound) { return this.defaultPaint; } int count = this.lookupTable.size(); if (count == 0) { return this.defaultPaint; } // handle special case where value is less that item zero PaintItem item = (PaintItem) this.lookupTable.get(0); if (value < item.value) { return this.defaultPaint; } // for value in bounds, do the lookup... int low = 0; int high = this.lookupTable.size() - 1; while (high - low > 1) { int current = (low + high) / 2; item = (PaintItem) this.lookupTable.get(current); if (value >= item.value) { low = current; } else { high = current; } } if (high > low) { item = (PaintItem) this.lookupTable.get(high); if (value < item.value) { item = (PaintItem) this.lookupTable.get(low); } } return (item != null ? item.paint : this.defaultPaint); } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof LookupPaintScale)) { return false; } LookupPaintScale that = (LookupPaintScale) obj; if (this.lowerBound != that.lowerBound) { return false; } if (this.upperBound != that.upperBound) { return false; } if (!PaintUtils.equal(this.defaultPaint, that.defaultPaint)) { return false; } if (!this.lookupTable.equals(that.lookupTable)) { return false; } return true; } /** * Returns a clone of the instance. * * @return A clone. * * @throws CloneNotSupportedException if there is a problem cloning the * instance. */ @Override public Object clone() throws CloneNotSupportedException { LookupPaintScale clone = (LookupPaintScale) super.clone(); clone.lookupTable = new java.util.ArrayList(this.lookupTable); return clone; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.defaultPaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.defaultPaint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/NotOutlierException.java000066400000000000000000000036551463604235500315230ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * NotOutlierException.java * ------------------------ * (C) Copyright 2003-present, by David Browning and Contributors. * * Original Author: David Browning (for Australian Institute of Marine * Science); * Contributor(s): -; * */ package org.jfree.chart.renderer; /** * An exception that is generated by the {@link Outlier}, {@link OutlierList} * and {@link OutlierListCollection} classes. */ public class NotOutlierException extends Exception { /** * Creates a new exception. * * @param message the exception message. */ public NotOutlierException(String message) { super(message); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/Outlier.java000066400000000000000000000135751463604235500271650ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------ * Outlier.java * ------------ * (C) Copyright 2003-present, by David Browning and Contributors. * * Original Author: David Browning (for Australian Institute of Marine * Science); * Contributor(s): David Gilbert; * */ package org.jfree.chart.renderer; import java.awt.geom.Point2D; /** * Represents one outlier in the box and whisker plot. *

* All the coordinates in this class are in Java2D space. */ public class Outlier implements Comparable { /** * The xy coordinates of the bounding box containing the outlier ellipse. */ private Point2D point; /** The radius of the ellipse */ private double radius; /** * Constructs an outlier item consisting of a point and the radius of the * outlier ellipse * * @param xCoord the x coordinate of the point. * @param yCoord the y coordinate of the point. * @param radius the radius of the ellipse. */ public Outlier(double xCoord, double yCoord, double radius) { this.point = new Point2D.Double(xCoord - radius, yCoord - radius); this.radius = radius; } /** * Returns the xy coordinates of the bounding box containing the outlier * ellipse. * * @return The location of the outlier ellipse. */ public Point2D getPoint() { return this.point; } /** * Sets the xy coordinates of the bounding box containing the outlier * ellipse. * * @param point the location. */ public void setPoint(Point2D point) { this.point = point; } /** * Returns the x coordinate of the bounding box containing the outlier * ellipse. * * @return The x coordinate. */ public double getX() { return getPoint().getX(); } /** * Returns the y coordinate of the bounding box containing the outlier * ellipse. * * @return The y coordinate. */ public double getY() { return getPoint().getY(); } /** * Returns the radius of the outlier ellipse. * * @return The radius. */ public double getRadius() { return this.radius; } /** * Sets the radius of the outlier ellipse. * * @param radius the new radius. */ public void setRadius(double radius) { this.radius = radius; } /** * Compares this object with the specified object for order, based on * the outlier's point. * * @param o the Object to be compared. * @return A negative integer, zero, or a positive integer as this object * is less than, equal to, or greater than the specified object. * */ @Override public int compareTo(Object o) { Outlier outlier = (Outlier) o; Point2D p1 = getPoint(); Point2D p2 = outlier.getPoint(); if (p1.equals(p2)) { return 0; } else if ((p1.getX() < p2.getX()) || (p1.getY() < p2.getY())) { return -1; } else { return 1; } } /** * Returns a true if outlier is overlapped and false if it is not. * Overlapping is determined by the respective bounding boxes plus * a small margin. * * @param other the other outlier. * * @return A {@code boolean} indicating whether or not an overlap has * occurred. */ public boolean overlaps(Outlier other) { return ((other.getX() >= getX() - (this.radius * 1.1)) && (other.getX() <= getX() + (this.radius * 1.1)) && (other.getY() >= getY() - (this.radius * 1.1)) && (other.getY() <= getY() + (this.radius * 1.1))); } /** * Tests this outlier for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof Outlier)) { return false; } Outlier that = (Outlier) obj; if (!this.point.equals(that.point)) { return false; } if (this.radius != that.radius) { return false; } return true; } /** * Returns a textual representation of the outlier. * * @return A {@code String} representing the outlier. */ @Override public String toString() { return "{" + getX() + "," + getY() + "}"; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/OutlierList.java000066400000000000000000000121361463604235500300110ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * OutlierList.java * ---------------- * (C) Copyright 2003-present, by David Browning and Contributors. * * Original Author: David Browning (for Australian Institute of Marine * Science); * Contributor(s): David Gilbert; * */ package org.jfree.chart.renderer; import java.awt.geom.Point2D; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * A collection of outliers for a single entity in a box and whisker plot. * * Outliers are grouped in lists for each entity. Lists contain * one or more outliers, determined by whether overlaps have * occured. Overlapping outliers are grouped in the same list. * * Each list contains an averaged outlier, which is the same as a single * outlier if there is only one outlier in the list, but the average of * all the outliers in the list if there is more than one. * * NB This is simply my scheme for displaying outliers, and might not be * acceptable by the wider community. */ public class OutlierList { /** Storage for the outliers. */ private List outliers; /** The averaged outlier. */ private Outlier averagedOutlier; /** * A flag that indicates whether or not there are multiple outliers in the * list. */ private boolean multiple = false; /** * Creates a new list containing a single outlier. * * @param outlier the outlier. */ public OutlierList(Outlier outlier) { this.outliers = new ArrayList(); setAveragedOutlier(outlier); } /** * Adds an outlier to the list. * * @param outlier the outlier. * * @return A boolean. */ public boolean add(Outlier outlier) { return this.outliers.add(outlier); } /** * Returns the number of outliers in the list. * * @return The item count. */ public int getItemCount() { return this.outliers.size(); } /** * Returns the averaged outlier. * * @return The averaged outlier. */ public Outlier getAveragedOutlier() { return this.averagedOutlier; } /** * Sets the averaged outlier. * * @param averagedOutlier the averaged outlier. */ public void setAveragedOutlier(Outlier averagedOutlier) { this.averagedOutlier = averagedOutlier; } /** * Returns {@code true} if the list contains multiple outliers, and * {@code false} otherwise. * * @return A boolean. */ public boolean isMultiple() { return this.multiple; } /** * Sets the flag that indicates whether or not this list represents * multiple outliers. * * @param multiple the flag. */ public void setMultiple(boolean multiple) { this.multiple = multiple; } /** * Returns {@code true} if the outlier overlaps, and * {@code false} otherwise. * * @param other the outlier. * * @return A boolean. */ public boolean isOverlapped(Outlier other) { if (other == null) { return false; } boolean result = other.overlaps(getAveragedOutlier()); return result; } /** * Updates the averaged outlier. * */ public void updateAveragedOutlier() { double totalXCoords = 0.0; double totalYCoords = 0.0; int size = getItemCount(); for (Iterator iterator = this.outliers.iterator(); iterator.hasNext();) { Outlier o = (Outlier) iterator.next(); totalXCoords += o.getX(); totalYCoords += o.getY(); } getAveragedOutlier().getPoint().setLocation( new Point2D.Double(totalXCoords / size, totalYCoords / size)); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/OutlierListCollection.java000066400000000000000000000131231463604235500320220ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * OutlierListCollection.java * -------------------------- * (C) Copyright 2003-present, by David Browning and Contributors. * * Original Author: David Browning (for Australian Institute of Marine * Science); * Contributor(s): -; * */ package org.jfree.chart.renderer; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * A collection of outlier lists for a box and whisker plot. Each collection is * associated with a single box and whisker entity. * * Outliers are grouped in lists for each entity. Lists contain * one or more outliers, determined by whether overlaps have * occurred. Overlapping outliers are grouped in the same list. * * @see org.jfree.chart.renderer.OutlierList */ public class OutlierListCollection { /** Storage for the outlier lists. */ private final List outlierLists; /** * Unbelievably, outliers which are more than 2 * interquartile range are * called far outs... See Tukey EDA (a classic one of a kind...) */ private boolean highFarOut = false; /** * A flag that indicates whether or not the collection contains low far * out values. */ private boolean lowFarOut = false; /** * Creates a new empty collection. */ public OutlierListCollection() { this.outlierLists = new ArrayList<>(); } /** * A flag to indicate the presence of one or more far out values at the * top end of the range. * * @return A {@code boolean}. */ public boolean isHighFarOut() { return this.highFarOut; } /** * Sets the flag that indicates the presence of one or more far out values * at the top end of the range. * * @param farOut the flag. */ public void setHighFarOut(boolean farOut) { this.highFarOut = farOut; } /** * A flag to indicate the presence of one or more far out values at the * bottom end of the range. * * @return A {@code boolean}. */ public boolean isLowFarOut() { return this.lowFarOut; } /** * Sets the flag that indicates the presence of one or more far out values * at the bottom end of the range. * * @param farOut the flag. */ public void setLowFarOut(boolean farOut) { this.lowFarOut = farOut; } /** * Appends the specified element as a new {@code OutlierList} to the * end of this list if it does not overlap an outlier in an existing list. * * If it does overlap, it is appended to the outlier list which it overlaps * and that list is updated. * * @param outlier element to be appended to this list. * * @return {@code true} (as per the general contract of Collection.add). */ public boolean add(Outlier outlier) { if (this.outlierLists.isEmpty()) { return this.outlierLists.add(new OutlierList(outlier)); } else { boolean updated = false; for (OutlierList list : this.outlierLists) { if (list.isOverlapped(outlier)) { updated = updateOutlierList(list, outlier); } } if (!updated) { //System.err.print(" creating new outlier list "); updated = this.outlierLists.add(new OutlierList(outlier)); } return updated; } } /** * Returns an iterator for the outlier lists. * * @return An iterator. */ public Iterator iterator() { return this.outlierLists.iterator(); } /** * Updates the outlier list by adding the outlier to the end of the list and * setting the averaged outlier to the average x and y coordinate values * of the outliers in the list. * * @param list the outlier list to be updated. * @param outlier the outlier to be added * * @return true (as per the general contract of Collection.add). */ private boolean updateOutlierList(OutlierList list, Outlier outlier) { boolean result = list.add(outlier); list.updateAveragedOutlier(); list.setMultiple(true); return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/PaintScale.java000066400000000000000000000047461463604235500275650ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * PaintScale.java * --------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer; import java.awt.Paint; import org.jfree.chart.renderer.xy.XYBlockRenderer; /** * A source for {@code Paint} instances, used by the * {@link XYBlockRenderer}. *

* NOTE: Classes that implement this interface should also implement * {@code PublicCloneable} and {@code Serializable}, so * that any renderer (or other object instance) that references an instance of * this interface can still be cloned or serialized. */ public interface PaintScale { /** * Returns the lower bound for the scale. * * @return The lower bound. * * @see #getUpperBound() */ double getLowerBound(); /** * Returns the upper bound for the scale. * * @return The upper bound. * * @see #getLowerBound() */ double getUpperBound(); /** * Returns a {@code Paint} instance for the specified value. * * @param value the value. * * @return A {@code Paint} instance (never {@code null}). */ Paint getPaint(double value); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/PolarItemRenderer.java000066400000000000000000000143401463604235500311140ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * PolarItemRenderer.java * ---------------------- * (C) Copyright 2004-present, by Solution Engineering, Inc. and Contributors. * * Original Author: Daniel Bridenbecker, Solution Engineering, Inc.; * Contributor(s): David Gilbert; * */ package org.jfree.chart.renderer; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.util.List; import org.jfree.chart.LegendItem; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.event.RendererChangeListener; import org.jfree.chart.labels.XYToolTipGenerator; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.PolarPlot; import org.jfree.chart.urls.XYURLGenerator; import org.jfree.data.xy.XYDataset; /** * The interface for a renderer that can be used by the {@link PolarPlot} * class. */ public interface PolarItemRenderer { /** * Plots the data for a given series. * * @param g2 the drawing surface. * @param dataArea the data area. * @param info collects plot rendering info. * @param plot the plot. * @param dataset the dataset. * @param seriesIndex the series index. */ void drawSeries(Graphics2D g2, Rectangle2D dataArea, PlotRenderingInfo info, PolarPlot plot, XYDataset dataset, int seriesIndex); /** * Draw the angular gridlines - the spokes. * * @param g2 the drawing surface. * @param plot the plot. * @param ticks the ticks. * @param dataArea the data area. */ void drawAngularGridLines(Graphics2D g2, PolarPlot plot, List ticks, Rectangle2D dataArea); /** * Draw the radial gridlines - the rings. * * @param g2 the drawing surface. * @param plot the plot. * @param radialAxis the radial axis. * @param ticks the ticks. * @param dataArea the data area. */ void drawRadialGridLines(Graphics2D g2, PolarPlot plot, ValueAxis radialAxis, List ticks, Rectangle2D dataArea); /** * Return the legend for the given series. * * @param series the series index. * * @return The legend item. */ LegendItem getLegendItem(int series); /** * Returns the plot that this renderer has been assigned to. * * @return The plot. */ PolarPlot getPlot(); /** * Sets the plot that this renderer is assigned to. This method will be * called by the plot class...you do not need to call it yourself. * * @param plot the plot. */ void setPlot(PolarPlot plot); /** * Adds a change listener. * * @param listener the listener. */ void addChangeListener(RendererChangeListener listener); /** * Removes a change listener. * * @param listener the listener. */ void removeChangeListener(RendererChangeListener listener); //// TOOL TIP GENERATOR /////////////////////////////////////////////////// /** * Returns the tool tip generator for a data item. * * @param row the row index (zero based). * @param column the column index (zero based). * * @return The generator (possibly {@code null}). */ XYToolTipGenerator getToolTipGenerator(int row, int column); /** * Returns the tool tip generator for a series. * * @param series the series index (zero based). * * @return The generator (possibly {@code null}). * * @see #setSeriesToolTipGenerator(int, XYToolTipGenerator) */ XYToolTipGenerator getSeriesToolTipGenerator(int series); /** * Sets the tool tip generator for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero based). * @param generator the generator ({@code null} permitted). * * @see #getSeriesToolTipGenerator(int) */ void setSeriesToolTipGenerator(int series, XYToolTipGenerator generator); /** * Returns the base tool tip generator. * * @return The generator (possibly {@code null}). * * @see #setBaseToolTipGenerator(XYToolTipGenerator) */ XYToolTipGenerator getBaseToolTipGenerator(); /** * Sets the base tool tip generator and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param generator the generator ({@code null} permitted). * * @see #getBaseToolTipGenerator() */ void setBaseToolTipGenerator(XYToolTipGenerator generator); //// URL GENERATOR //////////////////////////////////////////////////////// /** * Returns the URL generator for HTML image maps. * * @return The URL generator (possibly null). */ XYURLGenerator getURLGenerator(); /** * Sets the URL generator for HTML image maps. * * @param urlGenerator the URL generator (null permitted). */ void setURLGenerator(XYURLGenerator urlGenerator); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/RendererState.java000066400000000000000000000067211463604235500303040ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * RendererState.java * ------------------ * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; */ package org.jfree.chart.renderer; import org.jfree.chart.ChartRenderingInfo; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.plot.PlotRenderingInfo; /** * Represents the current state of a renderer. */ public class RendererState { /** The plot rendering info. */ private PlotRenderingInfo info; /** * A flag that indicates whether or not rendering hints should be added to * identify chart elements. It is initialised from the corresponding flag * in the JFreeChart instance. */ private boolean elementHinting; /** * Creates a new state object. * * @param info the plot rendering info. */ public RendererState(PlotRenderingInfo info) { this.info = info; this.elementHinting = false; } /** * Returns the flag that controls whether or not the renderer should * add rendering hints to the output that identify chart elements. * * @return A boolean. */ public boolean getElementHinting() { return this.elementHinting; } /** * Sets the elementHinting flag. * * @param hinting the new flag value. */ public void setElementHinting(boolean hinting) { this.elementHinting = hinting; } /** * Returns the plot rendering info. * * @return The info. */ public PlotRenderingInfo getInfo() { return this.info; } /** * A convenience method that returns a reference to the entity * collection (may be {@code null}) being used to record * chart entities. * * @return The entity collection (possibly {@code null}). */ public EntityCollection getEntityCollection() { EntityCollection result = null; if (this.info != null) { ChartRenderingInfo owner = this.info.getOwner(); if (owner != null) { result = owner.getEntityCollection(); } } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/RendererUtils.java000066400000000000000000000224111463604235500303160ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * RendererUtils.java * ------------------ * (C) Copyright 2007-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer; import org.jfree.chart.util.Args; import org.jfree.data.DomainOrder; import org.jfree.data.xy.XYDataset; /** * Utility methods related to the rendering process. */ public class RendererUtils { /** * Finds the lower index of the range of live items in the specified data * series. * * @param dataset the dataset ({@code null} not permitted). * @param series the series index. * @param xLow the lowest x-value in the live range. * @param xHigh the highest x-value in the live range. * * @return The index of the required item. * * @see #findLiveItemsUpperBound(XYDataset, int, double, double) */ public static int findLiveItemsLowerBound(XYDataset dataset, int series, double xLow, double xHigh) { Args.nullNotPermitted(dataset, "dataset"); if (xLow >= xHigh) { throw new IllegalArgumentException("Requires xLow < xHigh."); } int itemCount = dataset.getItemCount(series); if (itemCount <= 1) { return 0; } if (dataset.getDomainOrder() == DomainOrder.ASCENDING) { // for data in ascending order by x-value, we are (broadly) looking // for the index of the highest x-value that is less than xLow int low = 0; int high = itemCount - 1; double lowValue = dataset.getXValue(series, low); if (lowValue >= xLow) { // special case where the lowest x-value is >= xLow return low; } double highValue = dataset.getXValue(series, high); if (highValue < xLow) { // special case where the highest x-value is < xLow return high; } while (high - low > 1) { int mid = (low + high) / 2; double midV = dataset.getXValue(series, mid); if (midV >= xLow) { high = mid; } else { low = mid; } } return high; } else if (dataset.getDomainOrder() == DomainOrder.DESCENDING) { // when the x-values are sorted in descending order, the lower // bound is found by calculating relative to the xHigh value int low = 0; int high = itemCount - 1; double lowValue = dataset.getXValue(series, low); if (lowValue <= xHigh) { return low; } double highValue = dataset.getXValue(series, high); if (highValue > xHigh) { return high; } while (high - low > 1) { int mid = (low + high) / 2; double midV = dataset.getXValue(series, mid); if (midV > xHigh) { low = mid; } else { high = mid; } } return high; } else { // we don't know anything about the ordering of the x-values, // but we can still skip any initial values that fall outside the // range... int index = 0; // skip any items that don't need including... double x = dataset.getXValue(series, index); while (index < itemCount && x < xLow) { index++; if (index < itemCount) { x = dataset.getXValue(series, index); } } return Math.min(Math.max(0, index), itemCount - 1); } } /** * Finds the upper index of the range of live items in the specified data * series. * * @param dataset the dataset ({@code null} not permitted). * @param series the series index. * @param xLow the lowest x-value in the live range. * @param xHigh the highest x-value in the live range. * * @return The index of the required item. * * @see #findLiveItemsLowerBound(XYDataset, int, double, double) */ public static int findLiveItemsUpperBound(XYDataset dataset, int series, double xLow, double xHigh) { Args.nullNotPermitted(dataset, "dataset"); if (xLow >= xHigh) { throw new IllegalArgumentException("Requires xLow < xHigh."); } int itemCount = dataset.getItemCount(series); if (itemCount <= 1) { return 0; } if (dataset.getDomainOrder() == DomainOrder.ASCENDING) { int low = 0; int high = itemCount - 1; double lowValue = dataset.getXValue(series, low); if (lowValue > xHigh) { return low; } double highValue = dataset.getXValue(series, high); if (highValue <= xHigh) { return high; } int mid = (low + high) / 2; while (high - low > 1) { double midV = dataset.getXValue(series, mid); if (midV <= xHigh) { low = mid; } else { high = mid; } mid = (low + high) / 2; } return mid; } else if (dataset.getDomainOrder() == DomainOrder.DESCENDING) { // when the x-values are descending, the upper bound is found by // comparing against xLow int low = 0; int high = itemCount - 1; int mid = (low + high) / 2; double lowValue = dataset.getXValue(series, low); if (lowValue < xLow) { return low; } double highValue = dataset.getXValue(series, high); if (highValue >= xLow) { return high; } while (high - low > 1) { double midV = dataset.getXValue(series, mid); if (midV >= xLow) { low = mid; } else { high = mid; } mid = (low + high) / 2; } return mid; } else { // we don't know anything about the ordering of the x-values, // but we can still skip any trailing values that fall outside the // range... int index = itemCount - 1; // skip any items that don't need including... double x = dataset.getXValue(series, index); while (index >= 0 && x > xHigh) { index--; if (index >= 0) { x = dataset.getXValue(series, index); } } return Math.max(index, 0); } } /** * Finds a range of item indices that is guaranteed to contain all the * x-values from x0 to x1 (inclusive). * * @param dataset the dataset ({@code null} not permitted). * @param series the series index. * @param xLow the lower bound of the x-value range. * @param xHigh the upper bound of the x-value range. * * @return The indices of the boundary items. */ public static int[] findLiveItems(XYDataset dataset, int series, double xLow, double xHigh) { // here we could probably be a little faster by searching for both // indices simultaneously, but I'll look at that later if it seems // like it matters... int i0 = findLiveItemsLowerBound(dataset, series, xLow, xHigh); int i1 = findLiveItemsUpperBound(dataset, series, xLow, xHigh); if (i0 > i1) { i0 = i1; } return new int[] {i0, i1}; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/WaferMapRenderer.java000066400000000000000000000302271463604235500307240ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * WaferMapRenderer.java * --------------------- * (C) Copyright 2003-present, by Robert Redburn and Contributors. * * Original Author: Robert Redburn; * Contributor(s): David Gilbert; * */ package org.jfree.chart.renderer; import java.awt.Color; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Rectangle2D; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.jfree.chart.LegendItem; import org.jfree.chart.LegendItemCollection; import org.jfree.chart.plot.DrawingSupplier; import org.jfree.chart.plot.WaferMapPlot; import org.jfree.data.general.WaferMapDataset; /** * A renderer for wafer map plots. Provides color management facilities. */ public class WaferMapRenderer extends AbstractRenderer { /** paint index */ private Map paintIndex; /** plot */ private WaferMapPlot plot; /** paint limit */ private int paintLimit; /** default paint limit */ private static final int DEFAULT_PAINT_LIMIT = 35; /** default multivalue paint calculation */ public static final int POSITION_INDEX = 0; /** The default value index. */ public static final int VALUE_INDEX = 1; /** paint index method */ private int paintIndexMethod; /** * Creates a new renderer. */ public WaferMapRenderer() { this(null, null); } /** * Creates a new renderer. * * @param paintLimit the paint limit. * @param paintIndexMethod the paint index method. */ public WaferMapRenderer(int paintLimit, int paintIndexMethod) { this(Integer.valueOf(paintLimit), Integer.valueOf(paintIndexMethod)); } /** * Creates a new renderer. * * @param paintLimit the paint limit. * @param paintIndexMethod the paint index method. */ public WaferMapRenderer(Integer paintLimit, Integer paintIndexMethod) { super(); this.paintIndex = new HashMap(); if (paintLimit == null) { this.paintLimit = DEFAULT_PAINT_LIMIT; } else { this.paintLimit = paintLimit; } this.paintIndexMethod = VALUE_INDEX; if (paintIndexMethod != null) { if (isMethodValid(paintIndexMethod)) { this.paintIndexMethod = paintIndexMethod; } } } /** * Verifies that the passed paint index method is valid. * * @param method the method. * * @return {@code true} or false. */ private boolean isMethodValid(int method) { switch (method) { case POSITION_INDEX: return true; case VALUE_INDEX: return true; default: return false; } } /** * Returns the drawing supplier from the plot. * * @return The drawing supplier. */ @Override public DrawingSupplier getDrawingSupplier() { DrawingSupplier result = null; WaferMapPlot p = getPlot(); if (p != null) { result = p.getDrawingSupplier(); } return result; } /** * Returns the plot. * * @return The plot. */ public WaferMapPlot getPlot() { return this.plot; } /** * Sets the plot and build the paint index. * * @param plot the plot. */ public void setPlot(WaferMapPlot plot) { this.plot = plot; makePaintIndex(); } /** * Returns the paint for a given chip value. * * @param value the value. * * @return The paint. */ public Paint getChipColor(Number value) { return getSeriesPaint(getPaintIndex(value)); } /** * Returns the paint index for a given chip value. * * @param value the value. * * @return The paint index. */ private int getPaintIndex(Number value) { return ((Integer) this.paintIndex.get(value)); } /** * Builds a map of chip values to paint colors. * paintlimit is the maximum allowed number of colors. */ private void makePaintIndex() { if (this.plot == null) { return; } WaferMapDataset data = this.plot.getDataset(); Number dataMin = data.getMinValue(); Number dataMax = data.getMaxValue(); Set uniqueValues = data.getUniqueValues(); if (uniqueValues.size() <= this.paintLimit) { int count = 0; // assign a color for each unique value for (Iterator i = uniqueValues.iterator(); i.hasNext();) { this.paintIndex.put(i.next(), count++); } } else { // more values than paints so map // multiple values to the same color switch (this.paintIndexMethod) { case POSITION_INDEX: makePositionIndex(uniqueValues); break; case VALUE_INDEX: makeValueIndex(dataMax, dataMin, uniqueValues); break; default: break; } } } /** * Builds the paintindex by assigning colors based on the number * of unique values: totalvalues/totalcolors. * * @param uniqueValues the set of unique values. */ private void makePositionIndex(Set uniqueValues) { int valuesPerColor = (int) Math.ceil( (double) uniqueValues.size() / this.paintLimit ); int count = 0; // assign a color for each unique value int paint = 0; for (Iterator i = uniqueValues.iterator(); i.hasNext();) { this.paintIndex.put(i.next(), paint); if (++count % valuesPerColor == 0) { paint++; } if (paint > this.paintLimit) { paint = this.paintLimit; } } } /** * Builds the paintindex by assigning colors evenly across the range * of values: maxValue-minValue/totalcolors * * @param max the maximum value. * @param min the minumum value. * @param uniqueValues the unique values. */ private void makeValueIndex(Number max, Number min, Set uniqueValues) { double valueRange = max.doubleValue() - min.doubleValue(); double valueStep = valueRange / this.paintLimit; int paint = 0; double cutPoint = min.doubleValue() + valueStep; for (Iterator i = uniqueValues.iterator(); i.hasNext();) { Number value = (Number) i.next(); while (value.doubleValue() > cutPoint) { cutPoint += valueStep; paint++; if (paint > this.paintLimit) { paint = this.paintLimit; } } this.paintIndex.put(value, paint); } } /** * Builds the list of legend entries. called by getLegendItems in * WaferMapPlot to populate the plot legend. * * @return The legend items. */ public LegendItemCollection getLegendCollection() { LegendItemCollection result = new LegendItemCollection(); if (this.paintIndex != null && this.paintIndex.size() > 0) { if (this.paintIndex.size() <= this.paintLimit) { for (Iterator i = this.paintIndex.entrySet().iterator(); i.hasNext();) { // in this case, every color has a unique value Map.Entry entry = (Map.Entry) i.next(); String label = entry.getKey().toString(); String description = label; Shape shape = new Rectangle2D.Double(1d, 1d, 1d, 1d); Paint paint = lookupSeriesPaint(((Integer) entry.getValue())); Paint outlinePaint = Color.BLACK; Stroke outlineStroke = DEFAULT_STROKE; result.add(new LegendItem(label, description, null, null, shape, paint, outlineStroke, outlinePaint)); } } else { // in this case, every color has a range of values Set unique = new HashSet(); for (Iterator i = this.paintIndex.entrySet().iterator(); i.hasNext();) { Map.Entry entry = (Map.Entry) i.next(); if (unique.add(entry.getValue())) { String label = getMinPaintValue( (Integer) entry.getValue()).toString() + " - " + getMaxPaintValue( (Integer) entry.getValue()).toString(); String description = label; Shape shape = new Rectangle2D.Double(1d, 1d, 1d, 1d); Paint paint = getSeriesPaint(((Integer) entry.getValue())); Paint outlinePaint = Color.BLACK; Stroke outlineStroke = DEFAULT_STROKE; result.add(new LegendItem(label, description, null, null, shape, paint, outlineStroke, outlinePaint)); } } // end foreach map entry } // end else } return result; } /** * Returns the minimum chip value assigned to a color * in the paintIndex * * @param index the index. * * @return The value. */ private Number getMinPaintValue(Integer index) { double minValue = Double.POSITIVE_INFINITY; for (Iterator i = this.paintIndex.entrySet().iterator(); i.hasNext();) { Map.Entry entry = (Map.Entry) i.next(); if (((Integer) entry.getValue()).equals(index)) { if (((Number) entry.getKey()).doubleValue() < minValue) { minValue = ((Number) entry.getKey()).doubleValue(); } } } return minValue; } /** * Returns the maximum chip value assigned to a color * in the paintIndex * * @param index the index. * * @return The value */ private Number getMaxPaintValue(Integer index) { double maxValue = Double.NEGATIVE_INFINITY; for (Iterator i = this.paintIndex.entrySet().iterator(); i.hasNext();) { Map.Entry entry = (Map.Entry) i.next(); if (((Integer) entry.getValue()).equals(index)) { if (((Number) entry.getKey()).doubleValue() > maxValue) { maxValue = ((Number) entry.getKey()).doubleValue(); } } } return maxValue; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/category/000077500000000000000000000000001463604235500265015ustar00rootroot00000000000000AbstractCategoryItemRenderer.java000066400000000000000000001730401463604235500350410ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/category/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------------- * AbstractCategoryItemRenderer.java * --------------------------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Richard Atkinson; * Peter Kolb (patch 2497611); * */ package org.jfree.chart.renderer.category; import java.awt.AlphaComposite; import java.awt.Composite; import java.awt.Font; import java.awt.GradientPaint; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Ellipse2D; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import org.jfree.chart.LegendItem; import org.jfree.chart.LegendItemCollection; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.CategoryItemEntity; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.labels.CategoryItemLabelGenerator; import org.jfree.chart.labels.CategorySeriesLabelGenerator; import org.jfree.chart.labels.CategoryToolTipGenerator; import org.jfree.chart.labels.ItemLabelPosition; import org.jfree.chart.labels.StandardCategorySeriesLabelGenerator; import org.jfree.chart.plot.CategoryCrosshairState; import org.jfree.chart.plot.CategoryMarker; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.DrawingSupplier; import org.jfree.chart.plot.IntervalMarker; import org.jfree.chart.plot.Marker; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.ValueMarker; import org.jfree.chart.renderer.AbstractRenderer; import org.jfree.chart.text.TextUtils; import org.jfree.chart.ui.GradientPaintTransformer; import org.jfree.chart.ui.LengthAdjustmentType; import org.jfree.chart.ui.RectangleAnchor; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.urls.CategoryURLGenerator; import org.jfree.chart.util.CloneUtils; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SortOrder; import org.jfree.data.KeyedValues2DItemKey; import org.jfree.data.Range; import org.jfree.data.category.CategoryDataset; import org.jfree.data.general.DatasetUtils; /** * An abstract base class that you can use to implement a new * {@link CategoryItemRenderer}. When you create a new * {@link CategoryItemRenderer} you are not required to extend this class, * but it makes the job easier. */ public abstract class AbstractCategoryItemRenderer extends AbstractRenderer implements CategoryItemRenderer, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 1247553218442497391L; /** The plot that the renderer is assigned to. */ private CategoryPlot plot; /** A list of item label generators (one per series). */ private Map itemLabelGeneratorMap; /** The default item label generator. */ private CategoryItemLabelGenerator defaultItemLabelGenerator; /** A list of tool tip generators (one per series). */ private Map toolTipGeneratorMap; /** The default tool tip generator. */ private CategoryToolTipGenerator defaultToolTipGenerator; /** A list of item label generators (one per series). */ private Map itemURLGeneratorMap; /** The default item label generator. */ private CategoryURLGenerator defaultItemURLGenerator; /** The legend item label generator. */ private CategorySeriesLabelGenerator legendItemLabelGenerator; /** The legend item tool tip generator. */ private CategorySeriesLabelGenerator legendItemToolTipGenerator; /** The legend item URL generator. */ private CategorySeriesLabelGenerator legendItemURLGenerator; /** The number of rows in the dataset (temporary record). */ private transient int rowCount; /** The number of columns in the dataset (temporary record). */ private transient int columnCount; /** * Creates a new renderer with no tool tip generator and no URL generator. * The defaults (no tool tip or URL generators) have been chosen to * minimise the processing required to generate a default chart. If you * require tool tips or URLs, then you can easily add the required * generators. */ protected AbstractCategoryItemRenderer() { this.itemLabelGeneratorMap = new HashMap<>(); this.toolTipGeneratorMap = new HashMap<>(); this.itemURLGeneratorMap = new HashMap<>(); this.legendItemLabelGenerator = new StandardCategorySeriesLabelGenerator(); } /** * Returns the number of passes through the dataset required by the * renderer. This method returns {@code 1}, subclasses should * override if they need more passes. * * @return The pass count. */ @Override public int getPassCount() { return 1; } /** * Returns the plot that the renderer has been assigned to (where * {@code null} indicates that the renderer is not currently assigned * to a plot). * * @return The plot (possibly {@code null}). * * @see #setPlot(CategoryPlot) */ @Override public CategoryPlot getPlot() { return this.plot; } /** * Sets the plot that the renderer has been assigned to. This method is * usually called by the {@link CategoryPlot}, in normal usage you * shouldn't need to call this method directly. * * @param plot the plot ({@code null} not permitted). * * @see #getPlot() */ @Override public void setPlot(CategoryPlot plot) { Args.nullNotPermitted(plot, "plot"); this.plot = plot; } // ITEM LABEL GENERATOR /** * Returns the item label generator for a data item. This implementation * simply passes control to the {@link #getSeriesItemLabelGenerator(int)} * method. If, for some reason, you want a different generator for * individual items, you can override this method. * * @param row the row index (zero based). * @param column the column index (zero based). * * @return The generator (possibly {@code null}). */ @Override public CategoryItemLabelGenerator getItemLabelGenerator(int row, int column) { return getSeriesItemLabelGenerator(row); } /** * Returns the item label generator for a series. * * @param series the series index (zero based). * * @return The generator (possibly {@code null}). * * @see #setSeriesItemLabelGenerator(int, CategoryItemLabelGenerator) */ @Override public CategoryItemLabelGenerator getSeriesItemLabelGenerator(int series) { // otherwise look up the generator table CategoryItemLabelGenerator generator = this.itemLabelGeneratorMap.get( series); if (generator == null) { generator = this.defaultItemLabelGenerator; } return generator; } /** * Sets the item label generator for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero based). * @param generator the generator ({@code null} permitted). * * @see #getSeriesItemLabelGenerator(int) */ @Override public void setSeriesItemLabelGenerator(int series, CategoryItemLabelGenerator generator) { setSeriesItemLabelGenerator(series, generator, true); } /** * Sets the item label generator for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero based). * @param generator the generator ({@code null} permitted). * @param notify notify listeners? * * @see #getSeriesItemLabelGenerator(int) */ @Override public void setSeriesItemLabelGenerator(int series, CategoryItemLabelGenerator generator, boolean notify) { this.itemLabelGeneratorMap.put(series, generator); if (notify) { fireChangeEvent(); } } /** * Returns the default item label generator. * * @return The generator (possibly {@code null}). * * @see #setDefaultItemLabelGenerator(CategoryItemLabelGenerator) */ @Override public CategoryItemLabelGenerator getDefaultItemLabelGenerator() { return this.defaultItemLabelGenerator; } /** * Sets the default item label generator and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param generator the generator ({@code null} permitted). * * @see #getDefaultItemLabelGenerator() */ @Override public void setDefaultItemLabelGenerator( CategoryItemLabelGenerator generator) { setDefaultItemLabelGenerator(generator, true); } /** * Sets the default item label generator and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param generator the generator ({@code null} permitted). * @param notify notify listeners? * * @see #getDefaultItemLabelGenerator() */ @Override public void setDefaultItemLabelGenerator( CategoryItemLabelGenerator generator, boolean notify) { this.defaultItemLabelGenerator = generator; if (notify) { fireChangeEvent(); } } // TOOL TIP GENERATOR /** * Returns the tool tip generator that should be used for the specified * item. This method looks up the generator using the "three-layer" * approach outlined in the general description of this interface. You * can override this method if you want to return a different generator per * item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The generator (possibly {@code null}). */ @Override public CategoryToolTipGenerator getToolTipGenerator(int row, int column) { CategoryToolTipGenerator result = getSeriesToolTipGenerator(row); if (result == null) { result = this.defaultToolTipGenerator; } return result; } /** * Returns the tool tip generator for the specified series (a "layer 1" * generator). * * @param series the series index (zero-based). * * @return The tool tip generator (possibly {@code null}). * * @see #setSeriesToolTipGenerator(int, CategoryToolTipGenerator) */ @Override public CategoryToolTipGenerator getSeriesToolTipGenerator(int series) { return this.toolTipGeneratorMap.get(series); } /** * Sets the tool tip generator for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param generator the generator ({@code null} permitted). * * @see #getSeriesToolTipGenerator(int) */ @Override public void setSeriesToolTipGenerator(int series, CategoryToolTipGenerator generator) { setSeriesToolTipGenerator(series, generator, true); } /** * Sets the tool tip generator for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param generator the generator ({@code null} permitted). * @param notify notify listeners? * * @see #getSeriesToolTipGenerator(int) */ @Override public void setSeriesToolTipGenerator(int series, CategoryToolTipGenerator generator, boolean notify) { this.toolTipGeneratorMap.put(series, generator); if (notify) { fireChangeEvent(); } } /** * Returns the default tool tip generator (the "layer 2" generator). * * @return The tool tip generator (possibly {@code null}). * * @see #setDefaultToolTipGenerator(CategoryToolTipGenerator) */ @Override public CategoryToolTipGenerator getDefaultToolTipGenerator() { return this.defaultToolTipGenerator; } /** * Sets the default tool tip generator and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param generator the generator ({@code null} permitted). * * @see #getDefaultToolTipGenerator() */ @Override public void setDefaultToolTipGenerator(CategoryToolTipGenerator generator) { setDefaultToolTipGenerator(generator, true); } /** * Sets the default tool tip generator and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param generator the generator ({@code null} permitted). * @param notify notify listeners? * * @see #getDefaultToolTipGenerator() */ @Override public void setDefaultToolTipGenerator(CategoryToolTipGenerator generator, boolean notify) { this.defaultToolTipGenerator = generator; if (notify) { fireChangeEvent(); } } // URL GENERATOR /** * Returns the URL generator for a data item. This method just calls the * getSeriesItemURLGenerator method, but you can override this behaviour if * you want to. * * @param row the row index (zero based). * @param column the column index (zero based). * * @return The URL generator. */ @Override public CategoryURLGenerator getItemURLGenerator(int row, int column) { return getSeriesItemURLGenerator(row); } /** * Returns the URL generator for a series. * * @param series the series index (zero based). * * @return The URL generator for the series. * * @see #setSeriesItemURLGenerator(int, CategoryURLGenerator) */ @Override public CategoryURLGenerator getSeriesItemURLGenerator(int series) { // otherwise look up the generator table CategoryURLGenerator generator = this.itemURLGeneratorMap.get(series); if (generator == null) { generator = this.defaultItemURLGenerator; } return generator; } /** * Sets the URL generator for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero based). * @param generator the generator. * * @see #getSeriesItemURLGenerator(int) */ @Override public void setSeriesItemURLGenerator(int series, CategoryURLGenerator generator) { setSeriesItemURLGenerator(series, generator, true); } /** * Sets the URL generator for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero based). * @param generator the generator. * @param notify notify listeners? * * @see #getSeriesItemURLGenerator(int) */ @Override public void setSeriesItemURLGenerator(int series, CategoryURLGenerator generator, boolean notify) { this.itemURLGeneratorMap.put(series, generator); if (notify) { fireChangeEvent(); } } /** * Returns the default item URL generator. * * @return The item URL generator. * * @see #setDefaultItemURLGenerator(CategoryURLGenerator) */ @Override public CategoryURLGenerator getDefaultItemURLGenerator() { return this.defaultItemURLGenerator; } /** * Sets the default item URL generator and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param generator the item URL generator ({@code null} permitted). * * @see #getDefaultItemURLGenerator() */ @Override public void setDefaultItemURLGenerator(CategoryURLGenerator generator) { setDefaultItemURLGenerator(generator, true); } /** * Sets the default item URL generator and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param generator the item URL generator ({@code null} permitted). * @param notify notify listeners? * * @see #getDefaultItemURLGenerator() */ @Override public void setDefaultItemURLGenerator(CategoryURLGenerator generator, boolean notify) { this.defaultItemURLGenerator = generator; if (notify) { fireChangeEvent(); } } /** * Returns the number of rows in the dataset. This value is updated in the * {@link AbstractCategoryItemRenderer#initialise} method. * * @return The row count. */ public int getRowCount() { return this.rowCount; } /** * Returns the number of columns in the dataset. This value is updated in * the {@link AbstractCategoryItemRenderer#initialise} method. * * @return The column count. */ public int getColumnCount() { return this.columnCount; } /** * Creates a new state instance---this method is called from the * {@link #initialise(Graphics2D, Rectangle2D, CategoryPlot, int, * PlotRenderingInfo)} method. Subclasses can override this method if * they need to use a subclass of {@link CategoryItemRendererState}. * * @param info collects plot rendering info ({@code null} permitted). * * @return The new state instance (never {@code null}). */ protected CategoryItemRendererState createState(PlotRenderingInfo info) { return new CategoryItemRendererState(info); } /** * Initialises the renderer and returns a state object that will be used * for the remainder of the drawing process for a single chart. The state * object allows for the fact that the renderer may be used simultaneously * by multiple threads (each thread will work with a separate state object). * * @param g2 the graphics device. * @param dataArea the data area. * @param plot the plot. * @param rendererIndex the renderer index. * @param info an object for returning information about the structure of * the plot ({@code null} permitted). * * @return The renderer state. */ @Override public CategoryItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, CategoryPlot plot, int rendererIndex, PlotRenderingInfo info) { setPlot(plot); CategoryDataset data = plot.getDataset(rendererIndex); if (data != null) { this.rowCount = data.getRowCount(); this.columnCount = data.getColumnCount(); } else { this.rowCount = 0; this.columnCount = 0; } CategoryItemRendererState state = createState(info); state.setElementHinting(plot.fetchElementHintingFlag()); int[] visibleSeriesTemp = new int[this.rowCount]; int visibleSeriesCount = 0; for (int row = 0; row < this.rowCount; row++) { if (isSeriesVisible(row)) { visibleSeriesTemp[visibleSeriesCount] = row; visibleSeriesCount++; } } int[] visibleSeries = new int[visibleSeriesCount]; System.arraycopy(visibleSeriesTemp, 0, visibleSeries, 0, visibleSeriesCount); state.setVisibleSeriesArray(visibleSeries); return state; } /** * Adds a {@code KEY_BEGIN_ELEMENT} hint to the graphics target. This * hint is recognised by JFreeSVG (in theory it could be used by * other {@code Graphics2D} implementations also). * * @param g2 the graphics target ({@code null} not permitted). * @param rowKey the row key that identifies the element ({@code null} not * permitted). * @param columnKey the column key that identifies the element * ({@code null} not permitted). */ protected void beginElementGroup(Graphics2D g2, Comparable rowKey, Comparable columnKey) { beginElementGroup(g2, new KeyedValues2DItemKey(rowKey, columnKey)); } /** * Returns the range of values the renderer requires to display all the * items from the specified dataset. * * @param dataset the dataset ({@code null} permitted). * * @return The range (or {@code null} if the dataset is * {@code null} or empty). */ @Override public Range findRangeBounds(CategoryDataset dataset) { return findRangeBounds(dataset, false); } /** * Returns the range of values the renderer requires to display all the * items from the specified dataset. * * @param dataset the dataset ({@code null} permitted). * @param includeInterval include the y-interval if the dataset has one. * * @return The range ({@code null} if the dataset is {@code null} * or empty). */ protected Range findRangeBounds(CategoryDataset dataset, boolean includeInterval) { if (dataset == null) { return null; } if (getDataBoundsIncludesVisibleSeriesOnly()) { List visibleSeriesKeys = new ArrayList<>(); int seriesCount = dataset.getRowCount(); for (int s = 0; s < seriesCount; s++) { if (isSeriesVisible(s)) { visibleSeriesKeys.add(dataset.getRowKey(s)); } } return DatasetUtils.findRangeBounds(dataset, visibleSeriesKeys, includeInterval); } else { return DatasetUtils.findRangeBounds(dataset, includeInterval); } } /** * Returns the Java2D coordinate for the middle of the specified data item. * * @param rowKey the row key. * @param columnKey the column key. * @param dataset the dataset. * @param axis the axis. * @param area the data area. * @param edge the edge along which the axis lies. * * @return The Java2D coordinate for the middle of the item. */ @Override public double getItemMiddle(Comparable rowKey, Comparable columnKey, CategoryDataset dataset, CategoryAxis axis, Rectangle2D area, RectangleEdge edge) { return axis.getCategoryMiddle(columnKey, dataset.getColumnKeys(), area, edge); } /** * Draws a background for the data area. The default implementation just * gets the plot to draw the background, but some renderers will override * this behaviour. * * @param g2 the graphics device. * @param plot the plot. * @param dataArea the data area. */ @Override public void drawBackground(Graphics2D g2, CategoryPlot plot, Rectangle2D dataArea) { plot.drawBackground(g2, dataArea); } /** * Draws an outline for the data area. The default implementation just * gets the plot to draw the outline, but some renderers will override this * behaviour. * * @param g2 the graphics device. * @param plot the plot. * @param dataArea the data area. */ @Override public void drawOutline(Graphics2D g2, CategoryPlot plot, Rectangle2D dataArea) { plot.drawOutline(g2, dataArea); } /** * Draws a grid line against the domain axis. *

* Note that this default implementation assumes that the horizontal axis * is the domain axis. If this is not the case, you will need to override * this method. * * @param g2 the graphics device. * @param plot the plot. * @param dataArea the area for plotting data. * @param value the Java2D value at which the grid line should be drawn. * */ @Override public void drawDomainGridline(Graphics2D g2, CategoryPlot plot, Rectangle2D dataArea, double value) { Line2D line = null; PlotOrientation orientation = plot.getOrientation(); if (orientation == PlotOrientation.HORIZONTAL) { line = new Line2D.Double(dataArea.getMinX(), value, dataArea.getMaxX(), value); } else if (orientation == PlotOrientation.VERTICAL) { line = new Line2D.Double(value, dataArea.getMinY(), value, dataArea.getMaxY()); } Paint paint = plot.getDomainGridlinePaint(); if (paint == null) { paint = CategoryPlot.DEFAULT_GRIDLINE_PAINT; } g2.setPaint(paint); Stroke stroke = plot.getDomainGridlineStroke(); if (stroke == null) { stroke = CategoryPlot.DEFAULT_GRIDLINE_STROKE; } g2.setStroke(stroke); Object saved = g2.getRenderingHint(RenderingHints.KEY_STROKE_CONTROL); g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE); g2.draw(line); g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, saved); } /** * Draws a line perpendicular to the range axis. * * @param g2 the graphics device. * @param plot the plot. * @param axis the value axis. * @param dataArea the area for plotting data. * @param value the value at which the grid line should be drawn. * @param paint the paint ({@code null} not permitted). * @param stroke the stroke ({@code null} not permitted). */ @Override public void drawRangeLine(Graphics2D g2, CategoryPlot plot, ValueAxis axis, Rectangle2D dataArea, double value, Paint paint, Stroke stroke) { Range range = axis.getRange(); if (!range.contains(value)) { return; } PlotOrientation orientation = plot.getOrientation(); Line2D line = null; double v = axis.valueToJava2D(value, dataArea, plot.getRangeAxisEdge()); if (orientation == PlotOrientation.HORIZONTAL) { line = new Line2D.Double(v, dataArea.getMinY(), v, dataArea.getMaxY()); } else if (orientation == PlotOrientation.VERTICAL) { line = new Line2D.Double(dataArea.getMinX(), v, dataArea.getMaxX(), v); } g2.setPaint(paint); g2.setStroke(stroke); Object saved = g2.getRenderingHint(RenderingHints.KEY_STROKE_CONTROL); g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE); g2.draw(line); g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, saved); } /** * Draws a marker for the domain axis. * * @param g2 the graphics device (not {@code null}). * @param plot the plot (not {@code null}). * @param axis the range axis (not {@code null}). * @param marker the marker to be drawn (not {@code null}). * @param dataArea the area inside the axes (not {@code null}). * * @see #drawRangeMarker(Graphics2D, CategoryPlot, ValueAxis, Marker, * Rectangle2D) */ @Override public void drawDomainMarker(Graphics2D g2, CategoryPlot plot, CategoryAxis axis, CategoryMarker marker, Rectangle2D dataArea) { Comparable category = marker.getKey(); CategoryDataset dataset = plot.getDataset(plot.getIndexOf(this)); int columnIndex = dataset.getColumnIndex(category); if (columnIndex < 0) { return; } final Composite savedComposite = g2.getComposite(); g2.setComposite(AlphaComposite.getInstance( AlphaComposite.SRC_OVER, marker.getAlpha())); PlotOrientation orientation = plot.getOrientation(); Rectangle2D bounds; if (marker.getDrawAsLine()) { double v = axis.getCategoryMiddle(columnIndex, dataset.getColumnCount(), dataArea, plot.getDomainAxisEdge()); Line2D line = null; if (orientation == PlotOrientation.HORIZONTAL) { line = new Line2D.Double(dataArea.getMinX(), v, dataArea.getMaxX(), v); } else if (orientation == PlotOrientation.VERTICAL) { line = new Line2D.Double(v, dataArea.getMinY(), v, dataArea.getMaxY()); } else { throw new IllegalStateException(); } g2.setPaint(marker.getPaint()); g2.setStroke(marker.getStroke()); g2.draw(line); bounds = line.getBounds2D(); } else { double v0 = axis.getCategoryStart(columnIndex, dataset.getColumnCount(), dataArea, plot.getDomainAxisEdge()); double v1 = axis.getCategoryEnd(columnIndex, dataset.getColumnCount(), dataArea, plot.getDomainAxisEdge()); Rectangle2D area = null; if (orientation == PlotOrientation.HORIZONTAL) { area = new Rectangle2D.Double(dataArea.getMinX(), v0, dataArea.getWidth(), (v1 - v0)); } else if (orientation == PlotOrientation.VERTICAL) { area = new Rectangle2D.Double(v0, dataArea.getMinY(), (v1 - v0), dataArea.getHeight()); } g2.setPaint(marker.getPaint()); g2.fill(area); bounds = area; } String label = marker.getLabel(); RectangleAnchor anchor = marker.getLabelAnchor(); if (label != null) { Font labelFont = marker.getLabelFont(); g2.setFont(labelFont); g2.setPaint(marker.getLabelPaint()); Point2D coordinates = calculateDomainMarkerTextAnchorPoint( g2, orientation, dataArea, bounds, marker.getLabelOffset(), marker.getLabelOffsetType(), anchor); TextUtils.drawAlignedString(label, g2, (float) coordinates.getX(), (float) coordinates.getY(), marker.getLabelTextAnchor()); } g2.setComposite(savedComposite); } /** * Draws a marker for the range axis. * * @param g2 the graphics device (not {@code null}). * @param plot the plot (not {@code null}). * @param axis the range axis (not {@code null}). * @param marker the marker to be drawn (not {@code null}). * @param dataArea the area inside the axes (not {@code null}). * * @see #drawDomainMarker(Graphics2D, CategoryPlot, CategoryAxis, * CategoryMarker, Rectangle2D) */ @Override public void drawRangeMarker(Graphics2D g2, CategoryPlot plot, ValueAxis axis, Marker marker, Rectangle2D dataArea) { if (marker instanceof ValueMarker) { ValueMarker vm = (ValueMarker) marker; double value = vm.getValue(); Range range = axis.getRange(); if (!range.contains(value)) { return; } final Composite savedComposite = g2.getComposite(); g2.setComposite(AlphaComposite.getInstance( AlphaComposite.SRC_OVER, marker.getAlpha())); PlotOrientation orientation = plot.getOrientation(); double v = axis.valueToJava2D(value, dataArea, plot.getRangeAxisEdge()); Line2D line = null; if (orientation == PlotOrientation.HORIZONTAL) { line = new Line2D.Double(v, dataArea.getMinY(), v, dataArea.getMaxY()); } else if (orientation == PlotOrientation.VERTICAL) { line = new Line2D.Double(dataArea.getMinX(), v, dataArea.getMaxX(), v); } else { throw new IllegalStateException(); } g2.setPaint(marker.getPaint()); g2.setStroke(marker.getStroke()); g2.draw(line); String label = marker.getLabel(); RectangleAnchor anchor = marker.getLabelAnchor(); if (label != null) { Font labelFont = marker.getLabelFont(); g2.setFont(labelFont); Point2D coordinates = calculateRangeMarkerTextAnchorPoint( g2, orientation, dataArea, line.getBounds2D(), marker.getLabelOffset(), LengthAdjustmentType.EXPAND, anchor); Rectangle2D rect = TextUtils.calcAlignedStringBounds(label, g2, (float) coordinates.getX(), (float) coordinates.getY(), marker.getLabelTextAnchor()); g2.setPaint(marker.getLabelBackgroundColor()); g2.fill(rect); g2.setPaint(marker.getLabelPaint()); TextUtils.drawAlignedString(label, g2, (float) coordinates.getX(), (float) coordinates.getY(), marker.getLabelTextAnchor()); } g2.setComposite(savedComposite); } else if (marker instanceof IntervalMarker) { IntervalMarker im = (IntervalMarker) marker; double start = im.getStartValue(); double end = im.getEndValue(); Range range = axis.getRange(); if (!(range.intersects(start, end))) { return; } final Composite savedComposite = g2.getComposite(); g2.setComposite(AlphaComposite.getInstance( AlphaComposite.SRC_OVER, marker.getAlpha())); double start2d = axis.valueToJava2D(start, dataArea, plot.getRangeAxisEdge()); double end2d = axis.valueToJava2D(end, dataArea, plot.getRangeAxisEdge()); double low = Math.min(start2d, end2d); double high = Math.max(start2d, end2d); PlotOrientation orientation = plot.getOrientation(); Rectangle2D rect = null; if (orientation == PlotOrientation.HORIZONTAL) { // clip left and right bounds to data area low = Math.max(low, dataArea.getMinX()); high = Math.min(high, dataArea.getMaxX()); rect = new Rectangle2D.Double(low, dataArea.getMinY(), high - low, dataArea.getHeight()); } else if (orientation == PlotOrientation.VERTICAL) { // clip top and bottom bounds to data area low = Math.max(low, dataArea.getMinY()); high = Math.min(high, dataArea.getMaxY()); rect = new Rectangle2D.Double(dataArea.getMinX(), low, dataArea.getWidth(), high - low); } Paint p = marker.getPaint(); if (p instanceof GradientPaint) { GradientPaint gp = (GradientPaint) p; GradientPaintTransformer t = im.getGradientPaintTransformer(); if (t != null) { gp = t.transform(gp, rect); } g2.setPaint(gp); } else { g2.setPaint(p); } g2.fill(rect); // now draw the outlines, if visible... if (im.getOutlinePaint() != null && im.getOutlineStroke() != null) { if (orientation == PlotOrientation.VERTICAL) { Line2D line = new Line2D.Double(); double x0 = dataArea.getMinX(); double x1 = dataArea.getMaxX(); g2.setPaint(im.getOutlinePaint()); g2.setStroke(im.getOutlineStroke()); if (range.contains(start)) { line.setLine(x0, start2d, x1, start2d); g2.draw(line); } if (range.contains(end)) { line.setLine(x0, end2d, x1, end2d); g2.draw(line); } } else { // PlotOrientation.HORIZONTAL Line2D line = new Line2D.Double(); double y0 = dataArea.getMinY(); double y1 = dataArea.getMaxY(); g2.setPaint(im.getOutlinePaint()); g2.setStroke(im.getOutlineStroke()); if (range.contains(start)) { line.setLine(start2d, y0, start2d, y1); g2.draw(line); } if (range.contains(end)) { line.setLine(end2d, y0, end2d, y1); g2.draw(line); } } } String label = marker.getLabel(); RectangleAnchor anchor = marker.getLabelAnchor(); if (label != null) { Font labelFont = marker.getLabelFont(); g2.setFont(labelFont); Point2D coords = calculateRangeMarkerTextAnchorPoint( g2, orientation, dataArea, rect, marker.getLabelOffset(), marker.getLabelOffsetType(), anchor); Rectangle2D r = TextUtils.calcAlignedStringBounds(label, g2, (float) coords.getX(), (float) coords.getY(), marker.getLabelTextAnchor()); g2.setPaint(marker.getLabelBackgroundColor()); g2.fill(r); g2.setPaint(marker.getLabelPaint()); TextUtils.drawAlignedString(label, g2, (float) coords.getX(), (float) coords.getY(), marker.getLabelTextAnchor()); } g2.setComposite(savedComposite); } } /** * Calculates the {@code (x, y)} coordinates for drawing the label for a * marker on the range axis. * * @param g2 the graphics device. * @param orientation the plot orientation. * @param dataArea the data area. * @param markerArea the rectangle surrounding the marker. * @param markerOffset the marker offset. * @param labelOffsetType the label offset type. * @param anchor the label anchor. * * @return The coordinates for drawing the marker label. */ protected Point2D calculateDomainMarkerTextAnchorPoint(Graphics2D g2, PlotOrientation orientation, Rectangle2D dataArea, Rectangle2D markerArea, RectangleInsets markerOffset, LengthAdjustmentType labelOffsetType, RectangleAnchor anchor) { Rectangle2D anchorRect = null; if (orientation == PlotOrientation.HORIZONTAL) { anchorRect = markerOffset.createAdjustedRectangle(markerArea, LengthAdjustmentType.CONTRACT, labelOffsetType); } else if (orientation == PlotOrientation.VERTICAL) { anchorRect = markerOffset.createAdjustedRectangle(markerArea, labelOffsetType, LengthAdjustmentType.CONTRACT); } return anchor.getAnchorPoint(anchorRect); } /** * Calculates the (x, y) coordinates for drawing a marker label. * * @param g2 the graphics device. * @param orientation the plot orientation. * @param dataArea the data area. * @param markerArea the rectangle surrounding the marker. * @param markerOffset the marker offset. * @param labelOffsetType the label offset type. * @param anchor the label anchor. * * @return The coordinates for drawing the marker label. */ protected Point2D calculateRangeMarkerTextAnchorPoint(Graphics2D g2, PlotOrientation orientation, Rectangle2D dataArea, Rectangle2D markerArea, RectangleInsets markerOffset, LengthAdjustmentType labelOffsetType, RectangleAnchor anchor) { Rectangle2D anchorRect = null; if (orientation == PlotOrientation.HORIZONTAL) { anchorRect = markerOffset.createAdjustedRectangle(markerArea, labelOffsetType, LengthAdjustmentType.CONTRACT); } else if (orientation == PlotOrientation.VERTICAL) { anchorRect = markerOffset.createAdjustedRectangle(markerArea, LengthAdjustmentType.CONTRACT, labelOffsetType); } return anchor.getAnchorPoint(anchorRect); } /** * Returns a legend item for a series. This default implementation will * return {@code null} if {@link #isSeriesVisible(int)} or * {@link #isSeriesVisibleInLegend(int)} returns {@code false}. * * @param datasetIndex the dataset index (zero-based). * @param series the series index (zero-based). * * @return The legend item (possibly {@code null}). * * @see #getLegendItems() */ @Override public LegendItem getLegendItem(int datasetIndex, int series) { CategoryPlot p = getPlot(); if (p == null) { return null; } // check that a legend item needs to be displayed... if (!isSeriesVisible(series) || !isSeriesVisibleInLegend(series)) { return null; } CategoryDataset dataset = p.getDataset(datasetIndex); String label = this.legendItemLabelGenerator.generateLabel(dataset, series); String description = label; String toolTipText = null; if (this.legendItemToolTipGenerator != null) { toolTipText = this.legendItemToolTipGenerator.generateLabel( dataset, series); } String urlText = null; if (this.legendItemURLGenerator != null) { urlText = this.legendItemURLGenerator.generateLabel(dataset, series); } Shape shape = lookupLegendShape(series); Paint paint = lookupSeriesPaint(series); Paint outlinePaint = lookupSeriesOutlinePaint(series); Stroke outlineStroke = lookupSeriesOutlineStroke(series); LegendItem item = new LegendItem(label, description, toolTipText, urlText, shape, paint, outlineStroke, outlinePaint); item.setLabelFont(lookupLegendTextFont(series)); Paint labelPaint = lookupLegendTextPaint(series); if (labelPaint != null) { item.setLabelPaint(labelPaint); } item.setSeriesKey(dataset.getRowKey(series)); item.setSeriesIndex(series); item.setDataset(dataset); item.setDatasetIndex(datasetIndex); return item; } /** * Tests this renderer for equality with another object. * * @param obj the object. * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof AbstractCategoryItemRenderer)) { return false; } AbstractCategoryItemRenderer that = (AbstractCategoryItemRenderer) obj; if (!Objects.equals(this.itemLabelGeneratorMap, that.itemLabelGeneratorMap)) { return false; } if (!Objects.equals(this.defaultItemLabelGenerator, that.defaultItemLabelGenerator)) { return false; } if (!Objects.equals(this.toolTipGeneratorMap, that.toolTipGeneratorMap)) { return false; } if (!Objects.equals(this.defaultToolTipGenerator, that.defaultToolTipGenerator)) { return false; } if (!Objects.equals(this.itemURLGeneratorMap, that.itemURLGeneratorMap)) { return false; } if (!Objects.equals(this.defaultItemURLGenerator, that.defaultItemURLGenerator)) { return false; } if (!Objects.equals(this.legendItemLabelGenerator, that.legendItemLabelGenerator)) { return false; } if (!Objects.equals(this.legendItemToolTipGenerator, that.legendItemToolTipGenerator)) { return false; } if (!Objects.equals(this.legendItemURLGenerator, that.legendItemURLGenerator)) { return false; } return super.equals(obj); } /** * Returns a hash code for the renderer. * * @return The hash code. */ @Override public int hashCode() { int result = super.hashCode(); return result; } /** * Returns the drawing supplier from the plot. * * @return The drawing supplier (possibly {@code null}). */ @Override public DrawingSupplier getDrawingSupplier() { DrawingSupplier result = null; CategoryPlot cp = getPlot(); if (cp != null) { result = cp.getDrawingSupplier(); } return result; } /** * Considers the current (x, y) coordinate and updates the crosshair point * if it meets the criteria (usually means the (x, y) coordinate is the * closest to the anchor point so far). * * @param crosshairState the crosshair state ({@code null} permitted, * but the method does nothing in that case). * @param rowKey the row key. * @param columnKey the column key. * @param value the data value. * @param datasetIndex the dataset index. * @param transX the x-value translated to Java2D space. * @param transY the y-value translated to Java2D space. * @param orientation the plot orientation ({@code null} not permitted). */ protected void updateCrosshairValues(CategoryCrosshairState crosshairState, Comparable rowKey, Comparable columnKey, double value, int datasetIndex, double transX, double transY, PlotOrientation orientation) { Args.nullNotPermitted(orientation, "orientation"); if (crosshairState != null) { if (this.plot.isRangeCrosshairLockedOnData()) { // both axes crosshairState.updateCrosshairPoint(rowKey, columnKey, value, datasetIndex, transX, transY, orientation); } else { crosshairState.updateCrosshairX(rowKey, columnKey, datasetIndex, transX, orientation); } } } /** * Draws an item label. * * @param g2 the graphics device. * @param orientation the orientation. * @param dataset the dataset. * @param row the row. * @param column the column. * @param x the x coordinate (in Java2D space). * @param y the y coordinate (in Java2D space). * @param negative indicates a negative value (which affects the item * label position). */ protected void drawItemLabel(Graphics2D g2, PlotOrientation orientation, CategoryDataset dataset, int row, int column, double x, double y, boolean negative) { CategoryItemLabelGenerator generator = getItemLabelGenerator(row, column); if (generator != null) { Font labelFont = getItemLabelFont(row, column); Paint paint = getItemLabelPaint(row, column); g2.setFont(labelFont); g2.setPaint(paint); String label = generator.generateLabel(dataset, row, column); ItemLabelPosition position; if (!negative) { position = getPositiveItemLabelPosition(row, column); } else { position = getNegativeItemLabelPosition(row, column); } Point2D anchorPoint = calculateLabelAnchorPoint( position.getItemLabelAnchor(), x, y, orientation); TextUtils.drawRotatedString(label, g2, (float) anchorPoint.getX(), (float) anchorPoint.getY(), position.getTextAnchor(), position.getAngle(), position.getRotationAnchor()); } } /** * Returns an independent copy of the renderer. The {@code plot} * reference is shallow copied. * * @return A clone. * * @throws CloneNotSupportedException can be thrown if one of the objects * belonging to the renderer does not support cloning (for example, * an item label generator). */ @Override public Object clone() throws CloneNotSupportedException { AbstractCategoryItemRenderer clone = (AbstractCategoryItemRenderer) super.clone(); if (this.itemLabelGeneratorMap != null) { clone.itemLabelGeneratorMap = CloneUtils.cloneMapValues( this.itemLabelGeneratorMap); } if (this.defaultItemLabelGenerator != null) { if (this.defaultItemLabelGenerator instanceof PublicCloneable) { PublicCloneable pc = (PublicCloneable) this.defaultItemLabelGenerator; clone.defaultItemLabelGenerator = (CategoryItemLabelGenerator) pc.clone(); } else { throw new CloneNotSupportedException( "ItemLabelGenerator not cloneable."); } } if (this.toolTipGeneratorMap != null) { clone.toolTipGeneratorMap = CloneUtils.cloneMapValues( this.toolTipGeneratorMap); } if (this.defaultToolTipGenerator != null) { if (this.defaultToolTipGenerator instanceof PublicCloneable) { PublicCloneable pc = (PublicCloneable) this.defaultToolTipGenerator; clone.defaultToolTipGenerator = (CategoryToolTipGenerator) pc.clone(); } else { throw new CloneNotSupportedException( "Default tool tip generator not cloneable."); } } if (this.itemURLGeneratorMap != null) { clone.itemURLGeneratorMap = CloneUtils.cloneMapValues( this.itemURLGeneratorMap); } if (this.defaultItemURLGenerator != null) { if (this.defaultItemURLGenerator instanceof PublicCloneable) { PublicCloneable pc = (PublicCloneable) this.defaultItemURLGenerator; clone.defaultItemURLGenerator = (CategoryURLGenerator) pc.clone(); } else { throw new CloneNotSupportedException( "Default item URL generator not cloneable."); } } if (this.legendItemLabelGenerator instanceof PublicCloneable) { clone.legendItemLabelGenerator = (CategorySeriesLabelGenerator) ObjectUtils.clone(this.legendItemLabelGenerator); } if (this.legendItemToolTipGenerator instanceof PublicCloneable) { clone.legendItemToolTipGenerator = (CategorySeriesLabelGenerator) ObjectUtils.clone(this.legendItemToolTipGenerator); } if (this.legendItemURLGenerator instanceof PublicCloneable) { clone.legendItemURLGenerator = (CategorySeriesLabelGenerator) ObjectUtils.clone(this.legendItemURLGenerator); } return clone; } /** * Returns a domain axis for a plot. * * @param plot the plot. * @param index the axis index. * * @return A domain axis. */ protected CategoryAxis getDomainAxis(CategoryPlot plot, int index) { CategoryAxis result = plot.getDomainAxis(index); if (result == null) { result = plot.getDomainAxis(); } return result; } /** * Returns a range axis for a plot. * * @param plot the plot. * @param index the axis index. * * @return A range axis. */ protected ValueAxis getRangeAxis(CategoryPlot plot, int index) { ValueAxis result = plot.getRangeAxis(index); if (result == null) { result = plot.getRangeAxis(); } return result; } /** * Returns a (possibly empty) collection of legend items for the series * that this renderer is responsible for drawing. * * @return The legend item collection (never {@code null}). * * @see #getLegendItem(int, int) */ @Override public LegendItemCollection getLegendItems() { LegendItemCollection result = new LegendItemCollection(); if (this.plot == null) { return result; } int index = this.plot.getIndexOf(this); CategoryDataset dataset = this.plot.getDataset(index); if (dataset == null) { return result; } int seriesCount = dataset.getRowCount(); if (plot.getRowRenderingOrder().equals(SortOrder.ASCENDING)) { for (int i = 0; i < seriesCount; i++) { if (isSeriesVisibleInLegend(i)) { LegendItem item = getLegendItem(index, i); if (item != null) { result.add(item); } } } } else { for (int i = seriesCount - 1; i >= 0; i--) { if (isSeriesVisibleInLegend(i)) { LegendItem item = getLegendItem(index, i); if (item != null) { result.add(item); } } } } return result; } /** * Returns the legend item label generator. * * @return The label generator (never {@code null}). * * @see #setLegendItemLabelGenerator(CategorySeriesLabelGenerator) */ public CategorySeriesLabelGenerator getLegendItemLabelGenerator() { return this.legendItemLabelGenerator; } /** * Sets the legend item label generator and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param generator the generator ({@code null} not permitted). * * @see #getLegendItemLabelGenerator() */ public void setLegendItemLabelGenerator( CategorySeriesLabelGenerator generator) { Args.nullNotPermitted(generator, "generator"); this.legendItemLabelGenerator = generator; fireChangeEvent(); } /** * Returns the legend item tool tip generator. * * @return The tool tip generator (possibly {@code null}). * * @see #setLegendItemToolTipGenerator(CategorySeriesLabelGenerator) */ public CategorySeriesLabelGenerator getLegendItemToolTipGenerator() { return this.legendItemToolTipGenerator; } /** * Sets the legend item tool tip generator and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param generator the generator ({@code null} permitted). * * @see #setLegendItemToolTipGenerator(CategorySeriesLabelGenerator) */ public void setLegendItemToolTipGenerator( CategorySeriesLabelGenerator generator) { this.legendItemToolTipGenerator = generator; fireChangeEvent(); } /** * Returns the legend item URL generator. * * @return The URL generator (possibly {@code null}). * * @see #setLegendItemURLGenerator(CategorySeriesLabelGenerator) */ public CategorySeriesLabelGenerator getLegendItemURLGenerator() { return this.legendItemURLGenerator; } /** * Sets the legend item URL generator and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param generator the generator ({@code null} permitted). * * @see #getLegendItemURLGenerator() */ public void setLegendItemURLGenerator( CategorySeriesLabelGenerator generator) { this.legendItemURLGenerator = generator; fireChangeEvent(); } /** * Adds an entity with the specified hotspot. * * @param entities the entity collection. * @param dataset the dataset. * @param row the row index. * @param column the column index. * @param hotspot the hotspot ({@code null} not permitted). */ protected void addItemEntity(EntityCollection entities, CategoryDataset dataset, int row, int column, Shape hotspot) { Args.nullNotPermitted(hotspot, "hotspot"); if (!getItemCreateEntity(row, column)) { return; } String tip = null; CategoryToolTipGenerator tipster = getToolTipGenerator(row, column); if (tipster != null) { tip = tipster.generateToolTip(dataset, row, column); } String url = null; CategoryURLGenerator urlster = getItemURLGenerator(row, column); if (urlster != null) { url = urlster.generateURL(dataset, row, column); } CategoryItemEntity entity = new CategoryItemEntity(hotspot, tip, url, dataset, dataset.getRowKey(row), dataset.getColumnKey(column)); entities.add(entity); } /** * Adds an entity to the collection. * * @param entities the entity collection being populated. * @param hotspot the entity area (if {@code null} a default will be * used). * @param dataset the dataset. * @param row the series. * @param column the item. * @param entityX the entity's center x-coordinate in user space (only * used if {@code area} is {@code null}). * @param entityY the entity's center y-coordinate in user space (only * used if {@code area} is {@code null}). */ protected void addEntity(EntityCollection entities, Shape hotspot, CategoryDataset dataset, int row, int column, double entityX, double entityY) { if (!getItemCreateEntity(row, column)) { return; } Shape s = hotspot; if (hotspot == null) { double r = getDefaultEntityRadius(); double w = r * 2; if (getPlot().getOrientation() == PlotOrientation.VERTICAL) { s = new Ellipse2D.Double(entityX - r, entityY - r, w, w); } else { s = new Ellipse2D.Double(entityY - r, entityX - r, w, w); } } String tip = null; CategoryToolTipGenerator generator = getToolTipGenerator(row, column); if (generator != null) { tip = generator.generateToolTip(dataset, row, column); } String url = null; CategoryURLGenerator urlster = getItemURLGenerator(row, column); if (urlster != null) { url = urlster.generateURL(dataset, row, column); } CategoryItemEntity entity = new CategoryItemEntity(s, tip, url, dataset, dataset.getRowKey(row), dataset.getColumnKey(column)); entities.add(entity); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/category/AreaRenderer.java000066400000000000000000000265031463604235500317110ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * AreaRenderer.java * ----------------- * (C) Copyright 2002-present, by Jon Iles and Contributors. * * Original Author: Jon Iles; * Contributor(s): David Gilbert; * Christian W. Zuckschwerdt; * */ package org.jfree.chart.renderer.category; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.GeneralPath; import java.awt.geom.Rectangle2D; import java.io.Serializable; import org.jfree.chart.LegendItem; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.renderer.AreaRendererEndType; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.category.CategoryDataset; /** * A category item renderer that draws area charts. You can use this renderer * with the {@link CategoryPlot} class. The example shown here is generated * by the {@code AreaChartDemo1.java} program included in the JFreeChart * Demo Collection: *

* AreaRendererSample.png */ public class AreaRenderer extends AbstractCategoryItemRenderer implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -4231878281385812757L; /** A flag that controls how the ends of the areas are drawn. */ private AreaRendererEndType endType; /** * Creates a new renderer. */ public AreaRenderer() { super(); this.endType = AreaRendererEndType.TAPER; setDefaultLegendShape(new Rectangle2D.Double(-4.0, -4.0, 8.0, 8.0)); } /** * Returns a token that controls how the renderer draws the end points. * The default value is {@link AreaRendererEndType#TAPER}. * * @return The end type (never {@code null}). * * @see #setEndType */ public AreaRendererEndType getEndType() { return this.endType; } /** * Sets a token that controls how the renderer draws the end points, and * sends a {@link RendererChangeEvent} to all registered listeners. * * @param type the end type ({@code null} not permitted). * * @see #getEndType() */ public void setEndType(AreaRendererEndType type) { Args.nullNotPermitted(type, "type"); this.endType = type; fireChangeEvent(); } /** * Returns a legend item for a series. * * @param datasetIndex the dataset index (zero-based). * @param series the series index (zero-based). * * @return The legend item. */ @Override public LegendItem getLegendItem(int datasetIndex, int series) { // if there is no plot, there is no dataset to access... CategoryPlot cp = getPlot(); if (cp == null) { return null; } // check that a legend item needs to be displayed... if (!isSeriesVisible(series) || !isSeriesVisibleInLegend(series)) { return null; } CategoryDataset dataset = cp.getDataset(datasetIndex); String label = getLegendItemLabelGenerator().generateLabel(dataset, series); String description = label; String toolTipText = null; if (getLegendItemToolTipGenerator() != null) { toolTipText = getLegendItemToolTipGenerator().generateLabel( dataset, series); } String urlText = null; if (getLegendItemURLGenerator() != null) { urlText = getLegendItemURLGenerator().generateLabel(dataset, series); } Shape shape = lookupLegendShape(series); Paint paint = lookupSeriesPaint(series); Paint outlinePaint = lookupSeriesOutlinePaint(series); Stroke outlineStroke = lookupSeriesOutlineStroke(series); LegendItem result = new LegendItem(label, description, toolTipText, urlText, shape, paint, outlineStroke, outlinePaint); result.setLabelFont(lookupLegendTextFont(series)); Paint labelPaint = lookupLegendTextPaint(series); if (labelPaint != null) { result.setLabelPaint(labelPaint); } result.setDataset(dataset); result.setDatasetIndex(datasetIndex); result.setSeriesKey(dataset.getRowKey(series)); result.setSeriesIndex(series); return result; } /** * Draw a single data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the data plot area. * @param plot the plot. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset. * @param row the row index (zero-based). * @param column the column index (zero-based). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, ValueAxis rangeAxis, CategoryDataset dataset, int row, int column, int pass) { // do nothing if item is not visible or null if (!getItemVisible(row, column)) { return; } Number value = dataset.getValue(row, column); if (value == null) { return; } PlotOrientation orientation = plot.getOrientation(); RectangleEdge axisEdge = plot.getDomainAxisEdge(); int count = dataset.getColumnCount(); float x0 = (float) domainAxis.getCategoryStart(column, count, dataArea, axisEdge); float x1 = (float) domainAxis.getCategoryMiddle(column, count, dataArea, axisEdge); float x2 = (float) domainAxis.getCategoryEnd(column, count, dataArea, axisEdge); x0 = Math.round(x0); x1 = Math.round(x1); x2 = Math.round(x2); if (this.endType == AreaRendererEndType.TRUNCATE) { if (column == 0) { x0 = x1; } else if (column == getColumnCount() - 1) { x2 = x1; } } double yy1 = value.doubleValue(); double yy0 = 0.0; if (this.endType == AreaRendererEndType.LEVEL) { yy0 = yy1; } if (column > 0) { Number n0 = dataset.getValue(row, column - 1); if (n0 != null) { yy0 = (n0.doubleValue() + yy1) / 2.0; } } double yy2 = 0.0; if (column < dataset.getColumnCount() - 1) { Number n2 = dataset.getValue(row, column + 1); if (n2 != null) { yy2 = (n2.doubleValue() + yy1) / 2.0; } } else if (this.endType == AreaRendererEndType.LEVEL) { yy2 = yy1; } RectangleEdge edge = plot.getRangeAxisEdge(); float y0 = (float) rangeAxis.valueToJava2D(yy0, dataArea, edge); float y1 = (float) rangeAxis.valueToJava2D(yy1, dataArea, edge); float y2 = (float) rangeAxis.valueToJava2D(yy2, dataArea, edge); float yz = (float) rangeAxis.valueToJava2D(0.0, dataArea, edge); double labelXX = x1; double labelYY = y1; g2.setPaint(getItemPaint(row, column)); g2.setStroke(getItemStroke(row, column)); GeneralPath area = new GeneralPath(); if (orientation == PlotOrientation.VERTICAL) { area.moveTo(x0, yz); area.lineTo(x0, y0); area.lineTo(x1, y1); area.lineTo(x2, y2); area.lineTo(x2, yz); } else if (orientation == PlotOrientation.HORIZONTAL) { area.moveTo(yz, x0); area.lineTo(y0, x0); area.lineTo(y1, x1); area.lineTo(y2, x2); area.lineTo(yz, x2); double temp = labelXX; labelXX = labelYY; labelYY = temp; } area.closePath(); g2.setPaint(getItemPaint(row, column)); g2.fill(area); // draw the item labels if there are any... if (isItemLabelVisible(row, column)) { drawItemLabel(g2, orientation, dataset, row, column, labelXX, labelYY, (value.doubleValue() < 0.0)); } // submit the current data point as a crosshair candidate int datasetIndex = plot.indexOf(dataset); updateCrosshairValues(state.getCrosshairState(), dataset.getRowKey(row), dataset.getColumnKey(column), yy1, datasetIndex, x1, y1, orientation); // add an item entity, if this information is being collected EntityCollection entities = state.getEntityCollection(); if (entities != null) { addItemEntity(entities, dataset, row, column, area); } } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object to test ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof AreaRenderer)) { return false; } AreaRenderer that = (AreaRenderer) obj; if (!this.endType.equals(that.endType)) { return false; } return super.equals(obj); } /** * Returns an independent copy of the renderer. * * @return A clone. * * @throws CloneNotSupportedException should not happen. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/category/BarPainter.java000066400000000000000000000063361463604235500314030ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * BarPainter.java * --------------- * (C) Copyright 2008-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.category; import java.awt.Graphics2D; import java.awt.geom.RectangularShape; import org.jfree.chart.ui.RectangleEdge; /** * The interface for plugin painter for the {@link BarRenderer} class. When * developing a class that implements this interface, bear in mind the * following: *

    *
  • the {@code equals(Object)} method should be overridden;
  • *
  • instances of the class should be immutable OR implement the * {@code PublicCloneable} interface, so that a renderer using the * painter can be cloned reliably; *
  • the class should be {@code Serializable}, otherwise chart * serialization will not be supported.
  • *
*/ public interface BarPainter { /** * Paints a single bar on behalf of a renderer. * * @param g2 the graphics target. * @param renderer the renderer. * @param row the row index for the item. * @param column the column index for the item. * @param bar the bounds for the bar. * @param base the base of the bar. */ void paintBar(Graphics2D g2, BarRenderer renderer, int row, int column, RectangularShape bar, RectangleEdge base); /** * Paints the shadow for a single bar on behalf of a renderer. * * @param g2 the graphics target. * @param renderer the renderer. * @param row the row index for the item. * @param column the column index for the item. * @param bar the bounds for the bar. * @param base the base of the bar. * @param pegShadow peg the shadow to the base of the bar? */ void paintBarShadow(Graphics2D g2, BarRenderer renderer, int row, int column, RectangularShape bar, RectangleEdge base, boolean pegShadow); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/category/BarRenderer.java000066400000000000000000001276011463604235500315460ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * BarRenderer.java * ---------------- * (C) Copyright 2002-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Christian W. Zuckschwerdt; * Peter Kolb (patches 2497611, 2791407); * */ package org.jfree.chart.renderer.category; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.LegendItem; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.labels.CategoryItemLabelGenerator; import org.jfree.chart.labels.ItemLabelAnchor; import org.jfree.chart.labels.ItemLabelPosition; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.text.TextUtils; import org.jfree.chart.ui.GradientPaintTransformer; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.StandardGradientPaintTransformer; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; import org.jfree.data.KeyedValues2DItemKey; import org.jfree.data.Range; import org.jfree.data.category.CategoryDataset; /** * A {@link CategoryItemRenderer} that draws individual data items as bars. * The example shown here is generated by the {@code BarChartDemo1.java} * program included in the JFreeChart Demo Collection: *

* BarChartDemo1.svg */ public class BarRenderer extends AbstractCategoryItemRenderer implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 6000649414965887481L; /** The default item margin percentage. */ public static final double DEFAULT_ITEM_MARGIN = 0.20; /** * Constant that controls the minimum width before a bar has an outline * drawn. */ public static final double BAR_OUTLINE_WIDTH_THRESHOLD = 3.0; /** * The default bar painter assigned to each new instance of this renderer. */ private static BarPainter defaultBarPainter = new GradientBarPainter(); /** * Returns the default bar painter. * * @return The default bar painter. */ public static BarPainter getDefaultBarPainter() { return BarRenderer.defaultBarPainter; } /** * Sets the default bar painter. * * @param painter the painter ({@code null} not permitted). */ public static void setDefaultBarPainter(BarPainter painter) { Args.nullNotPermitted(painter, "painter"); BarRenderer.defaultBarPainter = painter; } /** * The default value for the initialisation of the shadowsVisible flag. */ private static boolean defaultShadowsVisible = true; /** * Returns the default value for the {@code shadowsVisible} flag. * * @return A boolean. * * @see #setDefaultShadowsVisible(boolean) */ public static boolean getDefaultShadowsVisible() { return BarRenderer.defaultShadowsVisible; } /** * Sets the default value for the shadows visible flag. * * @param visible the new value for the default. * * @see #getDefaultShadowsVisible() */ public static void setDefaultShadowsVisible(boolean visible) { BarRenderer.defaultShadowsVisible = visible; } /** The margin between items (bars) within a category. */ private double itemMargin; /** A flag that controls whether or not bar outlines are drawn. */ private boolean drawBarOutline; /** The maximum bar width as a percentage of the available space. */ private double maximumBarWidth; /** The minimum bar length (in Java2D units). */ private double minimumBarLength; /** * An optional class used to transform gradient paint objects to fit each * bar. */ private GradientPaintTransformer gradientPaintTransformer; /** * The fallback position if a positive item label doesn't fit inside the * bar. */ private ItemLabelPosition positiveItemLabelPositionFallback; /** * The fallback position if a negative item label doesn't fit inside the * bar. */ private ItemLabelPosition negativeItemLabelPositionFallback; /** The upper clip (axis) value for the axis. */ private double upperClip; // TODO: this needs to move into the renderer state /** The lower clip (axis) value for the axis. */ private double lowerClip; // TODO: this needs to move into the renderer state /** The base value for the bars (defaults to 0.0). */ private double base; /** * A flag that controls whether the base value is included in the range * returned by the findRangeBounds() method. */ private boolean includeBaseInRange; /** * The bar painter (never {@code null}). */ private BarPainter barPainter; /** * The flag that controls whether or not shadows are drawn for the bars. */ private boolean shadowsVisible; /** * The shadow paint. */ private transient Paint shadowPaint; /** * The x-offset for the shadow effect. */ private double shadowXOffset; /** * The y-offset for the shadow effect. */ private double shadowYOffset; /** * Creates a new bar renderer with default settings. */ public BarRenderer() { super(); this.base = 0.0; this.includeBaseInRange = true; this.itemMargin = DEFAULT_ITEM_MARGIN; this.drawBarOutline = false; this.maximumBarWidth = 1.0; // 100 percent, so it will not apply unless changed this.positiveItemLabelPositionFallback = null; this.negativeItemLabelPositionFallback = null; this.gradientPaintTransformer = new StandardGradientPaintTransformer(); this.minimumBarLength = 0.0; setDefaultLegendShape(new Rectangle2D.Double(-4.0, -4.0, 8.0, 8.0)); this.barPainter = getDefaultBarPainter(); this.shadowsVisible = getDefaultShadowsVisible(); this.shadowPaint = Color.GRAY; this.shadowXOffset = 4.0; this.shadowYOffset = 4.0; } /** * Returns the base value for the bars. The default value is * {@code 0.0}. * * @return The base value for the bars. * * @see #setBase(double) */ public double getBase() { return this.base; } /** * Sets the base value for the bars and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param base the new base value. * * @see #getBase() */ public void setBase(double base) { this.base = base; fireChangeEvent(); } /** * Returns the item margin as a percentage of the available space for all * bars. * * @return The margin percentage (where 0.10 is ten percent). * * @see #setItemMargin(double) */ public double getItemMargin() { return this.itemMargin; } /** * Sets the item margin and sends a {@link RendererChangeEvent} to all * registered listeners. The value is expressed as a percentage of the * available width for plotting all the bars, with the resulting amount to * be distributed between all the bars evenly. * * @param percent the margin (where 0.10 is ten percent). * * @see #getItemMargin() */ public void setItemMargin(double percent) { this.itemMargin = percent; fireChangeEvent(); } /** * Returns a flag that controls whether or not bar outlines are drawn. * * @return A boolean. * * @see #setDrawBarOutline(boolean) */ public boolean isDrawBarOutline() { return this.drawBarOutline; } /** * Sets the flag that controls whether or not bar outlines are drawn and * sends a {@link RendererChangeEvent} to all registered listeners. * * @param draw the flag. * * @see #isDrawBarOutline() */ public void setDrawBarOutline(boolean draw) { this.drawBarOutline = draw; fireChangeEvent(); } /** * Returns the maximum bar width, as a percentage of the available drawing * space. * * @return The maximum bar width. * * @see #setMaximumBarWidth(double) */ public double getMaximumBarWidth() { return this.maximumBarWidth; } /** * Sets the maximum bar width, which is specified as a percentage of the * available space for all bars, and sends a {@link RendererChangeEvent} to * all registered listeners. * * @param percent the percent (where 0.05 is five percent). * * @see #getMaximumBarWidth() */ public void setMaximumBarWidth(double percent) { this.maximumBarWidth = percent; fireChangeEvent(); } /** * Returns the minimum bar length (in Java2D units). The default value is * 0.0. * * @return The minimum bar length. * * @see #setMinimumBarLength(double) */ public double getMinimumBarLength() { return this.minimumBarLength; } /** * Sets the minimum bar length and sends a {@link RendererChangeEvent} to * all registered listeners. The minimum bar length is specified in Java2D * units, and can be used to prevent bars that represent very small data * values from disappearing when drawn on the screen. Typically you would * set this to (say) 0.5 or 1.0 Java 2D units. Use this attribute with * caution, however, because setting it to a non-zero value will * artificially increase the length of bars representing small values, * which may misrepresent your data. * * @param min the minimum bar length (in Java2D units, must be >= 0.0). * * @see #getMinimumBarLength() */ public void setMinimumBarLength(double min) { if (min < 0.0) { throw new IllegalArgumentException("Requires 'min' >= 0.0"); } this.minimumBarLength = min; fireChangeEvent(); } /** * Returns the gradient paint transformer (an object used to transform * gradient paint objects to fit each bar). * * @return A transformer ({@code null} possible). * * @see #setGradientPaintTransformer(GradientPaintTransformer) */ public GradientPaintTransformer getGradientPaintTransformer() { return this.gradientPaintTransformer; } /** * Sets the gradient paint transformer and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param transformer the transformer ({@code null} permitted). * * @see #getGradientPaintTransformer() */ public void setGradientPaintTransformer( GradientPaintTransformer transformer) { this.gradientPaintTransformer = transformer; fireChangeEvent(); } /** * Returns the fallback position for positive item labels that don't fit * within a bar. * * @return The fallback position ({@code null} possible). * * @see #setPositiveItemLabelPositionFallback(ItemLabelPosition) */ public ItemLabelPosition getPositiveItemLabelPositionFallback() { return this.positiveItemLabelPositionFallback; } /** * Sets the fallback position for positive item labels that don't fit * within a bar, and sends a {@link RendererChangeEvent} to all registered * listeners. * * @param position the position ({@code null} permitted). * * @see #getPositiveItemLabelPositionFallback() */ public void setPositiveItemLabelPositionFallback( ItemLabelPosition position) { this.positiveItemLabelPositionFallback = position; fireChangeEvent(); } /** * Returns the fallback position for negative item labels that don't fit * within a bar. * * @return The fallback position ({@code null} possible). * * @see #setPositiveItemLabelPositionFallback(ItemLabelPosition) */ public ItemLabelPosition getNegativeItemLabelPositionFallback() { return this.negativeItemLabelPositionFallback; } /** * Sets the fallback position for negative item labels that don't fit * within a bar, and sends a {@link RendererChangeEvent} to all registered * listeners. * * @param position the position ({@code null} permitted). * * @see #getNegativeItemLabelPositionFallback() */ public void setNegativeItemLabelPositionFallback( ItemLabelPosition position) { this.negativeItemLabelPositionFallback = position; fireChangeEvent(); } /** * Returns the flag that controls whether or not the base value for the * bars is included in the range calculated by * {@link #findRangeBounds(CategoryDataset)}. * * @return {@code true} if the base is included in the range, and * {@code false} otherwise. * * @see #setIncludeBaseInRange(boolean) */ public boolean getIncludeBaseInRange() { return this.includeBaseInRange; } /** * Sets the flag that controls whether or not the base value for the bars * is included in the range calculated by * {@link #findRangeBounds(CategoryDataset)}. If the flag is changed, * a {@link RendererChangeEvent} is sent to all registered listeners. * * @param include the new value for the flag. * * @see #getIncludeBaseInRange() */ public void setIncludeBaseInRange(boolean include) { if (this.includeBaseInRange != include) { this.includeBaseInRange = include; fireChangeEvent(); } } /** * Returns the bar painter. * * @return The bar painter (never {@code null}). * * @see #setBarPainter(BarPainter) */ public BarPainter getBarPainter() { return this.barPainter; } /** * Sets the bar painter for this renderer and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param painter the painter ({@code null} not permitted). * * @see #getBarPainter() */ public void setBarPainter(BarPainter painter) { Args.nullNotPermitted(painter, "painter"); this.barPainter = painter; fireChangeEvent(); } /** * Returns the flag that controls whether or not shadows are drawn for * the bars. * * @return A boolean. */ public boolean getShadowsVisible() { return this.shadowsVisible; } /** * Sets the flag that controls whether or not shadows are * drawn by the renderer. * * @param visible the new flag value. */ public void setShadowVisible(boolean visible) { this.shadowsVisible = visible; fireChangeEvent(); } /** * Returns the shadow paint. * * @return The shadow paint. * * @see #setShadowPaint(Paint) */ public Paint getShadowPaint() { return this.shadowPaint; } /** * Sets the shadow paint and sends a {@link RendererChangeEvent} to all * registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getShadowPaint() */ public void setShadowPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.shadowPaint = paint; fireChangeEvent(); } /** * Returns the shadow x-offset. * * @return The shadow x-offset. */ public double getShadowXOffset() { return this.shadowXOffset; } /** * Sets the x-offset for the bar shadow and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param offset the offset. */ public void setShadowXOffset(double offset) { this.shadowXOffset = offset; fireChangeEvent(); } /** * Returns the shadow y-offset. * * @return The shadow y-offset. */ public double getShadowYOffset() { return this.shadowYOffset; } /** * Sets the y-offset for the bar shadow and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param offset the offset. */ public void setShadowYOffset(double offset) { this.shadowYOffset = offset; fireChangeEvent(); } /** * Returns the lower clip value. This value is recalculated in the * initialise() method. * * @return The value. */ public double getLowerClip() { // TODO: this attribute should be transferred to the renderer state. return this.lowerClip; } /** * Returns the upper clip value. This value is recalculated in the * initialise() method. * * @return The value. */ public double getUpperClip() { // TODO: this attribute should be transferred to the renderer state. return this.upperClip; } /** * Initialises the renderer and returns a state object that will be passed * to subsequent calls to the drawItem method. This method gets called * once at the start of the process of drawing a chart. * * @param g2 the graphics device. * @param dataArea the area in which the data is to be plotted. * @param plot the plot. * @param rendererIndex the renderer index. * @param info collects chart rendering information for return to caller. * * @return The renderer state. */ @Override public CategoryItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, CategoryPlot plot, int rendererIndex, PlotRenderingInfo info) { CategoryItemRendererState state = super.initialise(g2, dataArea, plot, rendererIndex, info); // get the clipping values... ValueAxis rangeAxis = plot.getRangeAxisForDataset(rendererIndex); this.lowerClip = rangeAxis.getRange().getLowerBound(); this.upperClip = rangeAxis.getRange().getUpperBound(); // calculate the bar width calculateBarWidth(plot, dataArea, rendererIndex, state); return state; } /** * Calculates the bar width and stores it in the renderer state. * * @param plot the plot. * @param dataArea the data area. * @param rendererIndex the renderer index. * @param state the renderer state. */ protected void calculateBarWidth(CategoryPlot plot, Rectangle2D dataArea, int rendererIndex, CategoryItemRendererState state) { CategoryAxis domainAxis = getDomainAxis(plot, rendererIndex); CategoryDataset dataset = plot.getDataset(rendererIndex); if (dataset != null) { int columns = dataset.getColumnCount(); int rows = state.getVisibleSeriesCount() >= 0 ? state.getVisibleSeriesCount() : dataset.getRowCount(); double space = 0.0; PlotOrientation orientation = plot.getOrientation(); if (orientation == PlotOrientation.HORIZONTAL) { space = dataArea.getHeight(); } else if (orientation == PlotOrientation.VERTICAL) { space = dataArea.getWidth(); } double maxWidth = space * getMaximumBarWidth(); double categoryMargin = 0.0; double currentItemMargin = 0.0; if (columns > 1) { categoryMargin = domainAxis.getCategoryMargin(); } if (rows > 1) { currentItemMargin = getItemMargin(); } double used = space * (1 - domainAxis.getLowerMargin() - domainAxis.getUpperMargin() - categoryMargin - currentItemMargin); if ((rows * columns) > 0) { state.setBarWidth(Math.min(used / (rows * columns), maxWidth)); } else { state.setBarWidth(Math.min(used, maxWidth)); } } } /** * Calculates the coordinate of the first "side" of a bar. This will be * the minimum x-coordinate for a vertical bar, and the minimum * y-coordinate for a horizontal bar. * * @param plot the plot. * @param orientation the plot orientation. * @param dataArea the data area. * @param domainAxis the domain axis. * @param state the renderer state (has the bar width precalculated). * @param row the row index. * @param column the column index. * * @return The coordinate. */ protected double calculateBarW0(CategoryPlot plot, PlotOrientation orientation, Rectangle2D dataArea, CategoryAxis domainAxis, CategoryItemRendererState state, int row, int column) { // calculate bar width... double space; if (orientation == PlotOrientation.HORIZONTAL) { space = dataArea.getHeight(); } else { space = dataArea.getWidth(); } double barW0 = domainAxis.getCategoryStart(column, getColumnCount(), dataArea, plot.getDomainAxisEdge()); int seriesCount = state.getVisibleSeriesCount() >= 0 ? state.getVisibleSeriesCount() : getRowCount(); int categoryCount = getColumnCount(); if (seriesCount > 1) { double seriesGap = space * getItemMargin() / (categoryCount * (seriesCount - 1)); double seriesW = calculateSeriesWidth(space, domainAxis, categoryCount, seriesCount); barW0 = barW0 + row * (seriesW + seriesGap) + (seriesW / 2.0) - (state.getBarWidth() / 2.0); } else { barW0 = domainAxis.getCategoryMiddle(column, getColumnCount(), dataArea, plot.getDomainAxisEdge()) - state.getBarWidth() / 2.0; } return barW0; } /** * Calculates the coordinates for the length of a single bar. * * @param value the value represented by the bar. * * @return The coordinates for each end of the bar (or {@code null} if * the bar is not visible for the current axis range). */ protected double[] calculateBarL0L1(double value) { double lclip = getLowerClip(); double uclip = getUpperClip(); double barLow = Math.min(this.base, value); double barHigh = Math.max(this.base, value); if (barHigh < lclip) { // bar is not visible return null; } if (barLow > uclip) { // bar is not visible return null; } barLow = Math.max(barLow, lclip); barHigh = Math.min(barHigh, uclip); return new double[] {barLow, barHigh}; } /** * Returns the range of values the renderer requires to display all the * items from the specified dataset. This takes into account the range * of values in the dataset, plus the flag that determines whether or not * the base value for the bars should be included in the range. * * @param dataset the dataset ({@code null} permitted). * @param includeInterval include the interval if the dataset has one? * * @return The range (or {@code null} if the dataset is * {@code null} or empty). */ @Override public Range findRangeBounds(CategoryDataset dataset, boolean includeInterval) { if (dataset == null) { return null; } Range result = super.findRangeBounds(dataset, includeInterval); if (result != null) { if (this.includeBaseInRange) { result = Range.expandToInclude(result, this.base); } } return result; } /** * Returns a legend item for a series. * * @param datasetIndex the dataset index (zero-based). * @param series the series index (zero-based). * * @return The legend item (possibly {@code null}). */ @Override public LegendItem getLegendItem(int datasetIndex, int series) { CategoryPlot cp = getPlot(); if (cp == null) { return null; } // check that a legend item needs to be displayed... if (!isSeriesVisible(series) || !isSeriesVisibleInLegend(series)) { return null; } CategoryDataset dataset = cp.getDataset(datasetIndex); String label = getLegendItemLabelGenerator().generateLabel(dataset, series); String description = label; String toolTipText = null; if (getLegendItemToolTipGenerator() != null) { toolTipText = getLegendItemToolTipGenerator().generateLabel( dataset, series); } String urlText = null; if (getLegendItemURLGenerator() != null) { urlText = getLegendItemURLGenerator().generateLabel(dataset, series); } Shape shape = lookupLegendShape(series); Paint paint = lookupSeriesPaint(series); Paint outlinePaint = lookupSeriesOutlinePaint(series); Stroke outlineStroke = lookupSeriesOutlineStroke(series); LegendItem result = new LegendItem(label, description, toolTipText, urlText, true, shape, true, paint, isDrawBarOutline(), outlinePaint, outlineStroke, false, new Line2D.Float(), new BasicStroke(1.0f), Color.BLACK); result.setLabelFont(lookupLegendTextFont(series)); Paint labelPaint = lookupLegendTextPaint(series); if (labelPaint != null) { result.setLabelPaint(labelPaint); } result.setDataset(dataset); result.setDatasetIndex(datasetIndex); result.setSeriesKey(dataset.getRowKey(series)); result.setSeriesIndex(series); if (this.gradientPaintTransformer != null) { result.setFillPaintTransformer(this.gradientPaintTransformer); } return result; } /** * Draws the bar for a single (series, category) data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the data area. * @param plot the plot. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset. * @param row the row index (zero-based). * @param column the column index (zero-based). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, ValueAxis rangeAxis, CategoryDataset dataset, int row, int column, int pass) { // nothing is drawn if the row index is not included in the list with // the indices of the visible rows... int visibleRow = state.getVisibleSeriesIndex(row); if (visibleRow < 0) { return; } // nothing is drawn for null values... Number dataValue = dataset.getValue(row, column); if (dataValue == null) { return; } final double value = dataValue.doubleValue(); PlotOrientation orientation = plot.getOrientation(); double barW0 = calculateBarW0(plot, orientation, dataArea, domainAxis, state, visibleRow, column); double[] barL0L1 = calculateBarL0L1(value); if (barL0L1 == null) { return; // the bar is not visible } RectangleEdge edge = plot.getRangeAxisEdge(); double transL0 = rangeAxis.valueToJava2D(barL0L1[0], dataArea, edge); double transL1 = rangeAxis.valueToJava2D(barL0L1[1], dataArea, edge); // in the following code, barL0 is (in Java2D coordinates) the LEFT // end of the bar for a horizontal bar chart, and the TOP end of the // bar for a vertical bar chart. Whether this is the BASE of the bar // or not depends also on (a) whether the data value is 'negative' // relative to the base value and (b) whether or not the range axis is // inverted. This only matters if/when we apply the minimumBarLength // attribute, because we should extend the non-base end of the bar boolean positive = (value >= this.base); boolean inverted = rangeAxis.isInverted(); double barL0 = Math.min(transL0, transL1); double barLength = Math.abs(transL1 - transL0); double barLengthAdj = 0.0; if (barLength > 0.0 && barLength < getMinimumBarLength()) { barLengthAdj = getMinimumBarLength() - barLength; } double barL0Adj = 0.0; RectangleEdge barBase; if (orientation == PlotOrientation.HORIZONTAL) { if (positive && inverted || !positive && !inverted) { barL0Adj = barLengthAdj; barBase = RectangleEdge.RIGHT; } else { barBase = RectangleEdge.LEFT; } } else { if (positive && !inverted || !positive && inverted) { barL0Adj = barLengthAdj; barBase = RectangleEdge.BOTTOM; } else { barBase = RectangleEdge.TOP; } } // draw the bar... Rectangle2D bar; if (orientation == PlotOrientation.HORIZONTAL) { bar = new Rectangle2D.Double(barL0 - barL0Adj, barW0, barLength + barLengthAdj, state.getBarWidth()); } else { bar = new Rectangle2D.Double(barW0, barL0 - barL0Adj, state.getBarWidth(), barLength + barLengthAdj); } if (state.getElementHinting()) { KeyedValues2DItemKey key = new KeyedValues2DItemKey( dataset.getRowKey(row), dataset.getColumnKey(column)); beginElementGroup(g2, key); } if (getShadowsVisible()) { this.barPainter.paintBarShadow(g2, this, row, column, bar, barBase, true); } this.barPainter.paintBar(g2, this, row, column, bar, barBase); if (state.getElementHinting()) { endElementGroup(g2); } CategoryItemLabelGenerator generator = getItemLabelGenerator(row, column); if (generator != null && isItemLabelVisible(row, column)) { drawItemLabel(g2, dataset, row, column, plot, generator, bar, (value < 0.0)); } // submit the current data point as a crosshair candidate int datasetIndex = plot.indexOf(dataset); updateCrosshairValues(state.getCrosshairState(), dataset.getRowKey(row), dataset.getColumnKey(column), value, datasetIndex, barW0, barL0, orientation); // add an item entity, if this information is being collected EntityCollection entities = state.getEntityCollection(); if (entities != null) { addItemEntity(entities, dataset, row, column, bar); } } /** * Calculates the available space for each series. * * @param space the space along the entire axis (in Java2D units). * @param axis the category axis. * @param categories the number of categories. * @param series the number of series. * * @return The width of one series. */ protected double calculateSeriesWidth(double space, CategoryAxis axis, int categories, int series) { double factor = 1.0 - getItemMargin() - axis.getLowerMargin() - axis.getUpperMargin(); if (categories > 1) { factor = factor - axis.getCategoryMargin(); } return (space * factor) / (categories * series); } /** * Draws an item label. This method is overridden so that the bar can be * used to calculate the label anchor point. * * @param g2 the graphics device. * @param data the dataset. * @param row the row. * @param column the column. * @param plot the plot. * @param generator the label generator. * @param bar the bar. * @param negative a flag indicating a negative value. */ protected void drawItemLabel(Graphics2D g2, CategoryDataset data, int row, int column, CategoryPlot plot, CategoryItemLabelGenerator generator, Rectangle2D bar, boolean negative) { String label = generator.generateLabel(data, row, column); if (label == null) { return; // nothing to do } Font labelFont = getItemLabelFont(row, column); g2.setFont(labelFont); Paint paint = getItemLabelPaint(row, column); g2.setPaint(paint); // find out where to place the label... ItemLabelPosition position; if (!negative) { position = getPositiveItemLabelPosition(row, column); } else { position = getNegativeItemLabelPosition(row, column); } // work out the label anchor point... Point2D anchorPoint = calculateLabelAnchorPoint( position.getItemLabelAnchor(), bar, plot.getOrientation()); if (position.getItemLabelAnchor().isInternal()) { Shape bounds = TextUtils.calculateRotatedStringBounds(label, g2, (float) anchorPoint.getX(), (float) anchorPoint.getY(), position.getTextAnchor(), position.getAngle(), position.getRotationAnchor()); if (bounds != null) { if (!bar.contains(bounds.getBounds2D())) { if (!negative) { position = getPositiveItemLabelPositionFallback(); } else { position = getNegativeItemLabelPositionFallback(); } if (position != null) { anchorPoint = calculateLabelAnchorPoint( position.getItemLabelAnchor(), bar, plot.getOrientation()); } } } } if (position != null) { TextUtils.drawRotatedString(label, g2, (float) anchorPoint.getX(), (float) anchorPoint.getY(), position.getTextAnchor(), position.getAngle(), position.getRotationAnchor()); } } /** * Calculates the item label anchor point. * * @param anchor the anchor. * @param bar the bar. * @param orientation the plot orientation. * * @return The anchor point. */ private Point2D calculateLabelAnchorPoint(ItemLabelAnchor anchor, Rectangle2D bar, PlotOrientation orientation) { Point2D result = null; double offset = getItemLabelAnchorOffset(); double x0 = bar.getX() - offset; double x1 = bar.getX(); double x2 = bar.getX() + offset; double x3 = bar.getCenterX(); double x4 = bar.getMaxX() - offset; double x5 = bar.getMaxX(); double x6 = bar.getMaxX() + offset; double y0 = bar.getMaxY() + offset; double y1 = bar.getMaxY(); double y2 = bar.getMaxY() - offset; double y3 = bar.getCenterY(); double y4 = bar.getMinY() + offset; double y5 = bar.getMinY(); double y6 = bar.getMinY() - offset; if (anchor == ItemLabelAnchor.CENTER) { result = new Point2D.Double(x3, y3); } else if (anchor == ItemLabelAnchor.INSIDE1) { result = new Point2D.Double(x4, y4); } else if (anchor == ItemLabelAnchor.INSIDE2) { result = new Point2D.Double(x4, y4); } else if (anchor == ItemLabelAnchor.INSIDE3) { result = new Point2D.Double(x4, y3); } else if (anchor == ItemLabelAnchor.INSIDE4) { result = new Point2D.Double(x4, y2); } else if (anchor == ItemLabelAnchor.INSIDE5) { result = new Point2D.Double(x4, y2); } else if (anchor == ItemLabelAnchor.INSIDE6) { result = new Point2D.Double(x3, y2); } else if (anchor == ItemLabelAnchor.INSIDE7) { result = new Point2D.Double(x2, y2); } else if (anchor == ItemLabelAnchor.INSIDE8) { result = new Point2D.Double(x2, y2); } else if (anchor == ItemLabelAnchor.INSIDE9) { result = new Point2D.Double(x2, y3); } else if (anchor == ItemLabelAnchor.INSIDE10) { result = new Point2D.Double(x2, y4); } else if (anchor == ItemLabelAnchor.INSIDE11) { result = new Point2D.Double(x2, y4); } else if (anchor == ItemLabelAnchor.INSIDE12) { result = new Point2D.Double(x3, y4); } else if (anchor == ItemLabelAnchor.OUTSIDE1) { result = new Point2D.Double(x5, y6); } else if (anchor == ItemLabelAnchor.OUTSIDE2) { result = new Point2D.Double(x6, y5); } else if (anchor == ItemLabelAnchor.OUTSIDE3) { result = new Point2D.Double(x6, y3); } else if (anchor == ItemLabelAnchor.OUTSIDE4) { result = new Point2D.Double(x6, y1); } else if (anchor == ItemLabelAnchor.OUTSIDE5) { result = new Point2D.Double(x5, y0); } else if (anchor == ItemLabelAnchor.OUTSIDE6) { result = new Point2D.Double(x3, y0); } else if (anchor == ItemLabelAnchor.OUTSIDE7) { result = new Point2D.Double(x1, y0); } else if (anchor == ItemLabelAnchor.OUTSIDE8) { result = new Point2D.Double(x0, y1); } else if (anchor == ItemLabelAnchor.OUTSIDE9) { result = new Point2D.Double(x0, y3); } else if (anchor == ItemLabelAnchor.OUTSIDE10) { result = new Point2D.Double(x0, y5); } else if (anchor == ItemLabelAnchor.OUTSIDE11) { result = new Point2D.Double(x1, y6); } else if (anchor == ItemLabelAnchor.OUTSIDE12) { result = new Point2D.Double(x3, y6); } return result; } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof BarRenderer)) { return false; } BarRenderer that = (BarRenderer) obj; if (this.base != that.base) { return false; } if (this.itemMargin != that.itemMargin) { return false; } if (this.drawBarOutline != that.drawBarOutline) { return false; } if (this.maximumBarWidth != that.maximumBarWidth) { return false; } if (this.minimumBarLength != that.minimumBarLength) { return false; } if (!Objects.equals(this.gradientPaintTransformer, that.gradientPaintTransformer)) { return false; } if (!Objects.equals(this.positiveItemLabelPositionFallback, that.positiveItemLabelPositionFallback)) { return false; } if (!Objects.equals(this.negativeItemLabelPositionFallback, that.negativeItemLabelPositionFallback)) { return false; } if (!this.barPainter.equals(that.barPainter)) { return false; } if (this.shadowsVisible != that.shadowsVisible) { return false; } if (!PaintUtils.equal(this.shadowPaint, that.shadowPaint)) { return false; } if (this.shadowXOffset != that.shadowXOffset) { return false; } if (this.shadowYOffset != that.shadowYOffset) { return false; } return super.equals(obj); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.shadowPaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.shadowPaint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/category/BoxAndWhiskerRenderer.java000066400000000000000000001165211463604235500335510ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * BoxAndWhiskerRenderer.java * -------------------------- * (C) Copyright 2003-present, by David Browning and Contributors. * * Original Author: David Browning (for the Australian Institute of Marine * Science); * Contributor(s): David Gilbert; * Tim Bardzil; * Rob Van der Sanden (patches 1866446 and 1888422); * Peter Becker (patches 2868585 and 2868608); * Martin Krauskopf (patch 3421088); * Martin Hoeller; * John Matthews; * */ package org.jfree.chart.renderer.category; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Ellipse2D; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.jfree.chart.LegendItem; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.renderer.Outlier; import org.jfree.chart.renderer.OutlierList; import org.jfree.chart.renderer.OutlierListCollection; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; import org.jfree.data.Range; import org.jfree.data.category.CategoryDataset; import org.jfree.data.statistics.BoxAndWhiskerCategoryDataset; /** * A box-and-whisker renderer. This renderer requires a * {@link BoxAndWhiskerCategoryDataset} and is for use with the * {@link CategoryPlot} class. The example shown here is generated * by the {@code BoxAndWhiskerChartDemo1.java} program included in the * JFreeChart Demo Collection: *

* BoxAndWhiskerRendererSample.png */ public class BoxAndWhiskerRenderer extends AbstractCategoryItemRenderer implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 632027470694481177L; /** The color used to paint the median line and average marker. */ private transient Paint artifactPaint; /** A flag that controls whether or not the box is filled. */ private boolean fillBox; /** The margin between items (boxes) within a category. */ private double itemMargin; /** * The maximum bar width as percentage of the available space in the plot. * Take care with the encoding - for example, 0.05 is five percent. */ private double maximumBarWidth; /** * A flag that controls whether or not the median indicator is drawn. */ private boolean medianVisible; /** * A flag that controls whether or not the mean indicator is drawn. */ private boolean meanVisible; /** * A flag that controls whether or not the maxOutlier is visible. */ private boolean maxOutlierVisible; /** * A flag that controls whether or not the minOutlier is visible. */ private boolean minOutlierVisible; /** * A flag that, if {@code true}, causes the whiskers to be drawn * using the outline paint for the series. The default value is * {@code false} and in that case the regular series paint is used. */ private boolean useOutlinePaintForWhiskers; /** * The width of the whiskers as fraction of the bar width. */ private double whiskerWidth; /** * Default constructor. */ public BoxAndWhiskerRenderer() { this.artifactPaint = Color.BLACK; this.fillBox = true; this.itemMargin = 0.20; this.maximumBarWidth = 1.0; this.medianVisible = true; this.meanVisible = true; this.minOutlierVisible = true; this.maxOutlierVisible = true; this.useOutlinePaintForWhiskers = false; this.whiskerWidth = 1.0; setDefaultLegendShape(new Rectangle2D.Double(-4.0, -4.0, 8.0, 8.0)); } /** * Returns the paint used to color the median and average markers. * * @return The paint used to draw the median and average markers (never * {@code null}). * * @see #setArtifactPaint(Paint) */ public Paint getArtifactPaint() { return this.artifactPaint; } /** * Sets the paint used to color the median and average markers and sends * a {@link RendererChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getArtifactPaint() */ public void setArtifactPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.artifactPaint = paint; fireChangeEvent(); } /** * Returns the flag that controls whether or not the box is filled. * * @return A boolean. * * @see #setFillBox(boolean) */ public boolean getFillBox() { return this.fillBox; } /** * Sets the flag that controls whether or not the box is filled and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param flag the flag. * * @see #getFillBox() */ public void setFillBox(boolean flag) { this.fillBox = flag; fireChangeEvent(); } /** * Returns the item margin. This is a percentage of the available space * that is allocated to the space between items in the chart. * * @return The margin. * * @see #setItemMargin(double) */ public double getItemMargin() { return this.itemMargin; } /** * Sets the item margin and sends a {@link RendererChangeEvent} to all * registered listeners. * * @param margin the margin (a percentage). * * @see #getItemMargin() */ public void setItemMargin(double margin) { this.itemMargin = margin; fireChangeEvent(); } /** * Returns the maximum bar width as a percentage of the available drawing * space. Take care with the encoding, for example 0.10 is ten percent. * * @return The maximum bar width. * * @see #setMaximumBarWidth(double) */ public double getMaximumBarWidth() { return this.maximumBarWidth; } /** * Sets the maximum bar width, which is specified as a percentage of the * available space for all bars, and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param percent the maximum bar width (a percentage, where 0.10 is ten * percent). * * @see #getMaximumBarWidth() */ public void setMaximumBarWidth(double percent) { this.maximumBarWidth = percent; fireChangeEvent(); } /** * Returns the flag that controls whether or not the mean indicator is * draw for each item. * * @return A boolean. * * @see #setMeanVisible(boolean) */ public boolean isMeanVisible() { return this.meanVisible; } /** * Sets the flag that controls whether or not the mean indicator is drawn * for each item, and sends a {@link RendererChangeEvent} to all * registered listeners. * * @param visible the new flag value. * * @see #isMeanVisible() */ public void setMeanVisible(boolean visible) { if (this.meanVisible == visible) { return; } this.meanVisible = visible; fireChangeEvent(); } /** * Returns the flag that controls whether or not the median indicator is * draw for each item. * * @return A boolean. * * @see #setMedianVisible(boolean) */ public boolean isMedianVisible() { return this.medianVisible; } /** * Sets the flag that controls whether or not the median indicator is drawn * for each item, and sends a {@link RendererChangeEvent} to all * registered listeners. * * @param visible the new flag value. * * @see #isMedianVisible() */ public void setMedianVisible(boolean visible) { if (this.medianVisible == visible) { return; } this.medianVisible = visible; fireChangeEvent(); } /** * Returns the flag that controls whether or not the minimum outlier is * draw for each item. * * @return A boolean. * * @see #setMinOutlierVisible(boolean) * * @since 1.5.2 */ public boolean isMinOutlierVisible() { return this.minOutlierVisible; } /** * Sets the flag that controls whether or not the minimum outlier is drawn * for each item, and sends a {@link RendererChangeEvent} to all * registered listeners. * * @param visible the new flag value. * * @see #isMinOutlierVisible() * * @since 1.5.2 */ public void setMinOutlierVisible(boolean visible) { if (this.minOutlierVisible == visible) { return; } this.minOutlierVisible = visible; fireChangeEvent(); } /** * Returns the flag that controls whether or not the maximum outlier is * draw for each item. * * @return A boolean. * * @see #setMaxOutlierVisible(boolean) * * @since 1.5.2 */ public boolean isMaxOutlierVisible() { return this.maxOutlierVisible; } /** * Sets the flag that controls whether or not the maximum outlier is drawn * for each item, and sends a {@link RendererChangeEvent} to all * registered listeners. * * @param visible the new flag value. * * @see #isMaxOutlierVisible() * * @since 1.5.2 */ public void setMaxOutlierVisible(boolean visible) { if (this.maxOutlierVisible == visible) { return; } this.maxOutlierVisible = visible; fireChangeEvent(); } /** * Returns the flag that, if {@code true}, causes the whiskers to * be drawn using the series outline paint. * * @return A boolean. */ public boolean getUseOutlinePaintForWhiskers() { return this.useOutlinePaintForWhiskers; } /** * Sets the flag that, if {@code true}, causes the whiskers to * be drawn using the series outline paint, and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param flag the new flag value. */ public void setUseOutlinePaintForWhiskers(boolean flag) { if (this.useOutlinePaintForWhiskers == flag) { return; } this.useOutlinePaintForWhiskers = flag; fireChangeEvent(); } /** * Returns the width of the whiskers as fraction of the bar width. * * @return The width of the whiskers. * * @see #setWhiskerWidth(double) */ public double getWhiskerWidth() { return this.whiskerWidth; } /** * Sets the width of the whiskers as a fraction of the bar width and sends * a {@link RendererChangeEvent} to all registered listeners. * * @param width a value between 0 and 1 indicating how wide the * whisker is supposed to be compared to the bar. * @see #getWhiskerWidth() * @see CategoryItemRendererState#getBarWidth() */ public void setWhiskerWidth(double width) { if (width < 0 || width > 1) { throw new IllegalArgumentException( "Value for whisker width out of range"); } if (width == this.whiskerWidth) { return; } this.whiskerWidth = width; fireChangeEvent(); } /** * Returns a legend item for a series. * * @param datasetIndex the dataset index (zero-based). * @param series the series index (zero-based). * * @return The legend item (possibly {@code null}). */ @Override public LegendItem getLegendItem(int datasetIndex, int series) { CategoryPlot cp = getPlot(); if (cp == null) { return null; } // check that a legend item needs to be displayed... if (!isSeriesVisible(series) || !isSeriesVisibleInLegend(series)) { return null; } CategoryDataset dataset = cp.getDataset(datasetIndex); String label = getLegendItemLabelGenerator().generateLabel(dataset, series); String description = label; String toolTipText = null; if (getLegendItemToolTipGenerator() != null) { toolTipText = getLegendItemToolTipGenerator().generateLabel( dataset, series); } String urlText = null; if (getLegendItemURLGenerator() != null) { urlText = getLegendItemURLGenerator().generateLabel(dataset, series); } Shape shape = lookupLegendShape(series); Paint paint = lookupSeriesPaint(series); Paint outlinePaint = lookupSeriesOutlinePaint(series); Stroke outlineStroke = lookupSeriesOutlineStroke(series); LegendItem result = new LegendItem(label, description, toolTipText, urlText, shape, paint, outlineStroke, outlinePaint); result.setLabelFont(lookupLegendTextFont(series)); Paint labelPaint = lookupLegendTextPaint(series); if (labelPaint != null) { result.setLabelPaint(labelPaint); } result.setDataset(dataset); result.setDatasetIndex(datasetIndex); result.setSeriesKey(dataset.getRowKey(series)); result.setSeriesIndex(series); return result; } /** * Returns the range of values from the specified dataset that the * renderer will require to display all the data. * * @param dataset the dataset. * * @return The range. */ @Override public Range findRangeBounds(CategoryDataset dataset) { return super.findRangeBounds(dataset, true); } /** * Initialises the renderer. This method gets called once at the start of * the process of drawing a chart. * * @param g2 the graphics device. * @param dataArea the area in which the data is to be plotted. * @param plot the plot. * @param rendererIndex the renderer index. * @param info collects chart rendering information for return to caller. * * @return The renderer state. */ @Override public CategoryItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, CategoryPlot plot, int rendererIndex, PlotRenderingInfo info) { CategoryItemRendererState state = super.initialise(g2, dataArea, plot, rendererIndex, info); // calculate the box width CategoryAxis domainAxis = getDomainAxis(plot, rendererIndex); CategoryDataset dataset = plot.getDataset(rendererIndex); if (dataset != null) { int columns = dataset.getColumnCount(); int rows = dataset.getRowCount(); double space = 0.0; PlotOrientation orientation = plot.getOrientation(); if (orientation == PlotOrientation.HORIZONTAL) { space = dataArea.getHeight(); } else if (orientation == PlotOrientation.VERTICAL) { space = dataArea.getWidth(); } double maxWidth = space * getMaximumBarWidth(); double categoryMargin = 0.0; double currentItemMargin = 0.0; if (columns > 1) { categoryMargin = domainAxis.getCategoryMargin(); } if (rows > 1) { currentItemMargin = getItemMargin(); } double used = space * (1 - domainAxis.getLowerMargin() - domainAxis.getUpperMargin() - categoryMargin - currentItemMargin); if ((rows * columns) > 0) { state.setBarWidth(Math.min(used / (dataset.getColumnCount() * dataset.getRowCount()), maxWidth)); } else { state.setBarWidth(Math.min(used, maxWidth)); } } return state; } /** * Draw a single data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the area in which the data is drawn. * @param plot the plot. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the data (must be an instance of * {@link BoxAndWhiskerCategoryDataset}). * @param row the row index (zero-based). * @param column the column index (zero-based). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, ValueAxis rangeAxis, CategoryDataset dataset, int row, int column, int pass) { // do nothing if item is not visible if (!getItemVisible(row, column)) { return; } if (!(dataset instanceof BoxAndWhiskerCategoryDataset)) { throw new IllegalArgumentException( "BoxAndWhiskerRenderer.drawItem() : the data should be " + "of type BoxAndWhiskerCategoryDataset only."); } PlotOrientation orientation = plot.getOrientation(); if (orientation == PlotOrientation.HORIZONTAL) { drawHorizontalItem(g2, state, dataArea, plot, domainAxis, rangeAxis, dataset, row, column); } else if (orientation == PlotOrientation.VERTICAL) { drawVerticalItem(g2, state, dataArea, plot, domainAxis, rangeAxis, dataset, row, column); } } /** * Draws the visual representation of a single data item when the plot has * a horizontal orientation. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the area within which the plot is being drawn. * @param plot the plot (can be used to obtain standard color * information etc). * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset (must be an instance of * {@link BoxAndWhiskerCategoryDataset}). * @param row the row index (zero-based). * @param column the column index (zero-based). */ public void drawHorizontalItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, ValueAxis rangeAxis, CategoryDataset dataset, int row, int column) { BoxAndWhiskerCategoryDataset bawDataset = (BoxAndWhiskerCategoryDataset) dataset; double categoryEnd = domainAxis.getCategoryEnd(column, getColumnCount(), dataArea, plot.getDomainAxisEdge()); double categoryStart = domainAxis.getCategoryStart(column, getColumnCount(), dataArea, plot.getDomainAxisEdge()); double categoryWidth = Math.abs(categoryEnd - categoryStart); double yy = categoryStart; int seriesCount = getRowCount(); int categoryCount = getColumnCount(); if (seriesCount > 1) { double seriesGap = dataArea.getHeight() * getItemMargin() / (categoryCount * (seriesCount - 1)); double usedWidth = (state.getBarWidth() * seriesCount) + (seriesGap * (seriesCount - 1)); // offset the start of the boxes if the total width used is smaller // than the category width double offset = (categoryWidth - usedWidth) / 2; yy = yy + offset + (row * (state.getBarWidth() + seriesGap)); } else { // offset the start of the box if the box width is smaller than // the category width double offset = (categoryWidth - state.getBarWidth()) / 2; yy = yy + offset; } g2.setPaint(getItemPaint(row, column)); Stroke s = getItemStroke(row, column); g2.setStroke(s); RectangleEdge location = plot.getRangeAxisEdge(); Number xQ1 = bawDataset.getQ1Value(row, column); Number xQ3 = bawDataset.getQ3Value(row, column); Number xMax = bawDataset.getMaxRegularValue(row, column); Number xMin = bawDataset.getMinRegularValue(row, column); Shape box = null; if (xQ1 != null && xQ3 != null && xMax != null && xMin != null) { double xxQ1 = rangeAxis.valueToJava2D(xQ1.doubleValue(), dataArea, location); double xxQ3 = rangeAxis.valueToJava2D(xQ3.doubleValue(), dataArea, location); double xxMax = rangeAxis.valueToJava2D(xMax.doubleValue(), dataArea, location); double xxMin = rangeAxis.valueToJava2D(xMin.doubleValue(), dataArea, location); double yymid = yy + state.getBarWidth() / 2.0; double halfW = (state.getBarWidth() / 2.0) * this.whiskerWidth; // draw the box... box = new Rectangle2D.Double(Math.min(xxQ1, xxQ3), yy, Math.abs(xxQ1 - xxQ3), state.getBarWidth()); if (this.fillBox) { g2.fill(box); } Paint outlinePaint = getItemOutlinePaint(row, column); if (this.useOutlinePaintForWhiskers) { g2.setPaint(outlinePaint); } // draw the upper shadow... g2.draw(new Line2D.Double(xxMax, yymid, xxQ3, yymid)); g2.draw(new Line2D.Double(xxMax, yymid - halfW, xxMax, yymid + halfW)); // draw the lower shadow... g2.draw(new Line2D.Double(xxMin, yymid, xxQ1, yymid)); g2.draw(new Line2D.Double(xxMin, yymid - halfW, xxMin, yymid + halfW)); g2.setStroke(getItemOutlineStroke(row, column)); g2.setPaint(outlinePaint); g2.draw(box); } // draw mean - SPECIAL AIMS REQUIREMENT... g2.setPaint(this.artifactPaint); double aRadius; // average radius if (this.meanVisible) { Number xMean = bawDataset.getMeanValue(row, column); if (xMean != null) { double xxMean = rangeAxis.valueToJava2D(xMean.doubleValue(), dataArea, location); aRadius = state.getBarWidth() / 4; // here we check that the average marker will in fact be // visible before drawing it... if ((xxMean > (dataArea.getMinX() - aRadius)) && (xxMean < (dataArea.getMaxX() + aRadius))) { Ellipse2D.Double avgEllipse = new Ellipse2D.Double(xxMean - aRadius, yy + aRadius, aRadius * 2, aRadius * 2); g2.fill(avgEllipse); g2.draw(avgEllipse); } } } // draw median... if (this.medianVisible) { Number xMedian = bawDataset.getMedianValue(row, column); if (xMedian != null) { double xxMedian = rangeAxis.valueToJava2D(xMedian.doubleValue(), dataArea, location); g2.draw(new Line2D.Double(xxMedian, yy, xxMedian, yy + state.getBarWidth())); } } // collect entity and tool tip information... if (state.getInfo() != null && box != null) { EntityCollection entities = state.getEntityCollection(); if (entities != null) { addItemEntity(entities, dataset, row, column, box); } } } /** * Draws the visual representation of a single data item when the plot has * a vertical orientation. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the area within which the plot is being drawn. * @param plot the plot (can be used to obtain standard color information * etc). * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset (must be an instance of * {@link BoxAndWhiskerCategoryDataset}). * @param row the row index (zero-based). * @param column the column index (zero-based). */ public void drawVerticalItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, ValueAxis rangeAxis, CategoryDataset dataset, int row, int column) { BoxAndWhiskerCategoryDataset bawDataset = (BoxAndWhiskerCategoryDataset) dataset; double categoryEnd = domainAxis.getCategoryEnd(column, getColumnCount(), dataArea, plot.getDomainAxisEdge()); double categoryStart = domainAxis.getCategoryStart(column, getColumnCount(), dataArea, plot.getDomainAxisEdge()); double categoryWidth = categoryEnd - categoryStart; double xx = categoryStart; int seriesCount = getRowCount(); int categoryCount = getColumnCount(); if (seriesCount > 1) { double seriesGap = dataArea.getWidth() * getItemMargin() / (categoryCount * (seriesCount - 1)); double usedWidth = (state.getBarWidth() * seriesCount) + (seriesGap * (seriesCount - 1)); // offset the start of the boxes if the total width used is smaller // than the category width double offset = (categoryWidth - usedWidth) / 2; xx = xx + offset + (row * (state.getBarWidth() + seriesGap)); } else { // offset the start of the box if the box width is smaller than the // category width double offset = (categoryWidth - state.getBarWidth()) / 2; xx = xx + offset; } double yyAverage; double yyOutlier; Paint itemPaint = getItemPaint(row, column); g2.setPaint(itemPaint); Stroke s = getItemStroke(row, column); g2.setStroke(s); double aRadius = 0; // average radius RectangleEdge location = plot.getRangeAxisEdge(); Number yQ1 = bawDataset.getQ1Value(row, column); Number yQ3 = bawDataset.getQ3Value(row, column); Number yMax = bawDataset.getMaxRegularValue(row, column); Number yMin = bawDataset.getMinRegularValue(row, column); Shape box = null; if (yQ1 != null && yQ3 != null && yMax != null && yMin != null) { double yyQ1 = rangeAxis.valueToJava2D(yQ1.doubleValue(), dataArea, location); double yyQ3 = rangeAxis.valueToJava2D(yQ3.doubleValue(), dataArea, location); double yyMax = rangeAxis.valueToJava2D(yMax.doubleValue(), dataArea, location); double yyMin = rangeAxis.valueToJava2D(yMin.doubleValue(), dataArea, location); double xxmid = xx + state.getBarWidth() / 2.0; double halfW = (state.getBarWidth() / 2.0) * this.whiskerWidth; // draw the body... box = new Rectangle2D.Double(xx, Math.min(yyQ1, yyQ3), state.getBarWidth(), Math.abs(yyQ1 - yyQ3)); if (this.fillBox) { g2.fill(box); } Paint outlinePaint = getItemOutlinePaint(row, column); if (this.useOutlinePaintForWhiskers) { g2.setPaint(outlinePaint); } // draw the upper shadow... g2.draw(new Line2D.Double(xxmid, yyMax, xxmid, yyQ3)); g2.draw(new Line2D.Double(xxmid - halfW, yyMax, xxmid + halfW, yyMax)); // draw the lower shadow... g2.draw(new Line2D.Double(xxmid, yyMin, xxmid, yyQ1)); g2.draw(new Line2D.Double(xxmid - halfW, yyMin, xxmid + halfW, yyMin)); g2.setStroke(getItemOutlineStroke(row, column)); g2.setPaint(outlinePaint); g2.draw(box); } g2.setPaint(this.artifactPaint); // draw mean - SPECIAL AIMS REQUIREMENT... if (this.meanVisible) { Number yMean = bawDataset.getMeanValue(row, column); if (yMean != null) { yyAverage = rangeAxis.valueToJava2D(yMean.doubleValue(), dataArea, location); aRadius = state.getBarWidth() / 4; // here we check that the average marker will in fact be // visible before drawing it... if ((yyAverage > (dataArea.getMinY() - aRadius)) && (yyAverage < (dataArea.getMaxY() + aRadius))) { Ellipse2D.Double avgEllipse = new Ellipse2D.Double( xx + aRadius, yyAverage - aRadius, aRadius * 2, aRadius * 2); g2.fill(avgEllipse); g2.draw(avgEllipse); } } } // draw median... if (this.medianVisible) { Number yMedian = bawDataset.getMedianValue(row, column); if (yMedian != null) { double yyMedian = rangeAxis.valueToJava2D( yMedian.doubleValue(), dataArea, location); g2.draw(new Line2D.Double(xx, yyMedian, xx + state.getBarWidth(), yyMedian)); } } // draw yOutliers... double maxAxisValue = rangeAxis.valueToJava2D( rangeAxis.getUpperBound(), dataArea, location) + aRadius; double minAxisValue = rangeAxis.valueToJava2D( rangeAxis.getLowerBound(), dataArea, location) - aRadius; g2.setPaint(itemPaint); // draw outliers double oRadius = state.getBarWidth() / 3; // outlier radius List outliers = new ArrayList(); OutlierListCollection outlierListCollection = new OutlierListCollection(); // From outlier array sort out which are outliers and put these into a // list If there are any farouts, set the flag on the // OutlierListCollection List yOutliers = bawDataset.getOutliers(row, column); if (yOutliers != null) { for (int i = 0; i < yOutliers.size(); i++) { double outlier = ((Number) yOutliers.get(i)).doubleValue(); Number minOutlier = bawDataset.getMinOutlier(row, column); Number maxOutlier = bawDataset.getMaxOutlier(row, column); Number minRegular = bawDataset.getMinRegularValue(row, column); Number maxRegular = bawDataset.getMaxRegularValue(row, column); if (outlier > maxOutlier.doubleValue()) { outlierListCollection.setHighFarOut(true); } else if (outlier < minOutlier.doubleValue()) { outlierListCollection.setLowFarOut(true); } else if (outlier > maxRegular.doubleValue()) { yyOutlier = rangeAxis.valueToJava2D(outlier, dataArea, location); outliers.add(new Outlier(xx + state.getBarWidth() / 2.0, yyOutlier, oRadius)); } else if (outlier < minRegular.doubleValue()) { yyOutlier = rangeAxis.valueToJava2D(outlier, dataArea, location); outliers.add(new Outlier(xx + state.getBarWidth() / 2.0, yyOutlier, oRadius)); } Collections.sort(outliers); } // Process outliers. Each outlier is either added to the // appropriate outlier list or a new outlier list is made for (Iterator iterator = outliers.iterator(); iterator.hasNext();) { Outlier outlier = (Outlier) iterator.next(); outlierListCollection.add(outlier); } for (Iterator iterator = outlierListCollection.iterator(); iterator.hasNext();) { OutlierList list = (OutlierList) iterator.next(); Outlier outlier = list.getAveragedOutlier(); Point2D point = outlier.getPoint(); if (list.isMultiple()) { drawMultipleEllipse(point, state.getBarWidth(), oRadius, g2); } else { drawEllipse(point, oRadius, g2); } } // draw farout indicators if (isMaxOutlierVisible() && outlierListCollection.isHighFarOut()) { drawHighFarOut(aRadius / 2.0, g2, xx + state.getBarWidth() / 2.0, maxAxisValue); } if (isMinOutlierVisible() && outlierListCollection.isLowFarOut()) { drawLowFarOut(aRadius / 2.0, g2, xx + state.getBarWidth() / 2.0, minAxisValue); } } // collect entity and tool tip information... if (state.getInfo() != null && box != null) { EntityCollection entities = state.getEntityCollection(); if (entities != null) { addItemEntity(entities, dataset, row, column, box); } } } /** * Draws a dot to represent an outlier. * * @param point the location. * @param oRadius the radius. * @param g2 the graphics device. */ private void drawEllipse(Point2D point, double oRadius, Graphics2D g2) { Ellipse2D dot = new Ellipse2D.Double(point.getX() + oRadius / 2, point.getY(), oRadius, oRadius); g2.draw(dot); } /** * Draws two dots to represent the average value of more than one outlier. * * @param point the location * @param boxWidth the box width. * @param oRadius the radius. * @param g2 the graphics device. */ private void drawMultipleEllipse(Point2D point, double boxWidth, double oRadius, Graphics2D g2) { Ellipse2D dot1 = new Ellipse2D.Double(point.getX() - (boxWidth / 2) + oRadius, point.getY(), oRadius, oRadius); Ellipse2D dot2 = new Ellipse2D.Double(point.getX() + (boxWidth / 2), point.getY(), oRadius, oRadius); g2.draw(dot1); g2.draw(dot2); } /** * Draws a triangle to indicate the presence of far-out values. * * @param aRadius the radius. * @param g2 the graphics device. * @param xx the x coordinate. * @param m the y coordinate. */ private void drawHighFarOut(double aRadius, Graphics2D g2, double xx, double m) { double side = aRadius * 2; g2.draw(new Line2D.Double(xx - side, m + side, xx + side, m + side)); g2.draw(new Line2D.Double(xx - side, m + side, xx, m)); g2.draw(new Line2D.Double(xx + side, m + side, xx, m)); } /** * Draws a triangle to indicate the presence of far-out values. * * @param aRadius the radius. * @param g2 the graphics device. * @param xx the x coordinate. * @param m the y coordinate. */ private void drawLowFarOut(double aRadius, Graphics2D g2, double xx, double m) { double side = aRadius * 2; g2.draw(new Line2D.Double(xx - side, m - side, xx + side, m - side)); g2.draw(new Line2D.Double(xx - side, m - side, xx, m)); g2.draw(new Line2D.Double(xx + side, m - side, xx, m)); } /** * Tests this renderer for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof BoxAndWhiskerRenderer)) { return false; } BoxAndWhiskerRenderer that = (BoxAndWhiskerRenderer) obj; if (this.fillBox != that.fillBox) { return false; } if (this.itemMargin != that.itemMargin) { return false; } if (this.maximumBarWidth != that.maximumBarWidth) { return false; } if (this.meanVisible != that.meanVisible) { return false; } if (this.medianVisible != that.medianVisible) { return false; } if (this.minOutlierVisible != that.minOutlierVisible) { return false; } if (this.maxOutlierVisible != that.maxOutlierVisible) { return false; } if (this.useOutlinePaintForWhiskers != that.useOutlinePaintForWhiskers) { return false; } if (this.whiskerWidth != that.whiskerWidth) { return false; } if (!PaintUtils.equal(this.artifactPaint, that.artifactPaint)) { return false; } return super.equals(obj); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.artifactPaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.artifactPaint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/category/CategoryItemRenderer.java000066400000000000000000001532101463604235500334310ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * CategoryItemRenderer.java * ------------------------- * * (C) Copyright 2001-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Mark Watson (www.markwatson.com); * */ package org.jfree.chart.renderer.category; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Rectangle2D; import org.jfree.chart.LegendItem; import org.jfree.chart.LegendItemSource; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.event.RendererChangeListener; import org.jfree.chart.labels.CategoryItemLabelGenerator; import org.jfree.chart.labels.CategoryToolTipGenerator; import org.jfree.chart.labels.ItemLabelPosition; import org.jfree.chart.plot.CategoryMarker; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.Marker; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.urls.CategoryURLGenerator; import org.jfree.data.Range; import org.jfree.data.category.CategoryDataset; /** * A plug-in object that is used by the {@link CategoryPlot} class to display * individual data items from a {@link CategoryDataset}. *

* This interface defines the methods that must be provided by all renderers. * If you are implementing a custom renderer, you should consider extending the * {@link AbstractCategoryItemRenderer} class. *

* Most renderer attributes are defined using a two layer approach. When * looking up an attribute (for example, the outline paint) the renderer first * checks to see if there is a setting that applies to a specific series * that the renderer draws. If there is, that setting is used, but if it is * {@code null} the renderer looks up the default setting. Some attributes * allow the base setting to be {@code null}, while other attributes enforce * non-{@code null} values. */ public interface CategoryItemRenderer extends LegendItemSource { /** * Returns the number of passes through the dataset required by the * renderer. Usually this will be one, but some renderers may use * a second or third pass to overlay items on top of things that were * drawn in an earlier pass. * * @return The pass count. */ int getPassCount(); /** * Returns the plot that the renderer has been assigned to (where * {@code null} indicates that the renderer is not currently assigned * to a plot). * * @return The plot (possibly {@code null}). * * @see #setPlot(CategoryPlot) */ CategoryPlot getPlot(); /** * Sets the plot that the renderer has been assigned to. This method is * usually called by the {@link CategoryPlot}, in normal usage you * shouldn't need to call this method directly. * * @param plot the plot ({@code null} not permitted). * * @see #getPlot() */ void setPlot(CategoryPlot plot); /** * Adds a change listener. * * @param listener the listener. * * @see #removeChangeListener(RendererChangeListener) */ void addChangeListener(RendererChangeListener listener); /** * Removes a change listener. * * @param listener the listener. * * @see #addChangeListener(RendererChangeListener) */ void removeChangeListener(RendererChangeListener listener); /** * Returns the range of values the renderer requires to display all the * items from the specified dataset. * * @param dataset the dataset ({@code null} permitted). * * @return The range (or {@code null} if the dataset is * {@code null} or empty). */ Range findRangeBounds(CategoryDataset dataset); /** * Initialises the renderer. This method will be called before the first * item is rendered, giving the renderer an opportunity to initialise any * state information it wants to maintain. The renderer can do nothing if * it chooses. * * @param g2 the graphics device. * @param dataArea the area inside the axes. * @param plot the plot. * @param rendererIndex the renderer index. * @param info collects chart rendering information for return to caller. * * @return A state object (maintains state information relevant to one * chart drawing). */ CategoryItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, CategoryPlot plot, int rendererIndex, PlotRenderingInfo info); /** * Returns a boolean that indicates whether or not the specified item * should be drawn (this is typically used to hide an entire series). * * @param series the series index. * @param item the item index. * * @return A boolean. */ boolean getItemVisible(int series, int item); /** * Returns a boolean that indicates whether or not the specified series * should be drawn (this is typically used to hide an entire series). * * @param series the series index. * * @return A boolean. */ boolean isSeriesVisible(int series); /** * Returns the flag that controls whether a series is visible. * * @param series the series index (zero-based). * * @return The flag (possibly {@code null}). * * @see #setSeriesVisible(int, Boolean) */ Boolean getSeriesVisible(int series); /** * Sets the flag that controls whether a series is visible and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param visible the flag ({@code null} permitted). * * @see #getSeriesVisible(int) */ void setSeriesVisible(int series, Boolean visible); /** * Sets the flag that controls whether a series is visible and, if * requested, sends a {@link RendererChangeEvent} to all registered * listeners. * * @param series the series index. * @param visible the flag ({@code null} permitted). * @param notify notify listeners? * * @see #getSeriesVisible(int) */ void setSeriesVisible(int series, Boolean visible, boolean notify); /** * Returns the default visibility for all series. * * @return The default visibility. * * @see #setDefaultSeriesVisible(boolean) */ boolean getDefaultSeriesVisible(); /** * Sets the default visibility and sends a {@link RendererChangeEvent} to all * registered listeners. * * @param visible the flag. * * @see #getDefaultSeriesVisible() */ void setDefaultSeriesVisible(boolean visible); /** * Sets the default visibility and, if requested, sends * a {@link RendererChangeEvent} to all registered listeners. * * @param visible the visibility. * @param notify notify listeners? * * @see #getDefaultSeriesVisible() */ void setDefaultSeriesVisible(boolean visible, boolean notify); // SERIES VISIBLE IN LEGEND (not yet respected by all renderers) /** * Returns {@code true} if the series should be shown in the legend, * and {@code false} otherwise. * * @param series the series index. * * @return A boolean. */ boolean isSeriesVisibleInLegend(int series); /** * Returns the flag that controls whether a series is visible in the * legend. This method returns only the "per series" settings - to * incorporate the override and base settings as well, you need to use the * {@link #isSeriesVisibleInLegend(int)} method. * * @param series the series index (zero-based). * * @return The flag (possibly {@code null}). * * @see #setSeriesVisibleInLegend(int, Boolean) */ Boolean getSeriesVisibleInLegend(int series); /** * Sets the flag that controls whether a series is visible in the legend * and sends a {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param visible the flag ({@code null} permitted). * * @see #getSeriesVisibleInLegend(int) */ void setSeriesVisibleInLegend(int series, Boolean visible); /** * Sets the flag that controls whether a series is visible in the legend * and, if requested, sends a {@link RendererChangeEvent} to all registered * listeners. * * @param series the series index. * @param visible the flag ({@code null} permitted). * @param notify notify listeners? * * @see #getSeriesVisibleInLegend(int) */ void setSeriesVisibleInLegend(int series, Boolean visible, boolean notify); /** * Returns the default visibility in the legend for all series. * * @return The default visibility. * * @see #setDefaultSeriesVisibleInLegend(boolean) */ boolean getDefaultSeriesVisibleInLegend(); /** * Sets the default visibility in the legend and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param visible the flag. * * @see #getDefaultSeriesVisibleInLegend() */ void setDefaultSeriesVisibleInLegend(boolean visible); /** * Sets the default visibility in the legend and, if requested, sends * a {@link RendererChangeEvent} to all registered listeners. * * @param visible the visibility. * @param notify notify listeners? * * @see #getDefaultSeriesVisibleInLegend() */ void setDefaultSeriesVisibleInLegend(boolean visible, boolean notify); //// PAINT ///////////////////////////////////////////////////////////////// /** * Returns the paint used to fill data items as they are drawn. * * @param row the row (or series) index (zero-based). * @param column the column (or category) index (zero-based). * * @return The paint (never {@code null}). */ Paint getItemPaint(int row, int column); /** * Returns the paint used to fill an item drawn by the renderer. * * @param series the series index (zero-based). * * @return The paint (possibly {@code null}). * * @see #setSeriesPaint(int, Paint) */ Paint getSeriesPaint(int series); /** * Sets the paint used for a series and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param series the series index (zero-based). * @param paint the paint ({@code null} permitted). * * @see #getSeriesPaint(int) */ void setSeriesPaint(int series, Paint paint); /** * Sets the paint used for a series and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param paint the paint ({@code null} permitted). * @param notify notify listeners? */ void setSeriesPaint(int series, Paint paint, boolean notify); /** * Returns the default paint. During rendering, a renderer will first look * up the series paint and, if this is {@code null}, it will use the * default paint. * * @return The default paint (never {@code null}). * * @see #setDefaultPaint(Paint) */ Paint getDefaultPaint(); /** * Sets the default paint and sends a {@link RendererChangeEvent} to all * registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getDefaultPaint() */ void setDefaultPaint(Paint paint); /** * Sets the default paint and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * @param notify notify listeners? * * @see #getDefaultPaint() */ void setDefaultPaint(Paint paint, boolean notify); //// FILL PAINT ///////////////////////////////////////////////////////// /** * Returns the paint used to fill data items as they are drawn. * * @param row the row (or series) index (zero-based). * @param column the column (or category) index (zero-based). * * @return The paint (never {@code null}). */ Paint getItemFillPaint(int row, int column); /** * Returns the paint used to fill an item drawn by the renderer. * * @param series the series (zero-based index). * * @return The paint (possibly {@code null}). * * @see #setSeriesFillPaint(int, Paint) */ Paint getSeriesFillPaint(int series); /** * Sets the paint used for a series outline and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param paint the paint ({@code null} permitted). * * @see #getSeriesFillPaint(int) */ void setSeriesFillPaint(int series, Paint paint); /** * Returns the default outline paint. * * @return The paint (never {@code null}). * * @see #setDefaultFillPaint(Paint) */ Paint getDefaultFillPaint(); /** * Sets the default outline paint and sends a {@link RendererChangeEvent} to * all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getDefaultFillPaint() */ void setDefaultFillPaint(Paint paint); //// OUTLINE PAINT ///////////////////////////////////////////////////////// /** * Returns the paint used to outline data items as they are drawn. * * @param row the row (or series) index (zero-based). * @param column the column (or category) index (zero-based). * * @return The paint (never {@code null}). */ Paint getItemOutlinePaint(int row, int column); /** * Returns the paint used to outline an item drawn by the renderer. * * @param series the series (zero-based index). * * @return The paint (possibly {@code null}). * * @see #setSeriesOutlinePaint(int, Paint) */ Paint getSeriesOutlinePaint(int series); /** * Sets the paint used for a series outline and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param paint the paint ({@code null} permitted). * * @see #getSeriesOutlinePaint(int) */ void setSeriesOutlinePaint(int series, Paint paint); /** * Sets the paint used for a series outline and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param paint the paint ({@code null} permitted). * @param notify notify listeners? * * @see #getSeriesOutlinePaint(int) */ void setSeriesOutlinePaint(int series, Paint paint, boolean notify); /** * Returns the default outline paint. During rendering, the renderer * will look up the series outline paint and, if this is {@code null}, it * will use the default outline paint. * * @return The paint (never {@code null}). * * @see #setDefaultOutlinePaint(Paint) */ Paint getDefaultOutlinePaint(); /** * Sets the default outline paint and sends a {@link RendererChangeEvent} to * all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getDefaultOutlinePaint() */ void setDefaultOutlinePaint(Paint paint); /** * Sets the default outline paint and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * @param notify notify listeners? * * @see #getDefaultOutlinePaint() */ void setDefaultOutlinePaint(Paint paint, boolean notify); //// STROKE //////////////////////////////////////////////////////////////// /** * Returns the stroke used to draw data items. * * @param row the row (or series) index (zero-based). * @param column the column (or category) index (zero-based). * * @return The stroke (never {@code null}). */ Stroke getItemStroke(int row, int column); /** * Returns the stroke used to draw the items in a series. * * @param series the series (zero-based index). * * @return The stroke (never {@code null}). * * @see #setSeriesStroke(int, Stroke) */ Stroke getSeriesStroke(int series); /** * Sets the stroke used for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param stroke the stroke ({@code null} permitted). * * @see #getSeriesStroke(int) */ void setSeriesStroke(int series, Stroke stroke); /** * Sets the stroke used for a series and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param stroke the stroke ({@code null} permitted). * @param notify notify listeners? * * @see #getSeriesStroke(int) */ void setSeriesStroke(int series, Stroke stroke, boolean notify); /** * Returns the default stroke. * * @return The default stroke (never {@code null}). * * @see #setDefaultStroke(Stroke) */ Stroke getDefaultStroke(); /** * Sets the default stroke and sends a {@link RendererChangeEvent} to all * registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getDefaultStroke() */ void setDefaultStroke(Stroke stroke); /** * Sets the default stroke and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * @param notify notify listeners? * * @see #getDefaultStroke() */ void setDefaultStroke(Stroke stroke, boolean notify); //// OUTLINE STROKE //////////////////////////////////////////////////////// /** * Returns the stroke used to outline data items. *

* The default implementation passes control to the * lookupSeriesOutlineStroke method. You can override this method if you * require different behaviour. * * @param row the row (or series) index (zero-based). * @param column the column (or category) index (zero-based). * * @return The stroke (never {@code null}). */ Stroke getItemOutlineStroke(int row, int column); /** * Returns the stroke used to outline the items in a series. * * @param series the series (zero-based index). * * @return The stroke (possibly {@code null}). * * @see #setSeriesOutlineStroke(int, Stroke) */ Stroke getSeriesOutlineStroke(int series); /** * Sets the outline stroke used for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param stroke the stroke ({@code null} permitted). * * @see #getSeriesOutlineStroke(int) */ void setSeriesOutlineStroke(int series, Stroke stroke); /** * Sets the outline stroke used for a series and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param stroke the stroke ({@code null} permitted). * @param notify notify listeners? * * @see #getSeriesOutlineStroke(int) */ void setSeriesOutlineStroke(int series, Stroke stroke, boolean notify); /** * Returns the default outline stroke. * * @return The stroke (never {@code null}). * * @see #setDefaultOutlineStroke(Stroke) */ Stroke getDefaultOutlineStroke(); /** * Sets the default outline stroke and sends a {@link RendererChangeEvent} to * all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getDefaultOutlineStroke() */ void setDefaultOutlineStroke(Stroke stroke); /** * Sets the default outline stroke and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * @param notify notify listeners? * * @see #getDefaultOutlineStroke() */ void setDefaultOutlineStroke(Stroke stroke, boolean notify); //// SHAPE ///////////////////////////////////////////////////////////////// /** * Returns a shape used to represent a data item. * * @param row the row (or series) index (zero-based). * @param column the column (or category) index (zero-based). * * @return The shape (never {@code null}). */ Shape getItemShape(int row, int column); /** * Returns a shape used to represent the items in a series. * * @param series the series (zero-based index). * * @return The shape (possibly {@code null}). * * @see #setSeriesShape(int, Shape) */ Shape getSeriesShape(int series); /** * Sets the shape used for a series and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param series the series index (zero-based). * @param shape the shape ({@code null} permitted). * * @see #getSeriesShape(int) */ void setSeriesShape(int series, Shape shape); /** * Sets the shape used for a series and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param shape the shape ({@code null} permitted). * @param notify notify listeners? * * @see #getSeriesShape(int) */ void setSeriesShape(int series, Shape shape, boolean notify); /** * Returns the default shape. * * @return The shape (never {@code null}). * * @see #setDefaultShape(Shape) */ Shape getDefaultShape(); /** * Sets the default shape and sends a {@link RendererChangeEvent} to all * registered listeners. * * @param shape the shape ({@code null} not permitted). * * @see #getDefaultShape() */ void setDefaultShape(Shape shape); /** * Sets the default shape and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param shape the shape ({@code null} not permitted). * @param notify notify listeners? * * @see #getDefaultShape() */ void setDefaultShape(Shape shape, boolean notify); // ITEM LABELS VISIBLE /** * Returns {@code true} if an item label is visible, and * {@code false} otherwise. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return A boolean. */ boolean isItemLabelVisible(int row, int column); /** * Returns {@code true} if the item labels for a series are visible, * and {@code false} otherwise. * * @param series the series index (zero-based). * * @return A boolean. * * @see #setSeriesItemLabelsVisible(int, Boolean) */ boolean isSeriesItemLabelsVisible(int series); /** * Sets a flag that controls the visibility of the item labels for a series. * * @param series the series index (zero-based). * @param visible the flag. * * @see #isSeriesItemLabelsVisible(int) */ void setSeriesItemLabelsVisible(int series, boolean visible); /** * Sets a flag that controls the visibility of the item labels for a series. * * @param series the series index (zero-based). * @param visible the flag ({@code null} permitted). * * @see #isSeriesItemLabelsVisible(int) */ void setSeriesItemLabelsVisible(int series, Boolean visible); /** * Sets the visibility of item labels for a series and, if requested, sends * a {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param visible the visible flag. * @param notify a flag that controls whether or not listeners are * notified. * * @see #isSeriesItemLabelsVisible(int) */ void setSeriesItemLabelsVisible(int series, Boolean visible, boolean notify); /** * Returns the default setting for item label visibility. A {@code null} * result should be interpreted as equivalent to {@code Boolean.FALSE} * (this is an error in the API design, the return value should have been * a boolean primitive). * * @return A flag (possibly {@code null}). * * @see #setDefaultItemLabelsVisible(boolean) */ boolean getDefaultItemLabelsVisible(); /** * Sets the default flag that controls whether or not item labels are visible * and sends a {@link RendererChangeEvent} to all registered listeners. * * @param visible the flag. * * @see #getDefaultItemLabelsVisible() */ void setDefaultItemLabelsVisible(boolean visible); /** * Sets the default visibility for item labels and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param visible the visibility flag. * @param notify a flag that controls whether or not listeners are * notified. * * @see #getDefaultItemLabelsVisible() */ void setDefaultItemLabelsVisible(boolean visible, boolean notify); // ITEM LABEL GENERATOR /** * Returns the item label generator for the specified data item. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The generator (possibly {@code null}). */ CategoryItemLabelGenerator getItemLabelGenerator(int series, int item); /** * Returns the item label generator for a series. * * @param series the series index (zero-based). * * @return The label generator (possibly {@code null}). * * @see #setSeriesItemLabelGenerator(int, CategoryItemLabelGenerator) */ CategoryItemLabelGenerator getSeriesItemLabelGenerator(int series); /** * Sets the item label generator for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param generator the generator. * * @see #getSeriesItemLabelGenerator(int) */ void setSeriesItemLabelGenerator(int series, CategoryItemLabelGenerator generator); /** * Sets the item label generator for a series and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param generator the generator. * @param notify notify listeners? * * @see #getSeriesItemLabelGenerator(int) */ void setSeriesItemLabelGenerator(int series, CategoryItemLabelGenerator generator, boolean notify); /** * Returns the default item label generator. * * @return The generator (possibly {@code null}). * * @see #setDefaultItemLabelGenerator(CategoryItemLabelGenerator) */ CategoryItemLabelGenerator getDefaultItemLabelGenerator(); /** * Sets the default item label generator and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param generator the generator ({@code null} permitted). * * @see #getDefaultItemLabelGenerator() */ void setDefaultItemLabelGenerator(CategoryItemLabelGenerator generator); /** * Sets the default item label generator and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param generator the generator ({@code null} permitted). * @param notify notify listeners? * * @see #getDefaultItemLabelGenerator() */ void setDefaultItemLabelGenerator(CategoryItemLabelGenerator generator, boolean notify); // TOOL TIP GENERATOR /** * Returns the tool tip generator that should be used for the specified * item. This method looks up the generator using the "three-layer" * approach outlined in the general description of this interface. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The generator (possibly {@code null}). */ CategoryToolTipGenerator getToolTipGenerator(int row, int column); /** * Returns the tool tip generator for the specified series (a "layer 1" * generator). * * @param series the series index (zero-based). * * @return The tool tip generator (possibly {@code null}). * * @see #setSeriesToolTipGenerator(int, CategoryToolTipGenerator) */ CategoryToolTipGenerator getSeriesToolTipGenerator(int series); /** * Sets the tool tip generator for a series and sends a * {@link org.jfree.chart.event.RendererChangeEvent} to all registered * listeners. * * @param series the series index (zero-based). * @param generator the generator ({@code null} permitted). * * @see #getSeriesToolTipGenerator(int) */ void setSeriesToolTipGenerator(int series, CategoryToolTipGenerator generator); /** * Sets the tool tip generator for a series and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param generator the generator ({@code null} permitted). * @param notify notify listeners? * * @see #getSeriesToolTipGenerator(int) */ void setSeriesToolTipGenerator(int series, CategoryToolTipGenerator generator, boolean notify); /** * Returns the default tool tip generator (the "layer 2" generator). * * @return The tool tip generator (possibly {@code null}). * * @see #setDefaultToolTipGenerator(CategoryToolTipGenerator) */ CategoryToolTipGenerator getDefaultToolTipGenerator(); /** * Sets the default tool tip generator and sends a * {@link org.jfree.chart.event.RendererChangeEvent} to all registered * listeners. * * @param generator the generator ({@code null} permitted). * * @see #getDefaultToolTipGenerator() */ void setDefaultToolTipGenerator(CategoryToolTipGenerator generator); /** * Sets the default tool tip generator and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param generator the generator ({@code null} permitted). * @param notify notify listeners? * * @see #getDefaultToolTipGenerator() */ void setDefaultToolTipGenerator(CategoryToolTipGenerator generator, boolean notify); //// ITEM LABEL FONT ////////////////////////////////////////////////////// /** * Returns the font for an item label. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The font (never {@code null}). */ Font getItemLabelFont(int row, int column); /** * Returns the font for all the item labels in a series. * * @param series the series index (zero-based). * * @return The font (possibly {@code null}). * * @see #setSeriesItemLabelFont(int, Font) */ Font getSeriesItemLabelFont(int series); /** * Sets the item label font for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param font the font ({@code null} permitted). * * @see #getSeriesItemLabelFont(int) */ void setSeriesItemLabelFont(int series, Font font); /** * Sets the item label font for a series and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param font the font ({@code null} permitted). * @param notify notify listeners? * * @see #getSeriesItemLabelFont(int) */ void setSeriesItemLabelFont(int series, Font font, boolean notify); /** * Returns the default item label font (this is used when no other font * setting is available). * * @return The font (never {@code null}). * * @see #setDefaultItemLabelFont(Font) */ Font getDefaultItemLabelFont(); /** * Sets the default item label font and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param font the font ({@code null} not permitted). * * @see #getDefaultItemLabelFont() */ void setDefaultItemLabelFont(Font font); /** * Sets the default item label font and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param font the font ({@code null} not permitted). * @param notify notify listeners? * * @see #getDefaultItemLabelFont() */ void setDefaultItemLabelFont(Font font, boolean notify); //// ITEM LABEL PAINT ///////////////////////////////////////////////////// /** * Returns the paint used to draw an item label. * * @param row the row index (zero based). * @param column the column index (zero based). * * @return The paint (never {@code null}). */ Paint getItemLabelPaint(int row, int column); /** * Returns the paint used to draw the item labels for a series. * * @param series the series index (zero based). * * @return The paint (possibly {@code null}). * * @see #setSeriesItemLabelPaint(int, Paint) */ Paint getSeriesItemLabelPaint(int series); /** * Sets the item label paint for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series (zero based index). * @param paint the paint ({@code null} permitted). * * @see #getSeriesItemLabelPaint(int) */ void setSeriesItemLabelPaint(int series, Paint paint); /** * Sets the item label paint for a series and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series (zero based index). * @param paint the paint ({@code null} permitted). * @param notify notify listeners? * * @see #getSeriesItemLabelPaint(int) */ void setSeriesItemLabelPaint(int series, Paint paint, boolean notify); /** * Returns the default item label paint. * * @return The paint (never {@code null}). * * @see #setDefaultItemLabelPaint(Paint) */ Paint getDefaultItemLabelPaint(); /** * Sets the default item label paint and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getDefaultItemLabelPaint() */ void setDefaultItemLabelPaint(Paint paint); /** * Sets the default item label paint and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * @param notify notify listeners? * * @see #getDefaultItemLabelPaint() */ void setDefaultItemLabelPaint(Paint paint, boolean notify); // POSITIVE ITEM LABEL POSITION... /** * Returns the item label position for positive values. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The item label position (never {@code null}). */ ItemLabelPosition getPositiveItemLabelPosition(int row, int column); /** * Returns the item label position for all positive values in a series. * * @param series the series index (zero-based). * * @return The item label position. * * @see #setSeriesPositiveItemLabelPosition(int, ItemLabelPosition) */ ItemLabelPosition getSeriesPositiveItemLabelPosition(int series); /** * Sets the item label position for all positive values in a series and * sends a {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param position the position ({@code null} permitted). * * @see #getSeriesPositiveItemLabelPosition(int) */ void setSeriesPositiveItemLabelPosition(int series, ItemLabelPosition position); /** * Sets the item label position for all positive values in a series and (if * requested) sends a {@link RendererChangeEvent} to all registered * listeners. * * @param series the series index (zero-based). * @param position the position ({@code null} permitted). * @param notify notify registered listeners? * * @see #getSeriesPositiveItemLabelPosition(int) */ void setSeriesPositiveItemLabelPosition(int series, ItemLabelPosition position, boolean notify); /** * Returns the default positive item label position. * * @return The position. * * @see #setDefaultPositiveItemLabelPosition(ItemLabelPosition) */ ItemLabelPosition getDefaultPositiveItemLabelPosition(); /** * Sets the default positive item label position. * * @param position the position. * * @see #getDefaultPositiveItemLabelPosition() */ void setDefaultPositiveItemLabelPosition(ItemLabelPosition position); /** * Sets the default positive item label position and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param position the position. * @param notify notify registered listeners? * * @see #getDefaultPositiveItemLabelPosition() */ void setDefaultPositiveItemLabelPosition(ItemLabelPosition position, boolean notify); // NEGATIVE ITEM LABEL POSITION... /** * Returns the item label position for negative values. This method can be * overridden to provide customisation of the item label position for * individual data items. * * @param row the row index (zero-based). * @param column the column (zero-based). * * @return The item label position. */ ItemLabelPosition getNegativeItemLabelPosition(int row, int column); /** * Returns the item label position for all negative values in a series. * * @param series the series index (zero-based). * * @return The item label position. * * @see #setSeriesNegativeItemLabelPosition(int, ItemLabelPosition) */ ItemLabelPosition getSeriesNegativeItemLabelPosition(int series); /** * Sets the item label position for negative values in a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param position the position ({@code null} permitted). * * @see #getSeriesNegativeItemLabelPosition(int) */ void setSeriesNegativeItemLabelPosition(int series, ItemLabelPosition position); /** * Sets the item label position for negative values in a series and (if * requested) sends a {@link RendererChangeEvent} to all registered * listeners. * * @param series the series index (zero-based). * @param position the position ({@code null} permitted). * @param notify notify registered listeners? * * @see #getSeriesNegativeItemLabelPosition(int) */ void setSeriesNegativeItemLabelPosition(int series, ItemLabelPosition position, boolean notify); /** * Returns the default item label position for negative values. * * @return The position. * * @see #setDefaultNegativeItemLabelPosition(ItemLabelPosition) */ ItemLabelPosition getDefaultNegativeItemLabelPosition(); /** * Sets the default item label position for negative values and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param position the position. * * @see #getDefaultNegativeItemLabelPosition() */ void setDefaultNegativeItemLabelPosition(ItemLabelPosition position); /** * Sets the default negative item label position and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param position the position. * @param notify notify registered listeners? * * @see #getDefaultNegativeItemLabelPosition() */ void setDefaultNegativeItemLabelPosition(ItemLabelPosition position, boolean notify); // CREATE ENTITIES /** * Returns a flag that determines whether or not an entity is generated * for the specified item. The standard implementation of this method * will typically return the flag for the series or, if that is * {@code null}, the value returned by {@link #getDefaultCreateEntities()}. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return A boolean. */ boolean getItemCreateEntity(int series, int item); /** * Returns a boolean indicating whether or not entities should be created * for the items in a series. * * @param series the series index (zero-based). * * @return The flag for the series (possibly {@code null}). */ Boolean getSeriesCreateEntities(int series); /** * Sets a flag that indicates whether or not entities should be created during * rendering for the items in the specified series, and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param create the new flag value ({@code null} permitted). */ void setSeriesCreateEntities(int series, Boolean create); /** * Sets a flag that indicates whether or not entities should be created during * rendering for the items in the specified series, and sends a * {@link RendererChangeEvent} to all registered listeners if requested. * * @param series the series index (zero-based). * @param create the new flag value ({@code null} permitted). * @param notify notify listeners? */ void setSeriesCreateEntities(int series, Boolean create, boolean notify); /** * Returns the default value for the flag that controls whether or not * an entity is created for an item during rendering. * * @return A boolean. */ boolean getDefaultCreateEntities(); /** * Sets the default value for the flag that controls whether or not an * entity is created for an item during rendering and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param create the new flag value. */ void setDefaultCreateEntities(boolean create); /** * Sets the default value for the flag that controls whether or not an * entity is created for an item during rendering and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param create the new flag value. * @param notify notify listeners? */ void setDefaultCreateEntities(boolean create, boolean notify); // ITEM URL GENERATOR /** * Returns the URL generator for an item. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The item URL generator. */ CategoryURLGenerator getItemURLGenerator(int series, int item); /** * Returns the item URL generator for a series. * * @param series the series index (zero-based). * * @return The URL generator. * * @see #setSeriesItemURLGenerator(int, CategoryURLGenerator) */ CategoryURLGenerator getSeriesItemURLGenerator(int series); /** * Sets the item URL generator for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param generator the generator ({@code null} permitted). * * @see #getSeriesItemURLGenerator(int) */ void setSeriesItemURLGenerator(int series, CategoryURLGenerator generator); /** * Sets the item URL generator for a series and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param generator the generator ({@code null} permitted). * @param notify notify listeners? * * @see #getSeriesItemURLGenerator(int) */ void setSeriesItemURLGenerator(int series, CategoryURLGenerator generator, boolean notify); /** * Returns the default item URL generator. * * @return The item URL generator (possibly {@code null}). * * @see #setDefaultItemURLGenerator(CategoryURLGenerator) */ CategoryURLGenerator getDefaultItemURLGenerator(); /** * Sets the default item URL generator and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param generator the item URL generator ({@code null} permitted). * * @see #getDefaultItemURLGenerator() */ void setDefaultItemURLGenerator(CategoryURLGenerator generator); /** * Sets the default item URL generator and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param generator the item URL generator ({@code null} permitted). * @param notify notify listeners? * * @see #getDefaultItemURLGenerator() */ void setDefaultItemURLGenerator(CategoryURLGenerator generator, boolean notify); /** * Returns a legend item for a series. This method can return * {@code null}, in which case the series will have no entry in the * legend. * * @param datasetIndex the dataset index (zero-based). * @param series the series (zero-based index). * * @return The legend item (possibly {@code null}). */ LegendItem getLegendItem(int datasetIndex, int series); /** * Draws a background for the data area. * * @param g2 the graphics device. * @param plot the plot. * @param dataArea the data area. */ void drawBackground(Graphics2D g2, CategoryPlot plot, Rectangle2D dataArea); /** * Draws an outline for the data area. * * @param g2 the graphics device. * @param plot the plot. * @param dataArea the data area. */ void drawOutline(Graphics2D g2, CategoryPlot plot, Rectangle2D dataArea); /** * Draws a single data item. * * @param g2 the graphics device. * @param state state information for one chart. * @param dataArea the data plot area. * @param plot the plot. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the data. * @param row the row index (zero-based). * @param column the column index (zero-based). * @param pass the pass index. */ void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, ValueAxis rangeAxis, CategoryDataset dataset, int row, int column, int pass); /** * Draws a grid line against the domain axis. * * @param g2 the graphics device. * @param plot the plot. * @param dataArea the area for plotting data. * @param value the value. */ void drawDomainGridline(Graphics2D g2, CategoryPlot plot, Rectangle2D dataArea, double value); /** * Draws a grid line against the range axis. * * @param g2 the graphics device. * @param plot the plot. * @param axis the value axis. * @param dataArea the area for plotting data. * @param value the value. * @param paint the paint ({@code null} not permitted). * @param stroke the line stroke ({@code null} not permitted). */ void drawRangeLine(Graphics2D g2, CategoryPlot plot, ValueAxis axis, Rectangle2D dataArea, double value, Paint paint, Stroke stroke); /** * Draws a line (or some other marker) to indicate a particular category on * the domain axis. * * @param g2 the graphics device. * @param plot the plot. * @param axis the category axis. * @param marker the marker. * @param dataArea the area for plotting data. * * @see #drawRangeMarker(Graphics2D, CategoryPlot, ValueAxis, Marker, * Rectangle2D) */ void drawDomainMarker(Graphics2D g2, CategoryPlot plot, CategoryAxis axis, CategoryMarker marker, Rectangle2D dataArea); /** * Draws a line (or some other marker) to indicate a particular value on * the range axis. * * @param g2 the graphics device. * @param plot the plot. * @param axis the value axis. * @param marker the marker. * @param dataArea the area for plotting data. * * @see #drawDomainMarker(Graphics2D, CategoryPlot, CategoryAxis, * CategoryMarker, Rectangle2D) */ void drawRangeMarker(Graphics2D g2, CategoryPlot plot, ValueAxis axis, Marker marker, Rectangle2D dataArea); /** * Returns the Java2D coordinate for the middle of the specified data item. * * @param rowKey the row key. * @param columnKey the column key. * @param dataset the dataset. * @param axis the axis. * @param area the data area. * @param edge the edge along which the axis lies. * * @return The Java2D coordinate for the middle of the item. */ double getItemMiddle(Comparable rowKey, Comparable columnKey, CategoryDataset dataset, CategoryAxis axis, Rectangle2D area, RectangleEdge edge); } CategoryItemRendererState.java000066400000000000000000000140711463604235500343540ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/category/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------ * CategoryItemRendererState.java * ------------------------------ * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Peter Kolb (patch 2497611); * */ package org.jfree.chart.renderer.category; import org.jfree.chart.plot.CategoryCrosshairState; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.renderer.RendererState; /** * An object that retains temporary state information for a * {@link CategoryItemRenderer}. */ public class CategoryItemRendererState extends RendererState { /** The bar width. */ private double barWidth; /** The series running total. */ private double seriesRunningTotal; /** The array with the indices of the visible series.*/ private int[] visibleSeries; /** * State information for crosshairs in the plot (this is updated by the * renderer, but may be passed to several renderers in one chart). */ private CategoryCrosshairState crosshairState; /** * Creates a new object for recording temporary state information for a * renderer. * * @param info the plot rendering info ({@code null} permitted). */ public CategoryItemRendererState(PlotRenderingInfo info) { super(info); this.barWidth = 0.0; this.seriesRunningTotal = 0.0; } /** * Returns the bar width. * * @return The bar width. * * @see #setBarWidth(double) */ public double getBarWidth() { return this.barWidth; } /** * Sets the bar width. The renderer calculates this value and stores it * here - it is not intended that users can manually set the bar width. * * @param width the width. * * @see #getBarWidth() */ public void setBarWidth(double width) { this.barWidth = width; } /** * Returns the series running total. * * @return The running total. * * @see #setSeriesRunningTotal(double) */ public double getSeriesRunningTotal() { return this.seriesRunningTotal; } /** * Sets the series running total (this method is intended for the use of * the renderer only). * * @param total the new total. * * @see #getSeriesRunningTotal() */ void setSeriesRunningTotal(double total) { this.seriesRunningTotal = total; } /** * Returns the crosshair state, if any. * * @return The crosshair state (possibly {@code null}). * * @see #setCrosshairState(CategoryCrosshairState) */ public CategoryCrosshairState getCrosshairState() { return this.crosshairState; } /** * Sets the crosshair state. * * @param state the new state ({@code null} permitted). * * @see #getCrosshairState() */ public void setCrosshairState(CategoryCrosshairState state) { this.crosshairState = state; } /** * Returns the index of the row relative to the visible rows. If no * visible rows have been specified, the original row index is returned. * If the row index is not included in the array of visible rows, * -1 is returned. * * @param rowIndex the row index. * * @return The new row index or -1. */ public int getVisibleSeriesIndex(int rowIndex) { if (this.visibleSeries == null) { return rowIndex; } int index = -1; for (int vRow = 0; vRow < this.visibleSeries.length; vRow++) { if (this.visibleSeries[vRow] == rowIndex) { index = vRow; break; } } return index; } /** * Returns the number of visible series or -1 if no visible series have * been specified. * * @return The number or -1. */ public int getVisibleSeriesCount() { if (this.visibleSeries == null) { return -1; } return this.visibleSeries.length; } /** * Returns a copy of the visible series array. * * @return The visible series array (possibly {@code null}). */ public int[] getVisibleSeriesArray() { if (this.visibleSeries == null) { return null; } int[] result = new int[this.visibleSeries.length]; System.arraycopy(this.visibleSeries, 0, result, 0, this.visibleSeries.length); return result; } /** * Sets an array with the indices of the visible rows. * * @param visibleSeries the array ({@code null} permitted). */ public void setVisibleSeriesArray(int[] visibleSeries) { this.visibleSeries = visibleSeries; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/category/CategoryStepRenderer.java000066400000000000000000000311141463604235500334440ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * CategoryStepRenderer.java * ------------------------- * * (C) Copyright 2004-present, by Brian Cole and Contributors. * * Original Author: Brian Cole; * Contributor(s): David Gilbert; * */ package org.jfree.chart.renderer.category; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import org.jfree.chart.LegendItem; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.renderer.xy.XYStepRenderer; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.category.CategoryDataset; /** * A "step" renderer similar to {@link XYStepRenderer} but * that can be used with the {@link CategoryPlot} class. The example shown * here is generated by the {@code CategoryStepChartDemo1.java} program * included in the JFreeChart Demo Collection: *

* CategoryStepRendererSample.png */ public class CategoryStepRenderer extends AbstractCategoryItemRenderer implements Cloneable, PublicCloneable, Serializable { /** * State information for the renderer. */ protected static class State extends CategoryItemRendererState { /** * A working line for re-use to avoid creating large numbers of * objects. */ public Line2D line; /** * Creates a new state instance. * * @param info collects plot rendering information ({@code null} * permitted). */ public State(PlotRenderingInfo info) { super(info); this.line = new Line2D.Double(); } } /** For serialization. */ private static final long serialVersionUID = -5121079703118261470L; /** The stagger width. */ public static final int STAGGER_WIDTH = 5; // could make this configurable /** * A flag that controls whether or not the steps for multiple series are * staggered. */ private boolean stagger = false; /** * Creates a new renderer (stagger defaults to {@code false}). */ public CategoryStepRenderer() { this(false); } /** * Creates a new renderer. * * @param stagger should the horizontal part of the step be staggered by * series? */ public CategoryStepRenderer(boolean stagger) { this.stagger = stagger; setDefaultLegendShape(new Rectangle2D.Double(-4.0, -3.0, 8.0, 6.0)); } /** * Returns the flag that controls whether the series steps are staggered. * * @return A boolean. */ public boolean getStagger() { return this.stagger; } /** * Sets the flag that controls whether or not the series steps are * staggered and sends a {@link RendererChangeEvent} to all registered * listeners. * * @param shouldStagger a boolean. */ public void setStagger(boolean shouldStagger) { this.stagger = shouldStagger; fireChangeEvent(); } /** * Returns a legend item for a series. * * @param datasetIndex the dataset index (zero-based). * @param series the series index (zero-based). * * @return The legend item. */ @Override public LegendItem getLegendItem(int datasetIndex, int series) { CategoryPlot p = getPlot(); if (p == null) { return null; } // check that a legend item needs to be displayed... if (!isSeriesVisible(series) || !isSeriesVisibleInLegend(series)) { return null; } CategoryDataset dataset = p.getDataset(datasetIndex); String label = getLegendItemLabelGenerator().generateLabel(dataset, series); String description = label; String toolTipText = null; if (getLegendItemToolTipGenerator() != null) { toolTipText = getLegendItemToolTipGenerator().generateLabel( dataset, series); } String urlText = null; if (getLegendItemURLGenerator() != null) { urlText = getLegendItemURLGenerator().generateLabel(dataset, series); } Shape shape = lookupLegendShape(series); Paint paint = lookupSeriesPaint(series); LegendItem item = new LegendItem(label, description, toolTipText, urlText, shape, paint); item.setLabelFont(lookupLegendTextFont(series)); Paint labelPaint = lookupLegendTextPaint(series); if (labelPaint != null) { item.setLabelPaint(labelPaint); } item.setSeriesKey(dataset.getRowKey(series)); item.setSeriesIndex(series); item.setDataset(dataset); item.setDatasetIndex(datasetIndex); return item; } /** * Creates a new state instance. This method is called from * {@link #initialise(Graphics2D, Rectangle2D, CategoryPlot, int, * PlotRenderingInfo)}, and we override it to ensure that the state * contains a working Line2D instance. * * @param info the plot rendering info ({@code null} is permitted). * * @return A new state instance. */ @Override protected CategoryItemRendererState createState(PlotRenderingInfo info) { return new State(info); } /** * Draws a line taking into account the specified orientation. *

* In version 1.0.5, the signature of this method was changed by the * addition of the 'state' parameter. This is an incompatible change, but * is considered a low risk because it is unlikely that anyone has * subclassed this renderer. If this *does* cause trouble for you, please * report it as a bug. * * @param g2 the graphics device. * @param state the renderer state. * @param orientation the plot orientation. * @param x0 the x-coordinate for the start of the line. * @param y0 the y-coordinate for the start of the line. * @param x1 the x-coordinate for the end of the line. * @param y1 the y-coordinate for the end of the line. */ protected void drawLine(Graphics2D g2, State state, PlotOrientation orientation, double x0, double y0, double x1, double y1) { if (orientation == PlotOrientation.VERTICAL) { state.line.setLine(x0, y0, x1, y1); g2.draw(state.line); } else if (orientation == PlotOrientation.HORIZONTAL) { state.line.setLine(y0, x0, y1, x1); // switch x and y g2.draw(state.line); } } /** * Draw a single data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the area in which the data is drawn. * @param plot the plot. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset. * @param row the row index (zero-based). * @param column the column index (zero-based). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, ValueAxis rangeAxis, CategoryDataset dataset, int row, int column, int pass) { // do nothing if item is not visible if (!getItemVisible(row, column)) { return; } Number value = dataset.getValue(row, column); if (value == null) { return; } PlotOrientation orientation = plot.getOrientation(); // current data point... double x1s = domainAxis.getCategoryStart(column, getColumnCount(), dataArea, plot.getDomainAxisEdge()); double x1 = domainAxis.getCategoryMiddle(column, getColumnCount(), dataArea, plot.getDomainAxisEdge()); double x1e = 2 * x1 - x1s; // or: x1s + 2*(x1-x1s) double y1 = rangeAxis.valueToJava2D(value.doubleValue(), dataArea, plot.getRangeAxisEdge()); g2.setPaint(getItemPaint(row, column)); g2.setStroke(getItemStroke(row, column)); if (column != 0) { Number previousValue = dataset.getValue(row, column - 1); if (previousValue != null) { // previous data point... double previous = previousValue.doubleValue(); double x0s = domainAxis.getCategoryStart(column - 1, getColumnCount(), dataArea, plot.getDomainAxisEdge()); double x0 = domainAxis.getCategoryMiddle(column - 1, getColumnCount(), dataArea, plot.getDomainAxisEdge()); double x0e = 2 * x0 - x0s; // or: x0s + 2*(x0-x0s) double y0 = rangeAxis.valueToJava2D(previous, dataArea, plot.getRangeAxisEdge()); if (getStagger()) { int xStagger = row * STAGGER_WIDTH; if (xStagger > (x1s - x0e)) { xStagger = (int) (x1s - x0e); } x1s = x0e + xStagger; } drawLine(g2, (State) state, orientation, x0e, y0, x1s, y0); // extend x0's flat bar drawLine(g2, (State) state, orientation, x1s, y0, x1s, y1); // upright bar } } drawLine(g2, (State) state, orientation, x1s, y1, x1e, y1); // x1's flat bar // draw the item labels if there are any... if (isItemLabelVisible(row, column)) { drawItemLabel(g2, orientation, dataset, row, column, x1, y1, (value.doubleValue() < 0.0)); } // add an item entity, if this information is being collected EntityCollection entities = state.getEntityCollection(); if (entities != null) { Rectangle2D hotspot = new Rectangle2D.Double(); if (orientation == PlotOrientation.VERTICAL) { hotspot.setRect(x1s, y1, x1e - x1s, 4.0); } else { hotspot.setRect(y1 - 2.0, x1s, 4.0, x1e - x1s); } addItemEntity(entities, dataset, row, column, hotspot); } } /** * Tests this renderer for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof CategoryStepRenderer)) { return false; } CategoryStepRenderer that = (CategoryStepRenderer) obj; if (this.stagger != that.stagger) { return false; } return super.equals(obj); } } DefaultCategoryItemRenderer.java000066400000000000000000000037661463604235500346710ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/category/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------------- * DefaultCategoryItemRenderer.java * -------------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.category; import java.io.Serializable; import org.jfree.chart.plot.CategoryPlot; /** * A default renderer for the {@link CategoryPlot} class. This is simply an * alias for the {@link LineAndShapeRenderer} class. */ public class DefaultCategoryItemRenderer extends LineAndShapeRenderer implements Serializable { /** For serialization. */ private static final long serialVersionUID = -7793786349384231896L; // no new methods } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/category/GanttRenderer.java000066400000000000000000000545221463604235500321200ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * GanttRenderer.java * ------------------ * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.category; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Stroke; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.labels.CategoryItemLabelGenerator; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.SerialUtils; import org.jfree.data.category.CategoryDataset; import org.jfree.data.gantt.GanttCategoryDataset; /** * A renderer for simple Gantt charts. The example shown * here is generated by the {@code GanttDemo1.java} program * included in the JFreeChart Demo Collection: *

* GanttRendererSample.png */ public class GanttRenderer extends IntervalBarRenderer implements Serializable { /** For serialization. */ private static final long serialVersionUID = -4010349116350119512L; /** The paint for displaying the percentage complete. */ private transient Paint completePaint; /** The paint for displaying the incomplete part of a task. */ private transient Paint incompletePaint; /** * Controls the starting edge of the progress indicator (expressed as a * percentage of the overall bar width). */ private double startPercent; /** * Controls the ending edge of the progress indicator (expressed as a * percentage of the overall bar width). */ private double endPercent; /** * Creates a new renderer. */ public GanttRenderer() { super(); setIncludeBaseInRange(false); this.completePaint = Color.GREEN; this.incompletePaint = Color.RED; this.startPercent = 0.35; this.endPercent = 0.65; } /** * Returns the paint used to show the percentage complete. * * @return The paint (never {@code null}). * * @see #setCompletePaint(Paint) */ public Paint getCompletePaint() { return this.completePaint; } /** * Sets the paint used to show the percentage complete and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getCompletePaint() */ public void setCompletePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.completePaint = paint; fireChangeEvent(); } /** * Returns the paint used to show the percentage incomplete. * * @return The paint (never {@code null}). * * @see #setCompletePaint(Paint) */ public Paint getIncompletePaint() { return this.incompletePaint; } /** * Sets the paint used to show the percentage incomplete and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getIncompletePaint() */ public void setIncompletePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.incompletePaint = paint; fireChangeEvent(); } /** * Returns the position of the start of the progress indicator, as a * percentage of the bar width. * * @return The start percent. * * @see #setStartPercent(double) */ public double getStartPercent() { return this.startPercent; } /** * Sets the position of the start of the progress indicator, as a * percentage of the bar width, and sends a {@link RendererChangeEvent} to * all registered listeners. * * @param percent the percent. * * @see #getStartPercent() */ public void setStartPercent(double percent) { this.startPercent = percent; fireChangeEvent(); } /** * Returns the position of the end of the progress indicator, as a * percentage of the bar width. * * @return The end percent. * * @see #setEndPercent(double) */ public double getEndPercent() { return this.endPercent; } /** * Sets the position of the end of the progress indicator, as a percentage * of the bar width, and sends a {@link RendererChangeEvent} to all * registered listeners. * * @param percent the percent. * * @see #getEndPercent() */ public void setEndPercent(double percent) { this.endPercent = percent; fireChangeEvent(); } /** * Draws the bar for a single (series, category) data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the data area. * @param plot the plot. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset. * @param row the row index (zero-based). * @param column the column index (zero-based). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, ValueAxis rangeAxis, CategoryDataset dataset, int row, int column, int pass) { if (dataset instanceof GanttCategoryDataset) { GanttCategoryDataset gcd = (GanttCategoryDataset) dataset; drawTasks(g2, state, dataArea, plot, domainAxis, rangeAxis, gcd, row, column); } else { // let the superclass handle it... super.drawItem(g2, state, dataArea, plot, domainAxis, rangeAxis, dataset, row, column, pass); } } /** * Draws the tasks/subtasks for one item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the data plot area. * @param plot the plot. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the data. * @param row the row index (zero-based). * @param column the column index (zero-based). */ protected void drawTasks(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, ValueAxis rangeAxis, GanttCategoryDataset dataset, int row, int column) { int count = dataset.getSubIntervalCount(row, column); if (count == 0) { drawTask(g2, state, dataArea, plot, domainAxis, rangeAxis, dataset, row, column); } PlotOrientation orientation = plot.getOrientation(); for (int subinterval = 0; subinterval < count; subinterval++) { RectangleEdge rangeAxisLocation = plot.getRangeAxisEdge(); // value 0 Number value0 = dataset.getStartValue(row, column, subinterval); if (value0 == null) { return; } double translatedValue0 = rangeAxis.valueToJava2D( value0.doubleValue(), dataArea, rangeAxisLocation); // value 1 Number value1 = dataset.getEndValue(row, column, subinterval); if (value1 == null) { return; } double translatedValue1 = rangeAxis.valueToJava2D( value1.doubleValue(), dataArea, rangeAxisLocation); if (translatedValue1 < translatedValue0) { double temp = translatedValue1; translatedValue1 = translatedValue0; translatedValue0 = temp; } double rectStart = calculateBarW0(plot, plot.getOrientation(), dataArea, domainAxis, state, row, column); double rectLength = Math.abs(translatedValue1 - translatedValue0); double rectBreadth = state.getBarWidth(); // DRAW THE BARS... Rectangle2D bar = null; RectangleEdge barBase = null; if (plot.getOrientation() == PlotOrientation.HORIZONTAL) { bar = new Rectangle2D.Double(translatedValue0, rectStart, rectLength, rectBreadth); barBase = RectangleEdge.LEFT; } else if (plot.getOrientation() == PlotOrientation.VERTICAL) { bar = new Rectangle2D.Double(rectStart, translatedValue0, rectBreadth, rectLength); barBase = RectangleEdge.BOTTOM; } Rectangle2D completeBar = null; Rectangle2D incompleteBar = null; Number percent = dataset.getPercentComplete(row, column, subinterval); double start = getStartPercent(); double end = getEndPercent(); if (percent != null) { double p = percent.doubleValue(); if (orientation == PlotOrientation.HORIZONTAL) { completeBar = new Rectangle2D.Double(translatedValue0, rectStart + start * rectBreadth, rectLength * p, rectBreadth * (end - start)); incompleteBar = new Rectangle2D.Double(translatedValue0 + rectLength * p, rectStart + start * rectBreadth, rectLength * (1 - p), rectBreadth * (end - start)); } else if (orientation == PlotOrientation.VERTICAL) { completeBar = new Rectangle2D.Double(rectStart + start * rectBreadth, translatedValue0 + rectLength * (1 - p), rectBreadth * (end - start), rectLength * p); incompleteBar = new Rectangle2D.Double(rectStart + start * rectBreadth, translatedValue0, rectBreadth * (end - start), rectLength * (1 - p)); } } if (getShadowsVisible()) { getBarPainter().paintBarShadow(g2, this, row, column, bar, barBase, true); } getBarPainter().paintBar(g2, this, row, column, bar, barBase); if (completeBar != null) { g2.setPaint(getCompletePaint()); g2.fill(completeBar); } if (incompleteBar != null) { g2.setPaint(getIncompletePaint()); g2.fill(incompleteBar); } if (isDrawBarOutline() && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) { g2.setStroke(getItemStroke(row, column)); g2.setPaint(getItemOutlinePaint(row, column)); g2.draw(bar); } if (subinterval == count - 1) { // submit the current data point as a crosshair candidate int datasetIndex = plot.indexOf(dataset); Comparable columnKey = dataset.getColumnKey(column); Comparable rowKey = dataset.getRowKey(row); double xx = domainAxis.getCategorySeriesMiddle(columnKey, rowKey, dataset, getItemMargin(), dataArea, plot.getDomainAxisEdge()); updateCrosshairValues(state.getCrosshairState(), dataset.getRowKey(row), dataset.getColumnKey(column), value1.doubleValue(), datasetIndex, xx, translatedValue1, orientation); } // collect entity and tool tip information... if (state.getInfo() != null) { EntityCollection entities = state.getEntityCollection(); if (entities != null) { addItemEntity(entities, dataset, row, column, bar); } } } } /** * Draws a single task. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the data plot area. * @param plot the plot. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the data. * @param row the row index (zero-based). * @param column the column index (zero-based). */ protected void drawTask(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, ValueAxis rangeAxis, GanttCategoryDataset dataset, int row, int column) { PlotOrientation orientation = plot.getOrientation(); RectangleEdge rangeAxisLocation = plot.getRangeAxisEdge(); // Y0 Number value0 = dataset.getEndValue(row, column); if (value0 == null) { return; } double java2dValue0 = rangeAxis.valueToJava2D(value0.doubleValue(), dataArea, rangeAxisLocation); // Y1 Number value1 = dataset.getStartValue(row, column); if (value1 == null) { return; } double java2dValue1 = rangeAxis.valueToJava2D(value1.doubleValue(), dataArea, rangeAxisLocation); if (java2dValue1 < java2dValue0) { double temp = java2dValue1; java2dValue1 = java2dValue0; java2dValue0 = temp; value1 = value0; } double rectStart = calculateBarW0(plot, orientation, dataArea, domainAxis, state, row, column); double rectBreadth = state.getBarWidth(); double rectLength = Math.abs(java2dValue1 - java2dValue0); Rectangle2D bar = null; RectangleEdge barBase = null; if (orientation == PlotOrientation.HORIZONTAL) { bar = new Rectangle2D.Double(java2dValue0, rectStart, rectLength, rectBreadth); barBase = RectangleEdge.LEFT; } else if (orientation == PlotOrientation.VERTICAL) { bar = new Rectangle2D.Double(rectStart, java2dValue0, rectBreadth, rectLength); barBase = RectangleEdge.BOTTOM; } Rectangle2D completeBar = null; Rectangle2D incompleteBar = null; Number percent = dataset.getPercentComplete(row, column); double start = getStartPercent(); double end = getEndPercent(); if (percent != null) { double p = percent.doubleValue(); if (plot.getOrientation() == PlotOrientation.HORIZONTAL) { completeBar = new Rectangle2D.Double(java2dValue0, rectStart + start * rectBreadth, rectLength * p, rectBreadth * (end - start)); incompleteBar = new Rectangle2D.Double(java2dValue0 + rectLength * p, rectStart + start * rectBreadth, rectLength * (1 - p), rectBreadth * (end - start)); } else if (plot.getOrientation() == PlotOrientation.VERTICAL) { completeBar = new Rectangle2D.Double(rectStart + start * rectBreadth, java2dValue1 + rectLength * (1 - p), rectBreadth * (end - start), rectLength * p); incompleteBar = new Rectangle2D.Double(rectStart + start * rectBreadth, java2dValue1, rectBreadth * (end - start), rectLength * (1 - p)); } } if (getShadowsVisible()) { getBarPainter().paintBarShadow(g2, this, row, column, bar, barBase, true); } getBarPainter().paintBar(g2, this, row, column, bar, barBase); if (completeBar != null) { g2.setPaint(getCompletePaint()); g2.fill(completeBar); } if (incompleteBar != null) { g2.setPaint(getIncompletePaint()); g2.fill(incompleteBar); } // draw the outline... if (isDrawBarOutline() && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) { Stroke stroke = getItemOutlineStroke(row, column); Paint paint = getItemOutlinePaint(row, column); if (stroke != null && paint != null) { g2.setStroke(stroke); g2.setPaint(paint); g2.draw(bar); } } CategoryItemLabelGenerator generator = getItemLabelGenerator(row, column); if (generator != null && isItemLabelVisible(row, column)) { drawItemLabel(g2, dataset, row, column, plot, generator, bar, false); } // submit the current data point as a crosshair candidate int datasetIndex = plot.indexOf(dataset); Comparable columnKey = dataset.getColumnKey(column); Comparable rowKey = dataset.getRowKey(row); double xx = domainAxis.getCategorySeriesMiddle(columnKey, rowKey, dataset, getItemMargin(), dataArea, plot.getDomainAxisEdge()); updateCrosshairValues(state.getCrosshairState(), dataset.getRowKey(row), dataset.getColumnKey(column), value1.doubleValue(), datasetIndex, xx, java2dValue1, orientation); // collect entity and tool tip information... EntityCollection entities = state.getEntityCollection(); if (entities != null) { addItemEntity(entities, dataset, row, column, bar); } } /** * Returns the Java2D coordinate for the middle of the specified data item. * * @param rowKey the row key. * @param columnKey the column key. * @param dataset the dataset. * @param axis the axis. * @param area the drawing area. * @param edge the edge along which the axis lies. * * @return The Java2D coordinate. */ @Override public double getItemMiddle(Comparable rowKey, Comparable columnKey, CategoryDataset dataset, CategoryAxis axis, Rectangle2D area, RectangleEdge edge) { return axis.getCategorySeriesMiddle(columnKey, rowKey, dataset, getItemMargin(), area, edge); } /** * Tests this renderer for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof GanttRenderer)) { return false; } GanttRenderer that = (GanttRenderer) obj; if (!PaintUtils.equal(this.completePaint, that.completePaint)) { return false; } if (!PaintUtils.equal(this.incompletePaint, that.incompletePaint)) { return false; } if (this.startPercent != that.startPercent) { return false; } if (this.endPercent != that.endPercent) { return false; } return super.equals(obj); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.completePaint, stream); SerialUtils.writePaint(this.incompletePaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.completePaint = SerialUtils.readPaint(stream); this.incompletePaint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/category/GradientBarPainter.java000066400000000000000000000313461463604235500330600ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * GradientBarPainter.java * ----------------------- * (C) Copyright 2008-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.category; import java.awt.Color; import java.awt.GradientPaint; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Stroke; import java.awt.geom.Rectangle2D; import java.awt.geom.RectangularShape; import java.io.Serializable; import org.jfree.chart.HashUtils; import org.jfree.chart.ui.RectangleEdge; /** * An implementation of the {@link BarPainter} interface that uses several * gradient fills to enrich the appearance of the bars. */ public class GradientBarPainter implements BarPainter, Serializable { /** The division point between the first and second gradient regions. */ private double g1; /** The division point between the second and third gradient regions. */ private double g2; /** The division point between the third and fourth gradient regions. */ private double g3; /** * Creates a new instance. */ public GradientBarPainter() { this(0.10, 0.20, 0.80); } /** * Creates a new instance. * * @param g1 percentage value defining the line between regions 1 and 2. * @param g2 percentage value defining the line between regions 2 and 3. * @param g3 percentage value defining the line between regions 3 and 4. */ public GradientBarPainter(double g1, double g2, double g3) { this.g1 = g1; this.g2 = g2; this.g3 = g3; } /** * Paints a single bar instance. * * @param g2 the graphics target. * @param renderer the renderer. * @param row the row index. * @param column the column index. * @param bar the bar * @param base indicates which side of the rectangle is the base of the * bar. */ @Override public void paintBar(Graphics2D g2, BarRenderer renderer, int row, int column, RectangularShape bar, RectangleEdge base) { Paint itemPaint = renderer.getItemPaint(row, column); Color c0, c1; if (itemPaint instanceof Color) { c0 = (Color) itemPaint; c1 = c0.brighter(); } else if (itemPaint instanceof GradientPaint) { GradientPaint gp = (GradientPaint) itemPaint; c0 = gp.getColor1(); c1 = gp.getColor2(); } else { c0 = Color.BLUE; c1 = Color.BLUE.brighter(); } // as a special case, if the bar colour has alpha == 0, we draw // nothing. if (c0.getAlpha() == 0) { return; } if (base == RectangleEdge.TOP || base == RectangleEdge.BOTTOM) { Rectangle2D[] regions = splitVerticalBar(bar, this.g1, this.g2, this.g3); GradientPaint gp = new GradientPaint((float) regions[0].getMinX(), 0.0f, c0, (float) regions[0].getMaxX(), 0.0f, Color.WHITE); g2.setPaint(gp); g2.fill(regions[0]); gp = new GradientPaint((float) regions[1].getMinX(), 0.0f, Color.WHITE, (float) regions[1].getMaxX(), 0.0f, c0); g2.setPaint(gp); g2.fill(regions[1]); gp = new GradientPaint((float) regions[2].getMinX(), 0.0f, c0, (float) regions[2].getMaxX(), 0.0f, c1); g2.setPaint(gp); g2.fill(regions[2]); gp = new GradientPaint((float) regions[3].getMinX(), 0.0f, c1, (float) regions[3].getMaxX(), 0.0f, c0); g2.setPaint(gp); g2.fill(regions[3]); } else if (base == RectangleEdge.LEFT || base == RectangleEdge.RIGHT) { Rectangle2D[] regions = splitHorizontalBar(bar, this.g1, this.g2, this.g3); GradientPaint gp = new GradientPaint(0.0f, (float) regions[0].getMinY(), c0, 0.0f, (float) regions[0].getMaxY(), Color.WHITE); g2.setPaint(gp); g2.fill(regions[0]); gp = new GradientPaint(0.0f, (float) regions[1].getMinY(), Color.WHITE, 0.0f, (float) regions[1].getMaxY(), c0); g2.setPaint(gp); g2.fill(regions[1]); gp = new GradientPaint(0.0f, (float) regions[2].getMinY(), c0, 0.0f, (float) regions[2].getMaxY(), c1); g2.setPaint(gp); g2.fill(regions[2]); gp = new GradientPaint(0.0f, (float) regions[3].getMinY(), c1, 0.0f, (float) regions[3].getMaxY(), c0); g2.setPaint(gp); g2.fill(regions[3]); } // draw the outline... if (renderer.isDrawBarOutline() /*&& state.getBarWidth() > renderer.BAR_OUTLINE_WIDTH_THRESHOLD*/) { Stroke stroke = renderer.getItemOutlineStroke(row, column); Paint paint = renderer.getItemOutlinePaint(row, column); if (stroke != null && paint != null) { g2.setStroke(stroke); g2.setPaint(paint); g2.draw(bar); } } } /** * Paints a single bar instance. * * @param g2 the graphics target. * @param renderer the renderer. * @param row the row index. * @param column the column index. * @param bar the bar * @param base indicates which side of the rectangle is the base of the * bar. * @param pegShadow peg the shadow to the base of the bar? */ @Override public void paintBarShadow(Graphics2D g2, BarRenderer renderer, int row, int column, RectangularShape bar, RectangleEdge base, boolean pegShadow) { // handle a special case - if the bar colour has alpha == 0, it is // invisible so we shouldn't draw any shadow Paint itemPaint = renderer.getItemPaint(row, column); if (itemPaint instanceof Color) { Color c = (Color) itemPaint; if (c.getAlpha() == 0) { return; } } RectangularShape shadow = createShadow(bar, renderer.getShadowXOffset(), renderer.getShadowYOffset(), base, pegShadow); g2.setPaint(renderer.getShadowPaint()); g2.fill(shadow); } /** * Creates a shadow for the bar. * * @param bar the bar shape. * @param xOffset the x-offset for the shadow. * @param yOffset the y-offset for the shadow. * @param base the edge that is the base of the bar. * @param pegShadow peg the shadow to the base? * * @return A rectangle for the shadow. */ private Rectangle2D createShadow(RectangularShape bar, double xOffset, double yOffset, RectangleEdge base, boolean pegShadow) { double x0 = bar.getMinX(); double x1 = bar.getMaxX(); double y0 = bar.getMinY(); double y1 = bar.getMaxY(); if (base == RectangleEdge.TOP) { x0 += xOffset; x1 += xOffset; if (!pegShadow) { y0 += yOffset; } y1 += yOffset; } else if (base == RectangleEdge.BOTTOM) { x0 += xOffset; x1 += xOffset; y0 += yOffset; if (!pegShadow) { y1 += yOffset; } } else if (base == RectangleEdge.LEFT) { if (!pegShadow) { x0 += xOffset; } x1 += xOffset; y0 += yOffset; y1 += yOffset; } else if (base == RectangleEdge.RIGHT) { x0 += xOffset; if (!pegShadow) { x1 += xOffset; } y0 += yOffset; y1 += yOffset; } return new Rectangle2D.Double(x0, y0, (x1 - x0), (y1 - y0)); } /** * Splits a bar into subregions (elsewhere, these subregions will have * different gradients applied to them). * * @param bar the bar shape. * @param a the first division. * @param b the second division. * @param c the third division. * * @return An array containing four subregions. */ private Rectangle2D[] splitVerticalBar(RectangularShape bar, double a, double b, double c) { Rectangle2D[] result = new Rectangle2D[4]; double x0 = bar.getMinX(); double x1 = Math.rint(x0 + (bar.getWidth() * a)); double x2 = Math.rint(x0 + (bar.getWidth() * b)); double x3 = Math.rint(x0 + (bar.getWidth() * c)); result[0] = new Rectangle2D.Double(bar.getMinX(), bar.getMinY(), x1 - x0, bar.getHeight()); result[1] = new Rectangle2D.Double(x1, bar.getMinY(), x2 - x1, bar.getHeight()); result[2] = new Rectangle2D.Double(x2, bar.getMinY(), x3 - x2, bar.getHeight()); result[3] = new Rectangle2D.Double(x3, bar.getMinY(), bar.getMaxX() - x3, bar.getHeight()); return result; } /** * Splits a bar into subregions (elsewhere, these subregions will have * different gradients applied to them). * * @param bar the bar shape. * @param a the first division. * @param b the second division. * @param c the third division. * * @return An array containing four subregions. */ private Rectangle2D[] splitHorizontalBar(RectangularShape bar, double a, double b, double c) { Rectangle2D[] result = new Rectangle2D[4]; double y0 = bar.getMinY(); double y1 = Math.rint(y0 + (bar.getHeight() * a)); double y2 = Math.rint(y0 + (bar.getHeight() * b)); double y3 = Math.rint(y0 + (bar.getHeight() * c)); result[0] = new Rectangle2D.Double(bar.getMinX(), bar.getMinY(), bar.getWidth(), y1 - y0); result[1] = new Rectangle2D.Double(bar.getMinX(), y1, bar.getWidth(), y2 - y1); result[2] = new Rectangle2D.Double(bar.getMinX(), y2, bar.getWidth(), y3 - y2); result[3] = new Rectangle2D.Double(bar.getMinX(), y3, bar.getWidth(), bar.getMaxY() - y3); return result; } /** * Tests this instance for equality with an arbitrary object. * * @param obj the obj ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof GradientBarPainter)) { return false; } GradientBarPainter that = (GradientBarPainter) obj; if (this.g1 != that.g1) { return false; } if (this.g2 != that.g2) { return false; } if (this.g3 != that.g3) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int hash = 37; hash = HashUtils.hashCode(hash, this.g1); hash = HashUtils.hashCode(hash, this.g2); hash = HashUtils.hashCode(hash, this.g3); return hash; } } GroupedStackedBarRenderer.java000066400000000000000000000321571463604235500343150ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/category/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------ * GroupedStackedBarRenderer.java * ------------------------------ * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.category; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.labels.CategoryItemLabelGenerator; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.KeyToGroupMap; import org.jfree.data.Range; import org.jfree.data.category.CategoryDataset; import org.jfree.data.general.DatasetUtils; /** * A renderer that draws stacked bars within groups. This will probably be * merged with the {@link StackedBarRenderer} class at some point. The example * shown here is generated by the {@code StackedBarChartDemo4.java} * program included in the JFreeChart Demo Collection: *

* GroupedStackedBarRendererSample.png */ public class GroupedStackedBarRenderer extends StackedBarRenderer implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -2725921399005922939L; /** A map used to assign each series to a group. */ private KeyToGroupMap seriesToGroupMap; /** * Creates a new renderer. */ public GroupedStackedBarRenderer() { super(); this.seriesToGroupMap = new KeyToGroupMap(); } /** * Updates the map used to assign each series to a group, and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param map the map ({@code null} not permitted). */ public void setSeriesToGroupMap(KeyToGroupMap map) { Args.nullNotPermitted(map, "map"); this.seriesToGroupMap = map; fireChangeEvent(); } /** * Returns the range of values the renderer requires to display all the * items from the specified dataset. * * @param dataset the dataset ({@code null} permitted). * * @return The range (or {@code null} if the dataset is * {@code null} or empty). */ @Override public Range findRangeBounds(CategoryDataset dataset) { if (dataset == null) { return null; } Range r = DatasetUtils.findStackedRangeBounds( dataset, this.seriesToGroupMap); return r; } /** * Calculates the bar width and stores it in the renderer state. We * override the method in the base class to take account of the * series-to-group mapping. * * @param plot the plot. * @param dataArea the data area. * @param rendererIndex the renderer index. * @param state the renderer state. */ @Override protected void calculateBarWidth(CategoryPlot plot, Rectangle2D dataArea, int rendererIndex, CategoryItemRendererState state) { // calculate the bar width CategoryAxis xAxis = plot.getDomainAxisForDataset(rendererIndex); CategoryDataset data = plot.getDataset(rendererIndex); if (data != null) { PlotOrientation orientation = plot.getOrientation(); double space = 0.0; if (orientation == PlotOrientation.HORIZONTAL) { space = dataArea.getHeight(); } else if (orientation == PlotOrientation.VERTICAL) { space = dataArea.getWidth(); } double maxWidth = space * getMaximumBarWidth(); int groups = this.seriesToGroupMap.getGroupCount(); int categories = data.getColumnCount(); int columns = groups * categories; double categoryMargin = 0.0; double itemMargin = 0.0; if (categories > 1) { categoryMargin = xAxis.getCategoryMargin(); } if (groups > 1) { itemMargin = getItemMargin(); } double used = space * (1 - xAxis.getLowerMargin() - xAxis.getUpperMargin() - categoryMargin - itemMargin); if (columns > 0) { state.setBarWidth(Math.min(used / columns, maxWidth)); } else { state.setBarWidth(Math.min(used, maxWidth)); } } } /** * Calculates the coordinate of the first "side" of a bar. This will be * the minimum x-coordinate for a vertical bar, and the minimum * y-coordinate for a horizontal bar. * * @param plot the plot. * @param orientation the plot orientation. * @param dataArea the data area. * @param domainAxis the domain axis. * @param state the renderer state (has the bar width precalculated). * @param row the row index. * @param column the column index. * * @return The coordinate. */ @Override protected double calculateBarW0(CategoryPlot plot, PlotOrientation orientation, Rectangle2D dataArea, CategoryAxis domainAxis, CategoryItemRendererState state, int row, int column) { // calculate bar width... double space; if (orientation == PlotOrientation.HORIZONTAL) { space = dataArea.getHeight(); } else { space = dataArea.getWidth(); } double barW0 = domainAxis.getCategoryStart(column, getColumnCount(), dataArea, plot.getDomainAxisEdge()); int groupCount = this.seriesToGroupMap.getGroupCount(); int groupIndex = this.seriesToGroupMap.getGroupIndex( this.seriesToGroupMap.getGroup(plot.getDataset( plot.getIndexOf(this)).getRowKey(row))); int categoryCount = getColumnCount(); if (groupCount > 1) { double groupGap = space * getItemMargin() / (categoryCount * (groupCount - 1)); double groupW = calculateSeriesWidth(space, domainAxis, categoryCount, groupCount); barW0 = barW0 + groupIndex * (groupW + groupGap) + (groupW / 2.0) - (state.getBarWidth() / 2.0); } else { barW0 = domainAxis.getCategoryMiddle(column, getColumnCount(), dataArea, plot.getDomainAxisEdge()) - state.getBarWidth() / 2.0; } return barW0; } /** * Draws a stacked bar for a specific item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the plot area. * @param plot the plot. * @param domainAxis the domain (category) axis. * @param rangeAxis the range (value) axis. * @param dataset the data. * @param row the row index (zero-based). * @param column the column index (zero-based). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, ValueAxis rangeAxis, CategoryDataset dataset, int row, int column, int pass) { // nothing is drawn for null values... Number dataValue = dataset.getValue(row, column); if (dataValue == null) { return; } double value = dataValue.doubleValue(); Comparable group = this.seriesToGroupMap.getGroup( dataset.getRowKey(row)); PlotOrientation orientation = plot.getOrientation(); double barW0 = calculateBarW0(plot, orientation, dataArea, domainAxis, state, row, column); double positiveBase = 0.0; double negativeBase = 0.0; for (int i = 0; i < row; i++) { if (group.equals(this.seriesToGroupMap.getGroup( dataset.getRowKey(i)))) { Number v = dataset.getValue(i, column); if (v != null) { double d = v.doubleValue(); if (d > 0) { positiveBase = positiveBase + d; } else { negativeBase = negativeBase + d; } } } } double translatedBase; double translatedValue; boolean positive = (value > 0.0); boolean inverted = rangeAxis.isInverted(); RectangleEdge barBase; if (orientation == PlotOrientation.HORIZONTAL) { if (positive && inverted || !positive && !inverted) { barBase = RectangleEdge.RIGHT; } else { barBase = RectangleEdge.LEFT; } } else { if (positive && !inverted || !positive && inverted) { barBase = RectangleEdge.BOTTOM; } else { barBase = RectangleEdge.TOP; } } RectangleEdge location = plot.getRangeAxisEdge(); if (value > 0.0) { translatedBase = rangeAxis.valueToJava2D(positiveBase, dataArea, location); translatedValue = rangeAxis.valueToJava2D(positiveBase + value, dataArea, location); } else { translatedBase = rangeAxis.valueToJava2D(negativeBase, dataArea, location); translatedValue = rangeAxis.valueToJava2D(negativeBase + value, dataArea, location); } double barL0 = Math.min(translatedBase, translatedValue); double barLength = Math.max(Math.abs(translatedValue - translatedBase), getMinimumBarLength()); Rectangle2D bar; if (orientation == PlotOrientation.HORIZONTAL) { bar = new Rectangle2D.Double(barL0, barW0, barLength, state.getBarWidth()); } else { bar = new Rectangle2D.Double(barW0, barL0, state.getBarWidth(), barLength); } getBarPainter().paintBar(g2, this, row, column, bar, barBase); CategoryItemLabelGenerator generator = getItemLabelGenerator(row, column); if (generator != null && isItemLabelVisible(row, column)) { drawItemLabel(g2, dataset, row, column, plot, generator, bar, (value < 0.0)); } // collect entity and tool tip information... if (state.getInfo() != null) { EntityCollection entities = state.getEntityCollection(); if (entities != null) { addItemEntity(entities, dataset, row, column, bar); } } } /** * Tests this renderer for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof GroupedStackedBarRenderer)) { return false; } GroupedStackedBarRenderer that = (GroupedStackedBarRenderer) obj; if (!this.seriesToGroupMap.equals(that.seriesToGroupMap)) { return false; } return super.equals(obj); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/category/IntervalBarRenderer.java000066400000000000000000000212761463604235500332540ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * IntervalBarRenderer.java * ------------------------ * (C) Copyright 2002-present, by Jeremy Bowman and Contributors. * * Original Author: Jeremy Bowman; * Contributor(s): David Gilbert; * Christian W. Zuckschwerdt; * Peter Kolb (patch 2497611, 2791407); * */ package org.jfree.chart.renderer.category; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.labels.CategoryItemLabelGenerator; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.ui.RectangleEdge; import org.jfree.data.Range; import org.jfree.data.category.CategoryDataset; import org.jfree.data.category.IntervalCategoryDataset; /** * A renderer that handles the drawing of bars for a bar plot where * each bar has a high and low value. This renderer is for use with the * {@link CategoryPlot} class. The example shown here is generated by the * {@code IntervalBarChartDemo1.java} program included in the JFreeChart * Demo Collection: *

* IntervalBarRendererSample.png */ public class IntervalBarRenderer extends BarRenderer { /** For serialization. */ private static final long serialVersionUID = -5068857361615528725L; /** * Constructs a new renderer. */ public IntervalBarRenderer() { super(); } /** * Returns the range of values from the specified dataset. For this * renderer, this is equivalent to calling * {@code findRangeBounds(dataset, true)}. * * @param dataset the dataset ({@code null} permitted). * * @return The range (or {@code null} if the dataset is * {@code null} or empty). */ @Override public Range findRangeBounds(CategoryDataset dataset) { return findRangeBounds(dataset, true); } /** * Draws the bar for a single (series, category) data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the data area. * @param plot the plot. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset. * @param row the row index (zero-based). * @param column the column index (zero-based). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, ValueAxis rangeAxis, CategoryDataset dataset, int row, int column, int pass) { if (dataset instanceof IntervalCategoryDataset) { IntervalCategoryDataset d = (IntervalCategoryDataset) dataset; drawInterval(g2, state, dataArea, plot, domainAxis, rangeAxis, d, row, column); } else { super.drawItem(g2, state, dataArea, plot, domainAxis, rangeAxis, dataset, row, column, pass); } } /** * Draws a single interval. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the data plot area. * @param plot the plot. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the data. * @param row the row index (zero-based). * @param column the column index (zero-based). */ protected void drawInterval(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, ValueAxis rangeAxis, IntervalCategoryDataset dataset, int row, int column) { int visibleRow = state.getVisibleSeriesIndex(row); if (visibleRow < 0) { return; } PlotOrientation orientation = plot.getOrientation(); double rectX = 0.0; double rectY = 0.0; RectangleEdge rangeAxisLocation = plot.getRangeAxisEdge(); // Y0 Number value0 = dataset.getEndValue(row, column); if (value0 == null) { return; } double java2dValue0 = rangeAxis.valueToJava2D(value0.doubleValue(), dataArea, rangeAxisLocation); // Y1 Number value1 = dataset.getStartValue(row, column); if (value1 == null) { return; } double java2dValue1 = rangeAxis.valueToJava2D( value1.doubleValue(), dataArea, rangeAxisLocation); if (java2dValue1 < java2dValue0) { double temp = java2dValue1; java2dValue1 = java2dValue0; java2dValue0 = temp; } // BAR WIDTH double rectWidth = state.getBarWidth(); // BAR HEIGHT double rectHeight = Math.abs(java2dValue1 - java2dValue0); RectangleEdge barBase = RectangleEdge.LEFT; if (orientation == PlotOrientation.HORIZONTAL) { // BAR Y rectX = java2dValue0; rectY = calculateBarW0(getPlot(), orientation, dataArea, domainAxis, state, visibleRow, column); rectHeight = state.getBarWidth(); rectWidth = Math.abs(java2dValue1 - java2dValue0); barBase = RectangleEdge.LEFT; } else if (orientation.isVertical()) { // BAR X rectX = calculateBarW0(getPlot(), orientation, dataArea, domainAxis, state, visibleRow, column); rectY = java2dValue0; barBase = RectangleEdge.BOTTOM; } Rectangle2D bar = new Rectangle2D.Double(rectX, rectY, rectWidth, rectHeight); BarPainter painter = getBarPainter(); if (state.getElementHinting()) { beginElementGroup(g2, dataset.getRowKey(row), dataset.getColumnKey(column)); } if (getShadowsVisible()) { painter.paintBarShadow(g2, this, row, column, bar, barBase, false); } getBarPainter().paintBar(g2, this, row, column, bar, barBase); if (state.getElementHinting()) { endElementGroup(g2); } CategoryItemLabelGenerator generator = getItemLabelGenerator(row, column); if (generator != null && isItemLabelVisible(row, column)) { drawItemLabel(g2, dataset, row, column, plot, generator, bar, false); } // add an item entity, if this information is being collected EntityCollection entities = state.getEntityCollection(); if (entities != null) { addItemEntity(entities, dataset, row, column, bar); } } /** * Tests this renderer for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof IntervalBarRenderer)) { return false; } // there are no fields to check return super.equals(obj); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/category/LayeredBarRenderer.java000066400000000000000000000376311463604235500330570ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * LayeredBarRenderer.java * ----------------------- * (C) Copyright 2003-present, by Arnaud Lelievre and Contributors. * * Original Author: Arnaud Lelievre (for Garden); * Contributor(s): David Gilbert; * Zoheb Borbora; * */ package org.jfree.chart.renderer.category; import java.awt.GradientPaint; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Stroke; import java.awt.geom.Rectangle2D; import java.io.Serializable; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.labels.CategoryItemLabelGenerator; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.ui.GradientPaintTransformer; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.ObjectList; import org.jfree.data.category.CategoryDataset; /** * A {@link CategoryItemRenderer} that represents data using bars which are * superimposed. The example shown here is generated by the * {@code LayeredBarChartDemo1.java} program included in the JFreeChart * Demo Collection: *

* LayeredBarRendererSample.png */ public class LayeredBarRenderer extends BarRenderer implements Serializable { /** For serialization. */ private static final long serialVersionUID = -8716572894780469487L; /** A list of the width of each series bar. */ protected ObjectList seriesBarWidthList; /** * Default constructor. */ public LayeredBarRenderer() { super(); this.seriesBarWidthList = new ObjectList(); } /** * Returns the bar width for a series, or {@code Double.NaN} if no * width has been set. * * @param series the series index (zero based). * * @return The width for the series (1.0=100%, it is the maximum). */ public double getSeriesBarWidth(int series) { double result = Double.NaN; Number n = (Number) this.seriesBarWidthList.get(series); if (n != null) { result = n.doubleValue(); } return result; } /** * Sets the width of the bars of a series. * * @param series the series index (zero based). * @param width the width of the series bar in percentage (1.0=100%, it is * the maximum). */ public void setSeriesBarWidth(int series, double width) { this.seriesBarWidthList.set(series, width); } /** * Calculates the bar width and stores it in the renderer state. * * @param plot the plot. * @param dataArea the data area. * @param rendererIndex the renderer index. * @param state the renderer state. */ @Override protected void calculateBarWidth(CategoryPlot plot, Rectangle2D dataArea, int rendererIndex, CategoryItemRendererState state) { // calculate the bar width - this calculation differs from the // BarRenderer calculation because the bars are layered on top of one // another, so there is effectively only one bar per category for // the purpose of the bar width calculation CategoryAxis domainAxis = getDomainAxis(plot, rendererIndex); CategoryDataset dataset = plot.getDataset(rendererIndex); if (dataset != null) { int columns = dataset.getColumnCount(); int rows = dataset.getRowCount(); double space = 0.0; PlotOrientation orientation = plot.getOrientation(); if (orientation == PlotOrientation.HORIZONTAL) { space = dataArea.getHeight(); } else if (orientation == PlotOrientation.VERTICAL) { space = dataArea.getWidth(); } double maxWidth = space * getMaximumBarWidth(); double categoryMargin = 0.0; if (columns > 1) { categoryMargin = domainAxis.getCategoryMargin(); } double used = space * (1 - domainAxis.getLowerMargin() - domainAxis.getUpperMargin() - categoryMargin); if ((rows * columns) > 0) { state.setBarWidth(Math.min(used / (dataset.getColumnCount()), maxWidth)); } else { state.setBarWidth(Math.min(used, maxWidth)); } } } /** * Draws the bar for one item in the dataset. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the plot area. * @param plot the plot. * @param domainAxis the domain (category) axis. * @param rangeAxis the range (value) axis. * @param data the data. * @param row the row index (zero-based). * @param column the column index (zero-based). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, ValueAxis rangeAxis, CategoryDataset data, int row, int column, int pass) { PlotOrientation orientation = plot.getOrientation(); if (orientation.isHorizontal()) { drawHorizontalItem(g2, state, dataArea, plot, domainAxis, rangeAxis, data, row, column); } else if (orientation.isVertical()) { drawVerticalItem(g2, state, dataArea, plot, domainAxis, rangeAxis, data, row, column); } } /** * Draws the bar for a single (series, category) data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the data area. * @param plot the plot. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset. * @param row the row index (zero-based). * @param column the column index (zero-based). */ protected void drawHorizontalItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, ValueAxis rangeAxis, CategoryDataset dataset, int row, int column) { // nothing is drawn for null values... Number dataValue = dataset.getValue(row, column); if (dataValue == null) { return; } // X double value = dataValue.doubleValue(); double base = getBase(); double lclip = getLowerClip(); double uclip = getUpperClip(); if (uclip <= 0.0) { // cases 1, 2, 3 and 4 if (value >= uclip) { return; // bar is not visible } base = uclip; if (value <= lclip) { value = lclip; } } else if (lclip <= 0.0) { // cases 5, 6, 7 and 8 if (value >= uclip) { value = uclip; } else { if (value <= lclip) { value = lclip; } } } else { // cases 9, 10, 11 and 12 if (value <= lclip) { return; // bar is not visible } base = lclip; if (value >= uclip) { value = uclip; } } RectangleEdge edge = plot.getRangeAxisEdge(); double transX1 = rangeAxis.valueToJava2D(base, dataArea, edge); double transX2 = rangeAxis.valueToJava2D(value, dataArea, edge); double rectX = Math.min(transX1, transX2); double rectWidth = Math.abs(transX2 - transX1); // Y double rectY = domainAxis.getCategoryMiddle(column, getColumnCount(), dataArea, plot.getDomainAxisEdge()) - state.getBarWidth() / 2.0; int seriesCount = getRowCount(); // draw the bar... double shift = 0.0; double rectHeight; double widthFactor = 1.0; double seriesBarWidth = getSeriesBarWidth(row); if (!Double.isNaN(seriesBarWidth)) { widthFactor = seriesBarWidth; } rectHeight = widthFactor * state.getBarWidth(); rectY = rectY + (1 - widthFactor) * state.getBarWidth() / 2.0; if (seriesCount > 1) { shift = rectHeight * 0.20 / (seriesCount - 1); } Rectangle2D bar = new Rectangle2D.Double(rectX, (rectY + ((seriesCount - 1 - row) * shift)), rectWidth, (rectHeight - (seriesCount - 1 - row) * shift * 2)); if (state.getElementHinting()) { beginElementGroup(g2, dataset.getRowKey(row), dataset.getColumnKey(column)); } Paint itemPaint = getItemPaint(row, column); GradientPaintTransformer t = getGradientPaintTransformer(); if (t != null && itemPaint instanceof GradientPaint) { itemPaint = t.transform((GradientPaint) itemPaint, bar); } g2.setPaint(itemPaint); g2.fill(bar); // draw the outline... if (isDrawBarOutline() && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) { Stroke stroke = getItemOutlineStroke(row, column); Paint paint = getItemOutlinePaint(row, column); if (stroke != null && paint != null) { g2.setStroke(stroke); g2.setPaint(paint); g2.draw(bar); } } CategoryItemLabelGenerator generator = getItemLabelGenerator(row, column); if (generator != null && isItemLabelVisible(row, column)) { drawItemLabel(g2, dataset, row, column, plot, generator, bar, value < base); } // collect entity and tool tip information... EntityCollection entities = state.getEntityCollection(); if (entities != null) { addItemEntity(entities, dataset, row, column, bar); } } /** * Draws the bar for a single (series, category) data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the data area. * @param plot the plot. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset. * @param row the row index (zero-based). * @param column the column index (zero-based). */ protected void drawVerticalItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, ValueAxis rangeAxis, CategoryDataset dataset, int row, int column) { // nothing is drawn for null values... Number dataValue = dataset.getValue(row, column); if (dataValue == null) { return; } // BAR X double rectX = domainAxis.getCategoryMiddle(column, getColumnCount(), dataArea, plot.getDomainAxisEdge()) - state.getBarWidth() / 2.0; int seriesCount = getRowCount(); // BAR Y double value = dataValue.doubleValue(); double base = getBase(); double lclip = getLowerClip(); double uclip = getUpperClip(); if (uclip <= 0.0) { // cases 1, 2, 3 and 4 if (value >= uclip) { return; // bar is not visible } base = uclip; if (value <= lclip) { value = lclip; } } else if (lclip <= 0.0) { // cases 5, 6, 7 and 8 if (value >= uclip) { value = uclip; } else { if (value <= lclip) { value = lclip; } } } else { // cases 9, 10, 11 and 12 if (value <= lclip) { return; // bar is not visible } base = getLowerClip(); if (value >= uclip) { value = uclip; } } RectangleEdge edge = plot.getRangeAxisEdge(); double transY1 = rangeAxis.valueToJava2D(base, dataArea, edge); double transY2 = rangeAxis.valueToJava2D(value, dataArea, edge); double rectY = Math.min(transY2, transY1); double rectWidth; double rectHeight = Math.abs(transY2 - transY1); // draw the bar... double shift = 0.0; double widthFactor = 1.0; double seriesBarWidth = getSeriesBarWidth(row); if (!Double.isNaN(seriesBarWidth)) { widthFactor = seriesBarWidth; } rectWidth = widthFactor * state.getBarWidth(); rectX = rectX + (1 - widthFactor) * state.getBarWidth() / 2.0; if (seriesCount > 1) { // needs to be improved !!! shift = rectWidth * 0.20 / (seriesCount - 1); } Rectangle2D bar = new Rectangle2D.Double( (rectX + ((seriesCount - 1 - row) * shift)), rectY, (rectWidth - (seriesCount - 1 - row) * shift * 2), rectHeight); if (state.getElementHinting()) { beginElementGroup(g2, dataset.getRowKey(row), dataset.getColumnKey(column)); } Paint itemPaint = getItemPaint(row, column); GradientPaintTransformer t = getGradientPaintTransformer(); if (t != null && itemPaint instanceof GradientPaint) { itemPaint = t.transform((GradientPaint) itemPaint, bar); } g2.setPaint(itemPaint); g2.fill(bar); if (isDrawBarOutline() && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) { g2.setStroke(getItemOutlineStroke(row, column)); g2.setPaint(getItemOutlinePaint(row, column)); g2.draw(bar); } if (state.getElementHinting()) { endElementGroup(g2); } // draw the item labels if there are any... CategoryItemLabelGenerator generator = getItemLabelGenerator(row, column); if (generator != null && isItemLabelVisible(row, column)) { drawItemLabel(g2, dataset, row, column, plot, generator, bar, value < base); } // collect entity and tool tip information... EntityCollection entities = state.getEntityCollection(); if (entities != null) { addItemEntity(entities, dataset, row, column, bar); } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/category/LevelRenderer.java000066400000000000000000000372651463604235500321170ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * LevelRenderer.java * ------------------ * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Peter Kolb (patch 2511330); * */ package org.jfree.chart.renderer.category; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Stroke; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import org.jfree.chart.HashUtils; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.labels.CategoryItemLabelGenerator; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.category.CategoryDataset; /** * A {@link CategoryItemRenderer} that draws individual data items as * horizontal lines, spaced in the same way as bars in a bar chart. The * example shown here is generated by the * {@code OverlaidBarChartDemo2.java} program included in the JFreeChart * Demo Collection: *

* LevelRendererSample.png */ public class LevelRenderer extends AbstractCategoryItemRenderer implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -8204856624355025117L; /** The default item margin percentage. */ public static final double DEFAULT_ITEM_MARGIN = 0.20; /** The margin between items within a category. */ private double itemMargin; /** The maximum item width as a percentage of the available space. */ private double maxItemWidth; /** * Creates a new renderer with default settings. */ public LevelRenderer() { super(); this.itemMargin = DEFAULT_ITEM_MARGIN; this.maxItemWidth = 1.0; // 100 percent, so it will not apply unless // changed setDefaultLegendShape(new Rectangle2D.Float(-5.0f, -1.0f, 10.0f, 2.0f)); // set the outline paint to fully transparent, then the legend shape // will just have the same colour as the lines drawn by the renderer setDefaultOutlinePaint(new Color(0, 0, 0, 0)); } /** * Returns the item margin. * * @return The margin. * * @see #setItemMargin(double) */ public double getItemMargin() { return this.itemMargin; } /** * Sets the item margin and sends a {@link RendererChangeEvent} to all * registered listeners. The value is expressed as a percentage of the * available width for plotting all the bars, with the resulting amount to * be distributed between all the bars evenly. * * @param percent the new margin. * * @see #getItemMargin() */ public void setItemMargin(double percent) { this.itemMargin = percent; fireChangeEvent(); } /** * Returns the maximum width, as a percentage of the available drawing * space. * * @return The maximum width. * * @see #setMaximumItemWidth(double) */ public double getMaximumItemWidth() { return this.maxItemWidth; } /** * Sets the maximum item width, which is specified as a percentage of the * available space for all items, and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param percent the percent. * * @see #getMaximumItemWidth() */ public void setMaximumItemWidth(double percent) { this.maxItemWidth = percent; fireChangeEvent(); } /** * Initialises the renderer and returns a state object that will be passed * to subsequent calls to the drawItem method. *

* This method gets called once at the start of the process of drawing a * chart. * * @param g2 the graphics device. * @param dataArea the area in which the data is to be plotted. * @param plot the plot. * @param rendererIndex the renderer index. * @param info collects chart rendering information for return to caller. * * @return The renderer state. */ @Override public CategoryItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, CategoryPlot plot, int rendererIndex, PlotRenderingInfo info) { CategoryItemRendererState state = super.initialise(g2, dataArea, plot, rendererIndex, info); calculateItemWidth(plot, dataArea, rendererIndex, state); return state; } /** * Calculates the bar width and stores it in the renderer state. * * @param plot the plot. * @param dataArea the data area. * @param rendererIndex the renderer index. * @param state the renderer state. */ protected void calculateItemWidth(CategoryPlot plot, Rectangle2D dataArea, int rendererIndex, CategoryItemRendererState state) { CategoryAxis domainAxis = getDomainAxis(plot, rendererIndex); CategoryDataset dataset = plot.getDataset(rendererIndex); if (dataset != null) { int columns = dataset.getColumnCount(); int rows = state.getVisibleSeriesCount() >= 0 ? state.getVisibleSeriesCount() : dataset.getRowCount(); double space = 0.0; PlotOrientation orientation = plot.getOrientation(); if (orientation == PlotOrientation.HORIZONTAL) { space = dataArea.getHeight(); } else if (orientation == PlotOrientation.VERTICAL) { space = dataArea.getWidth(); } double maxWidth = space * getMaximumItemWidth(); double categoryMargin = 0.0; double currentItemMargin = 0.0; if (columns > 1) { categoryMargin = domainAxis.getCategoryMargin(); } if (rows > 1) { currentItemMargin = getItemMargin(); } double used = space * (1 - domainAxis.getLowerMargin() - domainAxis.getUpperMargin() - categoryMargin - currentItemMargin); if ((rows * columns) > 0) { state.setBarWidth(Math.min(used / (rows * columns), maxWidth)); } else { state.setBarWidth(Math.min(used, maxWidth)); } } } /** * Calculates the coordinate of the first "side" of a bar. This will be * the minimum x-coordinate for a vertical bar, and the minimum * y-coordinate for a horizontal bar. * * @param plot the plot. * @param orientation the plot orientation. * @param dataArea the data area. * @param domainAxis the domain axis. * @param state the renderer state (has the bar width precalculated). * @param row the row index. * @param column the column index. * * @return The coordinate. */ protected double calculateBarW0(CategoryPlot plot, PlotOrientation orientation, Rectangle2D dataArea, CategoryAxis domainAxis, CategoryItemRendererState state, int row, int column) { // calculate bar width... double space; if (orientation.isHorizontal()) { space = dataArea.getHeight(); } else { space = dataArea.getWidth(); } double barW0 = domainAxis.getCategoryStart(column, getColumnCount(), dataArea, plot.getDomainAxisEdge()); int seriesCount = state.getVisibleSeriesCount(); if (seriesCount < 0) { seriesCount = getRowCount(); } int categoryCount = getColumnCount(); if (seriesCount > 1) { double seriesGap = space * getItemMargin() / (categoryCount * (seriesCount - 1)); double seriesW = calculateSeriesWidth(space, domainAxis, categoryCount, seriesCount); barW0 = barW0 + row * (seriesW + seriesGap) + (seriesW / 2.0) - (state.getBarWidth() / 2.0); } else { barW0 = domainAxis.getCategoryMiddle(column, getColumnCount(), dataArea, plot.getDomainAxisEdge()) - state.getBarWidth() / 2.0; } return barW0; } /** * Draws the bar for a single (series, category) data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the data area. * @param plot the plot. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset. * @param row the row index (zero-based). * @param column the column index (zero-based). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, ValueAxis rangeAxis, CategoryDataset dataset, int row, int column, int pass) { // nothing is drawn if the row index is not included in the list with // the indices of the visible rows... int visibleRow = state.getVisibleSeriesIndex(row); if (visibleRow < 0) { return; } // nothing is drawn for null values... Number dataValue = dataset.getValue(row, column); if (dataValue == null) { return; } double value = dataValue.doubleValue(); PlotOrientation orientation = plot.getOrientation(); double barW0 = calculateBarW0(plot, orientation, dataArea, domainAxis, state, visibleRow, column); RectangleEdge edge = plot.getRangeAxisEdge(); double barL = rangeAxis.valueToJava2D(value, dataArea, edge); // draw the bar... Line2D line; double x, y; if (orientation.isHorizontal()) { x = barL; y = barW0 + state.getBarWidth() / 2.0; line = new Line2D.Double(barL, barW0, barL, barW0 + state.getBarWidth()); } else { x = barW0 + state.getBarWidth() / 2.0; y = barL; line = new Line2D.Double(barW0, barL, barW0 + state.getBarWidth(), barL); } if (state.getElementHinting()) { beginElementGroup(g2, dataset.getRowKey(row), dataset.getColumnKey(column)); } Stroke itemStroke = getItemStroke(row, column); Paint itemPaint = getItemPaint(row, column); g2.setStroke(itemStroke); g2.setPaint(itemPaint); g2.draw(line); if (state.getElementHinting()) { endElementGroup(g2); } CategoryItemLabelGenerator generator = getItemLabelGenerator(row, column); if (generator != null && isItemLabelVisible(row, column)) { drawItemLabel(g2, orientation, dataset, row, column, x, y, (value < 0.0)); } // submit the current data point as a crosshair candidate int datasetIndex = plot.indexOf(dataset); updateCrosshairValues(state.getCrosshairState(), dataset.getRowKey(row), dataset.getColumnKey(column), value, datasetIndex, barW0, barL, orientation); // collect entity and tool tip information... EntityCollection entities = state.getEntityCollection(); if (entities != null) { addItemEntity(entities, dataset, row, column, line.getBounds()); } } /** * Calculates the available space for each series. * * @param space the space along the entire axis (in Java2D units). * @param axis the category axis. * @param categories the number of categories. * @param series the number of series. * * @return The width of one series. */ protected double calculateSeriesWidth(double space, CategoryAxis axis, int categories, int series) { double factor = 1.0 - getItemMargin() - axis.getLowerMargin() - axis.getUpperMargin(); if (categories > 1) { factor = factor - axis.getCategoryMargin(); } return (space * factor) / (categories * series); } /** * Returns the Java2D coordinate for the middle of the specified data item. * * @param rowKey the row key. * @param columnKey the column key. * @param dataset the dataset. * @param axis the axis. * @param area the drawing area. * @param edge the edge along which the axis lies. * * @return The Java2D coordinate. */ @Override public double getItemMiddle(Comparable rowKey, Comparable columnKey, CategoryDataset dataset, CategoryAxis axis, Rectangle2D area, RectangleEdge edge) { return axis.getCategorySeriesMiddle(columnKey, rowKey, dataset, this.itemMargin, area, edge); } /** * Tests an object for equality with this instance. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof LevelRenderer)) { return false; } LevelRenderer that = (LevelRenderer) obj; if (this.itemMargin != that.itemMargin) { return false; } if (this.maxItemWidth != that.maxItemWidth) { return false; } return super.equals(obj); } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int hash = super.hashCode(); hash = HashUtils.hashCode(hash, this.itemMargin); hash = HashUtils.hashCode(hash, this.maxItemWidth); return hash; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/category/LineAndShapeRenderer.java000066400000000000000000000701071463604235500333330ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * LineAndShapeRenderer.java * ------------------------- * (C) Copyright 2001-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Mark Watson (www.markwatson.com); * Jeremy Bowman; * Richard Atkinson; * Christian W. Zuckschwerdt; * Peter Kolb (patch 2497611); * */ package org.jfree.chart.renderer.category; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.LegendItem; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.util.BooleanList; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.ShapeUtils; import org.jfree.data.category.CategoryDataset; /** * A renderer that draws shapes for each data item, and lines between data * items (for use with the {@link CategoryPlot} class). * The example shown here is generated by the {@code LineChartDemo1.java} * program included in the JFreeChart Demo Collection: *

* LineAndShapeRendererSample.png */ public class LineAndShapeRenderer extends AbstractCategoryItemRenderer implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -197749519869226398L; /** * A table of flags that control (per series) whether or not lines are * visible. */ private BooleanList seriesLinesVisible; /** * A flag indicating whether or not lines are drawn between non-null * points. */ private boolean defaultLinesVisible; /** * A table of flags that control (per series) whether or not shapes are * visible. */ private BooleanList seriesShapesVisible; /** The default value returned by the getShapeVisible() method. */ private boolean defaultShapesVisible; /** * A table of flags that control (per series) whether or not shapes are * filled. */ private BooleanList seriesShapesFilled; /** The default value returned by the getShapeFilled() method. */ private boolean defaultShapesFilled; /** * A flag that controls whether the fill paint is used for filling * shapes. */ private boolean useFillPaint; /** A flag that controls whether outlines are drawn for shapes. */ private boolean drawOutlines; /** * A flag that controls whether the outline paint is used for drawing shape * outlines - if not, the regular series paint is used. */ private boolean useOutlinePaint; /** * A flag that controls whether or not the x-position for each item is * offset within the category according to the series. */ private boolean useSeriesOffset; /** * The item margin used for series offsetting - this allows the positioning * to match the bar positions of the {@link BarRenderer} class. */ private double itemMargin; /** * Creates a renderer with both lines and shapes visible by default. */ public LineAndShapeRenderer() { this(true, true); } /** * Creates a new renderer with lines and/or shapes visible. * * @param lines draw lines? * @param shapes draw shapes? */ public LineAndShapeRenderer(boolean lines, boolean shapes) { super(); this.seriesLinesVisible = new BooleanList(); this.defaultLinesVisible = lines; this.seriesShapesVisible = new BooleanList(); this.defaultShapesVisible = shapes; this.seriesShapesFilled = new BooleanList(); this.defaultShapesFilled = true; this.useFillPaint = false; this.drawOutlines = true; this.useOutlinePaint = false; this.useSeriesOffset = false; // preserves old behaviour this.itemMargin = 0.0; } // LINES VISIBLE /** * Returns the flag used to control whether or not the line for an item is * visible. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return A boolean. */ public boolean getItemLineVisible(int series, int item) { Boolean flag = getSeriesLinesVisible(series); if (flag != null) { return flag; } return this.defaultLinesVisible; } /** * Returns the flag used to control whether or not the lines for a series * are visible. * * @param series the series index (zero-based). * * @return The flag (possibly {@code null}). * * @see #setSeriesLinesVisible(int, Boolean) */ public Boolean getSeriesLinesVisible(int series) { return this.seriesLinesVisible.getBoolean(series); } /** * Sets the 'lines visible' flag for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param flag the flag ({@code null} permitted). * * @see #getSeriesLinesVisible(int) */ public void setSeriesLinesVisible(int series, Boolean flag) { this.seriesLinesVisible.setBoolean(series, flag); fireChangeEvent(); } /** * Sets the 'lines visible' flag for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param visible the flag. * * @see #getSeriesLinesVisible(int) */ public void setSeriesLinesVisible(int series, boolean visible) { setSeriesLinesVisible(series, Boolean.valueOf(visible)); } /** * Returns the default 'lines visible' attribute. * * @return The default flag. * * @see #getDefaultLinesVisible() */ public boolean getDefaultLinesVisible() { return this.defaultLinesVisible; } /** * Sets the default 'lines visible' flag and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param flag the flag. * * @see #getDefaultLinesVisible() */ public void setDefaultLinesVisible(boolean flag) { this.defaultLinesVisible = flag; fireChangeEvent(); } // SHAPES VISIBLE /** * Returns the flag used to control whether or not the shape for an item is * visible. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return A boolean. */ public boolean getItemShapeVisible(int series, int item) { Boolean flag = getSeriesShapesVisible(series); if (flag != null) { return flag; } return this.defaultShapesVisible; } /** * Returns the flag used to control whether or not the shapes for a series * are visible. * * @param series the series index (zero-based). * * @return A boolean. * * @see #setSeriesShapesVisible(int, Boolean) */ public Boolean getSeriesShapesVisible(int series) { return this.seriesShapesVisible.getBoolean(series); } /** * Sets the 'shapes visible' flag for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param visible the flag. * * @see #getSeriesShapesVisible(int) */ public void setSeriesShapesVisible(int series, boolean visible) { setSeriesShapesVisible(series, Boolean.valueOf(visible)); } /** * Sets the 'shapes visible' flag for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param flag the flag. * * @see #getSeriesShapesVisible(int) */ public void setSeriesShapesVisible(int series, Boolean flag) { this.seriesShapesVisible.setBoolean(series, flag); fireChangeEvent(); } /** * Returns the default 'shape visible' attribute. * * @return The base flag. * * @see #setDefaultShapesVisible(boolean) */ public boolean getDefaultShapesVisible() { return this.defaultShapesVisible; } /** * Sets the default 'shapes visible' flag and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param flag the flag. * * @see #getDefaultShapesVisible() */ public void setDefaultShapesVisible(boolean flag) { this.defaultShapesVisible = flag; fireChangeEvent(); } /** * Returns {@code true} if outlines should be drawn for shapes, and * {@code false} otherwise. * * @return A boolean. * * @see #setDrawOutlines(boolean) */ public boolean getDrawOutlines() { return this.drawOutlines; } /** * Sets the flag that controls whether outlines are drawn for * shapes, and sends a {@link RendererChangeEvent} to all registered * listeners. *

* In some cases, shapes look better if they do NOT have an outline, but * this flag allows you to set your own preference. * * @param flag the flag. * * @see #getDrawOutlines() */ public void setDrawOutlines(boolean flag) { this.drawOutlines = flag; fireChangeEvent(); } /** * Returns the flag that controls whether the outline paint is used for * shape outlines. If not, the regular series paint is used. * * @return A boolean. * * @see #setUseOutlinePaint(boolean) */ public boolean getUseOutlinePaint() { return this.useOutlinePaint; } /** * Sets the flag that controls whether the outline paint is used for shape * outlines, and sends a {@link RendererChangeEvent} to all registered * listeners. * * @param use the flag. * * @see #getUseOutlinePaint() */ public void setUseOutlinePaint(boolean use) { this.useOutlinePaint = use; fireChangeEvent(); } // SHAPES FILLED /** * Returns the flag used to control whether or not the shape for an item * is filled. The default implementation passes control to the * {@code getSeriesShapesFilled} method. You can override this method * if you require different behaviour. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return A boolean. */ public boolean getItemShapeFilled(int series, int item) { return getSeriesShapesFilled(series); } /** * Returns the flag used to control whether or not the shapes for a series * are filled. * * @param series the series index (zero-based). * * @return A boolean. */ public boolean getSeriesShapesFilled(int series) { Boolean flag = this.seriesShapesFilled.getBoolean(series); if (flag != null) { return flag; } return this.defaultShapesFilled; } /** * Sets the 'shapes filled' flag for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param filled the flag. * * @see #getSeriesShapesFilled(int) */ public void setSeriesShapesFilled(int series, Boolean filled) { this.seriesShapesFilled.setBoolean(series, filled); fireChangeEvent(); } /** * Sets the 'shapes filled' flag for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param filled the flag. * * @see #getSeriesShapesFilled(int) */ public void setSeriesShapesFilled(int series, boolean filled) { // delegate setSeriesShapesFilled(series, Boolean.valueOf(filled)); } /** * Returns the default 'shape filled' attribute. * * @return The base flag. * * @see #setDefaultShapesFilled(boolean) */ public boolean getDefaultShapesFilled() { return this.defaultShapesFilled; } /** * Sets the default 'shapes filled' flag and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param flag the flag. * * @see #getDefaultShapesFilled() */ public void setDefaultShapesFilled(boolean flag) { this.defaultShapesFilled = flag; fireChangeEvent(); } /** * Returns {@code true} if the renderer should use the fill paint * setting to fill shapes, and {@code false} if it should just * use the regular paint. * * @return A boolean. * * @see #setUseFillPaint(boolean) */ public boolean getUseFillPaint() { return this.useFillPaint; } /** * Sets the flag that controls whether the fill paint is used to fill * shapes, and sends a {@link RendererChangeEvent} to all * registered listeners. * * @param flag the flag. * * @see #getUseFillPaint() */ public void setUseFillPaint(boolean flag) { this.useFillPaint = flag; fireChangeEvent(); } /** * Returns the flag that controls whether or not the x-position for each * data item is offset within the category according to the series. * * @return A boolean. * * @see #setUseSeriesOffset(boolean) */ public boolean getUseSeriesOffset() { return this.useSeriesOffset; } /** * Sets the flag that controls whether or not the x-position for each * data item is offset within its category according to the series, and * sends a {@link RendererChangeEvent} to all registered listeners. * * @param offset the offset. * * @see #getUseSeriesOffset() */ public void setUseSeriesOffset(boolean offset) { this.useSeriesOffset = offset; fireChangeEvent(); } /** * Returns the item margin, which is the gap between items within a * category (expressed as a percentage of the overall category width). * This can be used to match the offset alignment with the bars drawn by * a {@link BarRenderer}). * * @return The item margin. * * @see #setItemMargin(double) * @see #getUseSeriesOffset() */ public double getItemMargin() { return this.itemMargin; } /** * Sets the item margin, which is the gap between items within a category * (expressed as a percentage of the overall category width), and sends * a {@link RendererChangeEvent} to all registered listeners. * * @param margin the margin (0.0 <= margin < 1.0). * * @see #getItemMargin() * @see #getUseSeriesOffset() */ public void setItemMargin(double margin) { if (margin < 0.0 || margin >= 1.0) { throw new IllegalArgumentException("Requires 0.0 <= margin < 1.0."); } this.itemMargin = margin; fireChangeEvent(); } /** * Returns a legend item for a series. * * @param datasetIndex the dataset index (zero-based). * @param series the series index (zero-based). * * @return The legend item. */ @Override public LegendItem getLegendItem(int datasetIndex, int series) { CategoryPlot cp = getPlot(); if (cp == null) { return null; } if (isSeriesVisible(series) && isSeriesVisibleInLegend(series)) { CategoryDataset dataset = cp.getDataset(datasetIndex); String label = getLegendItemLabelGenerator().generateLabel( dataset, series); String description = label; String toolTipText = null; if (getLegendItemToolTipGenerator() != null) { toolTipText = getLegendItemToolTipGenerator().generateLabel( dataset, series); } String urlText = null; if (getLegendItemURLGenerator() != null) { urlText = getLegendItemURLGenerator().generateLabel( dataset, series); } Shape shape = lookupLegendShape(series); Paint paint = lookupSeriesPaint(series); Paint fillPaint = (this.useFillPaint ? getItemFillPaint(series, 0) : paint); boolean shapeOutlineVisible = this.drawOutlines; Paint outlinePaint = (this.useOutlinePaint ? getItemOutlinePaint(series, 0) : paint); Stroke outlineStroke = lookupSeriesOutlineStroke(series); boolean lineVisible = getItemLineVisible(series, 0); boolean shapeVisible = getItemShapeVisible(series, 0); LegendItem result = new LegendItem(label, description, toolTipText, urlText, shapeVisible, shape, getItemShapeFilled(series, 0), fillPaint, shapeOutlineVisible, outlinePaint, outlineStroke, lineVisible, new Line2D.Double(-7.0, 0.0, 7.0, 0.0), getItemStroke(series, 0), getItemPaint(series, 0)); result.setLabelFont(lookupLegendTextFont(series)); Paint labelPaint = lookupLegendTextPaint(series); if (labelPaint != null) { result.setLabelPaint(labelPaint); } result.setDataset(dataset); result.setDatasetIndex(datasetIndex); result.setSeriesKey(dataset.getRowKey(series)); result.setSeriesIndex(series); return result; } return null; } /** * This renderer uses two passes to draw the data. * * @return The pass count ({@code 2} for this renderer). */ @Override public int getPassCount() { return 2; } /** * Draw a single data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the area in which the data is drawn. * @param plot the plot. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset. * @param row the row index (zero-based). * @param column the column index (zero-based). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, ValueAxis rangeAxis, CategoryDataset dataset, int row, int column, int pass) { // do nothing if item is not visible if (!getItemVisible(row, column)) { return; } // do nothing if both the line and shape are not visible if (!getItemLineVisible(row, column) && !getItemShapeVisible(row, column)) { return; } // nothing is drawn for null... Number v = dataset.getValue(row, column); if (v == null) { return; } int visibleRow = state.getVisibleSeriesIndex(row); if (visibleRow < 0) { return; } int visibleRowCount = state.getVisibleSeriesCount(); PlotOrientation orientation = plot.getOrientation(); // current data point... double x1; if (this.useSeriesOffset) { x1 = domainAxis.getCategorySeriesMiddle(column, dataset.getColumnCount(), visibleRow, visibleRowCount, this.itemMargin, dataArea, plot.getDomainAxisEdge()); } else { x1 = domainAxis.getCategoryMiddle(column, getColumnCount(), dataArea, plot.getDomainAxisEdge()); } double value = v.doubleValue(); double y1 = rangeAxis.valueToJava2D(value, dataArea, plot.getRangeAxisEdge()); if (pass == 0 && getItemLineVisible(row, column)) { if (column != 0) { Number previousValue = dataset.getValue(row, column - 1); if (previousValue != null) { // previous data point... double previous = previousValue.doubleValue(); double x0; if (this.useSeriesOffset) { x0 = domainAxis.getCategorySeriesMiddle( column - 1, dataset.getColumnCount(), visibleRow, visibleRowCount, this.itemMargin, dataArea, plot.getDomainAxisEdge()); } else { x0 = domainAxis.getCategoryMiddle(column - 1, getColumnCount(), dataArea, plot.getDomainAxisEdge()); } double y0 = rangeAxis.valueToJava2D(previous, dataArea, plot.getRangeAxisEdge()); Line2D line = null; if (orientation == PlotOrientation.HORIZONTAL) { line = new Line2D.Double(y0, x0, y1, x1); } else if (orientation == PlotOrientation.VERTICAL) { line = new Line2D.Double(x0, y0, x1, y1); } g2.setPaint(getItemPaint(row, column)); g2.setStroke(getItemStroke(row, column)); g2.draw(line); } } } if (pass == 1) { Shape shape = getItemShape(row, column); if (orientation == PlotOrientation.HORIZONTAL) { shape = ShapeUtils.createTranslatedShape(shape, y1, x1); } else if (orientation == PlotOrientation.VERTICAL) { shape = ShapeUtils.createTranslatedShape(shape, x1, y1); } if (getItemShapeVisible(row, column)) { if (getItemShapeFilled(row, column)) { if (this.useFillPaint) { g2.setPaint(getItemFillPaint(row, column)); } else { g2.setPaint(getItemPaint(row, column)); } g2.fill(shape); } if (this.drawOutlines) { if (this.useOutlinePaint) { g2.setPaint(getItemOutlinePaint(row, column)); } else { g2.setPaint(getItemPaint(row, column)); } g2.setStroke(getItemOutlineStroke(row, column)); g2.draw(shape); } } // draw the item label if there is one... if (isItemLabelVisible(row, column)) { if (orientation == PlotOrientation.HORIZONTAL) { drawItemLabel(g2, orientation, dataset, row, column, y1, x1, (value < 0.0)); } else if (orientation == PlotOrientation.VERTICAL) { drawItemLabel(g2, orientation, dataset, row, column, x1, y1, (value < 0.0)); } } // submit the current data point as a crosshair candidate int datasetIndex = plot.indexOf(dataset); updateCrosshairValues(state.getCrosshairState(), dataset.getRowKey(row), dataset.getColumnKey(column), value, datasetIndex, x1, y1, orientation); // add an item entity, if this information is being collected EntityCollection entities = state.getEntityCollection(); if (entities != null) { addItemEntity(entities, dataset, row, column, shape); } } } /** * Tests this renderer for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof LineAndShapeRenderer)) { return false; } LineAndShapeRenderer that = (LineAndShapeRenderer) obj; if (this.defaultLinesVisible != that.defaultLinesVisible) { return false; } if (!Objects.equals(this.seriesLinesVisible, that.seriesLinesVisible)) { return false; } if (this.defaultShapesVisible != that.defaultShapesVisible) { return false; } if (!Objects.equals(this.seriesShapesVisible, that.seriesShapesVisible)) { return false; } if (!Objects.equals(this.seriesShapesFilled, that.seriesShapesFilled)) { return false; } if (this.defaultShapesFilled != that.defaultShapesFilled) { return false; } if (this.useOutlinePaint != that.useOutlinePaint) { return false; } if (this.useSeriesOffset != that.useSeriesOffset) { return false; } if (this.itemMargin != that.itemMargin) { return false; } return super.equals(obj); } /** * Returns an independent copy of the renderer. * * @return A clone. * * @throws CloneNotSupportedException should not happen. */ @Override public Object clone() throws CloneNotSupportedException { LineAndShapeRenderer clone = (LineAndShapeRenderer) super.clone(); clone.seriesLinesVisible = (BooleanList) this.seriesLinesVisible.clone(); clone.seriesShapesVisible = (BooleanList) this.seriesShapesVisible.clone(); clone.seriesShapesFilled = (BooleanList) this.seriesShapesFilled.clone(); return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/category/MinMaxCategoryRenderer.java000066400000000000000000000444111463604235500337260ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * MinMaxCategoryRenderer.java * --------------------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: Tomer Peretz; * Contributor(s): David Gilbert; * Christian W. Zuckschwerdt; * Nicolas Brodu (for Astrium and EADS Corporate Research * Center); */ package org.jfree.chart.renderer.category; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Component; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.AffineTransform; import java.awt.geom.Arc2D; import java.awt.geom.GeneralPath; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import javax.swing.Icon; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.SerialUtils; import org.jfree.data.category.CategoryDataset; /** * Renderer for drawing min max plot. This renderer draws all the series under * the same category in the same x position using {@code objectIcon} and * a line from the maximum value to the minimum value. For use with the * {@link CategoryPlot} class. The example shown here is generated by * the {@code MinMaxCategoryPlotDemo1.java} program included in the * JFreeChart Demo Collection: *

* MinMaxCategoryRendererSample.png */ public class MinMaxCategoryRenderer extends AbstractCategoryItemRenderer { /** For serialization. */ private static final long serialVersionUID = 2935615937671064911L; /** A flag indicating whether or not lines are drawn between XY points. */ private boolean plotLines = false; /** * The paint of the line between the minimum value and the maximum value. */ private transient Paint groupPaint = Color.BLACK; /** * The stroke of the line between the minimum value and the maximum value. */ private transient Stroke groupStroke = new BasicStroke(1.0f); /** The icon used to indicate the minimum value.*/ private transient Icon minIcon = getIcon(new Arc2D.Double(-4, -4, 8, 8, 0, 360, Arc2D.OPEN), null, Color.BLACK); /** The icon used to indicate the maximum value.*/ private transient Icon maxIcon = getIcon(new Arc2D.Double(-4, -4, 8, 8, 0, 360, Arc2D.OPEN), null, Color.BLACK); /** The icon used to indicate the values.*/ private transient Icon objectIcon = getIcon(new Line2D.Double(-4, 0, 4, 0), false, true); /** The last category. */ private int lastCategory = -1; /** The minimum. */ private double min; /** The maximum. */ private double max; /** * Default constructor. */ public MinMaxCategoryRenderer() { super(); } /** * Gets whether or not lines are drawn between category points. * * @return boolean true if line will be drawn between sequenced categories, * otherwise false. * * @see #setDrawLines(boolean) */ public boolean isDrawLines() { return this.plotLines; } /** * Sets the flag that controls whether or not lines are drawn to connect * the items within a series and sends a {@link RendererChangeEvent} to * all registered listeners. * * @param draw the new value of the flag. * * @see #isDrawLines() */ public void setDrawLines(boolean draw) { if (this.plotLines != draw) { this.plotLines = draw; fireChangeEvent(); } } /** * Returns the paint used to draw the line between the minimum and maximum * value items in each category. * * @return The paint (never {@code null}). * * @see #setGroupPaint(Paint) */ public Paint getGroupPaint() { return this.groupPaint; } /** * Sets the paint used to draw the line between the minimum and maximum * value items in each category and sends a {@link RendererChangeEvent} to * all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getGroupPaint() */ public void setGroupPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.groupPaint = paint; fireChangeEvent(); } /** * Returns the stroke used to draw the line between the minimum and maximum * value items in each category. * * @return The stroke (never {@code null}). * * @see #setGroupStroke(Stroke) */ public Stroke getGroupStroke() { return this.groupStroke; } /** * Sets the stroke of the line between the minimum value and the maximum * value and sends a {@link RendererChangeEvent} to all registered * listeners. * * @param stroke the new stroke ({@code null} not permitted). */ public void setGroupStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.groupStroke = stroke; fireChangeEvent(); } /** * Returns the icon drawn for each data item. * * @return The icon (never {@code null}). * * @see #setObjectIcon(Icon) */ public Icon getObjectIcon() { return this.objectIcon; } /** * Sets the icon drawn for each data item and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param icon the icon. * * @see #getObjectIcon() */ public void setObjectIcon(Icon icon) { Args.nullNotPermitted(icon, "icon"); this.objectIcon = icon; fireChangeEvent(); } /** * Returns the icon displayed for the maximum value data item within each * category. * * @return The icon (never {@code null}). * * @see #setMaxIcon(Icon) */ public Icon getMaxIcon() { return this.maxIcon; } /** * Sets the icon displayed for the maximum value data item within each * category and sends a {@link RendererChangeEvent} to all registered * listeners. * * @param icon the icon ({@code null} not permitted). * * @see #getMaxIcon() */ public void setMaxIcon(Icon icon) { Args.nullNotPermitted(icon, "icon"); this.maxIcon = icon; fireChangeEvent(); } /** * Returns the icon displayed for the minimum value data item within each * category. * * @return The icon (never {@code null}). * * @see #setMinIcon(Icon) */ public Icon getMinIcon() { return this.minIcon; } /** * Sets the icon displayed for the minimum value data item within each * category and sends a {@link RendererChangeEvent} to all registered * listeners. * * @param icon the icon ({@code null} not permitted). * * @see #getMinIcon() */ public void setMinIcon(Icon icon) { Args.nullNotPermitted(icon, "icon"); this.minIcon = icon; fireChangeEvent(); } /** * Draw a single data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the area in which the data is drawn. * @param plot the plot. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset. * @param row the row index (zero-based). * @param column the column index (zero-based). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, ValueAxis rangeAxis, CategoryDataset dataset, int row, int column, int pass) { // first check the number we are plotting... Number value = dataset.getValue(row, column); if (value != null) { // current data point... double x1 = domainAxis.getCategoryMiddle(column, getColumnCount(), dataArea, plot.getDomainAxisEdge()); double y1 = rangeAxis.valueToJava2D(value.doubleValue(), dataArea, plot.getRangeAxisEdge()); Shape hotspot = new Rectangle2D.Double(x1 - 4, y1 - 4, 8.0, 8.0); g2.setPaint(getItemPaint(row, column)); g2.setStroke(getItemStroke(row, column)); PlotOrientation orient = plot.getOrientation(); if (orient == PlotOrientation.VERTICAL) { this.objectIcon.paintIcon(null, g2, (int) x1, (int) y1); } else { this.objectIcon.paintIcon(null, g2, (int) y1, (int) x1); } if (this.lastCategory == column) { if (this.min > value.doubleValue()) { this.min = value.doubleValue(); } if (this.max < value.doubleValue()) { this.max = value.doubleValue(); } // last series, so we are ready to draw the min and max if (dataset.getRowCount() - 1 == row) { g2.setPaint(this.groupPaint); g2.setStroke(this.groupStroke); double minY = rangeAxis.valueToJava2D(this.min, dataArea, plot.getRangeAxisEdge()); double maxY = rangeAxis.valueToJava2D(this.max, dataArea, plot.getRangeAxisEdge()); if (orient == PlotOrientation.VERTICAL) { g2.draw(new Line2D.Double(x1, minY, x1, maxY)); this.minIcon.paintIcon(null, g2, (int) x1, (int) minY); this.maxIcon.paintIcon(null, g2, (int) x1, (int) maxY); } else { g2.draw(new Line2D.Double(minY, x1, maxY, x1)); this.minIcon.paintIcon(null, g2, (int) minY, (int) x1); this.maxIcon.paintIcon(null, g2, (int) maxY, (int) x1); } } } else { // reset the min and max this.lastCategory = column; this.min = value.doubleValue(); this.max = value.doubleValue(); } // connect to the previous point if (this.plotLines) { if (column != 0) { Number previousValue = dataset.getValue(row, column - 1); if (previousValue != null) { // previous data point... double previous = previousValue.doubleValue(); double x0 = domainAxis.getCategoryMiddle(column - 1, getColumnCount(), dataArea, plot.getDomainAxisEdge()); double y0 = rangeAxis.valueToJava2D(previous, dataArea, plot.getRangeAxisEdge()); g2.setPaint(getItemPaint(row, column)); g2.setStroke(getItemStroke(row, column)); Line2D line; if (orient == PlotOrientation.VERTICAL) { line = new Line2D.Double(x0, y0, x1, y1); } else { line = new Line2D.Double(y0, x0, y1, x1); } g2.draw(line); } } } // add an item entity, if this information is being collected EntityCollection entities = state.getEntityCollection(); if (entities != null) { addItemEntity(entities, dataset, row, column, hotspot); } } } /** * Tests this instance for equality with an arbitrary object. The icon * fields are NOT included in the test, so this implementation is a little * weak. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof MinMaxCategoryRenderer)) { return false; } MinMaxCategoryRenderer that = (MinMaxCategoryRenderer) obj; if (this.plotLines != that.plotLines) { return false; } if (!PaintUtils.equal(this.groupPaint, that.groupPaint)) { return false; } if (!this.groupStroke.equals(that.groupStroke)) { return false; } return super.equals(obj); } /** * Returns an icon. * * @param shape the shape. * @param fillPaint the fill paint. * @param outlinePaint the outline paint. * * @return The icon. */ private Icon getIcon(Shape shape, final Paint fillPaint, final Paint outlinePaint) { final int width = shape.getBounds().width; final int height = shape.getBounds().height; final GeneralPath path = new GeneralPath(shape); return new Icon() { @Override public void paintIcon(Component c, Graphics g, int x, int y) { Graphics2D g2 = (Graphics2D) g; path.transform(AffineTransform.getTranslateInstance(x, y)); if (fillPaint != null) { g2.setPaint(fillPaint); g2.fill(path); } if (outlinePaint != null) { g2.setPaint(outlinePaint); g2.draw(path); } path.transform(AffineTransform.getTranslateInstance(-x, -y)); } @Override public int getIconWidth() { return width; } @Override public int getIconHeight() { return height; } }; } /** * Returns an icon from a shape. * * @param shape the shape. * @param fill the fill flag. * @param outline the outline flag. * * @return The icon. */ private Icon getIcon(Shape shape, final boolean fill, final boolean outline) { final int width = shape.getBounds().width; final int height = shape.getBounds().height; final GeneralPath path = new GeneralPath(shape); return new Icon() { @Override public void paintIcon(Component c, Graphics g, int x, int y) { Graphics2D g2 = (Graphics2D) g; path.transform(AffineTransform.getTranslateInstance(x, y)); if (fill) { g2.fill(path); } if (outline) { g2.draw(path); } path.transform(AffineTransform.getTranslateInstance(-x, -y)); } @Override public int getIconWidth() { return width; } @Override public int getIconHeight() { return height; } }; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeStroke(this.groupStroke, stream); SerialUtils.writePaint(this.groupPaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.groupStroke = SerialUtils.readStroke(stream); this.groupPaint = SerialUtils.readPaint(stream); this.minIcon = getIcon(new Arc2D.Double(-4, -4, 8, 8, 0, 360, Arc2D.OPEN), null, Color.BLACK); this.maxIcon = getIcon(new Arc2D.Double(-4, -4, 8, 8, 0, 360, Arc2D.OPEN), null, Color.BLACK); this.objectIcon = getIcon(new Line2D.Double(-4, 0, 4, 0), false, true); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/category/ScatterRenderer.java000066400000000000000000000463261463604235500324530ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * ScatterRenderer.java * -------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): David Forslund; * Peter Kolb (patches 2497611, 2791407); * */ package org.jfree.chart.renderer.category; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.List; import java.util.Objects; import org.jfree.chart.LegendItem; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.util.BooleanList; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.ShapeUtils; import org.jfree.data.Range; import org.jfree.data.category.CategoryDataset; import org.jfree.data.statistics.MultiValueCategoryDataset; /** * A renderer that handles the multiple values from a * {@link MultiValueCategoryDataset} by plotting a shape for each value for * each given item in the dataset. The example shown here is generated by * the {@code ScatterRendererDemo1.java} program included in the * JFreeChart Demo Collection: *

* ScatterRendererSample.png */ public class ScatterRenderer extends AbstractCategoryItemRenderer implements Cloneable, PublicCloneable, Serializable { /** * A table of flags that control (per series) whether or not shapes are * filled. */ private BooleanList seriesShapesFilled; /** * The default value returned by the getShapeFilled() method. */ private boolean baseShapesFilled; /** * A flag that controls whether the fill paint is used for filling * shapes. */ private boolean useFillPaint; /** * A flag that controls whether outlines are drawn for shapes. */ private boolean drawOutlines; /** * A flag that controls whether the outline paint is used for drawing shape * outlines - if not, the regular series paint is used. */ private boolean useOutlinePaint; /** * A flag that controls whether or not the x-position for each item is * offset within the category according to the series. */ private boolean useSeriesOffset; /** * The item margin used for series offsetting - this allows the positioning * to match the bar positions of the {@link BarRenderer} class. */ private double itemMargin; /** * Constructs a new renderer. */ public ScatterRenderer() { this.seriesShapesFilled = new BooleanList(); this.baseShapesFilled = true; this.useFillPaint = false; this.drawOutlines = false; this.useOutlinePaint = false; this.useSeriesOffset = true; this.itemMargin = 0.20; } /** * Returns the flag that controls whether or not the x-position for each * data item is offset within the category according to the series. * * @return A boolean. * * @see #setUseSeriesOffset(boolean) */ public boolean getUseSeriesOffset() { return this.useSeriesOffset; } /** * Sets the flag that controls whether or not the x-position for each * data item is offset within its category according to the series, and * sends a {@link RendererChangeEvent} to all registered listeners. * * @param offset the offset. * * @see #getUseSeriesOffset() */ public void setUseSeriesOffset(boolean offset) { this.useSeriesOffset = offset; fireChangeEvent(); } /** * Returns the item margin, which is the gap between items within a * category (expressed as a percentage of the overall category width). * This can be used to match the offset alignment with the bars drawn by * a {@link BarRenderer}). * * @return The item margin. * * @see #setItemMargin(double) * @see #getUseSeriesOffset() */ public double getItemMargin() { return this.itemMargin; } /** * Sets the item margin, which is the gap between items within a category * (expressed as a percentage of the overall category width), and sends * a {@link RendererChangeEvent} to all registered listeners. * * @param margin the margin (0.0 <= margin < 1.0). * * @see #getItemMargin() * @see #getUseSeriesOffset() */ public void setItemMargin(double margin) { if (margin < 0.0 || margin >= 1.0) { throw new IllegalArgumentException("Requires 0.0 <= margin < 1.0."); } this.itemMargin = margin; fireChangeEvent(); } /** * Returns {@code true} if outlines should be drawn for shapes, and * {@code false} otherwise. * * @return A boolean. * * @see #setDrawOutlines(boolean) */ public boolean getDrawOutlines() { return this.drawOutlines; } /** * Sets the flag that controls whether outlines are drawn for * shapes, and sends a {@link RendererChangeEvent} to all registered * listeners. *

In some cases, shapes look better if they do NOT have an outline, but * this flag allows you to set your own preference.

* * @param flag the flag. * * @see #getDrawOutlines() */ public void setDrawOutlines(boolean flag) { this.drawOutlines = flag; fireChangeEvent(); } /** * Returns the flag that controls whether the outline paint is used for * shape outlines. If not, the regular series paint is used. * * @return A boolean. * * @see #setUseOutlinePaint(boolean) */ public boolean getUseOutlinePaint() { return this.useOutlinePaint; } /** * Sets the flag that controls whether the outline paint is used for shape * outlines, and sends a {@link RendererChangeEvent} to all registered * listeners. * * @param use the flag. * * @see #getUseOutlinePaint() */ public void setUseOutlinePaint(boolean use) { this.useOutlinePaint = use; fireChangeEvent(); } // SHAPES FILLED /** * Returns the flag used to control whether or not the shape for an item * is filled. The default implementation passes control to the * {@code getSeriesShapesFilled} method. You can override this method * if you require different behaviour. * * @param series the series index (zero-based). * @param item the item index (zero-based). * @return A boolean. */ public boolean getItemShapeFilled(int series, int item) { return getSeriesShapesFilled(series); } /** * Returns the flag used to control whether or not the shapes for a series * are filled. * * @param series the series index (zero-based). * @return A boolean. */ public boolean getSeriesShapesFilled(int series) { Boolean flag = this.seriesShapesFilled.getBoolean(series); if (flag != null) { return flag; } else { return this.baseShapesFilled; } } /** * Sets the 'shapes filled' flag for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param filled the flag. */ public void setSeriesShapesFilled(int series, Boolean filled) { this.seriesShapesFilled.setBoolean(series, filled); fireChangeEvent(); } /** * Sets the 'shapes filled' flag for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param filled the flag. */ public void setSeriesShapesFilled(int series, boolean filled) { this.seriesShapesFilled.setBoolean(series, filled); fireChangeEvent(); } /** * Returns the base 'shape filled' attribute. * * @return The base flag. */ public boolean getBaseShapesFilled() { return this.baseShapesFilled; } /** * Sets the base 'shapes filled' flag and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param flag the flag. */ public void setBaseShapesFilled(boolean flag) { this.baseShapesFilled = flag; fireChangeEvent(); } /** * Returns {@code true} if the renderer should use the fill paint * setting to fill shapes, and {@code false} if it should just * use the regular paint. * * @return A boolean. */ public boolean getUseFillPaint() { return this.useFillPaint; } /** * Sets the flag that controls whether the fill paint is used to fill * shapes, and sends a {@link RendererChangeEvent} to all * registered listeners. * * @param flag the flag. */ public void setUseFillPaint(boolean flag) { this.useFillPaint = flag; fireChangeEvent(); } /** * Returns the range of values the renderer requires to display all the * items from the specified dataset. This takes into account the range * between the min/max values, possibly ignoring invisible series. * * @param dataset the dataset ({@code null} permitted). * * @return The range (or {@code null} if the dataset is * {@code null} or empty). */ @Override public Range findRangeBounds(CategoryDataset dataset) { return findRangeBounds(dataset, true); } /** * Draw a single data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the area in which the data is drawn. * @param plot the plot. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset. * @param row the row index (zero-based). * @param column the column index (zero-based). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, ValueAxis rangeAxis, CategoryDataset dataset, int row, int column, int pass) { // do nothing if item is not visible if (!getItemVisible(row, column)) { return; } int visibleRow = state.getVisibleSeriesIndex(row); if (visibleRow < 0) { return; } int visibleRowCount = state.getVisibleSeriesCount(); PlotOrientation orientation = plot.getOrientation(); MultiValueCategoryDataset d = (MultiValueCategoryDataset) dataset; List values = d.getValues(row, column); if (values == null) { return; } int valueCount = values.size(); for (int i = 0; i < valueCount; i++) { // current data point... double x1; if (this.useSeriesOffset) { x1 = domainAxis.getCategorySeriesMiddle(column, dataset.getColumnCount(), visibleRow, visibleRowCount, this.itemMargin, dataArea, plot.getDomainAxisEdge()); } else { x1 = domainAxis.getCategoryMiddle(column, getColumnCount(), dataArea, plot.getDomainAxisEdge()); } Number n = (Number) values.get(i); double value = n.doubleValue(); double y1 = rangeAxis.valueToJava2D(value, dataArea, plot.getRangeAxisEdge()); Shape shape = getItemShape(row, column); if (orientation == PlotOrientation.HORIZONTAL) { shape = ShapeUtils.createTranslatedShape(shape, y1, x1); } else if (orientation == PlotOrientation.VERTICAL) { shape = ShapeUtils.createTranslatedShape(shape, x1, y1); } if (getItemShapeFilled(row, column)) { if (this.useFillPaint) { g2.setPaint(getItemFillPaint(row, column)); } else { g2.setPaint(getItemPaint(row, column)); } g2.fill(shape); } if (this.drawOutlines) { if (this.useOutlinePaint) { g2.setPaint(getItemOutlinePaint(row, column)); } else { g2.setPaint(getItemPaint(row, column)); } g2.setStroke(getItemOutlineStroke(row, column)); g2.draw(shape); } } } /** * Returns a legend item for a series. * * @param datasetIndex the dataset index (zero-based). * @param series the series index (zero-based). * * @return The legend item. */ @Override public LegendItem getLegendItem(int datasetIndex, int series) { CategoryPlot cp = getPlot(); if (cp == null) { return null; } if (isSeriesVisible(series) && isSeriesVisibleInLegend(series)) { CategoryDataset dataset = cp.getDataset(datasetIndex); String label = getLegendItemLabelGenerator().generateLabel( dataset, series); String description = label; String toolTipText = null; if (getLegendItemToolTipGenerator() != null) { toolTipText = getLegendItemToolTipGenerator().generateLabel( dataset, series); } String urlText = null; if (getLegendItemURLGenerator() != null) { urlText = getLegendItemURLGenerator().generateLabel( dataset, series); } Shape shape = lookupLegendShape(series); Paint paint = lookupSeriesPaint(series); Paint fillPaint = (this.useFillPaint ? getItemFillPaint(series, 0) : paint); boolean shapeOutlineVisible = this.drawOutlines; Paint outlinePaint = (this.useOutlinePaint ? getItemOutlinePaint(series, 0) : paint); Stroke outlineStroke = lookupSeriesOutlineStroke(series); LegendItem result = new LegendItem(label, description, toolTipText, urlText, true, shape, getItemShapeFilled(series, 0), fillPaint, shapeOutlineVisible, outlinePaint, outlineStroke, false, new Line2D.Double(-7.0, 0.0, 7.0, 0.0), getItemStroke(series, 0), getItemPaint(series, 0)); result.setLabelFont(lookupLegendTextFont(series)); Paint labelPaint = lookupLegendTextPaint(series); if (labelPaint != null) { result.setLabelPaint(labelPaint); } result.setDataset(dataset); result.setDatasetIndex(datasetIndex); result.setSeriesKey(dataset.getRowKey(series)); result.setSeriesIndex(series); return result; } return null; } /** * Tests this renderer for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof ScatterRenderer)) { return false; } ScatterRenderer that = (ScatterRenderer) obj; if (!Objects.equals(this.seriesShapesFilled, that.seriesShapesFilled)) { return false; } if (this.baseShapesFilled != that.baseShapesFilled) { return false; } if (this.useFillPaint != that.useFillPaint) { return false; } if (this.drawOutlines != that.drawOutlines) { return false; } if (this.useOutlinePaint != that.useOutlinePaint) { return false; } if (this.useSeriesOffset != that.useSeriesOffset) { return false; } if (this.itemMargin != that.itemMargin) { return false; } return super.equals(obj); } /** * Returns an independent copy of the renderer. * * @return A clone. * * @throws CloneNotSupportedException should not happen. */ @Override public Object clone() throws CloneNotSupportedException { ScatterRenderer clone = (ScatterRenderer) super.clone(); clone.seriesShapesFilled = (BooleanList) this.seriesShapesFilled.clone(); return clone; } /** * Provides serialization support. * * @param stream the output stream. * @throws java.io.IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); } /** * Provides serialization support. * * @param stream the input stream. * @throws java.io.IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/category/StackedAreaRenderer.java000066400000000000000000000426511463604235500332120ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * StackedAreaRenderer.java * ------------------------ * (C) Copyright 2002-present, by Dan Rivett (d.rivett@ukonline.co.uk) and * Contributors. * * Original Author: Dan Rivett (adapted from AreaRenderer); * Contributor(s): Jon Iles; * David Gilbert; * Christian W. Zuckschwerdt; * Peter Kolb (patch 2511330); */ package org.jfree.chart.renderer.category; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.geom.GeneralPath; import java.awt.geom.Rectangle2D; import java.io.Serializable; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.DataUtils; import org.jfree.data.Range; import org.jfree.data.category.CategoryDataset; import org.jfree.data.general.DatasetUtils; /** * A renderer that draws stacked area charts for a {@link CategoryPlot}. * The example shown here is generated by the * {@code StackedAreaChartDemo1.java} program included in the * JFreeChart Demo Collection: *

* StackedAreaRendererSample.png */ public class StackedAreaRenderer extends AreaRenderer implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -3595635038460823663L; /** A flag that controls whether the areas display values or percentages. */ private boolean renderAsPercentages; /** * Creates a new renderer. */ public StackedAreaRenderer() { this(false); } /** * Creates a new renderer. * * @param renderAsPercentages a flag that controls whether the data values * are rendered as percentages. */ public StackedAreaRenderer(boolean renderAsPercentages) { super(); this.renderAsPercentages = renderAsPercentages; } /** * Returns {@code true} if the renderer displays each item value as * a percentage (so that the stacked areas add to 100%), and * {@code false} otherwise. * * @return A boolean. */ public boolean getRenderAsPercentages() { return this.renderAsPercentages; } /** * Sets the flag that controls whether the renderer displays each item * value as a percentage (so that the stacked areas add to 100%), and sends * a {@link RendererChangeEvent} to all registered listeners. * * @param asPercentages the flag. */ public void setRenderAsPercentages(boolean asPercentages) { this.renderAsPercentages = asPercentages; fireChangeEvent(); } /** * Returns the number of passes ({@code 2}) required by this renderer. * The first pass is used to draw the areas, the second pass is used to * draw the item labels (if visible). * * @return The number of passes required by the renderer. */ @Override public int getPassCount() { return 2; } /** * Returns the range of values the renderer requires to display all the * items from the specified dataset. * * @param dataset the dataset ({@code null} not permitted). * * @return The range (or {@code null} if the dataset is empty). */ @Override public Range findRangeBounds(CategoryDataset dataset) { if (dataset == null) { return null; } if (this.renderAsPercentages) { return new Range(0.0, 1.0); } else { return DatasetUtils.findStackedRangeBounds(dataset); } } /** * Draw a single data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the data plot area. * @param plot the plot. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the data. * @param row the row index (zero-based). * @param column the column index (zero-based). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, ValueAxis rangeAxis, CategoryDataset dataset, int row, int column, int pass) { if (!isSeriesVisible(row)) { return; } // setup for collecting optional entity info... Shape entityArea; EntityCollection entities = state.getEntityCollection(); double y1 = 0.0; Number n = dataset.getValue(row, column); if (n != null) { y1 = n.doubleValue(); if (this.renderAsPercentages) { double total = DataUtils.calculateColumnTotal(dataset, column, state.getVisibleSeriesArray()); y1 = y1 / total; } } double[] stack1 = getStackValues(dataset, row, column, state.getVisibleSeriesArray()); // leave the y values (y1, y0) untranslated as it is going to be be // stacked up later by previous series values, after this it will be // translated. double xx1 = domainAxis.getCategoryMiddle(column, getColumnCount(), dataArea, plot.getDomainAxisEdge()); // get the previous point and the next point so we can calculate a // "hot spot" for the area (used by the chart entity)... double y0 = 0.0; n = dataset.getValue(row, Math.max(column - 1, 0)); if (n != null) { y0 = n.doubleValue(); if (this.renderAsPercentages) { double total = DataUtils.calculateColumnTotal(dataset, Math.max(column - 1, 0), state.getVisibleSeriesArray()); y0 = y0 / total; } } double[] stack0 = getStackValues(dataset, row, Math.max(column - 1, 0), state.getVisibleSeriesArray()); // FIXME: calculate xx0 double xx0 = domainAxis.getCategoryStart(column, getColumnCount(), dataArea, plot.getDomainAxisEdge()); int itemCount = dataset.getColumnCount(); double y2 = 0.0; n = dataset.getValue(row, Math.min(column + 1, itemCount - 1)); if (n != null) { y2 = n.doubleValue(); if (this.renderAsPercentages) { double total = DataUtils.calculateColumnTotal(dataset, Math.min(column + 1, itemCount - 1), state.getVisibleSeriesArray()); y2 = y2 / total; } } double[] stack2 = getStackValues(dataset, row, Math.min(column + 1, itemCount - 1), state.getVisibleSeriesArray()); double xx2 = domainAxis.getCategoryEnd(column, getColumnCount(), dataArea, plot.getDomainAxisEdge()); // FIXME: calculate xxLeft and xxRight double xxLeft = xx0; double xxRight = xx2; double[] stackLeft = averageStackValues(stack0, stack1); double[] stackRight = averageStackValues(stack1, stack2); double[] adjStackLeft = adjustedStackValues(stack0, stack1); double[] adjStackRight = adjustedStackValues(stack1, stack2); float transY1; RectangleEdge edge1 = plot.getRangeAxisEdge(); GeneralPath left = new GeneralPath(); GeneralPath right = new GeneralPath(); if (y1 >= 0.0) { // handle positive value transY1 = (float) rangeAxis.valueToJava2D(y1 + stack1[1], dataArea, edge1); float transStack1 = (float) rangeAxis.valueToJava2D(stack1[1], dataArea, edge1); float transStackLeft = (float) rangeAxis.valueToJava2D( adjStackLeft[1], dataArea, edge1); // LEFT POLYGON if (y0 >= 0.0) { double yleft = (y0 + y1) / 2.0 + stackLeft[1]; float transYLeft = (float) rangeAxis.valueToJava2D(yleft, dataArea, edge1); left.moveTo((float) xx1, transY1); left.lineTo((float) xx1, transStack1); left.lineTo((float) xxLeft, transStackLeft); left.lineTo((float) xxLeft, transYLeft); left.closePath(); } else { left.moveTo((float) xx1, transStack1); left.lineTo((float) xx1, transY1); left.lineTo((float) xxLeft, transStackLeft); left.closePath(); } float transStackRight = (float) rangeAxis.valueToJava2D( adjStackRight[1], dataArea, edge1); // RIGHT POLYGON if (y2 >= 0.0) { double yright = (y1 + y2) / 2.0 + stackRight[1]; float transYRight = (float) rangeAxis.valueToJava2D(yright, dataArea, edge1); right.moveTo((float) xx1, transStack1); right.lineTo((float) xx1, transY1); right.lineTo((float) xxRight, transYRight); right.lineTo((float) xxRight, transStackRight); right.closePath(); } else { right.moveTo((float) xx1, transStack1); right.lineTo((float) xx1, transY1); right.lineTo((float) xxRight, transStackRight); right.closePath(); } } else { // handle negative value transY1 = (float) rangeAxis.valueToJava2D(y1 + stack1[0], dataArea, edge1); float transStack1 = (float) rangeAxis.valueToJava2D(stack1[0], dataArea, edge1); float transStackLeft = (float) rangeAxis.valueToJava2D( adjStackLeft[0], dataArea, edge1); // LEFT POLYGON if (y0 >= 0.0) { left.moveTo((float) xx1, transStack1); left.lineTo((float) xx1, transY1); left.lineTo((float) xxLeft, transStackLeft); left.clone(); } else { double yleft = (y0 + y1) / 2.0 + stackLeft[0]; float transYLeft = (float) rangeAxis.valueToJava2D(yleft, dataArea, edge1); left.moveTo((float) xx1, transY1); left.lineTo((float) xx1, transStack1); left.lineTo((float) xxLeft, transStackLeft); left.lineTo((float) xxLeft, transYLeft); left.closePath(); } float transStackRight = (float) rangeAxis.valueToJava2D( adjStackRight[0], dataArea, edge1); // RIGHT POLYGON if (y2 >= 0.0) { right.moveTo((float) xx1, transStack1); right.lineTo((float) xx1, transY1); right.lineTo((float) xxRight, transStackRight); right.closePath(); } else { double yright = (y1 + y2) / 2.0 + stackRight[0]; float transYRight = (float) rangeAxis.valueToJava2D(yright, dataArea, edge1); right.moveTo((float) xx1, transStack1); right.lineTo((float) xx1, transY1); right.lineTo((float) xxRight, transYRight); right.lineTo((float) xxRight, transStackRight); right.closePath(); } } if (pass == 0) { Paint itemPaint = getItemPaint(row, column); g2.setPaint(itemPaint); g2.fill(left); g2.fill(right); // add an entity for the item... if (entities != null) { GeneralPath gp = new GeneralPath(left); gp.append(right, false); entityArea = gp; addItemEntity(entities, dataset, row, column, entityArea); } } else if (pass == 1) { drawItemLabel(g2, plot.getOrientation(), dataset, row, column, xx1, transY1, y1 < 0.0); } } /** * Calculates the stacked values (one positive and one negative) of all * series up to, but not including, {@code series} for the specified * item. It returns [0.0, 0.0] if {@code series} is the first series. * * @param dataset the dataset ({@code null} not permitted). * @param series the series index. * @param index the item index. * @param validRows the valid rows. * * @return An array containing the cumulative negative and positive values * for all series values up to but excluding {@code series} * for {@code index}. */ protected double[] getStackValues(CategoryDataset dataset, int series, int index, int[] validRows) { double[] result = new double[2]; double total = 0.0; if (this.renderAsPercentages) { total = DataUtils.calculateColumnTotal(dataset, index, validRows); } for (int i = 0; i < series; i++) { if (isSeriesVisible(i)) { double v = 0.0; Number n = dataset.getValue(i, index); if (n != null) { v = n.doubleValue(); if (this.renderAsPercentages) { v = v / total; } } if (!Double.isNaN(v)) { if (v >= 0.0) { result[1] += v; } else { result[0] += v; } } } } return result; } /** * Returns a pair of "stack" values calculated as the mean of the two * specified stack value pairs. * * @param stack1 the first stack pair. * @param stack2 the second stack pair. * * @return A pair of average stack values. */ private double[] averageStackValues(double[] stack1, double[] stack2) { double[] result = new double[2]; result[0] = (stack1[0] + stack2[0]) / 2.0; result[1] = (stack1[1] + stack2[1]) / 2.0; return result; } /** * Calculates adjusted stack values from the supplied values. The value is * the mean of the supplied values, unless either of the supplied values * is zero, in which case the adjusted value is zero also. * * @param stack1 the first stack pair. * @param stack2 the second stack pair. * * @return A pair of average stack values. */ private double[] adjustedStackValues(double[] stack1, double[] stack2) { double[] result = new double[2]; if (stack1[0] == 0.0 || stack2[0] == 0.0) { result[0] = 0.0; } else { result[0] = (stack1[0] + stack2[0]) / 2.0; } if (stack1[1] == 0.0 || stack2[1] == 0.0) { result[1] = 0.0; } else { result[1] = (stack1[1] + stack2[1]) / 2.0; } return result; } /** * Checks this instance for equality with an arbitrary object. * * @param obj the object ({@code null} not permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof StackedAreaRenderer)) { return false; } StackedAreaRenderer that = (StackedAreaRenderer) obj; if (this.renderAsPercentages != that.renderAsPercentages) { return false; } return super.equals(obj); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/category/StackedBarRenderer.java000066400000000000000000000326121463604235500330420ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * StackedBarRenderer.java * ----------------------- * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Richard Atkinson; * Thierry Saura; * Christian W. Zuckschwerdt; * Peter Kolb (patch 2511330); * */ package org.jfree.chart.renderer.category; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.labels.CategoryItemLabelGenerator; import org.jfree.chart.labels.ItemLabelAnchor; import org.jfree.chart.labels.ItemLabelPosition; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.TextAnchor; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.DataUtils; import org.jfree.data.Range; import org.jfree.data.category.CategoryDataset; import org.jfree.data.general.DatasetUtils; /** * A stacked bar renderer for use with the {@link CategoryPlot} class. * The example shown here is generated by the * {@code StackedBarChartDemo1.java} program included in the * JFreeChart Demo Collection: *

* StackedBarRendererSample.png */ public class StackedBarRenderer extends BarRenderer implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ static final long serialVersionUID = 6402943811500067531L; /** A flag that controls whether the bars display values or percentages. */ private boolean renderAsPercentages; /** * Creates a new renderer. By default, the renderer has no tool tip * generator and no URL generator. These defaults have been chosen to * minimise the processing required to generate a default chart. If you * require tool tips or URLs, then you can easily add the required * generators. */ public StackedBarRenderer() { this(false); } /** * Creates a new renderer. * * @param renderAsPercentages a flag that controls whether the data values * are rendered as percentages. */ public StackedBarRenderer(boolean renderAsPercentages) { super(); this.renderAsPercentages = renderAsPercentages; // set the default item label positions, which will only be used if // the user requests visible item labels... ItemLabelPosition p = new ItemLabelPosition(ItemLabelAnchor.CENTER, TextAnchor.CENTER); setDefaultPositiveItemLabelPosition(p); setDefaultNegativeItemLabelPosition(p); setPositiveItemLabelPositionFallback(null); setNegativeItemLabelPositionFallback(null); } /** * Returns {@code true} if the renderer displays each item value as * a percentage (so that the stacked bars add to 100%), and * {@code false} otherwise. * * @return A boolean. * * @see #setRenderAsPercentages(boolean) */ public boolean getRenderAsPercentages() { return this.renderAsPercentages; } /** * Sets the flag that controls whether the renderer displays each item * value as a percentage (so that the stacked bars add to 100%), and sends * a {@link RendererChangeEvent} to all registered listeners. * * @param asPercentages the flag. * * @see #getRenderAsPercentages() */ public void setRenderAsPercentages(boolean asPercentages) { this.renderAsPercentages = asPercentages; fireChangeEvent(); } /** * Returns the number of passes ({@code 3}) required by this renderer. * The first pass is used to draw the bar shadows, the second pass is used * to draw the bars, and the third pass is used to draw the item labels * (if visible). * * @return The number of passes required by the renderer. */ @Override public int getPassCount() { return 3; } /** * Returns the range of values the renderer requires to display all the * items from the specified dataset. * * @param dataset the dataset ({@code null} permitted). * * @return The range (or {@code null} if the dataset is empty). */ @Override public Range findRangeBounds(CategoryDataset dataset) { if (dataset == null) { return null; } if (this.renderAsPercentages) { return new Range(0.0, 1.0); } else { return DatasetUtils.findStackedRangeBounds(dataset, getBase()); } } /** * Calculates the bar width and stores it in the renderer state. * * @param plot the plot. * @param dataArea the data area. * @param rendererIndex the renderer index. * @param state the renderer state. */ @Override protected void calculateBarWidth(CategoryPlot plot, Rectangle2D dataArea, int rendererIndex, CategoryItemRendererState state) { // calculate the bar width CategoryAxis xAxis = plot.getDomainAxisForDataset(rendererIndex); CategoryDataset data = plot.getDataset(rendererIndex); if (data != null) { PlotOrientation orientation = plot.getOrientation(); double space = 0.0; if (orientation == PlotOrientation.HORIZONTAL) { space = dataArea.getHeight(); } else if (orientation == PlotOrientation.VERTICAL) { space = dataArea.getWidth(); } double maxWidth = space * getMaximumBarWidth(); int columns = data.getColumnCount(); double categoryMargin = 0.0; if (columns > 1) { categoryMargin = xAxis.getCategoryMargin(); } double used = space * (1 - xAxis.getLowerMargin() - xAxis.getUpperMargin() - categoryMargin); if (columns > 0) { state.setBarWidth(Math.min(used / columns, maxWidth)); } else { state.setBarWidth(Math.min(used, maxWidth)); } } } /** * Draws a stacked bar for a specific item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the plot area. * @param plot the plot. * @param domainAxis the domain (category) axis. * @param rangeAxis the range (value) axis. * @param dataset the data. * @param row the row index (zero-based). * @param column the column index (zero-based). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, ValueAxis rangeAxis, CategoryDataset dataset, int row, int column, int pass) { if (!isSeriesVisible(row)) { return; } // nothing is drawn for null values... Number dataValue = dataset.getValue(row, column); if (dataValue == null) { return; } double value = dataValue.doubleValue(); double total = 0.0; // only needed if calculating percentages if (this.renderAsPercentages) { total = DataUtils.calculateColumnTotal(dataset, column, state.getVisibleSeriesArray()); value = value / total; } PlotOrientation orientation = plot.getOrientation(); double barW0 = domainAxis.getCategoryMiddle(column, getColumnCount(), dataArea, plot.getDomainAxisEdge()) - state.getBarWidth() / 2.0; double positiveBase = getBase(); double negativeBase = positiveBase; for (int i = 0; i < row; i++) { Number v = dataset.getValue(i, column); if (v != null && isSeriesVisible(i)) { double d = v.doubleValue(); if (this.renderAsPercentages) { d = d / total; } if (d > 0) { positiveBase = positiveBase + d; } else { negativeBase = negativeBase + d; } } } double translatedBase; double translatedValue; boolean positive = (value > 0.0); boolean inverted = rangeAxis.isInverted(); RectangleEdge barBase; if (orientation == PlotOrientation.HORIZONTAL) { if (positive && inverted || !positive && !inverted) { barBase = RectangleEdge.RIGHT; } else { barBase = RectangleEdge.LEFT; } } else { if (positive && !inverted || !positive && inverted) { barBase = RectangleEdge.BOTTOM; } else { barBase = RectangleEdge.TOP; } } RectangleEdge location = plot.getRangeAxisEdge(); if (positive) { translatedBase = rangeAxis.valueToJava2D(positiveBase, dataArea, location); translatedValue = rangeAxis.valueToJava2D(positiveBase + value, dataArea, location); } else { translatedBase = rangeAxis.valueToJava2D(negativeBase, dataArea, location); translatedValue = rangeAxis.valueToJava2D(negativeBase + value, dataArea, location); } double barL0 = Math.min(translatedBase, translatedValue); double barLength = Math.max(Math.abs(translatedValue - translatedBase), getMinimumBarLength()); Rectangle2D bar; if (orientation == PlotOrientation.HORIZONTAL) { bar = new Rectangle2D.Double(barL0, barW0, barLength, state.getBarWidth()); } else { bar = new Rectangle2D.Double(barW0, barL0, state.getBarWidth(), barLength); } if (pass == 0) { if (getShadowsVisible()) { boolean pegToBase = (positive && (positiveBase == getBase())) || (!positive && (negativeBase == getBase())); getBarPainter().paintBarShadow(g2, this, row, column, bar, barBase, pegToBase); } } else if (pass == 1) { getBarPainter().paintBar(g2, this, row, column, bar, barBase); // add an item entity, if this information is being collected EntityCollection entities = state.getEntityCollection(); if (entities != null) { addItemEntity(entities, dataset, row, column, bar); } } else if (pass == 2) { CategoryItemLabelGenerator generator = getItemLabelGenerator(row, column); if (generator != null && isItemLabelVisible(row, column)) { drawItemLabel(g2, dataset, row, column, plot, generator, bar, (value < 0.0)); } } } /** * Tests this renderer for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof StackedBarRenderer)) { return false; } StackedBarRenderer that = (StackedBarRenderer) obj; if (this.renderAsPercentages != that.renderAsPercentages) { return false; } return super.equals(obj); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/category/StandardBarPainter.java000066400000000000000000000156351463604235500330660ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * StandardBarPainter.java * ----------------------- * (C) Copyright 2008-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; */ package org.jfree.chart.renderer.category; import java.awt.Color; import java.awt.GradientPaint; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Stroke; import java.awt.geom.Rectangle2D; import java.awt.geom.RectangularShape; import java.io.Serializable; import org.jfree.chart.ui.GradientPaintTransformer; import org.jfree.chart.ui.RectangleEdge; /** * An implementation of the {@link BarPainter} interface that preserves the * behaviour of bar painting that existed prior to the introduction of the * {@link BarPainter} interface. * * @see GradientBarPainter */ public class StandardBarPainter implements BarPainter, Serializable { /** * Creates a new instance. */ public StandardBarPainter() { } /** * Paints a single bar instance. * * @param g2 the graphics target. * @param renderer the renderer. * @param row the row index. * @param column the column index. * @param bar the bar * @param base indicates which side of the rectangle is the base of the * bar. */ @Override public void paintBar(Graphics2D g2, BarRenderer renderer, int row, int column, RectangularShape bar, RectangleEdge base) { Paint itemPaint = renderer.getItemPaint(row, column); GradientPaintTransformer t = renderer.getGradientPaintTransformer(); if (t != null && itemPaint instanceof GradientPaint) { itemPaint = t.transform((GradientPaint) itemPaint, bar); } g2.setPaint(itemPaint); g2.fill(bar); // draw the outline... if (renderer.isDrawBarOutline()) { // && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) { Stroke stroke = renderer.getItemOutlineStroke(row, column); Paint paint = renderer.getItemOutlinePaint(row, column); if (stroke != null && paint != null) { g2.setStroke(stroke); g2.setPaint(paint); g2.draw(bar); } } } /** * Paints a single bar instance. * * @param g2 the graphics target. * @param renderer the renderer. * @param row the row index. * @param column the column index. * @param bar the bar * @param base indicates which side of the rectangle is the base of the * bar. * @param pegShadow peg the shadow to the base of the bar? */ @Override public void paintBarShadow(Graphics2D g2, BarRenderer renderer, int row, int column, RectangularShape bar, RectangleEdge base, boolean pegShadow) { // handle a special case - if the bar colour has alpha == 0, it is // invisible so we shouldn't draw any shadow Paint itemPaint = renderer.getItemPaint(row, column); if (itemPaint instanceof Color) { Color c = (Color) itemPaint; if (c.getAlpha() == 0) { return; } } RectangularShape shadow = createShadow(bar, renderer.getShadowXOffset(), renderer.getShadowYOffset(), base, pegShadow); g2.setPaint(renderer.getShadowPaint()); g2.fill(shadow); } /** * Creates a shadow for the bar. * * @param bar the bar shape. * @param xOffset the x-offset for the shadow. * @param yOffset the y-offset for the shadow. * @param base the edge that is the base of the bar. * @param pegShadow peg the shadow to the base? * * @return A rectangle for the shadow. */ private Rectangle2D createShadow(RectangularShape bar, double xOffset, double yOffset, RectangleEdge base, boolean pegShadow) { double x0 = bar.getMinX(); double x1 = bar.getMaxX(); double y0 = bar.getMinY(); double y1 = bar.getMaxY(); if (base == RectangleEdge.TOP) { x0 += xOffset; x1 += xOffset; if (!pegShadow) { y0 += yOffset; } y1 += yOffset; } else if (base == RectangleEdge.BOTTOM) { x0 += xOffset; x1 += xOffset; y0 += yOffset; if (!pegShadow) { y1 += yOffset; } } else if (base == RectangleEdge.LEFT) { if (!pegShadow) { x0 += xOffset; } x1 += xOffset; y0 += yOffset; y1 += yOffset; } else if (base == RectangleEdge.RIGHT) { x0 += xOffset; if (!pegShadow) { x1 += xOffset; } y0 += yOffset; y1 += yOffset; } return new Rectangle2D.Double(x0, y0, (x1 - x0), (y1 - y0)); } /** * Tests this instance for equality with an arbitrary object. * * @param obj the obj ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof StandardBarPainter)) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int hash = 37; // no fields to compute... return hash; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/category/StatisticalBarRenderer.java000066400000000000000000000510311463604235500337440ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * StatisticalBarRenderer.java * --------------------------- * (C) Copyright 2002-present, by Pascal Collet and Contributors. * * Original Author: Pascal Collet; * Contributor(s): David Gilbert; * Christian W. Zuckschwerdt; * Peter Kolb (patches 2497611, 2791407); * Martin Hoeller; * */ package org.jfree.chart.renderer.category; import java.awt.BasicStroke; import java.awt.Color; import java.awt.GradientPaint; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Stroke; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.labels.CategoryItemLabelGenerator; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.ui.GradientPaintTransformer; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; import org.jfree.data.Range; import org.jfree.data.category.CategoryDataset; import org.jfree.data.statistics.StatisticalCategoryDataset; /** * A renderer that handles the drawing a bar plot where * each bar has a mean value and a standard deviation line. The example shown * here is generated by the {@code StatisticalBarChartDemo1.java} program * included in the JFreeChart Demo Collection: *

* StatisticalBarRendererSample.png */ public class StatisticalBarRenderer extends BarRenderer implements CategoryItemRenderer, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -4986038395414039117L; /** The paint used to show the error indicator. */ private transient Paint errorIndicatorPaint; /** * The stroke used to draw the error indicators. */ private transient Stroke errorIndicatorStroke; /** * Default constructor. */ public StatisticalBarRenderer() { super(); this.errorIndicatorPaint = Color.GRAY; this.errorIndicatorStroke = new BasicStroke(1.0f); } /** * Returns the paint used for the error indicators. * * @return The paint used for the error indicators (possibly * {@code null}). * * @see #setErrorIndicatorPaint(Paint) */ public Paint getErrorIndicatorPaint() { return this.errorIndicatorPaint; } /** * Sets the paint used for the error indicators (if {@code null}, * the item outline paint is used instead) and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} permitted). * * @see #getErrorIndicatorPaint() */ public void setErrorIndicatorPaint(Paint paint) { this.errorIndicatorPaint = paint; fireChangeEvent(); } /** * Returns the stroke used to draw the error indicators. If this is * {@code null}, the renderer will use the item outline stroke). * * @return The stroke (possibly {@code null}). * * @see #setErrorIndicatorStroke(Stroke) */ public Stroke getErrorIndicatorStroke() { return this.errorIndicatorStroke; } /** * Sets the stroke used to draw the error indicators, and sends a * {@link RendererChangeEvent} to all registered listeners. If you set * this to {@code null}, the renderer will use the item outline * stroke. * * @param stroke the stroke ({@code null} permitted). * * @see #getErrorIndicatorStroke() */ public void setErrorIndicatorStroke(Stroke stroke) { this.errorIndicatorStroke = stroke; fireChangeEvent(); } /** * Returns the range of values the renderer requires to display all the * items from the specified dataset. This takes into account the range * between the min/max values, possibly ignoring invisible series. * * @param dataset the dataset ({@code null} permitted). * * @return The range (or {@code null} if the dataset is * {@code null} or empty). */ @Override public Range findRangeBounds(CategoryDataset dataset) { return findRangeBounds(dataset, true); } /** * Draws the bar with its standard deviation line range for a single * (series, category) data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the data area. * @param plot the plot. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param data the data. * @param row the row index (zero-based). * @param column the column index (zero-based). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, ValueAxis rangeAxis, CategoryDataset data, int row, int column, int pass) { int visibleRow = state.getVisibleSeriesIndex(row); if (visibleRow < 0) { return; } // defensive check if (!(data instanceof StatisticalCategoryDataset)) { throw new IllegalArgumentException( "Requires StatisticalCategoryDataset."); } StatisticalCategoryDataset statData = (StatisticalCategoryDataset) data; PlotOrientation orientation = plot.getOrientation(); if (orientation == PlotOrientation.HORIZONTAL) { drawHorizontalItem(g2, state, dataArea, plot, domainAxis, rangeAxis, statData, visibleRow, row, column); } else if (orientation == PlotOrientation.VERTICAL) { drawVerticalItem(g2, state, dataArea, plot, domainAxis, rangeAxis, statData, visibleRow, row, column); } } /** * Draws an item for a plot with a horizontal orientation. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the data area. * @param plot the plot. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the data. * @param visibleRow the visible row index. * @param row the row index (zero-based). * @param column the column index (zero-based). */ protected void drawHorizontalItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, ValueAxis rangeAxis, StatisticalCategoryDataset dataset, int visibleRow, int row, int column) { // BAR Y double rectY = calculateBarW0(plot, PlotOrientation.HORIZONTAL, dataArea, domainAxis, state, visibleRow, column); // BAR X Number meanValue = dataset.getMeanValue(row, column); if (meanValue == null) { return; } double value = meanValue.doubleValue(); double base = 0.0; double lclip = getLowerClip(); double uclip = getUpperClip(); if (uclip <= 0.0) { // cases 1, 2, 3 and 4 if (value >= uclip) { return; // bar is not visible } base = uclip; if (value <= lclip) { value = lclip; } } else if (lclip <= 0.0) { // cases 5, 6, 7 and 8 if (value >= uclip) { value = uclip; } else { if (value <= lclip) { value = lclip; } } } else { // cases 9, 10, 11 and 12 if (value <= lclip) { return; // bar is not visible } base = getLowerClip(); if (value >= uclip) { value = uclip; } } RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); double transY1 = rangeAxis.valueToJava2D(base, dataArea, yAxisLocation); double transY2 = rangeAxis.valueToJava2D(value, dataArea, yAxisLocation); double rectX = Math.min(transY2, transY1); double rectHeight = state.getBarWidth(); double rectWidth = Math.abs(transY2 - transY1); Rectangle2D bar = new Rectangle2D.Double(rectX, rectY, rectWidth, rectHeight); Paint itemPaint = getItemPaint(row, column); GradientPaintTransformer t = getGradientPaintTransformer(); if (t != null && itemPaint instanceof GradientPaint) { itemPaint = t.transform((GradientPaint) itemPaint, bar); } g2.setPaint(itemPaint); g2.fill(bar); // draw the outline... if (isDrawBarOutline() && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) { Stroke stroke = getItemOutlineStroke(row, column); Paint paint = getItemOutlinePaint(row, column); if (stroke != null && paint != null) { g2.setStroke(stroke); g2.setPaint(paint); g2.draw(bar); } } // standard deviation lines Number n = dataset.getStdDevValue(row, column); if (n != null) { double valueDelta = n.doubleValue(); double highVal = rangeAxis.valueToJava2D(meanValue.doubleValue() + valueDelta, dataArea, yAxisLocation); double lowVal = rangeAxis.valueToJava2D(meanValue.doubleValue() - valueDelta, dataArea, yAxisLocation); if (this.errorIndicatorPaint != null) { g2.setPaint(this.errorIndicatorPaint); } else { g2.setPaint(getItemOutlinePaint(row, column)); } if (this.errorIndicatorStroke != null) { g2.setStroke(this.errorIndicatorStroke); } else { g2.setStroke(getItemOutlineStroke(row, column)); } Line2D line; line = new Line2D.Double(lowVal, rectY + rectHeight / 2.0d, highVal, rectY + rectHeight / 2.0d); g2.draw(line); line = new Line2D.Double(highVal, rectY + rectHeight * 0.25, highVal, rectY + rectHeight * 0.75); g2.draw(line); line = new Line2D.Double(lowVal, rectY + rectHeight * 0.25, lowVal, rectY + rectHeight * 0.75); g2.draw(line); } CategoryItemLabelGenerator generator = getItemLabelGenerator(row, column); if (generator != null && isItemLabelVisible(row, column)) { drawItemLabel(g2, dataset, row, column, plot, generator, bar, (value < 0.0)); } // add an item entity, if this information is being collected EntityCollection entities = state.getEntityCollection(); if (entities != null) { addItemEntity(entities, dataset, row, column, bar); } } /** * Draws an item for a plot with a vertical orientation. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the data area. * @param plot the plot. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the data. * @param visibleRow the visible row index. * @param row the row index (zero-based). * @param column the column index (zero-based). */ protected void drawVerticalItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, ValueAxis rangeAxis, StatisticalCategoryDataset dataset, int visibleRow, int row, int column) { // BAR X double rectX = calculateBarW0(plot, PlotOrientation.VERTICAL, dataArea, domainAxis, state, visibleRow, column); // BAR Y Number meanValue = dataset.getMeanValue(row, column); if (meanValue == null) { return; } double value = meanValue.doubleValue(); double base = 0.0; double lclip = getLowerClip(); double uclip = getUpperClip(); if (uclip <= 0.0) { // cases 1, 2, 3 and 4 if (value >= uclip) { return; // bar is not visible } base = uclip; if (value <= lclip) { value = lclip; } } else if (lclip <= 0.0) { // cases 5, 6, 7 and 8 if (value >= uclip) { value = uclip; } else { if (value <= lclip) { value = lclip; } } } else { // cases 9, 10, 11 and 12 if (value <= lclip) { return; // bar is not visible } base = getLowerClip(); if (value >= uclip) { value = uclip; } } RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); double transY1 = rangeAxis.valueToJava2D(base, dataArea, yAxisLocation); double transY2 = rangeAxis.valueToJava2D(value, dataArea, yAxisLocation); double rectY = Math.min(transY2, transY1); double rectWidth = state.getBarWidth(); double rectHeight = Math.abs(transY2 - transY1); Rectangle2D bar = new Rectangle2D.Double(rectX, rectY, rectWidth, rectHeight); Paint itemPaint = getItemPaint(row, column); GradientPaintTransformer t = getGradientPaintTransformer(); if (t != null && itemPaint instanceof GradientPaint) { itemPaint = t.transform((GradientPaint) itemPaint, bar); } g2.setPaint(itemPaint); g2.fill(bar); // draw the outline... if (isDrawBarOutline() && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) { Stroke stroke = getItemOutlineStroke(row, column); Paint paint = getItemOutlinePaint(row, column); if (stroke != null && paint != null) { g2.setStroke(stroke); g2.setPaint(paint); g2.draw(bar); } } // standard deviation lines Number n = dataset.getStdDevValue(row, column); if (n != null) { double valueDelta = n.doubleValue(); double highVal = rangeAxis.valueToJava2D(meanValue.doubleValue() + valueDelta, dataArea, yAxisLocation); double lowVal = rangeAxis.valueToJava2D(meanValue.doubleValue() - valueDelta, dataArea, yAxisLocation); if (this.errorIndicatorPaint != null) { g2.setPaint(this.errorIndicatorPaint); } else { g2.setPaint(getItemOutlinePaint(row, column)); } if (this.errorIndicatorStroke != null) { g2.setStroke(this.errorIndicatorStroke); } else { g2.setStroke(getItemOutlineStroke(row, column)); } Line2D line; line = new Line2D.Double(rectX + rectWidth / 2.0d, lowVal, rectX + rectWidth / 2.0d, highVal); g2.draw(line); line = new Line2D.Double(rectX + rectWidth / 2.0d - 5.0d, highVal, rectX + rectWidth / 2.0d + 5.0d, highVal); g2.draw(line); line = new Line2D.Double(rectX + rectWidth / 2.0d - 5.0d, lowVal, rectX + rectWidth / 2.0d + 5.0d, lowVal); g2.draw(line); } CategoryItemLabelGenerator generator = getItemLabelGenerator(row, column); if (generator != null && isItemLabelVisible(row, column)) { drawItemLabel(g2, dataset, row, column, plot, generator, bar, (value < 0.0)); } // add an item entity, if this information is being collected EntityCollection entities = state.getEntityCollection(); if (entities != null) { addItemEntity(entities, dataset, row, column, bar); } } /** * Tests this renderer for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof StatisticalBarRenderer)) { return false; } StatisticalBarRenderer that = (StatisticalBarRenderer) obj; if (!PaintUtils.equal(this.errorIndicatorPaint, that.errorIndicatorPaint)) { return false; } if (!Objects.equals(this.errorIndicatorStroke, that.errorIndicatorStroke)) { return false; } return super.equals(obj); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.errorIndicatorPaint, stream); SerialUtils.writeStroke(this.errorIndicatorStroke, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.errorIndicatorPaint = SerialUtils.readPaint(stream); this.errorIndicatorStroke = SerialUtils.readStroke(stream); } } StatisticalLineAndShapeRenderer.java000066400000000000000000000416151463604235500354630ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/category/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------------ * StatisticalLineAndShapeRenderer.java * ------------------------------------ * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: Mofeed Shahin; * Contributor(s): David Gilbert; * Peter Kolb (patch 2497611); * */ package org.jfree.chart.renderer.category; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.HashUtils; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; import org.jfree.chart.util.ShapeUtils; import org.jfree.data.Range; import org.jfree.data.category.CategoryDataset; import org.jfree.data.statistics.StatisticalCategoryDataset; /** * A renderer that draws shapes for each data item, and lines between data * items. Each point has a mean value and a standard deviation line. For use * with the {@link CategoryPlot} class. The example shown * here is generated by the {@code StatisticalLineChartDemo1.java} program * included in the JFreeChart Demo Collection: *

* StatisticalLineRendererSample.png */ public class StatisticalLineAndShapeRenderer extends LineAndShapeRenderer implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -3557517173697777579L; /** The paint used to show the error indicator. */ private transient Paint errorIndicatorPaint; /** * The stroke used to draw the error indicators. If null, the renderer * will use the itemOutlineStroke. */ private transient Stroke errorIndicatorStroke; /** * Constructs a default renderer (draws shapes and lines). */ public StatisticalLineAndShapeRenderer() { this(true, true); } /** * Constructs a new renderer. * * @param linesVisible draw lines? * @param shapesVisible draw shapes? */ public StatisticalLineAndShapeRenderer(boolean linesVisible, boolean shapesVisible) { super(linesVisible, shapesVisible); this.errorIndicatorPaint = null; this.errorIndicatorStroke = null; } /** * Returns the paint used for the error indicators. * * @return The paint used for the error indicators (possibly * {@code null}). * * @see #setErrorIndicatorPaint(Paint) */ public Paint getErrorIndicatorPaint() { return this.errorIndicatorPaint; } /** * Sets the paint used for the error indicators (if {@code null}, * the item paint is used instead) and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} permitted). * * @see #getErrorIndicatorPaint() */ public void setErrorIndicatorPaint(Paint paint) { this.errorIndicatorPaint = paint; fireChangeEvent(); } /** * Returns the stroke used for the error indicators. * * @return The stroke used for the error indicators (possibly * {@code null}). * * @see #setErrorIndicatorStroke(Stroke) */ public Stroke getErrorIndicatorStroke() { return this.errorIndicatorStroke; } /** * Sets the stroke used for the error indicators (if {@code null}, * the item outline stroke is used instead) and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} permitted). * * @see #getErrorIndicatorStroke() */ public void setErrorIndicatorStroke(Stroke stroke) { this.errorIndicatorStroke = stroke; fireChangeEvent(); } /** * Returns the range of values the renderer requires to display all the * items from the specified dataset. * * @param dataset the dataset ({@code null} permitted). * * @return The range (or {@code null} if the dataset is * {@code null} or empty). */ @Override public Range findRangeBounds(CategoryDataset dataset) { return findRangeBounds(dataset, true); } /** * Draw a single data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the area in which the data is drawn. * @param plot the plot. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset (a {@link StatisticalCategoryDataset} is * required). * @param row the row index (zero-based). * @param column the column index (zero-based). * @param pass the pass. */ @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, ValueAxis rangeAxis, CategoryDataset dataset, int row, int column, int pass) { // do nothing if item is not visible if (!getItemVisible(row, column)) { return; } // if the dataset is not a StatisticalCategoryDataset then just revert // to the superclass (LineAndShapeRenderer) behaviour... if (!(dataset instanceof StatisticalCategoryDataset)) { super.drawItem(g2, state, dataArea, plot, domainAxis, rangeAxis, dataset, row, column, pass); return; } int visibleRow = state.getVisibleSeriesIndex(row); if (visibleRow < 0) { return; } int visibleRowCount = state.getVisibleSeriesCount(); StatisticalCategoryDataset statDataset = (StatisticalCategoryDataset) dataset; Number meanValue = statDataset.getMeanValue(row, column); if (meanValue == null) { return; } PlotOrientation orientation = plot.getOrientation(); // current data point... double x1; if (getUseSeriesOffset()) { x1 = domainAxis.getCategorySeriesMiddle(column, dataset.getColumnCount(), visibleRow, visibleRowCount, getItemMargin(), dataArea, plot.getDomainAxisEdge()); } else { x1 = domainAxis.getCategoryMiddle(column, getColumnCount(), dataArea, plot.getDomainAxisEdge()); } double y1 = rangeAxis.valueToJava2D(meanValue.doubleValue(), dataArea, plot.getRangeAxisEdge()); // draw the standard deviation lines *before* the shapes (if they're // visible) - it looks better if the shape fill colour is different to // the line colour Number sdv = statDataset.getStdDevValue(row, column); if (pass == 1 && sdv != null) { //standard deviation lines RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); double valueDelta = sdv.doubleValue(); double highVal, lowVal; if ((meanValue.doubleValue() + valueDelta) > rangeAxis.getRange().getUpperBound()) { highVal = rangeAxis.valueToJava2D( rangeAxis.getRange().getUpperBound(), dataArea, yAxisLocation); } else { highVal = rangeAxis.valueToJava2D(meanValue.doubleValue() + valueDelta, dataArea, yAxisLocation); } if ((meanValue.doubleValue() + valueDelta) < rangeAxis.getRange().getLowerBound()) { lowVal = rangeAxis.valueToJava2D( rangeAxis.getRange().getLowerBound(), dataArea, yAxisLocation); } else { lowVal = rangeAxis.valueToJava2D(meanValue.doubleValue() - valueDelta, dataArea, yAxisLocation); } if (this.errorIndicatorPaint != null) { g2.setPaint(this.errorIndicatorPaint); } else { g2.setPaint(getItemPaint(row, column)); } if (this.errorIndicatorStroke != null) { g2.setStroke(this.errorIndicatorStroke); } else { g2.setStroke(getItemOutlineStroke(row, column)); } Line2D line = new Line2D.Double(); if (orientation == PlotOrientation.HORIZONTAL) { line.setLine(lowVal, x1, highVal, x1); g2.draw(line); line.setLine(lowVal, x1 - 5.0d, lowVal, x1 + 5.0d); g2.draw(line); line.setLine(highVal, x1 - 5.0d, highVal, x1 + 5.0d); g2.draw(line); } else { // PlotOrientation.VERTICAL line.setLine(x1, lowVal, x1, highVal); g2.draw(line); line.setLine(x1 - 5.0d, highVal, x1 + 5.0d, highVal); g2.draw(line); line.setLine(x1 - 5.0d, lowVal, x1 + 5.0d, lowVal); g2.draw(line); } } Shape hotspot = null; if (pass == 1 && getItemShapeVisible(row, column)) { Shape shape = getItemShape(row, column); if (orientation == PlotOrientation.HORIZONTAL) { shape = ShapeUtils.createTranslatedShape(shape, y1, x1); } else if (orientation == PlotOrientation.VERTICAL) { shape = ShapeUtils.createTranslatedShape(shape, x1, y1); } hotspot = shape; if (getItemShapeFilled(row, column)) { if (getUseFillPaint()) { g2.setPaint(getItemFillPaint(row, column)); } else { g2.setPaint(getItemPaint(row, column)); } g2.fill(shape); } if (getDrawOutlines()) { if (getUseOutlinePaint()) { g2.setPaint(getItemOutlinePaint(row, column)); } else { g2.setPaint(getItemPaint(row, column)); } g2.setStroke(getItemOutlineStroke(row, column)); g2.draw(shape); } // draw the item label if there is one... if (isItemLabelVisible(row, column)) { if (orientation == PlotOrientation.HORIZONTAL) { drawItemLabel(g2, orientation, dataset, row, column, y1, x1, (meanValue.doubleValue() < 0.0)); } else if (orientation == PlotOrientation.VERTICAL) { drawItemLabel(g2, orientation, dataset, row, column, x1, y1, (meanValue.doubleValue() < 0.0)); } } } if (pass == 0 && getItemLineVisible(row, column)) { if (column != 0) { Number previousValue = statDataset.getValue(row, column - 1); if (previousValue != null) { // previous data point... double previous = previousValue.doubleValue(); double x0; if (getUseSeriesOffset()) { x0 = domainAxis.getCategorySeriesMiddle( column - 1, dataset.getColumnCount(), visibleRow, visibleRowCount, getItemMargin(), dataArea, plot.getDomainAxisEdge()); } else { x0 = domainAxis.getCategoryMiddle(column - 1, getColumnCount(), dataArea, plot.getDomainAxisEdge()); } double y0 = rangeAxis.valueToJava2D(previous, dataArea, plot.getRangeAxisEdge()); Line2D line = null; if (orientation == PlotOrientation.HORIZONTAL) { line = new Line2D.Double(y0, x0, y1, x1); } else if (orientation == PlotOrientation.VERTICAL) { line = new Line2D.Double(x0, y0, x1, y1); } g2.setPaint(getItemPaint(row, column)); g2.setStroke(getItemStroke(row, column)); g2.draw(line); } } } if (pass == 1) { // add an item entity, if this information is being collected EntityCollection entities = state.getEntityCollection(); if (entities != null) { addEntity(entities, hotspot, dataset, row, column, x1, y1); } } } /** * Tests this renderer for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof StatisticalLineAndShapeRenderer)) { return false; } StatisticalLineAndShapeRenderer that = (StatisticalLineAndShapeRenderer) obj; if (!PaintUtils.equal(this.errorIndicatorPaint, that.errorIndicatorPaint)) { return false; } if (!Objects.equals(this.errorIndicatorStroke, that.errorIndicatorStroke)) { return false; } return super.equals(obj); } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int hash = super.hashCode(); hash = HashUtils.hashCode(hash, this.errorIndicatorPaint); return hash; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.errorIndicatorPaint, stream); SerialUtils.writeStroke(this.errorIndicatorStroke, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.errorIndicatorPaint = SerialUtils.readPaint(stream); this.errorIndicatorStroke = SerialUtils.readStroke(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/category/WaterfallBarRenderer.java000066400000000000000000000413651463604235500334120ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * WaterfallBarRenderer.java * ------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: Darshan Shah; * Contributor(s): David Gilbert; * */ package org.jfree.chart.renderer.category; import java.awt.Color; import java.awt.GradientPaint; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Stroke; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.labels.CategoryItemLabelGenerator; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.renderer.AbstractRenderer; import org.jfree.chart.ui.GradientPaintTransformType; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.StandardGradientPaintTransformer; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.SerialUtils; import org.jfree.data.Range; import org.jfree.data.category.CategoryDataset; /** * A renderer that handles the drawing of waterfall bar charts, for use with * the {@link CategoryPlot} class. Some quirks to note: *
    *
  • the value in the last category of the dataset should be (redundantly) * specified as the sum of the items in the preceding categories - otherwise * the final bar in the plot will be incorrectly plotted;
  • *
  • the bar colors are defined using special methods in this class - the * inherited methods (for example, * {@link AbstractRenderer#setSeriesPaint(int, Paint)}) are ignored;
  • *
* The example shown here is generated by the * {@code WaterfallChartDemo1.java} program included in the JFreeChart * Demo Collection: *

* WaterfallBarRendererSample.png */ public class WaterfallBarRenderer extends BarRenderer { /** For serialization. */ private static final long serialVersionUID = -2482910643727230911L; /** The paint used to draw the first bar. */ private transient Paint firstBarPaint; /** The paint used to draw the last bar. */ private transient Paint lastBarPaint; /** The paint used to draw bars having positive values. */ private transient Paint positiveBarPaint; /** The paint used to draw bars having negative values. */ private transient Paint negativeBarPaint; /** * Constructs a new renderer with default values for the bar colors. */ public WaterfallBarRenderer() { this(new GradientPaint(0.0f, 0.0f, new Color(0x22, 0x22, 0xFF), 0.0f, 0.0f, new Color(0x66, 0x66, 0xFF)), new GradientPaint(0.0f, 0.0f, new Color(0x22, 0xFF, 0x22), 0.0f, 0.0f, new Color(0x66, 0xFF, 0x66)), new GradientPaint(0.0f, 0.0f, new Color(0xFF, 0x22, 0x22), 0.0f, 0.0f, new Color(0xFF, 0x66, 0x66)), new GradientPaint(0.0f, 0.0f, new Color(0xFF, 0xFF, 0x22), 0.0f, 0.0f, new Color(0xFF, 0xFF, 0x66))); } /** * Constructs a new waterfall renderer. * * @param firstBarPaint the color of the first bar ({@code null} not * permitted). * @param positiveBarPaint the color for bars with positive values * ({@code null} not permitted). * @param negativeBarPaint the color for bars with negative values * ({@code null} not permitted). * @param lastBarPaint the color of the last bar ({@code null} not * permitted). */ public WaterfallBarRenderer(Paint firstBarPaint, Paint positiveBarPaint, Paint negativeBarPaint, Paint lastBarPaint) { super(); Args.nullNotPermitted(firstBarPaint, "firstBarPaint"); Args.nullNotPermitted(positiveBarPaint, "positiveBarPaint"); Args.nullNotPermitted(negativeBarPaint, "negativeBarPaint"); Args.nullNotPermitted(lastBarPaint, "lastBarPaint"); this.firstBarPaint = firstBarPaint; this.lastBarPaint = lastBarPaint; this.positiveBarPaint = positiveBarPaint; this.negativeBarPaint = negativeBarPaint; setGradientPaintTransformer(new StandardGradientPaintTransformer( GradientPaintTransformType.CENTER_VERTICAL)); setMinimumBarLength(1.0); } /** * Returns the paint used to draw the first bar. * * @return The paint (never {@code null}). */ public Paint getFirstBarPaint() { return this.firstBarPaint; } /** * Sets the paint that will be used to draw the first bar and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). */ public void setFirstBarPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.firstBarPaint = paint; fireChangeEvent(); } /** * Returns the paint used to draw the last bar. * * @return The paint (never {@code null}). */ public Paint getLastBarPaint() { return this.lastBarPaint; } /** * Sets the paint that will be used to draw the last bar and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). */ public void setLastBarPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.lastBarPaint = paint; fireChangeEvent(); } /** * Returns the paint used to draw bars with positive values. * * @return The paint (never {@code null}). */ public Paint getPositiveBarPaint() { return this.positiveBarPaint; } /** * Sets the paint that will be used to draw bars having positive values. * * @param paint the paint ({@code null} not permitted). */ public void setPositiveBarPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.positiveBarPaint = paint; fireChangeEvent(); } /** * Returns the paint used to draw bars with negative values. * * @return The paint (never {@code null}). */ public Paint getNegativeBarPaint() { return this.negativeBarPaint; } /** * Sets the paint that will be used to draw bars having negative values, * and sends a {@link RendererChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). */ public void setNegativeBarPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.negativeBarPaint = paint; fireChangeEvent(); } /** * Returns the range of values the renderer requires to display all the * items from the specified dataset. * * @param dataset the dataset ({@code null} not permitted). * * @return The range (or {@code null} if the dataset is empty). */ @Override public Range findRangeBounds(CategoryDataset dataset) { if (dataset == null) { return null; } boolean allItemsNull = true; // we'll set this to false if there is at // least one non-null data item... double minimum = 0.0; double maximum = 0.0; int columnCount = dataset.getColumnCount(); for (int row = 0; row < dataset.getRowCount(); row++) { double runningTotal = 0.0; for (int column = 0; column <= columnCount - 1; column++) { Number n = dataset.getValue(row, column); if (n != null) { allItemsNull = false; double value = n.doubleValue(); if (column == columnCount - 1) { // treat the last column value as an absolute runningTotal = value; } else { runningTotal = runningTotal + value; } minimum = Math.min(minimum, runningTotal); maximum = Math.max(maximum, runningTotal); } } } if (!allItemsNull) { return new Range(minimum, maximum); } else { return null; } } /** * Draws the bar for a single (series, category) data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the data area. * @param plot the plot. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset. * @param row the row index (zero-based). * @param column the column index (zero-based). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, CategoryItemRendererState state, Rectangle2D dataArea, CategoryPlot plot, CategoryAxis domainAxis, ValueAxis rangeAxis, CategoryDataset dataset, int row, int column, int pass) { double previous = state.getSeriesRunningTotal(); if (column == dataset.getColumnCount() - 1) { previous = 0.0; } double current = 0.0; Number n = dataset.getValue(row, column); if (n != null) { current = previous + n.doubleValue(); } state.setSeriesRunningTotal(current); int categoryCount = getColumnCount(); PlotOrientation orientation = plot.getOrientation(); double rectX = 0.0; double rectY = 0.0; RectangleEdge rangeAxisLocation = plot.getRangeAxisEdge(); // Y0 double j2dy0 = rangeAxis.valueToJava2D(previous, dataArea, rangeAxisLocation); // Y1 double j2dy1 = rangeAxis.valueToJava2D(current, dataArea, rangeAxisLocation); double valDiff = current - previous; if (j2dy1 < j2dy0) { double temp = j2dy1; j2dy1 = j2dy0; j2dy0 = temp; } // BAR WIDTH double rectWidth = state.getBarWidth(); // BAR HEIGHT double rectHeight = Math.max(getMinimumBarLength(), Math.abs(j2dy1 - j2dy0)); Comparable seriesKey = dataset.getRowKey(row); Comparable categoryKey = dataset.getColumnKey(column); if (orientation == PlotOrientation.HORIZONTAL) { rectY = domainAxis.getCategorySeriesMiddle(categoryKey, seriesKey, dataset, getItemMargin(), dataArea, RectangleEdge.LEFT); rectX = j2dy0; rectHeight = state.getBarWidth(); rectY = rectY - rectHeight / 2.0; rectWidth = Math.max(getMinimumBarLength(), Math.abs(j2dy1 - j2dy0)); } else if (orientation == PlotOrientation.VERTICAL) { rectX = domainAxis.getCategorySeriesMiddle(categoryKey, seriesKey, dataset, getItemMargin(), dataArea, RectangleEdge.TOP); rectX = rectX - rectWidth / 2.0; rectY = j2dy0; } Rectangle2D bar = new Rectangle2D.Double(rectX, rectY, rectWidth, rectHeight); Paint seriesPaint; if (column == 0) { seriesPaint = getFirstBarPaint(); } else if (column == categoryCount - 1) { seriesPaint = getLastBarPaint(); } else { if (valDiff >= 0.0) { seriesPaint = getPositiveBarPaint(); } else { seriesPaint = getNegativeBarPaint(); } } if (getGradientPaintTransformer() != null && seriesPaint instanceof GradientPaint) { GradientPaint gp = (GradientPaint) seriesPaint; seriesPaint = getGradientPaintTransformer().transform(gp, bar); } g2.setPaint(seriesPaint); g2.fill(bar); // draw the outline... if (isDrawBarOutline() && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) { Stroke stroke = getItemOutlineStroke(row, column); Paint paint = getItemOutlinePaint(row, column); if (stroke != null && paint != null) { g2.setStroke(stroke); g2.setPaint(paint); g2.draw(bar); } } CategoryItemLabelGenerator generator = getItemLabelGenerator(row, column); if (generator != null && isItemLabelVisible(row, column)) { drawItemLabel(g2, dataset, row, column, plot, generator, bar, (valDiff < 0.0)); } // add an item entity, if this information is being collected EntityCollection entities = state.getEntityCollection(); if (entities != null) { addItemEntity(entities, dataset, row, column, bar); } } /** * Tests an object for equality with this instance. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!super.equals(obj)) { return false; } if (!(obj instanceof WaterfallBarRenderer)) { return false; } WaterfallBarRenderer that = (WaterfallBarRenderer) obj; if (!PaintUtils.equal(this.firstBarPaint, that.firstBarPaint)) { return false; } if (!PaintUtils.equal(this.lastBarPaint, that.lastBarPaint)) { return false; } if (!PaintUtils.equal(this.positiveBarPaint, that.positiveBarPaint)) { return false; } if (!PaintUtils.equal(this.negativeBarPaint, that.negativeBarPaint)) { return false; } return true; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.firstBarPaint, stream); SerialUtils.writePaint(this.lastBarPaint, stream); SerialUtils.writePaint(this.positiveBarPaint, stream); SerialUtils.writePaint(this.negativeBarPaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.firstBarPaint = SerialUtils.readPaint(stream); this.lastBarPaint = SerialUtils.readPaint(stream); this.positiveBarPaint = SerialUtils.readPaint(stream); this.negativeBarPaint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/category/package.html000066400000000000000000000002671463604235500307670ustar00rootroot00000000000000 Plug-in renderers for the {@link org.jfree.chart.plot.CategoryPlot} class. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/package.html000066400000000000000000000003721463604235500271470ustar00rootroot00000000000000 Core support for the plug-in renderers used by the {@link org.jfree.chart.plot.CategoryPlot} and {@link org.jfree.chart.plot.XYPlot} classes. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/000077500000000000000000000000001463604235500253245ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/AbstractXYItemRenderer.java000066400000000000000000001731251463604235500325320ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * AbstractXYItemRenderer.java * --------------------------- * (C) Copyright 2002-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Richard Atkinson; * Focus Computer Services Limited; * Tim Bardzil; * Sergei Ivanov; * Peter Kolb (patch 2809117); * Martin Krauskopf; */ package org.jfree.chart.renderer.xy; import java.awt.AlphaComposite; import java.awt.Composite; import java.awt.Font; import java.awt.GradientPaint; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Ellipse2D; import java.awt.geom.GeneralPath; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; import org.jfree.chart.LegendItem; import org.jfree.chart.LegendItemCollection; import org.jfree.chart.annotations.Annotation; import org.jfree.chart.annotations.XYAnnotation; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.entity.XYItemEntity; import org.jfree.chart.event.AnnotationChangeEvent; import org.jfree.chart.event.AnnotationChangeListener; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.labels.ItemLabelPosition; import org.jfree.chart.labels.StandardXYSeriesLabelGenerator; import org.jfree.chart.labels.XYItemLabelGenerator; import org.jfree.chart.labels.XYSeriesLabelGenerator; import org.jfree.chart.labels.XYToolTipGenerator; import org.jfree.chart.plot.CrosshairState; import org.jfree.chart.plot.DrawingSupplier; import org.jfree.chart.plot.IntervalMarker; import org.jfree.chart.plot.Marker; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.ValueMarker; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.AbstractRenderer; import org.jfree.chart.text.TextUtils; import org.jfree.chart.ui.GradientPaintTransformer; import org.jfree.chart.ui.Layer; import org.jfree.chart.ui.LengthAdjustmentType; import org.jfree.chart.ui.RectangleAnchor; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.urls.XYURLGenerator; import org.jfree.chart.util.CloneUtils; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.Range; import org.jfree.data.general.DatasetUtils; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYItemKey; /** * A base class that can be used to create new {@link XYItemRenderer} * implementations. */ public abstract class AbstractXYItemRenderer extends AbstractRenderer implements XYItemRenderer, AnnotationChangeListener, Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 8019124836026607990L; /** The plot. */ private XYPlot plot; /** A list of item label generators (one per series). */ private Map itemLabelGeneratorMap; /** The default item label generator. */ private XYItemLabelGenerator defaultItemLabelGenerator; /** A list of tool tip generators (one per series). */ private Map toolTipGeneratorMap; /** The default tool tip generator. */ private XYToolTipGenerator defaultToolTipGenerator; /** The URL text generator. */ private XYURLGenerator urlGenerator; /** * Annotations to be drawn in the background layer ('underneath' the data * items). */ private List backgroundAnnotations; /** * Annotations to be drawn in the foreground layer ('on top' of the data * items). */ private List foregroundAnnotations; /** The legend item label generator. */ private XYSeriesLabelGenerator legendItemLabelGenerator; /** The legend item tool tip generator. */ private XYSeriesLabelGenerator legendItemToolTipGenerator; /** The legend item URL generator. */ private XYSeriesLabelGenerator legendItemURLGenerator; /** * Creates a renderer where the tooltip generator and the URL generator are * both {@code null}. */ protected AbstractXYItemRenderer() { super(); this.itemLabelGeneratorMap = new HashMap<>(); this.toolTipGeneratorMap = new HashMap<>(); this.urlGenerator = null; this.backgroundAnnotations = new java.util.ArrayList(); this.foregroundAnnotations = new java.util.ArrayList(); this.legendItemLabelGenerator = new StandardXYSeriesLabelGenerator( "{0}"); } /** * Returns the number of passes through the data that the renderer requires * in order to draw the chart. Most charts will require a single pass, but * some require two passes. * * @return The pass count. */ @Override public int getPassCount() { return 1; } /** * Returns the plot that the renderer is assigned to. * * @return The plot (possibly {@code null}). */ @Override public XYPlot getPlot() { return this.plot; } /** * Sets the plot that the renderer is assigned to. * * @param plot the plot ({@code null} permitted). */ @Override public void setPlot(XYPlot plot) { this.plot = plot; } /** * Initialises the renderer and returns a state object that should be * passed to all subsequent calls to the drawItem() method. *

* This method will be called before the first item is rendered, giving the * renderer an opportunity to initialise any state information it wants to * maintain. The renderer can do nothing if it chooses. * * @param g2 the graphics device. * @param dataArea the area inside the axes. * @param plot the plot. * @param dataset the dataset. * @param info an optional info collection object to return data back to * the caller. * * @return The renderer state (never {@code null}). */ @Override public XYItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, XYPlot plot, XYDataset dataset, PlotRenderingInfo info) { return new XYItemRendererState(info); } /** * Adds a {@code KEY_BEGIN_ELEMENT} hint to the graphics target. This * hint is recognised by JFreeSVG (in theory it could be used by * other {@code Graphics2D} implementations also). * * @param g2 the graphics target ({@code null} not permitted). * @param seriesKey the series key that identifies the element * ({@code null} not permitted). * @param itemIndex the item index. */ protected void beginElementGroup(Graphics2D g2, Comparable seriesKey, int itemIndex) { beginElementGroup(g2, new XYItemKey(seriesKey, itemIndex)); } // ITEM LABEL GENERATOR /** * Returns the label generator for a data item. This implementation simply * passes control to the {@link #getSeriesItemLabelGenerator(int)} method. * If, for some reason, you want a different generator for individual * items, you can override this method. * * @param series the series index (zero based). * @param item the item index (zero based). * * @return The generator (possibly {@code null}). */ @Override public XYItemLabelGenerator getItemLabelGenerator(int series, int item) { // otherwise look up the generator table XYItemLabelGenerator generator = this.itemLabelGeneratorMap.get(series); if (generator == null) { generator = this.defaultItemLabelGenerator; } return generator; } /** * Returns the item label generator for a series. * * @param series the series index (zero based). * * @return The generator (possibly {@code null}). */ @Override public XYItemLabelGenerator getSeriesItemLabelGenerator(int series) { return this.itemLabelGeneratorMap.get(series); } /** * Sets the item label generator for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero based). * @param generator the generator ({@code null} permitted). */ @Override public void setSeriesItemLabelGenerator(int series, XYItemLabelGenerator generator) { this.itemLabelGeneratorMap.put(series, generator); fireChangeEvent(); } /** * Returns the default item label generator. * * @return The generator (possibly {@code null}). */ @Override public XYItemLabelGenerator getDefaultItemLabelGenerator() { return this.defaultItemLabelGenerator; } /** * Sets the default item label generator and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param generator the generator ({@code null} permitted). */ @Override public void setDefaultItemLabelGenerator(XYItemLabelGenerator generator) { this.defaultItemLabelGenerator = generator; fireChangeEvent(); } // TOOL TIP GENERATOR /** * Returns the tool tip generator for a data item. If, for some reason, * you want a different generator for individual items, you can override * this method. * * @param series the series index (zero based). * @param item the item index (zero based). * * @return The generator (possibly {@code null}). */ @Override public XYToolTipGenerator getToolTipGenerator(int series, int item) { // otherwise look up the generator table XYToolTipGenerator generator = this.toolTipGeneratorMap.get(series); if (generator == null) { generator = this.defaultToolTipGenerator; } return generator; } /** * Returns the tool tip generator for a series. * * @param series the series index (zero based). * * @return The generator (possibly {@code null}). */ @Override public XYToolTipGenerator getSeriesToolTipGenerator(int series) { return this.toolTipGeneratorMap.get(series); } /** * Sets the tool tip generator for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero based). * @param generator the generator ({@code null} permitted). */ @Override public void setSeriesToolTipGenerator(int series, XYToolTipGenerator generator) { this.toolTipGeneratorMap.put(series, generator); fireChangeEvent(); } /** * Returns the default tool tip generator. * * @return The generator (possibly {@code null}). * * @see #setDefaultToolTipGenerator(XYToolTipGenerator) */ @Override public XYToolTipGenerator getDefaultToolTipGenerator() { return this.defaultToolTipGenerator; } /** * Sets the default tool tip generator and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param generator the generator ({@code null} permitted). * * @see #getDefaultToolTipGenerator() */ @Override public void setDefaultToolTipGenerator(XYToolTipGenerator generator) { this.defaultToolTipGenerator = generator; fireChangeEvent(); } // URL GENERATOR /** * Returns the URL generator for HTML image maps. * * @return The URL generator (possibly {@code null}). */ @Override public XYURLGenerator getURLGenerator() { return this.urlGenerator; } /** * Sets the URL generator for HTML image maps and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param urlGenerator the URL generator ({@code null} permitted). */ @Override public void setURLGenerator(XYURLGenerator urlGenerator) { this.urlGenerator = urlGenerator; fireChangeEvent(); } /** * Adds an annotation and sends a {@link RendererChangeEvent} to all * registered listeners. The annotation is added to the foreground * layer. * * @param annotation the annotation ({@code null} not permitted). */ @Override public void addAnnotation(XYAnnotation annotation) { // defer argument checking addAnnotation(annotation, Layer.FOREGROUND); } /** * Adds an annotation to the specified layer and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param annotation the annotation ({@code null} not permitted). * @param layer the layer ({@code null} not permitted). */ @Override public void addAnnotation(XYAnnotation annotation, Layer layer) { Args.nullNotPermitted(annotation, "annotation"); if (layer.equals(Layer.FOREGROUND)) { this.foregroundAnnotations.add(annotation); annotation.addChangeListener(this); fireChangeEvent(); } else if (layer.equals(Layer.BACKGROUND)) { this.backgroundAnnotations.add(annotation); annotation.addChangeListener(this); fireChangeEvent(); } else { // should never get here throw new RuntimeException("Unknown layer."); } } /** * Removes the specified annotation and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param annotation the annotation to remove ({@code null} not * permitted). * * @return A boolean to indicate whether or not the annotation was * successfully removed. */ @Override public boolean removeAnnotation(XYAnnotation annotation) { boolean removed = this.foregroundAnnotations.remove(annotation); removed = removed & this.backgroundAnnotations.remove(annotation); annotation.removeChangeListener(this); fireChangeEvent(); return removed; } /** * Removes all annotations and sends a {@link RendererChangeEvent} * to all registered listeners. */ @Override public void removeAnnotations() { for(int i = 0; i < this.foregroundAnnotations.size(); i++){ XYAnnotation annotation = (XYAnnotation) this.foregroundAnnotations.get(i); annotation.removeChangeListener(this); } for(int i = 0; i < this.backgroundAnnotations.size(); i++){ XYAnnotation annotation = (XYAnnotation) this.backgroundAnnotations.get(i); annotation.removeChangeListener(this); } this.foregroundAnnotations.clear(); this.backgroundAnnotations.clear(); fireChangeEvent(); } /** * Receives notification of a change to an {@link Annotation} added to * this renderer. * * @param event information about the event (not used here). */ @Override public void annotationChanged(AnnotationChangeEvent event) { fireChangeEvent(); } /** * Returns a collection of the annotations that are assigned to the * renderer. * * @return A collection of annotations (possibly empty but never * {@code null}). */ public Collection getAnnotations() { List result = new java.util.ArrayList(this.foregroundAnnotations); result.addAll(this.backgroundAnnotations); return result; } /** * Returns the legend item label generator. * * @return The label generator (never {@code null}). * * @see #setLegendItemLabelGenerator(XYSeriesLabelGenerator) */ @Override public XYSeriesLabelGenerator getLegendItemLabelGenerator() { return this.legendItemLabelGenerator; } /** * Sets the legend item label generator and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param generator the generator ({@code null} not permitted). * * @see #getLegendItemLabelGenerator() */ @Override public void setLegendItemLabelGenerator(XYSeriesLabelGenerator generator) { Args.nullNotPermitted(generator, "generator"); this.legendItemLabelGenerator = generator; fireChangeEvent(); } /** * Returns the legend item tool tip generator. * * @return The tool tip generator (possibly {@code null}). * * @see #setLegendItemToolTipGenerator(XYSeriesLabelGenerator) */ public XYSeriesLabelGenerator getLegendItemToolTipGenerator() { return this.legendItemToolTipGenerator; } /** * Sets the legend item tool tip generator and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param generator the generator ({@code null} permitted). * * @see #getLegendItemToolTipGenerator() */ public void setLegendItemToolTipGenerator( XYSeriesLabelGenerator generator) { this.legendItemToolTipGenerator = generator; fireChangeEvent(); } /** * Returns the legend item URL generator. * * @return The URL generator (possibly {@code null}). * * @see #setLegendItemURLGenerator(XYSeriesLabelGenerator) */ public XYSeriesLabelGenerator getLegendItemURLGenerator() { return this.legendItemURLGenerator; } /** * Sets the legend item URL generator and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param generator the generator ({@code null} permitted). * * @see #getLegendItemURLGenerator() */ public void setLegendItemURLGenerator(XYSeriesLabelGenerator generator) { this.legendItemURLGenerator = generator; fireChangeEvent(); } /** * Returns the lower and upper bounds (range) of the x-values in the * specified dataset. * * @param dataset the dataset ({@code null} permitted). * * @return The range ({@code null} if the dataset is {@code null} * or empty). * * @see #findRangeBounds(XYDataset) */ @Override public Range findDomainBounds(XYDataset dataset) { return findDomainBounds(dataset, false); } /** * Returns the lower and upper bounds (range) of the x-values in the * specified dataset. * * @param dataset the dataset ({@code null} permitted). * @param includeInterval include the interval (if any) for the dataset? * * @return The range ({@code null} if the dataset is {@code null} * or empty). */ protected Range findDomainBounds(XYDataset dataset, boolean includeInterval) { if (dataset == null) { return null; } if (getDataBoundsIncludesVisibleSeriesOnly()) { List visibleSeriesKeys = new ArrayList(); int seriesCount = dataset.getSeriesCount(); for (int s = 0; s < seriesCount; s++) { if (isSeriesVisible(s)) { visibleSeriesKeys.add(dataset.getSeriesKey(s)); } } return DatasetUtils.findDomainBounds(dataset, visibleSeriesKeys, includeInterval); } return DatasetUtils.findDomainBounds(dataset, includeInterval); } /** * Returns the range of values the renderer requires to display all the * items from the specified dataset. * * @param dataset the dataset ({@code null} permitted). * * @return The range ({@code null} if the dataset is {@code null} * or empty). * * @see #findDomainBounds(XYDataset) */ @Override public Range findRangeBounds(XYDataset dataset) { return findRangeBounds(dataset, false); } /** * Returns the range of values the renderer requires to display all the * items from the specified dataset. * * @param dataset the dataset ({@code null} permitted). * @param includeInterval include the interval (if any) for the dataset? * * @return The range ({@code null} if the dataset is {@code null} * or empty). */ protected Range findRangeBounds(XYDataset dataset, boolean includeInterval) { if (dataset == null) { return null; } if (getDataBoundsIncludesVisibleSeriesOnly()) { List visibleSeriesKeys = new ArrayList(); int seriesCount = dataset.getSeriesCount(); for (int s = 0; s < seriesCount; s++) { if (isSeriesVisible(s)) { visibleSeriesKeys.add(dataset.getSeriesKey(s)); } } // the bounds should be calculated using just the items within // the current range of the x-axis...if there is one Range xRange = null; XYPlot p = getPlot(); if (p != null) { ValueAxis xAxis = null; int index = p.getIndexOf(this); if (index >= 0) { xAxis = this.plot.getDomainAxisForDataset(index); } if (xAxis != null) { xRange = xAxis.getRange(); } } if (xRange == null) { xRange = new Range(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); } return DatasetUtils.findRangeBounds(dataset, visibleSeriesKeys, xRange, includeInterval); } return DatasetUtils.findRangeBounds(dataset, includeInterval); } /** * Returns a (possibly empty) collection of legend items for the series * that this renderer is responsible for drawing. * * @return The legend item collection (never {@code null}). */ @Override public LegendItemCollection getLegendItems() { if (this.plot == null) { return new LegendItemCollection(); } LegendItemCollection result = new LegendItemCollection(); int index = this.plot.getIndexOf(this); XYDataset dataset = this.plot.getDataset(index); if (dataset != null) { int seriesCount = dataset.getSeriesCount(); for (int i = 0; i < seriesCount; i++) { if (isSeriesVisibleInLegend(i)) { LegendItem item = getLegendItem(index, i); if (item != null) { result.add(item); } } } } return result; } /** * Returns a default legend item for the specified series. Subclasses * should override this method to generate customised items. * * @param datasetIndex the dataset index (zero-based). * @param series the series index (zero-based). * * @return A legend item for the series. */ @Override public LegendItem getLegendItem(int datasetIndex, int series) { XYPlot xyplot = getPlot(); if (xyplot == null) { return null; } XYDataset dataset = xyplot.getDataset(datasetIndex); if (dataset == null) { return null; } String label = this.legendItemLabelGenerator.generateLabel(dataset, series); String description = label; String toolTipText = null; if (getLegendItemToolTipGenerator() != null) { toolTipText = getLegendItemToolTipGenerator().generateLabel( dataset, series); } String urlText = null; if (getLegendItemURLGenerator() != null) { urlText = getLegendItemURLGenerator().generateLabel(dataset, series); } Shape shape = lookupLegendShape(series); Paint paint = lookupSeriesPaint(series); LegendItem item = new LegendItem(label, paint); item.setToolTipText(toolTipText); item.setURLText(urlText); item.setLabelFont(lookupLegendTextFont(series)); Paint labelPaint = lookupLegendTextPaint(series); if (labelPaint != null) { item.setLabelPaint(labelPaint); } item.setSeriesKey(dataset.getSeriesKey(series)); item.setSeriesIndex(series); item.setDataset(dataset); item.setDatasetIndex(datasetIndex); if (getTreatLegendShapeAsLine()) { item.setLineVisible(true); item.setLine(shape); item.setLinePaint(paint); item.setShapeVisible(false); } else { Paint outlinePaint = lookupSeriesOutlinePaint(series); Stroke outlineStroke = lookupSeriesOutlineStroke(series); item.setOutlinePaint(outlinePaint); item.setOutlineStroke(outlineStroke); } return item; } /** * Fills a band between two values on the axis. This can be used to color * bands between the grid lines. * * @param g2 the graphics device. * @param plot the plot. * @param axis the domain axis. * @param dataArea the data area. * @param start the start value. * @param end the end value. */ @Override public void fillDomainGridBand(Graphics2D g2, XYPlot plot, ValueAxis axis, Rectangle2D dataArea, double start, double end) { double x1 = axis.valueToJava2D(start, dataArea, plot.getDomainAxisEdge()); double x2 = axis.valueToJava2D(end, dataArea, plot.getDomainAxisEdge()); Rectangle2D band; if (plot.getOrientation() == PlotOrientation.VERTICAL) { band = new Rectangle2D.Double(Math.min(x1, x2), dataArea.getMinY(), Math.abs(x2 - x1), dataArea.getHeight()); } else { band = new Rectangle2D.Double(dataArea.getMinX(), Math.min(x1, x2), dataArea.getWidth(), Math.abs(x2 - x1)); } Paint paint = plot.getDomainTickBandPaint(); if (paint != null) { g2.setPaint(paint); g2.fill(band); } } /** * Fills a band between two values on the range axis. This can be used to * color bands between the grid lines. * * @param g2 the graphics device. * @param plot the plot. * @param axis the range axis. * @param dataArea the data area. * @param start the start value. * @param end the end value. */ @Override public void fillRangeGridBand(Graphics2D g2, XYPlot plot, ValueAxis axis, Rectangle2D dataArea, double start, double end) { double y1 = axis.valueToJava2D(start, dataArea, plot.getRangeAxisEdge()); double y2 = axis.valueToJava2D(end, dataArea, plot.getRangeAxisEdge()); Rectangle2D band; if (plot.getOrientation() == PlotOrientation.VERTICAL) { band = new Rectangle2D.Double(dataArea.getMinX(), Math.min(y1, y2), dataArea.getWidth(), Math.abs(y2 - y1)); } else { band = new Rectangle2D.Double(Math.min(y1, y2), dataArea.getMinY(), Math.abs(y2 - y1), dataArea.getHeight()); } Paint paint = plot.getRangeTickBandPaint(); if (paint != null) { g2.setPaint(paint); g2.fill(band); } } /** * Draws a line perpendicular to the domain axis. * * @param g2 the graphics device. * @param plot the plot. * @param axis the value axis. * @param dataArea the area for plotting data. * @param value the value at which the grid line should be drawn. * @param paint the paint ({@code null} not permitted). * @param stroke the stroke ({@code null} not permitted). */ @Override public void drawDomainLine(Graphics2D g2, XYPlot plot, ValueAxis axis, Rectangle2D dataArea, double value, Paint paint, Stroke stroke) { Range range = axis.getRange(); if (!range.contains(value)) { return; } PlotOrientation orientation = plot.getOrientation(); Line2D line = null; double v = axis.valueToJava2D(value, dataArea, plot.getDomainAxisEdge()); if (orientation.isHorizontal()) { line = new Line2D.Double(dataArea.getMinX(), v, dataArea.getMaxX(), v); } else if (orientation.isVertical()) { line = new Line2D.Double(v, dataArea.getMinY(), v, dataArea.getMaxY()); } g2.setPaint(paint); g2.setStroke(stroke); Object saved = g2.getRenderingHint(RenderingHints.KEY_STROKE_CONTROL); g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE); g2.draw(line); g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, saved); } /** * Draws a line perpendicular to the range axis. * * @param g2 the graphics device. * @param plot the plot. * @param axis the value axis. * @param dataArea the area for plotting data. * @param value the value at which the grid line should be drawn. * @param paint the paint. * @param stroke the stroke. */ @Override public void drawRangeLine(Graphics2D g2, XYPlot plot, ValueAxis axis, Rectangle2D dataArea, double value, Paint paint, Stroke stroke) { Range range = axis.getRange(); if (!range.contains(value)) { return; } PlotOrientation orientation = plot.getOrientation(); Line2D line = null; double v = axis.valueToJava2D(value, dataArea, plot.getRangeAxisEdge()); if (orientation == PlotOrientation.HORIZONTAL) { line = new Line2D.Double(v, dataArea.getMinY(), v, dataArea.getMaxY()); } else if (orientation == PlotOrientation.VERTICAL) { line = new Line2D.Double(dataArea.getMinX(), v, dataArea.getMaxX(), v); } g2.setPaint(paint); g2.setStroke(stroke); Object saved = g2.getRenderingHint(RenderingHints.KEY_STROKE_CONTROL); g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE); g2.draw(line); g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, saved); } /** * Draws a line on the chart perpendicular to the x-axis to mark * a value or range of values. * * @param g2 the graphics device. * @param plot the plot. * @param domainAxis the domain axis. * @param marker the marker line. * @param dataArea the axis data area. */ @Override public void drawDomainMarker(Graphics2D g2, XYPlot plot, ValueAxis domainAxis, Marker marker, Rectangle2D dataArea) { if (marker instanceof ValueMarker) { ValueMarker vm = (ValueMarker) marker; double value = vm.getValue(); Range range = domainAxis.getRange(); if (!range.contains(value)) { return; } double v = domainAxis.valueToJava2D(value, dataArea, plot.getDomainAxisEdge()); PlotOrientation orientation = plot.getOrientation(); Line2D line = null; if (orientation == PlotOrientation.HORIZONTAL) { line = new Line2D.Double(dataArea.getMinX(), v, dataArea.getMaxX(), v); } else if (orientation == PlotOrientation.VERTICAL) { line = new Line2D.Double(v, dataArea.getMinY(), v, dataArea.getMaxY()); } else { throw new IllegalStateException("Unrecognised orientation."); } final Composite originalComposite = g2.getComposite(); g2.setComposite(AlphaComposite.getInstance( AlphaComposite.SRC_OVER, marker.getAlpha())); g2.setPaint(marker.getPaint()); g2.setStroke(marker.getStroke()); g2.draw(line); String label = marker.getLabel(); RectangleAnchor anchor = marker.getLabelAnchor(); if (label != null) { Font labelFont = marker.getLabelFont(); g2.setFont(labelFont); Point2D coords = calculateDomainMarkerTextAnchorPoint( g2, orientation, dataArea, line.getBounds2D(), marker.getLabelOffset(), LengthAdjustmentType.EXPAND, anchor); Rectangle2D r = TextUtils.calcAlignedStringBounds(label, g2, (float) coords.getX(), (float) coords.getY(), marker.getLabelTextAnchor()); g2.setPaint(marker.getLabelBackgroundColor()); g2.fill(r); g2.setPaint(marker.getLabelPaint()); TextUtils.drawAlignedString(label, g2, (float) coords.getX(), (float) coords.getY(), marker.getLabelTextAnchor()); } g2.setComposite(originalComposite); } else if (marker instanceof IntervalMarker) { IntervalMarker im = (IntervalMarker) marker; double start = im.getStartValue(); double end = im.getEndValue(); Range range = domainAxis.getRange(); if (!(range.intersects(start, end))) { return; } double start2d = domainAxis.valueToJava2D(start, dataArea, plot.getDomainAxisEdge()); double end2d = domainAxis.valueToJava2D(end, dataArea, plot.getDomainAxisEdge()); double low = Math.min(start2d, end2d); double high = Math.max(start2d, end2d); PlotOrientation orientation = plot.getOrientation(); Rectangle2D rect = null; if (orientation == PlotOrientation.HORIZONTAL) { // clip top and bottom bounds to data area low = Math.max(low, dataArea.getMinY()); high = Math.min(high, dataArea.getMaxY()); rect = new Rectangle2D.Double(dataArea.getMinX(), low, dataArea.getWidth(), high - low); } else if (orientation == PlotOrientation.VERTICAL) { // clip left and right bounds to data area low = Math.max(low, dataArea.getMinX()); high = Math.min(high, dataArea.getMaxX()); rect = new Rectangle2D.Double(low, dataArea.getMinY(), high - low, dataArea.getHeight()); } final Composite originalComposite = g2.getComposite(); g2.setComposite(AlphaComposite.getInstance( AlphaComposite.SRC_OVER, marker.getAlpha())); Paint p = marker.getPaint(); if (p instanceof GradientPaint) { GradientPaint gp = (GradientPaint) p; GradientPaintTransformer t = im.getGradientPaintTransformer(); if (t != null) { gp = t.transform(gp, rect); } g2.setPaint(gp); } else { g2.setPaint(p); } g2.fill(rect); // now draw the outlines, if visible... if (im.getOutlinePaint() != null && im.getOutlineStroke() != null) { if (orientation == PlotOrientation.VERTICAL) { Line2D line = new Line2D.Double(); double y0 = dataArea.getMinY(); double y1 = dataArea.getMaxY(); g2.setPaint(im.getOutlinePaint()); g2.setStroke(im.getOutlineStroke()); if (range.contains(start)) { line.setLine(start2d, y0, start2d, y1); g2.draw(line); } if (range.contains(end)) { line.setLine(end2d, y0, end2d, y1); g2.draw(line); } } else { // PlotOrientation.HORIZONTAL Line2D line = new Line2D.Double(); double x0 = dataArea.getMinX(); double x1 = dataArea.getMaxX(); g2.setPaint(im.getOutlinePaint()); g2.setStroke(im.getOutlineStroke()); if (range.contains(start)) { line.setLine(x0, start2d, x1, start2d); g2.draw(line); } if (range.contains(end)) { line.setLine(x0, end2d, x1, end2d); g2.draw(line); } } } String label = marker.getLabel(); RectangleAnchor anchor = marker.getLabelAnchor(); if (label != null) { Font labelFont = marker.getLabelFont(); g2.setFont(labelFont); Point2D coords = calculateDomainMarkerTextAnchorPoint( g2, orientation, dataArea, rect, marker.getLabelOffset(), marker.getLabelOffsetType(), anchor); Rectangle2D r = TextUtils.calcAlignedStringBounds(label, g2, (float) coords.getX(), (float) coords.getY(), marker.getLabelTextAnchor()); g2.setPaint(marker.getLabelBackgroundColor()); g2.fill(r); g2.setPaint(marker.getLabelPaint()); TextUtils.drawAlignedString(label, g2, (float) coords.getX(), (float) coords.getY(), marker.getLabelTextAnchor()); } g2.setComposite(originalComposite); } } /** * Calculates the {@code (x, y)} coordinates for drawing a marker label. * * @param g2 the graphics device. * @param orientation the plot orientation. * @param dataArea the data area. * @param markerArea the rectangle surrounding the marker area. * @param markerOffset the marker label offset. * @param labelOffsetType the label offset type. * @param anchor the label anchor. * * @return The coordinates for drawing the marker label. */ protected Point2D calculateDomainMarkerTextAnchorPoint(Graphics2D g2, PlotOrientation orientation, Rectangle2D dataArea, Rectangle2D markerArea, RectangleInsets markerOffset, LengthAdjustmentType labelOffsetType, RectangleAnchor anchor) { Rectangle2D anchorRect = null; if (orientation == PlotOrientation.HORIZONTAL) { anchorRect = markerOffset.createAdjustedRectangle(markerArea, LengthAdjustmentType.CONTRACT, labelOffsetType); } else if (orientation == PlotOrientation.VERTICAL) { anchorRect = markerOffset.createAdjustedRectangle(markerArea, labelOffsetType, LengthAdjustmentType.CONTRACT); } return anchor.getAnchorPoint(anchorRect); } /** * Draws a line on the chart perpendicular to the y-axis to mark a value * or range of values. * * @param g2 the graphics device. * @param plot the plot. * @param rangeAxis the range axis. * @param marker the marker line. * @param dataArea the axis data area. */ @Override public void drawRangeMarker(Graphics2D g2, XYPlot plot, ValueAxis rangeAxis, Marker marker, Rectangle2D dataArea) { if (marker instanceof ValueMarker) { ValueMarker vm = (ValueMarker) marker; double value = vm.getValue(); Range range = rangeAxis.getRange(); if (!range.contains(value)) { return; } double v = rangeAxis.valueToJava2D(value, dataArea, plot.getRangeAxisEdge()); PlotOrientation orientation = plot.getOrientation(); Line2D line = null; if (orientation == PlotOrientation.HORIZONTAL) { line = new Line2D.Double(v, dataArea.getMinY(), v, dataArea.getMaxY()); } else if (orientation == PlotOrientation.VERTICAL) { line = new Line2D.Double(dataArea.getMinX(), v, dataArea.getMaxX(), v); } else { throw new IllegalStateException("Unrecognised orientation."); } final Composite originalComposite = g2.getComposite(); g2.setComposite(AlphaComposite.getInstance( AlphaComposite.SRC_OVER, marker.getAlpha())); g2.setPaint(marker.getPaint()); g2.setStroke(marker.getStroke()); g2.draw(line); String label = marker.getLabel(); RectangleAnchor anchor = marker.getLabelAnchor(); if (label != null) { Font labelFont = marker.getLabelFont(); g2.setFont(labelFont); Point2D coords = calculateRangeMarkerTextAnchorPoint( g2, orientation, dataArea, line.getBounds2D(), marker.getLabelOffset(), LengthAdjustmentType.EXPAND, anchor); Rectangle2D r = TextUtils.calcAlignedStringBounds(label, g2, (float) coords.getX(), (float) coords.getY(), marker.getLabelTextAnchor()); g2.setPaint(marker.getLabelBackgroundColor()); g2.fill(r); g2.setPaint(marker.getLabelPaint()); TextUtils.drawAlignedString(label, g2, (float) coords.getX(), (float) coords.getY(), marker.getLabelTextAnchor()); } g2.setComposite(originalComposite); } else if (marker instanceof IntervalMarker) { IntervalMarker im = (IntervalMarker) marker; double start = im.getStartValue(); double end = im.getEndValue(); Range range = rangeAxis.getRange(); if (!(range.intersects(start, end))) { return; } double start2d = rangeAxis.valueToJava2D(start, dataArea, plot.getRangeAxisEdge()); double end2d = rangeAxis.valueToJava2D(end, dataArea, plot.getRangeAxisEdge()); double low = Math.min(start2d, end2d); double high = Math.max(start2d, end2d); PlotOrientation orientation = plot.getOrientation(); Rectangle2D rect = null; if (orientation == PlotOrientation.HORIZONTAL) { // clip left and right bounds to data area low = Math.max(low, dataArea.getMinX()); high = Math.min(high, dataArea.getMaxX()); rect = new Rectangle2D.Double(low, dataArea.getMinY(), high - low, dataArea.getHeight()); } else if (orientation == PlotOrientation.VERTICAL) { // clip top and bottom bounds to data area low = Math.max(low, dataArea.getMinY()); high = Math.min(high, dataArea.getMaxY()); rect = new Rectangle2D.Double(dataArea.getMinX(), low, dataArea.getWidth(), high - low); } final Composite originalComposite = g2.getComposite(); g2.setComposite(AlphaComposite.getInstance( AlphaComposite.SRC_OVER, marker.getAlpha())); Paint p = marker.getPaint(); if (p instanceof GradientPaint) { GradientPaint gp = (GradientPaint) p; GradientPaintTransformer t = im.getGradientPaintTransformer(); if (t != null) { gp = t.transform(gp, rect); } g2.setPaint(gp); } else { g2.setPaint(p); } g2.fill(rect); // now draw the outlines, if visible... if (im.getOutlinePaint() != null && im.getOutlineStroke() != null) { if (orientation == PlotOrientation.VERTICAL) { Line2D line = new Line2D.Double(); double x0 = dataArea.getMinX(); double x1 = dataArea.getMaxX(); g2.setPaint(im.getOutlinePaint()); g2.setStroke(im.getOutlineStroke()); if (range.contains(start)) { line.setLine(x0, start2d, x1, start2d); g2.draw(line); } if (range.contains(end)) { line.setLine(x0, end2d, x1, end2d); g2.draw(line); } } else { // PlotOrientation.HORIZONTAL Line2D line = new Line2D.Double(); double y0 = dataArea.getMinY(); double y1 = dataArea.getMaxY(); g2.setPaint(im.getOutlinePaint()); g2.setStroke(im.getOutlineStroke()); if (range.contains(start)) { line.setLine(start2d, y0, start2d, y1); g2.draw(line); } if (range.contains(end)) { line.setLine(end2d, y0, end2d, y1); g2.draw(line); } } } String label = marker.getLabel(); RectangleAnchor anchor = marker.getLabelAnchor(); if (label != null) { Font labelFont = marker.getLabelFont(); g2.setFont(labelFont); Point2D coords = calculateRangeMarkerTextAnchorPoint( g2, orientation, dataArea, rect, marker.getLabelOffset(), marker.getLabelOffsetType(), anchor); Rectangle2D r = TextUtils.calcAlignedStringBounds(label, g2, (float) coords.getX(), (float) coords.getY(), marker.getLabelTextAnchor()); g2.setPaint(marker.getLabelBackgroundColor()); g2.fill(r); g2.setPaint(marker.getLabelPaint()); TextUtils.drawAlignedString(label, g2, (float) coords.getX(), (float) coords.getY(), marker.getLabelTextAnchor()); } g2.setComposite(originalComposite); } } /** * Calculates the (x, y) coordinates for drawing a marker label. * * @param g2 the graphics device. * @param orientation the plot orientation. * @param dataArea the data area. * @param markerArea the marker area. * @param markerOffset the marker offset. * @param labelOffsetForRange ?? * @param anchor the label anchor. * * @return The coordinates for drawing the marker label. */ private Point2D calculateRangeMarkerTextAnchorPoint(Graphics2D g2, PlotOrientation orientation, Rectangle2D dataArea, Rectangle2D markerArea, RectangleInsets markerOffset, LengthAdjustmentType labelOffsetForRange, RectangleAnchor anchor) { Rectangle2D anchorRect = null; if (orientation == PlotOrientation.HORIZONTAL) { anchorRect = markerOffset.createAdjustedRectangle(markerArea, labelOffsetForRange, LengthAdjustmentType.CONTRACT); } else if (orientation == PlotOrientation.VERTICAL) { anchorRect = markerOffset.createAdjustedRectangle(markerArea, LengthAdjustmentType.CONTRACT, labelOffsetForRange); } return anchor.getAnchorPoint(anchorRect); } /** * Returns a clone of the renderer. * * @return A clone. * * @throws CloneNotSupportedException if the renderer does not support * cloning. */ @Override protected Object clone() throws CloneNotSupportedException { AbstractXYItemRenderer clone = (AbstractXYItemRenderer) super.clone(); // 'plot' : just retain reference, not a deep copy clone.itemLabelGeneratorMap = CloneUtils.cloneMapValues( this.itemLabelGeneratorMap); if (this.defaultItemLabelGenerator != null && this.defaultItemLabelGenerator instanceof PublicCloneable) { PublicCloneable pc = (PublicCloneable) this.defaultItemLabelGenerator; clone.defaultItemLabelGenerator = (XYItemLabelGenerator) pc.clone(); } clone.toolTipGeneratorMap = CloneUtils.cloneMapValues( this.toolTipGeneratorMap); if (this.defaultToolTipGenerator != null && this.defaultToolTipGenerator instanceof PublicCloneable) { PublicCloneable pc = (PublicCloneable) this.defaultToolTipGenerator; clone.defaultToolTipGenerator = (XYToolTipGenerator) pc.clone(); } if (this.legendItemLabelGenerator instanceof PublicCloneable) { clone.legendItemLabelGenerator = (XYSeriesLabelGenerator) ObjectUtils.clone(this.legendItemLabelGenerator); } if (this.legendItemToolTipGenerator instanceof PublicCloneable) { clone.legendItemToolTipGenerator = (XYSeriesLabelGenerator) ObjectUtils.clone(this.legendItemToolTipGenerator); } if (this.legendItemURLGenerator instanceof PublicCloneable) { clone.legendItemURLGenerator = (XYSeriesLabelGenerator) ObjectUtils.clone(this.legendItemURLGenerator); } clone.foregroundAnnotations = (List) ObjectUtils.deepClone( this.foregroundAnnotations); clone.backgroundAnnotations = (List) ObjectUtils.deepClone( this.backgroundAnnotations); return clone; } /** * Tests this renderer for equality with another object. * * @param obj the object ({@code null} permitted). * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof AbstractXYItemRenderer)) { return false; } AbstractXYItemRenderer that = (AbstractXYItemRenderer) obj; if (!this.itemLabelGeneratorMap.equals(that.itemLabelGeneratorMap)) { return false; } if (!Objects.equals(this.defaultItemLabelGenerator, that.defaultItemLabelGenerator)) { return false; } if (!this.toolTipGeneratorMap.equals(that.toolTipGeneratorMap)) { return false; } if (!Objects.equals(this.defaultToolTipGenerator, that.defaultToolTipGenerator)) { return false; } if (!Objects.equals(this.urlGenerator, that.urlGenerator)) { return false; } if (!this.foregroundAnnotations.equals(that.foregroundAnnotations)) { return false; } if (!this.backgroundAnnotations.equals(that.backgroundAnnotations)) { return false; } if (!Objects.equals(this.legendItemLabelGenerator, that.legendItemLabelGenerator)) { return false; } if (!Objects.equals(this.legendItemToolTipGenerator, that.legendItemToolTipGenerator)) { return false; } if (!Objects.equals(this.legendItemURLGenerator, that.legendItemURLGenerator)) { return false; } return super.equals(obj); } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + itemLabelGeneratorMap.hashCode(); result = 31 * result + (defaultItemLabelGenerator != null ? defaultItemLabelGenerator.hashCode() : 0); result = 31 * result + toolTipGeneratorMap.hashCode(); result = 31 * result + (defaultToolTipGenerator != null ? defaultToolTipGenerator.hashCode() : 0); result = 31 * result + (urlGenerator != null ? urlGenerator.hashCode() : 0); result = 31 * result + (backgroundAnnotations != null ? backgroundAnnotations.hashCode() : 0); result = 31 * result + (foregroundAnnotations != null ? foregroundAnnotations.hashCode() : 0); result = 31 * result + (legendItemLabelGenerator != null ? legendItemLabelGenerator.hashCode() : 0); result = 31 * result + (legendItemToolTipGenerator != null ? legendItemToolTipGenerator.hashCode() : 0); result = 31 * result + (legendItemURLGenerator != null ? legendItemURLGenerator.hashCode() : 0); return result; } /** * Returns the drawing supplier from the plot. * * @return The drawing supplier (possibly {@code null}). */ @Override public DrawingSupplier getDrawingSupplier() { DrawingSupplier result = null; XYPlot p = getPlot(); if (p != null) { result = p.getDrawingSupplier(); } return result; } /** * Considers the current (x, y) coordinate and updates the crosshair point * if it meets the criteria (usually means the (x, y) coordinate is the * closest to the anchor point so far). * * @param crosshairState the crosshair state ({@code null} permitted, * but the method does nothing in that case). * @param x the x-value (in data space). * @param y the y-value (in data space). * @param datasetIndex the index of the dataset for the point. * @param transX the x-value translated to Java2D space. * @param transY the y-value translated to Java2D space. * @param orientation the plot orientation ({@code null} not * permitted). */ protected void updateCrosshairValues(CrosshairState crosshairState, double x, double y, int datasetIndex, double transX, double transY, PlotOrientation orientation) { Args.nullNotPermitted(orientation, "orientation"); if (crosshairState != null) { // do we need to update the crosshair values? if (this.plot.isDomainCrosshairLockedOnData()) { if (this.plot.isRangeCrosshairLockedOnData()) { // both axes crosshairState.updateCrosshairPoint(x, y, datasetIndex, transX, transY, orientation); } else { // just the domain axis... crosshairState.updateCrosshairX(x, transX, datasetIndex); } } else { if (this.plot.isRangeCrosshairLockedOnData()) { // just the range axis... crosshairState.updateCrosshairY(y, transY, datasetIndex); } } } } /** * Draws an item label. * * @param g2 the graphics device. * @param orientation the orientation. * @param dataset the dataset. * @param series the series index (zero-based). * @param item the item index (zero-based). * @param x the x coordinate (in Java2D space). * @param y the y coordinate (in Java2D space). * @param negative indicates a negative value (which affects the item * label position). */ protected void drawItemLabel(Graphics2D g2, PlotOrientation orientation, XYDataset dataset, int series, int item, double x, double y, boolean negative) { XYItemLabelGenerator generator = getItemLabelGenerator(series, item); if (generator != null) { Font labelFont = getItemLabelFont(series, item); Paint paint = getItemLabelPaint(series, item); g2.setFont(labelFont); g2.setPaint(paint); String label = generator.generateLabel(dataset, series, item); // get the label position.. ItemLabelPosition position; if (!negative) { position = getPositiveItemLabelPosition(series, item); } else { position = getNegativeItemLabelPosition(series, item); } // work out the label anchor point... Point2D anchorPoint = calculateLabelAnchorPoint( position.getItemLabelAnchor(), x, y, orientation); TextUtils.drawRotatedString(label, g2, (float) anchorPoint.getX(), (float) anchorPoint.getY(), position.getTextAnchor(), position.getAngle(), position.getRotationAnchor()); } } /** * Draws all the annotations for the specified layer. * * @param g2 the graphics device. * @param dataArea the data area. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param layer the layer ({@code null} not permitted). * @param info the plot rendering info. */ @Override public void drawAnnotations(Graphics2D g2, Rectangle2D dataArea, ValueAxis domainAxis, ValueAxis rangeAxis, Layer layer, PlotRenderingInfo info) { Iterator iterator = null; if (layer.equals(Layer.FOREGROUND)) { iterator = this.foregroundAnnotations.iterator(); } else if (layer.equals(Layer.BACKGROUND)) { iterator = this.backgroundAnnotations.iterator(); } else { // should not get here throw new RuntimeException("Unknown layer."); } while (iterator.hasNext()) { XYAnnotation annotation = (XYAnnotation) iterator.next(); int index = this.plot.getIndexOf(this); annotation.draw(g2, this.plot, dataArea, domainAxis, rangeAxis, index, info); } } /** * Adds an entity to the collection. Note the the {@code entityX} and * {@code entityY} coordinates are in Java2D space, should already be * adjusted for the plot orientation, and will only be used if * {@code hotspot} is {@code null}. * * @param entities the entity collection being populated. * @param hotspot the entity area (if {@code null} a default will be * used). * @param dataset the dataset. * @param series the series. * @param item the item. * @param entityX the entity x-coordinate (in Java2D space, only used if * {@code hotspot} is {@code null}). * @param entityY the entity y-coordinate (in Java2D space, only used if * {@code hotspot} is {@code null}). */ protected void addEntity(EntityCollection entities, Shape hotspot, XYDataset dataset, int series, int item, double entityX, double entityY) { if (!getItemCreateEntity(series, item)) { return; } // if not hotspot is provided, we create a default based on the // provided data coordinates (which are already in Java2D space) if (hotspot == null) { double r = getDefaultEntityRadius(); double w = r * 2; hotspot = new Ellipse2D.Double(entityX - r, entityY - r, w, w); } String tip = null; XYToolTipGenerator generator = getToolTipGenerator(series, item); if (generator != null) { tip = generator.generateToolTip(dataset, series, item); } String url = null; if (getURLGenerator() != null) { url = getURLGenerator().generateURL(dataset, series, item); } XYItemEntity entity = new XYItemEntity(hotspot, dataset, series, item, tip, url); entities.add(entity); } /** * Utility method delegating to {@link GeneralPath#moveTo} taking double as * parameters. * * @param hotspot the region under construction ({@code null} not * permitted); * @param x the x coordinate; * @param y the y coordinate; */ protected static void moveTo(GeneralPath hotspot, double x, double y) { hotspot.moveTo((float) x, (float) y); } /** * Utility method delegating to {@link GeneralPath#lineTo} taking double as * parameters. * * @param hotspot the region under construction ({@code null} not * permitted); * @param x the x coordinate; * @param y the y coordinate; */ protected static void lineTo(GeneralPath hotspot, double x, double y) { hotspot.lineTo((float) x, (float) y); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/CandlestickRenderer.java000066400000000000000000000744171463604235500321170ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * CandlestickRenderer.java * ------------------------ * (C) Copyright 2001-present, by David Gilbert. * * Original Authors: David Gilbert; * Sylvain Vieujot; * Contributor(s): Richard Atkinson; * Christian W. Zuckschwerdt; * Jerome Fisher; * */ package org.jfree.chart.renderer.xy; import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Composite; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Stroke; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.labels.HighLowItemLabelGenerator; import org.jfree.chart.labels.XYToolTipGenerator; import org.jfree.chart.plot.CrosshairState; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; import org.jfree.data.Range; import org.jfree.data.xy.IntervalXYDataset; import org.jfree.data.xy.OHLCDataset; import org.jfree.data.xy.XYDataset; /** * A renderer that draws candlesticks on an {@link XYPlot} (requires a * {@link OHLCDataset}). The example shown here is generated * by the {@code CandlestickChartDemo1.java} program included in the * JFreeChart demo collection: *

* CandlestickRendererSample.png *

* This renderer does not include code to calculate the crosshair point for the * plot. */ public class CandlestickRenderer extends AbstractXYItemRenderer implements XYItemRenderer, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 50390395841817121L; /** The average width method. */ public static final int WIDTHMETHOD_AVERAGE = 0; /** The smallest width method. */ public static final int WIDTHMETHOD_SMALLEST = 1; /** The interval data method. */ public static final int WIDTHMETHOD_INTERVALDATA = 2; /** The method of automatically calculating the candle width. */ private int autoWidthMethod = WIDTHMETHOD_AVERAGE; /** * The number (generally between 0.0 and 1.0) by which the available space * automatically calculated for the candles will be multiplied to determine * the actual width to use. */ private double autoWidthFactor = 4.5 / 7; /** The minimum gap between one candle and the next */ private double autoWidthGap = 0.0; /** The candle width. */ private double candleWidth; /** The maximum candlewidth in milliseconds. */ private double maxCandleWidthInMilliseconds = 1000.0 * 60.0 * 60.0 * 20.0; /** Temporary storage for the maximum candle width. */ private double maxCandleWidth; /** * The paint used to fill the candle when the price moved up from open to * close. */ private transient Paint upPaint; /** * The paint used to fill the candle when the price moved down from open * to close. */ private transient Paint downPaint; /** A flag controlling whether or not volume bars are drawn on the chart. */ private boolean drawVolume; /** * The paint used to fill the volume bars (if they are visible). Once * initialised, this field should never be set to {@code null}. */ private transient Paint volumePaint; /** Temporary storage for the maximum volume. */ private transient double maxVolume; /** * A flag that controls whether or not the renderer's outline paint is * used to draw the outline of the candlestick. The default value is * {@code false} to avoid a change of behaviour for existing code. */ private boolean useOutlinePaint; /** * Creates a new renderer for candlestick charts. */ public CandlestickRenderer() { this(-1.0); } /** * Creates a new renderer for candlestick charts. *

* Use -1 for the candle width if you prefer the width to be calculated * automatically. * * @param candleWidth The candle width. */ public CandlestickRenderer(double candleWidth) { this(candleWidth, true, new HighLowItemLabelGenerator()); } /** * Creates a new renderer for candlestick charts. *

* Use -1 for the candle width if you prefer the width to be calculated * automatically. * * @param candleWidth the candle width. * @param drawVolume a flag indicating whether or not volume bars should * be drawn. * @param toolTipGenerator the tool tip generator. {@code null} is * none. */ public CandlestickRenderer(double candleWidth, boolean drawVolume, XYToolTipGenerator toolTipGenerator) { super(); setDefaultToolTipGenerator(toolTipGenerator); this.candleWidth = candleWidth; this.drawVolume = drawVolume; this.volumePaint = Color.GRAY; this.upPaint = Color.GREEN; this.downPaint = Color.RED; this.useOutlinePaint = false; // false preserves the old behaviour // prior to introducing this flag } /** * Returns the width of each candle. * * @return The candle width. * * @see #setCandleWidth(double) */ public double getCandleWidth() { return this.candleWidth; } /** * Sets the candle width and sends a {@link RendererChangeEvent} to all * registered listeners. *

* If you set the width to a negative value, the renderer will calculate * the candle width automatically based on the space available on the chart. * * @param width The width. * @see #setAutoWidthMethod(int) * @see #setAutoWidthGap(double) * @see #setAutoWidthFactor(double) * @see #setMaxCandleWidthInMilliseconds(double) */ public void setCandleWidth(double width) { if (width != this.candleWidth) { this.candleWidth = width; fireChangeEvent(); } } /** * Returns the maximum width (in milliseconds) of each candle. * * @return The maximum candle width in milliseconds. * * @see #setMaxCandleWidthInMilliseconds(double) */ public double getMaxCandleWidthInMilliseconds() { return this.maxCandleWidthInMilliseconds; } /** * Sets the maximum candle width (in milliseconds) and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param millis The maximum width. * * @see #getMaxCandleWidthInMilliseconds() * @see #setCandleWidth(double) * @see #setAutoWidthMethod(int) * @see #setAutoWidthGap(double) * @see #setAutoWidthFactor(double) */ public void setMaxCandleWidthInMilliseconds(double millis) { this.maxCandleWidthInMilliseconds = millis; fireChangeEvent(); } /** * Returns the method of automatically calculating the candle width. * * @return The method of automatically calculating the candle width. * * @see #setAutoWidthMethod(int) */ public int getAutoWidthMethod() { return this.autoWidthMethod; } /** * Sets the method of automatically calculating the candle width and * sends a {@link RendererChangeEvent} to all registered listeners. *

* {@code WIDTHMETHOD_AVERAGE}: Divides the entire display (ignoring * scale factor) by the number of items, and uses this as the available * width.
* {@code WIDTHMETHOD_SMALLEST}: Checks the interval between each * item, and uses the smallest as the available width.
* {@code WIDTHMETHOD_INTERVALDATA}: Assumes that the dataset supports * the IntervalXYDataset interface, and uses the startXValue - endXValue as * the available width. *
* * @param autoWidthMethod The method of automatically calculating the * candle width. * * @see #WIDTHMETHOD_AVERAGE * @see #WIDTHMETHOD_SMALLEST * @see #WIDTHMETHOD_INTERVALDATA * @see #getAutoWidthMethod() * @see #setCandleWidth(double) * @see #setAutoWidthGap(double) * @see #setAutoWidthFactor(double) * @see #setMaxCandleWidthInMilliseconds(double) */ public void setAutoWidthMethod(int autoWidthMethod) { if (this.autoWidthMethod != autoWidthMethod) { this.autoWidthMethod = autoWidthMethod; fireChangeEvent(); } } /** * Returns the factor by which the available space automatically * calculated for the candles will be multiplied to determine the actual * width to use. * * @return The width factor (generally between 0.0 and 1.0). * * @see #setAutoWidthFactor(double) */ public double getAutoWidthFactor() { return this.autoWidthFactor; } /** * Sets the factor by which the available space automatically calculated * for the candles will be multiplied to determine the actual width to use. * * @param autoWidthFactor The width factor (generally between 0.0 and 1.0). * * @see #getAutoWidthFactor() * @see #setCandleWidth(double) * @see #setAutoWidthMethod(int) * @see #setAutoWidthGap(double) * @see #setMaxCandleWidthInMilliseconds(double) */ public void setAutoWidthFactor(double autoWidthFactor) { if (this.autoWidthFactor != autoWidthFactor) { this.autoWidthFactor = autoWidthFactor; fireChangeEvent(); } } /** * Returns the amount of space to leave on the left and right of each * candle when automatically calculating widths. * * @return The gap. * * @see #setAutoWidthGap(double) */ public double getAutoWidthGap() { return this.autoWidthGap; } /** * Sets the amount of space to leave on the left and right of each candle * when automatically calculating widths and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param autoWidthGap The gap. * * @see #getAutoWidthGap() * @see #setCandleWidth(double) * @see #setAutoWidthMethod(int) * @see #setAutoWidthFactor(double) * @see #setMaxCandleWidthInMilliseconds(double) */ public void setAutoWidthGap(double autoWidthGap) { if (this.autoWidthGap != autoWidthGap) { this.autoWidthGap = autoWidthGap; fireChangeEvent(); } } /** * Returns the paint used to fill candles when the price moves up from open * to close. * * @return The paint (possibly {@code null}). * * @see #setUpPaint(Paint) */ public Paint getUpPaint() { return this.upPaint; } /** * Sets the paint used to fill candles when the price moves up from open * to close and sends a {@link RendererChangeEvent} to all registered * listeners. * * @param paint the paint ({@code null} permitted). * * @see #getUpPaint() */ public void setUpPaint(Paint paint) { this.upPaint = paint; fireChangeEvent(); } /** * Returns the paint used to fill candles when the price moves down from * open to close. * * @return The paint (possibly {@code null}). * * @see #setDownPaint(Paint) */ public Paint getDownPaint() { return this.downPaint; } /** * Sets the paint used to fill candles when the price moves down from open * to close and sends a {@link RendererChangeEvent} to all registered * listeners. * * @param paint The paint ({@code null} permitted). */ public void setDownPaint(Paint paint) { this.downPaint = paint; fireChangeEvent(); } /** * Returns a flag indicating whether or not volume bars are drawn on the * chart. * * @return A boolean. * * @see #setDrawVolume(boolean) */ public boolean getDrawVolume() { return this.drawVolume; } /** * Sets a flag that controls whether or not volume bars are drawn in the * background and sends a {@link RendererChangeEvent} to all registered * listeners. * * @param flag the flag. * * @see #getDrawVolume() */ public void setDrawVolume(boolean flag) { if (this.drawVolume != flag) { this.drawVolume = flag; fireChangeEvent(); } } /** * Returns the paint that is used to fill the volume bars if they are * visible. * * @return The paint (never {@code null}). * * @see #setVolumePaint(Paint) */ public Paint getVolumePaint() { return this.volumePaint; } /** * Sets the paint used to fill the volume bars, and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getVolumePaint() * @see #getDrawVolume() */ public void setVolumePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.volumePaint = paint; fireChangeEvent(); } /** * Returns the flag that controls whether or not the renderer's outline * paint is used to draw the candlestick outline. The default value is * {@code false}. * * @return A boolean. * * @see #setUseOutlinePaint(boolean) */ public boolean getUseOutlinePaint() { return this.useOutlinePaint; } /** * Sets the flag that controls whether or not the renderer's outline * paint is used to draw the candlestick outline, and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param use the new flag value. * * @see #getUseOutlinePaint() */ public void setUseOutlinePaint(boolean use) { if (this.useOutlinePaint != use) { this.useOutlinePaint = use; fireChangeEvent(); } } /** * Returns the range of values the renderer requires to display all the * items from the specified dataset. * * @param dataset the dataset ({@code null} permitted). * * @return The range ({@code null} if the dataset is {@code null} * or empty). */ @Override public Range findRangeBounds(XYDataset dataset) { return findRangeBounds(dataset, true); } /** * Initialises the renderer then returns the number of 'passes' through the * data that the renderer will require (usually just one). This method * will be called before the first item is rendered, giving the renderer * an opportunity to initialise any state information it wants to maintain. * The renderer can do nothing if it chooses. * * @param g2 the graphics device. * @param dataArea the area inside the axes. * @param plot the plot. * @param dataset the data. * @param info an optional info collection object to return data back to * the caller. * * @return The number of passes the renderer requires. */ @Override public XYItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, XYPlot plot, XYDataset dataset, PlotRenderingInfo info) { // calculate the maximum allowed candle width from the axis... ValueAxis axis = plot.getDomainAxis(); double x1 = axis.getLowerBound(); double x2 = x1 + this.maxCandleWidthInMilliseconds; RectangleEdge edge = plot.getDomainAxisEdge(); double xx1 = axis.valueToJava2D(x1, dataArea, edge); double xx2 = axis.valueToJava2D(x2, dataArea, edge); this.maxCandleWidth = Math.abs(xx2 - xx1); // Absolute value, since the relative x // positions are reversed for horizontal orientation // calculate the highest volume in the dataset... if (this.drawVolume) { OHLCDataset highLowDataset = (OHLCDataset) dataset; this.maxVolume = 0.0; for (int series = 0; series < highLowDataset.getSeriesCount(); series++) { for (int item = 0; item < highLowDataset.getItemCount(series); item++) { double volume = highLowDataset.getVolumeValue(series, item); if (volume > this.maxVolume) { this.maxVolume = volume; } } } } return new XYItemRendererState(info); } /** * Draws the visual representation of a single data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the area within which the plot is being drawn. * @param info collects info about the drawing. * @param plot the plot (can be used to obtain standard color * information etc). * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset. * @param series the series index (zero-based). * @param item the item index (zero-based). * @param crosshairState crosshair information for the plot * ({@code null} permitted). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item, CrosshairState crosshairState, int pass) { boolean horiz; PlotOrientation orientation = plot.getOrientation(); if (orientation == PlotOrientation.HORIZONTAL) { horiz = true; } else if (orientation == PlotOrientation.VERTICAL) { horiz = false; } else { return; } // setup for collecting optional entity info... EntityCollection entities = null; if (info != null) { entities = info.getOwner().getEntityCollection(); } OHLCDataset highLowData = (OHLCDataset) dataset; double x = highLowData.getXValue(series, item); double yHigh = highLowData.getHighValue(series, item); double yLow = highLowData.getLowValue(series, item); double yOpen = highLowData.getOpenValue(series, item); double yClose = highLowData.getCloseValue(series, item); RectangleEdge domainEdge = plot.getDomainAxisEdge(); double xx = domainAxis.valueToJava2D(x, dataArea, domainEdge); RectangleEdge edge = plot.getRangeAxisEdge(); double yyHigh = rangeAxis.valueToJava2D(yHigh, dataArea, edge); double yyLow = rangeAxis.valueToJava2D(yLow, dataArea, edge); double yyOpen = rangeAxis.valueToJava2D(yOpen, dataArea, edge); double yyClose = rangeAxis.valueToJava2D(yClose, dataArea, edge); double volumeWidth; double stickWidth; if (this.candleWidth > 0) { // These are deliberately not bounded to minimums/maxCandleWidth to // retain old behaviour. volumeWidth = this.candleWidth; stickWidth = this.candleWidth; } else { double xxWidth = 0; int itemCount; switch (this.autoWidthMethod) { case WIDTHMETHOD_AVERAGE: itemCount = highLowData.getItemCount(series); if (horiz) { xxWidth = dataArea.getHeight() / itemCount; } else { xxWidth = dataArea.getWidth() / itemCount; } break; case WIDTHMETHOD_SMALLEST: // Note: It would be nice to pre-calculate this per series itemCount = highLowData.getItemCount(series); double lastPos = -1; xxWidth = dataArea.getWidth(); for (int i = 0; i < itemCount; i++) { double pos = domainAxis.valueToJava2D( highLowData.getXValue(series, i), dataArea, domainEdge); if (lastPos != -1) { xxWidth = Math.min(xxWidth, Math.abs(pos - lastPos)); } lastPos = pos; } break; case WIDTHMETHOD_INTERVALDATA: IntervalXYDataset intervalXYData = (IntervalXYDataset) dataset; double startPos = domainAxis.valueToJava2D( intervalXYData.getStartXValue(series, item), dataArea, plot.getDomainAxisEdge()); double endPos = domainAxis.valueToJava2D( intervalXYData.getEndXValue(series, item), dataArea, plot.getDomainAxisEdge()); xxWidth = Math.abs(endPos - startPos); break; } xxWidth -= 2 * this.autoWidthGap; xxWidth *= this.autoWidthFactor; xxWidth = Math.min(xxWidth, this.maxCandleWidth); volumeWidth = Math.max(Math.min(1, this.maxCandleWidth), xxWidth); stickWidth = Math.max(Math.min(3, this.maxCandleWidth), xxWidth); } Paint p = getItemPaint(series, item); Paint outlinePaint = null; if (this.useOutlinePaint) { outlinePaint = getItemOutlinePaint(series, item); } Stroke s = getItemStroke(series, item); g2.setStroke(s); if (this.drawVolume) { int volume = (int) highLowData.getVolumeValue(series, item); double volumeHeight = volume / this.maxVolume; double min, max; if (horiz) { min = dataArea.getMinX(); max = dataArea.getMaxX(); } else { min = dataArea.getMinY(); max = dataArea.getMaxY(); } double zzVolume = volumeHeight * (max - min); g2.setPaint(getVolumePaint()); Composite originalComposite = g2.getComposite(); g2.setComposite(AlphaComposite.getInstance( AlphaComposite.SRC_OVER, 0.3f)); if (horiz) { g2.fill(new Rectangle2D.Double(min, xx - volumeWidth / 2, zzVolume, volumeWidth)); } else { g2.fill(new Rectangle2D.Double(xx - volumeWidth / 2, max - zzVolume, volumeWidth, zzVolume)); } g2.setComposite(originalComposite); } if (this.useOutlinePaint) { g2.setPaint(outlinePaint); } else { g2.setPaint(p); } double yyMaxOpenClose = Math.max(yyOpen, yyClose); double yyMinOpenClose = Math.min(yyOpen, yyClose); double maxOpenClose = Math.max(yOpen, yClose); double minOpenClose = Math.min(yOpen, yClose); // draw the upper shadow if (yHigh > maxOpenClose) { if (horiz) { g2.draw(new Line2D.Double(yyHigh, xx, yyMaxOpenClose, xx)); } else { g2.draw(new Line2D.Double(xx, yyHigh, xx, yyMaxOpenClose)); } } // draw the lower shadow if (yLow < minOpenClose) { if (horiz) { g2.draw(new Line2D.Double(yyLow, xx, yyMinOpenClose, xx)); } else { g2.draw(new Line2D.Double(xx, yyLow, xx, yyMinOpenClose)); } } // draw the body Rectangle2D body; Rectangle2D hotspot; double length = Math.abs(yyHigh - yyLow); double base = Math.min(yyHigh, yyLow); if (horiz) { body = new Rectangle2D.Double(yyMinOpenClose, xx - stickWidth / 2, yyMaxOpenClose - yyMinOpenClose, stickWidth); hotspot = new Rectangle2D.Double(base, xx - stickWidth / 2, length, stickWidth); } else { body = new Rectangle2D.Double(xx - stickWidth / 2, yyMinOpenClose, stickWidth, yyMaxOpenClose - yyMinOpenClose); hotspot = new Rectangle2D.Double(xx - stickWidth / 2, base, stickWidth, length); } if (yClose > yOpen) { if (this.upPaint != null) { g2.setPaint(this.upPaint); } else { g2.setPaint(p); } g2.fill(body); } else { if (this.downPaint != null) { g2.setPaint(this.downPaint); } else { g2.setPaint(p); } g2.fill(body); } if (this.useOutlinePaint) { g2.setPaint(outlinePaint); } else { g2.setPaint(p); } g2.draw(body); // add an entity for the item... if (entities != null) { addEntity(entities, hotspot, dataset, series, item, 0.0, 0.0); } } /** * Tests this renderer for equality with another object. * * @param obj the object ({@code null} permitted). * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof CandlestickRenderer)) { return false; } CandlestickRenderer that = (CandlestickRenderer) obj; if (this.candleWidth != that.candleWidth) { return false; } if (!PaintUtils.equal(this.upPaint, that.upPaint)) { return false; } if (!PaintUtils.equal(this.downPaint, that.downPaint)) { return false; } if (this.drawVolume != that.drawVolume) { return false; } if (this.maxCandleWidthInMilliseconds != that.maxCandleWidthInMilliseconds) { return false; } if (this.autoWidthMethod != that.autoWidthMethod) { return false; } if (this.autoWidthFactor != that.autoWidthFactor) { return false; } if (this.autoWidthGap != that.autoWidthGap) { return false; } if (this.useOutlinePaint != that.useOutlinePaint) { return false; } if (!PaintUtils.equal(this.volumePaint, that.volumePaint)) { return false; } return super.equals(obj); } /** * Returns a clone of the renderer. * * @return A clone. * * @throws CloneNotSupportedException if the renderer cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.upPaint, stream); SerialUtils.writePaint(this.downPaint, stream); SerialUtils.writePaint(this.volumePaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.upPaint = SerialUtils.readPaint(stream); this.downPaint = SerialUtils.readPaint(stream); this.volumePaint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/ClusteredXYBarRenderer.java000066400000000000000000000323241463604235500325220ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * ClusteredXYBarRenderer.java * --------------------------- * (C) Copyright 2003-present, by Paolo Cova and Contributors. * * Original Author: Paolo Cova; * Contributor(s): David Gilbert; * Christian W. Zuckschwerdt; * Matthias Rose; * */ package org.jfree.chart.renderer.xy; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.labels.XYItemLabelGenerator; import org.jfree.chart.plot.CrosshairState; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.Range; import org.jfree.data.xy.IntervalXYDataset; import org.jfree.data.xy.XYDataset; /** * An extension of {@link XYBarRenderer} that displays bars for different * series values at the same x next to each other. The assumption here is * that for each x (time or else) there is a y value for each series. If * this is not the case, there will be spaces between bars for a given x. * The example shown here is generated by the * {@code ClusteredXYBarRendererDemo1.java} program included in the * JFreeChart demo collection: *

* ClusteredXYBarRendererSample.png *

* This renderer does not include code to calculate the crosshair point for the * plot. */ public class ClusteredXYBarRenderer extends XYBarRenderer implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 5864462149177133147L; /** Determines whether bar center should be interval start. */ private final boolean centerBarAtStartValue; /** * Default constructor. Bar margin is set to 0.0. */ public ClusteredXYBarRenderer() { this(0.0, false); } /** * Constructs a new XY clustered bar renderer. * * @param margin the percentage amount to trim from the width of each bar. * @param centerBarAtStartValue if true, bars will be centered on the * start of the time period. */ public ClusteredXYBarRenderer(double margin, boolean centerBarAtStartValue) { super(margin); this.centerBarAtStartValue = centerBarAtStartValue; } /** * Returns the number of passes through the dataset that this renderer * requires. In this case, two passes are required, the first for drawing * the shadows (if visible), and the second for drawing the bars. * * @return {@code 2}. */ @Override public int getPassCount() { return 2; } /** * Returns the x-value bounds for the specified dataset. * * @param dataset the dataset ({@code null} permitted). * * @return The bounds (possibly {@code null}). */ @Override public Range findDomainBounds(XYDataset dataset) { if (dataset == null) { return null; } // need to handle cluster centering as a special case if (this.centerBarAtStartValue) { return findDomainBoundsWithOffset((IntervalXYDataset) dataset); } else { return super.findDomainBounds(dataset); } } /** * Iterates over the items in an {@link IntervalXYDataset} to find * the range of x-values including the interval OFFSET so that it centers * the interval around the start value. * * @param dataset the dataset ({@code null} not permitted). * * @return The range (possibly {@code null}). */ protected Range findDomainBoundsWithOffset(IntervalXYDataset dataset) { Args.nullNotPermitted(dataset, "dataset"); double minimum = Double.POSITIVE_INFINITY; double maximum = Double.NEGATIVE_INFINITY; int seriesCount = dataset.getSeriesCount(); double lvalue; double uvalue; for (int series = 0; series < seriesCount; series++) { int itemCount = dataset.getItemCount(series); for (int item = 0; item < itemCount; item++) { lvalue = dataset.getStartXValue(series, item); uvalue = dataset.getEndXValue(series, item); double offset = (uvalue - lvalue) / 2.0; lvalue = lvalue - offset; uvalue = uvalue - offset; minimum = Math.min(minimum, lvalue); maximum = Math.max(maximum, uvalue); } } if (minimum > maximum) { return null; } else { return new Range(minimum, maximum); } } /** * Draws the visual representation of a single data item. This method * is mostly copied from the superclass, the change is that in the * calculated space for a singe bar we draw bars for each series next to * each other. The width of each bar is the available width divided by * the number of series. Bars for each series are drawn in order left to * right. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the area within which the plot is being drawn. * @param info collects information about the drawing. * @param plot the plot (can be used to obtain standard color * information etc). * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset. * @param series the series index. * @param item the item index. * @param crosshairState crosshair information for the plot * ({@code null} permitted). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item, CrosshairState crosshairState, int pass) { if (!getItemVisible(series, item)) { return; } IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset; double y0; double y1; if (getUseYInterval()) { y0 = intervalDataset.getStartYValue(series, item); y1 = intervalDataset.getEndYValue(series, item); } else { y0 = getBase(); y1 = intervalDataset.getYValue(series, item); } if (Double.isNaN(y0) || Double.isNaN(y1)) { return; } double yy0 = rangeAxis.valueToJava2D(y0, dataArea, plot.getRangeAxisEdge()); double yy1 = rangeAxis.valueToJava2D(y1, dataArea, plot.getRangeAxisEdge()); RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); double x0 = intervalDataset.getStartXValue(series, item); double xx0 = domainAxis.valueToJava2D(x0, dataArea, xAxisLocation); double x1 = intervalDataset.getEndXValue(series, item); double xx1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation); double intervalW = xx1 - xx0; // this may be negative double baseX = xx0; if (this.centerBarAtStartValue) { baseX = baseX - intervalW / 2.0; } double m = getMargin(); if (m > 0.0) { double cut = intervalW * getMargin(); intervalW = intervalW - cut; baseX = baseX + (cut / 2); } double intervalH = Math.abs(yy0 - yy1); // we don't need the sign PlotOrientation orientation = plot.getOrientation(); List visibleSeries = new ArrayList<>(); for (int i = 0; i < dataset.getSeriesCount(); i++) { if (isSeriesVisible(i)) { visibleSeries.add(i); } } int numSeries = visibleSeries.size(); double seriesBarWidth = intervalW / numSeries; // may be negative int visibleSeriesIndex = visibleSeries.indexOf(series); Rectangle2D bar = null; if (orientation == PlotOrientation.HORIZONTAL) { double barY0 = baseX + (seriesBarWidth * visibleSeriesIndex); double barY1 = barY0 + seriesBarWidth; double rx = Math.min(yy0, yy1); double rw = intervalH; double ry = Math.min(barY0, barY1); double rh = Math.abs(barY1 - barY0); bar = new Rectangle2D.Double(rx, ry, rw, rh); } else if (orientation == PlotOrientation.VERTICAL) { double barX0 = baseX + (seriesBarWidth * visibleSeriesIndex); double barX1 = barX0 + seriesBarWidth; double rx = Math.min(barX0, barX1); double rw = Math.abs(barX1 - barX0); double ry = Math.min(yy0, yy1); double rh = intervalH; bar = new Rectangle2D.Double(rx, ry, rw, rh); } else { throw new IllegalStateException(); } boolean positive = (y1 > 0.0); boolean inverted = rangeAxis.isInverted(); RectangleEdge barBase; if (orientation == PlotOrientation.HORIZONTAL) { if (positive && inverted || !positive && !inverted) { barBase = RectangleEdge.RIGHT; } else { barBase = RectangleEdge.LEFT; } } else { if (positive && !inverted || !positive && inverted) { barBase = RectangleEdge.BOTTOM; } else { barBase = RectangleEdge.TOP; } } if (pass == 0 && getShadowsVisible()) { getBarPainter().paintBarShadow(g2, this, series, item, bar, barBase, !getUseYInterval()); } if (pass == 1) { getBarPainter().paintBar(g2, this, series, item, bar, barBase); if (isItemLabelVisible(series, item)) { XYItemLabelGenerator generator = getItemLabelGenerator(series, item); drawItemLabel(g2, dataset, series, item, plot, generator, bar, y1 < 0.0); } // add an entity for the item... if (info != null) { EntityCollection entities = info.getOwner().getEntityCollection(); if (entities != null) { addEntity(entities, bar, dataset, series, item, bar.getCenterX(), bar.getCenterY()); } } } } /** * Tests this renderer for equality with an arbitrary object, returning * {@code true} if {@code obj} is a {@code ClusteredXYBarRenderer} with the * same settings as this renderer, and {@code false} otherwise. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof ClusteredXYBarRenderer)) { return false; } ClusteredXYBarRenderer that = (ClusteredXYBarRenderer) obj; if (this.centerBarAtStartValue != that.centerBarAtStartValue) { return false; } return super.equals(obj); } /** * Returns a clone of the renderer. * * @return A clone. * * @throws CloneNotSupportedException if the renderer cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/CyclicXYItemRenderer.java000066400000000000000000000415451463604235500321750ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * CyclicXYItemRenderer.java * --------------------------- * (C) Copyright 2003-present, by Nicolas Brodu and Contributors. * * Original Author: Nicolas Brodu; * Contributor(s): David Gilbert; * */ package org.jfree.chart.renderer.xy; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import org.jfree.chart.axis.CyclicNumberAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.labels.XYToolTipGenerator; import org.jfree.chart.plot.CrosshairState; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.urls.XYURLGenerator; import org.jfree.data.DomainOrder; import org.jfree.data.general.DatasetChangeListener; import org.jfree.data.general.DatasetGroup; import org.jfree.data.xy.XYDataset; /** * The Cyclic XY item renderer is specially designed to handle cyclic axis. * While the standard renderer would draw a line across the plot when a cycling * occurs, the cyclic renderer splits the line at each cycle end instead. This * is done by interpolating new points at cycle boundary. Thus, correct * appearance is restored. * * The Cyclic XY item renderer works exactly like a standard XY item renderer * with non-cyclic axis. */ public class CyclicXYItemRenderer extends StandardXYItemRenderer implements Serializable { /** For serialization. */ private static final long serialVersionUID = 4035912243303764892L; /** * Default constructor. */ public CyclicXYItemRenderer() { super(); } /** * Creates a new renderer. * * @param type the renderer type. */ public CyclicXYItemRenderer(int type) { super(type); } /** * Creates a new renderer. * * @param type the renderer type. * @param labelGenerator the tooltip generator. */ public CyclicXYItemRenderer(int type, XYToolTipGenerator labelGenerator) { super(type, labelGenerator); } /** * Creates a new renderer. * * @param type the renderer type. * @param labelGenerator the tooltip generator. * @param urlGenerator the url generator. */ public CyclicXYItemRenderer(int type, XYToolTipGenerator labelGenerator, XYURLGenerator urlGenerator) { super(type, labelGenerator, urlGenerator); } /** * Draws the visual representation of a single data item. * When using cyclic axis, do not draw a line from right to left when * cycling as would a standard XY item renderer, but instead draw a line * from the previous point to the cycle bound in the last cycle, and a line * from the cycle bound to current point in the current cycle. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the data area. * @param info the plot rendering info. * @param plot the plot. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset. * @param series the series index. * @param item the item index. * @param crosshairState crosshair information for the plot * ({@code null} permitted). * @param pass the current pass index. */ @Override public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item, CrosshairState crosshairState, int pass) { if ((!getPlotLines()) || ((!(domainAxis instanceof CyclicNumberAxis)) && (!(rangeAxis instanceof CyclicNumberAxis))) || (item <= 0)) { super.drawItem(g2, state, dataArea, info, plot, domainAxis, rangeAxis, dataset, series, item, crosshairState, pass); return; } // get the previous data point... double xn = dataset.getXValue(series, item - 1); double yn = dataset.getYValue(series, item - 1); // If null, don't draw line => then delegate to parent if (Double.isNaN(yn)) { super.drawItem(g2, state, dataArea, info, plot, domainAxis, rangeAxis, dataset, series, item, crosshairState, pass); return; } double[] x = new double[2]; double[] y = new double[2]; x[0] = xn; y[0] = yn; // get the data point... xn = dataset.getXValue(series, item); yn = dataset.getYValue(series, item); // If null, don't draw line at all if (Double.isNaN(yn)) { return; } x[1] = xn; y[1] = yn; // Now split the segment as needed double xcycleBound = Double.NaN; double ycycleBound = Double.NaN; boolean xBoundMapping = false, yBoundMapping = false; CyclicNumberAxis cnax = null, cnay = null; if (domainAxis instanceof CyclicNumberAxis) { cnax = (CyclicNumberAxis) domainAxis; xcycleBound = cnax.getCycleBound(); xBoundMapping = cnax.isBoundMappedToLastCycle(); // If the segment must be splitted, insert a new point // Strict test forces to have real segments (not 2 equal points) // and avoids division by 0 if ((x[0] != x[1]) && ((xcycleBound >= x[0]) && (xcycleBound <= x[1]) || (xcycleBound >= x[1]) && (xcycleBound <= x[0]))) { double[] nx = new double[3]; double[] ny = new double[3]; nx[0] = x[0]; nx[2] = x[1]; ny[0] = y[0]; ny[2] = y[1]; nx[1] = xcycleBound; ny[1] = (y[1] - y[0]) * (xcycleBound - x[0]) / (x[1] - x[0]) + y[0]; x = nx; y = ny; } } if (rangeAxis instanceof CyclicNumberAxis) { cnay = (CyclicNumberAxis) rangeAxis; ycycleBound = cnay.getCycleBound(); yBoundMapping = cnay.isBoundMappedToLastCycle(); // The split may occur in either x splitted segments, if any, but // not in both if ((y[0] != y[1]) && ((ycycleBound >= y[0]) && (ycycleBound <= y[1]) || (ycycleBound >= y[1]) && (ycycleBound <= y[0]))) { double[] nx = new double[x.length + 1]; double[] ny = new double[y.length + 1]; nx[0] = x[0]; nx[2] = x[1]; ny[0] = y[0]; ny[2] = y[1]; ny[1] = ycycleBound; nx[1] = (x[1] - x[0]) * (ycycleBound - y[0]) / (y[1] - y[0]) + x[0]; if (x.length == 3) { nx[3] = x[2]; ny[3] = y[2]; } x = nx; y = ny; } else if ((x.length == 3) && (y[1] != y[2]) && ((ycycleBound >= y[1]) && (ycycleBound <= y[2]) || (ycycleBound >= y[2]) && (ycycleBound <= y[1]))) { double[] nx = new double[4]; double[] ny = new double[4]; nx[0] = x[0]; nx[1] = x[1]; nx[3] = x[2]; ny[0] = y[0]; ny[1] = y[1]; ny[3] = y[2]; ny[2] = ycycleBound; nx[2] = (x[2] - x[1]) * (ycycleBound - y[1]) / (y[2] - y[1]) + x[1]; x = nx; y = ny; } } // If the line is not wrapping, then parent is OK if (x.length == 2) { super.drawItem(g2, state, dataArea, info, plot, domainAxis, rangeAxis, dataset, series, item, crosshairState, pass); return; } OverwriteDataSet newset = new OverwriteDataSet(x, y, dataset); if (cnax != null) { if (xcycleBound == x[0]) { cnax.setBoundMappedToLastCycle(x[1] <= xcycleBound); } if (xcycleBound == x[1]) { cnax.setBoundMappedToLastCycle(x[0] <= xcycleBound); } } if (cnay != null) { if (ycycleBound == y[0]) { cnay.setBoundMappedToLastCycle(y[1] <= ycycleBound); } if (ycycleBound == y[1]) { cnay.setBoundMappedToLastCycle(y[0] <= ycycleBound); } } super.drawItem( g2, state, dataArea, info, plot, domainAxis, rangeAxis, newset, series, 1, crosshairState, pass ); if (cnax != null) { if (xcycleBound == x[1]) { cnax.setBoundMappedToLastCycle(x[2] <= xcycleBound); } if (xcycleBound == x[2]) { cnax.setBoundMappedToLastCycle(x[1] <= xcycleBound); } } if (cnay != null) { if (ycycleBound == y[1]) { cnay.setBoundMappedToLastCycle(y[2] <= ycycleBound); } if (ycycleBound == y[2]) { cnay.setBoundMappedToLastCycle(y[1] <= ycycleBound); } } super.drawItem(g2, state, dataArea, info, plot, domainAxis, rangeAxis, newset, series, 2, crosshairState, pass); if (x.length == 4) { if (cnax != null) { if (xcycleBound == x[2]) { cnax.setBoundMappedToLastCycle(x[3] <= xcycleBound); } if (xcycleBound == x[3]) { cnax.setBoundMappedToLastCycle(x[2] <= xcycleBound); } } if (cnay != null) { if (ycycleBound == y[2]) { cnay.setBoundMappedToLastCycle(y[3] <= ycycleBound); } if (ycycleBound == y[3]) { cnay.setBoundMappedToLastCycle(y[2] <= ycycleBound); } } super.drawItem(g2, state, dataArea, info, plot, domainAxis, rangeAxis, newset, series, 3, crosshairState, pass); } if (cnax != null) { cnax.setBoundMappedToLastCycle(xBoundMapping); } if (cnay != null) { cnay.setBoundMappedToLastCycle(yBoundMapping); } } /** * A dataset to hold the interpolated points when drawing new lines. */ protected static class OverwriteDataSet implements XYDataset { /** The delegate dataset. */ protected XYDataset delegateSet; /** Storage for the x and y values. */ Double[] x, y; /** * Creates a new dataset. * * @param x the x values. * @param y the y values. * @param delegateSet the dataset. */ public OverwriteDataSet(double [] x, double[] y, XYDataset delegateSet) { this.delegateSet = delegateSet; this.x = new Double[x.length]; this.y = new Double[y.length]; for (int i = 0; i < x.length; ++i) { this.x[i] = x[i]; this.y[i] = y[i]; } } /** * Returns the order of the domain (X) values. * * @return The domain order. */ @Override public DomainOrder getDomainOrder() { return DomainOrder.NONE; } /** * Returns the number of items for the given series. * * @param series the series index (zero-based). * * @return The item count. */ @Override public int getItemCount(int series) { return this.x.length; } /** * Returns the x-value. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The x-value. */ @Override public Number getX(int series, int item) { return this.x[item]; } /** * Returns the x-value (as a double primitive) for an item within a * series. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The x-value. */ @Override public double getXValue(int series, int item) { double result = Double.NaN; Number xx = getX(series, item); if (xx != null) { result = xx.doubleValue(); } return result; } /** * Returns the y-value. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The y-value. */ @Override public Number getY(int series, int item) { return this.y[item]; } /** * Returns the y-value (as a double primitive) for an item within a * series. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The y-value. */ @Override public double getYValue(int series, int item) { double result = Double.NaN; Number yy = getY(series, item); if (yy != null) { result = yy.doubleValue(); } return result; } /** * Returns the number of series in the dataset. * * @return The series count. */ @Override public int getSeriesCount() { return this.delegateSet.getSeriesCount(); } /** * Returns the name of the given series. * * @param series the series index (zero-based). * * @return The series name. */ @Override public Comparable getSeriesKey(int series) { return this.delegateSet.getSeriesKey(series); } /** * Returns the index of the named series, or -1. * * @param seriesName the series name. * * @return The index. */ @Override public int indexOf(Comparable seriesName) { return this.delegateSet.indexOf(seriesName); } /** * Does nothing. * * @param listener ignored. */ @Override public void addChangeListener(DatasetChangeListener listener) { // unused in parent } /** * Does nothing. * * @param listener ignored. */ @Override public void removeChangeListener(DatasetChangeListener listener) { // unused in parent } /** * Returns the dataset group. * * @return The dataset group. */ @Override public DatasetGroup getGroup() { // unused but must return something, so while we are at it... return this.delegateSet.getGroup(); } /** * Does nothing. * * @param group ignored. */ @Override public void setGroup(DatasetGroup group) { // unused in parent } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/DefaultXYItemRenderer.java000066400000000000000000000036501463604235500323460ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * DefaultXYItemRenderer.java * -------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import java.io.Serializable; /** * A default renderer for the {@link org.jfree.chart.plot.XYPlot} class. This * is an alias for the {@link XYLineAndShapeRenderer} class. */ public class DefaultXYItemRenderer extends XYLineAndShapeRenderer implements Serializable { /** For serialization. */ static final long serialVersionUID = 3450423530996888074L; // no new methods } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/DeviationRenderer.java000066400000000000000000000317341463604235500316100ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * DeviationRenderer.java * ---------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import java.awt.AlphaComposite; import java.awt.Composite; import java.awt.Graphics2D; import java.awt.geom.GeneralPath; import java.awt.geom.Rectangle2D; import java.util.List; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.plot.CrosshairState; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.RectangleEdge; import org.jfree.data.Range; import org.jfree.data.xy.IntervalXYDataset; import org.jfree.data.xy.XYDataset; /** * A specialised subclass of the {@link XYLineAndShapeRenderer} that requires * an {@link IntervalXYDataset} and represents the y-interval by shading an * area behind the y-values on the chart. * The example shown here is generated by the * {@code DeviationRendererDemo1.java} program included in the JFreeChart demo * collection: *

* DeviationRendererSample.png */ public class DeviationRenderer extends XYLineAndShapeRenderer { /** * A state object that is passed to each call to {@code drawItem()}. */ public static class State extends XYLineAndShapeRenderer.State { /** * A list of coordinates for the upper y-values in the current series * (after translation into Java2D space). */ public List upperCoordinates; /** * A list of coordinates for the lower y-values in the current series * (after translation into Java2D space). */ public List lowerCoordinates; /** * Creates a new state instance. * * @param info the plot rendering info. */ public State(PlotRenderingInfo info) { super(info); this.lowerCoordinates = new java.util.ArrayList(); this.upperCoordinates = new java.util.ArrayList(); } } /** The alpha transparency for the interval shading. */ protected float alpha; /** * Creates a new renderer that displays lines and shapes for the data * items, as well as the shaded area for the y-interval. */ public DeviationRenderer() { this(true, true); } /** * Creates a new renderer. * * @param lines show lines between data items? * @param shapes show a shape for each data item? */ public DeviationRenderer(boolean lines, boolean shapes) { super(lines, shapes); super.setDrawSeriesLineAsPath(true); this.alpha = 0.5f; } /** * Returns the alpha transparency for the background shading. * * @return The alpha transparency. * * @see #setAlpha(float) */ public float getAlpha() { return this.alpha; } /** * Sets the alpha transparency for the background shading, and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param alpha the alpha (in the range 0.0f to 1.0f). * * @see #getAlpha() */ public void setAlpha(float alpha) { if (alpha < 0.0f || alpha > 1.0f) { throw new IllegalArgumentException( "Requires 'alpha' in the range 0.0 to 1.0."); } this.alpha = alpha; fireChangeEvent(); } /** * This method is overridden so that this flag cannot be changed---it is * set to {@code true} for this renderer. * * @param flag ignored. */ @Override public void setDrawSeriesLineAsPath(boolean flag) { // ignore } /** * Returns the range of values the renderer requires to display all the * items from the specified dataset. * * @param dataset the dataset ({@code null} permitted). * * @return The range ({@code null} if the dataset is {@code null} * or empty). */ @Override public Range findRangeBounds(XYDataset dataset) { return findRangeBounds(dataset, true); } /** * Initialises and returns a state object that can be passed to each * invocation of the {@link #drawItem} method. * * @param g2 the graphics target. * @param dataArea the data area. * @param plot the plot. * @param dataset the dataset. * @param info the plot rendering info. * * @return A newly initialised state object. */ @Override public XYItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, XYPlot plot, XYDataset dataset, PlotRenderingInfo info) { State state = new State(info); state.seriesPath = new GeneralPath(); state.setProcessVisibleItemsOnly(false); return state; } /** * Returns the number of passes (through the dataset) used by this * renderer. * * @return {@code 3}. */ @Override public int getPassCount() { return 3; } /** * Returns {@code true} if this is the pass where the shapes are * drawn. * * @param pass the pass index. * * @return A boolean. * * @see #isLinePass(int) */ @Override protected boolean isItemPass(int pass) { return (pass == 2); } /** * Returns {@code true} if this is the pass where the lines are * drawn. * * @param pass the pass index. * * @return A boolean. * * @see #isItemPass(int) */ @Override protected boolean isLinePass(int pass) { return (pass == 1); } /** * Draws the visual representation of a single data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the area within which the data is being drawn. * @param info collects information about the drawing. * @param plot the plot (can be used to obtain standard color * information etc). * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset. * @param series the series index (zero-based). * @param item the item index (zero-based). * @param crosshairState crosshair information for the plot * ({@code null} permitted). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item, CrosshairState crosshairState, int pass) { // do nothing if item is not visible if (!getItemVisible(series, item)) { return; } // first pass draws the shading if (pass == 0) { IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset; State drState = (State) state; double x = intervalDataset.getXValue(series, item); double yLow = intervalDataset.getStartYValue(series, item); double yHigh = intervalDataset.getEndYValue(series, item); RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); double xx = domainAxis.valueToJava2D(x, dataArea, xAxisLocation); double yyLow = rangeAxis.valueToJava2D(yLow, dataArea, yAxisLocation); double yyHigh = rangeAxis.valueToJava2D(yHigh, dataArea, yAxisLocation); PlotOrientation orientation = plot.getOrientation(); if (orientation == PlotOrientation.HORIZONTAL) { drState.lowerCoordinates.add(new double[] {yyLow, xx}); drState.upperCoordinates.add(new double[] {yyHigh, xx}); } else if (orientation == PlotOrientation.VERTICAL) { drState.lowerCoordinates.add(new double[] {xx, yyLow}); drState.upperCoordinates.add(new double[] {xx, yyHigh}); } if (item == (dataset.getItemCount(series) - 1)) { // last item in series, draw the lot... // set up the alpha-transparency... Composite originalComposite = g2.getComposite(); g2.setComposite(AlphaComposite.getInstance( AlphaComposite.SRC_OVER, this.alpha)); g2.setPaint(getItemFillPaint(series, item)); GeneralPath area = new GeneralPath(GeneralPath.WIND_NON_ZERO, drState.lowerCoordinates.size() + drState.upperCoordinates.size()); double[] coords = (double[]) drState.lowerCoordinates.get(0); area.moveTo((float) coords[0], (float) coords[1]); for (int i = 1; i < drState.lowerCoordinates.size(); i++) { coords = (double[]) drState.lowerCoordinates.get(i); area.lineTo((float) coords[0], (float) coords[1]); } int count = drState.upperCoordinates.size(); coords = (double[]) drState.upperCoordinates.get(count - 1); area.lineTo((float) coords[0], (float) coords[1]); for (int i = count - 2; i >= 0; i--) { coords = (double[]) drState.upperCoordinates.get(i); area.lineTo((float) coords[0], (float) coords[1]); } area.closePath(); g2.fill(area); g2.setComposite(originalComposite); drState.lowerCoordinates.clear(); drState.upperCoordinates.clear(); } } if (isLinePass(pass)) { // the following code handles the line for the y-values...it's // all done by code in the super class if (item == 0) { State s = (State) state; s.seriesPath.reset(); s.setLastPointGood(false); } if (getItemLineVisible(series, item)) { drawPrimaryLineAsPath(state, g2, plot, dataset, pass, series, item, domainAxis, rangeAxis, dataArea); } } // second pass adds shapes where the items are .. else if (isItemPass(pass)) { // setup for collecting optional entity info... EntityCollection entities = null; if (info != null) { entities = info.getOwner().getEntityCollection(); } drawSecondaryPass(g2, plot, dataset, pass, series, item, domainAxis, dataArea, rangeAxis, crosshairState, entities); } } /** * Tests this renderer for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof DeviationRenderer)) { return false; } DeviationRenderer that = (DeviationRenderer) obj; if (this.alpha != that.alpha) { return false; } return super.equals(obj); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/DeviationStepRenderer.java000066400000000000000000000310531463604235500324360ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * DeviationStepRenderer.java * -------------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.plot.CrosshairState; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.RectangleEdge; import org.jfree.data.xy.IntervalXYDataset; import org.jfree.data.xy.XYDataset; import java.awt.*; import java.awt.geom.GeneralPath; import java.awt.geom.Rectangle2D; /** * A specialised subclass of the {@link DeviationRenderer} that requires * an {@link IntervalXYDataset} and represents the y-interval by shading an * area behind the y-values on the chart, drawing only horizontal or * vertical lines (steps); * * @since 1.5.1 */ public class DeviationStepRenderer extends DeviationRenderer { /** * Creates a new renderer that displays lines and shapes for the data * items, as well as the shaded area for the y-interval. */ public DeviationStepRenderer() { super(); } /** * Creates a new renderer. * * @param lines show lines between data items? * @param shapes show a shape for each data item? */ public DeviationStepRenderer(boolean lines, boolean shapes) { super(lines, shapes); } /** * Draws the visual representation of a single data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the area within which the data is being drawn. * @param info collects information about the drawing. * @param plot the plot (can be used to obtain standard color * information etc). * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset. * @param series the series index (zero-based). * @param item the item index (zero-based). * @param crosshairState crosshair information for the plot * ({@code null} permitted). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item, CrosshairState crosshairState, int pass) { // do nothing if item is not visible if (!getItemVisible(series, item)) { return; } // first pass draws the shading if (pass == 0) { IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset; State drState = (State) state; double x = intervalDataset.getXValue(series, item); double yLow = intervalDataset.getStartYValue(series, item); double yHigh = intervalDataset.getEndYValue(series, item); RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); double xx = domainAxis.valueToJava2D(x, dataArea, xAxisLocation); double yyLow = rangeAxis.valueToJava2D(yLow, dataArea, yAxisLocation); double yyHigh = rangeAxis.valueToJava2D(yHigh, dataArea, yAxisLocation); PlotOrientation orientation = plot.getOrientation(); if (item > 0 && !Double.isNaN(xx)) { double yLowPrev = intervalDataset.getStartYValue(series, item-1); double yHighPrev = intervalDataset.getEndYValue(series, item-1); double yyLowPrev = rangeAxis.valueToJava2D(yLowPrev, dataArea, yAxisLocation); double yyHighPrev = rangeAxis.valueToJava2D(yHighPrev, dataArea, yAxisLocation); if(!Double.isNaN(yyLow) && !Double.isNaN(yyHigh)) { if (orientation == PlotOrientation.HORIZONTAL) { drState.lowerCoordinates.add(new double[]{yyLowPrev, xx}); drState.upperCoordinates.add(new double[]{yyHighPrev, xx}); } else if (orientation == PlotOrientation.VERTICAL) { drState.lowerCoordinates.add(new double[]{xx, yyLowPrev}); drState.upperCoordinates.add(new double[]{xx, yyHighPrev}); } } } boolean intervalGood = !Double.isNaN(xx) && !Double.isNaN(yLow) && !Double.isNaN(yHigh); if (intervalGood) { if (orientation == PlotOrientation.HORIZONTAL) { drState.lowerCoordinates.add(new double[]{yyLow, xx}); drState.upperCoordinates.add(new double[]{yyHigh, xx}); } else if (orientation == PlotOrientation.VERTICAL) { drState.lowerCoordinates.add(new double[]{xx, yyLow}); drState.upperCoordinates.add(new double[]{xx, yyHigh}); } } if (item == (dataset.getItemCount(series) - 1) || (!intervalGood && drState.lowerCoordinates.size() > 1)) { // draw items so far, either we reached the end of the series or the next interval is invalid // last item in series, draw the lot... // set up the alpha-transparency... Composite originalComposite = g2.getComposite(); g2.setComposite(AlphaComposite.getInstance( AlphaComposite.SRC_OVER, this.alpha)); g2.setPaint(getItemFillPaint(series, item)); GeneralPath area = new GeneralPath(GeneralPath.WIND_NON_ZERO, drState.lowerCoordinates.size() + drState.upperCoordinates.size()); double[] coords = (double[]) drState.lowerCoordinates.get(0); area.moveTo((float) coords[0], (float) coords[1]); for (int i = 1; i < drState.lowerCoordinates.size(); i++) { coords = (double[]) drState.lowerCoordinates.get(i); area.lineTo((float) coords[0], (float) coords[1]); } int count = drState.upperCoordinates.size(); coords = (double[]) drState.upperCoordinates.get(count - 1); area.lineTo((float) coords[0], (float) coords[1]); for (int i = count - 2; i >= 0; i--) { coords = (double[]) drState.upperCoordinates.get(i); area.lineTo((float) coords[0], (float) coords[1]); } area.closePath(); g2.fill(area); g2.setComposite(originalComposite); drState.lowerCoordinates.clear(); drState.upperCoordinates.clear(); } } if (isLinePass(pass)) { // the following code handles the line for the y-values...it's // all done by code in the super class if (item == 0) { State s = (State) state; s.seriesPath.reset(); s.setLastPointGood(false); } if (getItemLineVisible(series, item)) { drawPrimaryLineAsPath(state, g2, plot, dataset, pass, series, item, domainAxis, rangeAxis, dataArea); } } // second pass adds shapes where the items are .. else if (isItemPass(pass)) { // setup for collecting optional entity info... EntityCollection entities = null; if (info != null) { entities = info.getOwner().getEntityCollection(); } drawSecondaryPass(g2, plot, dataset, pass, series, item, domainAxis, dataArea, rangeAxis, crosshairState, entities); } } /** * Draws the item (first pass). This method draws the lines * connecting the items. Instead of drawing separate lines, * a {@code GeneralPath} is constructed and drawn at the end of * the series painting. * * @param g2 the graphics device. * @param state the renderer state. * @param plot the plot (can be used to obtain standard color information * etc). * @param dataset the dataset. * @param pass the pass. * @param series the series index (zero-based). * @param item the item index (zero-based). * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataArea the area within which the data is being drawn. */ @Override protected void drawPrimaryLineAsPath(XYItemRendererState state, Graphics2D g2, XYPlot plot, XYDataset dataset, int pass, int series, int item, ValueAxis domainAxis, ValueAxis rangeAxis, Rectangle2D dataArea) { RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); // get the data point... double x1 = dataset.getXValue(series, item); double y1 = dataset.getYValue(series, item); double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation); double transY1 = rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation); XYLineAndShapeRenderer.State s = (XYLineAndShapeRenderer.State) state; // update path to reflect latest point if (!Double.isNaN(transX1) && !Double.isNaN(transY1)) { float x = (float) transX1; float y = (float) transY1; PlotOrientation orientation = plot.getOrientation(); if (orientation == PlotOrientation.HORIZONTAL) { x = (float) transY1; y = (float) transX1; } if (s.isLastPointGood()) { if (item > 0) { if (orientation == PlotOrientation.HORIZONTAL) { s.seriesPath.lineTo(s.seriesPath.getCurrentPoint().getX(), y); } else { s.seriesPath.lineTo(x, s.seriesPath.getCurrentPoint().getY()); } } s.seriesPath.lineTo(x, y); } else { s.seriesPath.moveTo(x, y); } s.setLastPointGood(true); } else { s.setLastPointGood(false); } // if this is the last item, draw the path ... if (item == s.getLastItemIndex()) { // draw path drawFirstPassShape(g2, pass, series, item, s.seriesPath); } } /** * Tests this renderer for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof DeviationStepRenderer)) { return false; } DeviationStepRenderer that = (DeviationStepRenderer) obj; if (this.alpha != that.alpha) { return false; } return super.equals(obj); } }jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/GradientXYBarPainter.java000066400000000000000000000311261463604235500321600ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * GradientXYBarPainter.java * ------------------------- * (C) Copyright 2008-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; */ package org.jfree.chart.renderer.xy; import java.awt.Color; import java.awt.GradientPaint; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Stroke; import java.awt.geom.Rectangle2D; import java.awt.geom.RectangularShape; import java.io.Serializable; import org.jfree.chart.HashUtils; import org.jfree.chart.ui.RectangleEdge; /** * An implementation of the {@link XYBarPainter} interface that uses several * gradient fills to enrich the appearance of the bars. */ public class GradientXYBarPainter implements XYBarPainter, Serializable { /** The division point between the first and second gradient regions. */ private double g1; /** The division point between the second and third gradient regions. */ private double g2; /** The division point between the third and fourth gradient regions. */ private double g3; /** * Creates a new instance. */ public GradientXYBarPainter() { this(0.10, 0.20, 0.80); } /** * Creates a new instance. * * @param g1 the division between regions 1 and 2. * @param g2 the division between regions 2 and 3. * @param g3 the division between regions 3 and 4. */ public GradientXYBarPainter(double g1, double g2, double g3) { this.g1 = g1; this.g2 = g2; this.g3 = g3; } /** * Paints a single bar instance. * * @param g2 the graphics target. * @param renderer the renderer. * @param row the row index. * @param column the column index. * @param bar the bar * @param base indicates which side of the rectangle is the base of the * bar. */ @Override public void paintBar(Graphics2D g2, XYBarRenderer renderer, int row, int column, RectangularShape bar, RectangleEdge base) { Paint itemPaint = renderer.getItemPaint(row, column); Color c0, c1; if (itemPaint instanceof Color) { c0 = (Color) itemPaint; c1 = c0.brighter(); } else if (itemPaint instanceof GradientPaint) { GradientPaint gp = (GradientPaint) itemPaint; c0 = gp.getColor1(); c1 = gp.getColor2(); } else { c0 = Color.BLUE; c1 = Color.BLUE.brighter(); } // as a special case, if the bar colour has alpha == 0, we draw // nothing. if (c0.getAlpha() == 0) { return; } if (base == RectangleEdge.TOP || base == RectangleEdge.BOTTOM) { Rectangle2D[] regions = splitVerticalBar(bar, this.g1, this.g2, this.g3); GradientPaint gp = new GradientPaint((float) regions[0].getMinX(), 0.0f, c0, (float) regions[0].getMaxX(), 0.0f, Color.WHITE); g2.setPaint(gp); g2.fill(regions[0]); gp = new GradientPaint((float) regions[1].getMinX(), 0.0f, Color.WHITE, (float) regions[1].getMaxX(), 0.0f, c0); g2.setPaint(gp); g2.fill(regions[1]); gp = new GradientPaint((float) regions[2].getMinX(), 0.0f, c0, (float) regions[2].getMaxX(), 0.0f, c1); g2.setPaint(gp); g2.fill(regions[2]); gp = new GradientPaint((float) regions[3].getMinX(), 0.0f, c1, (float) regions[3].getMaxX(), 0.0f, c0); g2.setPaint(gp); g2.fill(regions[3]); } else if (base == RectangleEdge.LEFT || base == RectangleEdge.RIGHT) { Rectangle2D[] regions = splitHorizontalBar(bar, this.g1, this.g2, this.g3); GradientPaint gp = new GradientPaint(0.0f, (float) regions[0].getMinY(), c0, 0.0f, (float) regions[0].getMaxX(), Color.WHITE); g2.setPaint(gp); g2.fill(regions[0]); gp = new GradientPaint(0.0f, (float) regions[1].getMinY(), Color.WHITE, 0.0f, (float) regions[1].getMaxY(), c0); g2.setPaint(gp); g2.fill(regions[1]); gp = new GradientPaint(0.0f, (float) regions[2].getMinY(), c0, 0.0f, (float) regions[2].getMaxY(), c1); g2.setPaint(gp); g2.fill(regions[2]); gp = new GradientPaint(0.0f, (float) regions[3].getMinY(), c1, 0.0f, (float) regions[3].getMaxY(), c0); g2.setPaint(gp); g2.fill(regions[3]); } // draw the outline... if (renderer.isDrawBarOutline()) { Stroke stroke = renderer.getItemOutlineStroke(row, column); Paint paint = renderer.getItemOutlinePaint(row, column); if (stroke != null && paint != null) { g2.setStroke(stroke); g2.setPaint(paint); g2.draw(bar); } } } /** * Paints a single bar instance. * * @param g2 the graphics target. * @param renderer the renderer. * @param row the row index. * @param column the column index. * @param bar the bar * @param base indicates which side of the rectangle is the base of the * bar. * @param pegShadow peg the shadow to the base of the bar? */ @Override public void paintBarShadow(Graphics2D g2, XYBarRenderer renderer, int row, int column, RectangularShape bar, RectangleEdge base, boolean pegShadow) { // handle a special case - if the bar colour has alpha == 0, it is // invisible so we shouldn't draw any shadow Paint itemPaint = renderer.getItemPaint(row, column); if (itemPaint instanceof Color) { Color c = (Color) itemPaint; if (c.getAlpha() == 0) { return; } } RectangularShape shadow = createShadow(bar, renderer.getShadowXOffset(), renderer.getShadowYOffset(), base, pegShadow); g2.setPaint(Color.GRAY); g2.fill(shadow); } /** * Creates a shadow for the bar. * * @param bar the bar shape. * @param xOffset the x-offset for the shadow. * @param yOffset the y-offset for the shadow. * @param base the edge that is the base of the bar. * @param pegShadow peg the shadow to the base? * * @return A rectangle for the shadow. */ private Rectangle2D createShadow(RectangularShape bar, double xOffset, double yOffset, RectangleEdge base, boolean pegShadow) { double x0 = bar.getMinX(); double x1 = bar.getMaxX(); double y0 = bar.getMinY(); double y1 = bar.getMaxY(); if (base == RectangleEdge.TOP) { x0 += xOffset; x1 += xOffset; if (!pegShadow) { y0 += yOffset; } y1 += yOffset; } else if (base == RectangleEdge.BOTTOM) { x0 += xOffset; x1 += xOffset; y0 += yOffset; if (!pegShadow) { y1 += yOffset; } } else if (base == RectangleEdge.LEFT) { if (!pegShadow) { x0 += xOffset; } x1 += xOffset; y0 += yOffset; y1 += yOffset; } else if (base == RectangleEdge.RIGHT) { x0 += xOffset; if (!pegShadow) { x1 += xOffset; } y0 += yOffset; y1 += yOffset; } return new Rectangle2D.Double(x0, y0, (x1 - x0), (y1 - y0)); } /** * Splits a bar into subregions (elsewhere, these subregions will have * different gradients applied to them). * * @param bar the bar shape. * @param a the first division. * @param b the second division. * @param c the third division. * * @return An array containing four subregions. */ private Rectangle2D[] splitVerticalBar(RectangularShape bar, double a, double b, double c) { Rectangle2D[] result = new Rectangle2D[4]; double x0 = bar.getMinX(); double x1 = Math.rint(x0 + (bar.getWidth() * a)); double x2 = Math.rint(x0 + (bar.getWidth() * b)); double x3 = Math.rint(x0 + (bar.getWidth() * c)); result[0] = new Rectangle2D.Double(bar.getMinX(), bar.getMinY(), x1 - x0, bar.getHeight()); result[1] = new Rectangle2D.Double(x1, bar.getMinY(), x2 - x1, bar.getHeight()); result[2] = new Rectangle2D.Double(x2, bar.getMinY(), x3 - x2, bar.getHeight()); result[3] = new Rectangle2D.Double(x3, bar.getMinY(), bar.getMaxX() - x3, bar.getHeight()); return result; } /** * Splits a bar into subregions (elsewhere, these subregions will have * different gradients applied to them). * * @param bar the bar shape. * @param a the first division. * @param b the second division. * @param c the third division. * * @return An array containing four subregions. */ private Rectangle2D[] splitHorizontalBar(RectangularShape bar, double a, double b, double c) { Rectangle2D[] result = new Rectangle2D[4]; double y0 = bar.getMinY(); double y1 = Math.rint(y0 + (bar.getHeight() * a)); double y2 = Math.rint(y0 + (bar.getHeight() * b)); double y3 = Math.rint(y0 + (bar.getHeight() * c)); result[0] = new Rectangle2D.Double(bar.getMinX(), bar.getMinY(), bar.getWidth(), y1 - y0); result[1] = new Rectangle2D.Double(bar.getMinX(), y1, bar.getWidth(), y2 - y1); result[2] = new Rectangle2D.Double(bar.getMinX(), y2, bar.getWidth(), y3 - y2); result[3] = new Rectangle2D.Double(bar.getMinX(), y3, bar.getWidth(), bar.getMaxY() - y3); return result; } /** * Tests this instance for equality with an arbitrary object. * * @param obj the obj ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof GradientXYBarPainter)) { return false; } GradientXYBarPainter that = (GradientXYBarPainter) obj; if (this.g1 != that.g1) { return false; } if (this.g2 != that.g2) { return false; } if (this.g3 != that.g3) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int hash = 37; hash = HashUtils.hashCode(hash, this.g1); hash = HashUtils.hashCode(hash, this.g2); hash = HashUtils.hashCode(hash, this.g3); return hash; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/HighLowRenderer.java000066400000000000000000000407561463604235500312330ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * HighLowRenderer.java * -------------------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Richard Atkinson; * Christian W. Zuckschwerdt; * */ package org.jfree.chart.renderer.xy; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.plot.CrosshairState; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; import org.jfree.data.Range; import org.jfree.data.general.DatasetUtils; import org.jfree.data.xy.OHLCDataset; import org.jfree.data.xy.XYDataset; /** * A renderer that draws high/low/open/close markers on an {@link XYPlot} * (requires a {@link OHLCDataset}). This renderer does not include code to * calculate the crosshair point for the plot. * * The example shown here is generated by the {@code HighLowChartDemo1.java} * program included in the JFreeChart Demo Collection: *

* HighLowRendererSample.png */ public class HighLowRenderer extends AbstractXYItemRenderer implements XYItemRenderer, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -8135673815876552516L; /** A flag that controls whether the open ticks are drawn. */ private boolean drawOpenTicks; /** A flag that controls whether the close ticks are drawn. */ private boolean drawCloseTicks; /** * The paint used for the open ticks (if {@code null}, the series * paint is used instead). */ private transient Paint openTickPaint; /** * The paint used for the close ticks (if {@code null}, the series * paint is used instead). */ private transient Paint closeTickPaint; /** * The tick length (in Java2D units). */ private double tickLength; /** * The default constructor. */ public HighLowRenderer() { super(); this.drawOpenTicks = true; this.drawCloseTicks = true; this.tickLength = 2.0; } /** * Returns the flag that controls whether open ticks are drawn. * * @return A boolean. * * @see #getDrawCloseTicks() * @see #setDrawOpenTicks(boolean) */ public boolean getDrawOpenTicks() { return this.drawOpenTicks; } /** * Sets the flag that controls whether open ticks are drawn, and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param draw the flag. * * @see #getDrawOpenTicks() */ public void setDrawOpenTicks(boolean draw) { this.drawOpenTicks = draw; fireChangeEvent(); } /** * Returns the flag that controls whether close ticks are drawn. * * @return A boolean. * * @see #getDrawOpenTicks() * @see #setDrawCloseTicks(boolean) */ public boolean getDrawCloseTicks() { return this.drawCloseTicks; } /** * Sets the flag that controls whether close ticks are drawn, and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param draw the flag. * * @see #getDrawCloseTicks() */ public void setDrawCloseTicks(boolean draw) { this.drawCloseTicks = draw; fireChangeEvent(); } /** * Returns the paint used to draw the ticks for the open values. * * @return The paint used to draw the ticks for the open values (possibly * {@code null}). * * @see #setOpenTickPaint(Paint) */ public Paint getOpenTickPaint() { return this.openTickPaint; } /** * Sets the paint used to draw the ticks for the open values and sends a * {@link RendererChangeEvent} to all registered listeners. If you set * this to {@code null} (the default), the series paint is used * instead. * * @param paint the paint ({@code null} permitted). * * @see #getOpenTickPaint() */ public void setOpenTickPaint(Paint paint) { this.openTickPaint = paint; fireChangeEvent(); } /** * Returns the paint used to draw the ticks for the close values. * * @return The paint used to draw the ticks for the close values (possibly * {@code null}). * * @see #setCloseTickPaint(Paint) */ public Paint getCloseTickPaint() { return this.closeTickPaint; } /** * Sets the paint used to draw the ticks for the close values and sends a * {@link RendererChangeEvent} to all registered listeners. If you set * this to {@code null} (the default), the series paint is used * instead. * * @param paint the paint ({@code null} permitted). * * @see #getCloseTickPaint() */ public void setCloseTickPaint(Paint paint) { this.closeTickPaint = paint; fireChangeEvent(); } /** * Returns the tick length (in Java2D units). * * @return The tick length. * * @see #setTickLength(double) */ public double getTickLength() { return this.tickLength; } /** * Sets the tick length (in Java2D units) and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param length the length. * * @see #getTickLength() */ public void setTickLength(double length) { this.tickLength = length; fireChangeEvent(); } /** * Returns the range of values the renderer requires to display all the * items from the specified dataset. * * @param dataset the dataset ({@code null} permitted). * * @return The range ({@code null} if the dataset is {@code null} * or empty). */ @Override public Range findRangeBounds(XYDataset dataset) { if (dataset != null) { return DatasetUtils.findRangeBounds(dataset, true); } else { return null; } } /** * Draws the visual representation of a single data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the area within which the plot is being drawn. * @param info collects information about the drawing. * @param plot the plot (can be used to obtain standard color * information etc). * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset. * @param series the series index (zero-based). * @param item the item index (zero-based). * @param crosshairState crosshair information for the plot * ({@code null} permitted). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item, CrosshairState crosshairState, int pass) { double x = dataset.getXValue(series, item); if (!domainAxis.getRange().contains(x)) { return; // the x value is not within the axis range } double xx = domainAxis.valueToJava2D(x, dataArea, plot.getDomainAxisEdge()); // setup for collecting optional entity info... Shape entityArea = null; EntityCollection entities = null; if (info != null) { entities = info.getOwner().getEntityCollection(); } PlotOrientation orientation = plot.getOrientation(); RectangleEdge location = plot.getRangeAxisEdge(); Paint itemPaint = getItemPaint(series, item); Stroke itemStroke = getItemStroke(series, item); g2.setPaint(itemPaint); g2.setStroke(itemStroke); if (dataset instanceof OHLCDataset) { OHLCDataset hld = (OHLCDataset) dataset; double yHigh = hld.getHighValue(series, item); double yLow = hld.getLowValue(series, item); if (!Double.isNaN(yHigh) && !Double.isNaN(yLow)) { double yyHigh = rangeAxis.valueToJava2D(yHigh, dataArea, location); double yyLow = rangeAxis.valueToJava2D(yLow, dataArea, location); if (orientation == PlotOrientation.HORIZONTAL) { g2.draw(new Line2D.Double(yyLow, xx, yyHigh, xx)); entityArea = new Rectangle2D.Double(Math.min(yyLow, yyHigh), xx - 1.0, Math.abs(yyHigh - yyLow), 2.0); } else if (orientation == PlotOrientation.VERTICAL) { g2.draw(new Line2D.Double(xx, yyLow, xx, yyHigh)); entityArea = new Rectangle2D.Double(xx - 1.0, Math.min(yyLow, yyHigh), 2.0, Math.abs(yyHigh - yyLow)); } } double delta = getTickLength(); if (domainAxis.isInverted()) { delta = -delta; } if (getDrawOpenTicks()) { double yOpen = hld.getOpenValue(series, item); if (!Double.isNaN(yOpen)) { double yyOpen = rangeAxis.valueToJava2D(yOpen, dataArea, location); if (this.openTickPaint != null) { g2.setPaint(this.openTickPaint); } else { g2.setPaint(itemPaint); } if (orientation == PlotOrientation.HORIZONTAL) { g2.draw(new Line2D.Double(yyOpen, xx + delta, yyOpen, xx)); } else if (orientation == PlotOrientation.VERTICAL) { g2.draw(new Line2D.Double(xx - delta, yyOpen, xx, yyOpen)); } } } if (getDrawCloseTicks()) { double yClose = hld.getCloseValue(series, item); if (!Double.isNaN(yClose)) { double yyClose = rangeAxis.valueToJava2D( yClose, dataArea, location); if (this.closeTickPaint != null) { g2.setPaint(this.closeTickPaint); } else { g2.setPaint(itemPaint); } if (orientation == PlotOrientation.HORIZONTAL) { g2.draw(new Line2D.Double(yyClose, xx, yyClose, xx - delta)); } else if (orientation == PlotOrientation.VERTICAL) { g2.draw(new Line2D.Double(xx, yyClose, xx + delta, yyClose)); } } } } else { // not a HighLowDataset, so just draw a line connecting this point // with the previous point... if (item > 0) { double x0 = dataset.getXValue(series, item - 1); double y0 = dataset.getYValue(series, item - 1); double y = dataset.getYValue(series, item); if (Double.isNaN(x0) || Double.isNaN(y0) || Double.isNaN(y)) { return; } double xx0 = domainAxis.valueToJava2D(x0, dataArea, plot.getDomainAxisEdge()); double yy0 = rangeAxis.valueToJava2D(y0, dataArea, location); double yy = rangeAxis.valueToJava2D(y, dataArea, location); if (orientation == PlotOrientation.HORIZONTAL) { g2.draw(new Line2D.Double(yy0, xx0, yy, xx)); } else if (orientation == PlotOrientation.VERTICAL) { g2.draw(new Line2D.Double(xx0, yy0, xx, yy)); } } } if (entities != null) { addEntity(entities, entityArea, dataset, series, item, 0.0, 0.0); } } /** * Returns a clone of the renderer. * * @return A clone. * * @throws CloneNotSupportedException if the renderer cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Tests this renderer for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof HighLowRenderer)) { return false; } HighLowRenderer that = (HighLowRenderer) obj; if (this.drawOpenTicks != that.drawOpenTicks) { return false; } if (this.drawCloseTicks != that.drawCloseTicks) { return false; } if (!PaintUtils.equal(this.openTickPaint, that.openTickPaint)) { return false; } if (!PaintUtils.equal(this.closeTickPaint, that.closeTickPaint)) { return false; } if (this.tickLength != that.tickLength) { return false; } if (!super.equals(obj)) { return false; } return true; } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.openTickPaint = SerialUtils.readPaint(stream); this.closeTickPaint = SerialUtils.readPaint(stream); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.openTickPaint, stream); SerialUtils.writePaint(this.closeTickPaint, stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/SamplingXYLineRenderer.java000066400000000000000000000301661463604235500325270ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * SamplingXYLineRenderer.java * --------------------------- * (C) Copyright 2008-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; */ package org.jfree.chart.renderer.xy; import java.awt.Graphics2D; import java.awt.Shape; import java.awt.geom.GeneralPath; import java.awt.geom.Line2D; import java.awt.geom.PathIterator; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.plot.CrosshairState; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; import org.jfree.chart.util.ShapeUtils; import org.jfree.data.xy.XYDataset; /** * A renderer that draws line charts. The renderer doesn't necessarily plot * every data item - instead, it tries to plot only those data items that * make a difference to the visual output (the other data items are skipped). * This renderer is designed for use with the {@link XYPlot} class. */ public class SamplingXYLineRenderer extends AbstractXYItemRenderer implements XYItemRenderer, Cloneable, PublicCloneable, Serializable { /** The shape that is used to represent a line in the legend. */ private transient Shape legendLine; /** * Creates a new renderer. */ public SamplingXYLineRenderer() { this.legendLine = new Line2D.Double(-7.0, 0.0, 7.0, 0.0); setDefaultLegendShape(this.legendLine); setTreatLegendShapeAsLine(true); } /** * Returns the number of passes through the data that the renderer requires * in order to draw the chart. Most charts will require a single pass, but * some require two passes. * * @return The pass count. */ @Override public int getPassCount() { return 1; } /** * Records the state for the renderer. This is used to preserve state * information between calls to the drawItem() method for a single chart * drawing. */ public static class State extends XYItemRendererState { /** The path for the current series. */ GeneralPath seriesPath; /** * A second path that draws vertical intervals to cover any extreme * values. */ GeneralPath intervalPath; /** * The minimum change in the x-value needed to trigger an update to * the seriesPath. */ double dX = 1.0; /** The last x-coordinate visited by the seriesPath. */ double lastX; /** The initial y-coordinate for the current x-coordinate. */ double openY = 0.0; /** The highest y-coordinate for the current x-coordinate. */ double highY = 0.0; /** The lowest y-coordinate for the current x-coordinate. */ double lowY = 0.0; /** The final y-coordinate for the current x-coordinate. */ double closeY = 0.0; /** * A flag that indicates if the last (x, y) point was 'good' * (non-null). */ boolean lastPointGood; /** * Creates a new state instance. * * @param info the plot rendering info. */ public State(PlotRenderingInfo info) { super(info); } /** * This method is called by the {@link XYPlot} at the start of each * series pass. We reset the state for the current series. * * @param dataset the dataset. * @param series the series index. * @param firstItem the first item index for this pass. * @param lastItem the last item index for this pass. * @param pass the current pass index. * @param passCount the number of passes. */ @Override public void startSeriesPass(XYDataset dataset, int series, int firstItem, int lastItem, int pass, int passCount) { this.seriesPath.reset(); this.intervalPath.reset(); this.lastPointGood = false; super.startSeriesPass(dataset, series, firstItem, lastItem, pass, passCount); } } /** * Initialises the renderer. *

* This method will be called before the first item is rendered, giving the * renderer an opportunity to initialise any state information it wants to * maintain. The renderer can do nothing if it chooses. * * @param g2 the graphics device. * @param dataArea the area inside the axes. * @param plot the plot. * @param data the data. * @param info an optional info collection object to return data back to * the caller. * * @return The renderer state. */ @Override public XYItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, XYPlot plot, XYDataset data, PlotRenderingInfo info) { double dpi = 72; // Integer dpiVal = (Integer) g2.getRenderingHint(HintKey.DPI); // if (dpiVal != null) { // dpi = dpiVal.intValue(); // } State state = new State(info); state.seriesPath = new GeneralPath(); state.intervalPath = new GeneralPath(); state.dX = 72.0 / dpi; return state; } /** * Draws the visual representation of a single data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the area within which the data is being drawn. * @param info collects information about the drawing. * @param plot the plot (can be used to obtain standard color * information etc). * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset. * @param series the series index (zero-based). * @param item the item index (zero-based). * @param crosshairState crosshair information for the plot * ({@code null} permitted). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item, CrosshairState crosshairState, int pass) { // do nothing if item is not visible if (!getItemVisible(series, item)) { return; } RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); // get the data point... double x1 = dataset.getXValue(series, item); double y1 = dataset.getYValue(series, item); double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation); double transY1 = rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation); State s = (State) state; // update path to reflect latest point if (!Double.isNaN(transX1) && !Double.isNaN(transY1)) { float x = (float) transX1; float y = (float) transY1; PlotOrientation orientation = plot.getOrientation(); if (orientation == PlotOrientation.HORIZONTAL) { x = (float) transY1; y = (float) transX1; } if (s.lastPointGood) { if ((Math.abs(x - s.lastX) > s.dX)) { s.seriesPath.lineTo(x, y); if (s.lowY < s.highY) { s.intervalPath.moveTo((float) s.lastX, (float) s.lowY); s.intervalPath.lineTo((float) s.lastX, (float) s.highY); } s.lastX = x; s.openY = y; s.highY = y; s.lowY = y; s.closeY = y; } else { s.highY = Math.max(s.highY, y); s.lowY = Math.min(s.lowY, y); s.closeY = y; } } else { s.seriesPath.moveTo(x, y); s.lastX = x; s.openY = y; s.highY = y; s.lowY = y; s.closeY = y; } s.lastPointGood = true; } else { s.lastPointGood = false; } // if this is the last item, draw the path ... if (item == s.getLastItemIndex()) { // draw path PathIterator pi = s.seriesPath.getPathIterator(null); int count = 0; while (!pi.isDone()) { count++; pi.next(); } g2.setStroke(getItemStroke(series, item)); g2.setPaint(getItemPaint(series, item)); g2.draw(s.seriesPath); g2.draw(s.intervalPath); } } /** * Returns a clone of the renderer. * * @return A clone. * * @throws CloneNotSupportedException if the clone cannot be created. */ @Override public Object clone() throws CloneNotSupportedException { SamplingXYLineRenderer clone = (SamplingXYLineRenderer) super.clone(); if (this.legendLine != null) { clone.legendLine = ShapeUtils.clone(this.legendLine); } return clone; } /** * Tests this renderer for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof SamplingXYLineRenderer)) { return false; } if (!super.equals(obj)) { return false; } SamplingXYLineRenderer that = (SamplingXYLineRenderer) obj; if (!ShapeUtils.equal(this.legendLine, that.legendLine)) { return false; } return true; } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.legendLine = SerialUtils.readShape(stream); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeShape(this.legendLine, stream); } }jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/StackedXYAreaRenderer.java000066400000000000000000000572601463604235500323200ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * StackedXYAreaRenderer.java * -------------------------- * (C) Copyright 2003-present, by Richard Atkinson and Contributors. * * Original Author: Richard Atkinson; * Contributor(s): Christian W. Zuckschwerdt; * David Gilbert; * Ulrich Voigt (patch #312); * */ package org.jfree.chart.renderer.xy; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Point; import java.awt.Polygon; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Area; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Objects; import java.util.Stack; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.entity.XYItemEntity; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.labels.XYToolTipGenerator; import org.jfree.chart.plot.CrosshairState; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.urls.XYURLGenerator; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; import org.jfree.chart.util.ShapeUtils; import org.jfree.data.Range; import org.jfree.data.general.DatasetUtils; import org.jfree.data.xy.TableXYDataset; import org.jfree.data.xy.XYDataset; /** * A stacked area renderer for the {@link XYPlot} class. *

* The example shown here is generated by the * {@code StackedXYAreaRendererDemo1.java} program included in the * JFreeChart demo collection: *

* StackedXYAreaRendererSample.png *

* SPECIAL NOTE: This renderer does not currently handle negative data values * correctly. This should get fixed at some point, but the current workaround * is to use the {@link StackedXYAreaRenderer2} class instead. */ public class StackedXYAreaRenderer extends XYAreaRenderer implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 5217394318178570889L; /** * A state object for use by this renderer. */ static class StackedXYAreaRendererState extends XYItemRendererState { /** The area for the current series. */ private Polygon seriesArea; /** The line. */ private Line2D line; /** The points from the last series. */ private Stack lastSeriesPoints; /** The points for the current series. */ private Stack currentSeriesPoints; /** * Creates a new state for the renderer. * * @param info the plot rendering info. */ public StackedXYAreaRendererState(PlotRenderingInfo info) { super(info); this.seriesArea = null; this.line = new Line2D.Double(); this.lastSeriesPoints = new Stack(); this.currentSeriesPoints = new Stack(); } /** * Returns the series area. * * @return The series area. */ public Polygon getSeriesArea() { return this.seriesArea; } /** * Sets the series area. * * @param area the area. */ public void setSeriesArea(Polygon area) { this.seriesArea = area; } /** * Returns the working line. * * @return The working line. */ public Line2D getLine() { return this.line; } /** * Returns the current series points. * * @return The current series points. */ public Stack getCurrentSeriesPoints() { return this.currentSeriesPoints; } /** * Sets the current series points. * * @param points the points. */ public void setCurrentSeriesPoints(Stack points) { this.currentSeriesPoints = points; } /** * Returns the last series points. * * @return The last series points. */ public Stack getLastSeriesPoints() { return this.lastSeriesPoints; } /** * Sets the last series points. * * @param points the points. */ public void setLastSeriesPoints(Stack points) { this.lastSeriesPoints = points; } } /** * Custom Paint for drawing all shapes, if null defaults to series shapes */ private transient Paint shapePaint = null; /** * Custom Stroke for drawing all shapes, if null defaults to series * strokes. */ private transient Stroke shapeStroke = null; /** * Creates a new renderer. */ public StackedXYAreaRenderer() { this(AREA); } /** * Constructs a new renderer. * * @param type the type of the renderer. */ public StackedXYAreaRenderer(int type) { this(type, null, null); } /** * Constructs a new renderer. To specify the type of renderer, use one of * the constants: {@code SHAPES}, {@code LINES}, {@code SHAPES_AND_LINES}, * {@code AREA} or {@code AREA_AND_SHAPES}. * * @param type the type of renderer. * @param labelGenerator the tool tip generator ({@code null} permitted). * @param urlGenerator the URL generator ({@code null} permitted). */ public StackedXYAreaRenderer(int type, XYToolTipGenerator labelGenerator, XYURLGenerator urlGenerator) { super(type, labelGenerator, urlGenerator); } /** * Returns the paint used for rendering shapes, or {@code null} if * using series paints. * * @return The paint (possibly {@code null}). * * @see #setShapePaint(Paint) */ public Paint getShapePaint() { return this.shapePaint; } /** * Sets the paint for rendering shapes and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param shapePaint the paint ({@code null} permitted). * * @see #getShapePaint() */ public void setShapePaint(Paint shapePaint) { this.shapePaint = shapePaint; fireChangeEvent(); } /** * Returns the stroke used for rendering shapes, or {@code null} if * using series strokes. * * @return The stroke (possibly {@code null}). * * @see #setShapeStroke(Stroke) */ public Stroke getShapeStroke() { return this.shapeStroke; } /** * Sets the stroke for rendering shapes and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param shapeStroke the stroke ({@code null} permitted). * * @see #getShapeStroke() */ public void setShapeStroke(Stroke shapeStroke) { this.shapeStroke = shapeStroke; fireChangeEvent(); } /** * Initialises the renderer. This method will be called before the first * item is rendered, giving the renderer an opportunity to initialise any * state information it wants to maintain. * * @param g2 the graphics device. * @param dataArea the area inside the axes. * @param plot the plot. * @param data the data. * @param info an optional info collection object to return data back to * the caller. * * @return A state object that should be passed to subsequent calls to the * drawItem() method. */ @Override public XYItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, XYPlot plot, XYDataset data, PlotRenderingInfo info) { XYItemRendererState state = new StackedXYAreaRendererState(info); // in the rendering process, there is special handling for item // zero, so we can't support processing of visible data items only state.setProcessVisibleItemsOnly(false); return state; } /** * Returns the number of passes required by the renderer. * * @return 2. */ @Override public int getPassCount() { return 2; } /** * Returns the range of values the renderer requires to display all the * items from the specified dataset. * * @param dataset the dataset ({@code null} permitted). * * @return The range ([0.0, 0.0] if the dataset contains no values, and * {@code null} if the dataset is {@code null}). * * @throws ClassCastException if {@code dataset} is not an instance * of {@link TableXYDataset}. */ @Override public Range findRangeBounds(XYDataset dataset) { if (dataset != null) { return DatasetUtils.findStackedRangeBounds( (TableXYDataset) dataset); } else { return null; } } /** * Draws the visual representation of a single data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the area within which the data is being drawn. * @param info collects information about the drawing. * @param plot the plot (can be used to obtain standard color information * etc). * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset. * @param series the series index (zero-based). * @param item the item index (zero-based). * @param crosshairState information about crosshairs on a plot. * @param pass the pass index. * * @throws ClassCastException if {@code state} is not an instance of * {@code StackedXYAreaRendererState} or {@code dataset} * is not an instance of {@link TableXYDataset}. */ @Override public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item, CrosshairState crosshairState, int pass) { PlotOrientation orientation = plot.getOrientation(); StackedXYAreaRendererState areaState = (StackedXYAreaRendererState) state; // Get the item count for the series, so that we can know which is the // end of the series. TableXYDataset tdataset = (TableXYDataset) dataset; int itemCount = tdataset.getItemCount(); // get the data point... double x1 = dataset.getXValue(series, item); double y1 = dataset.getYValue(series, item); boolean nullPoint = false; if (Double.isNaN(y1)) { y1 = 0.0; nullPoint = true; } // Get height adjustment based on stack and translate to Java2D values double ph1 = getPreviousHeight(tdataset, series, item); double transX1 = domainAxis.valueToJava2D(x1, dataArea, plot.getDomainAxisEdge()); double transY1 = rangeAxis.valueToJava2D(y1 + ph1, dataArea, plot.getRangeAxisEdge()); // Get series Paint and Stroke Paint seriesPaint = getItemPaint(series, item); Paint seriesFillPaint = seriesPaint; if (getUseFillPaint()) { seriesFillPaint = getItemFillPaint(series, item); } Stroke seriesStroke = getItemStroke(series, item); if (pass == 0) { // On first pass render the areas, line and outlines if (item == 0) { // Create a new Area for the series areaState.setSeriesArea(new Polygon()); areaState.setLastSeriesPoints( areaState.getCurrentSeriesPoints()); areaState.setCurrentSeriesPoints(new Stack()); // start from previous height (ph1) double transY2 = rangeAxis.valueToJava2D(ph1, dataArea, plot.getRangeAxisEdge()); // The first point is (x, 0) if (orientation == PlotOrientation.VERTICAL) { areaState.getSeriesArea().addPoint((int) transX1, (int) transY2); } else if (orientation == PlotOrientation.HORIZONTAL) { areaState.getSeriesArea().addPoint((int) transY2, (int) transX1); } } // Add each point to Area (x, y) if (orientation == PlotOrientation.VERTICAL) { Point point = new Point((int) transX1, (int) transY1); areaState.getSeriesArea().addPoint((int) point.getX(), (int) point.getY()); areaState.getCurrentSeriesPoints().push(point); } else if (orientation == PlotOrientation.HORIZONTAL) { areaState.getSeriesArea().addPoint((int) transY1, (int) transX1); } if (getPlotLines()) { if (item > 0) { // get the previous data point... double x0 = dataset.getXValue(series, item - 1); double y0 = dataset.getYValue(series, item - 1); double ph0 = getPreviousHeight(tdataset, series, item - 1); double transX0 = domainAxis.valueToJava2D(x0, dataArea, plot.getDomainAxisEdge()); double transY0 = rangeAxis.valueToJava2D(y0 + ph0, dataArea, plot.getRangeAxisEdge()); if (orientation == PlotOrientation.VERTICAL) { areaState.getLine().setLine(transX0, transY0, transX1, transY1); } else if (orientation == PlotOrientation.HORIZONTAL) { areaState.getLine().setLine(transY0, transX0, transY1, transX1); } g2.setPaint(seriesPaint); g2.setStroke(seriesStroke); g2.draw(areaState.getLine()); } } // Check if the item is the last item for the series and number of // items > 0. We can't draw an area for a single point. if (getPlotArea() && item > 0 && item == (itemCount - 1)) { double transY2 = rangeAxis.valueToJava2D(ph1, dataArea, plot.getRangeAxisEdge()); if (orientation == PlotOrientation.VERTICAL) { // Add the last point (x,0) areaState.getSeriesArea().addPoint((int) transX1, (int) transY2); } else if (orientation == PlotOrientation.HORIZONTAL) { // Add the last point (x,0) areaState.getSeriesArea().addPoint((int) transY2, (int) transX1); } // Add points from last series to complete the base of the // polygon if (series != 0) { Stack points = areaState.getLastSeriesPoints(); while (!points.empty()) { Point point = (Point) points.pop(); areaState.getSeriesArea().addPoint((int) point.getX(), (int) point.getY()); } } // Fill the polygon g2.setPaint(seriesFillPaint); g2.setStroke(seriesStroke); g2.fill(areaState.getSeriesArea()); // Draw an outline around the Area. if (isOutline()) { g2.setStroke(lookupSeriesOutlineStroke(series)); g2.setPaint(lookupSeriesOutlinePaint(series)); g2.draw(areaState.getSeriesArea()); } } int datasetIndex = plot.indexOf(dataset); updateCrosshairValues(crosshairState, x1, ph1 + y1, datasetIndex, transX1, transY1, orientation); } else if (pass == 1) { // On second pass render shapes and collect entity and tooltip // information Shape shape = null; if (getPlotShapes()) { shape = getItemShape(series, item); if (plot.getOrientation() == PlotOrientation.VERTICAL) { shape = ShapeUtils.createTranslatedShape(shape, transX1, transY1); } else if (plot.getOrientation() == PlotOrientation.HORIZONTAL) { shape = ShapeUtils.createTranslatedShape(shape, transY1, transX1); } if (!nullPoint) { if (getShapePaint() != null) { g2.setPaint(getShapePaint()); } else { g2.setPaint(seriesPaint); } if (getShapeStroke() != null) { g2.setStroke(getShapeStroke()); } else { g2.setStroke(seriesStroke); } g2.draw(shape); } } else { if (plot.getOrientation() == PlotOrientation.VERTICAL) { shape = new Rectangle2D.Double(transX1 - 3, transY1 - 3, 6.0, 6.0); } else if (plot.getOrientation() == PlotOrientation.HORIZONTAL) { shape = new Rectangle2D.Double(transY1 - 3, transX1 - 3, 6.0, 6.0); } } // collect entity and tool tip information... if (state.getInfo() != null) { EntityCollection entities = state.getEntityCollection(); if (entities != null && shape != null && !nullPoint) { // limit the entity hotspot area to the data area Area dataAreaHotspot = new Area(shape); dataAreaHotspot.intersect(new Area(dataArea)); if (!dataAreaHotspot.isEmpty()) { String tip = null; XYToolTipGenerator generator = getToolTipGenerator( series, item); if (generator != null) { tip = generator.generateToolTip(dataset, series, item); } String url = null; if (getURLGenerator() != null) { url = getURLGenerator().generateURL(dataset, series, item); } XYItemEntity entity = new XYItemEntity(dataAreaHotspot, dataset, series, item, tip, url); entities.add(entity); } } } } } /** * Calculates the stacked value of the all series up to, but not including * {@code series} for the specified item. It returns 0.0 if * {@code series} is the first series, i.e. 0. * * @param dataset the dataset. * @param series the series. * @param index the index. * * @return The cumulative value for all series' values up to but excluding * {@code series} for {@code index}. */ protected double getPreviousHeight(TableXYDataset dataset, int series, int index) { double result = 0.0; for (int i = 0; i < series; i++) { double value = dataset.getYValue(i, index); if (!Double.isNaN(value)) { result += value; } } return result; } /** * Tests the renderer for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof StackedXYAreaRenderer) || !super.equals(obj)) { return false; } StackedXYAreaRenderer that = (StackedXYAreaRenderer) obj; if (!PaintUtils.equal(this.shapePaint, that.shapePaint)) { return false; } if (!Objects.equals(this.shapeStroke, that.shapeStroke)) { return false; } return true; } /** * Returns a clone of the renderer. * * @return A clone. * * @throws CloneNotSupportedException if the renderer cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.shapePaint = SerialUtils.readPaint(stream); this.shapeStroke = SerialUtils.readStroke(stream); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.shapePaint, stream); SerialUtils.writeStroke(this.shapeStroke, stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/StackedXYAreaRenderer2.java000066400000000000000000000477441463604235500324100ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * StackedXYAreaRenderer2.java * --------------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert, based on * the StackedXYAreaRenderer class by Richard Atkinson; * Contributor(s): Ulrich Voigt (patch #312); * */ package org.jfree.chart.renderer.xy; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.geom.Area; import java.awt.geom.GeneralPath; import java.awt.geom.Rectangle2D; import java.io.Serializable; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.labels.XYToolTipGenerator; import org.jfree.chart.plot.CrosshairState; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.urls.XYURLGenerator; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.Range; import org.jfree.data.xy.TableXYDataset; import org.jfree.data.xy.XYDataset; /** * A stacked area renderer for the {@link XYPlot} class. * The example shown here is generated by the * {@code StackedXYAreaChartDemo2.java} program included in the * JFreeChart demo collection: *

* StackedXYAreaRenderer2Sample.png */ public class StackedXYAreaRenderer2 extends XYAreaRenderer2 implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 7752676509764539182L; /** * This flag controls whether or not the x-coordinates (in Java2D space) * are rounded to integers. When set to true, this can avoid the vertical * striping that anti-aliasing can generate. However, the rounding may not * be appropriate for output in high resolution formats (for example, * vector graphics formats such as SVG and PDF). */ private boolean roundXCoordinates; /** * Creates a new renderer. */ public StackedXYAreaRenderer2() { this(null, null); } /** * Constructs a new renderer. * * @param labelGenerator the tool tip generator to use ({@code null} * permitted). * @param urlGenerator the URL generator ({@code null} permitted). */ public StackedXYAreaRenderer2(XYToolTipGenerator labelGenerator, XYURLGenerator urlGenerator) { super(labelGenerator, urlGenerator); this.roundXCoordinates = true; } /** * Returns the flag that controls whether or not the x-coordinates (in * Java2D space) are rounded to integer values. * * @return The flag. * * @see #setRoundXCoordinates(boolean) */ public boolean getRoundXCoordinates() { return this.roundXCoordinates; } /** * Sets the flag that controls whether or not the x-coordinates (in * Java2D space) are rounded to integer values, and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param round the new flag value. * * @see #getRoundXCoordinates() */ public void setRoundXCoordinates(boolean round) { this.roundXCoordinates = round; fireChangeEvent(); } /** * Returns the range of values the renderer requires to display all the * items from the specified dataset. * * @param dataset the dataset ({@code null} permitted). * * @return The range (or {@code null} if the dataset is {@code null} or * empty). */ @Override public Range findRangeBounds(XYDataset dataset) { if (dataset == null) { return null; } double min = Double.POSITIVE_INFINITY; double max = Double.NEGATIVE_INFINITY; TableXYDataset d = (TableXYDataset) dataset; int itemCount = d.getItemCount(); for (int i = 0; i < itemCount; i++) { double[] stackValues = getStackValues((TableXYDataset) dataset, d.getSeriesCount(), i); min = Math.min(min, stackValues[0]); max = Math.max(max, stackValues[1]); } if (min == Double.POSITIVE_INFINITY) { return null; } return new Range(min, max); } /** * Returns the number of passes required by the renderer. * * @return 1. */ @Override public int getPassCount() { return 1; } /** * Draws the visual representation of a single data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the area within which the data is being drawn. * @param info collects information about the drawing. * @param plot the plot (can be used to obtain standard color information * etc). * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset. * @param series the series index (zero-based). * @param item the item index (zero-based). * @param crosshairState information about crosshairs on a plot. * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item, CrosshairState crosshairState, int pass) { // setup for collecting optional entity info... EntityCollection entities = null; if (info != null) { entities = info.getOwner().getEntityCollection(); } TableXYDataset tdataset = (TableXYDataset) dataset; PlotOrientation orientation = plot.getOrientation(); // get the data point... double x1 = dataset.getXValue(series, item); double y1 = dataset.getYValue(series, item); if (Double.isNaN(y1)) { y1 = 0.0; } double[] stack1 = getStackValues(tdataset, series, item); // get the previous point and the next point so we can calculate a // "hot spot" for the area (used by the chart entity)... double x0 = dataset.getXValue(series, Math.max(item - 1, 0)); double y0 = dataset.getYValue(series, Math.max(item - 1, 0)); if (Double.isNaN(y0)) { y0 = 0.0; } double[] stack0 = getStackValues(tdataset, series, Math.max(item - 1, 0)); int itemCount = dataset.getItemCount(series); double x2 = dataset.getXValue(series, Math.min(item + 1, itemCount - 1)); double y2 = dataset.getYValue(series, Math.min(item + 1, itemCount - 1)); if (Double.isNaN(y2)) { y2 = 0.0; } double[] stack2 = getStackValues(tdataset, series, Math.min(item + 1, itemCount - 1)); double xleft = (x0 + x1) / 2.0; double xright = (x1 + x2) / 2.0; double[] stackLeft = averageStackValues(stack0, stack1); double[] stackRight = averageStackValues(stack1, stack2); double[] adjStackLeft = adjustedStackValues(stack0, stack1); double[] adjStackRight = adjustedStackValues(stack1, stack2); RectangleEdge edge0 = plot.getDomainAxisEdge(); float transX1 = (float) domainAxis.valueToJava2D(x1, dataArea, edge0); float transXLeft = (float) domainAxis.valueToJava2D(xleft, dataArea, edge0); float transXRight = (float) domainAxis.valueToJava2D(xright, dataArea, edge0); if (this.roundXCoordinates) { transX1 = Math.round(transX1); transXLeft = Math.round(transXLeft); transXRight = Math.round(transXRight); } float transY1; RectangleEdge edge1 = plot.getRangeAxisEdge(); GeneralPath left = new GeneralPath(); GeneralPath right = new GeneralPath(); if (y1 >= 0.0) { // handle positive value transY1 = (float) rangeAxis.valueToJava2D(y1 + stack1[1], dataArea, edge1); float transStack1 = (float) rangeAxis.valueToJava2D(stack1[1], dataArea, edge1); float transStackLeft = (float) rangeAxis.valueToJava2D( adjStackLeft[1], dataArea, edge1); // LEFT POLYGON if (y0 >= 0.0) { double yleft = (y0 + y1) / 2.0 + stackLeft[1]; float transYLeft = (float) rangeAxis.valueToJava2D(yleft, dataArea, edge1); if (orientation == PlotOrientation.VERTICAL) { left.moveTo(transX1, transY1); left.lineTo(transX1, transStack1); left.lineTo(transXLeft, transStackLeft); left.lineTo(transXLeft, transYLeft); } else { left.moveTo(transY1, transX1); left.lineTo(transStack1, transX1); left.lineTo(transStackLeft, transXLeft); left.lineTo(transYLeft, transXLeft); } left.closePath(); } else { if (orientation == PlotOrientation.VERTICAL) { left.moveTo(transX1, transStack1); left.lineTo(transX1, transY1); left.lineTo(transXLeft, transStackLeft); } else { left.moveTo(transStack1, transX1); left.lineTo(transY1, transX1); left.lineTo(transStackLeft, transXLeft); } left.closePath(); } float transStackRight = (float) rangeAxis.valueToJava2D( adjStackRight[1], dataArea, edge1); // RIGHT POLYGON if (y2 >= 0.0) { double yright = (y1 + y2) / 2.0 + stackRight[1]; float transYRight = (float) rangeAxis.valueToJava2D(yright, dataArea, edge1); if (orientation == PlotOrientation.VERTICAL) { right.moveTo(transX1, transStack1); right.lineTo(transX1, transY1); right.lineTo(transXRight, transYRight); right.lineTo(transXRight, transStackRight); } else { right.moveTo(transStack1, transX1); right.lineTo(transY1, transX1); right.lineTo(transYRight, transXRight); right.lineTo(transStackRight, transXRight); } right.closePath(); } else { if (orientation == PlotOrientation.VERTICAL) { right.moveTo(transX1, transStack1); right.lineTo(transX1, transY1); right.lineTo(transXRight, transStackRight); } else { right.moveTo(transStack1, transX1); right.lineTo(transY1, transX1); right.lineTo(transStackRight, transXRight); } right.closePath(); } } else { // handle negative value transY1 = (float) rangeAxis.valueToJava2D(y1 + stack1[0], dataArea, edge1); float transStack1 = (float) rangeAxis.valueToJava2D(stack1[0], dataArea, edge1); float transStackLeft = (float) rangeAxis.valueToJava2D( adjStackLeft[0], dataArea, edge1); // LEFT POLYGON if (y0 >= 0.0) { if (orientation == PlotOrientation.VERTICAL) { left.moveTo(transX1, transStack1); left.lineTo(transX1, transY1); left.lineTo(transXLeft, transStackLeft); } else { left.moveTo(transStack1, transX1); left.lineTo(transY1, transX1); left.lineTo(transStackLeft, transXLeft); } left.clone(); } else { double yleft = (y0 + y1) / 2.0 + stackLeft[0]; float transYLeft = (float) rangeAxis.valueToJava2D(yleft, dataArea, edge1); if (orientation == PlotOrientation.VERTICAL) { left.moveTo(transX1, transY1); left.lineTo(transX1, transStack1); left.lineTo(transXLeft, transStackLeft); left.lineTo(transXLeft, transYLeft); } else { left.moveTo(transY1, transX1); left.lineTo(transStack1, transX1); left.lineTo(transStackLeft, transXLeft); left.lineTo(transYLeft, transXLeft); } left.closePath(); } float transStackRight = (float) rangeAxis.valueToJava2D( adjStackRight[0], dataArea, edge1); // RIGHT POLYGON if (y2 >= 0.0) { if (orientation == PlotOrientation.VERTICAL) { right.moveTo(transX1, transStack1); right.lineTo(transX1, transY1); right.lineTo(transXRight, transStackRight); } else { right.moveTo(transStack1, transX1); right.lineTo(transY1, transX1); right.lineTo(transStackRight, transXRight); } right.closePath(); } else { double yright = (y1 + y2) / 2.0 + stackRight[0]; float transYRight = (float) rangeAxis.valueToJava2D(yright, dataArea, edge1); if (orientation == PlotOrientation.VERTICAL) { right.moveTo(transX1, transStack1); right.lineTo(transX1, transY1); right.lineTo(transXRight, transYRight); right.lineTo(transXRight, transStackRight); } else { right.moveTo(transStack1, transX1); right.lineTo(transY1, transX1); right.lineTo(transYRight, transXRight); right.lineTo(transStackRight, transXRight); } right.closePath(); } } // Get series Paint and Stroke Paint itemPaint = getItemPaint(series, item); if (pass == 0) { g2.setPaint(itemPaint); g2.fill(left); g2.fill(right); } // add an entity for the item... if (entities != null) { // Create the entity area and limit it to the data area Area dataAreaHotspot = new Area(left); dataAreaHotspot.add(new Area(right)); dataAreaHotspot.intersect(new Area(dataArea)); if (!dataAreaHotspot.isEmpty()) { addEntity(entities, dataAreaHotspot, dataset, series, item, 0.0, 0.0); } } } /** * Calculates the stacked values (one positive and one negative) of all * series up to, but not including, {@code series} for the specified * item. It returns [0.0, 0.0] if {@code series} is the first series. * * @param dataset the dataset ({@code null} not permitted). * @param series the series index. * @param index the item index. * * @return An array containing the cumulative negative and positive values * for all series values up to but excluding {@code series} * for {@code index}. */ private double[] getStackValues(TableXYDataset dataset, int series, int index) { double[] result = new double[2]; for (int i = 0; i < series; i++) { double v = dataset.getYValue(i, index); if (!Double.isNaN(v)) { if (v >= 0.0) { result[1] += v; } else { result[0] += v; } } } return result; } /** * Returns a pair of "stack" values calculated as the mean of the two * specified stack value pairs. * * @param stack1 the first stack pair. * @param stack2 the second stack pair. * * @return A pair of average stack values. */ private double[] averageStackValues(double[] stack1, double[] stack2) { double[] result = new double[2]; result[0] = (stack1[0] + stack2[0]) / 2.0; result[1] = (stack1[1] + stack2[1]) / 2.0; return result; } /** * Calculates adjusted stack values from the supplied values. The value is * the mean of the supplied values, unless either of the supplied values * is zero, in which case the adjusted value is zero also. * * @param stack1 the first stack pair. * @param stack2 the second stack pair. * * @return A pair of average stack values. */ private double[] adjustedStackValues(double[] stack1, double[] stack2) { double[] result = new double[2]; if (stack1[0] == 0.0 || stack2[0] == 0.0) { result[0] = 0.0; } else { result[0] = (stack1[0] + stack2[0]) / 2.0; } if (stack1[1] == 0.0 || stack2[1] == 0.0) { result[1] = 0.0; } else { result[1] = (stack1[1] + stack2[1]) / 2.0; } return result; } /** * Tests this renderer for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof StackedXYAreaRenderer2)) { return false; } StackedXYAreaRenderer2 that = (StackedXYAreaRenderer2) obj; if (this.roundXCoordinates != that.roundXCoordinates) { return false; } return super.equals(obj); } /** * Returns a clone of the renderer. * * @return A clone. * * @throws CloneNotSupportedException if the renderer cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/StackedXYBarRenderer.java000066400000000000000000000347631463604235500321570ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * StackedXYBarRenderer.java * ------------------------- * (C) Copyright 2004-present, by Andreas Schroeder and Contributors. * * Original Author: Andreas Schroeder; * Contributor(s): David Gilbert; */ package org.jfree.chart.renderer.xy; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.labels.ItemLabelAnchor; import org.jfree.chart.labels.ItemLabelPosition; import org.jfree.chart.labels.XYItemLabelGenerator; import org.jfree.chart.plot.CrosshairState; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.TextAnchor; import org.jfree.data.Range; import org.jfree.data.general.DatasetUtils; import org.jfree.data.xy.IntervalXYDataset; import org.jfree.data.xy.TableXYDataset; import org.jfree.data.xy.XYDataset; /** * A bar renderer that displays the series items stacked. * The dataset used together with this renderer must be a * {@link org.jfree.data.xy.IntervalXYDataset} and a * {@link org.jfree.data.xy.TableXYDataset}. For example, the * dataset class {@link org.jfree.data.xy.CategoryTableXYDataset} * implements both interfaces. * * The example shown here is generated by the * {@code StackedXYBarChartDemo2.java} program included in the * JFreeChart demo collection: *

* StackedXYBarRendererSample.png */ public class StackedXYBarRenderer extends XYBarRenderer { /** For serialization. */ private static final long serialVersionUID = -7049101055533436444L; /** A flag that controls whether the bars display values or percentages. */ private boolean renderAsPercentages; /** * Creates a new renderer. */ public StackedXYBarRenderer() { this(0.0); } /** * Creates a new renderer. * * @param margin the percentual amount of the bars that are cut away. */ public StackedXYBarRenderer(double margin) { super(margin); this.renderAsPercentages = false; // set the default item label positions, which will only be used if // the user requests visible item labels... ItemLabelPosition p = new ItemLabelPosition(ItemLabelAnchor.CENTER, TextAnchor.CENTER); setDefaultPositiveItemLabelPosition(p); setDefaultNegativeItemLabelPosition(p); setPositiveItemLabelPositionFallback(null); setNegativeItemLabelPositionFallback(null); } /** * Returns {@code true} if the renderer displays each item value as * a percentage (so that the stacked bars add to 100%), and * {@code false} otherwise. * * @return A boolean. * * @see #setRenderAsPercentages(boolean) */ public boolean getRenderAsPercentages() { return this.renderAsPercentages; } /** * Sets the flag that controls whether the renderer displays each item * value as a percentage (so that the stacked bars add to 100%), and sends * a {@link RendererChangeEvent} to all registered listeners. * * @param asPercentages the flag. * * @see #getRenderAsPercentages() */ public void setRenderAsPercentages(boolean asPercentages) { this.renderAsPercentages = asPercentages; fireChangeEvent(); } /** * Returns {@code 3} to indicate that this renderer requires three * passes for drawing (shadows are drawn in the first pass, the bars in the * second, and item labels are drawn in the third pass so that * they always appear in front of all the bars). * * @return {@code 2}. */ @Override public int getPassCount() { return 3; } /** * Initialises the renderer and returns a state object that should be * passed to all subsequent calls to the drawItem() method. Here there is * nothing to do. * * @param g2 the graphics device. * @param dataArea the area inside the axes. * @param plot the plot. * @param data the data. * @param info an optional info collection object to return data back to * the caller. * * @return A state object. */ @Override public XYItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, XYPlot plot, XYDataset data, PlotRenderingInfo info) { return new XYBarRendererState(info); } /** * Returns the range of values the renderer requires to display all the * items from the specified dataset. * * @param dataset the dataset ({@code null} permitted). * * @return The range ({@code null} if the dataset is {@code null} * or empty). */ @Override public Range findRangeBounds(XYDataset dataset) { if (dataset != null) { if (this.renderAsPercentages) { return new Range(0.0, 1.0); } else { return DatasetUtils.findStackedRangeBounds( (TableXYDataset) dataset); } } else { return null; } } /** * Draws the visual representation of a single data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the area within which the plot is being drawn. * @param info collects information about the drawing. * @param plot the plot (can be used to obtain standard color information * etc). * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset. * @param series the series index (zero-based). * @param item the item index (zero-based). * @param crosshairState crosshair information for the plot * ({@code null} permitted). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item, CrosshairState crosshairState, int pass) { if (!getItemVisible(series, item)) { return; } if (!(dataset instanceof IntervalXYDataset && dataset instanceof TableXYDataset)) { String message = "dataset (type " + dataset.getClass().getName() + ") has wrong type:"; boolean and = false; if (!IntervalXYDataset.class.isAssignableFrom(dataset.getClass())) { message += " it is no IntervalXYDataset"; and = true; } if (!TableXYDataset.class.isAssignableFrom(dataset.getClass())) { if (and) { message += " and"; } message += " it is no TableXYDataset"; } throw new IllegalArgumentException(message); } IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset; double value = intervalDataset.getYValue(series, item); if (Double.isNaN(value)) { return; } // if we are rendering the values as percentages, we need to calculate // the total for the current item. Unfortunately here we end up // repeating the calculation more times than is strictly necessary - // hopefully I'll come back to this and find a way to add the // total(s) to the renderer state. The other problem is we implicitly // assume the dataset has no negative values...perhaps that can be // fixed too. double total = 0.0; if (this.renderAsPercentages) { total = DatasetUtils.calculateStackTotal( (TableXYDataset) dataset, item); value = value / total; } double positiveBase = 0.0; double negativeBase = 0.0; for (int i = 0; i < series; i++) { double v = dataset.getYValue(i, item); if (!Double.isNaN(v) && isSeriesVisible(i)) { if (this.renderAsPercentages) { v = v / total; } if (v > 0) { positiveBase = positiveBase + v; } else { negativeBase = negativeBase + v; } } } double translatedBase; double translatedValue; RectangleEdge edgeR = plot.getRangeAxisEdge(); if (value > 0.0) { translatedBase = rangeAxis.valueToJava2D(positiveBase, dataArea, edgeR); translatedValue = rangeAxis.valueToJava2D(positiveBase + value, dataArea, edgeR); } else { translatedBase = rangeAxis.valueToJava2D(negativeBase, dataArea, edgeR); translatedValue = rangeAxis.valueToJava2D(negativeBase + value, dataArea, edgeR); } RectangleEdge edgeD = plot.getDomainAxisEdge(); double startX = intervalDataset.getStartXValue(series, item); if (Double.isNaN(startX)) { return; } double translatedStartX = domainAxis.valueToJava2D(startX, dataArea, edgeD); double endX = intervalDataset.getEndXValue(series, item); if (Double.isNaN(endX)) { return; } double translatedEndX = domainAxis.valueToJava2D(endX, dataArea, edgeD); double translatedWidth = Math.max(1, Math.abs(translatedEndX - translatedStartX)); double translatedHeight = Math.abs(translatedValue - translatedBase); if (getMargin() > 0.0) { double cut = translatedWidth * getMargin(); translatedWidth = translatedWidth - cut; translatedStartX = translatedStartX + cut / 2; } Rectangle2D bar = null; PlotOrientation orientation = plot.getOrientation(); if (orientation == PlotOrientation.HORIZONTAL) { bar = new Rectangle2D.Double(Math.min(translatedBase, translatedValue), Math.min(translatedEndX, translatedStartX), translatedHeight, translatedWidth); } else if (orientation == PlotOrientation.VERTICAL) { bar = new Rectangle2D.Double(Math.min(translatedStartX, translatedEndX), Math.min(translatedBase, translatedValue), translatedWidth, translatedHeight); } else { throw new IllegalStateException(); } boolean positive = (value > 0.0); boolean inverted = rangeAxis.isInverted(); RectangleEdge barBase; if (orientation == PlotOrientation.HORIZONTAL) { if (positive && inverted || !positive && !inverted) { barBase = RectangleEdge.RIGHT; } else { barBase = RectangleEdge.LEFT; } } else { if (positive && !inverted || !positive && inverted) { barBase = RectangleEdge.BOTTOM; } else { barBase = RectangleEdge.TOP; } } if (pass == 0) { if (getShadowsVisible()) { getBarPainter().paintBarShadow(g2, this, series, item, bar, barBase, false); } } else if (pass == 1) { getBarPainter().paintBar(g2, this, series, item, bar, barBase); // add an entity for the item... if (info != null) { EntityCollection entities = info.getOwner() .getEntityCollection(); if (entities != null) { addEntity(entities, bar, dataset, series, item, bar.getCenterX(), bar.getCenterY()); } } } else if (pass == 2) { // handle item label drawing, now that we know all the bars have // been drawn... if (isItemLabelVisible(series, item)) { XYItemLabelGenerator generator = getItemLabelGenerator(series, item); drawItemLabel(g2, dataset, series, item, plot, generator, bar, value < 0.0); } } } /** * Tests this renderer for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof StackedXYBarRenderer)) { return false; } StackedXYBarRenderer that = (StackedXYBarRenderer) obj; if (this.renderAsPercentages != that.renderAsPercentages) { return false; } return super.equals(obj); } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = super.hashCode(); result = result * 37 + (this.renderAsPercentages ? 1 : 0); return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/StandardXYBarPainter.java000066400000000000000000000156441463604235500321720ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * StandardXYBarPainter.java * ------------------------- * (C) Copyright 2008-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import java.awt.Color; import java.awt.GradientPaint; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Stroke; import java.awt.geom.Rectangle2D; import java.awt.geom.RectangularShape; import java.io.Serializable; import org.jfree.chart.ui.GradientPaintTransformer; import org.jfree.chart.ui.RectangleEdge; /** * An implementation of the {@link XYBarPainter} interface that preserves the * behaviour of bar painting that existed prior to the introduction of the * {@link XYBarPainter} interface. * * @see GradientXYBarPainter */ public class StandardXYBarPainter implements XYBarPainter, Serializable { /** * Creates a new instance. */ public StandardXYBarPainter() { } /** * Paints a single bar instance. * * @param g2 the graphics target. * @param renderer the renderer. * @param row the row index. * @param column the column index. * @param bar the bar * @param base indicates which side of the rectangle is the base of the * bar. */ @Override public void paintBar(Graphics2D g2, XYBarRenderer renderer, int row, int column, RectangularShape bar, RectangleEdge base) { Paint itemPaint = renderer.getItemPaint(row, column); GradientPaintTransformer t = renderer.getGradientPaintTransformer(); if (t != null && itemPaint instanceof GradientPaint) { itemPaint = t.transform((GradientPaint) itemPaint, bar); } g2.setPaint(itemPaint); g2.fill(bar); // draw the outline... if (renderer.isDrawBarOutline()) { // && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) { Stroke stroke = renderer.getItemOutlineStroke(row, column); Paint paint = renderer.getItemOutlinePaint(row, column); if (stroke != null && paint != null) { g2.setStroke(stroke); g2.setPaint(paint); g2.draw(bar); } } } /** * Paints a single bar instance. * * @param g2 the graphics target. * @param renderer the renderer. * @param row the row index. * @param column the column index. * @param bar the bar * @param base indicates which side of the rectangle is the base of the * bar. * @param pegShadow peg the shadow to the base of the bar? */ @Override public void paintBarShadow(Graphics2D g2, XYBarRenderer renderer, int row, int column, RectangularShape bar, RectangleEdge base, boolean pegShadow) { // handle a special case - if the bar colour has alpha == 0, it is // invisible so we shouldn't draw any shadow Paint itemPaint = renderer.getItemPaint(row, column); if (itemPaint instanceof Color) { Color c = (Color) itemPaint; if (c.getAlpha() == 0) { return; } } RectangularShape shadow = createShadow(bar, renderer.getShadowXOffset(), renderer.getShadowYOffset(), base, pegShadow); g2.setPaint(Color.GRAY); g2.fill(shadow); } /** * Creates a shadow for the bar. * * @param bar the bar shape. * @param xOffset the x-offset for the shadow. * @param yOffset the y-offset for the shadow. * @param base the edge that is the base of the bar. * @param pegShadow peg the shadow to the base? * * @return A rectangle for the shadow. */ private Rectangle2D createShadow(RectangularShape bar, double xOffset, double yOffset, RectangleEdge base, boolean pegShadow) { double x0 = bar.getMinX(); double x1 = bar.getMaxX(); double y0 = bar.getMinY(); double y1 = bar.getMaxY(); if (base == RectangleEdge.TOP) { x0 += xOffset; x1 += xOffset; if (!pegShadow) { y0 += yOffset; } y1 += yOffset; } else if (base == RectangleEdge.BOTTOM) { x0 += xOffset; x1 += xOffset; y0 += yOffset; if (!pegShadow) { y1 += yOffset; } } else if (base == RectangleEdge.LEFT) { if (!pegShadow) { x0 += xOffset; } x1 += xOffset; y0 += yOffset; y1 += yOffset; } else if (base == RectangleEdge.RIGHT) { x0 += xOffset; if (!pegShadow) { x1 += xOffset; } y0 += yOffset; y1 += yOffset; } return new Rectangle2D.Double(x0, y0, (x1 - x0), (y1 - y0)); } /** * Tests this instance for equality with an arbitrary object. * * @param obj the obj ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof StandardXYBarPainter)) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int hash = 37; // no fields to compute... return hash; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/StandardXYItemRenderer.java000066400000000000000000001030061463604235500325160ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * StandardXYItemRenderer.java * --------------------------- * (C) Copyright 2001-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Mark Watson (www.markwatson.com); * Jonathan Nash; * Andreas Schneider; * Norbert Kiesel (for TBD Networks); * Christian W. Zuckschwerdt; * Bill Kelemen; * Nicolas Brodu (for Astrium and EADS Corporate Research * Center); * */ package org.jfree.chart.renderer.xy; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Paint; import java.awt.Point; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.GeneralPath; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import org.jfree.chart.LegendItem; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.labels.XYToolTipGenerator; import org.jfree.chart.plot.CrosshairState; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.urls.XYURLGenerator; import org.jfree.chart.util.BooleanList; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; import org.jfree.chart.util.ShapeUtils; import org.jfree.chart.util.UnitType; import org.jfree.data.xy.XYDataset; /** * Standard item renderer for an {@link XYPlot}. This class can draw (a) * shapes at each point, or (b) lines between points, or (c) both shapes and * lines. *

* This renderer has been retained for historical reasons and, in general, you * should use the {@link XYLineAndShapeRenderer} class instead. */ public class StandardXYItemRenderer extends AbstractXYItemRenderer implements XYItemRenderer, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -3271351259436865995L; /** Constant for the type of rendering (shapes only). */ public static final int SHAPES = 1; /** Constant for the type of rendering (lines only). */ public static final int LINES = 2; /** Constant for the type of rendering (shapes and lines). */ public static final int SHAPES_AND_LINES = SHAPES | LINES; /** Constant for the type of rendering (images only). */ public static final int IMAGES = 4; /** Constant for the type of rendering (discontinuous lines). */ public static final int DISCONTINUOUS = 8; /** Constant for the type of rendering (discontinuous lines). */ public static final int DISCONTINUOUS_LINES = LINES | DISCONTINUOUS; /** A flag indicating whether or not shapes are drawn at each XY point. */ private boolean baseShapesVisible; /** A flag indicating whether or not lines are drawn between XY points. */ private boolean plotLines; /** A flag indicating whether or not images are drawn between XY points. */ private boolean plotImages; /** A flag controlling whether or not discontinuous lines are used. */ private boolean plotDiscontinuous; /** Specifies how the gap threshold value is interpreted. */ private UnitType gapThresholdType = UnitType.RELATIVE; /** Threshold for deciding when to discontinue a line. */ private double gapThreshold = 1.0; /** * A table of flags that control (per series) whether or not shapes are * filled. */ private BooleanList seriesShapesFilled; /** The default value returned by the getShapeFilled() method. */ private boolean baseShapesFilled; /** * A flag that controls whether or not each series is drawn as a single * path. */ private boolean drawSeriesLineAsPath; /** * The shape that is used to represent a line in the legend. * This should never be set to {@code null}. */ private transient Shape legendLine; /** * Constructs a new renderer. */ public StandardXYItemRenderer() { this(LINES, null); } /** * Constructs a new renderer. To specify the type of renderer, use one of * the constants: {@link #SHAPES}, {@link #LINES} or * {@link #SHAPES_AND_LINES}. * * @param type the type. */ public StandardXYItemRenderer(int type) { this(type, null); } /** * Constructs a new renderer. To specify the type of renderer, use one of * the constants: {@link #SHAPES}, {@link #LINES} or * {@link #SHAPES_AND_LINES}. * * @param type the type of renderer. * @param toolTipGenerator the item label generator ({@code null} * permitted). */ public StandardXYItemRenderer(int type, XYToolTipGenerator toolTipGenerator) { this(type, toolTipGenerator, null); } /** * Constructs a new renderer. To specify the type of renderer, use one of * the constants: {@link #SHAPES}, {@link #LINES} or * {@link #SHAPES_AND_LINES}. * * @param type the type of renderer. * @param toolTipGenerator the item label generator ({@code null} * permitted). * @param urlGenerator the URL generator. */ public StandardXYItemRenderer(int type, XYToolTipGenerator toolTipGenerator, XYURLGenerator urlGenerator) { super(); setDefaultToolTipGenerator(toolTipGenerator); setURLGenerator(urlGenerator); if ((type & SHAPES) != 0) { this.baseShapesVisible = true; } if ((type & LINES) != 0) { this.plotLines = true; } if ((type & IMAGES) != 0) { this.plotImages = true; } if ((type & DISCONTINUOUS) != 0) { this.plotDiscontinuous = true; } this.seriesShapesFilled = new BooleanList(); this.baseShapesFilled = true; this.legendLine = new Line2D.Double(-7.0, 0.0, 7.0, 0.0); this.drawSeriesLineAsPath = false; } /** * Returns true if shapes are being plotted by the renderer. * * @return {@code true} if shapes are being plotted by the renderer. * * @see #setBaseShapesVisible */ public boolean getBaseShapesVisible() { return this.baseShapesVisible; } /** * Sets the flag that controls whether or not a shape is plotted at each * data point. * * @param flag the flag. * * @see #getBaseShapesVisible */ public void setBaseShapesVisible(boolean flag) { if (this.baseShapesVisible != flag) { this.baseShapesVisible = flag; fireChangeEvent(); } } // SHAPES FILLED /** * Returns the flag used to control whether or not the shape for an item is * filled. *

* The default implementation passes control to the * {@code getSeriesShapesFilled()} method. You can override this method * if you require different behaviour. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return A boolean. * * @see #getSeriesShapesFilled(int) */ public boolean getItemShapeFilled(int series, int item) { // otherwise look up the paint table Boolean flag = this.seriesShapesFilled.getBoolean(series); if (flag != null) { return flag; } else { return this.baseShapesFilled; } } /** * Returns the flag used to control whether or not the shapes for a series * are filled. * * @param series the series index (zero-based). * * @return A boolean. */ public Boolean getSeriesShapesFilled(int series) { return this.seriesShapesFilled.getBoolean(series); } /** * Sets the 'shapes filled' flag for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param flag the flag. * * @see #getSeriesShapesFilled(int) */ public void setSeriesShapesFilled(int series, Boolean flag) { this.seriesShapesFilled.setBoolean(series, flag); fireChangeEvent(); } /** * Returns the base 'shape filled' attribute. * * @return The base flag. * * @see #setBaseShapesFilled(boolean) */ public boolean getBaseShapesFilled() { return this.baseShapesFilled; } /** * Sets the base 'shapes filled' flag and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param flag the flag. * * @see #getBaseShapesFilled() */ public void setBaseShapesFilled(boolean flag) { this.baseShapesFilled = flag; } /** * Returns true if lines are being plotted by the renderer. * * @return {@code true} if lines are being plotted by the renderer. * * @see #setPlotLines(boolean) */ public boolean getPlotLines() { return this.plotLines; } /** * Sets the flag that controls whether or not a line is plotted between * each data point and sends a {@link RendererChangeEvent} to all * registered listeners. * * @param flag the flag. * * @see #getPlotLines() */ public void setPlotLines(boolean flag) { if (this.plotLines != flag) { this.plotLines = flag; fireChangeEvent(); } } /** * Returns the gap threshold type (relative or absolute). * * @return The type. * * @see #setGapThresholdType(UnitType) */ public UnitType getGapThresholdType() { return this.gapThresholdType; } /** * Sets the gap threshold type and sends a {@link RendererChangeEvent} to * all registered listeners. * * @param thresholdType the type ({@code null} not permitted). * * @see #getGapThresholdType() */ public void setGapThresholdType(UnitType thresholdType) { Args.nullNotPermitted(thresholdType, "thresholdType"); this.gapThresholdType = thresholdType; fireChangeEvent(); } /** * Returns the gap threshold for discontinuous lines. * * @return The gap threshold. * * @see #setGapThreshold(double) */ public double getGapThreshold() { return this.gapThreshold; } /** * Sets the gap threshold for discontinuous lines and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param t the threshold. * * @see #getGapThreshold() */ public void setGapThreshold(double t) { this.gapThreshold = t; fireChangeEvent(); } /** * Returns true if images are being plotted by the renderer. * * @return {@code true} if images are being plotted by the renderer. * * @see #setPlotImages(boolean) */ public boolean getPlotImages() { return this.plotImages; } /** * Sets the flag that controls whether or not an image is drawn at each * data point and sends a {@link RendererChangeEvent} to all registered * listeners. * * @param flag the flag. * * @see #getPlotImages() */ public void setPlotImages(boolean flag) { if (this.plotImages != flag) { this.plotImages = flag; fireChangeEvent(); } } /** * Returns a flag that controls whether or not the renderer shows * discontinuous lines. * * @return {@code true} if lines should be discontinuous. */ public boolean getPlotDiscontinuous() { return this.plotDiscontinuous; } /** * Sets the flag that controls whether or not the renderer shows * discontinuous lines, and sends a {@link RendererChangeEvent} to all * registered listeners. * * @param flag the new flag value. */ public void setPlotDiscontinuous(boolean flag) { if (this.plotDiscontinuous != flag) { this.plotDiscontinuous = flag; fireChangeEvent(); } } /** * Returns a flag that controls whether or not each series is drawn as a * single path. * * @return A boolean. * * @see #setDrawSeriesLineAsPath(boolean) */ public boolean getDrawSeriesLineAsPath() { return this.drawSeriesLineAsPath; } /** * Sets the flag that controls whether or not each series is drawn as a * single path. * * @param flag the flag. * * @see #getDrawSeriesLineAsPath() */ public void setDrawSeriesLineAsPath(boolean flag) { this.drawSeriesLineAsPath = flag; } /** * Returns the shape used to represent a line in the legend. * * @return The legend line (never {@code null}). * * @see #setLegendLine(Shape) */ public Shape getLegendLine() { return this.legendLine; } /** * Sets the shape used as a line in each legend item and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param line the line ({@code null} not permitted). * * @see #getLegendLine() */ public void setLegendLine(Shape line) { Args.nullNotPermitted(line, "line"); this.legendLine = line; fireChangeEvent(); } /** * Returns a legend item for a series. * * @param datasetIndex the dataset index (zero-based). * @param series the series index (zero-based). * * @return A legend item for the series. */ @Override public LegendItem getLegendItem(int datasetIndex, int series) { XYPlot plot = getPlot(); if (plot == null) { return null; } LegendItem result = null; XYDataset dataset = plot.getDataset(datasetIndex); if (dataset != null) { if (getItemVisible(series, 0)) { String label = getLegendItemLabelGenerator().generateLabel( dataset, series); String description = label; String toolTipText = null; if (getLegendItemToolTipGenerator() != null) { toolTipText = getLegendItemToolTipGenerator().generateLabel( dataset, series); } String urlText = null; if (getLegendItemURLGenerator() != null) { urlText = getLegendItemURLGenerator().generateLabel( dataset, series); } Shape shape = lookupLegendShape(series); boolean shapeFilled = getItemShapeFilled(series, 0); Paint paint = lookupSeriesPaint(series); Paint linePaint = paint; Stroke lineStroke = lookupSeriesStroke(series); result = new LegendItem(label, description, toolTipText, urlText, this.baseShapesVisible, shape, shapeFilled, paint, !shapeFilled, paint, lineStroke, this.plotLines, this.legendLine, lineStroke, linePaint); result.setLabelFont(lookupLegendTextFont(series)); Paint labelPaint = lookupLegendTextPaint(series); if (labelPaint != null) { result.setLabelPaint(labelPaint); } result.setDataset(dataset); result.setDatasetIndex(datasetIndex); result.setSeriesKey(dataset.getSeriesKey(series)); result.setSeriesIndex(series); } } return result; } /** * Records the state for the renderer. This is used to preserve state * information between calls to the drawItem() method for a single chart * drawing. */ public static class State extends XYItemRendererState { /** The path for the current series. */ public GeneralPath seriesPath; /** The series index. */ private int seriesIndex; /** * A flag that indicates if the last (x, y) point was 'good' * (non-null). */ private boolean lastPointGood; /** * Creates a new state instance. * * @param info the plot rendering info. */ public State(PlotRenderingInfo info) { super(info); } /** * Returns a flag that indicates if the last point drawn (in the * current series) was 'good' (non-null). * * @return A boolean. */ public boolean isLastPointGood() { return this.lastPointGood; } /** * Sets a flag that indicates if the last point drawn (in the current * series) was 'good' (non-null). * * @param good the flag. */ public void setLastPointGood(boolean good) { this.lastPointGood = good; } /** * Returns the series index for the current path. * * @return The series index for the current path. */ public int getSeriesIndex() { return this.seriesIndex; } /** * Sets the series index for the current path. * * @param index the index. */ public void setSeriesIndex(int index) { this.seriesIndex = index; } } /** * Initialises the renderer. *

* This method will be called before the first item is rendered, giving the * renderer an opportunity to initialise any state information it wants to * maintain. The renderer can do nothing if it chooses. * * @param g2 the graphics device. * @param dataArea the area inside the axes. * @param plot the plot. * @param data the data. * @param info an optional info collection object to return data back to * the caller. * * @return The renderer state. */ @Override public XYItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, XYPlot plot, XYDataset data, PlotRenderingInfo info) { State state = new State(info); state.seriesPath = new GeneralPath(); state.seriesIndex = -1; return state; } /** * Draws the visual representation of a single data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the area within which the data is being drawn. * @param info collects information about the drawing. * @param plot the plot (can be used to obtain standard color information * etc). * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset. * @param series the series index (zero-based). * @param item the item index (zero-based). * @param crosshairState crosshair information for the plot * ({@code null} permitted). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item, CrosshairState crosshairState, int pass) { boolean itemVisible = getItemVisible(series, item); // setup for collecting optional entity info... Shape entityArea = null; EntityCollection entities = null; if (info != null) { entities = info.getOwner().getEntityCollection(); } PlotOrientation orientation = plot.getOrientation(); Paint paint = getItemPaint(series, item); Stroke seriesStroke = getItemStroke(series, item); g2.setPaint(paint); g2.setStroke(seriesStroke); // get the data point... double x1 = dataset.getXValue(series, item); double y1 = dataset.getYValue(series, item); if (Double.isNaN(x1) || Double.isNaN(y1)) { itemVisible = false; } RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation); double transY1 = rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation); if (getPlotLines()) { if (this.drawSeriesLineAsPath) { State s = (State) state; if (s.getSeriesIndex() != series) { // we are starting a new series path s.seriesPath.reset(); s.lastPointGood = false; s.setSeriesIndex(series); } // update path to reflect latest point if (itemVisible && !Double.isNaN(transX1) && !Double.isNaN(transY1)) { float x = (float) transX1; float y = (float) transY1; if (orientation == PlotOrientation.HORIZONTAL) { x = (float) transY1; y = (float) transX1; } if (s.isLastPointGood()) { // TODO: check threshold s.seriesPath.lineTo(x, y); } else { s.seriesPath.moveTo(x, y); } s.setLastPointGood(true); } else { s.setLastPointGood(false); } if (item == dataset.getItemCount(series) - 1) { if (s.seriesIndex == series) { // draw path g2.setStroke(lookupSeriesStroke(series)); g2.setPaint(lookupSeriesPaint(series)); g2.draw(s.seriesPath); } } } else if (item != 0 && itemVisible) { // get the previous data point... double x0 = dataset.getXValue(series, item - 1); double y0 = dataset.getYValue(series, item - 1); if (!Double.isNaN(x0) && !Double.isNaN(y0)) { boolean drawLine = true; if (getPlotDiscontinuous()) { // only draw a line if the gap between the current and // previous data point is within the threshold int numX = dataset.getItemCount(series); double minX = dataset.getXValue(series, 0); double maxX = dataset.getXValue(series, numX - 1); if (this.gapThresholdType == UnitType.ABSOLUTE) { drawLine = Math.abs(x1 - x0) <= this.gapThreshold; } else { drawLine = Math.abs(x1 - x0) <= ((maxX - minX) / numX * getGapThreshold()); } } if (drawLine) { double transX0 = domainAxis.valueToJava2D(x0, dataArea, xAxisLocation); double transY0 = rangeAxis.valueToJava2D(y0, dataArea, yAxisLocation); // only draw if we have good values if (Double.isNaN(transX0) || Double.isNaN(transY0) || Double.isNaN(transX1) || Double.isNaN(transY1)) { return; } if (orientation == PlotOrientation.HORIZONTAL) { state.workingLine.setLine(transY0, transX0, transY1, transX1); } else if (orientation == PlotOrientation.VERTICAL) { state.workingLine.setLine(transX0, transY0, transX1, transY1); } if (state.workingLine.intersects(dataArea)) { g2.draw(state.workingLine); } } } } } // we needed to get this far even for invisible items, to ensure that // seriesPath updates happened, but now there is nothing more we need // to do for non-visible items... if (!itemVisible) { return; } if (getBaseShapesVisible()) { Shape shape = getItemShape(series, item); if (orientation == PlotOrientation.HORIZONTAL) { shape = ShapeUtils.createTranslatedShape(shape, transY1, transX1); } else if (orientation == PlotOrientation.VERTICAL) { shape = ShapeUtils.createTranslatedShape(shape, transX1, transY1); } if (shape.intersects(dataArea)) { if (getItemShapeFilled(series, item)) { g2.fill(shape); } else { g2.draw(shape); } } entityArea = shape; } if (getPlotImages()) { Image image = getImage(plot, series, item, transX1, transY1); if (image != null) { Point hotspot = getImageHotspot(plot, series, item, transX1, transY1, image); g2.drawImage(image, (int) (transX1 - hotspot.getX()), (int) (transY1 - hotspot.getY()), null); entityArea = new Rectangle2D.Double(transX1 - hotspot.getX(), transY1 - hotspot.getY(), image.getWidth(null), image.getHeight(null)); } } double xx = transX1; double yy = transY1; if (orientation == PlotOrientation.HORIZONTAL) { xx = transY1; yy = transX1; } // draw the item label if there is one... if (isItemLabelVisible(series, item)) { drawItemLabel(g2, orientation, dataset, series, item, xx, yy, (y1 < 0.0)); } int datasetIndex = plot.indexOf(dataset); updateCrosshairValues(crosshairState, x1, y1, datasetIndex, transX1, transY1, orientation); // add an entity for the item... if (entities != null && ShapeUtils.isPointInRect(dataArea, xx, yy)) { addEntity(entities, entityArea, dataset, series, item, xx, yy); } } /** * Tests this renderer for equality with another object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof StandardXYItemRenderer)) { return false; } StandardXYItemRenderer that = (StandardXYItemRenderer) obj; if (this.baseShapesVisible != that.baseShapesVisible) { return false; } if (this.plotLines != that.plotLines) { return false; } if (this.plotImages != that.plotImages) { return false; } if (this.plotDiscontinuous != that.plotDiscontinuous) { return false; } if (this.gapThresholdType != that.gapThresholdType) { return false; } if (this.gapThreshold != that.gapThreshold) { return false; } if (!this.seriesShapesFilled.equals(that.seriesShapesFilled)) { return false; } if (this.baseShapesFilled != that.baseShapesFilled) { return false; } if (this.drawSeriesLineAsPath != that.drawSeriesLineAsPath) { return false; } if (!ShapeUtils.equal(this.legendLine, that.legendLine)) { return false; } return super.equals(obj); } /** * Returns a clone of the renderer. * * @return A clone. * * @throws CloneNotSupportedException if the renderer cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { StandardXYItemRenderer clone = (StandardXYItemRenderer) super.clone(); clone.seriesShapesFilled = (BooleanList) this.seriesShapesFilled.clone(); clone.legendLine = ShapeUtils.clone(this.legendLine); return clone; } //////////////////////////////////////////////////////////////////////////// // PROTECTED METHODS // These provide the opportunity to subclass the standard renderer and // create custom effects. //////////////////////////////////////////////////////////////////////////// /** * Returns the image used to draw a single data item. * * @param plot the plot (can be used to obtain standard color information * etc). * @param series the series index. * @param item the item index. * @param x the x value of the item. * @param y the y value of the item. * * @return The image. * * @see #getPlotImages() */ protected Image getImage(Plot plot, int series, int item, double x, double y) { // this method must be overridden if you want to display images return null; } /** * Returns the hotspot of the image used to draw a single data item. * The hotspot is the point relative to the top left of the image * that should indicate the data item. The default is the center of the * image. * * @param plot the plot (can be used to obtain standard color information * etc). * @param image the image (can be used to get size information about the * image) * @param series the series index * @param item the item index * @param x the x value of the item * @param y the y value of the item * * @return The hotspot used to draw the data item. */ protected Point getImageHotspot(Plot plot, int series, int item, double x, double y, Image image) { int height = image.getHeight(null); int width = image.getWidth(null); return new Point(width / 2, height / 2); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.legendLine = SerialUtils.readShape(stream); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeShape(this.legendLine, stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/VectorRenderer.java000066400000000000000000000300761463604235500311260ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * VectorRenderer.java * ------------------- * (C) Copyright 2007-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import java.awt.Graphics2D; import java.awt.geom.GeneralPath; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.plot.CrosshairState; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.Range; import org.jfree.data.xy.VectorXYDataset; import org.jfree.data.xy.XYDataset; /** * A renderer that represents data from an {@link VectorXYDataset} by drawing a * line with an arrow at each (x, y) point. * The example shown here is generated by the {@code VectorPlotDemo1.java} * program included in the JFreeChart demo collection: *

* VectorRendererSample.png */ public class VectorRenderer extends AbstractXYItemRenderer implements XYItemRenderer, Cloneable, PublicCloneable, Serializable { /** The length of the base. */ private double baseLength = 0.10; /** The length of the head. */ private double headLength = 0.14; /** * Creates a new {@code VectorRenderer} instance with default * attributes. */ public VectorRenderer() { } /** * Returns the lower and upper bounds (range) of the x-values in the * specified dataset. * * @param dataset the dataset ({@code null} permitted). * * @return The range ({@code null} if the dataset is {@code null} * or empty). */ @Override public Range findDomainBounds(XYDataset dataset) { Args.nullNotPermitted(dataset, "dataset"); double minimum = Double.POSITIVE_INFINITY; double maximum = Double.NEGATIVE_INFINITY; int seriesCount = dataset.getSeriesCount(); double lvalue; double uvalue; if (dataset instanceof VectorXYDataset) { VectorXYDataset vdataset = (VectorXYDataset) dataset; for (int series = 0; series < seriesCount; series++) { int itemCount = dataset.getItemCount(series); for (int item = 0; item < itemCount; item++) { double delta = vdataset.getVectorXValue(series, item); if (delta < 0.0) { uvalue = vdataset.getXValue(series, item); lvalue = uvalue + delta; } else { lvalue = vdataset.getXValue(series, item); uvalue = lvalue + delta; } minimum = Math.min(minimum, lvalue); maximum = Math.max(maximum, uvalue); } } } else { for (int series = 0; series < seriesCount; series++) { int itemCount = dataset.getItemCount(series); for (int item = 0; item < itemCount; item++) { lvalue = dataset.getXValue(series, item); uvalue = lvalue; minimum = Math.min(minimum, lvalue); maximum = Math.max(maximum, uvalue); } } } if (minimum > maximum) { return null; } else { return new Range(minimum, maximum); } } /** * Returns the range of values the renderer requires to display all the * items from the specified dataset. * * @param dataset the dataset ({@code null} permitted). * * @return The range ({@code null} if the dataset is {@code null} * or empty). */ @Override public Range findRangeBounds(XYDataset dataset) { Args.nullNotPermitted(dataset, "dataset"); double minimum = Double.POSITIVE_INFINITY; double maximum = Double.NEGATIVE_INFINITY; int seriesCount = dataset.getSeriesCount(); double lvalue; double uvalue; if (dataset instanceof VectorXYDataset) { VectorXYDataset vdataset = (VectorXYDataset) dataset; for (int series = 0; series < seriesCount; series++) { int itemCount = dataset.getItemCount(series); for (int item = 0; item < itemCount; item++) { double delta = vdataset.getVectorYValue(series, item); if (delta < 0.0) { uvalue = vdataset.getYValue(series, item); lvalue = uvalue + delta; } else { lvalue = vdataset.getYValue(series, item); uvalue = lvalue + delta; } minimum = Math.min(minimum, lvalue); maximum = Math.max(maximum, uvalue); } } } else { for (int series = 0; series < seriesCount; series++) { int itemCount = dataset.getItemCount(series); for (int item = 0; item < itemCount; item++) { lvalue = dataset.getYValue(series, item); uvalue = lvalue; minimum = Math.min(minimum, lvalue); maximum = Math.max(maximum, uvalue); } } } if (minimum > maximum) { return null; } else { return new Range(minimum, maximum); } } /** * Draws the block representing the specified item. * * @param g2 the graphics device. * @param state the state. * @param dataArea the data area. * @param info the plot rendering info. * @param plot the plot. * @param domainAxis the x-axis. * @param rangeAxis the y-axis. * @param dataset the dataset. * @param series the series index. * @param item the item index. * @param crosshairState the crosshair state. * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item, CrosshairState crosshairState, int pass) { double x = dataset.getXValue(series, item); double y = dataset.getYValue(series, item); double dx = 0.0; double dy = 0.0; if (dataset instanceof VectorXYDataset) { dx = ((VectorXYDataset) dataset).getVectorXValue(series, item); dy = ((VectorXYDataset) dataset).getVectorYValue(series, item); } double xx0 = domainAxis.valueToJava2D(x, dataArea, plot.getDomainAxisEdge()); double yy0 = rangeAxis.valueToJava2D(y, dataArea, plot.getRangeAxisEdge()); double xx1 = domainAxis.valueToJava2D(x + dx, dataArea, plot.getDomainAxisEdge()); double yy1 = rangeAxis.valueToJava2D(y + dy, dataArea, plot.getRangeAxisEdge()); Line2D line; PlotOrientation orientation = plot.getOrientation(); if (orientation.equals(PlotOrientation.HORIZONTAL)) { line = new Line2D.Double(yy0, xx0, yy1, xx1); } else { line = new Line2D.Double(xx0, yy0, xx1, yy1); } g2.setPaint(getItemPaint(series, item)); g2.setStroke(getItemStroke(series, item)); g2.draw(line); // calculate the arrow head and draw it... double dxx = (xx1 - xx0); double dyy = (yy1 - yy0); double bx = xx0 + (1.0 - this.baseLength) * dxx; double by = yy0 + (1.0 - this.baseLength) * dyy; double cx = xx0 + (1.0 - this.headLength) * dxx; double cy = yy0 + (1.0 - this.headLength) * dyy; double angle = 0.0; if (dxx != 0.0) { angle = Math.PI / 2.0 - Math.atan(dyy / dxx); } double deltaX = 2.0 * Math.cos(angle); double deltaY = 2.0 * Math.sin(angle); double leftx = cx + deltaX; double lefty = cy - deltaY; double rightx = cx - deltaX; double righty = cy + deltaY; GeneralPath p = new GeneralPath(); if (orientation == PlotOrientation.VERTICAL) { p.moveTo((float) xx1, (float) yy1); p.lineTo((float) rightx, (float) righty); p.lineTo((float) bx, (float) by); p.lineTo((float) leftx, (float) lefty); } else { // orientation is HORIZONTAL p.moveTo((float) yy1, (float) xx1); p.lineTo((float) righty, (float) rightx); p.lineTo((float) by, (float) bx); p.lineTo((float) lefty, (float) leftx); } p.closePath(); g2.draw(p); // setup for collecting optional entity info... EntityCollection entities; if (info != null) { entities = info.getOwner().getEntityCollection(); if (entities != null) { addEntity(entities, line.getBounds(), dataset, series, item, 0.0, 0.0); } } } /** * Tests this {@code VectorRenderer} for equality with an arbitrary * object. This method returns {@code true} if and only if: *

    *
  • {@code obj} is an instance of {@code VectorRenderer} (not * {@code null});
  • *
  • {@code obj} has the same field values as this * {@code VectorRenderer};
  • *
* * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof VectorRenderer)) { return false; } VectorRenderer that = (VectorRenderer) obj; if (this.baseLength != that.baseLength) { return false; } if (this.headLength != that.headLength) { return false; } return super.equals(obj); } /** * Returns a clone of this renderer. * * @return A clone of this renderer. * * @throws CloneNotSupportedException if there is a problem creating the * clone. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/WindItemRenderer.java000066400000000000000000000154671463604235500314130ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * WindItemRenderer.java * --------------------- * (C) Copyright 2001-present, by Achilleus Mantzios and Contributors. * * Original Author: Achilleus Mantzios; * Contributor(s): David Gilbert; * */ package org.jfree.chart.renderer.xy; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Stroke; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.plot.CrosshairState; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.xy.WindDataset; import org.jfree.data.xy.XYDataset; /** * A specialised renderer for displaying wind intensity/direction data. * The example shown here is generated by the {@code WindChartDemo1.java} * program included in the JFreeChart demo collection: *

* WindItemRendererSample.png */ public class WindItemRenderer extends AbstractXYItemRenderer implements XYItemRenderer, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 8078914101916976844L; /** * Creates a new renderer. */ public WindItemRenderer() { super(); } /** * Draws the visual representation of a single data item. * * @param g2 the graphics device. * @param state the renderer state. * @param plotArea the area within which the plot is being drawn. * @param info optional information collection. * @param plot the plot (can be used to obtain standard color * information etc). * @param domainAxis the horizontal axis. * @param rangeAxis the vertical axis. * @param dataset the dataset. * @param series the series index (zero-based). * @param item the item index (zero-based). * @param crosshairState crosshair information for the plot * ({@code null} permitted). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D plotArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item, CrosshairState crosshairState, int pass) { WindDataset windData = (WindDataset) dataset; Paint seriesPaint = getItemPaint(series, item); Stroke seriesStroke = getItemStroke(series, item); g2.setPaint(seriesPaint); g2.setStroke(seriesStroke); // get the data point... Number x = windData.getX(series, item); Number windDir = windData.getWindDirection(series, item); Number wforce = windData.getWindForce(series, item); double windForce = wforce.doubleValue(); double wdirt = Math.toRadians(windDir.doubleValue() * (-30.0) - 90.0); double ax1, ax2, ay1, ay2, rax2, ray2; RectangleEdge domainAxisLocation = plot.getDomainAxisEdge(); RectangleEdge rangeAxisLocation = plot.getRangeAxisEdge(); ax1 = domainAxis.valueToJava2D(x.doubleValue(), plotArea, domainAxisLocation); ay1 = rangeAxis.valueToJava2D(0.0, plotArea, rangeAxisLocation); rax2 = x.doubleValue() + (windForce * Math.cos(wdirt) * 8000000.0); ray2 = windForce * Math.sin(wdirt); ax2 = domainAxis.valueToJava2D(rax2, plotArea, domainAxisLocation); ay2 = rangeAxis.valueToJava2D(ray2, plotArea, rangeAxisLocation); int diri = windDir.intValue(); int forcei = wforce.intValue(); String dirforce = diri + "-" + forcei; Line2D line = new Line2D.Double(ax1, ay1, ax2, ay2); g2.draw(line); g2.setPaint(Color.BLUE); g2.setFont(new Font("Dialog", 1, 9)); g2.drawString(dirforce, (float) ax1, (float) ay1); g2.setPaint(seriesPaint); g2.setStroke(seriesStroke); double alx2, aly2, arx2, ary2; double ralx2, raly2, rarx2, rary2; double aldir = Math.toRadians(windDir.doubleValue() * (-30.0) - 90.0 - 5.0); ralx2 = wforce.doubleValue() * Math.cos(aldir) * 8000000 * 0.8 + x.doubleValue(); raly2 = wforce.doubleValue() * Math.sin(aldir) * 0.8; alx2 = domainAxis.valueToJava2D(ralx2, plotArea, domainAxisLocation); aly2 = rangeAxis.valueToJava2D(raly2, plotArea, rangeAxisLocation); line = new Line2D.Double(alx2, aly2, ax2, ay2); g2.draw(line); double ardir = Math.toRadians(windDir.doubleValue() * (-30.0) - 90.0 + 5.0); rarx2 = wforce.doubleValue() * Math.cos(ardir) * 8000000 * 0.8 + x.doubleValue(); rary2 = wforce.doubleValue() * Math.sin(ardir) * 0.8; arx2 = domainAxis.valueToJava2D(rarx2, plotArea, domainAxisLocation); ary2 = rangeAxis.valueToJava2D(rary2, plotArea, rangeAxisLocation); line = new Line2D.Double(arx2, ary2, ax2, ay2); g2.draw(line); } /** * Returns a clone of the renderer. * * @return A clone. * * @throws CloneNotSupportedException if the renderer cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/XYAreaRenderer.java000066400000000000000000000616421463604235500310200ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * XYAreaRenderer.java * ------------------- * (C) Copyright 2002-present, by Hari and Contributors. * * Original Author: Hari (ourhari@hotmail.com); * Contributor(s): David Gilbert; * Richard Atkinson; * Christian W. Zuckschwerdt; * Martin Krauskopf; * Ulrich Voigt (patch #312); */ package org.jfree.chart.renderer.xy; import java.awt.BasicStroke; import java.awt.GradientPaint; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Area; import java.awt.geom.GeneralPath; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import org.jfree.chart.HashUtils; import org.jfree.chart.LegendItem; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.labels.XYSeriesLabelGenerator; import org.jfree.chart.labels.XYToolTipGenerator; import org.jfree.chart.plot.CrosshairState; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.GradientPaintTransformer; import org.jfree.chart.ui.StandardGradientPaintTransformer; import org.jfree.chart.urls.XYURLGenerator; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; import org.jfree.chart.util.ShapeUtils; import org.jfree.data.xy.XYDataset; /** * Area item renderer for an {@link XYPlot}. This class can draw (a) shapes at * each point, or (b) lines between points, or (c) both shapes and lines, * or (d) filled areas, or (e) filled areas and shapes. The example shown here * is generated by the {@code XYAreaRendererDemo1.java} program included * in the JFreeChart demo collection: *

* XYAreaRendererSample.png */ public class XYAreaRenderer extends AbstractXYItemRenderer implements XYItemRenderer, PublicCloneable { /** For serialization. */ private static final long serialVersionUID = -4481971353973876747L; /** * A state object used by this renderer. */ static class XYAreaRendererState extends XYItemRendererState { /** Working storage for the area under one series. */ public GeneralPath area; /** Working line that can be recycled. */ public Line2D line; /** * Creates a new state. * * @param info the plot rendering info. */ public XYAreaRendererState(PlotRenderingInfo info) { super(info); this.area = new GeneralPath(); this.line = new Line2D.Double(); } } /** Useful constant for specifying the type of rendering (shapes only). */ public static final int SHAPES = 1; /** Useful constant for specifying the type of rendering (lines only). */ public static final int LINES = 2; /** * Useful constant for specifying the type of rendering (shapes and lines). */ public static final int SHAPES_AND_LINES = 3; /** Useful constant for specifying the type of rendering (area only). */ public static final int AREA = 4; /** * Useful constant for specifying the type of rendering (area and shapes). */ public static final int AREA_AND_SHAPES = 5; /** A flag indicating whether or not shapes are drawn at each XY point. */ private boolean plotShapes; /** A flag indicating whether or not lines are drawn between XY points. */ private boolean plotLines; /** A flag indicating whether or not Area are drawn at each XY point. */ private boolean plotArea; /** A flag that controls whether or not the outline is shown. */ private boolean showOutline; /** * The shape used to represent an area in each legend item (this should * never be {@code null}). */ private transient Shape legendArea; /** * A flag that can be set to specify that the fill paint should be used * to fill the area under the renderer. */ private boolean useFillPaint; /** * A transformer that is applied to the paint used to fill under the * area *if* it is an instance of GradientPaint. */ private GradientPaintTransformer gradientTransformer; /** * Constructs a new renderer. */ public XYAreaRenderer() { this(AREA); } /** * Constructs a new renderer. * * @param type the type of the renderer. */ public XYAreaRenderer(int type) { this(type, null, null); } /** * Constructs a new renderer. To specify the type of renderer, use one of * the constants: {@code SHAPES}, {@code LINES}, {@code SHAPES_AND_LINES}, * {@code AREA} or {@code AREA_AND_SHAPES}. * * @param type the type of renderer. * @param toolTipGenerator the tool tip generator ({@code null} permitted). * @param urlGenerator the URL generator ({@code null} permitted). */ public XYAreaRenderer(int type, XYToolTipGenerator toolTipGenerator, XYURLGenerator urlGenerator) { super(); setDefaultToolTipGenerator(toolTipGenerator); setURLGenerator(urlGenerator); if (type == SHAPES) { this.plotShapes = true; } if (type == LINES) { this.plotLines = true; } if (type == SHAPES_AND_LINES) { this.plotShapes = true; this.plotLines = true; } if (type == AREA) { this.plotArea = true; } if (type == AREA_AND_SHAPES) { this.plotArea = true; this.plotShapes = true; } this.showOutline = false; GeneralPath area = new GeneralPath(); area.moveTo(0.0f, -4.0f); area.lineTo(3.0f, -2.0f); area.lineTo(4.0f, 4.0f); area.lineTo(-4.0f, 4.0f); area.lineTo(-3.0f, -2.0f); area.closePath(); this.legendArea = area; this.useFillPaint = false; this.gradientTransformer = new StandardGradientPaintTransformer(); } /** * Returns true if shapes are being plotted by the renderer. * * @return {@code true} if shapes are being plotted by the renderer. */ public boolean getPlotShapes() { return this.plotShapes; } /** * Returns true if lines are being plotted by the renderer. * * @return {@code true} if lines are being plotted by the renderer. */ public boolean getPlotLines() { return this.plotLines; } /** * Returns true if Area is being plotted by the renderer. * * @return {@code true} if Area is being plotted by the renderer. */ public boolean getPlotArea() { return this.plotArea; } /** * Returns a flag that controls whether or not outlines of the areas are * drawn. * * @return The flag. * * @see #setOutline(boolean) */ public boolean isOutline() { return this.showOutline; } /** * Sets a flag that controls whether or not outlines of the areas are drawn * and sends a {@link RendererChangeEvent} to all registered listeners. * * @param show the flag. * * @see #isOutline() */ public void setOutline(boolean show) { this.showOutline = show; fireChangeEvent(); } /** * Returns the shape used to represent an area in the legend. * * @return The legend area (never {@code null}). */ public Shape getLegendArea() { return this.legendArea; } /** * Sets the shape used as an area in each legend item and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param area the area ({@code null} not permitted). */ public void setLegendArea(Shape area) { Args.nullNotPermitted(area, "area"); this.legendArea = area; fireChangeEvent(); } /** * Returns the flag that controls whether the series fill paint is used to * fill the area under the line. * * @return A boolean. */ public boolean getUseFillPaint() { return this.useFillPaint; } /** * Sets the flag that controls whether or not the series fill paint is * used to fill the area under the line and sends a * {@link RendererChangeEvent} to all listeners. * * @param use the new flag value. */ public void setUseFillPaint(boolean use) { this.useFillPaint = use; fireChangeEvent(); } /** * Returns the gradient paint transformer. * * @return The gradient paint transformer (never {@code null}). */ public GradientPaintTransformer getGradientTransformer() { return this.gradientTransformer; } /** * Sets the gradient paint transformer and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param transformer the transformer ({@code null} not permitted). */ public void setGradientTransformer(GradientPaintTransformer transformer) { Args.nullNotPermitted(transformer, "transformer"); this.gradientTransformer = transformer; fireChangeEvent(); } /** * Initialises the renderer and returns a state object that should be * passed to all subsequent calls to the drawItem() method. * * @param g2 the graphics device. * @param dataArea the area inside the axes. * @param plot the plot. * @param data the data. * @param info an optional info collection object to return data back to * the caller. * * @return A state object for use by the renderer. */ @Override public XYItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, XYPlot plot, XYDataset data, PlotRenderingInfo info) { XYAreaRendererState state = new XYAreaRendererState(info); // in the rendering process, there is special handling for item // zero, so we can't support processing of visible data items only state.setProcessVisibleItemsOnly(false); return state; } /** * Returns a default legend item for the specified series. Subclasses * should override this method to generate customised items. * * @param datasetIndex the dataset index (zero-based). * @param series the series index (zero-based). * * @return A legend item for the series. */ @Override public LegendItem getLegendItem(int datasetIndex, int series) { LegendItem result = null; XYPlot xyplot = getPlot(); if (xyplot != null) { XYDataset dataset = xyplot.getDataset(datasetIndex); if (dataset != null) { XYSeriesLabelGenerator lg = getLegendItemLabelGenerator(); String label = lg.generateLabel(dataset, series); String description = label; String toolTipText = null; if (getLegendItemToolTipGenerator() != null) { toolTipText = getLegendItemToolTipGenerator().generateLabel( dataset, series); } String urlText = null; if (getLegendItemURLGenerator() != null) { urlText = getLegendItemURLGenerator().generateLabel( dataset, series); } Paint paint = lookupSeriesPaint(series); result = new LegendItem(label, description, toolTipText, urlText, this.legendArea, paint); result.setLabelFont(lookupLegendTextFont(series)); Paint labelPaint = lookupLegendTextPaint(series); if (labelPaint != null) { result.setLabelPaint(labelPaint); } result.setDataset(dataset); result.setDatasetIndex(datasetIndex); result.setSeriesKey(dataset.getSeriesKey(series)); result.setSeriesIndex(series); } } return result; } /** * Draws the visual representation of a single data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the area within which the data is being drawn. * @param info collects information about the drawing. * @param plot the plot (can be used to obtain standard color information * etc). * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset. * @param series the series index (zero-based). * @param item the item index (zero-based). * @param crosshairState crosshair information for the plot * ({@code null} permitted). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item, CrosshairState crosshairState, int pass) { if (!getItemVisible(series, item)) { return; } XYAreaRendererState areaState = (XYAreaRendererState) state; // get the data point... double x1 = dataset.getXValue(series, item); double y1 = dataset.getYValue(series, item); if (Double.isNaN(y1)) { y1 = 0.0; } double transX1 = domainAxis.valueToJava2D(x1, dataArea, plot.getDomainAxisEdge()); double transY1 = rangeAxis.valueToJava2D(y1, dataArea, plot.getRangeAxisEdge()); // get the previous point and the next point so we can calculate a // "hot spot" for the area (used by the chart entity)... int itemCount = dataset.getItemCount(series); double x0 = dataset.getXValue(series, Math.max(item - 1, 0)); double y0 = dataset.getYValue(series, Math.max(item - 1, 0)); if (Double.isNaN(y0)) { y0 = 0.0; } double transX0 = domainAxis.valueToJava2D(x0, dataArea, plot.getDomainAxisEdge()); double transY0 = rangeAxis.valueToJava2D(y0, dataArea, plot.getRangeAxisEdge()); double x2 = dataset.getXValue(series, Math.min(item + 1, itemCount - 1)); double y2 = dataset.getYValue(series, Math.min(item + 1, itemCount - 1)); if (Double.isNaN(y2)) { y2 = 0.0; } double transX2 = domainAxis.valueToJava2D(x2, dataArea, plot.getDomainAxisEdge()); double transY2 = rangeAxis.valueToJava2D(y2, dataArea, plot.getRangeAxisEdge()); double transZero = rangeAxis.valueToJava2D(0.0, dataArea, plot.getRangeAxisEdge()); if (item == 0) { // create a new area polygon for the series areaState.area = new GeneralPath(); // the first point is (x, 0) double zero = rangeAxis.valueToJava2D(0.0, dataArea, plot.getRangeAxisEdge()); if (plot.getOrientation().isVertical()) { moveTo(areaState.area, transX1, zero); } else if (plot.getOrientation().isHorizontal()) { moveTo(areaState.area, zero, transX1); } } // Add each point to Area (x, y) if (plot.getOrientation().isVertical()) { lineTo(areaState.area, transX1, transY1); } else if (plot.getOrientation().isHorizontal()) { lineTo(areaState.area, transY1, transX1); } PlotOrientation orientation = plot.getOrientation(); Paint paint = getItemPaint(series, item); Stroke stroke = getItemStroke(series, item); g2.setPaint(paint); g2.setStroke(stroke); Shape shape; if (getPlotShapes()) { shape = getItemShape(series, item); if (orientation == PlotOrientation.VERTICAL) { shape = ShapeUtils.createTranslatedShape(shape, transX1, transY1); } else if (orientation == PlotOrientation.HORIZONTAL) { shape = ShapeUtils.createTranslatedShape(shape, transY1, transX1); } g2.draw(shape); } if (getPlotLines()) { if (item > 0) { if (plot.getOrientation() == PlotOrientation.VERTICAL) { areaState.line.setLine(transX0, transY0, transX1, transY1); } else if (plot.getOrientation() == PlotOrientation.HORIZONTAL) { areaState.line.setLine(transY0, transX0, transY1, transX1); } g2.draw(areaState.line); } } // Check if the item is the last item for the series. // and number of items > 0. We can't draw an area for a single point. if (getPlotArea() && item > 0 && item == (itemCount - 1)) { if (orientation == PlotOrientation.VERTICAL) { // Add the last point (x,0) lineTo(areaState.area, transX1, transZero); areaState.area.closePath(); } else if (orientation == PlotOrientation.HORIZONTAL) { // Add the last point (x,0) lineTo(areaState.area, transZero, transX1); areaState.area.closePath(); } if (this.useFillPaint) { paint = lookupSeriesFillPaint(series); } if (paint instanceof GradientPaint) { GradientPaint gp = (GradientPaint) paint; GradientPaint adjGP = this.gradientTransformer.transform(gp, dataArea); g2.setPaint(adjGP); } g2.fill(areaState.area); // draw an outline around the Area. if (isOutline()) { Shape area = areaState.area; // Java2D has some issues drawing dashed lines around "large" // geometrical shapes - for example, see bug 6620013 in the // Java bug database. So, we'll check if the outline is // dashed and, if it is, do our own clipping before drawing // the outline... Stroke outlineStroke = lookupSeriesOutlineStroke(series); if (outlineStroke instanceof BasicStroke) { BasicStroke bs = (BasicStroke) outlineStroke; if (bs.getDashArray() != null) { Area poly = new Area(areaState.area); // we make the clip region slightly larger than the // dataArea so that the clipped edges don't show lines // on the chart Area clip = new Area(new Rectangle2D.Double( dataArea.getX() - 5.0, dataArea.getY() - 5.0, dataArea.getWidth() + 10.0, dataArea.getHeight() + 10.0)); poly.intersect(clip); area = poly; } } // end of workaround g2.setStroke(outlineStroke); g2.setPaint(lookupSeriesOutlinePaint(series)); g2.draw(area); } } int datasetIndex = plot.indexOf(dataset); updateCrosshairValues(crosshairState, x1, y1, datasetIndex, transX1, transY1, orientation); // collect entity and tool tip information... EntityCollection entities = state.getEntityCollection(); if (entities != null) { GeneralPath hotspot = new GeneralPath(); if (plot.getOrientation() == PlotOrientation.HORIZONTAL) { moveTo(hotspot, transZero, ((transX0 + transX1) / 2.0)); lineTo(hotspot, ((transY0 + transY1) / 2.0), ((transX0 + transX1) / 2.0)); lineTo(hotspot, transY1, transX1); lineTo(hotspot, ((transY1 + transY2) / 2.0), ((transX1 + transX2) / 2.0)); lineTo(hotspot, transZero, ((transX1 + transX2) / 2.0)); } else { // vertical orientation moveTo(hotspot, ((transX0 + transX1) / 2.0), transZero); lineTo(hotspot, ((transX0 + transX1) / 2.0), ((transY0 + transY1) / 2.0)); lineTo(hotspot, transX1, transY1); lineTo(hotspot, ((transX1 + transX2) / 2.0), ((transY1 + transY2) / 2.0)); lineTo(hotspot, ((transX1 + transX2) / 2.0), transZero); } hotspot.closePath(); // limit the entity hotspot area to the data area Area dataAreaHotspot = new Area(hotspot); dataAreaHotspot.intersect(new Area(dataArea)); if (!dataAreaHotspot.isEmpty()) { addEntity(entities, dataAreaHotspot, dataset, series, item, 0.0, 0.0); } } } /** * Returns a clone of the renderer. * * @return A clone. * * @throws CloneNotSupportedException if the renderer cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { XYAreaRenderer clone = (XYAreaRenderer) super.clone(); clone.legendArea = ShapeUtils.clone(this.legendArea); return clone; } /** * Tests this renderer for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYAreaRenderer)) { return false; } XYAreaRenderer that = (XYAreaRenderer) obj; if (this.plotArea != that.plotArea) { return false; } if (this.plotLines != that.plotLines) { return false; } if (this.plotShapes != that.plotShapes) { return false; } if (this.showOutline != that.showOutline) { return false; } if (this.useFillPaint != that.useFillPaint) { return false; } if (!this.gradientTransformer.equals(that.gradientTransformer)) { return false; } if (!ShapeUtils.equal(this.legendArea, that.legendArea)) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = super.hashCode(); result = HashUtils.hashCode(result, this.plotArea); result = HashUtils.hashCode(result, this.plotLines); result = HashUtils.hashCode(result, this.plotShapes); result = HashUtils.hashCode(result, this.useFillPaint); return result; } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.legendArea = SerialUtils.readShape(stream); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeShape(this.legendArea, stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/XYAreaRenderer2.java000066400000000000000000000355721463604235500311050ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * XYAreaRenderer2.java * -------------------- * (C) Copyright 2004-present, by Hari and Contributors. * * Original Author: Hari (ourhari@hotmail.com); * Contributor(s): David Gilbert; * Richard Atkinson; * Christian W. Zuckschwerdt; * Martin Krauskopf; * Ulrich Voigt (patch #312); */ package org.jfree.chart.renderer.xy; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Area; import java.awt.geom.GeneralPath; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import org.jfree.chart.LegendItem; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.entity.XYItemEntity; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.labels.XYSeriesLabelGenerator; import org.jfree.chart.labels.XYToolTipGenerator; import org.jfree.chart.plot.CrosshairState; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.urls.XYURLGenerator; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; import org.jfree.chart.util.ShapeUtils; import org.jfree.data.xy.XYDataset; /** * Area item renderer for an {@link XYPlot}. The example shown here is * generated by the {@code XYAreaRenderer2Demo1.java} program included in * the JFreeChart demo collection: *

* XYAreaRenderer2Sample.png */ public class XYAreaRenderer2 extends AbstractXYItemRenderer implements XYItemRenderer, PublicCloneable { /** For serialization. */ private static final long serialVersionUID = -7378069681579984133L; /** A flag that controls whether or not the outline is shown. */ private boolean showOutline; /** * The shape used to represent an area in each legend item (this should * never be {@code null}). */ private transient Shape legendArea; /** * Constructs a new renderer. */ public XYAreaRenderer2() { this(null, null); } /** * Constructs a new renderer. * * @param labelGenerator the tool tip generator to use ({@code null} * permitted). * @param urlGenerator the URL generator ({@code null} permitted). */ public XYAreaRenderer2(XYToolTipGenerator labelGenerator, XYURLGenerator urlGenerator) { super(); this.showOutline = false; setDefaultToolTipGenerator(labelGenerator); setURLGenerator(urlGenerator); GeneralPath area = new GeneralPath(); area.moveTo(0.0f, -4.0f); area.lineTo(3.0f, -2.0f); area.lineTo(4.0f, 4.0f); area.lineTo(-4.0f, 4.0f); area.lineTo(-3.0f, -2.0f); area.closePath(); this.legendArea = area; } /** * Returns a flag that controls whether or not outlines of the areas are * drawn. * * @return The flag. * * @see #setOutline(boolean) */ public boolean isOutline() { return this.showOutline; } /** * Sets a flag that controls whether or not outlines of the areas are * drawn, and sends a {@link RendererChangeEvent} to all registered * listeners. * * @param show the flag. * * @see #isOutline() */ public void setOutline(boolean show) { this.showOutline = show; fireChangeEvent(); } /** * Returns the shape used to represent an area in the legend. * * @return The legend area (never {@code null}). * * @see #setLegendArea(Shape) */ public Shape getLegendArea() { return this.legendArea; } /** * Sets the shape used as an area in each legend item and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param area the area ({@code null} not permitted). * * @see #getLegendArea() */ public void setLegendArea(Shape area) { Args.nullNotPermitted(area, "area"); this.legendArea = area; fireChangeEvent(); } /** * Returns a default legend item for the specified series. Subclasses * should override this method to generate customised items. * * @param datasetIndex the dataset index (zero-based). * @param series the series index (zero-based). * * @return A legend item for the series. */ @Override public LegendItem getLegendItem(int datasetIndex, int series) { LegendItem result = null; XYPlot xyplot = getPlot(); if (xyplot != null) { XYDataset dataset = xyplot.getDataset(datasetIndex); if (dataset != null) { XYSeriesLabelGenerator lg = getLegendItemLabelGenerator(); String label = lg.generateLabel(dataset, series); String description = label; String toolTipText = null; if (getLegendItemToolTipGenerator() != null) { toolTipText = getLegendItemToolTipGenerator().generateLabel( dataset, series); } String urlText = null; if (getLegendItemURLGenerator() != null) { urlText = getLegendItemURLGenerator().generateLabel( dataset, series); } Paint paint = lookupSeriesPaint(series); result = new LegendItem(label, description, toolTipText, urlText, this.legendArea, paint); result.setLabelFont(lookupLegendTextFont(series)); Paint labelPaint = lookupLegendTextPaint(series); if (labelPaint != null) { result.setLabelPaint(labelPaint); } result.setDataset(dataset); result.setDatasetIndex(datasetIndex); result.setSeriesKey(dataset.getSeriesKey(series)); result.setSeriesIndex(series); } } return result; } /** * Draws the visual representation of a single data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the area within which the data is being drawn. * @param info collects information about the drawing. * @param plot the plot (can be used to obtain standard color * information etc). * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset. * @param series the series index (zero-based). * @param item the item index (zero-based). * @param crosshairState crosshair information for the plot * ({@code null} permitted). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item, CrosshairState crosshairState, int pass) { if (!getItemVisible(series, item)) { return; } // get the data point... double x1 = dataset.getXValue(series, item); double y1 = dataset.getYValue(series, item); if (Double.isNaN(y1)) { y1 = 0.0; } double transX1 = domainAxis.valueToJava2D(x1, dataArea, plot.getDomainAxisEdge()); double transY1 = rangeAxis.valueToJava2D(y1, dataArea, plot.getRangeAxisEdge()); // get the previous point and the next point so we can calculate a // "hot spot" for the area (used by the chart entity)... double x0 = dataset.getXValue(series, Math.max(item - 1, 0)); double y0 = dataset.getYValue(series, Math.max(item - 1, 0)); if (Double.isNaN(y0)) { y0 = 0.0; } double transX0 = domainAxis.valueToJava2D(x0, dataArea, plot.getDomainAxisEdge()); double transY0 = rangeAxis.valueToJava2D(y0, dataArea, plot.getRangeAxisEdge()); int itemCount = dataset.getItemCount(series); double x2 = dataset.getXValue(series, Math.min(item + 1, itemCount - 1)); double y2 = dataset.getYValue(series, Math.min(item + 1, itemCount - 1)); if (Double.isNaN(y2)) { y2 = 0.0; } double transX2 = domainAxis.valueToJava2D(x2, dataArea, plot.getDomainAxisEdge()); double transY2 = rangeAxis.valueToJava2D(y2, dataArea, plot.getRangeAxisEdge()); double transZero = rangeAxis.valueToJava2D(0.0, dataArea, plot.getRangeAxisEdge()); GeneralPath hotspot = new GeneralPath(); if (plot.getOrientation() == PlotOrientation.HORIZONTAL) { moveTo(hotspot, transZero, ((transX0 + transX1) / 2.0)); lineTo(hotspot, ((transY0 + transY1) / 2.0), ((transX0 + transX1) / 2.0)); lineTo(hotspot, transY1, transX1); lineTo(hotspot, ((transY1 + transY2) / 2.0), ((transX1 + transX2) / 2.0)); lineTo(hotspot, transZero, ((transX1 + transX2) / 2.0)); } else { // vertical orientation moveTo(hotspot, ((transX0 + transX1) / 2.0), transZero); lineTo(hotspot, ((transX0 + transX1) / 2.0), ((transY0 + transY1) / 2.0)); lineTo(hotspot, transX1, transY1); lineTo(hotspot, ((transX1 + transX2) / 2.0), ((transY1 + transY2) / 2.0)); lineTo(hotspot, ((transX1 + transX2) / 2.0), transZero); } hotspot.closePath(); PlotOrientation orientation = plot.getOrientation(); Paint paint = getItemPaint(series, item); Stroke stroke = getItemStroke(series, item); g2.setPaint(paint); g2.setStroke(stroke); // Check if the item is the last item for the series. // and number of items > 0. We can't draw an area for a single point. g2.fill(hotspot); // draw an outline around the Area. if (isOutline()) { g2.setStroke(lookupSeriesOutlineStroke(series)); g2.setPaint(lookupSeriesOutlinePaint(series)); g2.draw(hotspot); } int datasetIndex = plot.indexOf(dataset); updateCrosshairValues(crosshairState, x1, y1, datasetIndex, transX1, transY1, orientation); // collect entity and tool tip information... if (state.getInfo() != null) { EntityCollection entities = state.getEntityCollection(); if (entities != null) { // limit the entity hotspot area to the data area Area dataAreaHotspot = new Area(hotspot); dataAreaHotspot.intersect(new Area(dataArea)); if (!dataAreaHotspot.isEmpty()) { String tip = null; XYToolTipGenerator generator = getToolTipGenerator(series, item); if (generator != null) { tip = generator.generateToolTip(dataset, series, item); } String url = null; if (getURLGenerator() != null) { url = getURLGenerator().generateURL(dataset, series, item); } XYItemEntity entity = new XYItemEntity(dataAreaHotspot, dataset, series, item, tip, url); entities.add(entity); } } } } /** * Tests this renderer for equality with an arbitrary object. * * @param obj the object ({@code null} not permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYAreaRenderer2)) { return false; } XYAreaRenderer2 that = (XYAreaRenderer2) obj; if (this.showOutline != that.showOutline) { return false; } if (!ShapeUtils.equal(this.legendArea, that.legendArea)) { return false; } return super.equals(obj); } /** * Returns a clone of the renderer. * * @return A clone. * * @throws CloneNotSupportedException if the renderer cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { XYAreaRenderer2 clone = (XYAreaRenderer2) super.clone(); clone.legendArea = ShapeUtils.clone(this.legendArea); return clone; } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.legendArea = SerialUtils.readShape(stream); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeShape(this.legendArea, stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/XYBarPainter.java000066400000000000000000000063461463604235500305100ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * XYBarPainter.java * ----------------- * (C) Copyright 2008-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import java.awt.Graphics2D; import java.awt.geom.RectangularShape; import org.jfree.chart.ui.RectangleEdge; /** * The interface for plugin painter for the {@link XYBarRenderer} class. When * developing a class that implements this interface, bear in mind the * following: *
    *
  • the {@code equals(Object)} method should be overridden;
  • *
  • instances of the class should be immutable OR implement the * {@code PublicCloneable} interface, so that a renderer using the * painter can be cloned reliably; *
  • the class should be {@code Serializable}, otherwise chart * serialization will not be supported.
  • *
*/ public interface XYBarPainter { /** * Paints a single bar on behalf of a renderer. * * @param g2 the graphics target. * @param renderer the renderer. * @param row the row index for the item. * @param column the column index for the item. * @param bar the bounds for the bar. * @param base the base of the bar. */ void paintBar(Graphics2D g2, XYBarRenderer renderer, int row, int column, RectangularShape bar, RectangleEdge base); /** * Paints the shadow for a single bar on behalf of a renderer. * * @param g2 the graphics target. * @param renderer the renderer. * @param row the row index for the item. * @param column the column index for the item. * @param bar the bounds for the bar. * @param base the base of the bar. * @param pegShadow peg the shadow to the base of the bar? */ void paintBarShadow(Graphics2D g2, XYBarRenderer renderer, int row, int column, RectangularShape bar, RectangleEdge base, boolean pegShadow); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/XYBarRenderer.java000066400000000000000000001304311463604235500306450ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * XYBarRenderer.java * ------------------ * (C) Copyright 2001-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Richard Atkinson; * Christian W. Zuckschwerdt; * Bill Kelemen; * Marc van Glabbeek (bug 1775452); * Richard West, Advanced Micro Devices, Inc.; * Yuri Blankenstein; * */ package org.jfree.chart.renderer.xy; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.LegendItem; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.labels.ItemLabelAnchor; import org.jfree.chart.labels.ItemLabelPosition; import org.jfree.chart.labels.XYItemLabelGenerator; import org.jfree.chart.labels.XYSeriesLabelGenerator; import org.jfree.chart.plot.CrosshairState; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.text.TextUtils; import org.jfree.chart.ui.GradientPaintTransformer; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.ui.StandardGradientPaintTransformer; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; import org.jfree.chart.util.ShapeUtils; import org.jfree.data.Range; import org.jfree.data.xy.IntervalXYDataset; import org.jfree.data.xy.XYDataset; /** * A renderer that draws bars on an {@link XYPlot} (requires an * {@link IntervalXYDataset}). The example shown here is generated by the * {@code XYBarChartDemo1.java} program included in the JFreeChart * demo collection: *

* XYBarRendererSample.png */ public class XYBarRenderer extends AbstractXYItemRenderer implements XYItemRenderer, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 770559577251370036L; /** * The default bar painter assigned to each new instance of this renderer. */ private static XYBarPainter defaultBarPainter = new GradientXYBarPainter(); /** * Returns the default bar painter. * * @return The default bar painter. */ public static XYBarPainter getDefaultBarPainter() { return XYBarRenderer.defaultBarPainter; } /** * Sets the default bar painter. * * @param painter the painter ({@code null} not permitted). */ public static void setDefaultBarPainter(XYBarPainter painter) { Args.nullNotPermitted(painter, "painter"); XYBarRenderer.defaultBarPainter = painter; } /** * The default value for the initialisation of the shadowsVisible flag. */ private static boolean defaultShadowsVisible = true; /** * Returns the default value for the {@code shadowsVisible} flag. * * @return A boolean. * * @see #setDefaultShadowsVisible(boolean) */ public static boolean getDefaultShadowsVisible() { return XYBarRenderer.defaultShadowsVisible; } /** * Sets the default value for the shadows visible flag. * * @param visible the new value for the default. * * @see #getDefaultShadowsVisible() */ public static void setDefaultShadowsVisible(boolean visible) { XYBarRenderer.defaultShadowsVisible = visible; } /** * The state class used by this renderer. */ protected class XYBarRendererState extends XYItemRendererState { /** Base for bars against the range axis, in Java 2D space. */ private double g2Base; /** * Creates a new state object. * * @param info the plot rendering info. */ public XYBarRendererState(PlotRenderingInfo info) { super(info); } /** * Returns the base (range) value in Java 2D space. * * @return The base value. */ public double getG2Base() { return this.g2Base; } /** * Sets the range axis base in Java2D space. * * @param value the value. */ public void setG2Base(double value) { this.g2Base = value; } } /** The default base value for the bars. */ private double base; /** * A flag that controls whether the bars use the y-interval supplied by the * dataset. */ private boolean useYInterval; /** Percentage margin (to reduce the width of bars). */ private double margin; /** A flag that controls whether or not bar outlines are drawn. */ private boolean drawBarOutline; /** * An optional class used to transform gradient paint objects to fit each * bar. */ private GradientPaintTransformer gradientPaintTransformer; /** * The shape used to represent a bar in each legend item (this should never * be {@code null}). */ private transient Shape legendBar; /** * The fallback position if a positive item label doesn't fit inside the * bar. */ private ItemLabelPosition positiveItemLabelPositionFallback; /** * The fallback position if a negative item label doesn't fit inside the * bar. */ private ItemLabelPosition negativeItemLabelPositionFallback; /** * The bar painter (never {@code null}). */ private XYBarPainter barPainter; /** * The flag that controls whether or not shadows are drawn for the bars. */ private boolean shadowsVisible; /** * The x-offset for the shadow effect. */ private double shadowXOffset; /** * The y-offset for the shadow effect. */ private double shadowYOffset; /** * A factor used to align the bars about the x-value. */ private double barAlignmentFactor; /** The minimum size for the bar to draw a label */ private Dimension minimumLabelSize; /** {@code true} if the label should be aligned to the visible part of the bar. */ private boolean showLabelInsideVisibleBar; /** * The default constructor. */ public XYBarRenderer() { this(0.0); } /** * Constructs a new renderer. * * @param margin the percentage amount to trim from the width of each bar. */ public XYBarRenderer(double margin) { super(); this.margin = margin; this.base = 0.0; this.useYInterval = false; this.gradientPaintTransformer = new StandardGradientPaintTransformer(); this.drawBarOutline = false; this.legendBar = new Rectangle2D.Double(-3.0, -5.0, 6.0, 10.0); this.barPainter = getDefaultBarPainter(); this.shadowsVisible = getDefaultShadowsVisible(); this.shadowXOffset = 4.0; this.shadowYOffset = 4.0; this.barAlignmentFactor = -1.0; } /** * Returns the base value for the bars. * * @return The base value for the bars. * * @see #setBase(double) */ public double getBase() { return this.base; } /** * Sets the base value for the bars and sends a {@link RendererChangeEvent} * to all registered listeners. The base value is not used if the dataset's * y-interval is being used to determine the bar length. * * @param base the new base value. * * @see #getBase() * @see #getUseYInterval() */ public void setBase(double base) { this.base = base; fireChangeEvent(); } /** * Returns a flag that determines whether the y-interval from the dataset is * used to calculate the length of each bar. * * @return A boolean. * * @see #setUseYInterval(boolean) */ public boolean getUseYInterval() { return this.useYInterval; } /** * Sets the flag that determines whether the y-interval from the dataset is * used to calculate the length of each bar, and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param use the flag. * * @see #getUseYInterval() */ public void setUseYInterval(boolean use) { if (this.useYInterval != use) { this.useYInterval = use; fireChangeEvent(); } } /** * Returns the margin which is a percentage amount by which the bars are * trimmed. * * @return The margin. * * @see #setMargin(double) */ public double getMargin() { return this.margin; } /** * Sets the percentage amount by which the bars are trimmed and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param margin the new margin. * * @see #getMargin() */ public void setMargin(double margin) { this.margin = margin; fireChangeEvent(); } /** * Returns a flag that controls whether or not bar outlines are drawn. * * @return A boolean. * * @see #setDrawBarOutline(boolean) */ public boolean isDrawBarOutline() { return this.drawBarOutline; } /** * Sets the flag that controls whether or not bar outlines are drawn and * sends a {@link RendererChangeEvent} to all registered listeners. * * @param draw the flag. * * @see #isDrawBarOutline() */ public void setDrawBarOutline(boolean draw) { this.drawBarOutline = draw; fireChangeEvent(); } /** * Returns the gradient paint transformer (an object used to transform * gradient paint objects to fit each bar). * * @return A transformer ({@code null} possible). * * @see #setGradientPaintTransformer(GradientPaintTransformer) */ public GradientPaintTransformer getGradientPaintTransformer() { return this.gradientPaintTransformer; } /** * Sets the gradient paint transformer and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param transformer the transformer ({@code null} permitted). * * @see #getGradientPaintTransformer() */ public void setGradientPaintTransformer( GradientPaintTransformer transformer) { this.gradientPaintTransformer = transformer; fireChangeEvent(); } /** * Returns the shape used to represent bars in each legend item. * * @return The shape used to represent bars in each legend item (never * {@code null}). * * @see #setLegendBar(Shape) */ public Shape getLegendBar() { return this.legendBar; } /** * Sets the shape used to represent bars in each legend item and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param bar the bar shape ({@code null} not permitted). * * @see #getLegendBar() */ public void setLegendBar(Shape bar) { Args.nullNotPermitted(bar, "bar"); this.legendBar = bar; fireChangeEvent(); } /** * Returns the fallback position for positive item labels that don't fit * within a bar. * * @return The fallback position ({@code null} possible). * * @see #setPositiveItemLabelPositionFallback(ItemLabelPosition) */ public ItemLabelPosition getPositiveItemLabelPositionFallback() { return this.positiveItemLabelPositionFallback; } /** * Sets the fallback position for positive item labels that don't fit * within a bar, and sends a {@link RendererChangeEvent} to all registered * listeners. * * @param position the position ({@code null} permitted). * * @see #getPositiveItemLabelPositionFallback() */ public void setPositiveItemLabelPositionFallback( ItemLabelPosition position) { this.positiveItemLabelPositionFallback = position; fireChangeEvent(); } /** * Returns the fallback position for negative item labels that don't fit * within a bar. * * @return The fallback position ({@code null} possible). * * @see #setNegativeItemLabelPositionFallback(ItemLabelPosition) */ public ItemLabelPosition getNegativeItemLabelPositionFallback() { return this.negativeItemLabelPositionFallback; } /** * Sets the fallback position for negative item labels that don't fit * within a bar, and sends a {@link RendererChangeEvent} to all registered * listeners. * * @param position the position ({@code null} permitted). * * @see #getNegativeItemLabelPositionFallback() */ public void setNegativeItemLabelPositionFallback( ItemLabelPosition position) { this.negativeItemLabelPositionFallback = position; fireChangeEvent(); } /** * Returns the bar painter. * * @return The bar painter (never {@code null}). */ public XYBarPainter getBarPainter() { return this.barPainter; } /** * Sets the bar painter and sends a {@link RendererChangeEvent} to all * registered listeners. * * @param painter the painter ({@code null} not permitted). */ public void setBarPainter(XYBarPainter painter) { Args.nullNotPermitted(painter, "painter"); this.barPainter = painter; fireChangeEvent(); } /** * Returns the flag that controls whether or not shadows are drawn for * the bars. * * @return A boolean. */ public boolean getShadowsVisible() { return this.shadowsVisible; } /** * Sets the flag that controls whether or not the renderer * draws shadows for the bars, and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param visible the new flag value. */ public void setShadowVisible(boolean visible) { this.shadowsVisible = visible; fireChangeEvent(); } /** * Returns the shadow x-offset. * * @return The shadow x-offset. */ public double getShadowXOffset() { return this.shadowXOffset; } /** * Sets the x-offset for the bar shadow and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param offset the offset. */ public void setShadowXOffset(double offset) { this.shadowXOffset = offset; fireChangeEvent(); } /** * Returns the shadow y-offset. * * @return The shadow y-offset. */ public double getShadowYOffset() { return this.shadowYOffset; } /** * Sets the y-offset for the bar shadow and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param offset the offset. */ public void setShadowYOffset(double offset) { this.shadowYOffset = offset; fireChangeEvent(); } /** * Returns the bar alignment factor. * * @return The bar alignment factor. */ public double getBarAlignmentFactor() { return this.barAlignmentFactor; } /** * Sets the bar alignment factor and sends a {@link RendererChangeEvent} * to all registered listeners. If the alignment factor is outside the * range 0.0 to 1.0, no alignment will be performed by the renderer. * * @param factor the factor. */ public void setBarAlignmentFactor(double factor) { this.barAlignmentFactor = factor; fireChangeEvent(); } /** * Returns the minimum size for the bar to draw a label. * * @return The minimum size to draw a label. */ public Dimension getMinimumLabelSize() { return minimumLabelSize; } /** * Sets the minimum size for the bar to draw a label. * * @param minimumLabelSize The size */ public void setMinimumLabelSize(Dimension minimumLabelSize) { this.minimumLabelSize = minimumLabelSize; fireChangeEvent(); } /** * Returns {@code true} if the label should be aligned to the visible part * of the bar. * * @return {@code true} if the label should be aligned to the visible part * of the bar. * @see #setShowLabelInsideVisibleBar(boolean) */ public boolean isShowLabelInsideVisibleBar() { return showLabelInsideVisibleBar; } /** * Sets whether the label should be aligned to the visible part of the * bar.
* This setting has no effect when {@link ItemLabelAnchor#isInternal()} * returns {@code false}. * * @param showLabelInsideVisibleBar {@code true} to align to the visible * part. */ public void setShowLabelInsideVisibleBar(boolean showLabelInsideVisibleBar) { this.showLabelInsideVisibleBar = showLabelInsideVisibleBar; fireChangeEvent(); } /** * Initialises the renderer and returns a state object that should be * passed to all subsequent calls to the drawItem() method. Here we * calculate the Java2D y-coordinate for zero, since all the bars have * their bases fixed at zero. * * @param g2 the graphics device. * @param dataArea the area inside the axes. * @param plot the plot. * @param dataset the data. * @param info an optional info collection object to return data back to * the caller. * * @return A state object. */ @Override public XYItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, XYPlot plot, XYDataset dataset, PlotRenderingInfo info) { XYBarRendererState state = new XYBarRendererState(info); ValueAxis rangeAxis = plot.getRangeAxisForDataset(plot.indexOf( dataset)); state.setG2Base(rangeAxis.valueToJava2D(this.base, dataArea, plot.getRangeAxisEdge())); return state; } /** * Returns a default legend item for the specified series. Subclasses * should override this method to generate customised items. * * @param datasetIndex the dataset index (zero-based). * @param series the series index (zero-based). * * @return A legend item for the series. */ @Override public LegendItem getLegendItem(int datasetIndex, int series) { XYPlot xyplot = getPlot(); if (xyplot == null) { return null; } XYDataset dataset = xyplot.getDataset(datasetIndex); if (dataset == null) { return null; } LegendItem result; XYSeriesLabelGenerator lg = getLegendItemLabelGenerator(); String label = lg.generateLabel(dataset, series); String description = label; String toolTipText = null; if (getLegendItemToolTipGenerator() != null) { toolTipText = getLegendItemToolTipGenerator().generateLabel( dataset, series); } String urlText = null; if (getLegendItemURLGenerator() != null) { urlText = getLegendItemURLGenerator().generateLabel(dataset, series); } Shape shape = this.legendBar; Paint paint = lookupSeriesPaint(series); Paint outlinePaint = lookupSeriesOutlinePaint(series); Stroke outlineStroke = lookupSeriesOutlineStroke(series); if (this.drawBarOutline) { result = new LegendItem(label, description, toolTipText, urlText, shape, paint, outlineStroke, outlinePaint); } else { result = new LegendItem(label, description, toolTipText, urlText, shape, paint); } result.setLabelFont(lookupLegendTextFont(series)); Paint labelPaint = lookupLegendTextPaint(series); if (labelPaint != null) { result.setLabelPaint(labelPaint); } result.setDataset(dataset); result.setDatasetIndex(datasetIndex); result.setSeriesKey(dataset.getSeriesKey(series)); result.setSeriesIndex(series); if (getGradientPaintTransformer() != null) { result.setFillPaintTransformer(getGradientPaintTransformer()); } return result; } /** * Draws the visual representation of a single data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the area within which the plot is being drawn. * @param info collects information about the drawing. * @param plot the plot (can be used to obtain standard color * information etc). * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset. * @param series the series index (zero-based). * @param item the item index (zero-based). * @param crosshairState crosshair information for the plot * ({@code null} permitted). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item, CrosshairState crosshairState, int pass) { if (!getItemVisible(series, item)) { return; } IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset; double value0; double value1; if (this.useYInterval) { value0 = intervalDataset.getStartYValue(series, item); value1 = intervalDataset.getEndYValue(series, item); } else { value0 = this.base; value1 = intervalDataset.getYValue(series, item); } if (Double.isNaN(value0) || Double.isNaN(value1)) { return; } if (value0 <= value1) { if (!rangeAxis.getRange().intersects(value0, value1)) { return; } } else { if (!rangeAxis.getRange().intersects(value1, value0)) { return; } } double translatedValue0 = rangeAxis.valueToJava2D(value0, dataArea, plot.getRangeAxisEdge()); double translatedValue1 = rangeAxis.valueToJava2D(value1, dataArea, plot.getRangeAxisEdge()); double bottom = Math.min(translatedValue0, translatedValue1); double top = Math.max(translatedValue0, translatedValue1); double startX = intervalDataset.getStartXValue(series, item); if (Double.isNaN(startX)) { return; } double endX = intervalDataset.getEndXValue(series, item); if (Double.isNaN(endX)) { return; } if (startX <= endX) { if (!domainAxis.getRange().intersects(startX, endX)) { return; } } else { if (!domainAxis.getRange().intersects(endX, startX)) { return; } } // is there an alignment adjustment to be made? if (this.barAlignmentFactor >= 0.0 && this.barAlignmentFactor <= 1.0) { double x = intervalDataset.getXValue(series, item); double interval = endX - startX; startX = x - interval * this.barAlignmentFactor; endX = startX + interval; } RectangleEdge location = plot.getDomainAxisEdge(); double translatedStartX = domainAxis.valueToJava2D(startX, dataArea, location); double translatedEndX = domainAxis.valueToJava2D(endX, dataArea, location); double translatedWidth = Math.max(1, Math.abs(translatedEndX - translatedStartX)); double left = Math.min(translatedStartX, translatedEndX); if (getMargin() > 0.0) { double cut = translatedWidth * getMargin(); translatedWidth = translatedWidth - cut; left = left + cut / 2; } Rectangle2D bar = null; PlotOrientation orientation = plot.getOrientation(); if (orientation.isHorizontal()) { // clip left and right bounds to data area bottom = Math.max(bottom, dataArea.getMinX()); top = Math.min(top, dataArea.getMaxX()); bar = new Rectangle2D.Double( bottom, left, top - bottom, translatedWidth); } else if (orientation.isVertical()) { // clip top and bottom bounds to data area bottom = Math.max(bottom, dataArea.getMinY()); top = Math.min(top, dataArea.getMaxY()); bar = new Rectangle2D.Double(left, bottom, translatedWidth, top - bottom); } boolean positive = (value1 > 0.0); boolean inverted = rangeAxis.isInverted(); RectangleEdge barBase; if (orientation.isHorizontal()) { if (positive && inverted || !positive && !inverted) { barBase = RectangleEdge.RIGHT; } else { barBase = RectangleEdge.LEFT; } } else { if (positive && !inverted || !positive && inverted) { barBase = RectangleEdge.BOTTOM; } else { barBase = RectangleEdge.TOP; } } if (state.getElementHinting()) { beginElementGroup(g2, dataset.getSeriesKey(series), item); } if (getShadowsVisible()) { this.barPainter.paintBarShadow(g2, this, series, item, bar, barBase, !this.useYInterval); } this.barPainter.paintBar(g2, this, series, item, bar, barBase); if (state.getElementHinting()) { endElementGroup(g2); } if (isItemLabelVisible(series, item)) { XYItemLabelGenerator generator = getItemLabelGenerator(series, item); drawItemLabel(g2, dataset, series, item, plot, generator, bar, value1 < 0.0); } // update the crosshair point double x1 = (startX + endX) / 2.0; double y1 = dataset.getYValue(series, item); double transX1 = domainAxis.valueToJava2D(x1, dataArea, location); double transY1 = rangeAxis.valueToJava2D(y1, dataArea, plot.getRangeAxisEdge()); int datasetIndex = plot.indexOf(dataset); updateCrosshairValues(crosshairState, x1, y1, datasetIndex, transX1, transY1, plot.getOrientation()); EntityCollection entities = state.getEntityCollection(); if (entities != null) { addEntity(entities, bar, dataset, series, item, 0.0, 0.0); } } /** * Draws an item label. This method is provided as an alternative to * {@link #drawItemLabel(Graphics2D, PlotOrientation, XYDataset, int, int, * double, double, boolean)} so that the bar can be used to calculate the * label anchor point. * * @param g the graphics device. * @param dataset the dataset. * @param series the series index. * @param item the item index. * @param plot the plot. * @param generator the label generator ({@code null} permitted, in * which case the method does nothing, just returns). * @param bar the bar. * @param negative a flag indicating a negative value. */ protected void drawItemLabel(Graphics2D g, XYDataset dataset, int series, int item, XYPlot plot, XYItemLabelGenerator generator, Rectangle2D bar, boolean negative) { if (generator == null) { return; // nothing to do } String label = generator.generateLabel(dataset, series, item); if (label == null) { return; // nothing to do } Graphics2D g2 = (Graphics2D) g.create(); Font labelFont = getItemLabelFont(series, item); g2.setFont(labelFont); Paint paint = getItemLabelPaint(series, item); g2.setPaint(paint); // find out where to place the label... ItemLabelPosition position; if (!negative) { position = getPositiveItemLabelPosition(series, item); } else { position = getNegativeItemLabelPosition(series, item); } Rectangle2D drawBar = bar; if (position.getItemLabelAnchor().isInternal()) { if (showLabelInsideVisibleBar && g2.getClipBounds() != null) { drawBar = drawBar.createIntersection(g2.getClipBounds().getBounds2D()); } Rectangle2D labelBar = getItemLabelInsets().createInsetRectangle(drawBar); if (minimumLabelSize != null && (labelBar.getWidth() < minimumLabelSize.getWidth() || labelBar.getHeight() < minimumLabelSize.getHeight())) { return; // nothing to do } } // work out the label anchor point... Point2D anchorPoint = calculateLabelAnchorPoint( position.getItemLabelAnchor(), drawBar, plot.getOrientation()); String drawLabel = calculateLabeltoDraw( label, anchorPoint, position, drawBar, g2); if (drawLabel == null) { if (!negative) { position = getPositiveItemLabelPositionFallback(); } else { position = getNegativeItemLabelPositionFallback(); } if (position != null) { g2 = (Graphics2D) g.create(); g2.setFont(labelFont); g2.setPaint(paint); if (position.getItemLabelAnchor().isInternal()) { if (showLabelInsideVisibleBar && g2.getClipBounds() != null) { drawBar = drawBar.createIntersection(g2.getClipBounds().getBounds2D()); } Rectangle2D labelBar = getItemLabelInsets().createInsetRectangle(drawBar); if (minimumLabelSize != null && (labelBar.getWidth() < minimumLabelSize.getWidth() || labelBar.getHeight() < minimumLabelSize.getHeight())) { return; // nothing to do } } anchorPoint = calculateLabelAnchorPoint( position.getItemLabelAnchor(), drawBar, plot.getOrientation()); drawLabel = calculateLabeltoDraw( label, anchorPoint, position, drawBar, g2); } } if (drawLabel != null) { TextUtils.drawRotatedString(drawLabel, g2, (float) anchorPoint.getX(), (float) anchorPoint.getY(), position.getTextAnchor(), position.getAngle(), position.getRotationAnchor()); } } /** * @return The label to draw or {@code null} if label should not be drawn. */ private String calculateLabeltoDraw(String label, Point2D anchorPoint, ItemLabelPosition position, Rectangle2D bar, Graphics2D g2) { if (!position.getItemLabelAnchor().isInternal()) { return label; } // Taking the bounds of the bounds will ceil the rectangle to its // smallest enclosing integer instance, this avoid rounding errors when // testing if the label fits Rectangle2D labelBar = getItemLabelInsets().createInsetRectangle(bar).getBounds(); switch (position.getItemLabelClip()) { case CLIP : Shape currentClip = g2.getClip(); if (currentClip == null) { g2.setClip(labelBar); } else { g2.setClip(labelBar .createIntersection(currentClip.getBounds2D())); } return label; case NONE : return label; default : } String result = label; while (result != null) { Shape bounds = TextUtils.calculateRotatedStringBounds(result, g2, (float) anchorPoint.getX(), (float) anchorPoint.getY(), position.getTextAnchor(), position.getAngle(), position.getRotationAnchor()); Rectangle2D bounds2D = bounds == null ? null : bounds.getBounds2D(); if (bounds2D != null && labelBar.contains(bounds2D)) { // Label fits return result; } else if (bounds2D != null && labelBar.getHeight() < bounds2D.getHeight()) { // Label will never fit, insufficient height return null; } else { switch (position.getItemLabelClip()) { case FIT : return null; case TRUNCATE : { String nextResult = result.replaceFirst(".(\\.{3})?$", "..."); if ("...".equals(nextResult) || result.equals(nextResult)) { return null; } else { result = nextResult; } break; } case TRUNCATE_WORD : { String nextResult = result .replaceFirst("\\W+\\w*(\\.{3})?$", "..."); if ("...".equals(nextResult) || result.equals(nextResult)) { return null; } else { result = nextResult; } break; } default : throw new IllegalStateException("Should never happen"); } } } return null; } /** * Calculates the item label anchor point. * *
     * Inside:
     *  +-----------------+
     *  | 10/11  12   1/2 |
     *  |   9     C    3  |
     *  |  7/8    6   4/5 |
     *  +-----------------+

     * Outside:
     * 10/11       12         1/2
     *     +----------------+
     *     |                |
     *   9 |                |  3
     *     |                |
     *     +----------------+
     *  7/8        6          4/5 
     * 
* * @param anchor the anchor. * @param bar the bar. * @param orientation the plot orientation. * * @return The anchor point. */ private Point2D calculateLabelAnchorPoint(ItemLabelAnchor anchor, Rectangle2D bar, PlotOrientation orientation) { Point2D result = null; RectangleInsets labelInsets = getItemLabelInsets(); Rectangle2D insideBar = labelInsets.createInsetRectangle(bar); Rectangle2D outsideBar = labelInsets.createOutsetRectangle(bar); if (anchor == ItemLabelAnchor.CENTER) { result = new Point2D.Double(bar.getCenterX(), bar.getCenterY()); } else if (anchor == ItemLabelAnchor.INSIDE1 || anchor == ItemLabelAnchor.INSIDE2) { result = new Point2D.Double(insideBar.getMaxX(), insideBar.getMinY()); } else if (anchor == ItemLabelAnchor.INSIDE3) { result = new Point2D.Double(insideBar.getMaxX(), bar.getCenterY()); } else if (anchor == ItemLabelAnchor.INSIDE4 || anchor == ItemLabelAnchor.INSIDE5) { result = new Point2D.Double(insideBar.getMaxX(), insideBar.getMaxY()); } else if (anchor == ItemLabelAnchor.INSIDE6) { result = new Point2D.Double(bar.getCenterX(), insideBar.getMaxY()); } else if (anchor == ItemLabelAnchor.INSIDE7 || anchor == ItemLabelAnchor.INSIDE8) { result = new Point2D.Double(insideBar.getMinX(), insideBar.getMaxY()); } else if (anchor == ItemLabelAnchor.INSIDE9) { result = new Point2D.Double(insideBar.getMinX(), bar.getCenterY()); } else if (anchor == ItemLabelAnchor.INSIDE10 || anchor == ItemLabelAnchor.INSIDE11) { result = new Point2D.Double(insideBar.getMinX(), insideBar.getMinY()); } else if (anchor == ItemLabelAnchor.INSIDE12) { result = new Point2D.Double(bar.getCenterX(), insideBar.getMinY()); } else if (anchor == ItemLabelAnchor.OUTSIDE1 || anchor == ItemLabelAnchor.OUTSIDE2) { result = new Point2D.Double(outsideBar.getMaxX(), outsideBar.getMinY()); } else if (anchor == ItemLabelAnchor.OUTSIDE3) { result = new Point2D.Double(outsideBar.getMaxX(), bar.getCenterY()); } else if (anchor == ItemLabelAnchor.OUTSIDE4 || anchor == ItemLabelAnchor.OUTSIDE5) { result = new Point2D.Double(outsideBar.getMaxX(), outsideBar.getMaxY()); } else if (anchor == ItemLabelAnchor.OUTSIDE6) { result = new Point2D.Double(bar.getCenterX(), outsideBar.getMaxY()); } else if (anchor == ItemLabelAnchor.OUTSIDE7 || anchor == ItemLabelAnchor.OUTSIDE8) { result = new Point2D.Double(outsideBar.getMinX(), outsideBar.getMaxY()); } else if (anchor == ItemLabelAnchor.OUTSIDE9) { result = new Point2D.Double(outsideBar.getMinX(), bar.getCenterY()); } else if (anchor == ItemLabelAnchor.OUTSIDE10 || anchor == ItemLabelAnchor.OUTSIDE11) { result = new Point2D.Double(outsideBar.getMinX(), outsideBar.getMinY()); } else if (anchor == ItemLabelAnchor.OUTSIDE12) { result = new Point2D.Double(bar.getCenterX(), outsideBar.getMinY()); } return result; } /** * Returns the lower and upper bounds (range) of the x-values in the * specified dataset. Since this renderer uses the x-interval in the * dataset, this is taken into account for the range. * * @param dataset the dataset ({@code null} permitted). * * @return The range ({@code null} if the dataset is * {@code null} or empty). */ @Override public Range findDomainBounds(XYDataset dataset) { return findDomainBounds(dataset, true); } /** * Returns the lower and upper bounds (range) of the y-values in the * specified dataset. If the renderer is plotting the y-interval from the * dataset, this is taken into account for the range. * * @param dataset the dataset ({@code null} permitted). * * @return The range ({@code null} if the dataset is * {@code null} or empty). */ @Override public Range findRangeBounds(XYDataset dataset) { return findRangeBounds(dataset, this.useYInterval); } /** * Returns a clone of the renderer. * * @return A clone. * * @throws CloneNotSupportedException if the renderer cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { XYBarRenderer result = (XYBarRenderer) super.clone(); if (this.gradientPaintTransformer != null) { result.gradientPaintTransformer = (GradientPaintTransformer) ObjectUtils.clone(this.gradientPaintTransformer); } result.legendBar = ShapeUtils.clone(this.legendBar); return result; } /** * Tests this renderer for equality with an arbitrary object. * * @param obj the object to test against ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYBarRenderer)) { return false; } XYBarRenderer that = (XYBarRenderer) obj; if (this.base != that.base) { return false; } if (this.drawBarOutline != that.drawBarOutline) { return false; } if (this.margin != that.margin) { return false; } if (this.useYInterval != that.useYInterval) { return false; } if (!Objects.equals(this.gradientPaintTransformer, that.gradientPaintTransformer)) { return false; } if (!ShapeUtils.equal(this.legendBar, that.legendBar)) { return false; } if (!Objects.equals(this.positiveItemLabelPositionFallback, that.positiveItemLabelPositionFallback)) { return false; } if (!Objects.equals(this.negativeItemLabelPositionFallback, that.negativeItemLabelPositionFallback)) { return false; } if (!this.barPainter.equals(that.barPainter)) { return false; } if (this.shadowsVisible != that.shadowsVisible) { return false; } if (this.shadowXOffset != that.shadowXOffset) { return false; } if (this.shadowYOffset != that.shadowYOffset) { return false; } if (this.barAlignmentFactor != that.barAlignmentFactor) { return false; } return super.equals(obj); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.legendBar = SerialUtils.readShape(stream); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeShape(this.legendBar, stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/XYBezierRenderer.java000066400000000000000000000552741463604235500313740ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * XYBezierRenderer.java * --------------------- * (C) Copyright, by Javier Robes and Contributors. * * Original Author: Javier Robes; * */ package org.jfree.chart.renderer.xy; import java.awt.GradientPaint; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.geom.GeneralPath; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.List; import java.util.Objects; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.GradientPaintTransformer; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.StandardGradientPaintTransformer; import org.jfree.chart.util.Args; import org.jfree.data.xy.XYDataset; /** * A renderer that connects data points with Bezier cubic curves and/or * draws shapes at each data point. This renderer is designed for use with * the {@link XYPlot} class. *

* * @since */ public class XYBezierRenderer extends XYLineAndShapeRenderer { /** * An enumeration of the fill types for the renderer. * * @since 1.0.17 */ public static enum FillType { /** No fill. */ NONE, /** Fill down to zero. */ TO_ZERO, /** Fill to the lower bound. */ TO_LOWER_BOUND, /** Fill to the upper bound. */ TO_UPPER_BOUND } /** * Represents state information that applies to a single rendering of * a chart. */ public static class XYBezierState extends State { /** The area to fill under the curve. */ public GeneralPath fillArea; /** The points. */ public List points; /** * Creates a new state instance. * * @param info the plot rendering info. */ public XYBezierState(PlotRenderingInfo info) { super(info); this.fillArea = new GeneralPath(); this.points = new ArrayList<>(); } } /** * Resolution of Bezier curves (number of line segments between points) */ private int precision; /** * Tension defines how sharply does the curve bends */ private double tension; /** * A flag that can be set to specify * to fill the area under the Bezier curve. */ private FillType fillType; private GradientPaintTransformer gradientPaintTransformer; /** * Creates a new instance with the precision attribute defaulting to 5, * the tension attribute defaulting to 2 * and no fill of the area 'under' the Bezier curve. */ public XYBezierRenderer() { this(5, 25, FillType.NONE); } /** * Creates a new renderer with the specified precision and tension * and no fill of the area 'under' (between '0' and) the Bezier curve. * * @param precision the number of points between data items. * @param tension value to define how sharply the curve bends */ public XYBezierRenderer(int precision, double tension) { this(precision, tension ,FillType.NONE); } /** * Creates a new renderer with the specified precision * and specified fill of the area 'under' (between '0' and) the Bezier curve. * * @param precision the number of points between data items. * @param tension value to define how sharply the Bezier curve bends * @param fillType the type of fill beneath the curve ({@code null} * not permitted). * * @since 1.0.17 */ public XYBezierRenderer(int precision, double tension, FillType fillType) { super(); if (precision <= 0) { throw new IllegalArgumentException("Requires precision > 0."); } if (tension <= 0) { throw new IllegalArgumentException("Requires precision > 0."); } Args.nullNotPermitted(fillType, "fillType"); this.precision = precision; this.tension = tension; this.fillType = fillType; this.gradientPaintTransformer = new StandardGradientPaintTransformer(); } /** * Returns the number of line segments used to approximate the Bezier * curve between data points. * * @return The number of line segments. * * @see #setPrecision(int) */ public int getPrecision() { return this.precision; } /** * Set the resolution of Bezier curves and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param p number of line segments between points (must be > 0). * * @see #getPrecision() */ public void setPrecision(int p) { if (p <= 0) { throw new IllegalArgumentException("Requires p > 0."); } this.precision = p; fireChangeEvent(); } /** * Returns the value of the tension which defines how sharply * does the curve bends * * @return The value of tesion. * * @see #setTension(double) */ public double getTension() { return this.tension; } /** * Set the value of the tension which defines how sharply * does the curve bends and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param t value of tension (must be > 0). * * @see #getTension() */ public void setTension(double t) { if (t <= 0) { throw new IllegalArgumentException("Requires tension > 0."); } this.tension = t; fireChangeEvent(); } /** * Returns the type of fill that the renderer draws beneath the curve. * * @return The type of fill (never {@code null}). * * @see #setFillType(FillType) * * @since 1.0.17 */ public FillType getFillType() { return this.fillType; } /** * Set the fill type and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param fillType the fill type ({@code null} not permitted). * * @see #getFillType() * * @since 1.0.17 */ public void setFillType(FillType fillType) { this.fillType = fillType; fireChangeEvent(); } /** * Returns the gradient paint transformer, or {@code null}. * * @return The gradient paint transformer (possibly {@code null}). * * @since 1.0.17 */ public GradientPaintTransformer getGradientPaintTransformer() { return this.gradientPaintTransformer; } /** * Sets the gradient paint transformer and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param gpt the transformer ({@code null} permitted). * * @since 1.0.17 */ public void setGradientPaintTransformer(GradientPaintTransformer gpt) { this.gradientPaintTransformer = gpt; fireChangeEvent(); } /** * Initialises the renderer. *

* This method will be called before the first item is rendered, giving the * renderer an opportunity to initialise any state information it wants to * maintain. The renderer can do nothing if it chooses. * * @param g2 the graphics device. * @param dataArea the area inside the axes. * @param plot the plot. * @param data the data. * @param info an optional info collection object to return data back to * the caller. * * @return The renderer state. */ @Override public XYItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, XYPlot plot, XYDataset data, PlotRenderingInfo info) { setDrawSeriesLineAsPath(true); XYBezierState state = new XYBezierState(info); state.setProcessVisibleItemsOnly(false); return state; } /** * Draws the item (first pass). This method draws the lines * connecting the items. Instead of drawing separate lines, * a GeneralPath is constructed and drawn at the end of * the series painting. * * @param g2 the graphics device. * @param state the renderer state. * @param plot the plot (can be used to obtain standard color information * etc). * @param dataset the dataset. * @param pass the pass. * @param series the series index (zero-based). * @param item the item index (zero-based). * @param xAxis the domain axis. * @param yAxis the range axis. * @param dataArea the area within which the data is being drawn. */ @Override protected void drawPrimaryLineAsPath(XYItemRendererState state, Graphics2D g2, XYPlot plot, XYDataset dataset, int pass, int series, int item, ValueAxis xAxis, ValueAxis yAxis, Rectangle2D dataArea) { XYBezierState s = (XYBezierState) state; RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); // get the data points double x1 = dataset.getXValue(series, item); double y1 = dataset.getYValue(series, item); double transX1 = xAxis.valueToJava2D(x1, dataArea, xAxisLocation); double transY1 = yAxis.valueToJava2D(y1, dataArea, yAxisLocation); // Collect points if (!Double.isNaN(transX1) && !Double.isNaN(transY1)) { Point2D p = plot.getOrientation() == PlotOrientation.HORIZONTAL ? new Point2D.Float((float) transY1, (float) transX1) : new Point2D.Float((float) transX1, (float) transY1); if (!s.points.contains(p)) s.points.add(p); } if (item == dataset.getItemCount(series) - 1) { // construct path if (s.points.size() > 1) { Point2D origin; if (this.fillType == FillType.TO_ZERO) { float xz = (float) xAxis.valueToJava2D(0, dataArea, yAxisLocation); float yz = (float) yAxis.valueToJava2D(0, dataArea, yAxisLocation); origin = plot.getOrientation() == PlotOrientation.HORIZONTAL ? new Point2D.Float(yz, xz) : new Point2D.Float(xz, yz); } else if (this.fillType == FillType.TO_LOWER_BOUND) { float xlb = (float) xAxis.valueToJava2D( xAxis.getLowerBound(), dataArea, xAxisLocation); float ylb = (float) yAxis.valueToJava2D( yAxis.getLowerBound(), dataArea, yAxisLocation); origin = plot.getOrientation() == PlotOrientation.HORIZONTAL ? new Point2D.Float(ylb, xlb) : new Point2D.Float(xlb, ylb); } else {// fillType == TO_UPPER_BOUND float xub = (float) xAxis.valueToJava2D( xAxis.getUpperBound(), dataArea, xAxisLocation); float yub = (float) yAxis.valueToJava2D( yAxis.getUpperBound(), dataArea, yAxisLocation); origin = plot.getOrientation() == PlotOrientation.HORIZONTAL ? new Point2D.Float(yub, xub) : new Point2D.Float(xub, yub); } // we need at least two points to draw something Point2D cp0 = s.points.get(0); s.seriesPath.moveTo(cp0.getX(), cp0.getY()); if (this.fillType != FillType.NONE) { if (plot.getOrientation() == PlotOrientation.HORIZONTAL) { s.fillArea.moveTo(origin.getX(), cp0.getY()); } else { s.fillArea.moveTo(cp0.getX(), origin.getY()); } s.fillArea.lineTo(cp0.getX(), cp0.getY()); } if (s.points.size() == 2) { // we need at least 3 points to Bezier. Draw simple line // for two points Point2D cp1 = s.points.get(1); if (this.fillType != FillType.NONE) { s.fillArea.lineTo(cp1.getX(), cp1.getY()); s.fillArea.lineTo(cp1.getX(), origin.getY()); s.fillArea.closePath(); } s.seriesPath.lineTo(cp1.getX(), cp1.getY()); } else if (s.points.size() == 3) { // with 3 points only initial and end Bezier curves are required. Point2D[] pInitial = getInitalPoints(s); addBezierPointsToSeriesPath(pInitial, s); Point2D[] pFinal = getFinalPoints(s); addBezierPointsToSeriesPath(pFinal, s); } else { // construct Bezier curve int np = s.points.size(); // number of points for(int i = 0; i < np - 1; i++) { if(i == 0) { // 3 points, 2 lines (initial an final Bezier curves) Point2D[] initial3Points = new Point2D[3]; initial3Points[0] = s.points.get(0); initial3Points[1] = s.points.get(1); initial3Points[2] = s.points.get(2); Point2D[] pInitial = calcSegmentPointsInitial(initial3Points); addBezierPointsToSeriesPath(pInitial, s); } if(i == np - 2) { Point2D[] final3Points = new Point2D[4]; final3Points[1] = s.points.get(np-3); final3Points[2] = s.points.get(np-2); final3Points[3] = s.points.get(np-1); // No need for final3Points[0]. Not required Point2D[] pFinal = calcSegmentPointsFinal(final3Points); addBezierPointsToSeriesPath(pFinal, s); } if ((i != 0) && (i != (np - 2))){ Point2D[] original4Points = new Point2D[4]; original4Points[0] = s.points.get(i - 1); original4Points[1] = s.points.get(i); original4Points[2] = s.points.get(i + 1); original4Points[3] = s.points.get(i + 2); Point2D[] pMedium = calculateSegmentPoints(original4Points); addBezierPointsToSeriesPath(pMedium, s); } } } // Add last point @ y=0 for fillPath and close path if (this.fillType != FillType.NONE) { if (plot.getOrientation() == PlotOrientation.HORIZONTAL) { s.fillArea.lineTo(origin.getX(), s.points.get( s.points.size() - 1).getY()); } else { s.fillArea.lineTo(s.points.get( s.points.size() - 1).getX(), origin.getY()); } s.fillArea.closePath(); } // fill under the curve... if (this.fillType != FillType.NONE) { Paint fp = getSeriesFillPaint(series); if (this.gradientPaintTransformer != null && fp instanceof GradientPaint) { GradientPaint gp = this.gradientPaintTransformer .transform((GradientPaint) fp, s.fillArea); g2.setPaint(gp); } else { g2.setPaint(fp); } g2.fill(s.fillArea); s.fillArea.reset(); } // then draw the line... drawFirstPassShape(g2, pass, series, item, s.seriesPath); } // reset points vector s.points = new ArrayList<>(); } } private void addBezierPointsToSeriesPath(Point2D[] segmentPoints, XYBezierState s) { double x; double y; for (int t = 0 ; t <= this.precision; t++) { double k = (double)t / this.precision; double r = 1- k; x = Math.pow(r, 3) * segmentPoints[0].getX() + 3 * k * Math.pow(r, 2) * segmentPoints[1].getX() + 3 * Math.pow(k, 2) * (1 - k) * segmentPoints[2].getX() + Math.pow(k, 3) * segmentPoints[3].getX(); y = Math.pow(r, 3) * segmentPoints[0].getY() + 3 * k * Math.pow(r, 2) * segmentPoints[1].getY() + 3 * Math.pow(k, 2) * (1 - k) * segmentPoints[2].getY() + Math.pow(k, 3) * segmentPoints[3].getY(); s.seriesPath.lineTo(x, y); if (this.fillType != FillType.NONE) { s.fillArea.lineTo(x, y); } } } private Point2D[] getFinalPoints(XYBezierState s) { Point2D[] final3Points = new Point2D[4]; final3Points[1] = s.points.get(0); final3Points[2] = s.points.get(1); final3Points[3] = s.points.get(2); // No need for final3Points[0]. Not required Point2D[] pFinal = calcSegmentPointsFinal(final3Points);//TENSION = 1.5 return pFinal; } private Point2D[] getInitalPoints(XYBezierState s) { Point2D[] initial3Points = new Point2D[3]; initial3Points[0] = s.points.get(0); initial3Points[1] = s.points.get(1); initial3Points[2] = s.points.get(2); Point2D[] pInitial = calcSegmentPointsInitial(initial3Points);// TENSION = 1.5 return pInitial; } private Point2D[] calculateSegmentPoints(Point2D[] original4Points) { Point2D[] points = new Point2D[4]; points[0] = original4Points[1]; points[3] = original4Points[2]; for(int i = 1; i < 3; i++) { Point2D aux1 = calcUnitaryVector(original4Points[i-1], original4Points[i]); Point2D aux2 = calcUnitaryVector(original4Points[i+1], original4Points[i]); Point2D aux3 = calcUnitaryVector(aux2, aux1); double x = original4Points[i].getX() + Math.pow(-1.0, i+1) * tension * aux3.getX(); double y = original4Points[i].getY() + Math.pow(-1.0, i+1) * tension * aux3.getY(); points[i] = new Point2D.Double(x, y); } return points; } private Point2D[] calcSegmentPointsInitial(Point2D[] original3P) { Point2D[] points = new Point2D[4]; points[0] = original3P[0];// Endpoint 1 points[3] = original3P[1];// Endpoint 2 // Control point 1 Point2D auxInitial = calcUnitaryVector(original3P[0], original3P[1]); points[1] = original3P[0];// new Point2D.Double(x0, y0); // Control point 2 Point2D aux2 = calcUnitaryVector(original3P[2], original3P[1]); Point2D aux3 = calcUnitaryVector(auxInitial, aux2); double x = original3P[1].getX() + tension * aux3.getX(); double y = original3P[1].getY() + tension * aux3.getY(); points[2] = new Point2D.Double(x, y); return points; } private Point2D[] calcSegmentPointsFinal(Point2D[] original3P) { /* * Each segment is defined by its two endpoints and two control points. A * control point determines the tangent at the corresponding endpoint. */ Point2D[] points = new Point2D[4]; points[0] = original3P[2];// Endpoint 1 points[3] = original3P[3];// Endpoint 2 // Control point 2: points[2] Point2D auxInitial = calcUnitaryVector(original3P[3], original3P[2]); points[2] = original3P[3];// new Point2D.Double(x0, y0); // Control point 1 Point2D aux1 = calcUnitaryVector(original3P[3], original3P[2]); Point2D aux2 = calcUnitaryVector(original3P[1], original3P[2]); Point2D aux3 = calcUnitaryVector(aux1, aux2); double x = original3P[2].getX() + tension * aux3.getX(); double y = original3P[2].getY() + tension * aux3.getY(); points[1] = new Point2D.Double(x, y); return points; } private Point2D calcUnitaryVector(Point2D pOrigin, Point2D pEnd) { double module = Math.sqrt(Math.pow(pEnd.getX() - pOrigin.getX(), 2) + Math.pow(pEnd.getY() - pOrigin.getY(), 2)); if (module == 0) { return null; } return new Point2D.Double((pEnd.getX() - pOrigin.getX()) / module, (pEnd.getY() - pOrigin.getY()) /module); } /** * Tests this renderer for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYBezierRenderer)) { return false; } XYBezierRenderer that = (XYBezierRenderer) obj; if (this.precision != that.precision) { return false; } if (this.fillType != that.fillType) { return false; } if (!Objects.equals(this.gradientPaintTransformer, that.gradientPaintTransformer)) { return false; } return super.equals(obj); } }jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/XYBlockRenderer.java000066400000000000000000000357021463604235500312000ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * XYBlockRenderer.java * -------------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import java.awt.BasicStroke; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.geom.Rectangle2D; import java.io.Serializable; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.plot.CrosshairState; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.LookupPaintScale; import org.jfree.chart.renderer.PaintScale; import org.jfree.chart.ui.RectangleAnchor; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.Range; import org.jfree.data.general.DatasetUtils; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYZDataset; /** * A renderer that represents data from an {@link XYZDataset} by drawing a * color block at each (x, y) point, where the color is a function of the * z-value from the dataset. The example shown here is generated by the * {@code XYBlockChartDemo1.java} program included in the JFreeChart * demo collection: *

* XYBlockRendererSample.png */ public class XYBlockRenderer extends AbstractXYItemRenderer implements XYItemRenderer, Cloneable, PublicCloneable, Serializable { /** * The block width (defaults to 1.0). */ private double blockWidth = 1.0; /** * The block height (defaults to 1.0). */ private double blockHeight = 1.0; /** * The anchor point used to align each block to its (x, y) location. The * default value is {@code RectangleAnchor.CENTER}. */ private RectangleAnchor blockAnchor = RectangleAnchor.CENTER; /** Temporary storage for the x-offset used to align the block anchor. */ private double xOffset; /** Temporary storage for the y-offset used to align the block anchor. */ private double yOffset; /** The paint scale. */ private PaintScale paintScale; /** * Creates a new {@code XYBlockRenderer} instance with default * attributes. */ public XYBlockRenderer() { updateOffsets(); this.paintScale = new LookupPaintScale(); } /** * Returns the block width, in data/axis units. * * @return The block width. * * @see #setBlockWidth(double) */ public double getBlockWidth() { return this.blockWidth; } /** * Sets the width of the blocks used to represent each data item and * sends a {@link RendererChangeEvent} to all registered listeners. * * @param width the new width, in data/axis units (must be > 0.0). * * @see #getBlockWidth() */ public void setBlockWidth(double width) { if (width <= 0.0) { throw new IllegalArgumentException( "The 'width' argument must be > 0.0"); } this.blockWidth = width; updateOffsets(); fireChangeEvent(); } /** * Returns the block height, in data/axis units. * * @return The block height. * * @see #setBlockHeight(double) */ public double getBlockHeight() { return this.blockHeight; } /** * Sets the height of the blocks used to represent each data item and * sends a {@link RendererChangeEvent} to all registered listeners. * * @param height the new height, in data/axis units (must be > 0.0). * * @see #getBlockHeight() */ public void setBlockHeight(double height) { if (height <= 0.0) { throw new IllegalArgumentException( "The 'height' argument must be > 0.0"); } this.blockHeight = height; updateOffsets(); fireChangeEvent(); } /** * Returns the anchor point used to align a block at its (x, y) location. * The default values is {@link RectangleAnchor#CENTER}. * * @return The anchor point (never {@code null}). * * @see #setBlockAnchor(RectangleAnchor) */ public RectangleAnchor getBlockAnchor() { return this.blockAnchor; } /** * Sets the anchor point used to align a block at its (x, y) location and * sends a {@link RendererChangeEvent} to all registered listeners. * * @param anchor the anchor. * * @see #getBlockAnchor() */ public void setBlockAnchor(RectangleAnchor anchor) { Args.nullNotPermitted(anchor, "anchor"); if (this.blockAnchor.equals(anchor)) { return; // no change } this.blockAnchor = anchor; updateOffsets(); fireChangeEvent(); } /** * Returns the paint scale used by the renderer. * * @return The paint scale (never {@code null}). * * @see #setPaintScale(PaintScale) */ public PaintScale getPaintScale() { return this.paintScale; } /** * Sets the paint scale used by the renderer and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param scale the scale ({@code null} not permitted). * * @see #getPaintScale() */ public void setPaintScale(PaintScale scale) { Args.nullNotPermitted(scale, "scale"); this.paintScale = scale; fireChangeEvent(); } /** * Updates the offsets to take into account the block width, height and * anchor. */ private void updateOffsets() { if (this.blockAnchor.equals(RectangleAnchor.BOTTOM_LEFT)) { this.xOffset = 0.0; this.yOffset = 0.0; } else if (this.blockAnchor.equals(RectangleAnchor.BOTTOM)) { this.xOffset = -this.blockWidth / 2.0; this.yOffset = 0.0; } else if (this.blockAnchor.equals(RectangleAnchor.BOTTOM_RIGHT)) { this.xOffset = -this.blockWidth; this.yOffset = 0.0; } else if (this.blockAnchor.equals(RectangleAnchor.LEFT)) { this.xOffset = 0.0; this.yOffset = -this.blockHeight / 2.0; } else if (this.blockAnchor.equals(RectangleAnchor.CENTER)) { this.xOffset = -this.blockWidth / 2.0; this.yOffset = -this.blockHeight / 2.0; } else if (this.blockAnchor.equals(RectangleAnchor.RIGHT)) { this.xOffset = -this.blockWidth; this.yOffset = -this.blockHeight / 2.0; } else if (this.blockAnchor.equals(RectangleAnchor.TOP_LEFT)) { this.xOffset = 0.0; this.yOffset = -this.blockHeight; } else if (this.blockAnchor.equals(RectangleAnchor.TOP)) { this.xOffset = -this.blockWidth / 2.0; this.yOffset = -this.blockHeight; } else if (this.blockAnchor.equals(RectangleAnchor.TOP_RIGHT)) { this.xOffset = -this.blockWidth; this.yOffset = -this.blockHeight; } } /** * Returns the lower and upper bounds (range) of the x-values in the * specified dataset. * * @param dataset the dataset ({@code null} permitted). * * @return The range ({@code null} if the dataset is {@code null} * or empty). * * @see #findRangeBounds(XYDataset) */ @Override public Range findDomainBounds(XYDataset dataset) { if (dataset == null) { return null; } Range r = DatasetUtils.findDomainBounds(dataset, false); if (r == null) { return null; } return new Range(r.getLowerBound() + this.xOffset, r.getUpperBound() + this.blockWidth + this.xOffset); } /** * Returns the range of values the renderer requires to display all the * items from the specified dataset. * * @param dataset the dataset ({@code null} permitted). * * @return The range ({@code null} if the dataset is {@code null} * or empty). * * @see #findDomainBounds(XYDataset) */ @Override public Range findRangeBounds(XYDataset dataset) { if (dataset != null) { Range r = DatasetUtils.findRangeBounds(dataset, false); if (r == null) { return null; } else { return new Range(r.getLowerBound() + this.yOffset, r.getUpperBound() + this.blockHeight + this.yOffset); } } else { return null; } } /** * Draws the block representing the specified item. * * @param g2 the graphics device. * @param state the state. * @param dataArea the data area. * @param info the plot rendering info. * @param plot the plot. * @param domainAxis the x-axis. * @param rangeAxis the y-axis. * @param dataset the dataset. * @param series the series index. * @param item the item index. * @param crosshairState the crosshair state. * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item, CrosshairState crosshairState, int pass) { double x = dataset.getXValue(series, item); double y = dataset.getYValue(series, item); double z = 0.0; if (dataset instanceof XYZDataset) { z = ((XYZDataset) dataset).getZValue(series, item); } Paint p = this.paintScale.getPaint(z); double xx0 = domainAxis.valueToJava2D(x + this.xOffset, dataArea, plot.getDomainAxisEdge()); double yy0 = rangeAxis.valueToJava2D(y + this.yOffset, dataArea, plot.getRangeAxisEdge()); double xx1 = domainAxis.valueToJava2D(x + this.blockWidth + this.xOffset, dataArea, plot.getDomainAxisEdge()); double yy1 = rangeAxis.valueToJava2D(y + this.blockHeight + this.yOffset, dataArea, plot.getRangeAxisEdge()); Rectangle2D block; PlotOrientation orientation = plot.getOrientation(); if (orientation.equals(PlotOrientation.HORIZONTAL)) { block = new Rectangle2D.Double(Math.min(yy0, yy1), Math.min(xx0, xx1), Math.abs(yy1 - yy0), Math.abs(xx0 - xx1)); } else { block = new Rectangle2D.Double(Math.min(xx0, xx1), Math.min(yy0, yy1), Math.abs(xx1 - xx0), Math.abs(yy1 - yy0)); } g2.setPaint(p); g2.fill(block); g2.setStroke(new BasicStroke(1.0f)); g2.draw(block); if (isItemLabelVisible(series, item)) { drawItemLabel(g2, orientation, dataset, series, item, block.getCenterX(), block.getCenterY(), y < 0.0); } int datasetIndex = plot.indexOf(dataset); double transX = domainAxis.valueToJava2D(x, dataArea, plot.getDomainAxisEdge()); double transY = rangeAxis.valueToJava2D(y, dataArea, plot.getRangeAxisEdge()); updateCrosshairValues(crosshairState, x, y, datasetIndex, transX, transY, orientation); EntityCollection entities = state.getEntityCollection(); if (entities != null) { addEntity(entities, block, dataset, series, item, block.getCenterX(), block.getCenterY()); } } /** * Tests this {@code XYBlockRenderer} for equality with an arbitrary * object. This method returns {@code true} if and only if: *

    *
  • {@code obj} is an instance of {@code XYBlockRenderer} (not * {@code null});
  • *
  • {@code obj} has the same field values as this * {@code XYBlockRenderer};
  • *
* * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYBlockRenderer)) { return false; } XYBlockRenderer that = (XYBlockRenderer) obj; if (this.blockHeight != that.blockHeight) { return false; } if (this.blockWidth != that.blockWidth) { return false; } if (!this.blockAnchor.equals(that.blockAnchor)) { return false; } if (!this.paintScale.equals(that.paintScale)) { return false; } return super.equals(obj); } /** * Returns a clone of this renderer. * * @return A clone of this renderer. * * @throws CloneNotSupportedException if there is a problem creating the * clone. */ @Override public Object clone() throws CloneNotSupportedException { XYBlockRenderer clone = (XYBlockRenderer) super.clone(); if (this.paintScale instanceof PublicCloneable) { PublicCloneable pc = (PublicCloneable) this.paintScale; clone.paintScale = (PaintScale) pc.clone(); } return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/XYBoxAndWhiskerRenderer.java000066400000000000000000000721731463604235500326610ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------- * XYBoxAndWhiskerRenderer.java * ---------------------------- * (C) Copyright 2003-present, by David Browning and Contributors. * * Original Author: David Browning (for Australian Institute of Marine * Science); * Contributor(s): David Gilbert; * */ package org.jfree.chart.renderer.xy; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Ellipse2D; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.labels.BoxAndWhiskerXYToolTipGenerator; import org.jfree.chart.plot.CrosshairState; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.Outlier; import org.jfree.chart.renderer.OutlierList; import org.jfree.chart.renderer.OutlierListCollection; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; import org.jfree.data.Range; import org.jfree.data.statistics.BoxAndWhiskerXYDataset; import org.jfree.data.xy.XYDataset; /** * A renderer that draws box-and-whisker items on an {@link XYPlot}. This * renderer requires a {@link BoxAndWhiskerXYDataset}). The example shown here * is generated by the{@code BoxAndWhiskerChartDemo2.java} program * included in the JFreeChart demo collection: *

* XYBoxAndWhiskerRendererSample.png *

* This renderer does not include any code to calculate the crosshair point. */ public class XYBoxAndWhiskerRenderer extends AbstractXYItemRenderer implements XYItemRenderer, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -8020170108532232324L; /** The box width. */ private double boxWidth; /** The paint used to fill the box. */ private transient Paint boxPaint; /** A flag that controls whether or not the box is filled. */ private boolean fillBox; /** * The paint used to draw various artifacts such as outliers, farout * symbol, average ellipse and median line. */ private transient Paint artifactPaint = Color.BLACK; /** * Creates a new renderer for box and whisker charts. */ public XYBoxAndWhiskerRenderer() { this(-1.0); } /** * Creates a new renderer for box and whisker charts. *

* Use -1 for the box width if you prefer the width to be calculated * automatically. * * @param boxWidth the box width. */ public XYBoxAndWhiskerRenderer(double boxWidth) { super(); this.boxWidth = boxWidth; this.boxPaint = Color.GREEN; this.fillBox = true; setDefaultToolTipGenerator(new BoxAndWhiskerXYToolTipGenerator()); } /** * Returns the width of each box. * * @return The box width. * * @see #setBoxWidth(double) */ public double getBoxWidth() { return this.boxWidth; } /** * Sets the box width and sends a {@link RendererChangeEvent} to all * registered listeners. *

* If you set the width to a negative value, the renderer will calculate * the box width automatically based on the space available on the chart. * * @param width the width. * * @see #getBoxWidth() */ public void setBoxWidth(double width) { if (width != this.boxWidth) { this.boxWidth = width; fireChangeEvent(); } } /** * Returns the paint used to fill boxes. * * @return The paint (possibly {@code null}). * * @see #setBoxPaint(Paint) */ public Paint getBoxPaint() { return this.boxPaint; } /** * Sets the paint used to fill boxes and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param paint the paint ({@code null} permitted). * * @see #getBoxPaint() */ public void setBoxPaint(Paint paint) { this.boxPaint = paint; fireChangeEvent(); } /** * Returns the flag that controls whether or not the box is filled. * * @return A boolean. * * @see #setFillBox(boolean) */ public boolean getFillBox() { return this.fillBox; } /** * Sets the flag that controls whether or not the box is filled and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param flag the flag. * * @see #setFillBox(boolean) */ public void setFillBox(boolean flag) { this.fillBox = flag; fireChangeEvent(); } /** * Returns the paint used to paint the various artifacts such as outliers, * farout symbol, median line and the averages ellipse. * * @return The paint (never {@code null}). * * @see #setArtifactPaint(Paint) */ public Paint getArtifactPaint() { return this.artifactPaint; } /** * Sets the paint used to paint the various artifacts such as outliers, * farout symbol, median line and the averages ellipse, and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getArtifactPaint() */ public void setArtifactPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.artifactPaint = paint; fireChangeEvent(); } /** * Returns the range of values the renderer requires to display all the * items from the specified dataset. * * @param dataset the dataset ({@code null} permitted). * * @return The range ({@code null} if the dataset is {@code null} * or empty). * * @see #findDomainBounds(XYDataset) */ @Override public Range findRangeBounds(XYDataset dataset) { return findRangeBounds(dataset, true); } /** * Returns the box paint or, if this is {@code null}, the item * paint. * * @param series the series index. * @param item the item index. * * @return The paint used to fill the box for the specified item (never * {@code null}). */ protected Paint lookupBoxPaint(int series, int item) { Paint p = getBoxPaint(); if (p != null) { return p; } else { // TODO: could change this to itemFillPaint(). For backwards // compatibility, it might require a useFillPaint flag. return getItemPaint(series, item); } } /** * Draws the visual representation of a single data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the area within which the plot is being drawn. * @param info collects info about the drawing. * @param plot the plot (can be used to obtain standard color * information etc). * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset (must be an instance of * {@link BoxAndWhiskerXYDataset}). * @param series the series index (zero-based). * @param item the item index (zero-based). * @param crosshairState crosshair information for the plot * ({@code null} permitted). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item, CrosshairState crosshairState, int pass) { PlotOrientation orientation = plot.getOrientation(); if (orientation == PlotOrientation.HORIZONTAL) { drawHorizontalItem(g2, dataArea, info, plot, domainAxis, rangeAxis, dataset, series, item, crosshairState, pass); } else if (orientation == PlotOrientation.VERTICAL) { drawVerticalItem(g2, dataArea, info, plot, domainAxis, rangeAxis, dataset, series, item, crosshairState, pass); } } /** * Draws the visual representation of a single data item. * * @param g2 the graphics device. * @param dataArea the area within which the plot is being drawn. * @param info collects info about the drawing. * @param plot the plot (can be used to obtain standard color * information etc). * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset (must be an instance of * {@link BoxAndWhiskerXYDataset}). * @param series the series index (zero-based). * @param item the item index (zero-based). * @param crosshairState crosshair information for the plot * ({@code null} permitted). * @param pass the pass index. */ public void drawHorizontalItem(Graphics2D g2, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item, CrosshairState crosshairState, int pass) { // setup for collecting optional entity info... EntityCollection entities = null; if (info != null) { entities = info.getOwner().getEntityCollection(); } BoxAndWhiskerXYDataset boxAndWhiskerData = (BoxAndWhiskerXYDataset) dataset; Number x = boxAndWhiskerData.getX(series, item); Number yMax = boxAndWhiskerData.getMaxRegularValue(series, item); Number yMin = boxAndWhiskerData.getMinRegularValue(series, item); Number yMedian = boxAndWhiskerData.getMedianValue(series, item); Number yAverage = boxAndWhiskerData.getMeanValue(series, item); Number yQ1Median = boxAndWhiskerData.getQ1Value(series, item); Number yQ3Median = boxAndWhiskerData.getQ3Value(series, item); double xx = domainAxis.valueToJava2D(x.doubleValue(), dataArea, plot.getDomainAxisEdge()); RectangleEdge location = plot.getRangeAxisEdge(); double yyMax = rangeAxis.valueToJava2D(yMax.doubleValue(), dataArea, location); double yyMin = rangeAxis.valueToJava2D(yMin.doubleValue(), dataArea, location); double yyMedian = rangeAxis.valueToJava2D(yMedian.doubleValue(), dataArea, location); double yyAverage = 0.0; if (yAverage != null) { yyAverage = rangeAxis.valueToJava2D(yAverage.doubleValue(), dataArea, location); } double yyQ1Median = rangeAxis.valueToJava2D(yQ1Median.doubleValue(), dataArea, location); double yyQ3Median = rangeAxis.valueToJava2D(yQ3Median.doubleValue(), dataArea, location); double exactBoxWidth = getBoxWidth(); double width = exactBoxWidth; double dataAreaX = dataArea.getHeight(); double maxBoxPercent = 0.1; double maxBoxWidth = dataAreaX * maxBoxPercent; if (exactBoxWidth <= 0.0) { int itemCount = boxAndWhiskerData.getItemCount(series); exactBoxWidth = dataAreaX / itemCount * 4.5 / 7; if (exactBoxWidth < 3) { width = 3; } else if (exactBoxWidth > maxBoxWidth) { width = maxBoxWidth; } else { width = exactBoxWidth; } } g2.setPaint(getItemPaint(series, item)); Stroke s = getItemStroke(series, item); g2.setStroke(s); // draw the upper shadow g2.draw(new Line2D.Double(yyMax, xx, yyQ3Median, xx)); g2.draw(new Line2D.Double(yyMax, xx - width / 2, yyMax, xx + width / 2)); // draw the lower shadow g2.draw(new Line2D.Double(yyMin, xx, yyQ1Median, xx)); g2.draw(new Line2D.Double(yyMin, xx - width / 2, yyMin, xx + width / 2)); // draw the body Shape box; if (yyQ1Median < yyQ3Median) { box = new Rectangle2D.Double(yyQ1Median, xx - width / 2, yyQ3Median - yyQ1Median, width); } else { box = new Rectangle2D.Double(yyQ3Median, xx - width / 2, yyQ1Median - yyQ3Median, width); } if (this.fillBox) { g2.setPaint(lookupBoxPaint(series, item)); g2.fill(box); } g2.setStroke(getItemOutlineStroke(series, item)); g2.setPaint(getItemOutlinePaint(series, item)); g2.draw(box); // draw median g2.setPaint(getArtifactPaint()); g2.draw(new Line2D.Double(yyMedian, xx - width / 2, yyMedian, xx + width / 2)); // draw average - SPECIAL AIMS REQUIREMENT if (yAverage != null) { double aRadius = width / 4; // here we check that the average marker will in fact be visible // before drawing it... if ((yyAverage > (dataArea.getMinX() - aRadius)) && (yyAverage < (dataArea.getMaxX() + aRadius))) { Ellipse2D.Double avgEllipse = new Ellipse2D.Double( yyAverage - aRadius, xx - aRadius, aRadius * 2, aRadius * 2); g2.fill(avgEllipse); g2.draw(avgEllipse); } } // FIXME: draw outliers // add an entity for the item... if (entities != null && box.intersects(dataArea)) { addEntity(entities, box, dataset, series, item, yyAverage, xx); } } /** * Draws the visual representation of a single data item. * * @param g2 the graphics device. * @param dataArea the area within which the plot is being drawn. * @param info collects info about the drawing. * @param plot the plot (can be used to obtain standard color * information etc). * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset (must be an instance of * {@link BoxAndWhiskerXYDataset}). * @param series the series index (zero-based). * @param item the item index (zero-based). * @param crosshairState crosshair information for the plot * ({@code null} permitted). * @param pass the pass index. */ public void drawVerticalItem(Graphics2D g2, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item, CrosshairState crosshairState, int pass) { // setup for collecting optional entity info... EntityCollection entities = null; if (info != null) { entities = info.getOwner().getEntityCollection(); } BoxAndWhiskerXYDataset boxAndWhiskerData = (BoxAndWhiskerXYDataset) dataset; Number x = boxAndWhiskerData.getX(series, item); Number yMax = boxAndWhiskerData.getMaxRegularValue(series, item); Number yMin = boxAndWhiskerData.getMinRegularValue(series, item); Number yMedian = boxAndWhiskerData.getMedianValue(series, item); Number yAverage = boxAndWhiskerData.getMeanValue(series, item); Number yQ1Median = boxAndWhiskerData.getQ1Value(series, item); Number yQ3Median = boxAndWhiskerData.getQ3Value(series, item); List yOutliers = boxAndWhiskerData.getOutliers(series, item); // yOutliers can be null, but we'd prefer it to be an empty list in // that case... if (yOutliers == null) { yOutliers = Collections.EMPTY_LIST; } double xx = domainAxis.valueToJava2D(x.doubleValue(), dataArea, plot.getDomainAxisEdge()); RectangleEdge location = plot.getRangeAxisEdge(); double yyMax = rangeAxis.valueToJava2D(yMax.doubleValue(), dataArea, location); double yyMin = rangeAxis.valueToJava2D(yMin.doubleValue(), dataArea, location); double yyMedian = rangeAxis.valueToJava2D(yMedian.doubleValue(), dataArea, location); double yyAverage = 0.0; if (yAverage != null) { yyAverage = rangeAxis.valueToJava2D(yAverage.doubleValue(), dataArea, location); } double yyQ1Median = rangeAxis.valueToJava2D(yQ1Median.doubleValue(), dataArea, location); double yyQ3Median = rangeAxis.valueToJava2D(yQ3Median.doubleValue(), dataArea, location); double yyOutlier; double exactBoxWidth = getBoxWidth(); double width = exactBoxWidth; double dataAreaX = dataArea.getMaxX() - dataArea.getMinX(); double maxBoxPercent = 0.1; double maxBoxWidth = dataAreaX * maxBoxPercent; if (exactBoxWidth <= 0.0) { int itemCount = boxAndWhiskerData.getItemCount(series); exactBoxWidth = dataAreaX / itemCount * 4.5 / 7; if (exactBoxWidth < 3) { width = 3; } else if (exactBoxWidth > maxBoxWidth) { width = maxBoxWidth; } else { width = exactBoxWidth; } } g2.setPaint(getItemPaint(series, item)); Stroke s = getItemStroke(series, item); g2.setStroke(s); // draw the upper shadow g2.draw(new Line2D.Double(xx, yyMax, xx, yyQ3Median)); g2.draw(new Line2D.Double(xx - width / 2, yyMax, xx + width / 2, yyMax)); // draw the lower shadow g2.draw(new Line2D.Double(xx, yyMin, xx, yyQ1Median)); g2.draw(new Line2D.Double(xx - width / 2, yyMin, xx + width / 2, yyMin)); // draw the body Shape box; if (yyQ1Median > yyQ3Median) { box = new Rectangle2D.Double(xx - width / 2, yyQ3Median, width, yyQ1Median - yyQ3Median); } else { box = new Rectangle2D.Double(xx - width / 2, yyQ1Median, width, yyQ3Median - yyQ1Median); } if (this.fillBox) { g2.setPaint(lookupBoxPaint(series, item)); g2.fill(box); } g2.setStroke(getItemOutlineStroke(series, item)); g2.setPaint(getItemOutlinePaint(series, item)); g2.draw(box); // draw median g2.setPaint(getArtifactPaint()); g2.draw(new Line2D.Double(xx - width / 2, yyMedian, xx + width / 2, yyMedian)); double aRadius = 0; // average radius double oRadius = width / 3; // outlier radius // draw average - SPECIAL AIMS REQUIREMENT if (yAverage != null) { aRadius = width / 4; // here we check that the average marker will in fact be visible // before drawing it... if ((yyAverage > (dataArea.getMinY() - aRadius)) && (yyAverage < (dataArea.getMaxY() + aRadius))) { Ellipse2D.Double avgEllipse = new Ellipse2D.Double(xx - aRadius, yyAverage - aRadius, aRadius * 2, aRadius * 2); g2.fill(avgEllipse); g2.draw(avgEllipse); } } List outliers = new ArrayList(); OutlierListCollection outlierListCollection = new OutlierListCollection(); /* From outlier array sort out which are outliers and put these into * an arraylist. If there are any farouts, set the flag on the * OutlierListCollection */ for (int i = 0; i < yOutliers.size(); i++) { double outlier = ((Number) yOutliers.get(i)).doubleValue(); if (outlier > boxAndWhiskerData.getMaxOutlier(series, item).doubleValue()) { outlierListCollection.setHighFarOut(true); } else if (outlier < boxAndWhiskerData.getMinOutlier(series, item).doubleValue()) { outlierListCollection.setLowFarOut(true); } else if (outlier > boxAndWhiskerData.getMaxRegularValue(series, item).doubleValue()) { yyOutlier = rangeAxis.valueToJava2D(outlier, dataArea, location); outliers.add(new Outlier(xx, yyOutlier, oRadius)); } else if (outlier < boxAndWhiskerData.getMinRegularValue(series, item).doubleValue()) { yyOutlier = rangeAxis.valueToJava2D(outlier, dataArea, location); outliers.add(new Outlier(xx, yyOutlier, oRadius)); } Collections.sort(outliers); } // Process outliers. Each outlier is either added to the appropriate // outlier list or a new outlier list is made for (Iterator iterator = outliers.iterator(); iterator.hasNext();) { Outlier outlier = (Outlier) iterator.next(); outlierListCollection.add(outlier); } // draw yOutliers double maxAxisValue = rangeAxis.valueToJava2D(rangeAxis.getUpperBound(), dataArea, location) + aRadius; double minAxisValue = rangeAxis.valueToJava2D(rangeAxis.getLowerBound(), dataArea, location) - aRadius; // draw outliers for (Iterator iterator = outlierListCollection.iterator(); iterator.hasNext();) { OutlierList list = (OutlierList) iterator.next(); Outlier outlier = list.getAveragedOutlier(); Point2D point = outlier.getPoint(); if (list.isMultiple()) { drawMultipleEllipse(point, width, oRadius, g2); } else { drawEllipse(point, oRadius, g2); } } // draw farout if (outlierListCollection.isHighFarOut()) { drawHighFarOut(aRadius, g2, xx, maxAxisValue); } if (outlierListCollection.isLowFarOut()) { drawLowFarOut(aRadius, g2, xx, minAxisValue); } // add an entity for the item... if (entities != null && box.intersects(dataArea)) { addEntity(entities, box, dataset, series, item, xx, yyAverage); } } /** * Draws an ellipse to represent an outlier. * * @param point the location. * @param oRadius the radius. * @param g2 the graphics device. */ protected void drawEllipse(Point2D point, double oRadius, Graphics2D g2) { Ellipse2D.Double dot = new Ellipse2D.Double(point.getX() + oRadius / 2, point.getY(), oRadius, oRadius); g2.draw(dot); } /** * Draws two ellipses to represent overlapping outliers. * * @param point the location. * @param boxWidth the box width. * @param oRadius the radius. * @param g2 the graphics device. */ protected void drawMultipleEllipse(Point2D point, double boxWidth, double oRadius, Graphics2D g2) { Ellipse2D.Double dot1 = new Ellipse2D.Double(point.getX() - (boxWidth / 2) + oRadius, point.getY(), oRadius, oRadius); Ellipse2D.Double dot2 = new Ellipse2D.Double(point.getX() + (boxWidth / 2), point.getY(), oRadius, oRadius); g2.draw(dot1); g2.draw(dot2); } /** * Draws a triangle to indicate the presence of far out values. * * @param aRadius the radius. * @param g2 the graphics device. * @param xx the x value. * @param m the max y value. */ protected void drawHighFarOut(double aRadius, Graphics2D g2, double xx, double m) { double side = aRadius * 2; g2.draw(new Line2D.Double(xx - side, m + side, xx + side, m + side)); g2.draw(new Line2D.Double(xx - side, m + side, xx, m)); g2.draw(new Line2D.Double(xx + side, m + side, xx, m)); } /** * Draws a triangle to indicate the presence of far out values. * * @param aRadius the radius. * @param g2 the graphics device. * @param xx the x value. * @param m the min y value. */ protected void drawLowFarOut(double aRadius, Graphics2D g2, double xx, double m) { double side = aRadius * 2; g2.draw(new Line2D.Double(xx - side, m - side, xx + side, m - side)); g2.draw(new Line2D.Double(xx - side, m - side, xx, m)); g2.draw(new Line2D.Double(xx + side, m - side, xx, m)); } /** * Tests this renderer for equality with another object. * * @param obj the object ({@code null} permitted). * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYBoxAndWhiskerRenderer)) { return false; } if (!super.equals(obj)) { return false; } XYBoxAndWhiskerRenderer that = (XYBoxAndWhiskerRenderer) obj; if (this.boxWidth != that.getBoxWidth()) { return false; } if (!PaintUtils.equal(this.boxPaint, that.boxPaint)) { return false; } if (!PaintUtils.equal(this.artifactPaint, that.artifactPaint)) { return false; } if (this.fillBox != that.fillBox) { return false; } return true; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.boxPaint, stream); SerialUtils.writePaint(this.artifactPaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.boxPaint = SerialUtils.readPaint(stream); this.artifactPaint = SerialUtils.readPaint(stream); } /** * Returns a clone of the renderer. * * @return A clone. * * @throws CloneNotSupportedException if the renderer cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/XYBubbleRenderer.java000066400000000000000000000321551463604235500313400ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * XYBubbleRenderer.java * --------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Christian W. Zuckschwerdt; * */ package org.jfree.chart.renderer.xy; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Ellipse2D; import java.awt.geom.Rectangle2D; import org.jfree.chart.LegendItem; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.plot.CrosshairState; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYZDataset; /** * A renderer that draws a circle at each data point with a diameter that is * determined by the z-value in the dataset (the renderer requires the dataset * to be an instance of {@link XYZDataset}. The example shown here * is generated by the {@code XYBubbleChartDemo1.java} program * included in the JFreeChart demo collection: *

* XYBubbleRendererSample.png */ public class XYBubbleRenderer extends AbstractXYItemRenderer implements XYItemRenderer, PublicCloneable { /** For serialization. */ public static final long serialVersionUID = -5221991598674249125L; /** * A constant to specify that the bubbles drawn by this renderer should be * scaled on both axes (see {@link #XYBubbleRenderer(int)}). */ public static final int SCALE_ON_BOTH_AXES = 0; /** * A constant to specify that the bubbles drawn by this renderer should be * scaled on the domain axis (see {@link #XYBubbleRenderer(int)}). */ public static final int SCALE_ON_DOMAIN_AXIS = 1; /** * A constant to specify that the bubbles drawn by this renderer should be * scaled on the range axis (see {@link #XYBubbleRenderer(int)}). */ public static final int SCALE_ON_RANGE_AXIS = 2; /** Controls how the width and height of the bubble are scaled. */ private int scaleType; /** * Constructs a new renderer. */ public XYBubbleRenderer() { this(SCALE_ON_BOTH_AXES); } /** * Constructs a new renderer with the specified type of scaling. * * @param scaleType the type of scaling (must be one of: * {@link #SCALE_ON_BOTH_AXES}, {@link #SCALE_ON_DOMAIN_AXIS}, * {@link #SCALE_ON_RANGE_AXIS}). */ public XYBubbleRenderer(int scaleType) { super(); if (scaleType < 0 || scaleType > 2) { throw new IllegalArgumentException("Invalid 'scaleType'."); } this.scaleType = scaleType; setDefaultLegendShape(new Ellipse2D.Double(-4.0, -4.0, 8.0, 8.0)); } /** * Returns the scale type that was set when the renderer was constructed. * * @return The scale type (one of: {@link #SCALE_ON_BOTH_AXES}, * {@link #SCALE_ON_DOMAIN_AXIS}, {@link #SCALE_ON_RANGE_AXIS}). */ public int getScaleType() { return this.scaleType; } /** * Draws the visual representation of a single data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the area within which the data is being drawn. * @param info collects information about the drawing. * @param plot the plot (can be used to obtain standard color * information etc). * @param domainAxis the domain (horizontal) axis. * @param rangeAxis the range (vertical) axis. * @param dataset the dataset (an {@link XYZDataset} is expected). * @param series the series index (zero-based). * @param item the item index (zero-based). * @param crosshairState crosshair information for the plot * ({@code null} permitted). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item, CrosshairState crosshairState, int pass) { // return straight away if the item is not visible if (!getItemVisible(series, item)) { return; } PlotOrientation orientation = plot.getOrientation(); // get the data point... double x = dataset.getXValue(series, item); double y = dataset.getYValue(series, item); double z = Double.NaN; if (dataset instanceof XYZDataset) { XYZDataset xyzData = (XYZDataset) dataset; z = xyzData.getZValue(series, item); } if (!Double.isNaN(z)) { RectangleEdge domainAxisLocation = plot.getDomainAxisEdge(); RectangleEdge rangeAxisLocation = plot.getRangeAxisEdge(); double transX = domainAxis.valueToJava2D(x, dataArea, domainAxisLocation); double transY = rangeAxis.valueToJava2D(y, dataArea, rangeAxisLocation); double transDomain; double transRange; double zero; switch(getScaleType()) { case SCALE_ON_DOMAIN_AXIS: zero = domainAxis.valueToJava2D(0.0, dataArea, domainAxisLocation); transDomain = domainAxis.valueToJava2D(z, dataArea, domainAxisLocation) - zero; transRange = transDomain; break; case SCALE_ON_RANGE_AXIS: zero = rangeAxis.valueToJava2D(0.0, dataArea, rangeAxisLocation); transRange = zero - rangeAxis.valueToJava2D(z, dataArea, rangeAxisLocation); transDomain = transRange; break; default: double zero1 = domainAxis.valueToJava2D(0.0, dataArea, domainAxisLocation); double zero2 = rangeAxis.valueToJava2D(0.0, dataArea, rangeAxisLocation); transDomain = domainAxis.valueToJava2D(z, dataArea, domainAxisLocation) - zero1; transRange = zero2 - rangeAxis.valueToJava2D(z, dataArea, rangeAxisLocation); } transDomain = Math.abs(transDomain); transRange = Math.abs(transRange); Ellipse2D circle = null; if (orientation == PlotOrientation.VERTICAL) { circle = new Ellipse2D.Double(transX - transDomain / 2.0, transY - transRange / 2.0, transDomain, transRange); } else if (orientation == PlotOrientation.HORIZONTAL) { circle = new Ellipse2D.Double(transY - transRange / 2.0, transX - transDomain / 2.0, transRange, transDomain); } else { throw new IllegalStateException(); } g2.setPaint(getItemPaint(series, item)); g2.fill(circle); g2.setStroke(getItemOutlineStroke(series, item)); g2.setPaint(getItemOutlinePaint(series, item)); g2.draw(circle); if (isItemLabelVisible(series, item)) { if (orientation == PlotOrientation.VERTICAL) { drawItemLabel(g2, orientation, dataset, series, item, transX, transY, false); } else if (orientation == PlotOrientation.HORIZONTAL) { drawItemLabel(g2, orientation, dataset, series, item, transY, transX, false); } } // add an entity if this info is being collected if (info != null) { EntityCollection entities = info.getOwner().getEntityCollection(); if (entities != null && circle.intersects(dataArea)) { addEntity(entities, circle, dataset, series, item, circle.getCenterX(), circle.getCenterY()); } } int datasetIndex = plot.indexOf(dataset); updateCrosshairValues(crosshairState, x, y, datasetIndex, transX, transY, orientation); } } /** * Returns a legend item for the specified series. The default method * is overridden so that the legend displays circles for all series. * * @param datasetIndex the dataset index (zero-based). * @param series the series index (zero-based). * * @return A legend item for the series. */ @Override public LegendItem getLegendItem(int datasetIndex, int series) { LegendItem result = null; XYPlot plot = getPlot(); if (plot == null) { return null; } XYDataset dataset = plot.getDataset(datasetIndex); if (dataset != null) { if (getItemVisible(series, 0)) { String label = getLegendItemLabelGenerator().generateLabel( dataset, series); String description = label; String toolTipText = null; if (getLegendItemToolTipGenerator() != null) { toolTipText = getLegendItemToolTipGenerator().generateLabel( dataset, series); } String urlText = null; if (getLegendItemURLGenerator() != null) { urlText = getLegendItemURLGenerator().generateLabel( dataset, series); } Shape shape = lookupLegendShape(series); Paint paint = lookupSeriesPaint(series); Paint outlinePaint = lookupSeriesOutlinePaint(series); Stroke outlineStroke = lookupSeriesOutlineStroke(series); result = new LegendItem(label, description, toolTipText, urlText, shape, paint, outlineStroke, outlinePaint); result.setLabelFont(lookupLegendTextFont(series)); Paint labelPaint = lookupLegendTextPaint(series); if (labelPaint != null) { result.setLabelPaint(labelPaint); } result.setDataset(dataset); result.setDatasetIndex(datasetIndex); result.setSeriesKey(dataset.getSeriesKey(series)); result.setSeriesIndex(series); } } return result; } /** * Tests this renderer for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYBubbleRenderer)) { return false; } XYBubbleRenderer that = (XYBubbleRenderer) obj; if (this.scaleType != that.scaleType) { return false; } return super.equals(obj); } /** * Returns a clone of the renderer. * * @return A clone. * * @throws CloneNotSupportedException if the renderer cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/XYDifferenceRenderer.java000066400000000000000000001321621463604235500321760ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * XYDifferenceRenderer.java * ------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Richard West, Advanced Micro Devices, Inc. (major rewrite * of difference drawing algorithm); * Patrick Schlott * Christoph Schroeder * Martin Hoeller * */ package org.jfree.chart.renderer.xy; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.GeneralPath; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Collections; import java.util.LinkedList; import org.jfree.chart.LegendItem; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.entity.XYItemEntity; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.labels.XYToolTipGenerator; import org.jfree.chart.plot.CrosshairState; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.urls.XYURLGenerator; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; import org.jfree.chart.util.ShapeUtils; import org.jfree.data.xy.XYDataset; /** * A renderer for an {@link XYPlot} that highlights the differences between two * series. The example shown here is generated by the * {@code DifferenceChartDemo1.java} program included in the JFreeChart * demo collection: *

* XYDifferenceRendererSample.png */ public class XYDifferenceRenderer extends AbstractXYItemRenderer implements XYItemRenderer, PublicCloneable { /** For serialization. */ private static final long serialVersionUID = -8447915602375584857L; /** The paint used to highlight positive differences (y(0) > y(1)). */ private transient Paint positivePaint; /** The paint used to highlight negative differences (y(0) < y(1)). */ private transient Paint negativePaint; /** Display shapes at each point? */ private boolean shapesVisible; /** The shape to display in the legend item. */ private transient Shape legendLine; /** * This flag controls whether or not the x-coordinates (in Java2D space) * are rounded to integers. When set to true, this can avoid the vertical * striping that anti-aliasing can generate. However, the rounding may not * be appropriate for output in high resolution formats (for example, * vector graphics formats such as SVG and PDF). */ private boolean roundXCoordinates; /** * Creates a new renderer with default attributes. */ public XYDifferenceRenderer() { this(Color.GREEN, Color.RED, false); } /** * Creates a new renderer. * * @param positivePaint the highlight color for positive differences * ({@code null} not permitted). * @param negativePaint the highlight color for negative differences * ({@code null} not permitted). * @param shapes draw shapes? */ public XYDifferenceRenderer(Paint positivePaint, Paint negativePaint, boolean shapes) { Args.nullNotPermitted(positivePaint, "positivePaint"); Args.nullNotPermitted(negativePaint, "negativePaint"); this.positivePaint = positivePaint; this.negativePaint = negativePaint; this.shapesVisible = shapes; this.legendLine = new Line2D.Double(-7.0, 0.0, 7.0, 0.0); this.roundXCoordinates = false; } /** * Returns the paint used to highlight positive differences. * * @return The paint (never {@code null}). * * @see #setPositivePaint(Paint) */ public Paint getPositivePaint() { return this.positivePaint; } /** * Sets the paint used to highlight positive differences and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getPositivePaint() */ public void setPositivePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.positivePaint = paint; fireChangeEvent(); } /** * Returns the paint used to highlight negative differences. * * @return The paint (never {@code null}). * * @see #setNegativePaint(Paint) */ public Paint getNegativePaint() { return this.negativePaint; } /** * Sets the paint used to highlight negative differences. * * @param paint the paint ({@code null} not permitted). * * @see #getNegativePaint() */ public void setNegativePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.negativePaint = paint; notifyListeners(new RendererChangeEvent(this)); } /** * Returns a flag that controls whether or not shapes are drawn for each * data value. * * @return A boolean. * * @see #setShapesVisible(boolean) */ public boolean getShapesVisible() { return this.shapesVisible; } /** * Sets a flag that controls whether or not shapes are drawn for each * data value, and sends a {@link RendererChangeEvent} to all registered * listeners. * * @param flag the flag. * * @see #getShapesVisible() */ public void setShapesVisible(boolean flag) { this.shapesVisible = flag; fireChangeEvent(); } /** * Returns the shape used to represent a line in the legend. * * @return The legend line (never {@code null}). * * @see #setLegendLine(Shape) */ public Shape getLegendLine() { return this.legendLine; } /** * Sets the shape used as a line in each legend item and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param line the line ({@code null} not permitted). * * @see #getLegendLine() */ public void setLegendLine(Shape line) { Args.nullNotPermitted(line, "line"); this.legendLine = line; fireChangeEvent(); } /** * Returns the flag that controls whether or not the x-coordinates (in * Java2D space) are rounded to integer values. * * @return The flag. * * @see #setRoundXCoordinates(boolean) */ public boolean getRoundXCoordinates() { return this.roundXCoordinates; } /** * Sets the flag that controls whether or not the x-coordinates (in * Java2D space) are rounded to integer values, and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param round the new flag value. * * @see #getRoundXCoordinates() */ public void setRoundXCoordinates(boolean round) { this.roundXCoordinates = round; fireChangeEvent(); } /** * Initialises the renderer and returns a state object that should be * passed to subsequent calls to the drawItem() method. This method will * be called before the first item is rendered, giving the renderer an * opportunity to initialise any state information it wants to maintain. * The renderer can do nothing if it chooses. * * @param g2 the graphics device. * @param dataArea the area inside the axes. * @param plot the plot. * @param data the data. * @param info an optional info collection object to return data back to * the caller. * * @return A state object. */ @Override public XYItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, XYPlot plot, XYDataset data, PlotRenderingInfo info) { XYItemRendererState state = super.initialise(g2, dataArea, plot, data, info); state.setProcessVisibleItemsOnly(false); return state; } /** * Returns {@code 2}, the number of passes required by the renderer. * The {@link XYPlot} will run through the dataset this number of times. * * @return The number of passes required by the renderer. */ @Override public int getPassCount() { return 2; } /** * Draws the visual representation of a single data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the area within which the data is being drawn. * @param info collects information about the drawing. * @param plot the plot (can be used to obtain standard color * information etc). * @param domainAxis the domain (horizontal) axis. * @param rangeAxis the range (vertical) axis. * @param dataset the dataset. * @param series the series index (zero-based). * @param item the item index (zero-based). * @param crosshairState crosshair information for the plot * ({@code null} permitted). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item, CrosshairState crosshairState, int pass) { if (pass == 0) { drawItemPass0(g2, dataArea, info, plot, domainAxis, rangeAxis, dataset, series, item, crosshairState); } else if (pass == 1) { drawItemPass1(g2, dataArea, info, plot, domainAxis, rangeAxis, dataset, series, item, crosshairState); } } /** * Draws the visual representation of a single data item, first pass. * * @param x_graphics the graphics device. * @param x_dataArea the area within which the data is being drawn. * @param x_info collects information about the drawing. * @param x_plot the plot (can be used to obtain standard color * information etc). * @param x_domainAxis the domain (horizontal) axis. * @param x_rangeAxis the range (vertical) axis. * @param x_dataset the dataset. * @param x_series the series index (zero-based). * @param x_item the item index (zero-based). * @param x_crosshairState crosshair information for the plot * ({@code null} permitted). */ protected void drawItemPass0(Graphics2D x_graphics, Rectangle2D x_dataArea, PlotRenderingInfo x_info, XYPlot x_plot, ValueAxis x_domainAxis, ValueAxis x_rangeAxis, XYDataset x_dataset, int x_series, int x_item, CrosshairState x_crosshairState) { if (!((0 == x_series) && (0 == x_item))) { return; } boolean b_impliedZeroSubtrahend = (1 == x_dataset.getSeriesCount()); // check if either series is a degenerate case (i.e. less than 2 points) if (isEitherSeriesDegenerate(x_dataset, b_impliedZeroSubtrahend)) { return; } // check if series are disjoint (i.e. domain-spans do not overlap) if (!b_impliedZeroSubtrahend && areSeriesDisjoint(x_dataset)) { return; } // polygon definitions LinkedList l_minuendXs = new LinkedList(); LinkedList l_minuendYs = new LinkedList(); LinkedList l_subtrahendXs = new LinkedList(); LinkedList l_subtrahendYs = new LinkedList(); LinkedList l_polygonXs = new LinkedList(); LinkedList l_polygonYs = new LinkedList(); // state int l_minuendItem = 0; int l_minuendItemCount = x_dataset.getItemCount(0); Double l_minuendCurX = null; Double l_minuendNextX = null; Double l_minuendCurY = null; Double l_minuendNextY = null; double l_minuendMaxY = Double.NEGATIVE_INFINITY; double l_minuendMinY = Double.POSITIVE_INFINITY; int l_subtrahendItem = 0; int l_subtrahendItemCount = 0; // actual value set below Double l_subtrahendCurX = null; Double l_subtrahendNextX = null; Double l_subtrahendCurY = null; Double l_subtrahendNextY = null; double l_subtrahendMaxY = Double.NEGATIVE_INFINITY; double l_subtrahendMinY = Double.POSITIVE_INFINITY; // if a subtrahend is not specified, assume it is zero if (b_impliedZeroSubtrahend) { l_subtrahendItem = 0; l_subtrahendItemCount = 2; l_subtrahendCurX = x_dataset.getXValue(0, 0); l_subtrahendNextX = x_dataset.getXValue(0, (l_minuendItemCount - 1)); l_subtrahendCurY = 0.0; l_subtrahendNextY = 0.0; l_subtrahendMaxY = 0.0; l_subtrahendMinY = 0.0; l_subtrahendXs.add(l_subtrahendCurX); l_subtrahendYs.add(l_subtrahendCurY); } else { l_subtrahendItemCount = x_dataset.getItemCount(1); } boolean b_minuendDone = false; boolean b_minuendAdvanced = true; boolean b_minuendAtIntersect = false; boolean b_minuendFastForward = false; boolean b_subtrahendDone = false; boolean b_subtrahendAdvanced = true; boolean b_subtrahendAtIntersect = false; boolean b_subtrahendFastForward = false; boolean b_colinear = false; boolean b_positive; // coordinate pairs double l_x1 = 0.0, l_y1 = 0.0; // current minuend point double l_x2 = 0.0, l_y2 = 0.0; // next minuend point double l_x3 = 0.0, l_y3 = 0.0; // current subtrahend point double l_x4 = 0.0, l_y4 = 0.0; // next subtrahend point // fast-forward through leading tails boolean b_fastForwardDone = false; while (!b_fastForwardDone) { // get the x and y coordinates l_x1 = x_dataset.getXValue(0, l_minuendItem); l_y1 = x_dataset.getYValue(0, l_minuendItem); l_x2 = x_dataset.getXValue(0, l_minuendItem + 1); l_y2 = x_dataset.getYValue(0, l_minuendItem + 1); l_minuendCurX = l_x1; l_minuendCurY = l_y1; l_minuendNextX = l_x2; l_minuendNextY = l_y2; if (b_impliedZeroSubtrahend) { l_x3 = l_subtrahendCurX; l_y3 = l_subtrahendCurY; l_x4 = l_subtrahendNextX; l_y4 = l_subtrahendNextY; } else { l_x3 = x_dataset.getXValue(1, l_subtrahendItem); l_y3 = x_dataset.getYValue(1, l_subtrahendItem); l_x4 = x_dataset.getXValue(1, l_subtrahendItem + 1); l_y4 = x_dataset.getYValue(1, l_subtrahendItem + 1); l_subtrahendCurX = l_x3; l_subtrahendCurY = l_y3; l_subtrahendNextX = l_x4; l_subtrahendNextY = l_y4; } if (l_x2 <= l_x3) { // minuend needs to be fast forwarded l_minuendItem++; b_minuendFastForward = true; continue; } if (l_x4 <= l_x1) { // subtrahend needs to be fast forwarded l_subtrahendItem++; b_subtrahendFastForward = true; continue; } // check if initial polygon needs to be clipped if ((l_x3 < l_x1) && (l_x1 < l_x4)) { // project onto subtrahend double l_slope = (l_y4 - l_y3) / (l_x4 - l_x3); l_subtrahendCurX = l_minuendCurX; l_subtrahendCurY = (l_slope * l_x1) + (l_y3 - (l_slope * l_x3)); l_subtrahendXs.add(l_subtrahendCurX); l_subtrahendYs.add(l_subtrahendCurY); } if ((l_x1 < l_x3) && (l_x3 < l_x2)) { // project onto minuend double l_slope = (l_y2 - l_y1) / (l_x2 - l_x1); l_minuendCurX = l_subtrahendCurX; l_minuendCurY = (l_slope * l_x3) + (l_y1 - (l_slope * l_x1)); l_minuendXs.add(l_minuendCurX); l_minuendYs.add(l_minuendCurY); } l_minuendMaxY = l_minuendCurY; l_minuendMinY = l_minuendCurY; l_subtrahendMaxY = l_subtrahendCurY; l_subtrahendMinY = l_subtrahendCurY; b_fastForwardDone = true; } // start of algorithm while (!b_minuendDone && !b_subtrahendDone) { if (!b_minuendDone && !b_minuendFastForward && b_minuendAdvanced) { l_x1 = x_dataset.getXValue(0, l_minuendItem); l_y1 = x_dataset.getYValue(0, l_minuendItem); l_minuendCurX = l_x1; l_minuendCurY = l_y1; if (!b_minuendAtIntersect) { l_minuendXs.add(l_minuendCurX); l_minuendYs.add(l_minuendCurY); } l_minuendMaxY = Math.max(l_minuendMaxY, l_y1); l_minuendMinY = Math.min(l_minuendMinY, l_y1); l_x2 = x_dataset.getXValue(0, l_minuendItem + 1); l_y2 = x_dataset.getYValue(0, l_minuendItem + 1); l_minuendNextX = l_x2; l_minuendNextY = l_y2; } // never updated the subtrahend if it is implied to be zero if (!b_impliedZeroSubtrahend && !b_subtrahendDone && !b_subtrahendFastForward && b_subtrahendAdvanced) { l_x3 = x_dataset.getXValue(1, l_subtrahendItem); l_y3 = x_dataset.getYValue(1, l_subtrahendItem); l_subtrahendCurX = l_x3; l_subtrahendCurY = l_y3; if (!b_subtrahendAtIntersect) { l_subtrahendXs.add(l_subtrahendCurX); l_subtrahendYs.add(l_subtrahendCurY); } l_subtrahendMaxY = Math.max(l_subtrahendMaxY, l_y3); l_subtrahendMinY = Math.min(l_subtrahendMinY, l_y3); l_x4 = x_dataset.getXValue(1, l_subtrahendItem + 1); l_y4 = x_dataset.getYValue(1, l_subtrahendItem + 1); l_subtrahendNextX = l_x4; l_subtrahendNextY = l_y4; } // deassert b_*FastForward (only matters for 1st time through loop) b_minuendFastForward = false; b_subtrahendFastForward = false; Double l_intersectX = null; Double l_intersectY = null; boolean b_intersect = false; b_minuendAtIntersect = false; b_subtrahendAtIntersect = false; // check for intersect if ((l_x2 == l_x4) && (l_y2 == l_y4)) { // check if line segments are colinear if ((l_x1 == l_x3) && (l_y1 == l_y3)) { b_colinear = true; } else { // the intersect is at the next point for both the minuend // and subtrahend l_intersectX = l_x2; l_intersectY = l_y2; b_intersect = true; b_minuendAtIntersect = true; b_subtrahendAtIntersect = true; } } else { // compute common denominator double l_denominator = ((l_y4 - l_y3) * (l_x2 - l_x1)) - ((l_x4 - l_x3) * (l_y2 - l_y1)); // compute common deltas double l_deltaY = l_y1 - l_y3; double l_deltaX = l_x1 - l_x3; // compute numerators double l_numeratorA = ((l_x4 - l_x3) * l_deltaY) - ((l_y4 - l_y3) * l_deltaX); double l_numeratorB = ((l_x2 - l_x1) * l_deltaY) - ((l_y2 - l_y1) * l_deltaX); // check if line segments are colinear if ((0 == l_numeratorA) && (0 == l_numeratorB) && (0 == l_denominator)) { b_colinear = true; } else { // check if previously colinear if (b_colinear) { // clear colinear points and flag l_minuendXs.clear(); l_minuendYs.clear(); l_subtrahendXs.clear(); l_subtrahendYs.clear(); l_polygonXs.clear(); l_polygonYs.clear(); b_colinear = false; // set new starting point for the polygon boolean b_useMinuend = ((l_x3 <= l_x1) && (l_x1 <= l_x4)); l_polygonXs.add(b_useMinuend ? l_minuendCurX : l_subtrahendCurX); l_polygonYs.add(b_useMinuend ? l_minuendCurY : l_subtrahendCurY); } } // compute slope components double l_slopeA = l_numeratorA / l_denominator; double l_slopeB = l_numeratorB / l_denominator; // test if both grahphs have a vertical rise at the same x-value boolean b_vertical = (l_x1 == l_x2) && (l_x3 == l_x4) && (l_x2 == l_x4); // check if the line segments intersect if (((0 < l_slopeA) && (l_slopeA <= 1) && (0 < l_slopeB) && (l_slopeB <= 1))|| b_vertical) { // compute the point of intersection double l_xi; double l_yi; if(b_vertical){ b_colinear = false; l_xi = l_x2; l_yi = l_x4; } else{ l_xi = l_x1 + (l_slopeA * (l_x2 - l_x1)); l_yi = l_y1 + (l_slopeA * (l_y2 - l_y1)); } l_intersectX = l_xi; l_intersectY = l_yi; b_intersect = true; b_minuendAtIntersect = ((l_xi == l_x2) && (l_yi == l_y2)); b_subtrahendAtIntersect = ((l_xi == l_x4) && (l_yi == l_y4)); // advance minuend and subtrahend to intesect l_minuendCurX = l_intersectX; l_minuendCurY = l_intersectY; l_subtrahendCurX = l_intersectX; l_subtrahendCurY = l_intersectY; } } if (b_intersect) { // create the polygon // add the minuend's points to polygon l_polygonXs.addAll(l_minuendXs); l_polygonYs.addAll(l_minuendYs); // add intersection point to the polygon l_polygonXs.add(l_intersectX); l_polygonYs.add(l_intersectY); // add the subtrahend's points to the polygon in reverse Collections.reverse(l_subtrahendXs); Collections.reverse(l_subtrahendYs); l_polygonXs.addAll(l_subtrahendXs); l_polygonYs.addAll(l_subtrahendYs); // create an actual polygon b_positive = (l_subtrahendMaxY <= l_minuendMaxY) && (l_subtrahendMinY <= l_minuendMinY); createPolygon(x_graphics, x_dataArea, x_plot, x_domainAxis, x_rangeAxis, b_positive, l_polygonXs, l_polygonYs); // clear the point vectors l_minuendXs.clear(); l_minuendYs.clear(); l_subtrahendXs.clear(); l_subtrahendYs.clear(); l_polygonXs.clear(); l_polygonYs.clear(); // set the maxY and minY values to intersect y-value double l_y = l_intersectY; l_minuendMaxY = l_y; l_subtrahendMaxY = l_y; l_minuendMinY = l_y; l_subtrahendMinY = l_y; // add interection point to new polygon l_polygonXs.add(l_intersectX); l_polygonYs.add(l_intersectY); } // advance the minuend if needed if (l_x2 <= l_x4) { l_minuendItem++; b_minuendAdvanced = true; } else { b_minuendAdvanced = false; } // advance the subtrahend if needed if (l_x4 <= l_x2) { l_subtrahendItem++; b_subtrahendAdvanced = true; } else { b_subtrahendAdvanced = false; } b_minuendDone = (l_minuendItem == (l_minuendItemCount - 1)); b_subtrahendDone = (l_subtrahendItem == (l_subtrahendItemCount - 1)); } // check if the final polygon needs to be clipped if (b_minuendDone && (l_x3 < l_x2) && (l_x2 < l_x4)) { // project onto subtrahend double l_slope = (l_y4 - l_y3) / (l_x4 - l_x3); l_subtrahendNextX = l_minuendNextX; l_subtrahendNextY = (l_slope * l_x2) + (l_y3 - (l_slope * l_x3)); } if (b_subtrahendDone && (l_x1 < l_x4) && (l_x4 < l_x2)) { // project onto minuend double l_slope = (l_y2 - l_y1) / (l_x2 - l_x1); l_minuendNextX = l_subtrahendNextX; l_minuendNextY = (l_slope * l_x4) + (l_y1 - (l_slope * l_x1)); } // consider last point of minuend and subtrahend for determining // positivity l_minuendMaxY = Math.max(l_minuendMaxY, l_minuendNextY); l_subtrahendMaxY = Math.max(l_subtrahendMaxY, l_subtrahendNextY); l_minuendMinY = Math.min(l_minuendMinY, l_minuendNextY); l_subtrahendMinY = Math.min(l_subtrahendMinY, l_subtrahendNextY); // add the last point of the minuned and subtrahend l_minuendXs.add(l_minuendNextX); l_minuendYs.add(l_minuendNextY); l_subtrahendXs.add(l_subtrahendNextX); l_subtrahendYs.add(l_subtrahendNextY); // create the polygon // add the minuend's points to polygon l_polygonXs.addAll(l_minuendXs); l_polygonYs.addAll(l_minuendYs); // add the subtrahend's points to the polygon in reverse Collections.reverse(l_subtrahendXs); Collections.reverse(l_subtrahendYs); l_polygonXs.addAll(l_subtrahendXs); l_polygonYs.addAll(l_subtrahendYs); // create an actual polygon b_positive = (l_subtrahendMaxY <= l_minuendMaxY) && (l_subtrahendMinY <= l_minuendMinY); createPolygon(x_graphics, x_dataArea, x_plot, x_domainAxis, x_rangeAxis, b_positive, l_polygonXs, l_polygonYs); } /** * Draws the visual representation of a single data item, second pass. In * the second pass, the renderer draws the lines and shapes for the * individual points in the two series. * * @param x_graphics the graphics device. * @param x_dataArea the area within which the data is being drawn. * @param x_info collects information about the drawing. * @param x_plot the plot (can be used to obtain standard color * information etc). * @param x_domainAxis the domain (horizontal) axis. * @param x_rangeAxis the range (vertical) axis. * @param x_dataset the dataset. * @param x_series the series index (zero-based). * @param x_item the item index (zero-based). * @param x_crosshairState crosshair information for the plot * ({@code null} permitted). */ protected void drawItemPass1(Graphics2D x_graphics, Rectangle2D x_dataArea, PlotRenderingInfo x_info, XYPlot x_plot, ValueAxis x_domainAxis, ValueAxis x_rangeAxis, XYDataset x_dataset, int x_series, int x_item, CrosshairState x_crosshairState) { Shape l_entityArea = null; EntityCollection l_entities = null; if (null != x_info) { l_entities = x_info.getOwner().getEntityCollection(); } Paint l_seriesPaint = getItemPaint(x_series, x_item); Stroke l_seriesStroke = getItemStroke(x_series, x_item); x_graphics.setPaint(l_seriesPaint); x_graphics.setStroke(l_seriesStroke); PlotOrientation l_orientation = x_plot.getOrientation(); RectangleEdge l_domainAxisLocation = x_plot.getDomainAxisEdge(); RectangleEdge l_rangeAxisLocation = x_plot.getRangeAxisEdge(); double l_x0 = x_dataset.getXValue(x_series, x_item); double l_y0 = x_dataset.getYValue(x_series, x_item); double l_x1 = x_domainAxis.valueToJava2D(l_x0, x_dataArea, l_domainAxisLocation); double l_y1 = x_rangeAxis.valueToJava2D(l_y0, x_dataArea, l_rangeAxisLocation); if (getShapesVisible()) { Shape l_shape = getItemShape(x_series, x_item); if (l_orientation == PlotOrientation.HORIZONTAL) { l_shape = ShapeUtils.createTranslatedShape(l_shape, l_y1, l_x1); } else { l_shape = ShapeUtils.createTranslatedShape(l_shape, l_x1, l_y1); } if (l_shape.intersects(x_dataArea)) { x_graphics.setPaint(getItemPaint(x_series, x_item)); x_graphics.fill(l_shape); } l_entityArea = l_shape; } // add an entity for the item... if (null != l_entities) { if (null == l_entityArea) { l_entityArea = new Rectangle2D.Double((l_x1 - 2), (l_y1 - 2), 4, 4); } String l_tip = null; XYToolTipGenerator l_tipGenerator = getToolTipGenerator(x_series, x_item); if (null != l_tipGenerator) { l_tip = l_tipGenerator.generateToolTip(x_dataset, x_series, x_item); } String l_url = null; XYURLGenerator l_urlGenerator = getURLGenerator(); if (null != l_urlGenerator) { l_url = l_urlGenerator.generateURL(x_dataset, x_series, x_item); } XYItemEntity l_entity = new XYItemEntity(l_entityArea, x_dataset, x_series, x_item, l_tip, l_url); l_entities.add(l_entity); } // draw the item label if there is one... if (isItemLabelVisible(x_series, x_item)) { drawItemLabel(x_graphics, l_orientation, x_dataset, x_series, x_item, l_x1, l_y1, (l_y1 < 0.0)); } int datasetIndex = x_plot.indexOf(x_dataset); updateCrosshairValues(x_crosshairState, l_x0, l_y0, datasetIndex, l_x1, l_y1, l_orientation); if (0 == x_item) { return; } double l_x2 = x_domainAxis.valueToJava2D(x_dataset.getXValue(x_series, (x_item - 1)), x_dataArea, l_domainAxisLocation); double l_y2 = x_rangeAxis.valueToJava2D(x_dataset.getYValue(x_series, (x_item - 1)), x_dataArea, l_rangeAxisLocation); Line2D l_line = null; if (PlotOrientation.HORIZONTAL == l_orientation) { l_line = new Line2D.Double(l_y1, l_x1, l_y2, l_x2); } else if (PlotOrientation.VERTICAL == l_orientation) { l_line = new Line2D.Double(l_x1, l_y1, l_x2, l_y2); } if ((null != l_line) && l_line.intersects(x_dataArea)) { x_graphics.setPaint(getItemPaint(x_series, x_item)); x_graphics.setStroke(getItemStroke(x_series, x_item)); x_graphics.draw(l_line); } } /** * Determines if a dataset is degenerate. A degenerate dataset is a * dataset where either series has less than two (2) points. * * @param x_dataset the dataset. * @param x_impliedZeroSubtrahend if false, do not check the subtrahend * * @return true if the dataset is degenerate. */ private boolean isEitherSeriesDegenerate(XYDataset x_dataset, boolean x_impliedZeroSubtrahend) { if (x_impliedZeroSubtrahend) { return (x_dataset.getItemCount(0) < 2); } return ((x_dataset.getItemCount(0) < 2) || (x_dataset.getItemCount(1) < 2)); } /** * Determines if the two (2) series are disjoint. * Disjoint series do not overlap in the domain space. * * @param x_dataset the dataset. * * @return true if the dataset is degenerate. */ private boolean areSeriesDisjoint(XYDataset x_dataset) { int l_minuendItemCount = x_dataset.getItemCount(0); double l_minuendFirst = x_dataset.getXValue(0, 0); double l_minuendLast = x_dataset.getXValue(0, l_minuendItemCount - 1); int l_subtrahendItemCount = x_dataset.getItemCount(1); double l_subtrahendFirst = x_dataset.getXValue(1, 0); double l_subtrahendLast = x_dataset.getXValue(1, l_subtrahendItemCount - 1); return ((l_minuendLast < l_subtrahendFirst) || (l_subtrahendLast < l_minuendFirst)); } /** * Draws the visual representation of a polygon * * @param x_graphics the graphics device. * @param x_dataArea the area within which the data is being drawn. * @param x_plot the plot (can be used to obtain standard color * information etc). * @param x_domainAxis the domain (horizontal) axis. * @param x_rangeAxis the range (vertical) axis. * @param x_positive indicates if the polygon is positive (true) or * negative (false). * @param x_xValues a linked list of the x values (expects values to be * of type Double). * @param x_yValues a linked list of the y values (expects values to be * of type Double). */ private void createPolygon (Graphics2D x_graphics, Rectangle2D x_dataArea, XYPlot x_plot, ValueAxis x_domainAxis, ValueAxis x_rangeAxis, boolean x_positive, LinkedList x_xValues, LinkedList x_yValues) { PlotOrientation l_orientation = x_plot.getOrientation(); RectangleEdge l_domainAxisLocation = x_plot.getDomainAxisEdge(); RectangleEdge l_rangeAxisLocation = x_plot.getRangeAxisEdge(); Object[] l_xValues = x_xValues.toArray(); Object[] l_yValues = x_yValues.toArray(); GeneralPath l_path = new GeneralPath(); if (PlotOrientation.VERTICAL == l_orientation) { double l_x = x_domainAxis.valueToJava2D(( (Double) l_xValues[0]), x_dataArea, l_domainAxisLocation); if (this.roundXCoordinates) { l_x = Math.rint(l_x); } double l_y = x_rangeAxis.valueToJava2D(( (Double) l_yValues[0]), x_dataArea, l_rangeAxisLocation); l_path.moveTo((float) l_x, (float) l_y); for (int i = 1; i < l_xValues.length; i++) { l_x = x_domainAxis.valueToJava2D(( (Double) l_xValues[i]), x_dataArea, l_domainAxisLocation); if (this.roundXCoordinates) { l_x = Math.rint(l_x); } l_y = x_rangeAxis.valueToJava2D(( (Double) l_yValues[i]), x_dataArea, l_rangeAxisLocation); l_path.lineTo((float) l_x, (float) l_y); } l_path.closePath(); } else { double l_x = x_domainAxis.valueToJava2D(( (Double) l_xValues[0]), x_dataArea, l_domainAxisLocation); if (this.roundXCoordinates) { l_x = Math.rint(l_x); } double l_y = x_rangeAxis.valueToJava2D(( (Double) l_yValues[0]), x_dataArea, l_rangeAxisLocation); l_path.moveTo((float) l_y, (float) l_x); for (int i = 1; i < l_xValues.length; i++) { l_x = x_domainAxis.valueToJava2D(( (Double) l_xValues[i]), x_dataArea, l_domainAxisLocation); if (this.roundXCoordinates) { l_x = Math.rint(l_x); } l_y = x_rangeAxis.valueToJava2D(( (Double) l_yValues[i]), x_dataArea, l_rangeAxisLocation); l_path.lineTo((float) l_y, (float) l_x); } l_path.closePath(); } if (l_path.intersects(x_dataArea)) { x_graphics.setPaint(x_positive ? getPositivePaint() : getNegativePaint()); x_graphics.fill(l_path); } } /** * Returns a default legend item for the specified series. Subclasses * should override this method to generate customised items. * * @param datasetIndex the dataset index (zero-based). * @param series the series index (zero-based). * * @return A legend item for the series. */ @Override public LegendItem getLegendItem(int datasetIndex, int series) { LegendItem result = null; XYPlot p = getPlot(); if (p != null) { XYDataset dataset = p.getDataset(datasetIndex); if (dataset != null) { if (getItemVisible(series, 0)) { String label = getLegendItemLabelGenerator().generateLabel( dataset, series); String description = label; String toolTipText = null; if (getLegendItemToolTipGenerator() != null) { toolTipText = getLegendItemToolTipGenerator().generateLabel( dataset, series); } String urlText = null; if (getLegendItemURLGenerator() != null) { urlText = getLegendItemURLGenerator().generateLabel( dataset, series); } Paint paint = lookupSeriesPaint(series); Stroke stroke = lookupSeriesStroke(series); Shape line = getLegendLine(); result = new LegendItem(label, description, toolTipText, urlText, line, stroke, paint); result.setLabelFont(lookupLegendTextFont(series)); Paint labelPaint = lookupLegendTextPaint(series); if (labelPaint != null) { result.setLabelPaint(labelPaint); } result.setDataset(dataset); result.setDatasetIndex(datasetIndex); result.setSeriesKey(dataset.getSeriesKey(series)); result.setSeriesIndex(series); } } } return result; } /** * Tests this renderer for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYDifferenceRenderer)) { return false; } if (!super.equals(obj)) { return false; } XYDifferenceRenderer that = (XYDifferenceRenderer) obj; if (!PaintUtils.equal(this.positivePaint, that.positivePaint)) { return false; } if (!PaintUtils.equal(this.negativePaint, that.negativePaint)) { return false; } if (this.shapesVisible != that.shapesVisible) { return false; } if (!ShapeUtils.equal(this.legendLine, that.legendLine)) { return false; } if (this.roundXCoordinates != that.roundXCoordinates) { return false; } return true; } /** * Returns a clone of the renderer. * * @return A clone. * * @throws CloneNotSupportedException if the renderer cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { XYDifferenceRenderer clone = (XYDifferenceRenderer) super.clone(); clone.legendLine = ShapeUtils.clone(this.legendLine); return clone; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.positivePaint, stream); SerialUtils.writePaint(this.negativePaint, stream); SerialUtils.writeShape(this.legendLine, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.positivePaint = SerialUtils.readPaint(stream); this.negativePaint = SerialUtils.readPaint(stream); this.legendLine = SerialUtils.readShape(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/XYDotRenderer.java000066400000000000000000000277561463604235500307060ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * XYDotRenderer.java * ------------------ * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Christian W. Zuckschwerdt; * */ package org.jfree.chart.renderer.xy; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import org.jfree.chart.LegendItem; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.plot.CrosshairState; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; import org.jfree.chart.util.ShapeUtils; import org.jfree.data.xy.XYDataset; /** * A renderer that draws a small dot at each data point for an {@link XYPlot}. * The example shown here is generated by the * {@code ScatterPlotDemo4.java} program included in the JFreeChart * demo collection: *

* XYDotRendererSample.png */ public class XYDotRenderer extends AbstractXYItemRenderer implements XYItemRenderer, PublicCloneable { /** For serialization. */ private static final long serialVersionUID = -2764344339073566425L; /** The dot width. */ private int dotWidth; /** The dot height. */ private int dotHeight; /** * The shape that is used to represent an item in the legend. */ private transient Shape legendShape; /** * Constructs a new renderer. */ public XYDotRenderer() { super(); this.dotWidth = 1; this.dotHeight = 1; this.legendShape = new Rectangle2D.Double(-3.0, -3.0, 6.0, 6.0); } /** * Returns the dot width (the default value is 1). * * @return The dot width. * * @see #setDotWidth(int) */ public int getDotWidth() { return this.dotWidth; } /** * Sets the dot width and sends a {@link RendererChangeEvent} to all * registered listeners. * * @param w the new width (must be greater than zero). * * @throws IllegalArgumentException if {@code w} is less than one. * * @see #getDotWidth() */ public void setDotWidth(int w) { if (w < 1) { throw new IllegalArgumentException("Requires w > 0."); } this.dotWidth = w; fireChangeEvent(); } /** * Returns the dot height (the default value is 1). * * @return The dot height. * * @see #setDotHeight(int) */ public int getDotHeight() { return this.dotHeight; } /** * Sets the dot height and sends a {@link RendererChangeEvent} to all * registered listeners. * * @param h the new height (must be greater than zero). * * @throws IllegalArgumentException if {@code h} is less than one. * * @see #getDotHeight() */ public void setDotHeight(int h) { if (h < 1) { throw new IllegalArgumentException("Requires h > 0."); } this.dotHeight = h; fireChangeEvent(); } /** * Returns the shape used to represent an item in the legend. * * @return The legend shape (never {@code null}). * * @see #setLegendShape(Shape) */ public Shape getLegendShape() { return this.legendShape; } /** * Sets the shape used as a line in each legend item and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param shape the shape ({@code null} not permitted). * * @see #getLegendShape() */ public void setLegendShape(Shape shape) { Args.nullNotPermitted(shape, "shape"); this.legendShape = shape; fireChangeEvent(); } /** * Draws the visual representation of a single data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the area within which the data is being drawn. * @param info collects information about the drawing. * @param plot the plot (can be used to obtain standard color * information etc). * @param domainAxis the domain (horizontal) axis. * @param rangeAxis the range (vertical) axis. * @param dataset the dataset. * @param series the series index (zero-based). * @param item the item index (zero-based). * @param crosshairState crosshair information for the plot * ({@code null} permitted). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item, CrosshairState crosshairState, int pass) { // do nothing if item is not visible if (!getItemVisible(series, item)) { return; } // get the data point... double x = dataset.getXValue(series, item); double y = dataset.getYValue(series, item); double adjx = (this.dotWidth - 1) / 2.0; double adjy = (this.dotHeight - 1) / 2.0; if (!Double.isNaN(y)) { RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); double transX = domainAxis.valueToJava2D(x, dataArea, xAxisLocation) - adjx; double transY = rangeAxis.valueToJava2D(y, dataArea, yAxisLocation) - adjy; g2.setPaint(getItemPaint(series, item)); PlotOrientation orientation = plot.getOrientation(); if (orientation == PlotOrientation.HORIZONTAL) { g2.fillRect((int) transY, (int) transX, this.dotHeight, this.dotWidth); } else if (orientation == PlotOrientation.VERTICAL) { g2.fillRect((int) transX, (int) transY, this.dotWidth, this.dotHeight); } int datasetIndex = plot.indexOf(dataset); updateCrosshairValues(crosshairState, x, y, datasetIndex, transX, transY, orientation); } } /** * Returns a legend item for the specified series. * * @param datasetIndex the dataset index (zero-based). * @param series the series index (zero-based). * * @return A legend item for the series (possibly {@code null}). */ @Override public LegendItem getLegendItem(int datasetIndex, int series) { // if the renderer isn't assigned to a plot, then we don't have a // dataset... XYPlot plot = getPlot(); if (plot == null) { return null; } XYDataset dataset = plot.getDataset(datasetIndex); if (dataset == null) { return null; } LegendItem result = null; if (getItemVisible(series, 0)) { String label = getLegendItemLabelGenerator().generateLabel(dataset, series); String description = label; String toolTipText = null; if (getLegendItemToolTipGenerator() != null) { toolTipText = getLegendItemToolTipGenerator().generateLabel( dataset, series); } String urlText = null; if (getLegendItemURLGenerator() != null) { urlText = getLegendItemURLGenerator().generateLabel( dataset, series); } Paint fillPaint = lookupSeriesPaint(series); result = new LegendItem(label, description, toolTipText, urlText, getLegendShape(), fillPaint); result.setLabelFont(lookupLegendTextFont(series)); Paint labelPaint = lookupLegendTextPaint(series); if (labelPaint != null) { result.setLabelPaint(labelPaint); } result.setSeriesKey(dataset.getSeriesKey(series)); result.setSeriesIndex(series); result.setDataset(dataset); result.setDatasetIndex(datasetIndex); } return result; } /** * Tests this renderer for equality with an arbitrary object. This method * returns {@code true} if and only if: * *

    *
  • {@code obj} is not {@code null};
  • *
  • {@code obj} is an instance of {@code XYDotRenderer};
  • *
  • both renderers have the same attribute values. *
* * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYDotRenderer)) { return false; } XYDotRenderer that = (XYDotRenderer) obj; if (this.dotWidth != that.dotWidth) { return false; } if (this.dotHeight != that.dotHeight) { return false; } if (!ShapeUtils.equal(this.legendShape, that.legendShape)) { return false; } return super.equals(obj); } /** * Returns a clone of the renderer. * * @return A clone. * * @throws CloneNotSupportedException if the renderer cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.legendShape = SerialUtils.readShape(stream); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeShape(this.legendShape, stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/XYErrorRenderer.java000066400000000000000000000361061463604235500312360ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * XYErrorRenderer.java * -------------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Stroke; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Objects; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.plot.CrosshairState; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.SerialUtils; import org.jfree.data.Range; import org.jfree.data.xy.IntervalXYDataset; import org.jfree.data.xy.XYDataset; /** * A line and shape renderer that can also display x and/or y-error values. * This renderer expects an {@link IntervalXYDataset}, otherwise it reverts * to the behaviour of the super class. The example shown here is generated by * the {@code XYErrorRendererDemo1.java} program included in the * JFreeChart demo collection: *

* XYErrorRendererSample.png */ public class XYErrorRenderer extends XYLineAndShapeRenderer { /** For serialization. */ static final long serialVersionUID = 5162283570955172424L; /** A flag that controls whether or not the x-error bars are drawn. */ private boolean drawXError; /** A flag that controls whether or not the y-error bars are drawn. */ private boolean drawYError; /** The length of the cap at the end of the error bars. */ private double capLength; /** * The paint used to draw the error bars (if {@code null} we use the * series paint). */ private transient Paint errorPaint; /** * The stroke used to draw the error bars (if {@code null} we use the * series outline stroke). */ private transient Stroke errorStroke; /** * Creates a new {@code XYErrorRenderer} instance. */ public XYErrorRenderer() { super(false, true); this.drawXError = true; this.drawYError = true; this.errorPaint = null; this.errorStroke = null; this.capLength = 4.0; } /** * Returns the flag that controls whether or not the renderer draws error * bars for the x-values. * * @return A boolean. * * @see #setDrawXError(boolean) */ public boolean getDrawXError() { return this.drawXError; } /** * Sets the flag that controls whether or not the renderer draws error * bars for the x-values and, if the flag changes, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param draw the flag value. * * @see #getDrawXError() */ public void setDrawXError(boolean draw) { if (this.drawXError != draw) { this.drawXError = draw; fireChangeEvent(); } } /** * Returns the flag that controls whether or not the renderer draws error * bars for the y-values. * * @return A boolean. * * @see #setDrawYError(boolean) */ public boolean getDrawYError() { return this.drawYError; } /** * Sets the flag that controls whether or not the renderer draws error * bars for the y-values and, if the flag changes, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param draw the flag value. * * @see #getDrawYError() */ public void setDrawYError(boolean draw) { if (this.drawYError != draw) { this.drawYError = draw; fireChangeEvent(); } } /** * Returns the length (in Java2D units) of the cap at the end of the error * bars. * * @return The cap length. * * @see #setCapLength(double) */ public double getCapLength() { return this.capLength; } /** * Sets the length of the cap at the end of the error bars, and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param length the length (in Java2D units). * * @see #getCapLength() */ public void setCapLength(double length) { this.capLength = length; fireChangeEvent(); } /** * Returns the paint used to draw the error bars. If this is * {@code null} (the default), the item paint is used instead. * * @return The paint (possibly {@code null}). * * @see #setErrorPaint(Paint) */ public Paint getErrorPaint() { return this.errorPaint; } /** * Sets the paint used to draw the error bars and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} permitted). * * @see #getErrorPaint() */ public void setErrorPaint(Paint paint) { this.errorPaint = paint; fireChangeEvent(); } /** * Returns the stroke used to draw the error bars. If this is * {@code null} (the default), the item outline stroke is used * instead. * * @return The stroke (possibly {@code null}). * * @see #setErrorStroke(Stroke) */ public Stroke getErrorStroke() { return this.errorStroke; } /** * Sets the stroke used to draw the error bars and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} permitted). * * @see #getErrorStroke() */ public void setErrorStroke(Stroke stroke) { this.errorStroke = stroke; fireChangeEvent(); } /** * Returns the range required by this renderer to display all the domain * values in the specified dataset. * * @param dataset the dataset ({@code null} permitted). * * @return The range, or {@code null} if the dataset is * {@code null}. */ @Override public Range findDomainBounds(XYDataset dataset) { // include the interval if there is one return findDomainBounds(dataset, true); } /** * Returns the range required by this renderer to display all the range * values in the specified dataset. * * @param dataset the dataset ({@code null} permitted). * * @return The range, or {@code null} if the dataset is * {@code null}. */ @Override public Range findRangeBounds(XYDataset dataset) { // include the interval if there is one return findRangeBounds(dataset, true); } /** * Draws the visual representation for one data item. * * @param g2 the graphics output target. * @param state the renderer state. * @param dataArea the data area. * @param info the plot rendering info. * @param plot the plot. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset. * @param series the series index. * @param item the item index. * @param crosshairState the crosshair state. * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item, CrosshairState crosshairState, int pass) { if (pass == 0 && dataset instanceof IntervalXYDataset && getItemVisible(series, item)) { IntervalXYDataset ixyd = (IntervalXYDataset) dataset; PlotOrientation orientation = plot.getOrientation(); if (this.drawXError) { // draw the error bar for the x-interval double x0 = ixyd.getStartXValue(series, item); double x1 = ixyd.getEndXValue(series, item); double y = ixyd.getYValue(series, item); RectangleEdge edge = plot.getDomainAxisEdge(); double xx0 = domainAxis.valueToJava2D(x0, dataArea, edge); double xx1 = domainAxis.valueToJava2D(x1, dataArea, edge); double yy = rangeAxis.valueToJava2D(y, dataArea, plot.getRangeAxisEdge()); Line2D line; Line2D cap1; Line2D cap2; double adj = this.capLength / 2.0; if (orientation == PlotOrientation.VERTICAL) { line = new Line2D.Double(xx0, yy, xx1, yy); cap1 = new Line2D.Double(xx0, yy - adj, xx0, yy + adj); cap2 = new Line2D.Double(xx1, yy - adj, xx1, yy + adj); } else { // PlotOrientation.HORIZONTAL line = new Line2D.Double(yy, xx0, yy, xx1); cap1 = new Line2D.Double(yy - adj, xx0, yy + adj, xx0); cap2 = new Line2D.Double(yy - adj, xx1, yy + adj, xx1); } if (this.errorPaint != null) { g2.setPaint(this.errorPaint); } else { g2.setPaint(getItemPaint(series, item)); } if (this.errorStroke != null) { g2.setStroke(this.errorStroke); } else { g2.setStroke(getItemStroke(series, item)); } g2.draw(line); g2.draw(cap1); g2.draw(cap2); } if (this.drawYError) { // draw the error bar for the y-interval double y0 = ixyd.getStartYValue(series, item); double y1 = ixyd.getEndYValue(series, item); double x = ixyd.getXValue(series, item); RectangleEdge edge = plot.getRangeAxisEdge(); double yy0 = rangeAxis.valueToJava2D(y0, dataArea, edge); double yy1 = rangeAxis.valueToJava2D(y1, dataArea, edge); double xx = domainAxis.valueToJava2D(x, dataArea, plot.getDomainAxisEdge()); Line2D line; Line2D cap1; Line2D cap2; double adj = this.capLength / 2.0; if (orientation == PlotOrientation.VERTICAL) { line = new Line2D.Double(xx, yy0, xx, yy1); cap1 = new Line2D.Double(xx - adj, yy0, xx + adj, yy0); cap2 = new Line2D.Double(xx - adj, yy1, xx + adj, yy1); } else { // PlotOrientation.HORIZONTAL line = new Line2D.Double(yy0, xx, yy1, xx); cap1 = new Line2D.Double(yy0, xx - adj, yy0, xx + adj); cap2 = new Line2D.Double(yy1, xx - adj, yy1, xx + adj); } if (this.errorPaint != null) { g2.setPaint(this.errorPaint); } else { g2.setPaint(getItemPaint(series, item)); } if (this.errorStroke != null) { g2.setStroke(this.errorStroke); } else { g2.setStroke(getItemStroke(series, item)); } g2.draw(line); g2.draw(cap1); g2.draw(cap2); } } super.drawItem(g2, state, dataArea, info, plot, domainAxis, rangeAxis, dataset, series, item, crosshairState, pass); } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYErrorRenderer)) { return false; } XYErrorRenderer that = (XYErrorRenderer) obj; if (this.drawXError != that.drawXError) { return false; } if (this.drawYError != that.drawYError) { return false; } if (this.capLength != that.capLength) { return false; } if (!PaintUtils.equal(this.errorPaint, that.errorPaint)) { return false; } if (!Objects.equals(this.errorStroke, that.errorStroke)) { return false; } return super.equals(obj); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.errorPaint = SerialUtils.readPaint(stream); this.errorStroke = SerialUtils.readStroke(stream); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.errorPaint, stream); SerialUtils.writeStroke(this.errorStroke, stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/XYItemRenderer.java000066400000000000000000001412741463604235500310460ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * XYItemRenderer.java * ------------------- * (C) Copyright 2001-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Mark Watson (www.markwatson.com); * Sylvain Vieujot; * Focus Computer Services Limited; * Richard Atkinson; * */ package org.jfree.chart.renderer.xy; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Rectangle2D; import org.jfree.chart.LegendItem; import org.jfree.chart.LegendItemSource; import org.jfree.chart.annotations.XYAnnotation; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.event.RendererChangeListener; import org.jfree.chart.labels.ItemLabelPosition; import org.jfree.chart.labels.XYItemLabelGenerator; import org.jfree.chart.labels.XYSeriesLabelGenerator; import org.jfree.chart.labels.XYToolTipGenerator; import org.jfree.chart.plot.CrosshairState; import org.jfree.chart.plot.Marker; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.Layer; import org.jfree.chart.urls.XYURLGenerator; import org.jfree.data.Range; import org.jfree.data.xy.XYDataset; /** * Interface for rendering the visual representation of a single (x, y) item on * an {@link XYPlot}. *

* To support cloning charts, it is recommended that renderers implement both * the {@link Cloneable} and {@code PublicCloneable} interfaces. */ public interface XYItemRenderer extends LegendItemSource { /** * Returns the plot that this renderer has been assigned to. * * @return The plot. */ XYPlot getPlot(); /** * Sets the plot that this renderer is assigned to. This method will be * called by the plot class...you do not need to call it yourself. * * @param plot the plot. */ void setPlot(XYPlot plot); /** * Returns the number of passes through the data required by the renderer. * * @return The pass count. */ int getPassCount(); /** * Returns the lower and upper bounds (range) of the x-values in the * specified dataset. * * @param dataset the dataset ({@code null} permitted). * * @return The range. */ Range findDomainBounds(XYDataset dataset); /** * Returns the lower and upper bounds (range) of the y-values in the * specified dataset. The implementation of this method will take * into account the presentation used by the renderers (for example, * a renderer that "stacks" values will return a bigger range than * a renderer that doesn't). * * @param dataset the dataset ({@code null} permitted). * * @return The range (or {@code null} if the dataset is * {@code null} or empty). */ Range findRangeBounds(XYDataset dataset); /** * Add a renderer change listener. * * @param listener the listener. * * @see #removeChangeListener(RendererChangeListener) */ void addChangeListener(RendererChangeListener listener); /** * Removes a change listener. * * @param listener the listener. * * @see #addChangeListener(RendererChangeListener) */ void removeChangeListener(RendererChangeListener listener); //// VISIBLE ////////////////////////////////////////////////////////////// /** * Returns a boolean that indicates whether or not the specified item * should be drawn (this is typically used to hide an entire series). * * @param series the series index. * @param item the item index. * * @return A boolean. */ boolean getItemVisible(int series, int item); /** * Returns a boolean that indicates whether or not the specified series * should be drawn (this is typically used to hide an entire series). * * @param series the series index. * * @return A boolean. */ boolean isSeriesVisible(int series); /** * Returns the flag that controls whether a series is visible. * * @param series the series index (zero-based). * * @return The flag (possibly {@code null}). * * @see #setSeriesVisible(int, Boolean) */ Boolean getSeriesVisible(int series); /** * Sets the flag that controls whether a series is visible and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param visible the flag ({@code null} permitted). * * @see #getSeriesVisible(int) */ void setSeriesVisible(int series, Boolean visible); /** * Sets the flag that controls whether a series is visible and, if * requested, sends a {@link RendererChangeEvent} to all registered * listeners. * * @param series the series index. * @param visible the flag ({@code null} permitted). * @param notify notify listeners? * * @see #getSeriesVisible(int) */ void setSeriesVisible(int series, Boolean visible, boolean notify); /** * Returns the default visibility for all series. * * @return The default visibility. * * @see #setDefaultSeriesVisible(boolean) */ boolean getDefaultSeriesVisible(); /** * Sets the default visibility and sends a {@link RendererChangeEvent} to all * registered listeners. * * @param visible the flag. * * @see #getDefaultSeriesVisible() */ void setDefaultSeriesVisible(boolean visible); /** * Sets the default visibility and, if requested, sends * a {@link RendererChangeEvent} to all registered listeners. * * @param visible the visibility. * @param notify notify listeners? * * @see #getDefaultSeriesVisible() */ void setDefaultSeriesVisible(boolean visible, boolean notify); // SERIES VISIBLE IN LEGEND (not yet respected by all renderers) /** * Returns {@code true} if the series should be shown in the legend, * and {@code false} otherwise. * * @param series the series index. * * @return A boolean. */ boolean isSeriesVisibleInLegend(int series); /** * Returns the flag that controls whether a series is visible in the * legend. This method returns only the "per series" settings - to * incorporate the override and base settings as well, you need to use the * {@link #isSeriesVisibleInLegend(int)} method. * * @param series the series index (zero-based). * * @return The flag (possibly {@code null}). * * @see #setSeriesVisibleInLegend(int, Boolean) */ Boolean getSeriesVisibleInLegend(int series); /** * Sets the flag that controls whether a series is visible in the legend * and sends a {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param visible the flag ({@code null} permitted). * * @see #getSeriesVisibleInLegend(int) */ void setSeriesVisibleInLegend(int series, Boolean visible); /** * Sets the flag that controls whether a series is visible in the legend * and, if requested, sends a {@link RendererChangeEvent} to all registered * listeners. * * @param series the series index. * @param visible the flag ({@code null} permitted). * @param notify notify listeners? * * @see #getSeriesVisibleInLegend(int) */ void setSeriesVisibleInLegend(int series, Boolean visible, boolean notify); /** * Returns the default visibility in the legend for all series. * * @return The default visibility. * * @see #setDefaultSeriesVisibleInLegend(boolean) */ boolean getDefaultSeriesVisibleInLegend(); /** * Sets the default visibility in the legend and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param visible the flag. * * @see #getDefaultSeriesVisibleInLegend() */ void setDefaultSeriesVisibleInLegend(boolean visible); /** * Sets the default visibility in the legend and, if requested, sends * a {@link RendererChangeEvent} to all registered listeners. * * @param visible the visibility. * @param notify notify listeners? * * @see #getDefaultSeriesVisibleInLegend() */ void setDefaultSeriesVisibleInLegend(boolean visible, boolean notify); //// PAINT //////////////////////////////////////////////////////////////// /** * Returns the paint used to color data items as they are drawn. * * @param row the row (or series) index (zero-based). * @param column the column (or category) index (zero-based). * * @return The paint (never {@code null}). */ Paint getItemPaint(int row, int column); /** * Returns the paint used to color an item drawn by the renderer. * * @param series the series index (zero-based). * * @return The paint (possibly {@code null}). * * @see #setSeriesPaint(int, Paint) */ Paint getSeriesPaint(int series); /** * Sets the paint used for a series and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param series the series index (zero-based). * @param paint the paint ({@code null} permitted). * * @see #getSeriesPaint(int) */ void setSeriesPaint(int series, Paint paint); /** * Sets the paint used for a series and sends a {@link RendererChangeEvent} * to all registered listeners if requested. * * @param series the series index (zero-based). * @param paint the paint ({@code null} permitted). * @param notify send a change event? * * @see #getSeriesPaint(int) */ void setSeriesPaint(int series, Paint paint, boolean notify); /** * Returns the default paint. * * @return The default paint (never {@code null}). * * @see #setDefaultPaint(Paint) */ Paint getDefaultPaint(); /** * Sets the default paint and sends a {@link RendererChangeEvent} to all * registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getDefaultPaint() */ void setDefaultPaint(Paint paint); /** * Sets the default paint and sends a {@link RendererChangeEvent} to all * registered listeners if requested. * * @param paint the paint ({@code null} not permitted). * @param notify send a change event? * * @see #getDefaultPaint() */ void setDefaultPaint(Paint paint, boolean notify); // FILL PAINT /** * Returns the paint used to fill data items as they are drawn. * * @param row the row (or series) index (zero-based). * @param column the column (or category) index (zero-based). * * @return The paint (never {@code null}). */ Paint getItemFillPaint(int row, int column); /** * Returns the paint used to fill an item drawn by the renderer. * * @param series the series index (zero-based). * * @return The paint (possibly {@code null}). */ Paint getSeriesFillPaint(int series); /** * Sets the paint used for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param paint the paint ({@code null} permitted). */ void setSeriesFillPaint(int series, Paint paint); /** * Sets the paint used for a series and sends a * {@link RendererChangeEvent} to all registered listeners if requested. * * @param series the series index (zero-based). * @param paint the paint ({@code null} permitted). * @param notify send a change event? */ void setSeriesFillPaint(int series, Paint paint, boolean notify); /** * Returns the default paint. * * @return The default paint (never {@code null}). */ Paint getDefaultFillPaint(); /** * Sets the default paint and sends a {@link RendererChangeEvent} to all * registered listeners. * * @param paint the paint ({@code null} not permitted). */ void setDefaultFillPaint(Paint paint); /** * Sets the default paint and sends a {@link RendererChangeEvent} to all * registered listeners if requested. * * @param paint the paint ({@code null} not permitted). * @param notify send a change event? */ void setDefaultFillPaint(Paint paint, boolean notify); //// OUTLINE PAINT //////////////////////////////////////////////////////// /** * Returns the paint used to outline data items as they are drawn. * * @param row the row (or series) index (zero-based). * @param column the column (or category) index (zero-based). * * @return The paint (never {@code null}). */ Paint getItemOutlinePaint(int row, int column); /** * Returns the paint used to outline an item drawn by the renderer. * * @param series the series (zero-based index). * * @return The paint (possibly {@code null}). * * @see #setSeriesOutlinePaint(int, Paint) */ Paint getSeriesOutlinePaint(int series); /** * Sets the paint used for a series outline and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param paint the paint ({@code null} permitted). * * @see #getSeriesOutlinePaint(int) */ void setSeriesOutlinePaint(int series, Paint paint); /** * Sets the paint used for a series outline and sends a * {@link RendererChangeEvent} to all registered listeners if requested. * * @param series the series index (zero-based). * @param paint the paint ({@code null} permitted). * @param notify send a change event? * * @see #getSeriesOutlinePaint(int) */ void setSeriesOutlinePaint(int series, Paint paint, boolean notify); /** * Returns the default outline paint. * * @return The paint (never {@code null}). * * @see #setDefaultOutlinePaint(Paint) */ Paint getDefaultOutlinePaint(); /** * Sets the default outline paint and sends a {@link RendererChangeEvent} to * all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getDefaultOutlinePaint() */ void setDefaultOutlinePaint(Paint paint); /** * Sets the default outline paint and sends a {@link RendererChangeEvent} to * all registered listeners if requested. * * @param paint the paint ({@code null} not permitted). * @param notify send a change event? * * @see #getDefaultOutlinePaint() */ void setDefaultOutlinePaint(Paint paint, boolean notify); //// STROKE /////////////////////////////////////////////////////////////// /** * Returns the stroke used to draw data items. * * @param row the row (or series) index (zero-based). * @param column the column (or category) index (zero-based). * * @return The stroke (never {@code null}). */ Stroke getItemStroke(int row, int column); /** * Returns the stroke used to draw the items in a series. * * @param series the series (zero-based index). * * @return The stroke (possibly {@code null}). * * @see #setSeriesStroke(int, Stroke) */ Stroke getSeriesStroke(int series); /** * Sets the stroke used for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param stroke the stroke ({@code null} permitted). * * @see #getSeriesStroke(int) */ void setSeriesStroke(int series, Stroke stroke); /** * Sets the stroke used for a series and sends a * {@link RendererChangeEvent} to all registered listeners if requested. * * @param series the series index (zero-based). * @param stroke the stroke ({@code null} permitted). * @param notify send a change event? * * @see #getSeriesStroke(int) */ void setSeriesStroke(int series, Stroke stroke, boolean notify); /** * Returns the default stroke. * * @return The default stroke (never {@code null}). * * @see #setDefaultStroke(Stroke) */ Stroke getDefaultStroke(); /** * Sets the default stroke and sends a {@link RendererChangeEvent} to all * registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getDefaultStroke() */ void setDefaultStroke(Stroke stroke); /** * Sets the default stroke and sends a {@link RendererChangeEvent} to all * registered listeners if requested. * * @param stroke the stroke ({@code null} not permitted). * @param notify send a change event? * * @see #getDefaultStroke() */ void setDefaultStroke(Stroke stroke, boolean notify); //// OUTLINE STROKE /////////////////////////////////////////////////////// /** * Returns the stroke used to outline data items. The default * implementation passes control to the lookupSeriesOutlineStroke method. * You can override this method if you require different behaviour. * * @param row the row (or series) index (zero-based). * @param column the column (or category) index (zero-based). * * @return The stroke (never {@code null}). */ Stroke getItemOutlineStroke(int row, int column); /** * Returns the stroke used to outline the items in a series. * * @param series the series (zero-based index). * * @return The stroke (possibly {@code null}). * * @see #setSeriesOutlineStroke(int, Stroke) */ Stroke getSeriesOutlineStroke(int series); /** * Sets the outline stroke used for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param stroke the stroke ({@code null} permitted). * * @see #getSeriesOutlineStroke(int) */ void setSeriesOutlineStroke(int series, Stroke stroke); /** * Sets the outline stroke used for a series and sends a * {@link RendererChangeEvent} to all registered listeners if requested. * * @param series the series index (zero-based). * @param stroke the stroke ({@code null} permitted). * @param notify send a change event? * * @see #getSeriesOutlineStroke(int) */ void setSeriesOutlineStroke(int series, Stroke stroke, boolean notify); /** * Returns the default outline stroke. * * @return The stroke (never {@code null}). * * @see #setDefaultOutlineStroke(Stroke) */ Stroke getDefaultOutlineStroke(); /** * Sets the base outline stroke and sends a {@link RendererChangeEvent} to * all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getDefaultOutlineStroke() */ void setDefaultOutlineStroke(Stroke stroke); /** * Sets the base outline stroke and sends a {@link RendererChangeEvent} to * all registered listeners if requested. * * @param stroke the stroke ({@code null} not permitted). * @param notify send a change event. * * @see #getDefaultOutlineStroke() */ void setDefaultOutlineStroke(Stroke stroke, boolean notify); //// SHAPE //////////////////////////////////////////////////////////////// /** * Returns a shape used to represent a data item. * * @param row the row (or series) index (zero-based). * @param column the column (or category) index (zero-based). * * @return The shape (never {@code null}). */ Shape getItemShape(int row, int column); /** * Returns a shape used to represent the items in a series. * * @param series the series (zero-based index). * * @return The shape (possibly {@code null}). * * @see #setSeriesShape(int, Shape) */ Shape getSeriesShape(int series); /** * Sets the shape used for a series and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param series the series index (zero-based). * @param shape the shape ({@code null} permitted). * * @see #getSeriesShape(int) */ void setSeriesShape(int series, Shape shape); /** * Sets the shape used for a series and sends a {@link RendererChangeEvent} * to all registered listeners if requested. * * @param series the series index (zero-based). * @param shape the shape ({@code null} permitted). * @param notify send a change event? * * @see #getSeriesShape(int) */ void setSeriesShape(int series, Shape shape, boolean notify); /** * Returns the default shape. * * @return The shape (never {@code null}). * * @see #setDefaultShape(Shape) */ Shape getDefaultShape(); /** * Sets the default shape and sends a {@link RendererChangeEvent} to all * registered listeners. * * @param shape the shape ({@code null} not permitted). * * @see #getDefaultShape() */ void setDefaultShape(Shape shape); /** * Sets the default shape and sends a {@link RendererChangeEvent} to all * registered listeners if requested. * * @param shape the shape ({@code null} not permitted). * @param notify send a change event? * * @see #getDefaultShape() */ void setDefaultShape(Shape shape, boolean notify); //// LEGEND ITEMS ///////////////////////////////////////////////////////// /** * Returns a legend item for a series from a dataset. * * @param datasetIndex the dataset index. * @param series the series (zero-based index). * * @return The legend item (possibly {@code null}). */ LegendItem getLegendItem(int datasetIndex, int series); //// LEGEND ITEM LABEL GENERATOR ////////////////////////////////////////// /** * Returns the legend item label generator. * * @return The legend item label generator (never {@code null}). * * @see #setLegendItemLabelGenerator(XYSeriesLabelGenerator) */ XYSeriesLabelGenerator getLegendItemLabelGenerator(); /** * Sets the legend item label generator and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param generator the generator ({@code null} not permitted). */ void setLegendItemLabelGenerator(XYSeriesLabelGenerator generator); //// TOOL TIP GENERATOR /////////////////////////////////////////////////// /** * Returns the tool tip generator for a data item. * * @param row the row index (zero based). * @param column the column index (zero based). * * @return The generator (possibly {@code null}). */ XYToolTipGenerator getToolTipGenerator(int row, int column); /** * Returns the tool tip generator for a series. * * @param series the series index (zero based). * * @return The generator (possibly {@code null}). * * @see #setSeriesToolTipGenerator(int, XYToolTipGenerator) */ XYToolTipGenerator getSeriesToolTipGenerator(int series); /** * Sets the tool tip generator for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero based). * @param generator the generator ({@code null} permitted). * * @see #getSeriesToolTipGenerator(int) */ void setSeriesToolTipGenerator(int series, XYToolTipGenerator generator); /** * Returns the default tool tip generator. * * @return The generator (possibly {@code null}). * * @see #setDefaultToolTipGenerator(XYToolTipGenerator) */ XYToolTipGenerator getDefaultToolTipGenerator(); /** * Sets the default tool tip generator and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param generator the generator ({@code null} permitted). * * @see #getDefaultToolTipGenerator() */ void setDefaultToolTipGenerator(XYToolTipGenerator generator); //// URL GENERATOR //////////////////////////////////////////////////////// /** * Returns the URL generator for HTML image maps. * * @return The URL generator (possibly null). */ XYURLGenerator getURLGenerator(); /** * Sets the URL generator for HTML image maps. * * @param urlGenerator the URL generator (null permitted). */ void setURLGenerator(XYURLGenerator urlGenerator); //// ITEM LABELS VISIBLE ////////////////////////////////////////////////// /** * Returns {@code true} if an item label is visible, and * {@code false} otherwise. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return A boolean. */ boolean isItemLabelVisible(int row, int column); /** * Returns {@code true} if the item labels for a series are visible, * and {@code false} otherwise. * * @param series the series index (zero-based). * * @return A boolean. */ boolean isSeriesItemLabelsVisible(int series); /** * Sets a flag that controls the visibility of the item labels for a * series and sends a {@link RendererChangeEvent} to all registered * listeners. * * @param series the series index (zero-based). * @param visible the flag. * * @see #isSeriesItemLabelsVisible(int) */ void setSeriesItemLabelsVisible(int series, boolean visible); /** * Sets a flag that controls the visibility of the item labels for a series. * * @param series the series index (zero-based). * @param visible the flag ({@code null} permitted). * * @see #isSeriesItemLabelsVisible(int) */ void setSeriesItemLabelsVisible(int series, Boolean visible); /** * Sets the visibility of item labels for a series and, if requested, * sends a {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param visible the visible flag. * @param notify a flag that controls whether or not listeners are * notified. * * @see #isSeriesItemLabelsVisible(int) */ void setSeriesItemLabelsVisible(int series, Boolean visible, boolean notify); /** * Returns the default setting for item label visibility. * * @return A flag (possibly {@code null}). * * @see #setDefaultItemLabelsVisible(boolean) */ boolean getDefaultItemLabelsVisible(); /** * Sets the default flag that controls whether or not item labels are visible. * * @param visible the flag. * * @see #getDefaultItemLabelsVisible() */ void setDefaultItemLabelsVisible(boolean visible); /** * Sets the default visibility for item labels and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param visible the visibility flag. * @param notify a flag that controls whether or not listeners are * notified. * * @see #getDefaultItemLabelsVisible() */ void setDefaultItemLabelsVisible(boolean visible, boolean notify); //// ITEM LABEL GENERATOR ///////////////////////////////////////////////// /** * Returns the item label generator for a data item. * * @param row the row index (zero based). * @param column the column index (zero based). * * @return The generator (possibly {@code null}). */ XYItemLabelGenerator getItemLabelGenerator(int row, int column); /** * Returns the item label generator for a series. * * @param series the series index (zero based). * * @return The generator (possibly {@code null}). * * @see #setSeriesItemLabelGenerator(int, XYItemLabelGenerator) */ XYItemLabelGenerator getSeriesItemLabelGenerator(int series); /** * Sets the item label generator for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero based). * @param generator the generator ({@code null} permitted). * * @see #getSeriesItemLabelGenerator(int) */ void setSeriesItemLabelGenerator(int series, XYItemLabelGenerator generator); /** * Returns the default item label generator. * * @return The generator (possibly {@code null}). * * @see #setDefaultItemLabelGenerator(XYItemLabelGenerator) */ XYItemLabelGenerator getDefaultItemLabelGenerator(); /** * Sets the default item label generator and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param generator the generator ({@code null} permitted). * * @see #getDefaultItemLabelGenerator() */ void setDefaultItemLabelGenerator(XYItemLabelGenerator generator); //// ITEM LABEL FONT /////////////////////////////////////////////////////// /** * Returns the font for an item label. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The font (never {@code null}). */ Font getItemLabelFont(int row, int column); /** * Returns the font for all the item labels in a series. * * @param series the series index (zero-based). * * @return The font (possibly {@code null}). */ Font getSeriesItemLabelFont(int series); /** * Sets the item label font for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param font the font ({@code null} permitted). * * @see #getSeriesItemLabelFont(int) */ void setSeriesItemLabelFont(int series, Font font); /** * Returns the default item label font (this is used when no other font * setting is available). * * @return The font (never {@code null}). * * @see #setDefaultItemLabelFont(Font) */ Font getDefaultItemLabelFont(); /** * Sets the default item label font and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param font the font ({@code null} not permitted). * * @see #getDefaultItemLabelFont() */ void setDefaultItemLabelFont(Font font); //// ITEM LABEL PAINT ///////////////////////////////////////////////////// /** * Returns the paint used to draw an item label. * * @param row the row index (zero based). * @param column the column index (zero based). * * @return The paint (never {@code null}). */ Paint getItemLabelPaint(int row, int column); /** * Returns the paint used to draw the item labels for a series. * * @param series the series index (zero based). * * @return The paint (possibly {@code null}). * * @see #setSeriesItemLabelPaint(int, Paint) */ Paint getSeriesItemLabelPaint(int series); /** * Sets the item label paint for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series (zero based index). * @param paint the paint ({@code null} permitted). * * @see #getSeriesItemLabelPaint(int) */ void setSeriesItemLabelPaint(int series, Paint paint); /** * Returns the default item label paint. * * @return The paint (never {@code null}). */ Paint getDefaultItemLabelPaint(); /** * Sets the default item label paint and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param paint the paint ({@code null} not permitted). */ void setDefaultItemLabelPaint(Paint paint); // POSITIVE ITEM LABEL POSITION... /** * Returns the item label position for positive values. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The item label position (never {@code null}). */ ItemLabelPosition getPositiveItemLabelPosition(int row, int column); /** * Returns the item label position for all positive values in a series. * * @param series the series index (zero-based). * * @return The item label position (never {@code null}). */ ItemLabelPosition getSeriesPositiveItemLabelPosition(int series); /** * Sets the item label position for all positive values in a series and * sends a {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param position the position ({@code null} permitted). */ void setSeriesPositiveItemLabelPosition(int series, ItemLabelPosition position); /** * Sets the item label position for all positive values in a series and (if * requested) sends a {@link RendererChangeEvent} to all registered * listeners. * * @param series the series index (zero-based). * @param position the position ({@code null} permitted). * @param notify notify registered listeners? */ void setSeriesPositiveItemLabelPosition(int series, ItemLabelPosition position, boolean notify); /** * Returns the default positive item label position. * * @return The position (never {@code null}). */ ItemLabelPosition getDefaultPositiveItemLabelPosition(); /** * Sets the default positive item label position. * * @param position the position ({@code null} not permitted). */ void setDefaultPositiveItemLabelPosition(ItemLabelPosition position); /** * Sets the default positive item label position and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param position the position ({@code null} not permitted). * @param notify notify registered listeners? */ void setDefaultPositiveItemLabelPosition(ItemLabelPosition position, boolean notify); // NEGATIVE ITEM LABEL POSITION... /** * Returns the item label position for negative values. This method can be * overridden to provide customisation of the item label position for * individual data items. * * @param row the row index (zero-based). * @param column the column (zero-based). * * @return The item label position (never {@code null}). */ ItemLabelPosition getNegativeItemLabelPosition(int row, int column); /** * Returns the item label position for all negative values in a series. * * @param series the series index (zero-based). * * @return The item label position (never {@code null}). */ ItemLabelPosition getSeriesNegativeItemLabelPosition(int series); /** * Sets the item label position for negative values in a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param position the position ({@code null} permitted). */ void setSeriesNegativeItemLabelPosition(int series, ItemLabelPosition position); /** * Sets the item label position for negative values in a series and (if * requested) sends a {@link RendererChangeEvent} to all registered * listeners. * * @param series the series index (zero-based). * @param position the position ({@code null} permitted). * @param notify notify registered listeners? */ void setSeriesNegativeItemLabelPosition(int series, ItemLabelPosition position, boolean notify); /** * Returns the default item label position for negative values. * * @return The position (never {@code null}). */ ItemLabelPosition getDefaultNegativeItemLabelPosition(); /** * Sets the default item label position for negative values and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param position the position ({@code null} not permitted). */ void setDefaultNegativeItemLabelPosition(ItemLabelPosition position); /** * Sets the default negative item label position and, if requested, sends a * {@link RendererChangeEvent} to all registered listeners. * * @param position the position ({@code null} not permitted). * @param notify notify registered listeners? */ void setDefaultNegativeItemLabelPosition(ItemLabelPosition position, boolean notify); // CREATE ENTITIES /** * Returns {@code true} if an entity should be created for an item, and * {@code false} otherwise. * * @param series the series. * @param item the item. * * @return A boolean. */ boolean getItemCreateEntity(int series, int item); /** * Returns {@code true} if entities should be created for a series, and * {@code false} otherwise. This method can return {@code null} in which * case the renderering framework will look at the default setting. * * @param series the series. * * @return A boolean. */ Boolean getSeriesCreateEntities(int series); /** * Sets a flag that specifies whether or not entities should be created for * a series during rendering, and sends a change event to registered * listeners. * * @param series the series. * @param create the flag value ({@code null} permitted). */ void setSeriesCreateEntities(int series, Boolean create); /** * Sets a flag that specifies whether or not entities should be created for * a series during rendering, and sends a change event to registered * listeners. * * @param series the series. * @param create the flag value ({@code null} permitted). * @param notify send a change event? */ void setSeriesCreateEntities(int series, Boolean create, boolean notify); /** * Returns the default value determining whether or not entities should be * created by the renderer. * * @return A boolean. */ boolean getDefaultCreateEntities(); /** * Sets the default value determining whether or not entities should be * created by the renderer, and sends a change event to all registered * listeners. * * @param create the flag value. */ void setDefaultCreateEntities(boolean create); /** * Sets the default value determining whether or not entities should be * created by the renderer, and sends a change event to all registered * listeners. * * @param create the flag value. * @param notify notify listeners? */ void setDefaultCreateEntities(boolean create, boolean notify); //// ANNOTATIONS ////////////////////////////////////////////////////////// /** * Adds an annotation and sends a {@link RendererChangeEvent} to all * registered listeners. The annotation is added to the foreground * layer. * * @param annotation the annotation ({@code null} not permitted). */ void addAnnotation(XYAnnotation annotation); /** * Adds an annotation to the specified layer. * * @param annotation the annotation ({@code null} not permitted). * @param layer the layer ({@code null} not permitted). */ void addAnnotation(XYAnnotation annotation, Layer layer); /** * Removes the specified annotation and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param annotation the annotation to remove ({@code null} not * permitted). * * @return A boolean to indicate whether or not the annotation was * successfully removed. */ boolean removeAnnotation(XYAnnotation annotation); /** * Removes all annotations and sends a {@link RendererChangeEvent} * to all registered listeners. */ void removeAnnotations(); /** * Draws all the annotations for the specified layer. * * @param g2 the graphics device. * @param dataArea the data area. * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param layer the layer. * @param info the plot rendering info. */ void drawAnnotations(Graphics2D g2, Rectangle2D dataArea, ValueAxis domainAxis, ValueAxis rangeAxis, Layer layer, PlotRenderingInfo info); //// DRAWING ////////////////////////////////////////////////////////////// /** * Initialises the renderer then returns the number of 'passes' through the * data that the renderer will require (usually just one). This method * will be called before the first item is rendered, giving the renderer * an opportunity to initialise any state information it wants to maintain. * The renderer can do nothing if it chooses. * * @param g2 the graphics device. * @param dataArea the area inside the axes. * @param plot the plot. * @param dataset the dataset. * @param info an optional info collection object to return data back to * the caller. * * @return The number of passes the renderer requires. */ XYItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, XYPlot plot, XYDataset dataset, PlotRenderingInfo info); /** * Called for each item to be plotted. *

* The {@link XYPlot} can make multiple passes through the dataset, * depending on the value returned by the renderer's initialise() method. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the area within which the data is being rendered. * @param info collects drawing info. * @param plot the plot (can be used to obtain standard color * information etc). * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset. * @param series the series index (zero-based). * @param item the item index (zero-based). * @param crosshairState crosshair information for the plot * ({@code null} permitted). * @param pass the pass index. */ void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item, CrosshairState crosshairState, int pass); /** * Fills a band between two values on the axis. This can be used to color * bands between the grid lines. * * @param g2 the graphics device. * @param plot the plot. * @param axis the domain axis. * @param dataArea the data area. * @param start the start value. * @param end the end value. */ void fillDomainGridBand(Graphics2D g2, XYPlot plot, ValueAxis axis, Rectangle2D dataArea, double start, double end); /** * Fills a band between two values on the range axis. This can be used to * color bands between the grid lines. * * @param g2 the graphics device. * @param plot the plot. * @param axis the range axis. * @param dataArea the data area. * @param start the start value. * @param end the end value. */ void fillRangeGridBand(Graphics2D g2, XYPlot plot, ValueAxis axis, Rectangle2D dataArea, double start, double end); /** * Draws a grid line against the domain axis. * * @param g2 the graphics device. * @param plot the plot. * @param axis the value axis. * @param dataArea the area for plotting data. * @param value the value. * @param paint the paint ({@code null} not permitted). * @param stroke the stroke ({@code null} not permitted). */ void drawDomainLine(Graphics2D g2, XYPlot plot, ValueAxis axis, Rectangle2D dataArea, double value, Paint paint, Stroke stroke); /** * Draws a line perpendicular to the range axis. * * @param g2 the graphics device. * @param plot the plot. * @param axis the value axis. * @param dataArea the area for plotting data. * @param value the data value. * @param paint the paint ({@code null} not permitted). * @param stroke the stroke ({@code null} not permitted). */ void drawRangeLine(Graphics2D g2, XYPlot plot, ValueAxis axis, Rectangle2D dataArea, double value, Paint paint, Stroke stroke); /** * Draws the specified {@code marker} against the domain axis. * * @param g2 the graphics device. * @param plot the plot. * @param axis the value axis. * @param marker the marker. * @param dataArea the axis data area. */ void drawDomainMarker(Graphics2D g2, XYPlot plot, ValueAxis axis, Marker marker, Rectangle2D dataArea); /** * Draws a horizontal line across the chart to represent a 'range marker'. * * @param g2 the graphics device. * @param plot the plot. * @param axis the value axis. * @param marker the marker line. * @param dataArea the axis data area. */ void drawRangeMarker(Graphics2D g2, XYPlot plot, ValueAxis axis, Marker marker, Rectangle2D dataArea); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/XYItemRendererState.java000066400000000000000000000132551463604235500320440ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * XYItemRendererState.java * ------------------------ * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Ulrich Voigt; * Greg Darke; * */ package org.jfree.chart.renderer.xy; import java.awt.geom.Line2D; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.RendererState; import org.jfree.data.xy.XYDataset; /** * The state for an {@link XYItemRenderer}. */ public class XYItemRendererState extends RendererState { /** * The first item in the series that will be displayed. */ private int firstItemIndex; /** * The last item in the current series that will be displayed. */ private int lastItemIndex; /** * A line object that the renderer can reuse to save instantiating a lot * of objects. */ public Line2D workingLine; /** * A flag that controls whether the plot should pass ALL data items to the * renderer, or just the items that will be visible. */ private boolean processVisibleItemsOnly; /** * Creates a new state. * * @param info the plot rendering info. */ public XYItemRendererState(PlotRenderingInfo info) { super(info); this.workingLine = new Line2D.Double(); this.processVisibleItemsOnly = true; } /** * Returns the flag that controls whether the plot passes all data * items in each series to the renderer, or just the visible items. The * default value is {@code true}. * * @return A boolean. * * @see #setProcessVisibleItemsOnly(boolean) */ public boolean getProcessVisibleItemsOnly() { return this.processVisibleItemsOnly; } /** * Sets the flag that controls whether the plot passes all data * items in each series to the renderer, or just the visible items. * * @param flag the new flag value. */ public void setProcessVisibleItemsOnly(boolean flag) { this.processVisibleItemsOnly = flag; } /** * Returns the first item index (this is updated with each call to * {@link #startSeriesPass(XYDataset, int, int, int, int, int)}. * * @return The first item index. */ public int getFirstItemIndex() { return this.firstItemIndex; } /** * Returns the last item index (this is updated with each call to * {@link #startSeriesPass(XYDataset, int, int, int, int, int)}. * * @return The last item index. */ public int getLastItemIndex() { return this.lastItemIndex; } /** * This method is called by the {@link XYPlot} when it starts a pass * through the (visible) items in a series. The default implementation * records the first and last item indices - override this method to * implement additional specialised behaviour. * * @param dataset the dataset. * @param series the series index. * @param firstItem the index of the first item in the series. * @param lastItem the index of the last item in the series. * @param pass the pass index. * @param passCount the number of passes. * * @see #endSeriesPass(XYDataset, int, int, int, int, int) */ public void startSeriesPass(XYDataset dataset, int series, int firstItem, int lastItem, int pass, int passCount) { this.firstItemIndex = firstItem; this.lastItemIndex = lastItem; } /** * This method is called by the {@link XYPlot} when it ends a pass * through the (visible) items in a series. The default implementation * does nothing, but you can override this method to implement specialised * behaviour. * * @param dataset the dataset. * @param series the series index. * @param firstItem the index of the first item in the series. * @param lastItem the index of the last item in the series. * @param pass the pass index. * @param passCount the number of passes. * * @see #startSeriesPass(XYDataset, int, int, int, int, int) */ public void endSeriesPass(XYDataset dataset, int series, int firstItem, int lastItem, int pass, int passCount) { // do nothing...this is just a hook for subclasses } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/XYLineAndShapeRenderer.java000066400000000000000000001173741463604235500324470ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * XYLineAndShapeRenderer.java * --------------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.GeneralPath; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.LegendItem; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.plot.CrosshairState; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.BooleanList; import org.jfree.chart.util.LineUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; import org.jfree.chart.util.ShapeUtils; import org.jfree.data.xy.XYDataset; /** * A renderer that connects data points with lines and/or draws shapes at each * data point. This renderer is designed for use with the {@link XYPlot} * class. The example shown here is generated by * the {@code XYLineAndShapeRendererDemo2.java} program included in the * JFreeChart demo collection: *

* XYLineAndShapeRendererSample.png * */ public class XYLineAndShapeRenderer extends AbstractXYItemRenderer implements XYItemRenderer, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -7435246895986425885L; /** * A table of flags that control (per series) whether or not lines are * visible. */ private BooleanList seriesLinesVisible; /** The default value returned by the getLinesVisible() method. */ private boolean defaultLinesVisible; /** The shape that is used to represent a line in the legend. */ private transient Shape legendLine; /** * A table of flags that control (per series) whether or not shapes are * visible. */ private BooleanList seriesShapesVisible; /** The default value returned by the getShapeVisible() method. */ private boolean defaultShapesVisible; /** * A table of flags that control (per series) whether or not shapes are * filled. */ private BooleanList seriesShapesFilled; /** The default value returned by the getShapeFilled() method. */ private boolean defaultShapesFilled; /** A flag that controls whether outlines are drawn for shapes. */ private boolean drawOutlines; /** * A flag that controls whether the fill paint is used for filling * shapes. */ private boolean useFillPaint; /** * A flag that controls whether the outline paint is used for drawing shape * outlines. */ private boolean useOutlinePaint; /** * A flag that controls whether or not each series is drawn as a single * path. */ private boolean drawSeriesLineAsPath; /** * Creates a new renderer with both lines and shapes visible. */ public XYLineAndShapeRenderer() { this(true, true); } /** * Creates a new renderer. * * @param lines lines visible? * @param shapes shapes visible? */ public XYLineAndShapeRenderer(boolean lines, boolean shapes) { this.seriesLinesVisible = new BooleanList(); this.defaultLinesVisible = lines; this.legendLine = new Line2D.Double(-7.0, 0.0, 7.0, 0.0); this.seriesShapesVisible = new BooleanList(); this.defaultShapesVisible = shapes; this.useFillPaint = false; // use item paint for fills by default this.seriesShapesFilled = new BooleanList(); this.defaultShapesFilled = true; this.drawOutlines = true; this.useOutlinePaint = false; // use item paint for outlines by // default, not outline paint this.drawSeriesLineAsPath = false; } /** * Returns a flag that controls whether each series is drawn as a single path. The default value is {@code false}. * * @return A boolean. * * @see #setDrawSeriesLineAsPath(boolean) */ public boolean getDrawSeriesLineAsPath() { return this.drawSeriesLineAsPath; } /** * Sets the flag that controls whether each series is drawn as a * single path and sends a {@link RendererChangeEvent} to all registered * listeners. * * @param flag the flag. * * @see #getDrawSeriesLineAsPath() */ public void setDrawSeriesLineAsPath(boolean flag) { if (this.drawSeriesLineAsPath != flag) { this.drawSeriesLineAsPath = flag; fireChangeEvent(); } } /** * Returns the number of passes through the data that the renderer requires * in order to draw the chart. Most charts will require a single pass, but * some require two passes. * * @return The pass count. */ @Override public int getPassCount() { return 2; } // LINES VISIBLE /** * Returns the flag used to control whether or not the shape for an item is * visible. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return A boolean. */ public boolean getItemLineVisible(int series, int item) { Boolean flag = getSeriesLinesVisible(series); if (flag != null) { return flag; } return this.defaultLinesVisible; } /** * Returns the flag used to control whether or not the lines for a series * are visible. * * @param series the series index (zero-based). * * @return The flag (possibly {@code null}). * * @see #setSeriesLinesVisible(int, Boolean) */ public Boolean getSeriesLinesVisible(int series) { return this.seriesLinesVisible.getBoolean(series); } /** * Sets the 'lines visible' flag for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param flag the flag ({@code null} permitted). * * @see #getSeriesLinesVisible(int) */ public void setSeriesLinesVisible(int series, Boolean flag) { this.seriesLinesVisible.setBoolean(series, flag); fireChangeEvent(); } /** * Sets the 'lines visible' flag for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param visible the flag. * * @see #getSeriesLinesVisible(int) */ public void setSeriesLinesVisible(int series, boolean visible) { setSeriesLinesVisible(series, Boolean.valueOf(visible)); } /** * Returns the default 'lines visible' attribute. * * @return The default flag. * * @see #setDefaultLinesVisible(boolean) */ public boolean getDefaultLinesVisible() { return this.defaultLinesVisible; } /** * Sets the default 'lines visible' flag and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param flag the flag. * * @see #getDefaultLinesVisible() */ public void setDefaultLinesVisible(boolean flag) { this.defaultLinesVisible = flag; fireChangeEvent(); } /** * Returns the shape used to represent a line in the legend. * * @return The legend line (never {@code null}). * * @see #setLegendLine(Shape) */ public Shape getLegendLine() { return this.legendLine; } /** * Sets the shape used as a line in each legend item and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param line the line ({@code null} not permitted). * * @see #getLegendLine() */ public void setLegendLine(Shape line) { Args.nullNotPermitted(line, "line"); this.legendLine = line; fireChangeEvent(); } // SHAPES VISIBLE /** * Returns the flag used to control whether or not the shape for an item is * visible. *

* The default implementation passes control to the * {@code getSeriesShapesVisible()} method. You can override this method * if you require different behaviour. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return A boolean. */ public boolean getItemShapeVisible(int series, int item) { Boolean flag = getSeriesShapesVisible(series); if (flag != null) { return flag; } return this.defaultShapesVisible; } /** * Returns the flag used to control whether or not the shapes for a series * are visible. * * @param series the series index (zero-based). * * @return A boolean. * * @see #setSeriesShapesVisible(int, Boolean) */ public Boolean getSeriesShapesVisible(int series) { return this.seriesShapesVisible.getBoolean(series); } /** * Sets the 'shapes visible' flag for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param visible the flag. * * @see #getSeriesShapesVisible(int) */ public void setSeriesShapesVisible(int series, boolean visible) { setSeriesShapesVisible(series, Boolean.valueOf(visible)); } /** * Sets the 'shapes visible' flag for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param flag the flag. * * @see #getSeriesShapesVisible(int) */ public void setSeriesShapesVisible(int series, Boolean flag) { this.seriesShapesVisible.setBoolean(series, flag); fireChangeEvent(); } /** * Returns the default 'shape visible' attribute. * * @return The default flag. * * @see #setDefaultShapesVisible(boolean) */ public boolean getDefaultShapesVisible() { return this.defaultShapesVisible; } /** * Sets the default 'shapes visible' flag and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param flag the flag. * * @see #getDefaultShapesVisible() */ public void setDefaultShapesVisible(boolean flag) { this.defaultShapesVisible = flag; fireChangeEvent(); } // SHAPES FILLED /** * Returns the flag used to control whether or not the shape for an item * is filled. *

* The default implementation passes control to the * {@code getSeriesShapesFilled} method. You can override this method * if you require different behaviour. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return A boolean. */ public boolean getItemShapeFilled(int series, int item) { Boolean flag = getSeriesShapesFilled(series); if (flag != null) { return flag; } return this.defaultShapesFilled; } /** * Returns the flag used to control whether or not the shapes for a series * are filled. * * @param series the series index (zero-based). * * @return A boolean. * * @see #setSeriesShapesFilled(int, Boolean) */ public Boolean getSeriesShapesFilled(int series) { return this.seriesShapesFilled.getBoolean(series); } /** * Sets the 'shapes filled' flag for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param flag the flag. * * @see #getSeriesShapesFilled(int) */ public void setSeriesShapesFilled(int series, boolean flag) { setSeriesShapesFilled(series, Boolean.valueOf(flag)); } /** * Sets the 'shapes filled' flag for a series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param series the series index (zero-based). * @param flag the flag. * * @see #getSeriesShapesFilled(int) */ public void setSeriesShapesFilled(int series, Boolean flag) { this.seriesShapesFilled.setBoolean(series, flag); fireChangeEvent(); } /** * Returns the default 'shape filled' attribute. * * @return The default flag. * * @see #setDefaultShapesFilled(boolean) */ public boolean getDefaultShapesFilled() { return this.defaultShapesFilled; } /** * Sets the default 'shapes filled' flag and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param flag the flag. * * @see #getDefaultShapesFilled() */ public void setDefaultShapesFilled(boolean flag) { this.defaultShapesFilled = flag; fireChangeEvent(); } /** * Returns {@code true} if outlines should be drawn for shapes, and * {@code false} otherwise. * * @return A boolean. * * @see #setDrawOutlines(boolean) */ public boolean getDrawOutlines() { return this.drawOutlines; } /** * Sets the flag that controls whether outlines are drawn for * shapes, and sends a {@link RendererChangeEvent} to all registered * listeners. *

* In some cases, shapes look better if they do NOT have an outline, but * this flag allows you to set your own preference. * * @param flag the flag. * * @see #getDrawOutlines() */ public void setDrawOutlines(boolean flag) { this.drawOutlines = flag; fireChangeEvent(); } /** * Returns {@code true} if the renderer should use the fill paint * setting to fill shapes, and {@code false} if it should just * use the regular paint. *

* Refer to {@code XYLineAndShapeRendererDemo2.java} to see the * effect of this flag. * * @return A boolean. * * @see #setUseFillPaint(boolean) * @see #getUseOutlinePaint() */ public boolean getUseFillPaint() { return this.useFillPaint; } /** * Sets the flag that controls whether the fill paint is used to fill * shapes, and sends a {@link RendererChangeEvent} to all * registered listeners. * * @param flag the flag. * * @see #getUseFillPaint() */ public void setUseFillPaint(boolean flag) { this.useFillPaint = flag; fireChangeEvent(); } /** * Returns {@code true} if the renderer should use the outline paint * setting to draw shape outlines, and {@code false} if it should just * use the regular paint. * * @return A boolean. * * @see #setUseOutlinePaint(boolean) * @see #getUseFillPaint() */ public boolean getUseOutlinePaint() { return this.useOutlinePaint; } /** * Sets the flag that controls whether the outline paint is used to draw * shape outlines, and sends a {@link RendererChangeEvent} to all * registered listeners. *

* Refer to {@code XYLineAndShapeRendererDemo2.java} to see the * effect of this flag. * * @param flag the flag. * * @see #getUseOutlinePaint() */ public void setUseOutlinePaint(boolean flag) { this.useOutlinePaint = flag; fireChangeEvent(); } /** * Records the state for the renderer. This is used to preserve state * information between calls to the drawItem() method for a single chart * drawing. */ public static class State extends XYItemRendererState { /** The path for the current series. */ public GeneralPath seriesPath; /** * A flag that indicates if the last (x, y) point was 'good' * (non-null). */ private boolean lastPointGood; /** * Creates a new state instance. * * @param info the plot rendering info. */ public State(PlotRenderingInfo info) { super(info); this.seriesPath = new GeneralPath(); } /** * Returns a flag that indicates if the last point drawn (in the * current series) was 'good' (non-null). * * @return A boolean. */ public boolean isLastPointGood() { return this.lastPointGood; } /** * Sets a flag that indicates if the last point drawn (in the current * series) was 'good' (non-null). * * @param good the flag. */ public void setLastPointGood(boolean good) { this.lastPointGood = good; } /** * This method is called by the {@link XYPlot} at the start of each * series pass. We reset the state for the current series. * * @param dataset the dataset. * @param series the series index. * @param firstItem the first item index for this pass. * @param lastItem the last item index for this pass. * @param pass the current pass index. * @param passCount the number of passes. */ @Override public void startSeriesPass(XYDataset dataset, int series, int firstItem, int lastItem, int pass, int passCount) { this.seriesPath.reset(); this.lastPointGood = false; super.startSeriesPass(dataset, series, firstItem, lastItem, pass, passCount); } } /** * Initialises the renderer. *

* This method will be called before the first item is rendered, giving the * renderer an opportunity to initialise any state information it wants to * maintain. The renderer can do nothing if it chooses. * * @param g2 the graphics device. * @param dataArea the area inside the axes. * @param plot the plot. * @param data the data. * @param info an optional info collection object to return data back to * the caller. * * @return The renderer state. */ @Override public XYItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, XYPlot plot, XYDataset data, PlotRenderingInfo info) { return new State(info); } /** * Draws the visual representation of a single data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the area within which the data is being drawn. * @param info collects information about the drawing. * @param plot the plot (can be used to obtain standard color * information etc). * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset. * @param series the series index (zero-based). * @param item the item index (zero-based). * @param crosshairState crosshair information for the plot * ({@code null} permitted). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item, CrosshairState crosshairState, int pass) { // do nothing if item is not visible if (!getItemVisible(series, item)) { return; } // first pass draws the background (lines, for instance) if (isLinePass(pass)) { if (getItemLineVisible(series, item)) { if (this.drawSeriesLineAsPath) { drawPrimaryLineAsPath(state, g2, plot, dataset, pass, series, item, domainAxis, rangeAxis, dataArea); } else { drawPrimaryLine(state, g2, plot, dataset, pass, series, item, domainAxis, rangeAxis, dataArea); } } } // second pass adds shapes where the items are .. else if (isItemPass(pass)) { // setup for collecting optional entity info... EntityCollection entities = null; if (info != null && info.getOwner() != null) { entities = info.getOwner().getEntityCollection(); } drawSecondaryPass(g2, plot, dataset, pass, series, item, domainAxis, dataArea, rangeAxis, crosshairState, entities); } } /** * Returns {@code true} if the specified pass is the one for drawing * lines. * * @param pass the pass. * * @return A boolean. */ protected boolean isLinePass(int pass) { return pass == 0; } /** * Returns {@code true} if the specified pass is the one for drawing * items. * * @param pass the pass. * * @return A boolean. */ protected boolean isItemPass(int pass) { return pass == 1; } /** * Draws the item (first pass). This method draws the lines * connecting the items. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the area within which the data is being drawn. * @param plot the plot (can be used to obtain standard color * information etc). * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset. * @param pass the pass. * @param series the series index (zero-based). * @param item the item index (zero-based). */ protected void drawPrimaryLine(XYItemRendererState state, Graphics2D g2, XYPlot plot, XYDataset dataset, int pass, int series, int item, ValueAxis domainAxis, ValueAxis rangeAxis, Rectangle2D dataArea) { if (item == 0) { return; } // get the data point... double x1 = dataset.getXValue(series, item); double y1 = dataset.getYValue(series, item); if (Double.isNaN(y1) || Double.isNaN(x1)) { return; } double x0 = dataset.getXValue(series, item - 1); double y0 = dataset.getYValue(series, item - 1); if (Double.isNaN(y0) || Double.isNaN(x0)) { return; } RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); double transX0 = domainAxis.valueToJava2D(x0, dataArea, xAxisLocation); double transY0 = rangeAxis.valueToJava2D(y0, dataArea, yAxisLocation); double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation); double transY1 = rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation); // only draw if we have good values if (Double.isNaN(transX0) || Double.isNaN(transY0) || Double.isNaN(transX1) || Double.isNaN(transY1)) { return; } PlotOrientation orientation = plot.getOrientation(); boolean visible; if (orientation == PlotOrientation.HORIZONTAL) { state.workingLine.setLine(transY0, transX0, transY1, transX1); } else if (orientation == PlotOrientation.VERTICAL) { state.workingLine.setLine(transX0, transY0, transX1, transY1); } visible = LineUtils.clipLine(state.workingLine, dataArea); if (visible) { drawFirstPassShape(g2, pass, series, item, state.workingLine); } } /** * Draws the first pass shape. * * @param g2 the graphics device. * @param pass the pass. * @param series the series index. * @param item the item index. * @param shape the shape. */ protected void drawFirstPassShape(Graphics2D g2, int pass, int series, int item, Shape shape) { g2.setStroke(getItemStroke(series, item)); g2.setPaint(getItemPaint(series, item)); g2.draw(shape); } /** * Draws the item (first pass). This method draws the lines * connecting the items. Instead of drawing separate lines, * a {@code GeneralPath} is constructed and drawn at the end of * the series painting. * * @param g2 the graphics device. * @param state the renderer state. * @param plot the plot (can be used to obtain standard color information * etc). * @param dataset the dataset. * @param pass the pass. * @param series the series index (zero-based). * @param item the item index (zero-based). * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataArea the area within which the data is being drawn. */ protected void drawPrimaryLineAsPath(XYItemRendererState state, Graphics2D g2, XYPlot plot, XYDataset dataset, int pass, int series, int item, ValueAxis domainAxis, ValueAxis rangeAxis, Rectangle2D dataArea) { RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); // get the data point... double x1 = dataset.getXValue(series, item); double y1 = dataset.getYValue(series, item); double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation); double transY1 = rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation); State s = (State) state; // update path to reflect latest point if (!Double.isNaN(transX1) && !Double.isNaN(transY1)) { float x = (float) transX1; float y = (float) transY1; PlotOrientation orientation = plot.getOrientation(); if (orientation == PlotOrientation.HORIZONTAL) { x = (float) transY1; y = (float) transX1; } if (s.isLastPointGood()) { s.seriesPath.lineTo(x, y); } else { s.seriesPath.moveTo(x, y); } s.setLastPointGood(true); } else { s.setLastPointGood(false); } // if this is the last item, draw the path ... if (item == s.getLastItemIndex()) { // draw path drawFirstPassShape(g2, pass, series, item, s.seriesPath); } } /** * Draws the item shapes and adds chart entities (second pass). This method * draws the shapes which mark the item positions. If {@code entities} * is not {@code null} it will be populated with entity information * for points that fall within the data area. * * @param g2 the graphics device. * @param plot the plot (can be used to obtain standard color * information etc). * @param domainAxis the domain axis. * @param dataArea the area within which the data is being drawn. * @param rangeAxis the range axis. * @param dataset the dataset. * @param pass the pass. * @param series the series index (zero-based). * @param item the item index (zero-based). * @param crosshairState the crosshair state. * @param entities the entity collection. */ protected void drawSecondaryPass(Graphics2D g2, XYPlot plot, XYDataset dataset, int pass, int series, int item, ValueAxis domainAxis, Rectangle2D dataArea, ValueAxis rangeAxis, CrosshairState crosshairState, EntityCollection entities) { Shape entityArea = null; // get the data point... double x1 = dataset.getXValue(series, item); double y1 = dataset.getYValue(series, item); if (Double.isNaN(y1) || Double.isNaN(x1)) { return; } PlotOrientation orientation = plot.getOrientation(); RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation); double transY1 = rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation); if (getItemShapeVisible(series, item)) { Shape shape = getItemShape(series, item); if (orientation == PlotOrientation.HORIZONTAL) { shape = ShapeUtils.createTranslatedShape(shape, transY1, transX1); } else if (orientation == PlotOrientation.VERTICAL) { shape = ShapeUtils.createTranslatedShape(shape, transX1, transY1); } entityArea = shape; if (shape.intersects(dataArea)) { if (getItemShapeFilled(series, item)) { if (this.useFillPaint) { g2.setPaint(getItemFillPaint(series, item)); } else { g2.setPaint(getItemPaint(series, item)); } g2.fill(shape); } if (this.drawOutlines) { if (getUseOutlinePaint()) { g2.setPaint(getItemOutlinePaint(series, item)); } else { g2.setPaint(getItemPaint(series, item)); } g2.setStroke(getItemOutlineStroke(series, item)); g2.draw(shape); } } } double xx = transX1; double yy = transY1; if (orientation == PlotOrientation.HORIZONTAL) { xx = transY1; yy = transX1; } // draw the item label if there is one... if (isItemLabelVisible(series, item)) { drawItemLabel(g2, orientation, dataset, series, item, xx, yy, (y1 < 0.0)); } int datasetIndex = plot.indexOf(dataset); updateCrosshairValues(crosshairState, x1, y1, datasetIndex, transX1, transY1, orientation); // add an entity for the item, but only if it falls within the data // area... if (entities != null && ShapeUtils.isPointInRect(dataArea, xx, yy)) { addEntity(entities, entityArea, dataset, series, item, xx, yy); } } /** * Returns a legend item for the specified series. * * @param datasetIndex the dataset index (zero-based). * @param series the series index (zero-based). * * @return A legend item for the series (possibly {@code null}). */ @Override public LegendItem getLegendItem(int datasetIndex, int series) { XYPlot plot = getPlot(); if (plot == null) { return null; } XYDataset dataset = plot.getDataset(datasetIndex); if (dataset == null) { return null; } if (!getItemVisible(series, 0)) { return null; } String label = getLegendItemLabelGenerator().generateLabel(dataset, series); String description = label; String toolTipText = null; if (getLegendItemToolTipGenerator() != null) { toolTipText = getLegendItemToolTipGenerator().generateLabel( dataset, series); } String urlText = null; if (getLegendItemURLGenerator() != null) { urlText = getLegendItemURLGenerator().generateLabel(dataset, series); } boolean shapeIsVisible = getItemShapeVisible(series, 0); Shape shape = lookupLegendShape(series); boolean shapeIsFilled = getItemShapeFilled(series, 0); Paint fillPaint = (this.useFillPaint ? lookupSeriesFillPaint(series) : lookupSeriesPaint(series)); boolean shapeOutlineVisible = this.drawOutlines; Paint outlinePaint = (this.useOutlinePaint ? lookupSeriesOutlinePaint( series) : lookupSeriesPaint(series)); Stroke outlineStroke = lookupSeriesOutlineStroke(series); boolean lineVisible = getItemLineVisible(series, 0); Stroke lineStroke = lookupSeriesStroke(series); Paint linePaint = lookupSeriesPaint(series); LegendItem result = new LegendItem(label, description, toolTipText, urlText, shapeIsVisible, shape, shapeIsFilled, fillPaint, shapeOutlineVisible, outlinePaint, outlineStroke, lineVisible, this.legendLine, lineStroke, linePaint); result.setLabelFont(lookupLegendTextFont(series)); Paint labelPaint = lookupLegendTextPaint(series); if (labelPaint != null) { result.setLabelPaint(labelPaint); } result.setSeriesKey(dataset.getSeriesKey(series)); result.setSeriesIndex(series); result.setDataset(dataset); result.setDatasetIndex(datasetIndex); return result; } /** * Returns a clone of the renderer. * * @return A clone. * * @throws CloneNotSupportedException if the clone cannot be created. */ @Override public Object clone() throws CloneNotSupportedException { XYLineAndShapeRenderer clone = (XYLineAndShapeRenderer) super.clone(); clone.seriesLinesVisible = (BooleanList) this.seriesLinesVisible.clone(); if (this.legendLine != null) { clone.legendLine = ShapeUtils.clone(this.legendLine); } clone.seriesShapesVisible = (BooleanList) this.seriesShapesVisible.clone(); clone.seriesShapesFilled = (BooleanList) this.seriesShapesFilled.clone(); return clone; } /** * Tests this renderer for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYLineAndShapeRenderer)) { return false; } if (!super.equals(obj)) { return false; } XYLineAndShapeRenderer that = (XYLineAndShapeRenderer) obj; if (!Objects.equals( this.seriesLinesVisible, that.seriesLinesVisible) ) { return false; } if (this.defaultLinesVisible != that.defaultLinesVisible) { return false; } if (!ShapeUtils.equal(this.legendLine, that.legendLine)) { return false; } if (!Objects.equals( this.seriesShapesVisible, that.seriesShapesVisible) ) { return false; } if (this.defaultShapesVisible != that.defaultShapesVisible) { return false; } if (!Objects.equals( this.seriesShapesFilled, that.seriesShapesFilled) ) { return false; } if (this.defaultShapesFilled != that.defaultShapesFilled) { return false; } if (this.drawOutlines != that.drawOutlines) { return false; } if (this.useOutlinePaint != that.useOutlinePaint) { return false; } if (this.useFillPaint != that.useFillPaint) { return false; } if (this.drawSeriesLineAsPath != that.drawSeriesLineAsPath) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code for this instance. */ @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + seriesLinesVisible.hashCode(); result = 31 * result + (defaultLinesVisible ? 1 : 0); result = 31 * result + seriesShapesVisible.hashCode(); result = 31 * result + (defaultShapesVisible ? 1 : 0); result = 31 * result + seriesShapesFilled.hashCode(); result = 31 * result + (defaultShapesFilled ? 1 : 0); result = 31 * result + (drawOutlines ? 1 : 0); result = 31 * result + (useFillPaint ? 1 : 0); result = 31 * result + (useOutlinePaint ? 1 : 0); result = 31 * result + (drawSeriesLineAsPath ? 1 : 0); return result; } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.legendLine = SerialUtils.readShape(stream); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeShape(this.legendLine, stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/XYShapeRenderer.java000066400000000000000000000474131463604235500312100ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * XYShapeRenderer.java * -------------------- * (C) Copyright 2008-present by Andreas Haumer, xS+S and Contributors. * * Original Author: Martin Hoeller (x Software + Systeme xS+S - Andreas * Haumer); * Contributor(s): David Gilbert; * */ package org.jfree.chart.renderer.xy; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Ellipse2D; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.plot.CrosshairState; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.LookupPaintScale; import org.jfree.chart.renderer.PaintScale; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; import org.jfree.chart.util.ShapeUtils; import org.jfree.data.Range; import org.jfree.data.general.DatasetUtils; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYZDataset; /** * A renderer that draws shapes at (x, y) coordinates and, if the dataset * is an instance of {@link XYZDataset}, fills the shapes with a paint that * is based on the z-value (the paint is obtained from a lookup table). The * renderer also allows for optional guidelines, horizontal and vertical lines * connecting the shape to the edges of the plot. *

* The example shown here is generated by the * {@code XYShapeRendererDemo1.java} program included in the JFreeChart * demo collection: *

* XYShapeRendererSample.png *

* This renderer has similarities to, but also differences from, the * {@link XYLineAndShapeRenderer}. */ public class XYShapeRenderer extends AbstractXYItemRenderer implements XYItemRenderer, Cloneable, PublicCloneable, Serializable { /** Auto generated serial version id. */ private static final long serialVersionUID = 8320552104211173221L; /** The paint scale (never null). */ private PaintScale paintScale; /** A flag that controls whether or not the shape outlines are drawn. */ private boolean drawOutlines; /** * A flag that controls whether or not the outline paint is used (if not, * the regular paint is used). */ private boolean useOutlinePaint; /** * A flag that controls whether or not the fill paint is used (if not, * the fill paint is used). */ private boolean useFillPaint; /** Flag indicating if guide lines should be drawn for every item. */ private boolean guideLinesVisible; /** The paint used for drawing the guide lines (never null). */ private transient Paint guideLinePaint; /** The stroke used for drawing the guide lines (never null). */ private transient Stroke guideLineStroke; /** * Creates a new {@code XYShapeRenderer} instance with default * attributes. */ public XYShapeRenderer() { this.paintScale = new LookupPaintScale(); this.useFillPaint = false; this.drawOutlines = false; this.useOutlinePaint = true; this.guideLinesVisible = false; this.guideLinePaint = Color.darkGray; this.guideLineStroke = new BasicStroke(); setDefaultShape(new Ellipse2D.Double(-5.0, -5.0, 10.0, 10.0)); setAutoPopulateSeriesShape(false); } /** * Returns the paint scale used by the renderer. * * @return The paint scale (never {@code null}). * * @see #setPaintScale(PaintScale) */ public PaintScale getPaintScale() { return this.paintScale; } /** * Sets the paint scale used by the renderer and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param scale the scale ({@code null} not permitted). * * @see #getPaintScale() */ public void setPaintScale(PaintScale scale) { Args.nullNotPermitted(scale, "scale"); this.paintScale = scale; notifyListeners(new RendererChangeEvent(this)); } /** * Returns {@code true} if outlines should be drawn for shapes, and * {@code false} otherwise. * * @return A boolean. * * @see #setDrawOutlines(boolean) */ public boolean getDrawOutlines() { return this.drawOutlines; } /** * Sets the flag that controls whether outlines are drawn for * shapes, and sends a {@link RendererChangeEvent} to all registered * listeners. *

* In some cases, shapes look better if they do NOT have an outline, but * this flag allows you to set your own preference. * * @param flag the flag. * * @see #getDrawOutlines() */ public void setDrawOutlines(boolean flag) { this.drawOutlines = flag; fireChangeEvent(); } /** * Returns {@code true} if the renderer should use the fill paint * setting to fill shapes, and {@code false} if it should just * use the regular paint. *

* Refer to {@code XYLineAndShapeRendererDemo2.java} to see the * effect of this flag. * * @return A boolean. * * @see #setUseFillPaint(boolean) * @see #getUseOutlinePaint() */ public boolean getUseFillPaint() { return this.useFillPaint; } /** * Sets the flag that controls whether the fill paint is used to fill * shapes, and sends a {@link RendererChangeEvent} to all * registered listeners. * * @param flag the flag. * * @see #getUseFillPaint() */ public void setUseFillPaint(boolean flag) { this.useFillPaint = flag; fireChangeEvent(); } /** * Returns the flag that controls whether the outline paint is used for * shape outlines. If not, the regular series paint is used. * * @return A boolean. * * @see #setUseOutlinePaint(boolean) */ public boolean getUseOutlinePaint() { return this.useOutlinePaint; } /** * Sets the flag that controls whether the outline paint is used for shape * outlines, and sends a {@link RendererChangeEvent} to all registered * listeners. * * @param use the flag. * * @see #getUseOutlinePaint() */ public void setUseOutlinePaint(boolean use) { this.useOutlinePaint = use; fireChangeEvent(); } /** * Returns a flag that controls whether or not guide lines are drawn for * each data item (the lines are horizontal and vertical "crosshairs" * linking the data point to the axes). * * @return A boolean. * * @see #setGuideLinesVisible(boolean) */ public boolean isGuideLinesVisible() { return this.guideLinesVisible; } /** * Sets the flag that controls whether or not guide lines are drawn for * each data item and sends a {@link RendererChangeEvent} to all registered * listeners. * * @param visible the new flag value. * * @see #isGuideLinesVisible() */ public void setGuideLinesVisible(boolean visible) { this.guideLinesVisible = visible; fireChangeEvent(); } /** * Returns the paint used to draw the guide lines. * * @return The paint (never {@code null}). * * @see #setGuideLinePaint(Paint) */ public Paint getGuideLinePaint() { return this.guideLinePaint; } /** * Sets the paint used to draw the guide lines and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getGuideLinePaint() */ public void setGuideLinePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.guideLinePaint = paint; fireChangeEvent(); } /** * Returns the stroke used to draw the guide lines. * * @return The stroke. * * @see #setGuideLineStroke(Stroke) */ public Stroke getGuideLineStroke() { return this.guideLineStroke; } /** * Sets the stroke used to draw the guide lines and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getGuideLineStroke() */ public void setGuideLineStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.guideLineStroke = stroke; fireChangeEvent(); } /** * Returns the lower and upper bounds (range) of the x-values in the * specified dataset. * * @param dataset the dataset ({@code null} permitted). * * @return The range ({@code null} if the dataset is {@code null} * or empty). */ @Override public Range findDomainBounds(XYDataset dataset) { if (dataset == null) { return null; } Range r = DatasetUtils.findDomainBounds(dataset, false); if (r == null) { return null; } double offset = 0; // TODO getSeriesShape(n).getBounds().width / 2; return new Range(r.getLowerBound() + offset, r.getUpperBound() + offset); } /** * Returns the range of values the renderer requires to display all the * items from the specified dataset. * * @param dataset the dataset ({@code null} permitted). * * @return The range ({@code null} if the dataset is {@code null} * or empty). */ @Override public Range findRangeBounds(XYDataset dataset) { if (dataset == null) { return null; } Range r = DatasetUtils.findRangeBounds(dataset, false); if (r == null) { return null; } double offset = 0; // TODO getSeriesShape(n).getBounds().height / 2; return new Range(r.getLowerBound() + offset, r.getUpperBound() + offset); } /** * Return the range of z-values in the specified dataset. * * @param dataset the dataset ({@code null} permitted). * * @return The range ({@code null} if the dataset is {@code null} * or empty). */ public Range findZBounds(XYZDataset dataset) { if (dataset != null) { return DatasetUtils.findZBounds(dataset); } else { return null; } } /** * Returns the number of passes required by this renderer. * * @return {@code 2}. */ @Override public int getPassCount() { return 2; } /** * Draws the block representing the specified item. * * @param g2 the graphics device. * @param state the state. * @param dataArea the data area. * @param info the plot rendering info. * @param plot the plot. * @param domainAxis the x-axis. * @param rangeAxis the y-axis. * @param dataset the dataset. * @param series the series index. * @param item the item index. * @param crosshairState the crosshair state. * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item, CrosshairState crosshairState, int pass) { Shape hotspot; EntityCollection entities = null; if (info != null) { entities = info.getOwner().getEntityCollection(); } double x = dataset.getXValue(series, item); double y = dataset.getYValue(series, item); if (Double.isNaN(x) || Double.isNaN(y)) { // can't draw anything return; } double transX = domainAxis.valueToJava2D(x, dataArea, plot.getDomainAxisEdge()); double transY = rangeAxis.valueToJava2D(y, dataArea, plot.getRangeAxisEdge()); PlotOrientation orientation = plot.getOrientation(); // draw optional guide lines if ((pass == 0) && this.guideLinesVisible) { g2.setStroke(this.guideLineStroke); g2.setPaint(this.guideLinePaint); if (orientation == PlotOrientation.HORIZONTAL) { g2.draw(new Line2D.Double(transY, dataArea.getMinY(), transY, dataArea.getMaxY())); g2.draw(new Line2D.Double(dataArea.getMinX(), transX, dataArea.getMaxX(), transX)); } else { g2.draw(new Line2D.Double(transX, dataArea.getMinY(), transX, dataArea.getMaxY())); g2.draw(new Line2D.Double(dataArea.getMinX(), transY, dataArea.getMaxX(), transY)); } } else if (pass == 1) { Shape shape = getItemShape(series, item); if (orientation == PlotOrientation.HORIZONTAL) { shape = ShapeUtils.createTranslatedShape(shape, transY, transX); } else if (orientation == PlotOrientation.VERTICAL) { shape = ShapeUtils.createTranslatedShape(shape, transX, transY); } hotspot = shape; if (shape.intersects(dataArea)) { //if (getItemShapeFilled(series, item)) { g2.setPaint(getPaint(dataset, series, item)); g2.fill(shape); //} if (this.drawOutlines) { if (getUseOutlinePaint()) { g2.setPaint(getItemOutlinePaint(series, item)); } else { g2.setPaint(getItemPaint(series, item)); } g2.setStroke(getItemOutlineStroke(series, item)); g2.draw(shape); } } int datasetIndex = plot.indexOf(dataset); updateCrosshairValues(crosshairState, x, y, datasetIndex, transX, transY, orientation); // add an entity for the item... if (entities != null) { addEntity(entities, hotspot, dataset, series, item, 0.0, 0.0); } } } /** * Get the paint for a given series and item from a dataset. * * @param dataset the dataset. * @param series the series index. * @param item the item index. * * @return The paint. */ protected Paint getPaint(XYDataset dataset, int series, int item) { Paint p; if (dataset instanceof XYZDataset) { double z = ((XYZDataset) dataset).getZValue(series, item); p = this.paintScale.getPaint(z); } else { if (this.useFillPaint) { p = getItemFillPaint(series, item); } else { p = getItemPaint(series, item); } } return p; } /** * Tests this instance for equality with an arbitrary object. This method * returns {@code true} if and only if: *

    *
  • {@code obj} is an instance of {@code XYShapeRenderer} (not * {@code null});
  • *
  • {@code obj} has the same field values as this * {@code XYShapeRenderer};
  • *
* * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYShapeRenderer)) { return false; } XYShapeRenderer that = (XYShapeRenderer) obj; if (!this.paintScale.equals(that.paintScale)) { return false; } if (this.drawOutlines != that.drawOutlines) { return false; } if (this.useOutlinePaint != that.useOutlinePaint) { return false; } if (this.useFillPaint != that.useFillPaint) { return false; } if (this.guideLinesVisible != that.guideLinesVisible) { return false; } if (!this.guideLinePaint.equals(that.guideLinePaint)) { return false; } if (!this.guideLineStroke.equals(that.guideLineStroke)) { return false; } return super.equals(obj); } /** * Returns a clone of this renderer. * * @return A clone of this renderer. * * @throws CloneNotSupportedException if there is a problem creating the * clone. */ @Override public Object clone() throws CloneNotSupportedException { XYShapeRenderer clone = (XYShapeRenderer) super.clone(); PublicCloneable pc = (PublicCloneable) this.paintScale; clone.paintScale = (PaintScale) pc.clone(); return clone; } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.guideLinePaint = SerialUtils.readPaint(stream); this.guideLineStroke = SerialUtils.readStroke(stream); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.guideLinePaint, stream); SerialUtils.writeStroke(this.guideLineStroke, stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/XYSplineRenderer.java000066400000000000000000000440701463604235500313760ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * XYSplineRenderer.java * --------------------- * (C) Copyright 2007-present, by Klaus Rheinwald and Contributors. * * Original Author: Klaus Rheinwald; * Contributor(s): Tobias von Petersdorff (tvp@math.umd.edu, * http://www.wam.umd.edu/~petersd/); * David Gilbert; * */ package org.jfree.chart.renderer.xy; import java.awt.GradientPaint; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.geom.GeneralPath; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.List; import java.util.Objects; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.GradientPaintTransformer; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.StandardGradientPaintTransformer; import org.jfree.chart.util.Args; import org.jfree.data.xy.XYDataset; /** * A renderer that connects data points with natural cubic splines and/or * draws shapes at each data point. This renderer is designed for use with * the {@link XYPlot} class. The example shown here is generated by the * {@code XYSplineRendererDemo1.java} program included in the JFreeChart * demo collection: *

* XYSplineRendererSample.png */ public class XYSplineRenderer extends XYLineAndShapeRenderer { /** * An enumeration of the fill types for the renderer. */ public enum FillType { /** No fill. */ NONE, /** Fill towards zero. */ TO_ZERO, /** Fill to lower bound. */ TO_LOWER_BOUND, /** Fill to upper bound. */ TO_UPPER_BOUND } /** * Represents state information that applies to a single rendering of * a chart. */ public static class XYSplineState extends State { /** The area to fill under the curve. */ public GeneralPath fillArea; /** The points. */ public List points; /** * Creates a new state instance. * * @param info the plot rendering info. */ public XYSplineState(PlotRenderingInfo info) { super(info); this.fillArea = new GeneralPath(); this.points = new ArrayList<>(); } } /** * Resolution of splines (number of line segments between points) */ private int precision; /** * A flag that can be set to specify * to fill the area under the spline. */ private FillType fillType; private GradientPaintTransformer gradientPaintTransformer; /** * Creates a new instance with the precision attribute defaulting to 5 * and no fill of the area 'under' the spline. */ public XYSplineRenderer() { this(5, FillType.NONE); } /** * Creates a new renderer with the specified precision * and no fill of the area 'under' (between '0' and) the spline. * * @param precision the number of points between data items. */ public XYSplineRenderer(int precision) { this(precision, FillType.NONE); } /** * Creates a new renderer with the specified precision * and specified fill of the area 'under' (between '0' and) the spline. * * @param precision the number of points between data items. * @param fillType the type of fill beneath the curve ({@code null} * not permitted). */ public XYSplineRenderer(int precision, FillType fillType) { super(); if (precision <= 0) { throw new IllegalArgumentException("Requires precision > 0."); } Args.nullNotPermitted(fillType, "fillType"); this.precision = precision; this.fillType = fillType; this.gradientPaintTransformer = new StandardGradientPaintTransformer(); } /** * Returns the number of line segments used to approximate the spline * curve between data points. * * @return The number of line segments. * * @see #setPrecision(int) */ public int getPrecision() { return this.precision; } /** * Set the resolution of splines and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param p number of line segments between points (must be > 0). * * @see #getPrecision() */ public void setPrecision(int p) { if (p <= 0) { throw new IllegalArgumentException("Requires p > 0."); } this.precision = p; fireChangeEvent(); } /** * Returns the type of fill that the renderer draws beneath the curve. * * @return The type of fill (never {@code null}). * * @see #setFillType(FillType) */ public FillType getFillType() { return this.fillType; } /** * Set the fill type and sends a {@link RendererChangeEvent} * to all registered listeners. * * @param fillType the fill type ({@code null} not permitted). * * @see #getFillType() */ public void setFillType(FillType fillType) { this.fillType = fillType; fireChangeEvent(); } /** * Returns the gradient paint transformer, or {@code null}. * * @return The gradient paint transformer (possibly {@code null}). */ public GradientPaintTransformer getGradientPaintTransformer() { return this.gradientPaintTransformer; } /** * Sets the gradient paint transformer and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param gpt the transformer ({@code null} permitted). */ public void setGradientPaintTransformer(GradientPaintTransformer gpt) { this.gradientPaintTransformer = gpt; fireChangeEvent(); } /** * Initialises the renderer. *

* This method will be called before the first item is rendered, giving the * renderer an opportunity to initialise any state information it wants to * maintain. The renderer can do nothing if it chooses. * * @param g2 the graphics device. * @param dataArea the area inside the axes. * @param plot the plot. * @param data the data. * @param info an optional info collection object to return data back to * the caller. * * @return The renderer state. */ @Override public XYItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, XYPlot plot, XYDataset data, PlotRenderingInfo info) { setDrawSeriesLineAsPath(true); XYSplineState state = new XYSplineState(info); state.setProcessVisibleItemsOnly(false); return state; } /** * Draws the item (first pass). This method draws the lines * connecting the items. Instead of drawing separate lines, * a GeneralPath is constructed and drawn at the end of * the series painting. * * @param g2 the graphics device. * @param state the renderer state. * @param plot the plot (can be used to obtain standard color information * etc). * @param dataset the dataset. * @param pass the pass. * @param series the series index (zero-based). * @param item the item index (zero-based). * @param xAxis the domain axis. * @param yAxis the range axis. * @param dataArea the area within which the data is being drawn. */ @Override protected void drawPrimaryLineAsPath(XYItemRendererState state, Graphics2D g2, XYPlot plot, XYDataset dataset, int pass, int series, int item, ValueAxis xAxis, ValueAxis yAxis, Rectangle2D dataArea) { XYSplineState s = (XYSplineState) state; RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); // get the data points double x1 = dataset.getXValue(series, item); double y1 = dataset.getYValue(series, item); double transX1 = xAxis.valueToJava2D(x1, dataArea, xAxisLocation); double transY1 = yAxis.valueToJava2D(y1, dataArea, yAxisLocation); // Collect points if (!Double.isNaN(transX1) && !Double.isNaN(transY1)) { Point2D p = plot.getOrientation() == PlotOrientation.HORIZONTAL ? new Point2D.Float((float) transY1, (float) transX1) : new Point2D.Float((float) transX1, (float) transY1); if (!s.points.contains(p)) s.points.add(p); } if (item == dataset.getItemCount(series) - 1) { // construct path if (s.points.size() > 1) { Point2D origin; if (this.fillType == FillType.TO_ZERO) { float xz = (float) xAxis.valueToJava2D(0, dataArea, yAxisLocation); float yz = (float) yAxis.valueToJava2D(0, dataArea, yAxisLocation); origin = plot.getOrientation() == PlotOrientation.HORIZONTAL ? new Point2D.Float(yz, xz) : new Point2D.Float(xz, yz); } else if (this.fillType == FillType.TO_LOWER_BOUND) { float xlb = (float) xAxis.valueToJava2D( xAxis.getLowerBound(), dataArea, xAxisLocation); float ylb = (float) yAxis.valueToJava2D( yAxis.getLowerBound(), dataArea, yAxisLocation); origin = plot.getOrientation() == PlotOrientation.HORIZONTAL ? new Point2D.Float(ylb, xlb) : new Point2D.Float(xlb, ylb); } else {// fillType == TO_UPPER_BOUND float xub = (float) xAxis.valueToJava2D( xAxis.getUpperBound(), dataArea, xAxisLocation); float yub = (float) yAxis.valueToJava2D( yAxis.getUpperBound(), dataArea, yAxisLocation); origin = plot.getOrientation() == PlotOrientation.HORIZONTAL ? new Point2D.Float(yub, xub) : new Point2D.Float(xub, yub); } // we need at least two points to draw something Point2D cp0 = s.points.get(0); s.seriesPath.moveTo(cp0.getX(), cp0.getY()); if (this.fillType != FillType.NONE) { if (plot.getOrientation() == PlotOrientation.HORIZONTAL) { s.fillArea.moveTo(origin.getX(), cp0.getY()); } else { s.fillArea.moveTo(cp0.getX(), origin.getY()); } s.fillArea.lineTo(cp0.getX(), cp0.getY()); } if (s.points.size() == 2) { // we need at least 3 points to spline. Draw simple line // for two points Point2D cp1 = s.points.get(1); if (this.fillType != FillType.NONE) { s.fillArea.lineTo(cp1.getX(), cp1.getY()); s.fillArea.lineTo(cp1.getX(), origin.getY()); s.fillArea.closePath(); } s.seriesPath.lineTo(cp1.getX(), cp1.getY()); } else { // construct spline int np = s.points.size(); // number of points float[] d = new float[np]; // Newton form coefficients float[] x = new float[np]; // x-coordinates of nodes float y, oldy; float t, oldt; float[] a = new float[np]; float t1; float t2; float[] h = new float[np]; for (int i = 0; i < np; i++) { Point2D.Float cpi = (Point2D.Float) s.points.get(i); x[i] = cpi.x; d[i] = cpi.y; } for (int i = 1; i <= np - 1; i++) h[i] = x[i] - x[i - 1]; float[] sub = new float[np - 1]; float[] diag = new float[np - 1]; float[] sup = new float[np - 1]; for (int i = 1; i <= np - 2; i++) { diag[i] = (h[i] + h[i + 1]) / 3; sup[i] = h[i + 1] / 6; sub[i] = h[i] / 6; a[i] = (d[i + 1] - d[i]) / h[i + 1] - (d[i] - d[i - 1]) / h[i]; } solveTridiag(sub, diag, sup, a, np - 2); // note that a[0]=a[np-1]=0 oldt = x[0]; oldy = d[0]; for (int i = 1; i <= np - 1; i++) { // loop over intervals between nodes for (int j = 1; j <= this.precision; j++) { t1 = (h[i] * j) / this.precision; t2 = h[i] - t1; y = ((-a[i - 1] / 6 * (t2 + h[i]) * t1 + d[i - 1]) * t2 + (-a[i] / 6 * (t1 + h[i]) * t2 + d[i]) * t1) / h[i]; t = x[i - 1] + t1; s.seriesPath.lineTo(t, y); if (this.fillType != FillType.NONE) { s.fillArea.lineTo(t, y); } } } } // Add last point @ y=0 for fillPath and close path if (this.fillType != FillType.NONE) { if (plot.getOrientation() == PlotOrientation.HORIZONTAL) { s.fillArea.lineTo(origin.getX(), s.points.get( s.points.size() - 1).getY()); } else { s.fillArea.lineTo(s.points.get( s.points.size() - 1).getX(), origin.getY()); } s.fillArea.closePath(); } // fill under the curve... if (this.fillType != FillType.NONE) { Paint fp = getSeriesFillPaint(series); if (this.gradientPaintTransformer != null && fp instanceof GradientPaint) { GradientPaint gp = this.gradientPaintTransformer .transform((GradientPaint) fp, s.fillArea); g2.setPaint(gp); } else { g2.setPaint(fp); } g2.fill(s.fillArea); s.fillArea.reset(); } // then draw the line... drawFirstPassShape(g2, pass, series, item, s.seriesPath); } // reset points vector s.points = new ArrayList<>(); } } private void solveTridiag(float[] sub, float[] diag, float[] sup, float[] b, int n) { /* solve linear system with tridiagonal n by n matrix a using Gaussian elimination *without* pivoting where a(i,i-1) = sub[i] for 2<=i<=n a(i,i) = diag[i] for 1<=i<=n a(i,i+1) = sup[i] for 1<=i<=n-1 (the values sub[1], sup[n] are ignored) right hand side vector b[1:n] is overwritten with solution NOTE: 1...n is used in all arrays, 0 is unused */ int i; /* factorization and forward substitution */ for (i = 2; i <= n; i++) { sub[i] /= diag[i - 1]; diag[i] -= sub[i] * sup[i - 1]; b[i] -= sub[i] * b[i - 1]; } b[n] /= diag[n]; for (i = n - 1; i >= 1; i--) b[i] = (b[i] - sup[i] * b[i + 1]) / diag[i]; } /** * Tests this renderer for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYSplineRenderer)) { return false; } XYSplineRenderer that = (XYSplineRenderer) obj; if (this.precision != that.precision) { return false; } if (this.fillType != that.fillType) { return false; } if (!Objects.equals(this.gradientPaintTransformer, that.gradientPaintTransformer)) { return false; } return super.equals(obj); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/XYStepAreaRenderer.java000066400000000000000000000527411463604235500316540ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * XYStepAreaRenderer.java * ----------------------- * (C) Copyright 2003-present, by Matthias Rose and Contributors. * * Original Author: Matthias Rose (based on XYAreaRenderer.java); * Contributor(s): David Gilbert; * Lukasz Rzeszotarski; * Michal Wozniak; */ package org.jfree.chart.renderer.xy; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Polygon; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Rectangle2D; import java.io.Serializable; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.labels.XYToolTipGenerator; import org.jfree.chart.plot.CrosshairState; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.urls.XYURLGenerator; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.ShapeUtils; import org.jfree.data.xy.XYDataset; /** * A step chart renderer that fills the area between the step and the x-axis. * The example shown here is generated by the * {@code XYStepAreaRendererDemo1.java} program included in the JFreeChart * demo collection: *

* XYStepAreaRendererSample.png */ public class XYStepAreaRenderer extends AbstractXYItemRenderer implements XYItemRenderer, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -7311560779702649635L; /** Useful constant for specifying the type of rendering (shapes only). */ public static final int SHAPES = 1; /** Useful constant for specifying the type of rendering (area only). */ public static final int AREA = 2; /** * Useful constant for specifying the type of rendering (area and shapes). */ public static final int AREA_AND_SHAPES = 3; /** A flag indicating whether or not shapes are drawn at each XY point. */ private boolean shapesVisible; /** A flag that controls whether or not shapes are filled for ALL series. */ private boolean shapesFilled; /** A flag indicating whether or not Area are drawn at each XY point. */ private boolean plotArea; /** A flag that controls whether or not the outline is shown. */ private boolean showOutline; /** Area of the complete series */ protected transient Polygon pArea = null; /** * The value on the range axis which defines the 'lower' border of the * area. */ private double rangeBase; /** * The factor (from 0.0 to 1.0) that determines the position of the * step. */ private double stepPoint; /** * Constructs a new renderer. */ public XYStepAreaRenderer() { this(AREA); } /** * Constructs a new renderer. * * @param type the type of the renderer. */ public XYStepAreaRenderer(int type) { this(type, null, null); } /** * Constructs a new renderer. *

* To specify the type of renderer, use one of the constants: * AREA, SHAPES or AREA_AND_SHAPES. * * @param type the type of renderer. * @param toolTipGenerator the tool tip generator to use * ({@code null} permitted). * @param urlGenerator the URL generator ({@code null} permitted). */ public XYStepAreaRenderer(int type, XYToolTipGenerator toolTipGenerator, XYURLGenerator urlGenerator) { super(); setDefaultToolTipGenerator(toolTipGenerator); setURLGenerator(urlGenerator); if (type == AREA) { this.plotArea = true; } else if (type == SHAPES) { this.shapesVisible = true; } else if (type == AREA_AND_SHAPES) { this.plotArea = true; this.shapesVisible = true; } this.showOutline = false; this.stepPoint = 1.0; } /** * Returns a flag that controls whether or not outlines of the areas are * drawn. * * @return The flag. * * @see #setOutline(boolean) */ public boolean isOutline() { return this.showOutline; } /** * Sets a flag that controls whether or not outlines of the areas are * drawn, and sends a {@link RendererChangeEvent} to all registered * listeners. * * @param show the flag. * * @see #isOutline() */ public void setOutline(boolean show) { this.showOutline = show; fireChangeEvent(); } /** * Returns true if shapes are being plotted by the renderer. * * @return {@code true} if shapes are being plotted by the renderer. * * @see #setShapesVisible(boolean) */ public boolean getShapesVisible() { return this.shapesVisible; } /** * Sets the flag that controls whether or not shapes are displayed for each * data item, and sends a {@link RendererChangeEvent} to all registered * listeners. * * @param flag the flag. * * @see #getShapesVisible() */ public void setShapesVisible(boolean flag) { this.shapesVisible = flag; fireChangeEvent(); } /** * Returns the flag that controls whether or not the shapes are filled. * * @return A boolean. * * @see #setShapesFilled(boolean) */ public boolean isShapesFilled() { return this.shapesFilled; } /** * Sets the 'shapes filled' for ALL series and sends a * {@link RendererChangeEvent} to all registered listeners. * * @param filled the flag. * * @see #isShapesFilled() */ public void setShapesFilled(boolean filled) { this.shapesFilled = filled; fireChangeEvent(); } /** * Returns true if Area is being plotted by the renderer. * * @return {@code true} if Area is being plotted by the renderer. * * @see #setPlotArea(boolean) */ public boolean getPlotArea() { return this.plotArea; } /** * Sets a flag that controls whether or not areas are drawn for each data * item and sends a {@link RendererChangeEvent} to all registered * listeners. * * @param flag the flag. * * @see #getPlotArea() */ public void setPlotArea(boolean flag) { this.plotArea = flag; fireChangeEvent(); } /** * Returns the value on the range axis which defines the 'lower' border of * the area. * * @return {@code double} the value on the range axis which defines * the 'lower' border of the area. * * @see #setRangeBase(double) */ public double getRangeBase() { return this.rangeBase; } /** * Sets the value on the range axis which defines the default border of the * area, and sends a {@link RendererChangeEvent} to all registered * listeners. E.g. setRangeBase(Double.NEGATIVE_INFINITY) lets areas always * reach the lower border of the plotArea. * * @param val the value on the range axis which defines the default border * of the area. * * @see #getRangeBase() */ public void setRangeBase(double val) { this.rangeBase = val; fireChangeEvent(); } /** * Returns the fraction of the domain position between two points on which * the step is drawn. The default is 1.0d, which means the step is drawn * at the domain position of the second`point. If the stepPoint is 0.5d the * step is drawn at half between the two points. * * @return The fraction of the domain position between two points where the * step is drawn. * * @see #setStepPoint(double) */ public double getStepPoint() { return stepPoint; } /** * Sets the step point and sends a {@link RendererChangeEvent} to all * registered listeners. * * @param stepPoint the step point (in the range 0.0 to 1.0) * * @see #getStepPoint() */ public void setStepPoint(double stepPoint) { if (stepPoint < 0.0d || stepPoint > 1.0d) { throw new IllegalArgumentException( "Requires stepPoint in [0.0;1.0]"); } this.stepPoint = stepPoint; fireChangeEvent(); } /** * Initialises the renderer. Here we calculate the Java2D y-coordinate for * zero, since all the bars have their bases fixed at zero. * * @param g2 the graphics device. * @param dataArea the area inside the axes. * @param plot the plot. * @param data the data. * @param info an optional info collection object to return data back to * the caller. * * @return The number of passes required by the renderer. */ @Override public XYItemRendererState initialise(Graphics2D g2, Rectangle2D dataArea, XYPlot plot, XYDataset data, PlotRenderingInfo info) { XYItemRendererState state = super.initialise(g2, dataArea, plot, data, info); // disable visible items optimisation - it doesn't work for this // renderer... state.setProcessVisibleItemsOnly(false); return state; } /** * Draws the visual representation of a single data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the area within which the data is being drawn. * @param info collects information about the drawing. * @param plot the plot (can be used to obtain standard color information * etc). * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset. * @param series the series index (zero-based). * @param item the item index (zero-based). * @param crosshairState crosshair information for the plot * ({@code null} permitted). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item, CrosshairState crosshairState, int pass) { PlotOrientation orientation = plot.getOrientation(); // Get the item count for the series, so that we can know which is the // end of the series. int itemCount = dataset.getItemCount(series); Paint paint = getItemPaint(series, item); Stroke seriesStroke = getItemStroke(series, item); g2.setPaint(paint); g2.setStroke(seriesStroke); // get the data point... double x1 = dataset.getXValue(series, item); double y1 = dataset.getYValue(series, item); double x = x1; double y = Double.isNaN(y1) ? getRangeBase() : y1; double transX1 = domainAxis.valueToJava2D(x, dataArea, plot.getDomainAxisEdge()); double transY1 = rangeAxis.valueToJava2D(y, dataArea, plot.getRangeAxisEdge()); // avoid possible sun.dc.pr.PRException: endPath: bad path transY1 = restrictValueToDataArea(transY1, plot, dataArea); if (this.pArea == null && !Double.isNaN(y1)) { // Create a new Area for the series this.pArea = new Polygon(); // start from Y = rangeBase double transY2 = rangeAxis.valueToJava2D(getRangeBase(), dataArea, plot.getRangeAxisEdge()); // avoid possible sun.dc.pr.PRException: endPath: bad path transY2 = restrictValueToDataArea(transY2, plot, dataArea); // The first point is (x, this.baseYValue) if (orientation == PlotOrientation.VERTICAL) { this.pArea.addPoint((int) transX1, (int) transY2); } else if (orientation == PlotOrientation.HORIZONTAL) { this.pArea.addPoint((int) transY2, (int) transX1); } } double transX0; double transY0; double x0; double y0; if (item > 0) { // get the previous data point... x0 = dataset.getXValue(series, item - 1); y0 = Double.isNaN(y1) ? y1 : dataset.getYValue(series, item - 1); x = x0; y = Double.isNaN(y0) ? getRangeBase() : y0; transX0 = domainAxis.valueToJava2D(x, dataArea, plot.getDomainAxisEdge()); transY0 = rangeAxis.valueToJava2D(y, dataArea, plot.getRangeAxisEdge()); // avoid possible sun.dc.pr.PRException: endPath: bad path transY0 = restrictValueToDataArea(transY0, plot, dataArea); if (Double.isNaN(y1)) { // NULL value -> insert point on base line // instead of 'step point' transX1 = transX0; transY0 = transY1; } if (transY0 != transY1) { // not just a horizontal bar but need to perform a 'step'. double transXs = transX0 + (getStepPoint() * (transX1 - transX0)); if (orientation == PlotOrientation.VERTICAL) { this.pArea.addPoint((int) transXs, (int) transY0); this.pArea.addPoint((int) transXs, (int) transY1); } else if (orientation == PlotOrientation.HORIZONTAL) { this.pArea.addPoint((int) transY0, (int) transXs); this.pArea.addPoint((int) transY1, (int) transXs); } } } Shape shape = null; if (!Double.isNaN(y1)) { // Add each point to Area (x, y) if (orientation == PlotOrientation.VERTICAL) { this.pArea.addPoint((int) transX1, (int) transY1); } else if (orientation == PlotOrientation.HORIZONTAL) { this.pArea.addPoint((int) transY1, (int) transX1); } if (getShapesVisible()) { shape = getItemShape(series, item); if (orientation == PlotOrientation.VERTICAL) { shape = ShapeUtils.createTranslatedShape(shape, transX1, transY1); } else if (orientation == PlotOrientation.HORIZONTAL) { shape = ShapeUtils.createTranslatedShape(shape, transY1, transX1); } if (isShapesFilled()) { g2.fill(shape); } else { g2.draw(shape); } } else { if (orientation == PlotOrientation.VERTICAL) { shape = new Rectangle2D.Double(transX1 - 2, transY1 - 2, 4.0, 4.0); } else if (orientation == PlotOrientation.HORIZONTAL) { shape = new Rectangle2D.Double(transY1 - 2, transX1 - 2, 4.0, 4.0); } } } // Check if the item is the last item for the series or if it // is a NULL value and number of items > 0. We can't draw an area for // a single point. if (getPlotArea() && item > 0 && this.pArea != null && (item == (itemCount - 1) || Double.isNaN(y1))) { double transY2 = rangeAxis.valueToJava2D(getRangeBase(), dataArea, plot.getRangeAxisEdge()); // avoid possible sun.dc.pr.PRException: endPath: bad path transY2 = restrictValueToDataArea(transY2, plot, dataArea); if (orientation == PlotOrientation.VERTICAL) { // Add the last point (x,0) this.pArea.addPoint((int) transX1, (int) transY2); } else if (orientation == PlotOrientation.HORIZONTAL) { // Add the last point (x,0) this.pArea.addPoint((int) transY2, (int) transX1); } // fill the polygon g2.fill(this.pArea); // draw an outline around the Area. if (isOutline()) { g2.setStroke(plot.getOutlineStroke()); g2.setPaint(plot.getOutlinePaint()); g2.draw(this.pArea); } // start new area when needed (see above) this.pArea = null; } // do we need to update the crosshair values? if (!Double.isNaN(y1)) { int datasetIndex = plot.indexOf(dataset); updateCrosshairValues(crosshairState, x1, y1, datasetIndex, transX1, transY1, orientation); } // collect entity and tool tip information... EntityCollection entities = state.getEntityCollection(); if (entities != null && shape != null) { addEntity(entities, shape, dataset, series, item, 0.0, 0.0); } } /** * Tests this renderer for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYStepAreaRenderer)) { return false; } XYStepAreaRenderer that = (XYStepAreaRenderer) obj; if (this.showOutline != that.showOutline) { return false; } if (this.shapesVisible != that.shapesVisible) { return false; } if (this.shapesFilled != that.shapesFilled) { return false; } if (this.plotArea != that.plotArea) { return false; } if (this.rangeBase != that.rangeBase) { return false; } if (this.stepPoint != that.stepPoint) { return false; } return super.equals(obj); } /** * Returns a clone of the renderer. * * @return A clone. * * @throws CloneNotSupportedException if the renderer cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Helper method which returns a value if it lies * inside the visible dataArea and otherwise the corresponding * coordinate on the border of the dataArea. The PlotOrientation * is taken into account. * Useful to avoid possible sun.dc.pr.PRException: endPath: bad path * which occurs when trying to draw lines/shapes which in large part * lie outside of the visible dataArea. * * @param value the value which shall be * @param dataArea the area within which the data is being drawn. * @param plot the plot (can be used to obtain standard color * information etc). * @return {@code double} value inside the data area. */ protected static double restrictValueToDataArea(double value, XYPlot plot, Rectangle2D dataArea) { double min = 0; double max = 0; if (plot.getOrientation() == PlotOrientation.VERTICAL) { min = dataArea.getMinY(); max = dataArea.getMaxY(); } else if (plot.getOrientation() == PlotOrientation.HORIZONTAL) { min = dataArea.getMinX(); max = dataArea.getMaxX(); } if (value < min) { value = min; } else if (value > max) { value = max; } return value; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/XYStepRenderer.java000066400000000000000000000306131463604235500310550ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * XYStepRenderer.java * ------------------- * (C) Copyright 2002-present, by Roger Studner and Contributors. * * Original Author: Roger Studner; * Contributor(s): David Gilbert; * Matthias Rose; * Gerald Struck (fix for bug 1569094); * Ulrich Voigt (patch 1874890); * Martin Hoeller (contribution to patch 1874890); * Matthias Noebl (for Cropster GmbH); * */ package org.jfree.chart.renderer.xy; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Stroke; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import org.jfree.chart.HashUtils; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.labels.XYToolTipGenerator; import org.jfree.chart.plot.CrosshairState; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.urls.XYURLGenerator; import org.jfree.chart.util.LineUtils; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.xy.XYDataset; /** * Line/Step item renderer for an {@link XYPlot}. This class draws lines * between data points, only allowing horizontal or vertical lines (steps). * The example shown here is generated by the * {@code XYStepRendererDemo1.java} program included in the JFreeChart * demo collection: *

* XYStepRendererSample.png */ public class XYStepRenderer extends XYLineAndShapeRenderer implements XYItemRenderer, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -8918141928884796108L; /** * The factor (from 0.0 to 1.0) that determines the position of the * step. */ private double stepPoint = 1.0d; /** * Constructs a new renderer with no tooltip or URL generation. */ public XYStepRenderer() { this(null, null); } /** * Constructs a new renderer with the specified tool tip and URL * generators. * * @param toolTipGenerator the item label generator ({@code null} * permitted). * @param urlGenerator the URL generator ({@code null} permitted). */ public XYStepRenderer(XYToolTipGenerator toolTipGenerator, XYURLGenerator urlGenerator) { super(); setDefaultToolTipGenerator(toolTipGenerator); setURLGenerator(urlGenerator); setDefaultShapesVisible(false); } /** * Returns the fraction of the domain position between two points on which * the step is drawn. The default is 1.0d, which means the step is drawn * at the domain position of the second`point. If the stepPoint is 0.5d the * step is drawn at half between the two points. * * @return The fraction of the domain position between two points where the * step is drawn. * * @see #setStepPoint(double) */ public double getStepPoint() { return this.stepPoint; } /** * Sets the step point and sends a {@link RendererChangeEvent} to all * registered listeners. * * @param stepPoint the step point (in the range 0.0 to 1.0) * * @see #getStepPoint() */ public void setStepPoint(double stepPoint) { if (stepPoint < 0.0d || stepPoint > 1.0d) { throw new IllegalArgumentException( "Requires stepPoint in [0.0;1.0]"); } this.stepPoint = stepPoint; fireChangeEvent(); } /** * Draws the visual representation of a single data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the area within which the data is being drawn. * @param info collects information about the drawing. * @param plot the plot (can be used to obtain standard color * information etc). * @param domainAxis the domain axis. * @param rangeAxis the vertical axis. * @param dataset the dataset. * @param series the series index (zero-based). * @param item the item index (zero-based). * @param crosshairState crosshair information for the plot * ({@code null} permitted). * @param pass the pass index. */ @Override public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item, CrosshairState crosshairState, int pass) { // do nothing if item is not visible if (!getItemVisible(series, item)) { return; } PlotOrientation orientation = plot.getOrientation(); Paint seriesPaint = getItemPaint(series, item); Stroke seriesStroke = getItemStroke(series, item); g2.setPaint(seriesPaint); g2.setStroke(seriesStroke); // get the data point... double x1 = dataset.getXValue(series, item); double y1 = dataset.getYValue(series, item); RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); double transX1 = domainAxis.valueToJava2D(x1, dataArea, xAxisLocation); double transY1 = (Double.isNaN(y1) ? Double.NaN : rangeAxis.valueToJava2D(y1, dataArea, yAxisLocation)); if (pass == 0 && item > 0) { // get the previous data point... double x0 = dataset.getXValue(series, item - 1); double y0 = dataset.getYValue(series, item - 1); double transX0 = domainAxis.valueToJava2D(x0, dataArea, xAxisLocation); double transY0 = (Double.isNaN(y0) ? Double.NaN : rangeAxis.valueToJava2D(y0, dataArea, yAxisLocation)); if (orientation == PlotOrientation.HORIZONTAL) { if (transY0 == transY1) { // this represents the situation // for drawing a horizontal bar. drawLine(g2, state.workingLine, transY0, transX0, transY1, transX1, dataArea); } else { //this handles the need to perform a 'step'. // calculate the step point double transXs = transX0 + (getStepPoint() * (transX1 - transX0)); drawLine(g2, state.workingLine, transY0, transX0, transY0, transXs, dataArea); drawLine(g2, state.workingLine, transY0, transXs, transY1, transXs, dataArea); drawLine(g2, state.workingLine, transY1, transXs, transY1, transX1, dataArea); } } else if (orientation == PlotOrientation.VERTICAL) { if (transY0 == transY1) { // this represents the situation // for drawing a horizontal bar. drawLine(g2, state.workingLine, transX0, transY0, transX1, transY1, dataArea); } else { //this handles the need to perform a 'step'. // calculate the step point double transXs = transX0 + (getStepPoint() * (transX1 - transX0)); drawLine(g2, state.workingLine, transX0, transY0, transXs, transY0, dataArea); drawLine(g2, state.workingLine, transXs, transY0, transXs, transY1, dataArea); drawLine(g2, state.workingLine, transXs, transY1, transX1, transY1, dataArea); } } // submit this data item as a candidate for the crosshair point int datasetIndex = plot.indexOf(dataset); updateCrosshairValues(crosshairState, x1, y1, datasetIndex, transX1, transY1, orientation); // collect entity and tool tip information... EntityCollection entities = state.getEntityCollection(); if (entities != null) { if (orientation == PlotOrientation.HORIZONTAL) { addEntity(entities, null, dataset, series, item, transY1, transX1); } else { addEntity(entities, null, dataset, series, item, transX1, transY1); } } } if (pass == 1) { // draw the item label if there is one... if (isItemLabelVisible(series, item)) { double xx = transX1; double yy = transY1; if (orientation == PlotOrientation.HORIZONTAL) { xx = transY1; yy = transX1; } drawItemLabel(g2, orientation, dataset, series, item, xx, yy, (y1 < 0.0)); } } } /** * A utility method that draws a line but only if none of the coordinates * are NaN values. * * @param g2 the graphics target. * @param line the line object. * @param x0 the x-coordinate for the starting point of the line. * @param y0 the y-coordinate for the starting point of the line. * @param x1 the x-coordinate for the ending point of the line. * @param y1 the y-coordinate for the ending point of the line. */ private void drawLine(Graphics2D g2, Line2D line, double x0, double y0, double x1, double y1, Rectangle2D dataArea) { if (Double.isNaN(x0) || Double.isNaN(x1) || Double.isNaN(y0) || Double.isNaN(y1)) { return; } line.setLine(x0, y0, x1, y1); boolean visible = LineUtils.clipLine(line, dataArea); if (visible) { g2.draw(line); } } /** * Tests this renderer for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYLineAndShapeRenderer)) { return false; } XYStepRenderer that = (XYStepRenderer) obj; if (this.stepPoint != that.stepPoint) { return false; } return super.equals(obj); } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { return HashUtils.hashCode(super.hashCode(), this.stepPoint); } /** * Returns a clone of the renderer. * * @return A clone. * * @throws CloneNotSupportedException if the renderer cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/YIntervalRenderer.java000066400000000000000000000257721463604235500316100ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * YIntervalRenderer.java * ---------------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.labels.ItemLabelPosition; import org.jfree.chart.labels.XYItemLabelGenerator; import org.jfree.chart.plot.CrosshairState; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.text.TextUtils; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.ShapeUtils; import org.jfree.data.Range; import org.jfree.data.xy.IntervalXYDataset; import org.jfree.data.xy.XYDataset; /** * A renderer that draws a line connecting the start and end Y values for an * {@link XYPlot}. The example shown here is generated by the * {@code YIntervalRendererDemo1.java} program included in the JFreeChart * demo collection: *

* YIntervalRendererSample.png */ public class YIntervalRenderer extends AbstractXYItemRenderer implements XYItemRenderer, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -2951586537224143260L; /** * An additional item label generator. If this is non-null, the item * label generated will be displayed near the lower y-value at the * position given by getNegativeItemLabelPosition(). */ private XYItemLabelGenerator additionalItemLabelGenerator; /** * The default constructor. */ public YIntervalRenderer() { super(); this.additionalItemLabelGenerator = null; } /** * Returns the generator for the item labels that appear near the lower * y-value. * * @return The generator (possibly {@code null}). * * @see #setAdditionalItemLabelGenerator(XYItemLabelGenerator) */ public XYItemLabelGenerator getAdditionalItemLabelGenerator() { return this.additionalItemLabelGenerator; } /** * Sets the generator for the item labels that appear near the lower * y-value and sends a {@link RendererChangeEvent} to all registered * listeners. If this is set to {@code null}, no item labels will be * drawn. * * @param generator the generator ({@code null} permitted). * * @see #getAdditionalItemLabelGenerator() */ public void setAdditionalItemLabelGenerator( XYItemLabelGenerator generator) { this.additionalItemLabelGenerator = generator; fireChangeEvent(); } /** * Returns the range of values the renderer requires to display all the * items from the specified dataset. * * @param dataset the dataset ({@code null} permitted). * * @return The range ({@code null} if the dataset is {@code null} or empty). */ @Override public Range findRangeBounds(XYDataset dataset) { return findRangeBounds(dataset, true); } /** * Draws the visual representation of a single data item. * * @param g2 the graphics device. * @param state the renderer state. * @param dataArea the area within which the plot is being drawn. * @param info collects information about the drawing. * @param plot the plot (can be used to obtain standard color * information etc). * @param domainAxis the domain axis. * @param rangeAxis the range axis. * @param dataset the dataset. * @param series the series index (zero-based). * @param item the item index (zero-based). * @param crosshairState crosshair information for the plot * ({@code null} permitted). * @param pass the pass index (ignored here). */ @Override public void drawItem(Graphics2D g2, XYItemRendererState state, Rectangle2D dataArea, PlotRenderingInfo info, XYPlot plot, ValueAxis domainAxis, ValueAxis rangeAxis, XYDataset dataset, int series, int item, CrosshairState crosshairState, int pass) { // do nothing if item is not visible if (!getItemVisible(series, item)) { return; } // setup for collecting optional entity info... EntityCollection entities = null; if (info != null) { entities = info.getOwner().getEntityCollection(); } IntervalXYDataset intervalDataset = (IntervalXYDataset) dataset; double x = intervalDataset.getXValue(series, item); double yLow = intervalDataset.getStartYValue(series, item); double yHigh = intervalDataset.getEndYValue(series, item); RectangleEdge xAxisLocation = plot.getDomainAxisEdge(); RectangleEdge yAxisLocation = plot.getRangeAxisEdge(); double xx = domainAxis.valueToJava2D(x, dataArea, xAxisLocation); double yyLow = rangeAxis.valueToJava2D(yLow, dataArea, yAxisLocation); double yyHigh = rangeAxis.valueToJava2D(yHigh, dataArea, yAxisLocation); Paint p = getItemPaint(series, item); Stroke s = getItemStroke(series, item); Line2D line = null; Shape shape = getItemShape(series, item); Shape top = null; Shape bottom = null; PlotOrientation orientation = plot.getOrientation(); if (orientation == PlotOrientation.HORIZONTAL) { line = new Line2D.Double(yyLow, xx, yyHigh, xx); top = ShapeUtils.createTranslatedShape(shape, yyHigh, xx); bottom = ShapeUtils.createTranslatedShape(shape, yyLow, xx); } else if (orientation == PlotOrientation.VERTICAL) { line = new Line2D.Double(xx, yyLow, xx, yyHigh); top = ShapeUtils.createTranslatedShape(shape, xx, yyHigh); bottom = ShapeUtils.createTranslatedShape(shape, xx, yyLow); } else { throw new IllegalStateException(); } g2.setPaint(p); g2.setStroke(s); g2.draw(line); g2.fill(top); g2.fill(bottom); // for item labels, we have a special case because there is the // possibility to draw (a) the regular item label near to just the // upper y-value, or (b) the regular item label near the upper y-value // PLUS an additional item label near the lower y-value. if (isItemLabelVisible(series, item)) { drawItemLabel(g2, orientation, dataset, series, item, xx, yyHigh, false); drawAdditionalItemLabel(g2, orientation, dataset, series, item, xx, yyLow); } // add an entity for the item... Shape hotspot = ShapeUtils.createLineRegion(line, 4.0f); if (entities != null && hotspot.intersects(dataArea)) { addEntity(entities, hotspot, dataset, series, item, 0.0, 0.0); } } /** * Draws an item label. * * @param g2 the graphics device. * @param orientation the orientation. * @param dataset the dataset. * @param series the series index (zero-based). * @param item the item index (zero-based). * @param x the x coordinate (in Java2D space). * @param y the y coordinate (in Java2D space). */ private void drawAdditionalItemLabel(Graphics2D g2, PlotOrientation orientation, XYDataset dataset, int series, int item, double x, double y) { if (this.additionalItemLabelGenerator == null) { return; } Font labelFont = getItemLabelFont(series, item); Paint paint = getItemLabelPaint(series, item); g2.setFont(labelFont); g2.setPaint(paint); String label = this.additionalItemLabelGenerator.generateLabel(dataset, series, item); ItemLabelPosition position = getNegativeItemLabelPosition(series, item); Point2D anchorPoint = calculateLabelAnchorPoint( position.getItemLabelAnchor(), x, y, orientation); TextUtils.drawRotatedString(label, g2, (float) anchorPoint.getX(), (float) anchorPoint.getY(), position.getTextAnchor(), position.getAngle(), position.getRotationAnchor()); } /** * Tests this renderer for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof YIntervalRenderer)) { return false; } YIntervalRenderer that = (YIntervalRenderer) obj; if (!Objects.equals(this.additionalItemLabelGenerator, that.additionalItemLabelGenerator)) { return false; } return super.equals(obj); } /** * Returns a clone of the renderer. * * @return A clone. * * @throws CloneNotSupportedException if the renderer cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/renderer/xy/package.html000066400000000000000000000002611463604235500276040ustar00rootroot00000000000000 Plug-in renderers for the {@link org.jfree.chart.plot.XYPlot} class. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/servlet/000077500000000000000000000000001463604235500245425ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/servlet/ChartDeleter.java000066400000000000000000000072231463604235500277570ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * ChartDeleter.java * ----------------- * (C) Copyright 2002-present, by Richard Atkinson and Contributors. * * Original Author: Richard Atkinson; * Contributor(s): -; * */ package org.jfree.chart.servlet; import java.io.File; import java.io.Serializable; import java.util.Iterator; import java.util.List; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; /** * Used for deleting charts from the temporary directory when the users session * expires. */ public class ChartDeleter implements HttpSessionBindingListener, Serializable { /** The chart names. */ private final List chartNames = new java.util.ArrayList(); /** * Blank constructor. */ public ChartDeleter() { super(); } /** * Add a chart to be deleted when the session expires * * @param filename the name of the chart in the temporary directory to be * deleted. */ public void addChart(String filename) { this.chartNames.add(filename); } /** * Checks to see if a chart is in the list of charts to be deleted * * @param filename the name of the chart in the temporary directory. * * @return A boolean value indicating whether the chart is present in the * list. */ public boolean isChartAvailable(String filename) { return (this.chartNames.contains(filename)); } /** * Binding this object to the session has no additional effects. * * @param event the session bind event. */ @Override public void valueBound(HttpSessionBindingEvent event) { // nothing to do } /** * When this object is unbound from the session (including upon session * expiry) the files that have been added to the ArrayList are iterated * and deleted. * * @param event the session unbind event. */ @Override public void valueUnbound(HttpSessionBindingEvent event) { Iterator iter = this.chartNames.listIterator(); while (iter.hasNext()) { String filename = (String) iter.next(); File file = new File( System.getProperty("java.io.tmpdir"), filename ); if (file.exists()) { file.delete(); } } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/servlet/DisplayChart.java000066400000000000000000000116361463604235500300030ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * DisplayChart.java * ----------------- * (C) Copyright 2002-present, by Richard Atkinson and Contributors. * * Original Author: Richard Atkinson; * Contributor(s): David Gilbert; * */ package org.jfree.chart.servlet; import java.io.File; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; /** * Servlet used for streaming charts to the client browser from the temporary * directory. You need to add this servlet and mapping to your deployment * descriptor (web.xml) in order to get it to work. The syntax is as follows: * * <xmp> * <servlet> * <servlet-name>DisplayChart</servlet-name> * <servlet-class>org.jfree.chart.servlet.DisplayChart</servlet-class> * </servlet> * <servlet-mapping> * <servlet-name>DisplayChart</servlet-name> * <url-pattern>/servlet/DisplayChart</url-pattern> * </servlet-mapping> * </xmp> */ public class DisplayChart extends HttpServlet { /** * Default constructor. */ public DisplayChart() { super(); } /** * Init method. * * @throws ServletException never. */ @Override public void init() throws ServletException { // nothing to do } /** * Service method. * * @param request the request. * @param response the response. * * @throws ServletException ??. * @throws IOException ??. */ @Override public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpSession session = request.getSession(); String filename = request.getParameter("filename"); if (filename == null) { throw new ServletException("Parameter 'filename' must be supplied"); } // Replace ".." with "" // This is to prevent access to the rest of the file system filename = ServletUtilities.searchReplace(filename, "..", ""); // Check the file exists File file = new File(System.getProperty("java.io.tmpdir"), filename); if (!file.exists()) { throw new ServletException( "Unable to display the chart with the filename '" + filename + "'."); } // Check that the graph being served was created by the current user // or that it begins with "public" boolean isChartInUserList = false; ChartDeleter chartDeleter = (ChartDeleter) session.getAttribute( "JFreeChart_Deleter"); if (chartDeleter != null) { isChartInUserList = chartDeleter.isChartAvailable(filename); } boolean isChartPublic = false; if (filename.length() >= 6) { if (filename.startsWith("public")) { isChartPublic = true; } } boolean isOneTimeChart = false; if (filename.startsWith(ServletUtilities.getTempOneTimeFilePrefix())) { isOneTimeChart = true; } if (isChartInUserList || isChartPublic || isOneTimeChart) { // Serve it up ServletUtilities.sendTempFile(file, response); if (isOneTimeChart) { file.delete(); } } else { throw new ServletException("Chart image not found"); } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/servlet/ServletUtilities.java000066400000000000000000000372121463604235500307320ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * ServletUtilities.java * --------------------- * (C) Copyright 2002-present, by Richard Atkinson and Contributors. * * Original Author: Richard Atkinson; * Contributor(s): J?rgen Hoffman; * David Gilbert; * Douglas Clayton; * */ package org.jfree.chart.servlet; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import java.util.TimeZone; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.jfree.chart.ChartRenderingInfo; import org.jfree.chart.ChartUtils; import org.jfree.chart.JFreeChart; import org.jfree.chart.util.Args; /** * Utility class used for servlet related JFreeChart operations. */ public class ServletUtilities { /** The filename prefix. */ private static String tempFilePrefix = "jfreechart-"; /** A prefix for "one time" charts. */ private static String tempOneTimeFilePrefix = "jfreechart-onetime-"; /** * Returns the prefix for the temporary file names generated by this class. * * @return The prefix (never {@code null}). */ public static String getTempFilePrefix() { return ServletUtilities.tempFilePrefix; } /** * Sets the prefix for the temporary file names generated by this class. * * @param prefix the prefix ({@code null} not permitted). */ public static void setTempFilePrefix(String prefix) { Args.nullNotPermitted(prefix, "prefix"); ServletUtilities.tempFilePrefix = prefix; } /** * Returns the prefix for "one time" temporary file names generated by * this class. * * @return The prefix. */ public static String getTempOneTimeFilePrefix() { return ServletUtilities.tempOneTimeFilePrefix; } /** * Sets the prefix for the "one time" temporary file names generated by * this class. * * @param prefix the prefix ({@code null} not permitted). */ public static void setTempOneTimeFilePrefix(String prefix) { Args.nullNotPermitted(prefix, "prefix"); ServletUtilities.tempOneTimeFilePrefix = prefix; } /** * Saves the chart as a PNG format file in the temporary directory. * * @param chart the JFreeChart to be saved. * @param width the width of the chart. * @param height the height of the chart. * @param session the HttpSession of the client (if {@code null}, the * temporary file is marked as "one-time" and deleted by * the {@link DisplayChart} servlet right after it is * streamed to the client). * * @return The filename of the chart saved in the temporary directory. * * @throws IOException if there is a problem saving the file. */ public static String saveChartAsPNG(JFreeChart chart, int width, int height, HttpSession session) throws IOException { return ServletUtilities.saveChartAsPNG(chart, width, height, null, session); } /** * Saves the chart as a PNG format file in the temporary directory and * populates the {@link ChartRenderingInfo} object which can be used to * generate an HTML image map. * * @param chart the chart to be saved ({@code null} not permitted). * @param width the width of the chart. * @param height the height of the chart. * @param info the ChartRenderingInfo object to be populated * ({@code null} permitted). * @param session the HttpSession of the client (if {@code null}, the * temporary file is marked as "one-time" and deleted by * the {@link DisplayChart} servlet right after it is * streamed to the client). * * @return The filename of the chart saved in the temporary directory. * * @throws IOException if there is a problem saving the file. */ public static String saveChartAsPNG(JFreeChart chart, int width, int height, ChartRenderingInfo info, HttpSession session) throws IOException { Args.nullNotPermitted(chart, "chart"); ServletUtilities.createTempDir(); String prefix = ServletUtilities.tempFilePrefix; if (session == null) { prefix = ServletUtilities.tempOneTimeFilePrefix; } File tempFile = File.createTempFile(prefix, ".png", new File(System.getProperty("java.io.tmpdir"))); ChartUtils.saveChartAsPNG(tempFile, chart, width, height, info); if (session != null) { ServletUtilities.registerChartForDeletion(tempFile, session); } return tempFile.getName(); } /** * Saves the chart as a JPEG format file in the temporary directory. *

* SPECIAL NOTE: Please avoid using JPEG as an image format for charts, * it is a "lossy" format that introduces visible distortions in the * resulting image - use PNG instead. In addition, note that JPEG output * is supported by JFreeChart only for JRE 1.4.2 or later. * * @param chart the JFreeChart to be saved. * @param width the width of the chart. * @param height the height of the chart. * @param session the HttpSession of the client (if {@code null}, the * temporary file is marked as "one-time" and deleted by * the {@link DisplayChart} servlet right after it is * streamed to the client). * * @return The filename of the chart saved in the temporary directory. * * @throws IOException if there is a problem saving the file. */ public static String saveChartAsJPEG(JFreeChart chart, int width, int height, HttpSession session) throws IOException { return ServletUtilities.saveChartAsJPEG(chart, width, height, null, session); } /** * Saves the chart as a JPEG format file in the temporary directory and * populates the {@code ChartRenderingInfo} object which can be used * to generate an HTML image map. *

* SPECIAL NOTE: Please avoid using JPEG as an image format for charts, * it is a "lossy" format that introduces visible distortions in the * resulting image - use PNG instead. In addition, note that JPEG output * is supported by JFreeChart only for JRE 1.4.2 or later. * * @param chart the chart to be saved ({@code null} not permitted). * @param width the width of the chart * @param height the height of the chart * @param info the ChartRenderingInfo object to be populated * @param session the HttpSession of the client (if {@code null}, the * temporary file is marked as "one-time" and deleted by * the {@link DisplayChart} servlet right after it is * streamed to the client). * * @return The filename of the chart saved in the temporary directory * * @throws IOException if there is a problem saving the file. */ public static String saveChartAsJPEG(JFreeChart chart, int width, int height, ChartRenderingInfo info, HttpSession session) throws IOException { Args.nullNotPermitted(chart, "chart"); ServletUtilities.createTempDir(); String prefix = ServletUtilities.tempFilePrefix; if (session == null) { prefix = ServletUtilities.tempOneTimeFilePrefix; } File tempFile = File.createTempFile(prefix, ".jpeg", new File(System.getProperty("java.io.tmpdir"))); ChartUtils.saveChartAsJPEG(tempFile, chart, width, height, info); if (session != null) { ServletUtilities.registerChartForDeletion(tempFile, session); } return tempFile.getName(); } /** * Creates the temporary directory if it does not exist. Throws a * {@code RuntimeException} if the temporary directory is * {@code null}. Uses the system property {@code java.io.tmpdir} * as the temporary directory. This sounds like a strange thing to do but * my temporary directory was not created on my default Tomcat 4.0.3 * installation. Could save some questions on the forum if it is created * when not present. */ protected static void createTempDir() { String tempDirName = System.getProperty("java.io.tmpdir"); if (tempDirName == null) { throw new RuntimeException("Temporary directory system property " + "(java.io.tmpdir) is null."); } // create the temporary directory if it doesn't exist File tempDir = new File(tempDirName); if (!tempDir.exists()) { tempDir.mkdirs(); } } /** * Adds a {@link ChartDeleter} object to the session object with the name * {@code JFreeChart_Deleter} if there is not already one bound to the * session and adds the filename to the list of charts to be deleted. * * @param tempFile the file to be deleted. * @param session the HTTP session of the client. */ protected static void registerChartForDeletion(File tempFile, HttpSession session) { // Add chart to deletion list in session if (session != null) { ChartDeleter chartDeleter = (ChartDeleter) session.getAttribute("JFreeChart_Deleter"); if (chartDeleter == null) { chartDeleter = new ChartDeleter(); session.setAttribute("JFreeChart_Deleter", chartDeleter); } chartDeleter.addChart(tempFile.getName()); } else { System.out.println("Session is null - chart will not be deleted"); } } /** * Binary streams the specified file in the temporary directory to the * HTTP response in 1KB chunks. * * @param filename the name of the file in the temporary directory. * @param response the HTTP response object. * * @throws IOException if there is an I/O problem. */ public static void sendTempFile(String filename, HttpServletResponse response) throws IOException { File file = new File(System.getProperty("java.io.tmpdir"), filename); ServletUtilities.sendTempFile(file, response); } /** * Binary streams the specified file to the HTTP response in 1KB chunks. * * @param file the file to be streamed. * @param response the HTTP response object. * * @throws IOException if there is an I/O problem. */ public static void sendTempFile(File file, HttpServletResponse response) throws IOException { String mimeType = null; String filename = file.getName(); if (filename.length() > 5) { if (filename.substring(filename.length() - 5, filename.length()).equals(".jpeg")) { mimeType = "image/jpeg"; } else if (filename.substring(filename.length() - 4, filename.length()).equals(".png")) { mimeType = "image/png"; } } ServletUtilities.sendTempFile(file, response, mimeType); } /** * Binary streams the specified file to the HTTP response in 1KB chunks. * * @param file the file to be streamed. * @param response the HTTP response object. * @param mimeType the mime type of the file, null allowed. * * @throws IOException if there is an I/O problem. */ public static void sendTempFile(File file, HttpServletResponse response, String mimeType) throws IOException { if (file.exists()) { BufferedInputStream bis = new BufferedInputStream( new FileInputStream(file)); // Set HTTP headers if (mimeType != null) { response.setHeader("Content-Type", mimeType); } response.setHeader("Content-Length", String.valueOf(file.length())); SimpleDateFormat sdf = new SimpleDateFormat( "EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH); sdf.setTimeZone(TimeZone.getTimeZone("GMT")); response.setHeader("Last-Modified", sdf.format(new Date(file.lastModified()))); BufferedOutputStream bos = new BufferedOutputStream( response.getOutputStream()); byte[] input = new byte[1024]; boolean eof = false; while (!eof) { int length = bis.read(input); if (length == -1) { eof = true; } else { bos.write(input, 0, length); } } bos.flush(); bis.close(); bos.close(); } else { throw new FileNotFoundException(file.getAbsolutePath()); } } /** * Perform a search/replace operation on a String * There are String methods to do this since (JDK 1.4) * * @param inputString the String to have the search/replace operation. * @param searchString the search String. * @param replaceString the replace String. * * @return The String with the replacements made. */ public static String searchReplace(String inputString, String searchString, String replaceString) { int i = inputString.indexOf(searchString); if (i == -1) { return inputString; } String r = ""; r += inputString.substring(0, i) + replaceString; if (i + searchString.length() < inputString.length()) { r += searchReplace(inputString.substring(i + searchString.length()), searchString, replaceString); } return r; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/servlet/package.html000066400000000000000000000002501463604235500270200ustar00rootroot00000000000000 Classes for providing useful servlet and JSP functionality. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/text/000077500000000000000000000000001463604235500240425ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/text/AttributedStringUtils.java000066400000000000000000000061541463604235500312320ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.text; import java.text.AttributedCharacterIterator; import java.text.AttributedString; import java.text.CharacterIterator; import java.util.Map; /** * Some utility methods for working with {@code AttributedString} objects. */ public class AttributedStringUtils { /** * Private constructor prevents object creation. */ private AttributedStringUtils() { } /** * Tests two attributed strings for equality. * * @param s1 string 1 ({@code null} permitted). * @param s2 string 2 ({@code null} permitted). * * @return {@code true} if {@code s1} and {@code s2} are * equal or both {@code null}, and {@code false} * otherwise. */ public static boolean equal(AttributedString s1, AttributedString s2) { if (s1 == null) { return (s2 == null); } if (s2 == null) { return false; } AttributedCharacterIterator it1 = s1.getIterator(); AttributedCharacterIterator it2 = s2.getIterator(); char c1 = it1.first(); char c2 = it2.first(); int start = 0; while (c1 != CharacterIterator.DONE) { int limit1 = it1.getRunLimit(); int limit2 = it2.getRunLimit(); if (limit1 != limit2) { return false; } // if maps aren't equivalent, return false Map m1 = it1.getAttributes(); Map m2 = it2.getAttributes(); if (!m1.equals(m2)) { return false; } // now check characters in the run are the same for (int i = start; i < limit1; i++) { if (c1 != c2) { return false; } c1 = it1.next(); c2 = it2.next(); } start = limit1; } return c2 == CharacterIterator.DONE; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/text/G2TextMeasurer.java000066400000000000000000000043571463604235500275370ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.text; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; /** * A {@link TextMeasurer} based on a {@link Graphics2D}. */ public class G2TextMeasurer implements TextMeasurer { /** The graphics device. */ private Graphics2D g2; /** * Creates a new text measurer. * * @param g2 the graphics device. */ public G2TextMeasurer(Graphics2D g2) { this.g2 = g2; } /** * Returns the string width. * * @param text the text. * @param start the index of the first character to measure. * @param end the index of the last character to measure. * * @return The string width. */ @Override public float getStringWidth(String text, int start, int end) { FontMetrics fm = this.g2.getFontMetrics(); Rectangle2D bounds = TextUtils.getTextBounds(text.substring(start, end), this.g2, fm); float result = (float) bounds.getWidth(); return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/text/TextBlock.java000066400000000000000000000247231463604235500266140ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.text; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.geom.Rectangle2D; import java.io.Serializable; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.jfree.chart.ui.HorizontalAlignment; import org.jfree.chart.ui.Size2D; import org.jfree.chart.ui.TextAnchor; import org.jfree.chart.util.ShapeUtils; /** * A list of {@link TextLine} objects that form a block of text. * * @see TextUtils#createTextBlock(String, Font, Paint) */ public class TextBlock implements Serializable { /** For serialization. */ private static final long serialVersionUID = -4333175719424385526L; /** Storage for the lines of text. */ private List lines; /** The alignment of the lines. */ private HorizontalAlignment lineAlignment; /** * Creates a new empty text block. */ public TextBlock() { this.lines = new java.util.ArrayList(); this.lineAlignment = HorizontalAlignment.CENTER; } /** * Returns the alignment of the lines of text within the block. * * @return The alignment (never {@code null}). */ public HorizontalAlignment getLineAlignment() { return this.lineAlignment; } /** * Sets the alignment of the lines of text within the block. * * @param alignment the alignment ({@code null} not permitted). */ public void setLineAlignment(HorizontalAlignment alignment) { if (alignment == null) { throw new IllegalArgumentException("Null 'alignment' argument."); } this.lineAlignment = alignment; } /** * Adds a line of text that will be displayed using the specified font. * * @param text the text. * @param font the font. * @param paint the paint. */ public void addLine(String text, Font font, Paint paint) { addLine(new TextLine(text, font, paint)); } /** * Adds a {@link TextLine} to the block. * * @param line the line. */ public void addLine(TextLine line) { this.lines.add(line); } /** * Returns the last line in the block. * * @return The last line in the block. */ public TextLine getLastLine() { TextLine last = null; final int index = this.lines.size() - 1; if (index >= 0) { last = (TextLine) this.lines.get(index); } return last; } /** * Returns an unmodifiable list containing the lines for the text block. * * @return A list of {@link TextLine} objects. */ public List getLines() { return Collections.unmodifiableList(this.lines); } /** * Returns the width and height of the text block. * * @param g2 the graphics device. * * @return The width and height. */ public Size2D calculateDimensions(Graphics2D g2) { double width = 0.0; double height = 0.0; Iterator iterator = this.lines.iterator(); while (iterator.hasNext()) { final TextLine line = (TextLine) iterator.next(); final Size2D dimension = line.calculateDimensions(g2); width = Math.max(width, dimension.getWidth()); height = height + dimension.getHeight(); } return new Size2D(width, height); } /** * Returns the bounds of the text block. * * @param g2 the graphics device ({@code null} not permitted). * @param anchorX the x-coordinate for the anchor point. * @param anchorY the y-coordinate for the anchor point. * @param anchor the text block anchor ({@code null} not permitted). * @param rotateX the x-coordinate for the rotation point. * @param rotateY the y-coordinate for the rotation point. * @param angle the rotation angle. * * @return The bounds. */ public Shape calculateBounds(Graphics2D g2, float anchorX, float anchorY, TextBlockAnchor anchor, float rotateX, float rotateY, double angle) { Size2D d = calculateDimensions(g2); float[] offsets = calculateOffsets(anchor, d.getWidth(), d.getHeight()); Rectangle2D bounds = new Rectangle2D.Double(anchorX + offsets[0], anchorY + offsets[1], d.getWidth(), d.getHeight()); Shape rotatedBounds = ShapeUtils.rotateShape(bounds, angle, rotateX, rotateY); return rotatedBounds; } /** * Draws the text block at a specific location. * * @param g2 the graphics device. * @param x the x-coordinate for the anchor point. * @param y the y-coordinate for the anchor point. * @param anchor the anchor point. */ public void draw(Graphics2D g2, float x, float y, TextBlockAnchor anchor) { draw(g2, x, y, anchor, 0.0f, 0.0f, 0.0); } /** * Draws the text block, aligning it with the specified anchor point and * rotating it about the specified rotation point. * * @param g2 the graphics device. * @param anchorX the x-coordinate for the anchor point. * @param anchorY the y-coordinate for the anchor point. * @param anchor the point on the text block that is aligned to the * anchor point. * @param rotateX the x-coordinate for the rotation point. * @param rotateY the x-coordinate for the rotation point. * @param angle the rotation (in radians). */ public void draw(Graphics2D g2, float anchorX, float anchorY, TextBlockAnchor anchor, float rotateX, float rotateY, double angle) { Size2D d = calculateDimensions(g2); float[] offsets = calculateOffsets(anchor, d.getWidth(), d.getHeight()); Iterator iterator = this.lines.iterator(); float yCursor = 0.0f; while (iterator.hasNext()) { TextLine line = (TextLine) iterator.next(); Size2D dimension = line.calculateDimensions(g2); float lineOffset = 0.0f; if (this.lineAlignment == HorizontalAlignment.CENTER) { lineOffset = (float) (d.getWidth() - dimension.getWidth()) / 2.0f; } else if (this.lineAlignment == HorizontalAlignment.RIGHT) { lineOffset = (float) (d.getWidth() - dimension.getWidth()); } line.draw(g2, anchorX + offsets[0] + lineOffset, anchorY + offsets[1] + yCursor, TextAnchor.TOP_LEFT, rotateX, rotateY, angle); yCursor = yCursor + (float) dimension.getHeight(); } } /** * Calculates the x and y offsets required to align the text block with the * specified anchor point. This assumes that the top left of the text * block is at (0.0, 0.0). * * @param anchor the anchor position. * @param width the width of the text block. * @param height the height of the text block. * * @return The offsets (float[0] = x offset, float[1] = y offset). */ private float[] calculateOffsets(TextBlockAnchor anchor, double width, double height) { float[] result = new float[2]; float xAdj = 0.0f; float yAdj = 0.0f; if (anchor == TextBlockAnchor.TOP_CENTER || anchor == TextBlockAnchor.CENTER || anchor == TextBlockAnchor.BOTTOM_CENTER) { xAdj = (float) -width / 2.0f; } else if (anchor == TextBlockAnchor.TOP_RIGHT || anchor == TextBlockAnchor.CENTER_RIGHT || anchor == TextBlockAnchor.BOTTOM_RIGHT) { xAdj = (float) -width; } if (anchor == TextBlockAnchor.TOP_LEFT || anchor == TextBlockAnchor.TOP_CENTER || anchor == TextBlockAnchor.TOP_RIGHT) { yAdj = 0.0f; } else if (anchor == TextBlockAnchor.CENTER_LEFT || anchor == TextBlockAnchor.CENTER || anchor == TextBlockAnchor.CENTER_RIGHT) { yAdj = (float) -height / 2.0f; } else if (anchor == TextBlockAnchor.BOTTOM_LEFT || anchor == TextBlockAnchor.BOTTOM_CENTER || anchor == TextBlockAnchor.BOTTOM_RIGHT) { yAdj = (float) -height; } result[0] = xAdj; result[1] = yAdj; return result; } /** * Tests this object for equality with an arbitrary object. * * @param obj the object to test against ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj instanceof TextBlock) { TextBlock block = (TextBlock) obj; return this.lines.equals(block.lines); } return false; } /** * Returns a hash code for this object. * * @return A hash code. */ @Override public int hashCode() { return (this.lines != null ? this.lines.hashCode() : 0); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/text/TextBlockAnchor.java000066400000000000000000000122651463604235500277450ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.text; import java.io.ObjectStreamException; import java.io.Serializable; /** * Used to indicate the position of an anchor point for a text block. */ public final class TextBlockAnchor implements Serializable { /** For serialization. */ private static final long serialVersionUID = -3045058380983401544L; /** Top/left. */ public static final TextBlockAnchor TOP_LEFT = new TextBlockAnchor("TextBlockAnchor.TOP_LEFT"); /** Top/center. */ public static final TextBlockAnchor TOP_CENTER = new TextBlockAnchor( "TextBlockAnchor.TOP_CENTER" ); /** Top/right. */ public static final TextBlockAnchor TOP_RIGHT = new TextBlockAnchor( "TextBlockAnchor.TOP_RIGHT" ); /** Middle/left. */ public static final TextBlockAnchor CENTER_LEFT = new TextBlockAnchor( "TextBlockAnchor.CENTER_LEFT" ); /** Middle/center. */ public static final TextBlockAnchor CENTER = new TextBlockAnchor("TextBlockAnchor.CENTER"); /** Middle/right. */ public static final TextBlockAnchor CENTER_RIGHT = new TextBlockAnchor( "TextBlockAnchor.CENTER_RIGHT" ); /** Bottom/left. */ public static final TextBlockAnchor BOTTOM_LEFT = new TextBlockAnchor("TextBlockAnchor.BOTTOM_LEFT"); /** Bottom/center. */ public static final TextBlockAnchor BOTTOM_CENTER = new TextBlockAnchor("TextBlockAnchor.BOTTOM_CENTER"); /** Bottom/right. */ public static final TextBlockAnchor BOTTOM_RIGHT = new TextBlockAnchor("TextBlockAnchor.BOTTOM_RIGHT"); /** The name. */ private final String name; /** * Private constructor. * * @param name the name. */ private TextBlockAnchor(String name) { this.name = name; } /** * Returns a string representing the object. * * @return The string. */ @Override public String toString() { return this.name; } /** * Returns {@code true} if this object is equal to the specified * object, and {@code false} otherwise. * * @param o the other object. * * @return A boolean. */ @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof TextBlockAnchor)) { return false; } TextBlockAnchor other = (TextBlockAnchor) o; if (!this.name.equals(other.name)) { return false; } return true; } /** * Returns a hash code value for the object. * * @return the hashcode */ @Override public int hashCode() { return this.name.hashCode(); } /** * Ensures that serialization returns the unique instances. * * @return The object. * * @throws ObjectStreamException if there is a problem. */ private Object readResolve() throws ObjectStreamException { if (this.equals(TextBlockAnchor.TOP_CENTER)) { return TextBlockAnchor.TOP_CENTER; } else if (this.equals(TextBlockAnchor.TOP_LEFT)) { return TextBlockAnchor.TOP_LEFT; } else if (this.equals(TextBlockAnchor.TOP_RIGHT)) { return TextBlockAnchor.TOP_RIGHT; } else if (this.equals(TextBlockAnchor.CENTER)) { return TextBlockAnchor.CENTER; } else if (this.equals(TextBlockAnchor.CENTER_LEFT)) { return TextBlockAnchor.CENTER_LEFT; } else if (this.equals(TextBlockAnchor.CENTER_RIGHT)) { return TextBlockAnchor.CENTER_RIGHT; } else if (this.equals(TextBlockAnchor.BOTTOM_CENTER)) { return TextBlockAnchor.BOTTOM_CENTER; } else if (this.equals(TextBlockAnchor.BOTTOM_LEFT)) { return TextBlockAnchor.BOTTOM_LEFT; } else if (this.equals(TextBlockAnchor.BOTTOM_RIGHT)) { return TextBlockAnchor.BOTTOM_RIGHT; } return null; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/text/TextBox.java000066400000000000000000000271401463604235500263060ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.text; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Stroke; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.ui.RectangleAnchor; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.ui.Size2D; import org.jfree.chart.util.SerialUtils; /** * A box containing a text block. */ public class TextBox implements Serializable { /** For serialization. */ private static final long serialVersionUID = 3360220213180203706L; /** The outline paint. */ private transient Paint outlinePaint; /** The outline stroke. */ private transient Stroke outlineStroke; /** The interior space. */ private RectangleInsets interiorGap; /** The background paint. */ private transient Paint backgroundPaint; /** The shadow paint. */ private transient Paint shadowPaint; /** The shadow x-offset. */ private double shadowXOffset = 2.0; /** The shadow y-offset. */ private double shadowYOffset = 2.0; /** The text block. */ private TextBlock textBlock; /** * Creates an empty text box. */ public TextBox() { this((TextBlock) null); } /** * Creates a text box. * * @param text the text. */ public TextBox(String text) { this((TextBlock) null); if (text != null) { this.textBlock = new TextBlock(); this.textBlock.addLine(text, new Font("SansSerif", Font.PLAIN, 10), Color.BLACK); } } /** * Creates a new text box. * * @param block the text block. */ public TextBox(TextBlock block) { this.outlinePaint = Color.BLACK; this.outlineStroke = new BasicStroke(1.0f); this.interiorGap = new RectangleInsets(1.0, 3.0, 1.0, 3.0); this.backgroundPaint = new Color(255, 255, 192); this.shadowPaint = Color.GRAY; this.shadowXOffset = 2.0; this.shadowYOffset = 2.0; this.textBlock = block; } /** * Returns the outline paint. * * @return The outline paint. */ public Paint getOutlinePaint() { return this.outlinePaint; } /** * Sets the outline paint. * * @param paint the paint. */ public void setOutlinePaint(Paint paint) { this.outlinePaint = paint; } /** * Returns the outline stroke. * * @return The outline stroke. */ public Stroke getOutlineStroke() { return this.outlineStroke; } /** * Sets the outline stroke. * * @param stroke the stroke. */ public void setOutlineStroke(Stroke stroke) { this.outlineStroke = stroke; } /** * Returns the interior gap. * * @return The interior gap. */ public RectangleInsets getInteriorGap() { return this.interiorGap; } /** * Sets the interior gap. * * @param gap the gap. */ public void setInteriorGap(RectangleInsets gap) { this.interiorGap = gap; } /** * Returns the background paint. * * @return The background paint. */ public Paint getBackgroundPaint() { return this.backgroundPaint; } /** * Sets the background paint. * * @param paint the paint. */ public void setBackgroundPaint(Paint paint) { this.backgroundPaint = paint; } /** * Returns the shadow paint. * * @return The shadow paint. */ public Paint getShadowPaint() { return this.shadowPaint; } /** * Sets the shadow paint. * * @param paint the paint. */ public void setShadowPaint(Paint paint) { this.shadowPaint = paint; } /** * Returns the x-offset for the shadow effect. * * @return The offset. */ public double getShadowXOffset() { return this.shadowXOffset; } /** * Sets the x-offset for the shadow effect. * * @param offset the offset (in Java2D units). */ public void setShadowXOffset(double offset) { this.shadowXOffset = offset; } /** * Returns the y-offset for the shadow effect. * * @return The offset. */ public double getShadowYOffset() { return this.shadowYOffset; } /** * Sets the y-offset for the shadow effect. * * @param offset the offset (in Java2D units). */ public void setShadowYOffset(double offset) { this.shadowYOffset = offset; } /** * Returns the text block. * * @return The text block. */ public TextBlock getTextBlock() { return this.textBlock; } /** * Sets the text block. * * @param block the block. */ public void setTextBlock(TextBlock block) { this.textBlock = block; } /** * Draws the text box. * * @param g2 the graphics device. * @param x the x-coordinate. * @param y the y-coordinate. * @param anchor the anchor point. */ public void draw(Graphics2D g2, float x, float y, RectangleAnchor anchor) { final Size2D d1 = this.textBlock.calculateDimensions(g2); final double w = this.interiorGap.extendWidth(d1.getWidth()); final double h = this.interiorGap.extendHeight(d1.getHeight()); final Size2D d2 = new Size2D(w, h); final Rectangle2D bounds = RectangleAnchor.createRectangle(d2, x, y, anchor); double xx = bounds.getX(); double yy = bounds.getY(); if (this.shadowPaint != null) { final Rectangle2D shadow = new Rectangle2D.Double( xx + this.shadowXOffset, yy + this.shadowYOffset, bounds.getWidth(), bounds.getHeight()); g2.setPaint(this.shadowPaint); g2.fill(shadow); } if (this.backgroundPaint != null) { g2.setPaint(this.backgroundPaint); g2.fill(bounds); } if (this.outlinePaint != null && this.outlineStroke != null) { g2.setPaint(this.outlinePaint); g2.setStroke(this.outlineStroke); g2.draw(bounds); } this.textBlock.draw(g2, (float) (xx + this.interiorGap.calculateLeftInset(w)), (float) (yy + this.interiorGap.calculateTopInset(h)), TextBlockAnchor.TOP_LEFT); } /** * Returns the height of the text box. * * @param g2 the graphics device. * * @return The height (in Java2D units). */ public double getHeight(Graphics2D g2) { final Size2D d = this.textBlock.calculateDimensions(g2); return this.interiorGap.extendHeight(d.getHeight()); } /** * Tests this object for equality with an arbitrary object. * * @param obj the object to test against ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof TextBox)) { return false; } final TextBox that = (TextBox) obj; if (!Objects.equals(this.outlinePaint, that.outlinePaint)) { return false; } if (!Objects.equals(this.outlineStroke, that.outlineStroke)) { return false; } if (!Objects.equals(this.interiorGap, that.interiorGap)) { return false; } if (!Objects.equals(this.backgroundPaint, that.backgroundPaint)) { return false; } if (!Objects.equals(this.shadowPaint, that.shadowPaint)) { return false; } if (this.shadowXOffset != that.shadowXOffset) { return false; } if (this.shadowYOffset != that.shadowYOffset) { return false; } if (!Objects.equals(this.textBlock, that.textBlock)) { return false; } return true; } /** * Returns a hash code for this object. * * @return A hash code. */ @Override public int hashCode() { int result; long temp; result = (this.outlinePaint != null ? this.outlinePaint.hashCode() : 0); result = 29 * result + (this.outlineStroke != null ? this.outlineStroke.hashCode() : 0); result = 29 * result + (this.interiorGap != null ? this.interiorGap.hashCode() : 0); result = 29 * result + (this.backgroundPaint != null ? this.backgroundPaint.hashCode() : 0); result = 29 * result + (this.shadowPaint != null ? this.shadowPaint.hashCode() : 0); temp = this.shadowXOffset != +0.0d ? Double.doubleToLongBits(this.shadowXOffset) : 0L; result = 29 * result + (int) (temp ^ (temp >>> 32)); temp = this.shadowYOffset != +0.0d ? Double.doubleToLongBits(this.shadowYOffset) : 0L; result = 29 * result + (int) (temp ^ (temp >>> 32)); result = 29 * result + (this.textBlock != null ? this.textBlock.hashCode() : 0); return result; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.outlinePaint, stream); SerialUtils.writeStroke(this.outlineStroke, stream); SerialUtils.writePaint(this.backgroundPaint, stream); SerialUtils.writePaint(this.shadowPaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.outlinePaint = SerialUtils.readPaint(stream); this.outlineStroke = SerialUtils.readStroke(stream); this.backgroundPaint = SerialUtils.readPaint(stream); this.shadowPaint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/text/TextFragment.java000066400000000000000000000222221463604235500273150ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.text; import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.font.LineMetrics; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.HashUtils; import org.jfree.chart.ui.Size2D; import org.jfree.chart.ui.TextAnchor; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.SerialUtils; /** * A text item, with an associated font, that fits on a single line (see * {@link TextLine}). Instances of the class are immutable. */ public class TextFragment implements Serializable { /** For serialization. */ private static final long serialVersionUID = 4465945952903143262L; /** The default font. */ public static final Font DEFAULT_FONT = new Font("Serif", Font.PLAIN, 12); /** The default text color. */ public static final Paint DEFAULT_PAINT = Color.BLACK; /** The text. */ private String text; /** The font. */ private Font font; /** The text color. */ private transient Paint paint; /** * The baseline offset (can be used to simulate subscripts and * superscripts). */ private float baselineOffset; /** * Creates a new text fragment. * * @param text the text ({@code null} not permitted). */ public TextFragment(String text) { this(text, DEFAULT_FONT, DEFAULT_PAINT); } /** * Creates a new text fragment. * * @param text the text ({@code null} not permitted). * @param font the font ({@code null} not permitted). */ public TextFragment(String text, Font font) { this(text, font, DEFAULT_PAINT); } /** * Creates a new text fragment. * * @param text the text ({@code null} not permitted). * @param font the font ({@code null} not permitted). * @param paint the text color ({@code null} not permitted). */ public TextFragment(String text, Font font, Paint paint) { this(text, font, paint, 0.0f); } /** * Creates a new text fragment. * * @param text the text ({@code null} not permitted). * @param font the font ({@code null} not permitted). * @param paint the text color ({@code null} not permitted). * @param baselineOffset the baseline offset. */ public TextFragment(String text, Font font, Paint paint, float baselineOffset) { if (text == null) { throw new IllegalArgumentException("Null 'text' argument."); } if (font == null) { throw new IllegalArgumentException("Null 'font' argument."); } if (paint == null) { throw new IllegalArgumentException("Null 'paint' argument."); } this.text = text; this.font = font; this.paint = paint; this.baselineOffset = baselineOffset; } /** * Returns the text. * * @return The text (possibly {@code null}). */ public String getText() { return this.text; } /** * Returns the font. * * @return The font (never {@code null}). */ public Font getFont() { return this.font; } /** * Returns the text paint. * * @return The text paint (never {@code null}). */ public Paint getPaint() { return this.paint; } /** * Returns the baseline offset. * * @return The baseline offset. */ public float getBaselineOffset() { return this.baselineOffset; } /** * Draws the text fragment. * * @param g2 the graphics device. * @param anchorX the x-coordinate of the anchor point. * @param anchorY the y-coordinate of the anchor point. * @param anchor the location of the text that is aligned to the anchor * point. * @param rotateX the x-coordinate of the rotation point. * @param rotateY the y-coordinate of the rotation point. * @param angle the angle. */ public void draw(Graphics2D g2, float anchorX, float anchorY, TextAnchor anchor, float rotateX, float rotateY, double angle) { g2.setFont(this.font); g2.setPaint(this.paint); TextUtils.drawRotatedString(this.text, g2, anchorX, anchorY + this.baselineOffset, anchor, angle, rotateX, rotateY); } /** * Calculates the dimensions of the text fragment. * * @param g2 the graphics device. * * @return The width and height of the text. */ public Size2D calculateDimensions(Graphics2D g2) { FontMetrics fm = g2.getFontMetrics(this.font); Rectangle2D bounds = TextUtils.getTextBounds(this.text, g2, fm); Size2D result = new Size2D(bounds.getWidth(), bounds.getHeight()); return result; } /** * Calculates the vertical offset between the baseline and the specified * text anchor. * * @param g2 the graphics device. * @param anchor the anchor. * * @return the offset. */ public float calculateBaselineOffset(Graphics2D g2, TextAnchor anchor) { float result = 0.0f; FontMetrics fm = g2.getFontMetrics(this.font); LineMetrics lm = fm.getLineMetrics("ABCxyz", g2); if (anchor.isTop()) { result = lm.getAscent(); } else if (anchor.isHalfAscent()) { result = lm.getAscent() / 2.0f; } else if (anchor.isVerticalCenter()) { result = lm.getAscent() / 2.0f - lm.getDescent() / 2.0f; } else if (anchor.isBottom()) { result = -lm.getDescent() - lm.getLeading(); } return result; } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object to test against ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof TextFragment)) { return false; } TextFragment tf = (TextFragment) obj; if (!Objects.equals(this.text, tf.text)) { return false; } if (!Objects.equals(this.font, tf.font)) { return false; } if (!PaintUtils.equal(this.paint, tf.paint)) { return false; } if (Float.floatToIntBits(this.baselineOffset) != Float.floatToIntBits(tf.baselineOffset)) { return false; } return true; } /** * Returns a hash code for this object. * * @return A hash code. */ @Override public int hashCode() { int hash = 7; hash = 83 * hash + Objects.hashCode(this.text); hash = 83 * hash + Objects.hashCode(this.font); hash = 83 * hash + HashUtils.hashCodeForPaint(this.paint); hash = 83 * hash + Float.floatToIntBits(this.baselineOffset); return hash; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.paint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.paint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/text/TextLine.java000066400000000000000000000202071463604235500264420ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.text; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; import java.io.Serializable; import java.util.Iterator; import java.util.List; import org.jfree.chart.ui.Size2D; import org.jfree.chart.ui.TextAnchor; /** * A sequence of {@link TextFragment} objects that together form a line of * text. A sequence of text lines is managed by the {@link TextBlock} class. */ public class TextLine implements Serializable { /** For serialization. */ private static final long serialVersionUID = 7100085690160465444L; /** Storage for the text fragments that make up the line. */ private List fragments; /** * Creates a new empty line. */ public TextLine() { this.fragments = new java.util.ArrayList(); } /** * Creates a new text line using the default font. * * @param text the text ({@code null} not permitted). */ public TextLine(String text) { this(text, TextFragment.DEFAULT_FONT); } /** * Creates a new text line. * * @param text the text ({@code null} not permitted). * @param font the text font ({@code null} not permitted). */ public TextLine(String text, Font font) { this.fragments = new java.util.ArrayList(); final TextFragment fragment = new TextFragment(text, font); this.fragments.add(fragment); } /** * Creates a new text line. * * @param text the text ({@code null} not permitted). * @param font the text font ({@code null} not permitted). * @param paint the text color ({@code null} not permitted). */ public TextLine(String text, Font font, Paint paint) { if (text == null) { throw new IllegalArgumentException("Null 'text' argument."); } if (font == null) { throw new IllegalArgumentException("Null 'font' argument."); } if (paint == null) { throw new IllegalArgumentException("Null 'paint' argument."); } this.fragments = new java.util.ArrayList(); final TextFragment fragment = new TextFragment(text, font, paint); this.fragments.add(fragment); } /** * Adds a text fragment to the text line. * * @param fragment the text fragment ({@code null} not permitted). */ public void addFragment(TextFragment fragment) { this.fragments.add(fragment); } /** * Removes a fragment from the line. * * @param fragment the fragment to remove. */ public void removeFragment(TextFragment fragment) { this.fragments.remove(fragment); } /** * Draws the text line. * * @param g2 the graphics device. * @param anchorX the x-coordinate for the anchor point. * @param anchorY the y-coordinate for the anchor point. * @param anchor the point on the text line that is aligned to the anchor * point. * @param rotateX the x-coordinate for the rotation point. * @param rotateY the y-coordinate for the rotation point. * @param angle the rotation angle (in radians). */ public void draw(Graphics2D g2, float anchorX, float anchorY, TextAnchor anchor, float rotateX, float rotateY, double angle) { Size2D dim = calculateDimensions(g2); float xAdj = 0.0f; if (anchor.isHorizontalCenter()) { xAdj = (float) -dim.getWidth() / 2.0f; } else if (anchor.isRight()) { xAdj = (float) -dim.getWidth(); } float x = anchorX + xAdj; final float yOffset = calculateBaselineOffset(g2, anchor); final Iterator iterator = this.fragments.iterator(); while (iterator.hasNext()) { final TextFragment fragment = (TextFragment) iterator.next(); final Size2D d = fragment.calculateDimensions(g2); fragment.draw(g2, x, anchorY + yOffset, TextAnchor.BASELINE_LEFT, rotateX, rotateY, angle); x = x + (float) d.getWidth(); } } /** * Calculates the width and height of the text line. * * @param g2 the graphics device. * * @return The width and height. */ public Size2D calculateDimensions(Graphics2D g2) { double width = 0.0; double height = 0.0; final Iterator iterator = this.fragments.iterator(); while (iterator.hasNext()) { final TextFragment fragment = (TextFragment) iterator.next(); final Size2D dimension = fragment.calculateDimensions(g2); width = width + dimension.getWidth(); height = Math.max(height, dimension.getHeight()); } return new Size2D(width, height); } /** * Returns the first text fragment in the line. * * @return The first text fragment in the line. */ public TextFragment getFirstTextFragment() { TextFragment result = null; if (this.fragments.size() > 0) { result = (TextFragment) this.fragments.get(0); } return result; } /** * Returns the last text fragment in the line. * * @return The last text fragment in the line. */ public TextFragment getLastTextFragment() { TextFragment result = null; if (this.fragments.size() > 0) { result = (TextFragment) this.fragments.get(this.fragments.size() - 1); } return result; } /** * Calculate the offsets required to translate from the specified anchor * position to the left baseline position. * * @param g2 the graphics device. * @param anchor the anchor position. * * @return The offsets. */ private float calculateBaselineOffset(Graphics2D g2, TextAnchor anchor) { float result = 0.0f; Iterator iterator = this.fragments.iterator(); while (iterator.hasNext()) { TextFragment fragment = (TextFragment) iterator.next(); result = Math.max(result, fragment.calculateBaselineOffset(g2, anchor)); } return result; } /** * Tests this object for equality with an arbitrary object. * * @param obj the object to test against ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (obj == this) { return true; } if (obj instanceof TextLine) { final TextLine line = (TextLine) obj; return this.fragments.equals(line.fragments); } return false; } /** * Returns a hash code for this object. * * @return A hash code. */ @Override public int hashCode() { return (this.fragments != null ? this.fragments.hashCode() : 0); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/text/TextMeasurer.java000066400000000000000000000033311463604235500273350ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.text; /** * An object that can measure text. */ public interface TextMeasurer { /** * Calculates the width of a {@code String} in the current * {@code Graphics} context. * * @param text the text. * @param start the start position of the substring to be measured. * @param end the position of the last character to be measured. * * @return The width of the string in Java2D units. */ float getStringWidth(String text, int start, int end); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/text/TextUtils.java000066400000000000000000000673731463604235500266720ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.text; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.font.FontRenderContext; import java.awt.font.LineMetrics; import java.awt.font.TextLayout; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.text.AttributedString; import java.text.BreakIterator; import org.jfree.chart.ui.TextAnchor; /** * Some utility methods for working with text in Java2D. */ public class TextUtils { /** * When this flag is set to {@code true}, strings will be drawn * as attributed strings with the attributes taken from the current font. * This allows for underlining, strike-out etc, but it means that * TextLayout will be used to render the text: * * http://www.jfree.org/phpBB2/viewtopic.php?p=45459&highlight=#45459 */ private static boolean drawStringsWithFontAttributes = false; /** * A flag that controls whether or not the rotated string workaround is * used. */ private static boolean useDrawRotatedStringWorkaround = true; /** * A flag that controls whether the FontMetrics.getStringBounds() method * is used or a workaround is applied. */ private static boolean useFontMetricsGetStringBounds = false; /** * Private constructor prevents object creation. */ private TextUtils() { // prevent instantiation } /** * Creates a {@link TextBlock} from a {@code String}. Line breaks * are added where the {@code String} contains '\n' characters. * * @param text the text. * @param font the font. * @param paint the paint. * * @return A text block. */ public static TextBlock createTextBlock(String text, Font font, Paint paint) { if (text == null) { throw new IllegalArgumentException("Null 'text' argument."); } TextBlock result = new TextBlock(); String input = text; boolean moreInputToProcess = (text.length() > 0); int start = 0; while (moreInputToProcess) { int index = input.indexOf("\n"); if (index > start) { String line = input.substring(start, index); if (index < input.length() - 1) { result.addLine(line, font, paint); input = input.substring(index + 1); } else { moreInputToProcess = false; } } else if (index == start) { if (index < input.length() - 1) { input = input.substring(index + 1); } else { moreInputToProcess = false; } } else { result.addLine(input, font, paint); moreInputToProcess = false; } } return result; } /** * Creates a new text block from the given string, breaking the * text into lines so that the {@code maxWidth} value is respected. * * @param text the text. * @param font the font. * @param paint the paint. * @param maxWidth the maximum width for each line. * @param measurer the text measurer. * * @return A text block. */ public static TextBlock createTextBlock(String text, Font font, Paint paint, float maxWidth, TextMeasurer measurer) { return createTextBlock(text, font, paint, maxWidth, Integer.MAX_VALUE, measurer); } /** * Creates a new text block from the given string, breaking the * text into lines so that the {@code maxWidth} value is * respected. * * @param text the text. * @param font the font. * @param paint the paint. * @param maxWidth the maximum width for each line. * @param maxLines the maximum number of lines. * @param measurer the text measurer. * * @return A text block. */ public static TextBlock createTextBlock(String text, Font font, Paint paint, float maxWidth, int maxLines, TextMeasurer measurer) { TextBlock result = new TextBlock(); BreakIterator iterator = BreakIterator.getLineInstance(); iterator.setText(text); int current = 0; int lines = 0; int length = text.length(); while (current < length && lines < maxLines) { int next = nextLineBreak(text, current, maxWidth, iterator, measurer); if (next == BreakIterator.DONE) { result.addLine(text.substring(current), font, paint); return result; } else if (next == current) { next++; // we must take one more character or we'll loop forever } result.addLine(text.substring(current, next), font, paint); lines++; current = next; while (current < text.length()&& text.charAt(current) == '\n') { current++; } } if (current < length) { TextLine lastLine = result.getLastLine(); TextFragment lastFragment = lastLine.getLastTextFragment(); String oldStr = lastFragment.getText(); String newStr = "..."; if (oldStr.length() > 3) { newStr = oldStr.substring(0, oldStr.length() - 3) + "..."; } lastLine.removeFragment(lastFragment); TextFragment newFragment = new TextFragment(newStr, lastFragment.getFont(), lastFragment.getPaint()); lastLine.addFragment(newFragment); } return result; } /** * Returns the character index of the next line break. If the next * character is wider than {@code width]} this method will return * {@code start} - the caller should check for this case. * * @param text the text ({@code null} not permitted). * @param start the start index. * @param width the target display width. * @param iterator the word break iterator. * @param measurer the text measurer. * * @return The index of the next line break. */ private static int nextLineBreak(String text, int start, float width, BreakIterator iterator, TextMeasurer measurer) { // this method is (loosely) based on code in JFreeReport's // TextParagraph class int current = start; int end; float x = 0.0f; boolean firstWord = true; int newline = text.indexOf('\n', start); if (newline < 0) { newline = Integer.MAX_VALUE; } while (((end = iterator.following(current)) != BreakIterator.DONE)) { x += measurer.getStringWidth(text, current, end); if (x > width) { if (firstWord) { while (measurer.getStringWidth(text, start, end) > width) { end--; if (end <= start) { return end; } } return end; } else { end = iterator.previous(); return end; } } else { if (end > newline) { return newline; } } // we found at least one word that fits ... firstWord = false; current = end; } return BreakIterator.DONE; } /** * Returns the bounds for the specified text. * * @param text the text ({@code null} permitted). * @param g2 the graphics context (not {@code null}). * @param fm the font metrics (not {@code null}). * * @return The text bounds ({@code null} if the {@code text} * argument is {@code null}). */ public static Rectangle2D getTextBounds(String text, Graphics2D g2, FontMetrics fm) { Rectangle2D bounds; if (TextUtils.useFontMetricsGetStringBounds) { bounds = fm.getStringBounds(text, g2); // getStringBounds() can return incorrect height for some Unicode // characters...see bug parade 6183356, let's replace it with // something correct LineMetrics lm = fm.getFont().getLineMetrics(text, g2.getFontRenderContext()); bounds.setRect(bounds.getX(), bounds.getY(), bounds.getWidth(), lm.getHeight()); } else { double width = fm.stringWidth(text); double height = fm.getHeight(); bounds = new Rectangle2D.Double(0.0, -fm.getAscent(), width, height); } return bounds; } /** * Returns the bounds of an aligned string. * * @param text the string ({@code null} not permitted). * @param g2 the graphics target ({@code null} not permitted). * @param x the x-coordinate. * @param y the y-coordinate. * @param anchor the anchor point that will be aligned to * {@code (x, y)} ({@code null} not permitted). * * @return The text bounds (never {@code null}). */ public static Rectangle2D calcAlignedStringBounds(String text, Graphics2D g2, float x, float y, TextAnchor anchor) { Rectangle2D textBounds = new Rectangle2D.Double(); float[] adjust = deriveTextBoundsAnchorOffsets(g2, text, anchor, textBounds); // adjust text bounds to match string position textBounds.setRect(x + adjust[0], y + adjust[1] + adjust[2], textBounds.getWidth(), textBounds.getHeight()); return textBounds; } /** * Draws a string such that the specified anchor point is aligned to the * given (x, y) location. * * @param text the text. * @param g2 the graphics device. * @param x the x coordinate (Java 2D). * @param y the y coordinate (Java 2D). * @param anchor the anchor location. * * @return The text bounds (adjusted for the text position). */ public static Rectangle2D drawAlignedString(String text, Graphics2D g2, float x, float y, TextAnchor anchor) { Rectangle2D textBounds = new Rectangle2D.Double(); float[] adjust = deriveTextBoundsAnchorOffsets(g2, text, anchor, textBounds); // adjust text bounds to match string position textBounds.setRect(x + adjust[0], y + adjust[1] + adjust[2], textBounds.getWidth(), textBounds.getHeight()); if (!drawStringsWithFontAttributes) { g2.drawString(text, x + adjust[0], y + adjust[1]); } else { AttributedString as = new AttributedString(text, g2.getFont().getAttributes()); g2.drawString(as.getIterator(), x + adjust[0], y + adjust[1]); } return textBounds; } /** * A utility method that calculates the anchor offsets for a string. * Normally, the (x, y) coordinate for drawing text is a point on the * baseline at the left of the text string. If you add these offsets to * (x, y) and draw the string, then the anchor point should coincide with * the (x, y) point. * * @param g2 the graphics device (not {@code null}). * @param text the text. * @param anchor the anchor point. * @param textBounds the text bounds (if not {@code null}, this * object will be updated by this method to match the * string bounds). * * @return The offsets. */ private static float[] deriveTextBoundsAnchorOffsets(Graphics2D g2, String text, TextAnchor anchor, Rectangle2D textBounds) { float[] result = new float[3]; FontRenderContext frc = g2.getFontRenderContext(); Font f = g2.getFont(); FontMetrics fm = g2.getFontMetrics(f); Rectangle2D bounds = TextUtils.getTextBounds(text, g2, fm); LineMetrics metrics = f.getLineMetrics(text, frc); float ascent = metrics.getAscent(); result[2] = -ascent; float halfAscent = ascent / 2.0f; float descent = metrics.getDescent(); float leading = metrics.getLeading(); float xAdj = 0.0f; float yAdj = 0.0f; if (anchor.isHorizontalCenter()) { xAdj = (float) -bounds.getWidth() / 2.0f; } else if (anchor.isRight()) { xAdj = (float) -bounds.getWidth(); } if (anchor.isTop()) { yAdj = -descent - leading + (float) bounds.getHeight(); } else if (anchor.isHalfAscent()) { yAdj = halfAscent; } else if (anchor.isVerticalCenter()) { yAdj = -descent - leading + (float) (bounds.getHeight() / 2.0); } else if (anchor.isBaseline()) { yAdj = 0.0f; } else if (anchor.isBottom()) { yAdj = -metrics.getDescent() - metrics.getLeading(); } if (textBounds != null) { textBounds.setRect(bounds); } result[0] = xAdj; result[1] = yAdj; return result; } /** * A utility method for drawing rotated text. *

* A common rotation is -Math.PI/2 which draws text 'vertically' (with the * top of the characters on the left). * * @param text the text. * @param g2 the graphics device. * @param angle the angle of the (clockwise) rotation (in radians). * @param x the x-coordinate. * @param y the y-coordinate. */ public static void drawRotatedString(String text, Graphics2D g2, double angle, float x, float y) { drawRotatedString(text, g2, x, y, angle, x, y); } /** * A utility method for drawing rotated text. *

* A common rotation is -Math.PI/2 which draws text 'vertically' (with the * top of the characters on the left). * * @param text the text. * @param g2 the graphics device. * @param textX the x-coordinate for the text (before rotation). * @param textY the y-coordinate for the text (before rotation). * @param angle the angle of the (clockwise) rotation (in radians). * @param rotateX the point about which the text is rotated. * @param rotateY the point about which the text is rotated. */ public static void drawRotatedString(String text, Graphics2D g2, float textX, float textY, double angle, float rotateX, float rotateY) { if ((text == null) || (text.equals(""))) { return; } if (angle == 0.0) { drawAlignedString(text, g2, textX, textY, TextAnchor.BASELINE_LEFT); return; } AffineTransform saved = g2.getTransform(); AffineTransform rotate = AffineTransform.getRotateInstance( angle, rotateX, rotateY); g2.transform(rotate); if (useDrawRotatedStringWorkaround) { // workaround for JDC bug ID 4312117 and others... TextLayout tl = new TextLayout(text, g2.getFont(), g2.getFontRenderContext()); tl.draw(g2, textX, textY); } else { if (!drawStringsWithFontAttributes) { g2.drawString(text, textX, textY); } else { AttributedString as = new AttributedString(text, g2.getFont().getAttributes()); g2.drawString(as.getIterator(), textX, textY); } } g2.setTransform(saved); } /** * Draws a string that is aligned by one anchor point and rotated about * another anchor point. * * @param text the text. * @param g2 the graphics device. * @param x the x-coordinate for positioning the text. * @param y the y-coordinate for positioning the text. * @param textAnchor the text anchor. * @param angle the rotation angle. * @param rotationX the x-coordinate for the rotation anchor point. * @param rotationY the y-coordinate for the rotation anchor point. */ public static void drawRotatedString(String text, Graphics2D g2, float x, float y, TextAnchor textAnchor, double angle, float rotationX, float rotationY) { if (text == null || text.equals("")) { return; } if (angle == 0.0) { drawAlignedString(text, g2, x, y, textAnchor); } else { float[] textAdj = deriveTextBoundsAnchorOffsets(g2, text, textAnchor); drawRotatedString(text, g2, x + textAdj[0], y + textAdj[1], angle, rotationX, rotationY); } } /** * Draws a string that is aligned by one anchor point and rotated about * another anchor point. * * @param text the text. * @param g2 the graphics device. * @param x the x-coordinate for positioning the text. * @param y the y-coordinate for positioning the text. * @param textAnchor the text anchor. * @param angle the rotation angle (in radians). * @param rotationAnchor the rotation anchor. */ public static void drawRotatedString(String text, Graphics2D g2, float x, float y, TextAnchor textAnchor, double angle, TextAnchor rotationAnchor) { if (text == null || text.equals("")) { return; } if (angle == 0.0) { drawAlignedString(text, g2, x, y, textAnchor); } else { float[] textAdj = deriveTextBoundsAnchorOffsets(g2, text, textAnchor); float[] rotateAdj = deriveRotationAnchorOffsets(g2, text, rotationAnchor); drawRotatedString(text, g2, x + textAdj[0], y + textAdj[1], angle, x + textAdj[0] + rotateAdj[0], y + textAdj[1] + rotateAdj[1]); } } /** * Returns a shape that represents the bounds of the string after the * specified rotation has been applied. * * @param text the text ({@code null} permitted). * @param g2 the graphics device. * @param x the x coordinate for the anchor point. * @param y the y coordinate for the anchor point. * @param textAnchor the text anchor. * @param angle the angle. * @param rotationAnchor the rotation anchor. * * @return The bounds (possibly {@code null}). */ public static Shape calculateRotatedStringBounds(String text, Graphics2D g2, float x, float y, TextAnchor textAnchor, double angle, TextAnchor rotationAnchor) { if (text == null || text.equals("")) { return null; } float[] textAdj = deriveTextBoundsAnchorOffsets(g2, text, textAnchor); float[] rotateAdj = deriveRotationAnchorOffsets(g2, text, rotationAnchor); Shape result = calculateRotatedStringBounds(text, g2, x + textAdj[0], y + textAdj[1], angle, x + textAdj[0] + rotateAdj[0], y + textAdj[1] + rotateAdj[1]); return result; } /** * A utility method that calculates the anchor offsets for a string. * Normally, the (x, y) coordinate for drawing text is a point on the * baseline at the left of the text string. If you add these offsets to * (x, y) and draw the string, then the anchor point should coincide with * the (x, y) point. * * @param g2 the graphics device (not {@code null}). * @param text the text. * @param anchor the anchor point. * * @return The offsets. */ private static float[] deriveTextBoundsAnchorOffsets(Graphics2D g2, String text, TextAnchor anchor) { float[] result = new float[2]; FontRenderContext frc = g2.getFontRenderContext(); Font f = g2.getFont(); FontMetrics fm = g2.getFontMetrics(f); Rectangle2D bounds = getTextBounds(text, g2, fm); LineMetrics metrics = f.getLineMetrics(text, frc); float ascent = metrics.getAscent(); float halfAscent = ascent / 2.0f; float descent = metrics.getDescent(); float leading = metrics.getLeading(); float xAdj = 0.0f; float yAdj = 0.0f; if (anchor.isHorizontalCenter()) { xAdj = (float) -bounds.getWidth() / 2.0f; } else if (anchor.isRight()) { xAdj = (float) -bounds.getWidth(); } if (anchor.isTop()) { yAdj = -descent - leading + (float) bounds.getHeight(); } else if (anchor.isHalfAscent()) { yAdj = halfAscent; } else if (anchor.isVerticalCenter()) { yAdj = -descent - leading + (float) (bounds.getHeight() / 2.0); } else if (anchor.isBaseline()) { yAdj = 0.0f; } else if (anchor.isBottom()) { yAdj = -metrics.getDescent() - metrics.getLeading(); } result[0] = xAdj; result[1] = yAdj; return result; } /** * A utility method that calculates the rotation anchor offsets for a * string. These offsets are relative to the text starting coordinate * ({@code BASELINE_LEFT}). * * @param g2 the graphics device. * @param text the text. * @param anchor the anchor point. * * @return The offsets. */ private static float[] deriveRotationAnchorOffsets(Graphics2D g2, String text, TextAnchor anchor) { float[] result = new float[2]; FontRenderContext frc = g2.getFontRenderContext(); LineMetrics metrics = g2.getFont().getLineMetrics(text, frc); FontMetrics fm = g2.getFontMetrics(); Rectangle2D bounds = TextUtils.getTextBounds(text, g2, fm); float ascent = metrics.getAscent(); float halfAscent = ascent / 2.0f; float descent = metrics.getDescent(); float leading = metrics.getLeading(); float xAdj = 0.0f; float yAdj = 0.0f; if (anchor.isLeft()) { xAdj = 0.0f; } else if (anchor.isHorizontalCenter()) { xAdj = (float) bounds.getWidth() / 2.0f; } else if (anchor.isRight()) { xAdj = (float) bounds.getWidth(); } if (anchor.isTop()) { yAdj = descent + leading - (float) bounds.getHeight(); } else if (anchor.isVerticalCenter()) { yAdj = descent + leading - (float) (bounds.getHeight() / 2.0); } else if (anchor.isHalfAscent()) { yAdj = -halfAscent; } else if (anchor.isBaseline()) { yAdj = 0.0f; } else if (anchor.isBottom()) { yAdj = metrics.getDescent() + metrics.getLeading(); } result[0] = xAdj; result[1] = yAdj; return result; } /** * Returns a shape that represents the bounds of the string after the * specified rotation has been applied. * * @param text the text ({@code null} permitted). * @param g2 the graphics device. * @param textX the x coordinate for the text. * @param textY the y coordinate for the text. * @param angle the angle. * @param rotateX the x coordinate for the rotation point. * @param rotateY the y coordinate for the rotation point. * * @return The bounds ({@code null} if {@code text} is * {@code null} or has zero length). */ public static Shape calculateRotatedStringBounds(String text, Graphics2D g2, float textX, float textY, double angle, float rotateX, float rotateY) { if ((text == null) || (text.equals(""))) { return null; } FontMetrics fm = g2.getFontMetrics(); Rectangle2D bounds = TextUtils.getTextBounds(text, g2, fm); AffineTransform translate = AffineTransform.getTranslateInstance( textX, textY); Shape translatedBounds = translate.createTransformedShape(bounds); AffineTransform rotate = AffineTransform.getRotateInstance( angle, rotateX, rotateY); Shape result = rotate.createTransformedShape(translatedBounds); return result; } /** * Returns the flag that controls whether the FontMetrics.getStringBounds() * method is used or not. If you are having trouble with label alignment * or positioning, try changing the value of this flag. * * @return A boolean. */ public static boolean getUseFontMetricsGetStringBounds() { return useFontMetricsGetStringBounds; } /** * Sets the flag that controls whether the FontMetrics.getStringBounds() * method is used or not. If you are having trouble with label alignment * or positioning, try changing the value of this flag. * * @param use the flag. */ public static void setUseFontMetricsGetStringBounds(boolean use) { useFontMetricsGetStringBounds = use; } /** * Returns the flag that controls whether or not a workaround is used for * drawing rotated strings. * * @return A boolean. */ public static boolean isUseDrawRotatedStringWorkaround() { return useDrawRotatedStringWorkaround; } /** * Sets the flag that controls whether or not a workaround is used for * drawing rotated strings. The related bug is on Sun's bug parade * (id 4312117) and the workaround involves using a {@code TextLayout} * instance to draw the text instead of calling the * {@code drawString()} method in the {@code Graphics2D} class. * * @param use the new flag value. */ public static void setUseDrawRotatedStringWorkaround(boolean use) { TextUtils.useDrawRotatedStringWorkaround = use; } /** * Returns the flag that controls whether or not strings are drawn using * the current font attributes (such as underlining, strikethrough etc). * The default value is {@code false}. * * @return A boolean. */ public static boolean getDrawStringsWithFontAttributes() { return TextUtils.drawStringsWithFontAttributes; } /** * Sets the flag that controls whether or not strings are drawn using the * current font attributes. This is a hack to allow underlining of titles * without big changes to the API. See: * http://www.jfree.org/phpBB2/viewtopic.php?p=45459&highlight=#45459 * * @param b the new flag value. */ public static void setDrawStringsWithFontAttributes(boolean b) { TextUtils.drawStringsWithFontAttributes = b; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/text/package-info.java000066400000000000000000000001451463604235500272310ustar00rootroot00000000000000/** * Text-related classes formerly in the JCommon class library. */ package org.jfree.chart.text; jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/title/000077500000000000000000000000001463604235500241775ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/title/CompositeTitle.java000066400000000000000000000205001463604235500300030ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * CompositeTitle.java * ------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Eric Penfold (patch 2006826); * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.title; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.HashUtils; import org.jfree.chart.block.BlockContainer; import org.jfree.chart.block.BorderArrangement; import org.jfree.chart.block.RectangleConstraint; import org.jfree.chart.event.TitleChangeEvent; import org.jfree.chart.ui.Size2D; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.SerialUtils; /** * A title that contains multiple titles within a {@link BlockContainer}. */ public class CompositeTitle extends Title implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -6770854036232562290L; /** * The background paint. */ private transient Paint backgroundPaint; /** A container for the individual titles. */ private BlockContainer container; /** * Creates a new composite title with a default border arrangement. */ public CompositeTitle() { this(new BlockContainer(new BorderArrangement())); } /** * Creates a new title using the specified container. * * @param container the container ({@code null} not permitted). */ public CompositeTitle(BlockContainer container) { Args.nullNotPermitted(container, "container"); this.container = container; this.backgroundPaint = null; } /** * Returns the background paint. * * @return The paint (possibly {@code null}). */ public Paint getBackgroundPaint() { return this.backgroundPaint; } /** * Sets the background paint and sends a {@link TitleChangeEvent} to all * registered listeners. If you set this attribute to {@code null}, * no background is painted (which makes the title background transparent). * * @param paint the background paint ({@code null} permitted). */ public void setBackgroundPaint(Paint paint) { this.backgroundPaint = paint; notifyListeners(new TitleChangeEvent(this)); } /** * Returns the container holding the titles. * * @return The title container (never {@code null}). */ public BlockContainer getContainer() { return this.container; } /** * Sets the title container. * * @param container the container ({@code null} not permitted). */ public void setTitleContainer(BlockContainer container) { Args.nullNotPermitted(container, "container"); this.container = container; } /** * Arranges the contents of the block, within the given constraints, and * returns the block size. * * @param g2 the graphics device. * @param constraint the constraint ({@code null} not permitted). * * @return The block size (in Java2D units, never {@code null}). */ @Override public Size2D arrange(Graphics2D g2, RectangleConstraint constraint) { RectangleConstraint contentConstraint = toContentConstraint(constraint); Size2D contentSize = this.container.arrange(g2, contentConstraint); return new Size2D(calculateTotalWidth(contentSize.getWidth()), calculateTotalHeight(contentSize.getHeight())); } /** * Draws the title on a Java 2D graphics device (such as the screen or a * printer). * * @param g2 the graphics device. * @param area the area allocated for the title. */ @Override public void draw(Graphics2D g2, Rectangle2D area) { draw(g2, area, null); } /** * Draws the block within the specified area. * * @param g2 the graphics device. * @param area the area. * @param params ignored ({@code null} permitted). * * @return Always {@code null}. */ @Override public Object draw(Graphics2D g2, Rectangle2D area, Object params) { area = trimMargin(area); drawBorder(g2, area); area = trimBorder(area); if (this.backgroundPaint != null) { g2.setPaint(this.backgroundPaint); g2.fill(area); } area = trimPadding(area); return this.container.draw(g2, area, params); } /** * Tests this title for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof CompositeTitle)) { return false; } CompositeTitle that = (CompositeTitle) obj; if (!Objects.equals(this.container, that.container)) { return false; } if (!PaintUtils.equal(this.backgroundPaint, that.backgroundPaint)) { return false; } if (!that.canEqual(this)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof CompositeTitle); } @Override public int hashCode() { int hash = super.hashCode(); // equals calls superclass, hashCode must also hash = 53 * hash + Objects.hashCode(this.container); hash = 53 * hash + HashUtils.hashCodeForPaint(this.backgroundPaint); return hash; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.backgroundPaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.backgroundPaint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/title/DateTitle.java000066400000000000000000000141761463604235500267320ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * DateTitle.java * -------------- * (C) Copyright 2000-present, by David Berry and Contributors. * * Original Author: David Berry; * Contributor(s): David Gilbert; * */ package org.jfree.chart.title; import java.awt.Color; import java.awt.Font; import java.awt.Paint; import java.io.Serializable; import java.text.DateFormat; import java.util.Date; import java.util.Locale; import org.jfree.chart.ui.HorizontalAlignment; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.ui.VerticalAlignment; /** * A chart title that displays the date. *

* Keep in mind that a chart can have several titles, and that they can appear * at the top, left, right or bottom of the chart - a {@code DateTitle} * will commonly appear at the bottom of a chart, although you can place it * anywhere. *

* By specifying the locale, dates are formatted to the correct standard for * the given locale. For example, a date would appear as "January 17, 2000" in * the US, but "17 January 2000" in most European locales. */ public class DateTitle extends TextTitle implements Serializable { /** For serialization. */ private static final long serialVersionUID = -465434812763159881L; /** * Creates a new chart title that displays the current date in the default * (LONG) format for the locale, positioned to the bottom right of the * chart. *

* The color will be black in 12 point, plain Helvetica font (maps to Arial * on Win32 systems without Helvetica). */ public DateTitle() { this(DateFormat.LONG); } /** * Creates a new chart title that displays the current date with the * specified style (for the default locale). *

* The date style should be one of: {@code SHORT}, * {@code MEDIUM}, {@code LONG} or {@code FULL} * (defined in {@code java.util.DateFormat}). * * @param style the date style. */ public DateTitle(int style) { this(style, Locale.getDefault(), new Font("Dialog", Font.PLAIN, 12), Color.BLACK); } /** * Creates a new chart title that displays the current date. *

* The date style should be one of: {@code SHORT}, * {@code MEDIUM}, {@code LONG} or {@code FULL} (defined * in {@code java.util.DateFormat}). *

* For the locale, you can use {@code Locale.getDefault()} for the * default locale. * * @param style the date style. * @param locale the locale. * @param font the font. * @param paint the text color. */ public DateTitle(int style, Locale locale, Font font, Paint paint) { this(style, locale, font, paint, RectangleEdge.BOTTOM, HorizontalAlignment.RIGHT, VerticalAlignment.CENTER, Title.DEFAULT_PADDING); } /** * Creates a new chart title that displays the current date. *

* The date style should be one of: {@code SHORT}, * {@code MEDIUM}, {@code LONG} or {@code FULL} (defined * in {@code java.util.DateFormat}). *

* For the locale, you can use {@code Locale.getDefault()} for the * default locale. * * @param style the date style. * @param locale the locale. * @param font the font (not null). * @param paint the text color (not null). * @param position the relative location of this title (use constants in * Title). * @param horizontalAlignment the horizontal text alignment of this title * (use constants in Title). * @param verticalAlignment the vertical text alignment of this title (use * constants in Title). * @param padding determines the blank space around the outside of the * title (not null). */ public DateTitle(int style, Locale locale, Font font, Paint paint, RectangleEdge position, HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment, RectangleInsets padding) { super(DateFormat.getDateInstance(style, locale).format(new Date()), font, paint, position, horizontalAlignment, verticalAlignment, padding); } /** * Set the format of the date. *

* The date style should be one of: {@code SHORT}, * {@code MEDIUM}, {@code LONG} or {@code FULL} (defined * in {@code java.util.DateFormat}). *

* For the locale, you can use {@code Locale.getDefault()} for the * default locale. * * @param style the date style. * @param locale the locale. */ public void setDateFormat(int style, Locale locale) { setText(DateFormat.getDateInstance(style, locale).format(new Date())); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/title/ImageTitle.java000066400000000000000000000313101463604235500270640ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * ImageTitle.java * --------------- * (C) Copyright 2000-present, by David Berry and Contributors; * * Original Author: David Berry; * Contributor(s): David Gilbert; * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.title; import java.awt.Graphics2D; import java.awt.Image; import java.awt.geom.Rectangle2D; import java.util.Objects; import org.jfree.chart.block.RectangleConstraint; import org.jfree.chart.event.TitleChangeEvent; import org.jfree.chart.ui.HorizontalAlignment; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.ui.Size2D; import org.jfree.chart.ui.VerticalAlignment; /** * A chart title that displays an image. This is useful, for example, if you * have an image of your corporate logo and want to use as a footnote or part * of a title in a chart you create. *

* ImageTitle needs an image passed to it in the constructor. For ImageTitle * to work, you must have already loaded this image from its source (disk or * URL). It is recommended you use something like * Toolkit.getDefaultToolkit().getImage() to get the image. Then, use * MediaTracker or some other message to make sure the image is fully loaded * from disk. *

* SPECIAL NOTE: this class fails to serialize, so if you are * relying on your charts to be serializable, please avoid using this class. */ public class ImageTitle extends Title { /** The title image. */ private Image image; /** * Creates a new image title. * * @param image the image ({@code null} not permitted). */ public ImageTitle(Image image) { this(image, image.getHeight(null), image.getWidth(null), Title.DEFAULT_POSITION, Title.DEFAULT_HORIZONTAL_ALIGNMENT, Title.DEFAULT_VERTICAL_ALIGNMENT, Title.DEFAULT_PADDING); } /** * Creates a new image title. * * @param image the image ({@code null} not permitted). * @param position the title position. * @param horizontalAlignment the horizontal alignment. * @param verticalAlignment the vertical alignment. */ public ImageTitle(Image image, RectangleEdge position, HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment) { this(image, image.getHeight(null), image.getWidth(null), position, horizontalAlignment, verticalAlignment, Title.DEFAULT_PADDING); } /** * Creates a new image title with the given image scaled to the given * width and height in the given location. * * @param image the image ({@code null} not permitted). * @param height the height used to draw the image. * @param width the width used to draw the image. * @param position the title position. * @param horizontalAlignment the horizontal alignment. * @param verticalAlignment the vertical alignment. * @param padding the amount of space to leave around the outside of the * title. */ public ImageTitle(Image image, int height, int width, RectangleEdge position, HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment, RectangleInsets padding) { super(position, horizontalAlignment, verticalAlignment, padding); if (image == null) { throw new NullPointerException("Null 'image' argument."); } this.image = image; setHeight(height); setWidth(width); } /** * Returns the image for the title. * * @return The image for the title (never {@code null}). */ public Image getImage() { return this.image; } /** * Sets the image for the title and notifies registered listeners that the * title has been modified. * * @param image the new image ({@code null} not permitted). */ public void setImage(Image image) { if (image == null) { throw new NullPointerException("Null 'image' argument."); } this.image = image; notifyListeners(new TitleChangeEvent(this)); } /** * Arranges the contents of the block, within the given constraints, and * returns the block size. * * @param g2 the graphics device. * @param constraint the constraint ({@code null} not permitted). * * @return The block size (in Java2D units, never {@code null}). */ @Override public Size2D arrange(Graphics2D g2, RectangleConstraint constraint) { Size2D s = new Size2D(this.image.getWidth(null), this.image.getHeight(null)); return new Size2D(calculateTotalWidth(s.getWidth()), calculateTotalHeight(s.getHeight())); } /** * Draws the title on a Java 2D graphics device (such as the screen or a * printer). * * @param g2 the graphics device. * @param area the area allocated for the title. */ @Override public void draw(Graphics2D g2, Rectangle2D area) { RectangleEdge position = getPosition(); if (position == RectangleEdge.TOP || position == RectangleEdge.BOTTOM) { drawHorizontal(g2, area); } else if (position == RectangleEdge.LEFT || position == RectangleEdge.RIGHT) { drawVertical(g2, area); } else { throw new RuntimeException("Invalid title position."); } } /** * Draws the title on a Java 2D graphics device (such as the screen or a * printer). * * @param g2 the graphics device. * @param chartArea the area within which the title (and plot) should be * drawn. * * @return The size of the area used by the title. */ protected Size2D drawHorizontal(Graphics2D g2, Rectangle2D chartArea) { double startY; double topSpace; double bottomSpace; double leftSpace; double rightSpace; double w = getWidth(); double h = getHeight(); RectangleInsets padding = getPadding(); topSpace = padding.calculateTopOutset(h); bottomSpace = padding.calculateBottomOutset(h); leftSpace = padding.calculateLeftOutset(w); rightSpace = padding.calculateRightOutset(w); if (getPosition() == RectangleEdge.TOP) { startY = chartArea.getY() + topSpace; } else { startY = chartArea.getY() + chartArea.getHeight() - bottomSpace - h; } // what is our alignment? HorizontalAlignment horizontalAlignment = getHorizontalAlignment(); double startX = 0.0; if (horizontalAlignment == HorizontalAlignment.CENTER) { startX = chartArea.getX() + leftSpace + chartArea.getWidth() / 2.0 - w / 2.0; } else if (horizontalAlignment == HorizontalAlignment.LEFT) { startX = chartArea.getX() + leftSpace; } else if (horizontalAlignment == HorizontalAlignment.RIGHT) { startX = chartArea.getX() + chartArea.getWidth() - rightSpace - w; } g2.drawImage(this.image, (int) startX, (int) startY, (int) w, (int) h, null); return new Size2D(chartArea.getWidth() + leftSpace + rightSpace, h + topSpace + bottomSpace); } /** * Draws the title on a Java 2D graphics device (such as the screen or a * printer). * * @param g2 the graphics device. * @param chartArea the area within which the title (and plot) should be * drawn. * * @return The size of the area used by the title. */ protected Size2D drawVertical(Graphics2D g2, Rectangle2D chartArea) { double startX; double topSpace = 0.0; double bottomSpace = 0.0; double leftSpace = 0.0; double rightSpace = 0.0; double w = getWidth(); double h = getHeight(); RectangleInsets padding = getPadding(); if (padding != null) { topSpace = padding.calculateTopOutset(h); bottomSpace = padding.calculateBottomOutset(h); leftSpace = padding.calculateLeftOutset(w); rightSpace = padding.calculateRightOutset(w); } if (getPosition() == RectangleEdge.LEFT) { startX = chartArea.getX() + leftSpace; } else { startX = chartArea.getMaxX() - rightSpace - w; } // what is our alignment? VerticalAlignment alignment = getVerticalAlignment(); double startY = 0.0; if (alignment == VerticalAlignment.CENTER) { startY = chartArea.getMinY() + topSpace + chartArea.getHeight() / 2.0 - h / 2.0; } else if (alignment == VerticalAlignment.TOP) { startY = chartArea.getMinY() + topSpace; } else if (alignment == VerticalAlignment.BOTTOM) { startY = chartArea.getMaxY() - bottomSpace - h; } g2.drawImage(this.image, (int) startX, (int) startY, (int) w, (int) h, null); return new Size2D(chartArea.getWidth() + leftSpace + rightSpace, h + topSpace + bottomSpace); } /** * Draws the block within the specified area. * * @param g2 the graphics device. * @param area the area. * @param params ignored ({@code null} permitted). * * @return Always {@code null}. */ @Override public Object draw(Graphics2D g2, Rectangle2D area, Object params) { draw(g2, area); return null; } /** * Tests this {@code ImageTitle} for equality with an arbitrary * object. Returns {@code true} if: *

    *
  • {@code obj} is an instance of {@code ImageTitle}; *
  • {@code obj} references the same image as this * {@code ImageTitle}; *
  • {@code super.equals(obj)} returns {@code true}; *
* * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof ImageTitle)) { return false; } ImageTitle that = (ImageTitle) obj; if (!Objects.equals(this.image, that.image)) { return false; } if (!that.canEqual(this)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof ImageTitle); } @Override public int hashCode() { int hash = super.hashCode(); // equals calls superclass, hashCode must also hash = 17 * hash + Objects.hashCode(this.image); return hash; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/title/LegendGraphic.java000066400000000000000000000552401463604235500275440ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * LegendGraphic.java * ------------------ * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.title; import java.awt.GradientPaint; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Objects; import org.jfree.chart.block.AbstractBlock; import org.jfree.chart.block.Block; import org.jfree.chart.block.LengthConstraintType; import org.jfree.chart.block.RectangleConstraint; import org.jfree.chart.ui.GradientPaintTransformer; import org.jfree.chart.ui.RectangleAnchor; import org.jfree.chart.ui.Size2D; import org.jfree.chart.ui.StandardGradientPaintTransformer; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; import org.jfree.chart.util.ShapeUtils; /** * The graphical item within a legend item. */ public class LegendGraphic extends AbstractBlock implements Block, PublicCloneable { /** For serialization. */ static final long serialVersionUID = -1338791523854985009L; /** * A flag that controls whether or not the shape is visible - see also * lineVisible. */ private boolean shapeVisible; /** * The shape to display. To allow for accurate positioning, the center * of the shape should be at (0, 0). */ private transient Shape shape; /** * Defines the location within the block to which the shape will be aligned. */ private RectangleAnchor shapeLocation; /** * Defines the point on the shape's bounding rectangle that will be * aligned to the drawing location when the shape is rendered. */ private RectangleAnchor shapeAnchor; /** A flag that controls whether or not the shape is filled. */ private boolean shapeFilled; /** The fill paint for the shape. */ private transient Paint fillPaint; /** * The fill paint transformer (used if the fillPaint is an instance of * GradientPaint). */ private GradientPaintTransformer fillPaintTransformer; /** A flag that controls whether or not the shape outline is visible. */ private boolean shapeOutlineVisible; /** The outline paint for the shape. */ private transient Paint outlinePaint; /** The outline stroke for the shape. */ private transient Stroke outlineStroke; /** * A flag that controls whether or not the line is visible - see also * shapeVisible. */ private boolean lineVisible; /** The line. */ private transient Shape line; /** The line stroke. */ private transient Stroke lineStroke; /** The line paint. */ private transient Paint linePaint; /** * Creates a new legend graphic. * * @param shape the shape ({@code null} not permitted). * @param fillPaint the fill paint ({@code null} not permitted). */ public LegendGraphic(Shape shape, Paint fillPaint) { Args.nullNotPermitted(shape, "shape"); Args.nullNotPermitted(fillPaint, "fillPaint"); this.shapeVisible = true; this.shape = shape; this.shapeAnchor = RectangleAnchor.CENTER; this.shapeLocation = RectangleAnchor.CENTER; this.shapeFilled = true; this.fillPaint = fillPaint; this.fillPaintTransformer = new StandardGradientPaintTransformer(); setPadding(2.0, 2.0, 2.0, 2.0); } /** * Returns a flag that controls whether or not the shape * is visible. * * @return A boolean. * * @see #setShapeVisible(boolean) */ public boolean isShapeVisible() { return this.shapeVisible; } /** * Sets a flag that controls whether or not the shape is * visible. * * @param visible the flag. * * @see #isShapeVisible() */ public void setShapeVisible(boolean visible) { this.shapeVisible = visible; } /** * Returns the shape. * * @return The shape. * * @see #setShape(Shape) */ public Shape getShape() { return this.shape; } /** * Sets the shape. * * @param shape the shape. * * @see #getShape() */ public void setShape(Shape shape) { this.shape = shape; } /** * Returns a flag that controls whether or not the shapes * are filled. * * @return A boolean. * * @see #setShapeFilled(boolean) */ public boolean isShapeFilled() { return this.shapeFilled; } /** * Sets a flag that controls whether or not the shape is * filled. * * @param filled the flag. * * @see #isShapeFilled() */ public void setShapeFilled(boolean filled) { this.shapeFilled = filled; } /** * Returns the paint used to fill the shape. * * @return The fill paint. * * @see #setFillPaint(Paint) */ public Paint getFillPaint() { return this.fillPaint; } /** * Sets the paint used to fill the shape. * * @param paint the paint. * * @see #getFillPaint() */ public void setFillPaint(Paint paint) { this.fillPaint = paint; } /** * Returns the transformer used when the fill paint is an instance of * {@code GradientPaint}. * * @return The transformer (never {@code null}). * * @see #setFillPaintTransformer(GradientPaintTransformer) */ public GradientPaintTransformer getFillPaintTransformer() { return this.fillPaintTransformer; } /** * Sets the transformer used when the fill paint is an instance of * {@code GradientPaint}. * * @param transformer the transformer ({@code null} not permitted). * * @see #getFillPaintTransformer() */ public void setFillPaintTransformer(GradientPaintTransformer transformer) { Args.nullNotPermitted(transformer, "transformer"); this.fillPaintTransformer = transformer; } /** * Returns a flag that controls whether the shape outline is visible. * * @return A boolean. * * @see #setShapeOutlineVisible(boolean) */ public boolean isShapeOutlineVisible() { return this.shapeOutlineVisible; } /** * Sets a flag that controls whether or not the shape outline * is visible. * * @param visible the flag. * * @see #isShapeOutlineVisible() */ public void setShapeOutlineVisible(boolean visible) { this.shapeOutlineVisible = visible; } /** * Returns the outline paint. * * @return The paint. * * @see #setOutlinePaint(Paint) */ public Paint getOutlinePaint() { return this.outlinePaint; } /** * Sets the outline paint. * * @param paint the paint. * * @see #getOutlinePaint() */ public void setOutlinePaint(Paint paint) { this.outlinePaint = paint; } /** * Returns the outline stroke. * * @return The stroke. * * @see #setOutlineStroke(Stroke) */ public Stroke getOutlineStroke() { return this.outlineStroke; } /** * Sets the outline stroke. * * @param stroke the stroke. * * @see #getOutlineStroke() */ public void setOutlineStroke(Stroke stroke) { this.outlineStroke = stroke; } /** * Returns the shape anchor. * * @return The shape anchor. * * @see #getShapeAnchor() */ public RectangleAnchor getShapeAnchor() { return this.shapeAnchor; } /** * Sets the shape anchor. This defines a point on the shapes bounding * rectangle that will be used to align the shape to a location. * * @param anchor the anchor ({@code null} not permitted). * * @see #setShapeAnchor(RectangleAnchor) */ public void setShapeAnchor(RectangleAnchor anchor) { Args.nullNotPermitted(anchor, "anchor"); this.shapeAnchor = anchor; } /** * Returns the shape location. * * @return The shape location. * * @see #setShapeLocation(RectangleAnchor) */ public RectangleAnchor getShapeLocation() { return this.shapeLocation; } /** * Sets the shape location. This defines a point within the drawing * area that will be used to align the shape to. * * @param location the location ({@code null} not permitted). * * @see #getShapeLocation() */ public void setShapeLocation(RectangleAnchor location) { Args.nullNotPermitted(location, "location"); this.shapeLocation = location; } /** * Returns the flag that controls whether or not the line is visible. * * @return A boolean. * * @see #setLineVisible(boolean) */ public boolean isLineVisible() { return this.lineVisible; } /** * Sets the flag that controls whether or not the line is visible. * * @param visible the flag. * * @see #isLineVisible() */ public void setLineVisible(boolean visible) { this.lineVisible = visible; } /** * Returns the line centered about (0, 0). * * @return The line. * * @see #setLine(Shape) */ public Shape getLine() { return this.line; } /** * Sets the line. A Shape is used here, because then you can use Line2D, * GeneralPath or any other Shape to represent the line. * * @param line the line. * * @see #getLine() */ public void setLine(Shape line) { this.line = line; } /** * Returns the line paint. * * @return The paint. * * @see #setLinePaint(Paint) */ public Paint getLinePaint() { return this.linePaint; } /** * Sets the line paint. * * @param paint the paint. * * @see #getLinePaint() */ public void setLinePaint(Paint paint) { this.linePaint = paint; } /** * Returns the line stroke. * * @return The stroke. * * @see #setLineStroke(Stroke) */ public Stroke getLineStroke() { return this.lineStroke; } /** * Sets the line stroke. * * @param stroke the stroke. * * @see #getLineStroke() */ public void setLineStroke(Stroke stroke) { this.lineStroke = stroke; } /** * Arranges the contents of the block, within the given constraints, and * returns the block size. * * @param g2 the graphics device. * @param constraint the constraint ({@code null} not permitted). * * @return The block size (in Java2D units, never {@code null}). */ @Override public Size2D arrange(Graphics2D g2, RectangleConstraint constraint) { RectangleConstraint contentConstraint = toContentConstraint(constraint); LengthConstraintType w = contentConstraint.getWidthConstraintType(); LengthConstraintType h = contentConstraint.getHeightConstraintType(); Size2D contentSize = null; if (w == LengthConstraintType.NONE) { if (h == LengthConstraintType.NONE) { contentSize = arrangeNN(g2); } else if (h == LengthConstraintType.RANGE) { throw new RuntimeException("Not yet implemented."); } else if (h == LengthConstraintType.FIXED) { throw new RuntimeException("Not yet implemented."); } } else if (w == LengthConstraintType.RANGE) { if (h == LengthConstraintType.NONE) { throw new RuntimeException("Not yet implemented."); } else if (h == LengthConstraintType.RANGE) { throw new RuntimeException("Not yet implemented."); } else if (h == LengthConstraintType.FIXED) { throw new RuntimeException("Not yet implemented."); } } else if (w == LengthConstraintType.FIXED) { if (h == LengthConstraintType.NONE) { throw new RuntimeException("Not yet implemented."); } else if (h == LengthConstraintType.RANGE) { throw new RuntimeException("Not yet implemented."); } else if (h == LengthConstraintType.FIXED) { contentSize = new Size2D(contentConstraint.getWidth(), contentConstraint.getHeight()); } } assert contentSize != null; return new Size2D(calculateTotalWidth(contentSize.getWidth()), calculateTotalHeight(contentSize.getHeight())); } /** * Performs the layout with no constraint, so the content size is * determined by the bounds of the shape and/or line drawn to represent * the series. * * @param g2 the graphics device. * * @return The content size. */ protected Size2D arrangeNN(Graphics2D g2) { Rectangle2D contentSize = new Rectangle2D.Double(); if (this.line != null) { contentSize.setRect(this.line.getBounds2D()); } if (this.shape != null) { contentSize = contentSize.createUnion(this.shape.getBounds2D()); } return new Size2D(contentSize.getWidth(), contentSize.getHeight()); } /** * Draws the graphic item within the specified area. * * @param g2 the graphics device. * @param area the area. */ @Override public void draw(Graphics2D g2, Rectangle2D area) { area = trimMargin(area); drawBorder(g2, area); area = trimBorder(area); area = trimPadding(area); if (this.lineVisible) { Point2D location = this.shapeLocation.getAnchorPoint(area); Shape aLine = ShapeUtils.createTranslatedShape(getLine(), this.shapeAnchor, location.getX(), location.getY()); g2.setPaint(this.linePaint); g2.setStroke(this.lineStroke); g2.draw(aLine); } if (this.shapeVisible) { Point2D location = this.shapeLocation.getAnchorPoint(area); Shape s = ShapeUtils.createTranslatedShape(this.shape, this.shapeAnchor, location.getX(), location.getY()); if (this.shapeFilled) { Paint p = this.fillPaint; if (p instanceof GradientPaint) { GradientPaint gp = (GradientPaint) this.fillPaint; p = this.fillPaintTransformer.transform(gp, s); } g2.setPaint(p); g2.fill(s); } if (this.shapeOutlineVisible) { g2.setPaint(this.outlinePaint); g2.setStroke(this.outlineStroke); g2.draw(s); } } } /** * Draws the block within the specified area. * * @param g2 the graphics device. * @param area the area. * @param params ignored ({@code null} permitted). * * @return Always {@code null}. */ @Override public Object draw(Graphics2D g2, Rectangle2D area, Object params) { draw(g2, area); return null; } /** * Tests this {@code LegendGraphic} instance for equality with an * arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof LegendGraphic)) { return false; } LegendGraphic that = (LegendGraphic) obj; if (this.shapeVisible != that.shapeVisible) { return false; } if (!ShapeUtils.equal(this.shape, that.shape)) { return false; } if (!PaintUtils.equal(this.fillPaint, that.fillPaint)) { return false; } if (this.shapeFilled != that.shapeFilled) { return false; } if (!Objects.equals(this.fillPaintTransformer, that.fillPaintTransformer)) { return false; } if (this.shapeOutlineVisible != that.shapeOutlineVisible) { return false; } if (!PaintUtils.equal(this.outlinePaint, that.outlinePaint)) { return false; } if (!Objects.equals(this.outlineStroke, that.outlineStroke)) { return false; } if (this.shapeAnchor != that.shapeAnchor) { return false; } if (this.shapeLocation != that.shapeLocation) { return false; } if (this.lineVisible != that.lineVisible) { return false; } if (!ShapeUtils.equal(this.line, that.line)) { return false; } if (!PaintUtils.equal(this.linePaint, that.linePaint)) { return false; } if (!Objects.equals(this.lineStroke, that.lineStroke)) { return false; } if (!that.canEqual(this)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof LegendGraphic); } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int hash = super.hashCode(); // equals calls superclass, hashCode must also hash = 23 * hash + (this.shapeVisible ? 1 : 0); hash = 23 * hash + Objects.hashCode(this.shape); hash = 23 * hash + Objects.hashCode(this.shapeLocation); hash = 23 * hash + Objects.hashCode(this.shapeAnchor); hash = 23 * hash + (this.shapeFilled ? 1 : 0); hash = 23 * hash + Objects.hashCode(this.fillPaint); hash = 23 * hash + Objects.hashCode(this.fillPaintTransformer); hash = 23 * hash + (this.shapeOutlineVisible ? 1 : 0); hash = 23 * hash + Objects.hashCode(this.outlinePaint); hash = 23 * hash + Objects.hashCode(this.outlineStroke); hash = 23 * hash + (this.lineVisible ? 1 : 0); hash = 23 * hash + Objects.hashCode(this.line); hash = 23 * hash + Objects.hashCode(this.lineStroke); hash = 23 * hash + Objects.hashCode(this.linePaint); return hash; } /** * Returns a clone of this {@code LegendGraphic} instance. * * @return A clone of this {@code LegendGraphic} instance. * * @throws CloneNotSupportedException if there is a problem cloning. */ @Override public Object clone() throws CloneNotSupportedException { LegendGraphic clone = (LegendGraphic) super.clone(); clone.shape = ShapeUtils.clone(this.shape); clone.line = ShapeUtils.clone(this.line); return clone; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writeShape(this.shape, stream); SerialUtils.writePaint(this.fillPaint, stream); SerialUtils.writePaint(this.outlinePaint, stream); SerialUtils.writeStroke(this.outlineStroke, stream); SerialUtils.writeShape(this.line, stream); SerialUtils.writePaint(this.linePaint, stream); SerialUtils.writeStroke(this.lineStroke, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.shape = SerialUtils.readShape(stream); this.fillPaint = SerialUtils.readPaint(stream); this.outlinePaint = SerialUtils.readPaint(stream); this.outlineStroke = SerialUtils.readStroke(stream); this.line = SerialUtils.readShape(stream); this.linePaint = SerialUtils.readPaint(stream); this.lineStroke = SerialUtils.readStroke(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/title/LegendItemBlockContainer.java000066400000000000000000000127661463604235500317110ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------- * LegendItemBlockContainer.java * ----------------------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.title; import java.awt.Graphics2D; import java.awt.Shape; import java.awt.geom.Rectangle2D; import org.jfree.chart.block.Arrangement; import org.jfree.chart.block.BlockContainer; import org.jfree.chart.block.BlockResult; import org.jfree.chart.block.EntityBlockParams; import org.jfree.chart.block.EntityBlockResult; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.entity.LegendItemEntity; import org.jfree.chart.entity.StandardEntityCollection; import org.jfree.data.general.Dataset; /** * A container that holds all the pieces of a single legend item. */ public class LegendItemBlockContainer extends BlockContainer { /** * The dataset. */ private Dataset dataset; /** * The series key. */ private Comparable seriesKey; /** The dataset index. */ private int datasetIndex; /** The series index. */ private int series; /** The tool tip text (can be {@code null}). */ private String toolTipText; /** The URL text (can be {@code null}). */ private String urlText; /** * Creates a new legend item block. * * @param arrangement the arrangement. * @param dataset the dataset. * @param seriesKey the series key. */ public LegendItemBlockContainer(Arrangement arrangement, Dataset dataset, Comparable seriesKey) { super(arrangement); this.dataset = dataset; this.seriesKey = seriesKey; } /** * Returns a reference to the dataset for the associated legend item. * * @return A dataset reference. */ public Dataset getDataset() { return this.dataset; } /** * Returns the series key. * * @return The series key. */ public Comparable getSeriesKey() { return this.seriesKey; } /** * Returns the series index. * * @return The series index. */ public int getSeriesIndex() { return this.series; } /** * Returns the tool tip text. * * @return The tool tip text (possibly {@code null}). */ public String getToolTipText() { return this.toolTipText; } /** * Sets the tool tip text. * * @param text the text ({@code null} permitted). */ public void setToolTipText(String text) { this.toolTipText = text; } /** * Returns the URL text. * * @return The URL text (possibly {@code null}). */ public String getURLText() { return this.urlText; } /** * Sets the URL text. * * @param text the text ({@code null} permitted). */ public void setURLText(String text) { this.urlText = text; } /** * Draws the block within the specified area. * * @param g2 the graphics device. * @param area the area. * @param params passed on to blocks within the container * ({@code null} permitted). * * @return An instance of {@link EntityBlockResult}, or {@code null}. */ @Override public Object draw(Graphics2D g2, Rectangle2D area, Object params) { // draw the block without collecting entities super.draw(g2, area, null); EntityBlockParams ebp; BlockResult r = new BlockResult(); if (params instanceof EntityBlockParams) { ebp = (EntityBlockParams) params; if (ebp.getGenerateEntities()) { EntityCollection ec = new StandardEntityCollection(); LegendItemEntity entity = new LegendItemEntity( (Shape) area.clone()); entity.setSeriesKey(this.seriesKey); entity.setDataset(this.dataset); entity.setToolTipText(getToolTipText()); entity.setURLText(getURLText()); ec.add(entity); r.setEntityCollection(ec); } } return r; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/title/LegendTitle.java000066400000000000000000000573561463604235500272620ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * LegendTitle.java * ---------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Pierre-Marie Le Biot; * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.title; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Arrays; import java.util.Objects; import org.jfree.chart.HashUtils; import org.jfree.chart.LegendItem; import org.jfree.chart.LegendItemCollection; import org.jfree.chart.LegendItemSource; import org.jfree.chart.block.Arrangement; import org.jfree.chart.block.Block; import org.jfree.chart.block.BlockContainer; import org.jfree.chart.block.BlockFrame; import org.jfree.chart.block.BlockResult; import org.jfree.chart.block.BorderArrangement; import org.jfree.chart.block.CenterArrangement; import org.jfree.chart.block.ColumnArrangement; import org.jfree.chart.block.EntityBlockParams; import org.jfree.chart.block.FlowArrangement; import org.jfree.chart.block.LabelBlock; import org.jfree.chart.block.RectangleConstraint; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.entity.StandardEntityCollection; import org.jfree.chart.entity.TitleEntity; import org.jfree.chart.event.TitleChangeEvent; import org.jfree.chart.ui.RectangleAnchor; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.ui.Size2D; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.SortOrder; /** * A chart title that displays a legend for the data in the chart. *

* The title can be populated with legend items manually, or you can assign a * reference to the plot, in which case the legend items will be automatically * created to match the dataset(s). */ public class LegendTitle extends Title implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 2644010518533854633L; /** The default item font. */ public static final Font DEFAULT_ITEM_FONT = new Font("SansSerif", Font.PLAIN, 12); /** The default item paint. */ public static final Paint DEFAULT_ITEM_PAINT = Color.BLACK; /** The sources for legend items. */ private LegendItemSource[] sources; /** The background paint (possibly {@code null}). */ private transient Paint backgroundPaint; /** The edge for the legend item graphic relative to the text. */ private RectangleEdge legendItemGraphicEdge; /** The anchor point for the legend item graphic. */ private RectangleAnchor legendItemGraphicAnchor; /** The legend item graphic location. */ private RectangleAnchor legendItemGraphicLocation; /** The padding for the legend item graphic. */ private RectangleInsets legendItemGraphicPadding; /** The item font. */ private Font itemFont; /** The item paint. */ private transient Paint itemPaint; /** The padding for the item labels. */ private RectangleInsets itemLabelPadding; /** * A container that holds and displays the legend items. */ private BlockContainer items; /** * The layout for the legend when it is positioned at the top or bottom * of the chart. */ private Arrangement hLayout; /** * The layout for the legend when it is positioned at the left or right * of the chart. */ private Arrangement vLayout; /** * An optional container for wrapping the legend items (allows for adding * a title or other text to the legend). */ private BlockContainer wrapper; /** * Whether to render legend items in ascending or descending order. */ private SortOrder sortOrder; /** * Constructs a new (empty) legend for the specified source. * * @param source the source. */ public LegendTitle(LegendItemSource source) { this(source, new FlowArrangement(), new ColumnArrangement()); } /** * Creates a new legend title with the specified arrangement. * * @param source the source. * @param hLayout the horizontal item arrangement ({@code null} not * permitted). * @param vLayout the vertical item arrangement ({@code null} not * permitted). */ public LegendTitle(LegendItemSource source, Arrangement hLayout, Arrangement vLayout) { this.sources = new LegendItemSource[] {source}; this.items = new BlockContainer(hLayout); this.hLayout = hLayout; this.vLayout = vLayout; this.backgroundPaint = null; this.legendItemGraphicEdge = RectangleEdge.LEFT; this.legendItemGraphicAnchor = RectangleAnchor.CENTER; this.legendItemGraphicLocation = RectangleAnchor.CENTER; this.legendItemGraphicPadding = new RectangleInsets(2.0, 2.0, 2.0, 2.0); this.itemFont = DEFAULT_ITEM_FONT; this.itemPaint = DEFAULT_ITEM_PAINT; this.itemLabelPadding = new RectangleInsets(2.0, 2.0, 2.0, 2.0); this.sortOrder = SortOrder.ASCENDING; } /** * Returns the legend item sources. * * @return The sources. */ public LegendItemSource[] getSources() { return this.sources; } /** * Sets the legend item sources and sends a {@link TitleChangeEvent} to * all registered listeners. * * @param sources the sources ({@code null} not permitted). */ public void setSources(LegendItemSource[] sources) { Args.nullNotPermitted(sources, "sources"); this.sources = sources; notifyListeners(new TitleChangeEvent(this)); } /** * Returns the background paint. * * @return The background paint (possibly {@code null}). */ public Paint getBackgroundPaint() { return this.backgroundPaint; } /** * Sets the background paint for the legend and sends a * {@link TitleChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} permitted). */ public void setBackgroundPaint(Paint paint) { this.backgroundPaint = paint; notifyListeners(new TitleChangeEvent(this)); } /** * Returns the location of the shape within each legend item. * * @return The location (never {@code null}). */ public RectangleEdge getLegendItemGraphicEdge() { return this.legendItemGraphicEdge; } /** * Sets the location of the shape within each legend item. * * @param edge the edge ({@code null} not permitted). */ public void setLegendItemGraphicEdge(RectangleEdge edge) { Args.nullNotPermitted(edge, "edge"); this.legendItemGraphicEdge = edge; notifyListeners(new TitleChangeEvent(this)); } /** * Returns the legend item graphic anchor. * * @return The graphic anchor (never {@code null}). */ public RectangleAnchor getLegendItemGraphicAnchor() { return this.legendItemGraphicAnchor; } /** * Sets the anchor point used for the graphic in each legend item. * * @param anchor the anchor point ({@code null} not permitted). */ public void setLegendItemGraphicAnchor(RectangleAnchor anchor) { Args.nullNotPermitted(anchor, "anchor"); this.legendItemGraphicAnchor = anchor; } /** * Returns the legend item graphic location. * * @return The location (never {@code null}). */ public RectangleAnchor getLegendItemGraphicLocation() { return this.legendItemGraphicLocation; } /** * Sets the legend item graphic location. * * @param anchor the anchor ({@code null} not permitted). */ public void setLegendItemGraphicLocation(RectangleAnchor anchor) { this.legendItemGraphicLocation = anchor; } /** * Returns the padding that will be applied to each item graphic. * * @return The padding (never {@code null}). */ public RectangleInsets getLegendItemGraphicPadding() { return this.legendItemGraphicPadding; } /** * Sets the padding that will be applied to each item graphic in the * legend and sends a {@link TitleChangeEvent} to all registered listeners. * * @param padding the padding ({@code null} not permitted). */ public void setLegendItemGraphicPadding(RectangleInsets padding) { Args.nullNotPermitted(padding, "padding"); this.legendItemGraphicPadding = padding; notifyListeners(new TitleChangeEvent(this)); } /** * Returns the item font. * * @return The font (never {@code null}). */ public Font getItemFont() { return this.itemFont; } /** * Sets the item font and sends a {@link TitleChangeEvent} to * all registered listeners. * * @param font the font ({@code null} not permitted). */ public void setItemFont(Font font) { Args.nullNotPermitted(font, "font"); this.itemFont = font; notifyListeners(new TitleChangeEvent(this)); } /** * Returns the item paint. * * @return The paint (never {@code null}). */ public Paint getItemPaint() { return this.itemPaint; } /** * Sets the item paint. * * @param paint the paint ({@code null} not permitted). */ public void setItemPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.itemPaint = paint; notifyListeners(new TitleChangeEvent(this)); } /** * Returns the padding used for the items labels. * * @return The padding (never {@code null}). */ public RectangleInsets getItemLabelPadding() { return this.itemLabelPadding; } /** * Sets the padding used for the item labels in the legend. * * @param padding the padding ({@code null} not permitted). */ public void setItemLabelPadding(RectangleInsets padding) { Args.nullNotPermitted(padding, "padding"); this.itemLabelPadding = padding; notifyListeners(new TitleChangeEvent(this)); } /** * Gets the order used to display legend items. * * @return The order (never {@code null}). */ public SortOrder getSortOrder() { return this.sortOrder; } /** * Sets the order used to display legend items. * * @param order Specifies ascending or descending order ({@code null} * not permitted). */ public void setSortOrder(SortOrder order) { Args.nullNotPermitted(order, "order"); this.sortOrder = order; notifyListeners(new TitleChangeEvent(this)); } /** * Fetches the latest legend items. */ protected void fetchLegendItems() { this.items.clear(); RectangleEdge p = getPosition(); if (RectangleEdge.isTopOrBottom(p)) { this.items.setArrangement(this.hLayout); } else { this.items.setArrangement(this.vLayout); } if (this.sortOrder.equals(SortOrder.ASCENDING)) { for (int s = 0; s < this.sources.length; s++) { LegendItemCollection legendItems = this.sources[s].getLegendItems(); if (legendItems != null) { for (int i = 0; i < legendItems.getItemCount(); i++) { addItemBlock(legendItems.get(i)); } } } } else { for (int s = this.sources.length - 1; s >= 0; s--) { LegendItemCollection legendItems = this.sources[s].getLegendItems(); if (legendItems != null) { for (int i = legendItems.getItemCount()-1; i >= 0; i--) { addItemBlock(legendItems.get(i)); } } } } } private void addItemBlock(LegendItem item) { Block block = createLegendItemBlock(item); this.items.add(block); } /** * Creates a legend item block. * * @param item the legend item. * * @return The block. */ protected Block createLegendItemBlock(LegendItem item) { BlockContainer result; LegendGraphic lg = new LegendGraphic(item.getShape(), item.getFillPaint()); lg.setFillPaintTransformer(item.getFillPaintTransformer()); lg.setShapeFilled(item.isShapeFilled()); lg.setLine(item.getLine()); lg.setLineStroke(item.getLineStroke()); lg.setLinePaint(item.getLinePaint()); lg.setLineVisible(item.isLineVisible()); lg.setShapeVisible(item.isShapeVisible()); lg.setShapeOutlineVisible(item.isShapeOutlineVisible()); lg.setOutlinePaint(item.getOutlinePaint()); lg.setOutlineStroke(item.getOutlineStroke()); lg.setPadding(this.legendItemGraphicPadding); LegendItemBlockContainer legendItem = new LegendItemBlockContainer( new BorderArrangement(), item.getDataset(), item.getSeriesKey()); lg.setShapeAnchor(getLegendItemGraphicAnchor()); lg.setShapeLocation(getLegendItemGraphicLocation()); legendItem.add(lg, this.legendItemGraphicEdge); Font textFont = item.getLabelFont(); if (textFont == null) { textFont = this.itemFont; } Paint textPaint = item.getLabelPaint(); if (textPaint == null) { textPaint = this.itemPaint; } LabelBlock labelBlock = new LabelBlock(item.getLabel(), textFont, textPaint); labelBlock.setPadding(this.itemLabelPadding); legendItem.add(labelBlock); legendItem.setToolTipText(item.getToolTipText()); legendItem.setURLText(item.getURLText()); result = new BlockContainer(new CenterArrangement()); result.add(legendItem); return result; } /** * Returns the container that holds the legend items. * * @return The container for the legend items. */ public BlockContainer getItemContainer() { return this.items; } /** * Arranges the contents of the block, within the given constraints, and * returns the block size. * * @param g2 the graphics device. * @param constraint the constraint ({@code null} not permitted). * * @return The block size (in Java2D units, never {@code null}). */ @Override public Size2D arrange(Graphics2D g2, RectangleConstraint constraint) { Size2D result = new Size2D(); fetchLegendItems(); if (this.items.isEmpty()) { return result; } BlockContainer container = this.wrapper; if (container == null) { container = this.items; } RectangleConstraint c = toContentConstraint(constraint); Size2D size = container.arrange(g2, c); result.height = calculateTotalHeight(size.height); result.width = calculateTotalWidth(size.width); return result; } /** * Draws the title on a Java 2D graphics device (such as the screen or a * printer). * * @param g2 the graphics device. * @param area the available area for the title. */ @Override public void draw(Graphics2D g2, Rectangle2D area) { draw(g2, area, null); } /** * Draws the block within the specified area. * * @param g2 the graphics device. * @param area the area. * @param params ignored ({@code null} permitted). * * @return An {@link org.jfree.chart.block.EntityBlockResult} or * {@code null}. */ @Override public Object draw(Graphics2D g2, Rectangle2D area, Object params) { Rectangle2D target = (Rectangle2D) area.clone(); Rectangle2D hotspot = (Rectangle2D) area.clone(); StandardEntityCollection sec = null; if (params instanceof EntityBlockParams && ((EntityBlockParams) params).getGenerateEntities()) { sec = new StandardEntityCollection(); sec.add(new TitleEntity(hotspot, this)); } target = trimMargin(target); if (this.backgroundPaint != null) { g2.setPaint(this.backgroundPaint); g2.fill(target); } BlockFrame border = getFrame(); border.draw(g2, target); border.getInsets().trim(target); BlockContainer container = this.wrapper; if (container == null) { container = this.items; } target = trimPadding(target); Object val = container.draw(g2, target, params); if (val instanceof BlockResult) { EntityCollection ec = ((BlockResult) val).getEntityCollection(); if (ec != null && sec != null) { sec.addAll(ec); ((BlockResult) val).setEntityCollection(sec); } } return val; } /** * Returns the wrapper container, if any. * * @return The wrapper container (possibly {@code null}). */ public BlockContainer getWrapper() { return this.wrapper; } /** * Sets the wrapper container for the legend. * * @param wrapper the wrapper container. */ public void setWrapper(BlockContainer wrapper) { this.wrapper = wrapper; } /** * Tests this title for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof LegendTitle)) { return false; } LegendTitle that = (LegendTitle) obj; if (!PaintUtils.equal(this.backgroundPaint, that.backgroundPaint)) { return false; } if (!PaintUtils.equal(this.itemPaint, that.itemPaint)) { return false; } if (!Arrays.deepEquals(this.sources, that.sources)) { return false; } if (!Objects.equals(this.legendItemGraphicEdge, that.legendItemGraphicEdge)) { return false; } if (!Objects.equals(this.legendItemGraphicAnchor, that.legendItemGraphicAnchor)) { return false; } if (!Objects.equals(this.legendItemGraphicLocation, that.legendItemGraphicLocation)) { return false; } if (!Objects.equals(this.legendItemGraphicPadding, that.legendItemGraphicPadding)) { return false; } if (!Objects.equals(this.itemFont, that.itemFont)) { return false; } if (!Objects.equals(this.itemLabelPadding, that.itemLabelPadding)) { return false; } if (!Objects.equals(this.items, that.items)) { return false; } if (!Objects.equals(this.hLayout, that.hLayout)) { return false; } if (!Objects.equals(this.vLayout, that.vLayout)) { return false; } if (!Objects.equals(this.wrapper, that.wrapper)) { return false; } if (!Objects.equals(this.sortOrder, that.sortOrder)) { return false; } return super.equals(obj); } @Override public int hashCode() { int hash = super.hashCode(); // equals calls superclass, hashCode must also hash = 67 * hash + Arrays.deepHashCode(this.sources); hash = 67 * hash + HashUtils.hashCodeForPaint(this.backgroundPaint); hash = 67 * hash + Objects.hashCode(this.legendItemGraphicEdge); hash = 67 * hash + Objects.hashCode(this.legendItemGraphicAnchor); hash = 67 * hash + Objects.hashCode(this.legendItemGraphicLocation); hash = 67 * hash + Objects.hashCode(this.legendItemGraphicPadding); hash = 67 * hash + Objects.hashCode(this.itemFont); hash = 67 * hash + HashUtils.hashCodeForPaint(this.itemPaint); hash = 67 * hash + Objects.hashCode(this.itemLabelPadding); hash = 67 * hash + Objects.hashCode(this.items); hash = 67 * hash + Objects.hashCode(this.hLayout); hash = 67 * hash + Objects.hashCode(this.vLayout); hash = 67 * hash + Objects.hashCode(this.wrapper); hash = 67 * hash + Objects.hashCode(this.sortOrder); return hash; } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof LegendTitle); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.backgroundPaint, stream); SerialUtils.writePaint(this.itemPaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.backgroundPaint = SerialUtils.readPaint(stream); this.itemPaint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/title/PaintScaleLegend.java000066400000000000000000000655421463604235500302200ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * PaintScaleLegend.java * --------------------- * (C) Copyright 2007-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Peter Kolb - see patch 2686872; * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.title; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Stroke; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Objects; import org.jfree.chart.HashUtils; import org.jfree.chart.axis.AxisLocation; import org.jfree.chart.axis.AxisSpace; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.block.LengthConstraintType; import org.jfree.chart.block.RectangleConstraint; import org.jfree.chart.event.AxisChangeEvent; import org.jfree.chart.event.AxisChangeListener; import org.jfree.chart.event.TitleChangeEvent; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.renderer.PaintScale; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.Size2D; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; import org.jfree.data.Range; /** * A legend that shows a range of values and their associated colors, driven * by an underlying {@link PaintScale} implementation. */ public class PaintScaleLegend extends Title implements AxisChangeListener, PublicCloneable { /** For serialization. */ static final long serialVersionUID = -1365146490993227503L; /** The paint scale (never {@code null}). */ private PaintScale scale; /** The value axis (never {@code null}). */ private ValueAxis axis; /** * The axis location (handles both orientations, never * {@code null}). */ private AxisLocation axisLocation; /** The offset between the axis and the paint strip (in Java2D units). */ private double axisOffset; /** The thickness of the paint strip (in Java2D units). */ private double stripWidth; /** * A flag that controls whether or not an outline is drawn around the * paint strip. */ private boolean stripOutlineVisible; /** The paint used to draw an outline around the paint strip. */ private transient Paint stripOutlinePaint; /** The stroke used to draw an outline around the paint strip. */ private transient Stroke stripOutlineStroke; /** The background paint (never {@code null}). */ private transient Paint backgroundPaint; /** * The number of subdivisions for the scale when rendering. */ private int subdivisions; /** * Creates a new instance. * * @param scale the scale ({@code null} not permitted). * @param axis the axis ({@code null} not permitted). */ public PaintScaleLegend(PaintScale scale, ValueAxis axis) { Args.nullNotPermitted(axis, "axis"); this.scale = scale; this.axis = axis; this.axis.addChangeListener(this); this.axisLocation = AxisLocation.BOTTOM_OR_LEFT; this.axisOffset = 0.0; this.axis.setRange(scale.getLowerBound(), scale.getUpperBound()); this.stripWidth = 15.0; this.stripOutlineVisible = true; this.stripOutlinePaint = Color.GRAY; this.stripOutlineStroke = new BasicStroke(0.5f); this.backgroundPaint = Color.WHITE; this.subdivisions = 100; } /** * Returns the scale used to convert values to colors. * * @return The scale (never {@code null}). * * @see #setScale(PaintScale) */ public PaintScale getScale() { return this.scale; } /** * Sets the scale and sends a {@link TitleChangeEvent} to all registered * listeners. * * @param scale the scale ({@code null} not permitted). * * @see #getScale() */ public void setScale(PaintScale scale) { Args.nullNotPermitted(scale, "scale"); this.scale = scale; notifyListeners(new TitleChangeEvent(this)); } /** * Returns the axis for the paint scale. * * @return The axis (never {@code null}). * * @see #setAxis(ValueAxis) */ public ValueAxis getAxis() { return this.axis; } /** * Sets the axis for the paint scale and sends a {@link TitleChangeEvent} * to all registered listeners. * * @param axis the axis ({@code null} not permitted). * * @see #getAxis() */ public void setAxis(ValueAxis axis) { Args.nullNotPermitted(axis, "axis"); this.axis.removeChangeListener(this); this.axis = axis; this.axis.addChangeListener(this); notifyListeners(new TitleChangeEvent(this)); } /** * Returns the axis location. * * @return The axis location (never {@code null}). * * @see #setAxisLocation(AxisLocation) */ public AxisLocation getAxisLocation() { return this.axisLocation; } /** * Sets the axis location and sends a {@link TitleChangeEvent} to all * registered listeners. * * @param location the location ({@code null} not permitted). * * @see #getAxisLocation() */ public void setAxisLocation(AxisLocation location) { Args.nullNotPermitted(location, "location"); this.axisLocation = location; notifyListeners(new TitleChangeEvent(this)); } /** * Returns the offset between the axis and the paint strip. * * @return The offset between the axis and the paint strip. * * @see #setAxisOffset(double) */ public double getAxisOffset() { return this.axisOffset; } /** * Sets the offset between the axis and the paint strip and sends a * {@link TitleChangeEvent} to all registered listeners. * * @param offset the offset. */ public void setAxisOffset(double offset) { this.axisOffset = offset; notifyListeners(new TitleChangeEvent(this)); } /** * Returns the width of the paint strip, in Java2D units. * * @return The width of the paint strip. * * @see #setStripWidth(double) */ public double getStripWidth() { return this.stripWidth; } /** * Sets the width of the paint strip and sends a {@link TitleChangeEvent} * to all registered listeners. * * @param width the width. * * @see #getStripWidth() */ public void setStripWidth(double width) { this.stripWidth = width; notifyListeners(new TitleChangeEvent(this)); } /** * Returns the flag that controls whether or not an outline is drawn * around the paint strip. * * @return A boolean. * * @see #setStripOutlineVisible(boolean) */ public boolean isStripOutlineVisible() { return this.stripOutlineVisible; } /** * Sets the flag that controls whether or not an outline is drawn around * the paint strip, and sends a {@link TitleChangeEvent} to all registered * listeners. * * @param visible the flag. * * @see #isStripOutlineVisible() */ public void setStripOutlineVisible(boolean visible) { this.stripOutlineVisible = visible; notifyListeners(new TitleChangeEvent(this)); } /** * Returns the paint used to draw the outline of the paint strip. * * @return The paint (never {@code null}). * * @see #setStripOutlinePaint(Paint) */ public Paint getStripOutlinePaint() { return this.stripOutlinePaint; } /** * Sets the paint used to draw the outline of the paint strip, and sends * a {@link TitleChangeEvent} to all registered listeners. * * @param paint the paint ({@code null} not permitted). * * @see #getStripOutlinePaint() */ public void setStripOutlinePaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); this.stripOutlinePaint = paint; notifyListeners(new TitleChangeEvent(this)); } /** * Returns the stroke used to draw the outline around the paint strip. * * @return The stroke (never {@code null}). * * @see #setStripOutlineStroke(Stroke) */ public Stroke getStripOutlineStroke() { return this.stripOutlineStroke; } /** * Sets the stroke used to draw the outline around the paint strip and * sends a {@link TitleChangeEvent} to all registered listeners. * * @param stroke the stroke ({@code null} not permitted). * * @see #getStripOutlineStroke() */ public void setStripOutlineStroke(Stroke stroke) { Args.nullNotPermitted(stroke, "stroke"); this.stripOutlineStroke = stroke; notifyListeners(new TitleChangeEvent(this)); } /** * Returns the background paint. * * @return The background paint. */ public Paint getBackgroundPaint() { return this.backgroundPaint; } /** * Sets the background paint and sends a {@link TitleChangeEvent} to all * registered listeners. * * @param paint the paint ({@code null} permitted). */ public void setBackgroundPaint(Paint paint) { this.backgroundPaint = paint; notifyListeners(new TitleChangeEvent(this)); } /** * Returns the number of subdivisions used to draw the scale. * * @return The subdivision count. */ public int getSubdivisionCount() { return this.subdivisions; } /** * Sets the subdivision count and sends a {@link TitleChangeEvent} to * all registered listeners. * * @param count the count. */ public void setSubdivisionCount(int count) { if (count <= 0) { throw new IllegalArgumentException("Requires 'count' > 0."); } this.subdivisions = count; notifyListeners(new TitleChangeEvent(this)); } /** * Receives notification of an axis change event and responds by firing * a title change event. * * @param event the event. */ @Override public void axisChanged(AxisChangeEvent event) { if (this.axis == event.getAxis()) { notifyListeners(new TitleChangeEvent(this)); } } /** * Arranges the contents of the block, within the given constraints, and * returns the block size. * * @param g2 the graphics device. * @param constraint the constraint ({@code null} not permitted). * * @return The block size (in Java2D units, never {@code null}). */ @Override public Size2D arrange(Graphics2D g2, RectangleConstraint constraint) { RectangleConstraint cc = toContentConstraint(constraint); LengthConstraintType w = cc.getWidthConstraintType(); LengthConstraintType h = cc.getHeightConstraintType(); Size2D contentSize = null; if (w == LengthConstraintType.NONE) { if (h == LengthConstraintType.NONE) { contentSize = new Size2D(getWidth(), getHeight()); } else if (h == LengthConstraintType.RANGE) { throw new RuntimeException("Not yet implemented."); } else if (h == LengthConstraintType.FIXED) { throw new RuntimeException("Not yet implemented."); } } else if (w == LengthConstraintType.RANGE) { if (h == LengthConstraintType.NONE) { throw new RuntimeException("Not yet implemented."); } else if (h == LengthConstraintType.RANGE) { contentSize = arrangeRR(g2, cc.getWidthRange(), cc.getHeightRange()); } else if (h == LengthConstraintType.FIXED) { throw new RuntimeException("Not yet implemented."); } } else if (w == LengthConstraintType.FIXED) { if (h == LengthConstraintType.NONE) { throw new RuntimeException("Not yet implemented."); } else if (h == LengthConstraintType.RANGE) { throw new RuntimeException("Not yet implemented."); } else if (h == LengthConstraintType.FIXED) { throw new RuntimeException("Not yet implemented."); } } assert contentSize != null; // suppress compiler warning return new Size2D(calculateTotalWidth(contentSize.getWidth()), calculateTotalHeight(contentSize.getHeight())); } /** * Returns the content size for the title. This will reflect the fact that * a text title positioned on the left or right of a chart will be rotated * 90 degrees. * * @param g2 the graphics device. * @param widthRange the width range. * @param heightRange the height range. * * @return The content size. */ protected Size2D arrangeRR(Graphics2D g2, Range widthRange, Range heightRange) { RectangleEdge position = getPosition(); if (position == RectangleEdge.TOP || position == RectangleEdge.BOTTOM) { float maxWidth = (float) widthRange.getUpperBound(); // determine the space required for the axis AxisSpace space = this.axis.reserveSpace(g2, null, new Rectangle2D.Double(0, 0, maxWidth, 100), RectangleEdge.BOTTOM, null); return new Size2D(maxWidth, this.stripWidth + this.axisOffset + space.getTop() + space.getBottom()); } else if (position == RectangleEdge.LEFT || position == RectangleEdge.RIGHT) { float maxHeight = (float) heightRange.getUpperBound(); AxisSpace space = this.axis.reserveSpace(g2, null, new Rectangle2D.Double(0, 0, 100, maxHeight), RectangleEdge.RIGHT, null); return new Size2D(this.stripWidth + this.axisOffset + space.getLeft() + space.getRight(), maxHeight); } else { throw new RuntimeException("Unrecognised position."); } } /** * Draws the legend within the specified area. * * @param g2 the graphics target ({@code null} not permitted). * @param area the drawing area ({@code null} not permitted). */ @Override public void draw(Graphics2D g2, Rectangle2D area) { draw(g2, area, null); } /** * Draws the legend within the specified area. * * @param g2 the graphics target ({@code null} not permitted). * @param area the drawing area ({@code null} not permitted). * @param params drawing parameters (ignored here). * * @return {@code null}. */ @Override public Object draw(Graphics2D g2, Rectangle2D area, Object params) { Rectangle2D target = (Rectangle2D) area.clone(); target = trimMargin(target); if (this.backgroundPaint != null) { g2.setPaint(this.backgroundPaint); g2.fill(target); } getFrame().draw(g2, target); getFrame().getInsets().trim(target); target = trimPadding(target); double base = this.axis.getLowerBound(); double increment = this.axis.getRange().getLength() / this.subdivisions; Rectangle2D r = new Rectangle2D.Double(); if (RectangleEdge.isTopOrBottom(getPosition())) { RectangleEdge axisEdge = Plot.resolveRangeAxisLocation( this.axisLocation, PlotOrientation.HORIZONTAL); if (axisEdge == RectangleEdge.TOP) { for (int i = 0; i < this.subdivisions; i++) { double v = base + (i * increment); Paint p = this.scale.getPaint(v); double vv0 = this.axis.valueToJava2D(v, target, RectangleEdge.TOP); double vv1 = this.axis.valueToJava2D(v + increment, target, RectangleEdge.TOP); double ww = Math.abs(vv1 - vv0) + 1.0; r.setRect(Math.min(vv0, vv1), target.getMaxY() - this.stripWidth, ww, this.stripWidth); g2.setPaint(p); g2.fill(r); } if (isStripOutlineVisible()) { g2.setPaint(this.stripOutlinePaint); g2.setStroke(this.stripOutlineStroke); g2.draw(new Rectangle2D.Double(target.getMinX(), target.getMaxY() - this.stripWidth, target.getWidth(), this.stripWidth)); } this.axis.draw(g2, target.getMaxY() - this.stripWidth - this.axisOffset, target, target, RectangleEdge.TOP, null); } else if (axisEdge == RectangleEdge.BOTTOM) { for (int i = 0; i < this.subdivisions; i++) { double v = base + (i * increment); Paint p = this.scale.getPaint(v); double vv0 = this.axis.valueToJava2D(v, target, RectangleEdge.BOTTOM); double vv1 = this.axis.valueToJava2D(v + increment, target, RectangleEdge.BOTTOM); double ww = Math.abs(vv1 - vv0) + 1.0; r.setRect(Math.min(vv0, vv1), target.getMinY(), ww, this.stripWidth); g2.setPaint(p); g2.fill(r); } if (isStripOutlineVisible()) { g2.setPaint(this.stripOutlinePaint); g2.setStroke(this.stripOutlineStroke); g2.draw(new Rectangle2D.Double(target.getMinX(), target.getMinY(), target.getWidth(), this.stripWidth)); } this.axis.draw(g2, target.getMinY() + this.stripWidth + this.axisOffset, target, target, RectangleEdge.BOTTOM, null); } } else { RectangleEdge axisEdge = Plot.resolveRangeAxisLocation( this.axisLocation, PlotOrientation.VERTICAL); if (axisEdge == RectangleEdge.LEFT) { for (int i = 0; i < this.subdivisions; i++) { double v = base + (i * increment); Paint p = this.scale.getPaint(v); double vv0 = this.axis.valueToJava2D(v, target, RectangleEdge.LEFT); double vv1 = this.axis.valueToJava2D(v + increment, target, RectangleEdge.LEFT); double hh = Math.abs(vv1 - vv0) + 1.0; r.setRect(target.getMaxX() - this.stripWidth, Math.min(vv0, vv1), this.stripWidth, hh); g2.setPaint(p); g2.fill(r); } if (isStripOutlineVisible()) { g2.setPaint(this.stripOutlinePaint); g2.setStroke(this.stripOutlineStroke); g2.draw(new Rectangle2D.Double(target.getMaxX() - this.stripWidth, target.getMinY(), this.stripWidth, target.getHeight())); } this.axis.draw(g2, target.getMaxX() - this.stripWidth - this.axisOffset, target, target, RectangleEdge.LEFT, null); } else if (axisEdge == RectangleEdge.RIGHT) { for (int i = 0; i < this.subdivisions; i++) { double v = base + (i * increment); Paint p = this.scale.getPaint(v); double vv0 = this.axis.valueToJava2D(v, target, RectangleEdge.LEFT); double vv1 = this.axis.valueToJava2D(v + increment, target, RectangleEdge.LEFT); double hh = Math.abs(vv1 - vv0) + 1.0; r.setRect(target.getMinX(), Math.min(vv0, vv1), this.stripWidth, hh); g2.setPaint(p); g2.fill(r); } if (isStripOutlineVisible()) { g2.setPaint(this.stripOutlinePaint); g2.setStroke(this.stripOutlineStroke); g2.draw(new Rectangle2D.Double(target.getMinX(), target.getMinY(), this.stripWidth, target.getHeight())); } this.axis.draw(g2, target.getMinX() + this.stripWidth + this.axisOffset, target, target, RectangleEdge.RIGHT, null); } } return null; } /** * Tests this legend for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof PaintScaleLegend)) { return false; } PaintScaleLegend that = (PaintScaleLegend) obj; if (!Objects.equals(this.scale, that.scale)) { return false; } if (!Objects.equals(this.axis, that.axis)) { return false; } if (!Objects.equals(this.axisLocation, that.axisLocation)) { return false; } if (Double.doubleToLongBits(this.axisOffset) != Double.doubleToLongBits(that.axisOffset)) { return false; } if (Double.doubleToLongBits(this.stripWidth) != Double.doubleToLongBits(that.stripWidth)) { return false; } if (this.stripOutlineVisible != that.stripOutlineVisible) { return false; } if (!PaintUtils.equal(this.stripOutlinePaint, that.stripOutlinePaint)) { return false; } if (!Objects.equals(this.stripOutlineStroke, that.stripOutlineStroke)) { return false; } if (!PaintUtils.equal(this.backgroundPaint, that.backgroundPaint)) { return false; } if (this.subdivisions != that.subdivisions) { return false; } if (!that.canEqual(this)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof PaintScaleLegend); } @Override public int hashCode() { int hash = super.hashCode(); // equals calls superclass, hashCode must also hash = 53 * hash + Objects.hashCode(this.scale); hash = 53 * hash + Objects.hashCode(this.axis); hash = 53 * hash + Objects.hashCode(this.axisLocation); hash = 53 * hash + (int) (Double.doubleToLongBits(this.axisOffset) ^ (Double.doubleToLongBits(this.axisOffset) >>> 32)); hash = 53 * hash + (int) (Double.doubleToLongBits(this.stripWidth) ^ (Double.doubleToLongBits(this.stripWidth) >>> 32)); hash = 53 * hash + (this.stripOutlineVisible ? 1 : 0); hash = 53 * hash + HashUtils.hashCodeForPaint(this.stripOutlinePaint); hash = 53 * hash + Objects.hashCode(this.stripOutlineStroke); hash = 53 * hash + HashUtils.hashCodeForPaint(this.backgroundPaint); hash = 53 * hash + this.subdivisions; return hash; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.backgroundPaint, stream); SerialUtils.writePaint(this.stripOutlinePaint, stream); SerialUtils.writeStroke(this.stripOutlineStroke, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.backgroundPaint = SerialUtils.readPaint(stream); this.stripOutlinePaint = SerialUtils.readPaint(stream); this.stripOutlineStroke = SerialUtils.readStroke(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/title/ShortTextTitle.java000066400000000000000000000202151463604235500300100ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * ShortTextTitle.java * ------------------- * (C) Copyright 2008-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.title; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import org.jfree.chart.block.LengthConstraintType; import org.jfree.chart.block.RectangleConstraint; import org.jfree.chart.text.TextUtils; import org.jfree.chart.ui.Size2D; import org.jfree.chart.ui.TextAnchor; import org.jfree.data.Range; /** * A text title that is only displayed if the entire text will be visible * without line wrapping. It is only intended for use with short titles - for * general purpose titles, you should use the {@link TextTitle} class. * * @see TextTitle */ public class ShortTextTitle extends TextTitle { /** * Creates a new title. * * @param text the text ({@code null} not permitted). */ public ShortTextTitle(String text) { setText(text); } /** * Performs a layout for this title, subject to the supplied constraint, * and returns the dimensions required for the title (if the title * cannot be displayed in the available space, this method will return * zero width and height for the dimensions). * * @param g2 the graphics target. * @param constraint the layout constraints. * * @return The dimensions for the title. */ @Override public Size2D arrange(Graphics2D g2, RectangleConstraint constraint) { RectangleConstraint cc = toContentConstraint(constraint); LengthConstraintType w = cc.getWidthConstraintType(); LengthConstraintType h = cc.getHeightConstraintType(); Size2D contentSize = null; if (w == LengthConstraintType.NONE) { if (h == LengthConstraintType.NONE) { contentSize = arrangeNN(g2); } else if (h == LengthConstraintType.RANGE) { throw new RuntimeException("Not yet implemented."); } else if (h == LengthConstraintType.FIXED) { throw new RuntimeException("Not yet implemented."); } } else if (w == LengthConstraintType.RANGE) { if (h == LengthConstraintType.NONE) { contentSize = arrangeRN(g2, cc.getWidthRange()); } else if (h == LengthConstraintType.RANGE) { contentSize = arrangeRR(g2, cc.getWidthRange(), cc.getHeightRange()); } else if (h == LengthConstraintType.FIXED) { throw new RuntimeException("Not yet implemented."); } } else if (w == LengthConstraintType.FIXED) { if (h == LengthConstraintType.NONE) { contentSize = arrangeFN(g2, cc.getWidth()); } else if (h == LengthConstraintType.RANGE) { throw new RuntimeException("Not yet implemented."); } else if (h == LengthConstraintType.FIXED) { throw new RuntimeException("Not yet implemented."); } } assert contentSize != null; if (contentSize.width <= 0.0 || contentSize.height <= 0.0) { return new Size2D(0.0, 0.0); } else { return new Size2D(calculateTotalWidth(contentSize.getWidth()), calculateTotalHeight(contentSize.getHeight())); } } /** * Arranges the content for this title assuming no bounds on the width * or the height, and returns the required size. * * @param g2 the graphics target. * * @return The content size. */ @Override protected Size2D arrangeNN(Graphics2D g2) { Range max = new Range(0.0, Float.MAX_VALUE); return arrangeRR(g2, max, max); } /** * Arranges the content for this title assuming a range constraint for the * width and no bounds on the height, and returns the required size. * * @param g2 the graphics target. * @param widthRange the range for the width. * * @return The content size. */ @Override protected Size2D arrangeRN(Graphics2D g2, Range widthRange) { Size2D s = arrangeNN(g2); if (widthRange.contains(s.getWidth())) { return s; } double ww = widthRange.constrain(s.getWidth()); return arrangeFN(g2, ww); } /** * Arranges the content for this title assuming a fixed width and no bounds * on the height, and returns the required size. This will reflect the * fact that a text title positioned on the left or right of a chart will * be rotated by 90 degrees. * * @param g2 the graphics target. * @param w the width. * * @return The content size. */ @Override protected Size2D arrangeFN(Graphics2D g2, double w) { g2.setFont(getFont()); FontMetrics fm = g2.getFontMetrics(getFont()); Rectangle2D bounds = TextUtils.getTextBounds(getText(), g2, fm); if (bounds.getWidth() <= w) { return new Size2D(w, bounds.getHeight()); } else { return new Size2D(0.0, 0.0); } } /** * Returns the content size for the title. * * @param g2 the graphics device. * @param widthRange the width range. * @param heightRange the height range. * * @return The content size. */ @Override protected Size2D arrangeRR(Graphics2D g2, Range widthRange, Range heightRange) { g2.setFont(getFont()); FontMetrics fm = g2.getFontMetrics(getFont()); Rectangle2D bounds = TextUtils.getTextBounds(getText(), g2, fm); if (bounds.getWidth() <= widthRange.getUpperBound() && bounds.getHeight() <= heightRange.getUpperBound()) { return new Size2D(bounds.getWidth(), bounds.getHeight()); } else { return new Size2D(0.0, 0.0); } } /** * Draws the title using the current font and paint. * * @param g2 the graphics target. * @param area the title area. * @param params optional parameters (ignored here). * * @return {@code null}. */ @Override public Object draw(Graphics2D g2, Rectangle2D area, Object params) { if (area.isEmpty()) { return null; } area = trimMargin(area); drawBorder(g2, area); area = trimBorder(area); area = trimPadding(area); g2.setFont(getFont()); g2.setPaint(getPaint()); TextUtils.drawAlignedString(getText(), g2, (float) area.getMinX(), (float) area.getMinY(), TextAnchor.TOP_LEFT); return null; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/title/TextTitle.java000066400000000000000000000753161463604235500270040ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * TextTitle.java * -------------- * (C) Copyright 2000-present, by David Berry and Contributors. * * Original Author: David Berry; * Contributor(s): David Gilbert; * Nicolas Brodu; * Peter Kolb - patch 2603321; * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.title; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.HashUtils; import org.jfree.chart.block.BlockResult; import org.jfree.chart.block.EntityBlockParams; import org.jfree.chart.block.LengthConstraintType; import org.jfree.chart.block.RectangleConstraint; import org.jfree.chart.entity.ChartEntity; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.entity.StandardEntityCollection; import org.jfree.chart.entity.TitleEntity; import org.jfree.chart.event.TitleChangeEvent; import org.jfree.chart.text.G2TextMeasurer; import org.jfree.chart.text.TextBlock; import org.jfree.chart.text.TextBlockAnchor; import org.jfree.chart.text.TextUtils; import org.jfree.chart.ui.HorizontalAlignment; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.ui.Size2D; import org.jfree.chart.ui.VerticalAlignment; import org.jfree.chart.util.PaintUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SerialUtils; import org.jfree.data.Range; /** * A chart title that displays a text string with automatic wrapping as * required. */ public class TextTitle extends Title implements Serializable, Cloneable, PublicCloneable { /** For serialization. */ private static final long serialVersionUID = 8372008692127477443L; /** The default font. */ public static final Font DEFAULT_FONT = new Font("SansSerif", Font.BOLD, 12); /** The default text color. */ public static final Paint DEFAULT_TEXT_PAINT = Color.BLACK; /** The title text. */ private String text; /** The font used to display the title. */ private Font font; /** The text alignment. */ private HorizontalAlignment textAlignment; /** The paint used to display the title text. */ private transient Paint paint; /** The background paint. */ private transient Paint backgroundPaint; /** The tool tip text (can be {@code null}). */ private String toolTipText; /** The URL text (can be {@code null}). */ private String urlText; /** The content. */ private TextBlock content; /** * A flag that controls whether the title expands to fit the available * space.. */ private boolean expandToFitSpace = false; /** * The maximum number of lines to display. */ private int maximumLinesToDisplay = Integer.MAX_VALUE; /** * Creates a new title, using default attributes where necessary. */ public TextTitle() { this(""); } /** * Creates a new title, using default attributes where necessary. * * @param text the title text ({@code null} not permitted). */ public TextTitle(String text) { this(text, TextTitle.DEFAULT_FONT, TextTitle.DEFAULT_TEXT_PAINT, Title.DEFAULT_POSITION, Title.DEFAULT_HORIZONTAL_ALIGNMENT, Title.DEFAULT_VERTICAL_ALIGNMENT, Title.DEFAULT_PADDING); } /** * Creates a new title, using default attributes where necessary. * * @param text the title text ({@code null} not permitted). * @param font the title font ({@code null} not permitted). */ public TextTitle(String text, Font font) { this(text, font, TextTitle.DEFAULT_TEXT_PAINT, Title.DEFAULT_POSITION, Title.DEFAULT_HORIZONTAL_ALIGNMENT, Title.DEFAULT_VERTICAL_ALIGNMENT, Title.DEFAULT_PADDING); } /** * Creates a new title. * * @param text the text for the title ({@code null} not permitted). * @param font the title font ({@code null} not permitted). * @param paint the title paint ({@code null} not permitted). * @param position the title position ({@code null} not permitted). * @param horizontalAlignment the horizontal alignment ({@code null} * not permitted). * @param verticalAlignment the vertical alignment ({@code null} not * permitted). * @param padding the space to leave around the outside of the title. */ public TextTitle(String text, Font font, Paint paint, RectangleEdge position, HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment, RectangleInsets padding) { super(position, horizontalAlignment, verticalAlignment, padding); if (text == null) { throw new NullPointerException("Null 'text' argument."); } if (font == null) { throw new NullPointerException("Null 'font' argument."); } if (paint == null) { throw new NullPointerException("Null 'paint' argument."); } this.text = text; this.font = font; this.paint = paint; // the textAlignment and the horizontalAlignment are separate things, // but it makes sense for the default textAlignment to match the // title's horizontal alignment... this.textAlignment = horizontalAlignment; this.backgroundPaint = null; this.content = null; this.toolTipText = null; this.urlText = null; } /** * Returns the title text. * * @return The text (never {@code null}). * * @see #setText(String) */ public String getText() { return this.text; } /** * Sets the title to the specified text and sends a * {@link TitleChangeEvent} to all registered listeners. * * @param text the text ({@code null} not permitted). */ public void setText(String text) { Args.nullNotPermitted(text, "text"); if (!this.text.equals(text)) { this.text = text; notifyListeners(new TitleChangeEvent(this)); } } /** * Returns the text alignment. This controls how the text is aligned * within the title's bounds, whereas the title's horizontal alignment * controls how the title's bounding rectangle is aligned within the * drawing space. * * @return The text alignment. */ public HorizontalAlignment getTextAlignment() { return this.textAlignment; } /** * Sets the text alignment and sends a {@link TitleChangeEvent} to * all registered listeners. * * @param alignment the alignment ({@code null} not permitted). */ public void setTextAlignment(HorizontalAlignment alignment) { Args.nullNotPermitted(alignment, "alignment"); this.textAlignment = alignment; notifyListeners(new TitleChangeEvent(this)); } /** * Returns the font used to display the title string. * * @return The font (never {@code null}). * * @see #setFont(Font) */ public Font getFont() { return this.font; } /** * Sets the font used to display the title string. Registered listeners * are notified that the title has been modified. * * @param font the new font ({@code null} not permitted). * * @see #getFont() */ public void setFont(Font font) { Args.nullNotPermitted(font, "font"); if (!this.font.equals(font)) { this.font = font; notifyListeners(new TitleChangeEvent(this)); } } /** * Returns the paint used to display the title string. * * @return The paint (never {@code null}). * * @see #setPaint(Paint) */ public Paint getPaint() { return this.paint; } /** * Sets the paint used to display the title string. Registered listeners * are notified that the title has been modified. * * @param paint the new paint ({@code null} not permitted). * * @see #getPaint() */ public void setPaint(Paint paint) { Args.nullNotPermitted(paint, "paint"); if (!this.paint.equals(paint)) { this.paint = paint; notifyListeners(new TitleChangeEvent(this)); } } /** * Returns the background paint. * * @return The paint (possibly {@code null}). */ public Paint getBackgroundPaint() { return this.backgroundPaint; } /** * Sets the background paint and sends a {@link TitleChangeEvent} to all * registered listeners. If you set this attribute to {@code null}, * no background is painted (which makes the title background transparent). * * @param paint the background paint ({@code null} permitted). */ public void setBackgroundPaint(Paint paint) { this.backgroundPaint = paint; notifyListeners(new TitleChangeEvent(this)); } /** * Returns the tool tip text. * * @return The tool tip text (possibly {@code null}). */ public String getToolTipText() { return this.toolTipText; } /** * Sets the tool tip text to the specified text and sends a * {@link TitleChangeEvent} to all registered listeners. * * @param text the text ({@code null} permitted). */ public void setToolTipText(String text) { this.toolTipText = text; notifyListeners(new TitleChangeEvent(this)); } /** * Returns the URL text. * * @return The URL text (possibly {@code null}). */ public String getURLText() { return this.urlText; } /** * Sets the URL text to the specified text and sends a * {@link TitleChangeEvent} to all registered listeners. * * @param text the text ({@code null} permitted). */ public void setURLText(String text) { this.urlText = text; notifyListeners(new TitleChangeEvent(this)); } /** * Returns the flag that controls whether or not the title expands to fit * the available space. * * @return The flag. */ public boolean getExpandToFitSpace() { return this.expandToFitSpace; } /** * Sets the flag that controls whether the title expands to fit the * available space, and sends a {@link TitleChangeEvent} to all registered * listeners. * * @param expand the flag. */ public void setExpandToFitSpace(boolean expand) { this.expandToFitSpace = expand; notifyListeners(new TitleChangeEvent(this)); } /** * Returns the maximum number of lines to display. * * @return The maximum. * * @see #setMaximumLinesToDisplay(int) */ public int getMaximumLinesToDisplay() { return this.maximumLinesToDisplay; } /** * Sets the maximum number of lines to display and sends a * {@link TitleChangeEvent} to all registered listeners. * * @param max the maximum. * * @see #getMaximumLinesToDisplay() */ public void setMaximumLinesToDisplay(int max) { this.maximumLinesToDisplay = max; notifyListeners(new TitleChangeEvent(this)); } /** * Arranges the contents of the block, within the given constraints, and * returns the block size. * * @param g2 the graphics device. * @param constraint the constraint ({@code null} not permitted). * * @return The block size (in Java2D units, never {@code null}). */ @Override public Size2D arrange(Graphics2D g2, RectangleConstraint constraint) { RectangleConstraint cc = toContentConstraint(constraint); LengthConstraintType w = cc.getWidthConstraintType(); LengthConstraintType h = cc.getHeightConstraintType(); Size2D contentSize = null; if (w == LengthConstraintType.NONE) { if (h == LengthConstraintType.NONE) { contentSize = arrangeNN(g2); } else if (h == LengthConstraintType.RANGE) { throw new RuntimeException("Not yet implemented."); } else if (h == LengthConstraintType.FIXED) { throw new RuntimeException("Not yet implemented."); } } else if (w == LengthConstraintType.RANGE) { if (h == LengthConstraintType.NONE) { contentSize = arrangeRN(g2, cc.getWidthRange()); } else if (h == LengthConstraintType.RANGE) { contentSize = arrangeRR(g2, cc.getWidthRange(), cc.getHeightRange()); } else if (h == LengthConstraintType.FIXED) { throw new RuntimeException("Not yet implemented."); } } else if (w == LengthConstraintType.FIXED) { if (h == LengthConstraintType.NONE) { contentSize = arrangeFN(g2, cc.getWidth()); } else if (h == LengthConstraintType.RANGE) { throw new RuntimeException("Not yet implemented."); } else if (h == LengthConstraintType.FIXED) { throw new RuntimeException("Not yet implemented."); } } assert contentSize != null; // suppress compiler warning return new Size2D(calculateTotalWidth(contentSize.getWidth()), calculateTotalHeight(contentSize.getHeight())); } /** * Arranges the content for this title assuming no bounds on the width * or the height, and returns the required size. This will reflect the * fact that a text title positioned on the left or right of a chart will * be rotated by 90 degrees. * * @param g2 the graphics target. * * @return The content size. */ protected Size2D arrangeNN(Graphics2D g2) { Range max = new Range(0.0, Float.MAX_VALUE); return arrangeRR(g2, max, max); } /** * Arranges the content for this title assuming a fixed width and no bounds * on the height, and returns the required size. This will reflect the * fact that a text title positioned on the left or right of a chart will * be rotated by 90 degrees. * * @param g2 the graphics target. * @param w the width. * * @return The content size. */ protected Size2D arrangeFN(Graphics2D g2, double w) { RectangleEdge position = getPosition(); if (position == RectangleEdge.TOP || position == RectangleEdge.BOTTOM) { float maxWidth = (float) w; g2.setFont(this.font); this.content = TextUtils.createTextBlock(this.text, this.font, this.paint, maxWidth, this.maximumLinesToDisplay, new G2TextMeasurer(g2)); this.content.setLineAlignment(this.textAlignment); Size2D contentSize = this.content.calculateDimensions(g2); if (this.expandToFitSpace) { return new Size2D(maxWidth, contentSize.getHeight()); } else { return contentSize; } } else if (position == RectangleEdge.LEFT || position == RectangleEdge.RIGHT) { float maxWidth = Float.MAX_VALUE; g2.setFont(this.font); this.content = TextUtils.createTextBlock(this.text, this.font, this.paint, maxWidth, this.maximumLinesToDisplay, new G2TextMeasurer(g2)); this.content.setLineAlignment(this.textAlignment); Size2D contentSize = this.content.calculateDimensions(g2); // transpose the dimensions, because the title is rotated if (this.expandToFitSpace) { return new Size2D(contentSize.getHeight(), maxWidth); } else { return new Size2D(contentSize.height, contentSize.width); } } else { throw new RuntimeException("Unrecognised exception."); } } /** * Arranges the content for this title assuming a range constraint for the * width and no bounds on the height, and returns the required size. This * will reflect the fact that a text title positioned on the left or right * of a chart will be rotated by 90 degrees. * * @param g2 the graphics target. * @param widthRange the range for the width. * * @return The content size. */ protected Size2D arrangeRN(Graphics2D g2, Range widthRange) { Size2D s = arrangeNN(g2); if (widthRange.contains(s.getWidth())) { return s; } double ww = widthRange.constrain(s.getWidth()); return arrangeFN(g2, ww); } /** * Returns the content size for the title. This will reflect the fact that * a text title positioned on the left or right of a chart will be rotated * 90 degrees. * * @param g2 the graphics device. * @param widthRange the width range. * @param heightRange the height range. * * @return The content size. */ protected Size2D arrangeRR(Graphics2D g2, Range widthRange, Range heightRange) { RectangleEdge position = getPosition(); if (position == RectangleEdge.TOP || position == RectangleEdge.BOTTOM) { float maxWidth = (float) widthRange.getUpperBound(); g2.setFont(this.font); this.content = TextUtils.createTextBlock(this.text, this.font, this.paint, maxWidth, this.maximumLinesToDisplay, new G2TextMeasurer(g2)); this.content.setLineAlignment(this.textAlignment); Size2D contentSize = this.content.calculateDimensions(g2); if (this.expandToFitSpace) { return new Size2D(maxWidth, contentSize.getHeight()); } else { return contentSize; } } else if (position == RectangleEdge.LEFT || position == RectangleEdge.RIGHT) { float maxWidth = (float) heightRange.getUpperBound(); g2.setFont(this.font); this.content = TextUtils.createTextBlock(this.text, this.font, this.paint, maxWidth, this.maximumLinesToDisplay, new G2TextMeasurer(g2)); this.content.setLineAlignment(this.textAlignment); Size2D contentSize = this.content.calculateDimensions(g2); // transpose the dimensions, because the title is rotated if (this.expandToFitSpace) { return new Size2D(contentSize.getHeight(), maxWidth); } else { return new Size2D(contentSize.height, contentSize.width); } } else { throw new RuntimeException("Unrecognised exception."); } } /** * Draws the title on a Java 2D graphics device (such as the screen or a * printer). * * @param g2 the graphics device. * @param area the area allocated for the title. */ @Override public void draw(Graphics2D g2, Rectangle2D area) { draw(g2, area, null); } /** * Draws the block within the specified area. * * @param g2 the graphics device. * @param area the area. * @param params if this is an instance of {@link EntityBlockParams} it * is used to determine whether or not an * {@link EntityCollection} is returned by this method. * * @return An {@link EntityCollection} containing a chart entity for the * title, or {@code null}. */ @Override public Object draw(Graphics2D g2, Rectangle2D area, Object params) { if (this.content == null) { return null; } area = trimMargin(area); drawBorder(g2, area); if (this.text.equals("")) { return null; } ChartEntity entity = null; if (params instanceof EntityBlockParams) { EntityBlockParams p = (EntityBlockParams) params; if (p.getGenerateEntities()) { entity = new TitleEntity(area, this, this.toolTipText, this.urlText); } } area = trimBorder(area); if (this.backgroundPaint != null) { g2.setPaint(this.backgroundPaint); g2.fill(area); } area = trimPadding(area); RectangleEdge position = getPosition(); if (position == RectangleEdge.TOP || position == RectangleEdge.BOTTOM) { drawHorizontal(g2, area); } else if (position == RectangleEdge.LEFT || position == RectangleEdge.RIGHT) { drawVertical(g2, area); } BlockResult result = new BlockResult(); if (entity != null) { StandardEntityCollection sec = new StandardEntityCollection(); sec.add(entity); result.setEntityCollection(sec); } return result; } /** * Draws a the title horizontally within the specified area. This method * will be called from the {@link #draw(Graphics2D, Rectangle2D) draw} * method. * * @param g2 the graphics device. * @param area the area for the title. */ protected void drawHorizontal(Graphics2D g2, Rectangle2D area) { Rectangle2D titleArea = (Rectangle2D) area.clone(); g2.setFont(this.font); g2.setPaint(this.paint); TextBlockAnchor anchor = null; float x = 0.0f; HorizontalAlignment horizontalAlignment = getHorizontalAlignment(); if (horizontalAlignment == HorizontalAlignment.LEFT) { x = (float) titleArea.getX(); anchor = TextBlockAnchor.TOP_LEFT; } else if (horizontalAlignment == HorizontalAlignment.RIGHT) { x = (float) titleArea.getMaxX(); anchor = TextBlockAnchor.TOP_RIGHT; } else if (horizontalAlignment == HorizontalAlignment.CENTER) { x = (float) titleArea.getCenterX(); anchor = TextBlockAnchor.TOP_CENTER; } float y = 0.0f; RectangleEdge position = getPosition(); if (position == RectangleEdge.TOP) { y = (float) titleArea.getY(); } else if (position == RectangleEdge.BOTTOM) { y = (float) titleArea.getMaxY(); if (horizontalAlignment == HorizontalAlignment.LEFT) { anchor = TextBlockAnchor.BOTTOM_LEFT; } else if (horizontalAlignment == HorizontalAlignment.CENTER) { anchor = TextBlockAnchor.BOTTOM_CENTER; } else if (horizontalAlignment == HorizontalAlignment.RIGHT) { anchor = TextBlockAnchor.BOTTOM_RIGHT; } } this.content.draw(g2, x, y, anchor); } /** * Draws a the title vertically within the specified area. This method * will be called from the {@link #draw(Graphics2D, Rectangle2D) draw} * method. * * @param g2 the graphics device. * @param area the area for the title. */ protected void drawVertical(Graphics2D g2, Rectangle2D area) { Rectangle2D titleArea = (Rectangle2D) area.clone(); g2.setFont(this.font); g2.setPaint(this.paint); TextBlockAnchor anchor = null; float y = 0.0f; VerticalAlignment verticalAlignment = getVerticalAlignment(); if (verticalAlignment == VerticalAlignment.TOP) { y = (float) titleArea.getY(); anchor = TextBlockAnchor.TOP_RIGHT; } else if (verticalAlignment == VerticalAlignment.BOTTOM) { y = (float) titleArea.getMaxY(); anchor = TextBlockAnchor.TOP_LEFT; } else if (verticalAlignment == VerticalAlignment.CENTER) { y = (float) titleArea.getCenterY(); anchor = TextBlockAnchor.TOP_CENTER; } float x = 0.0f; RectangleEdge position = getPosition(); if (position == RectangleEdge.LEFT) { x = (float) titleArea.getX(); } else if (position == RectangleEdge.RIGHT) { x = (float) titleArea.getMaxX(); if (verticalAlignment == VerticalAlignment.TOP) { anchor = TextBlockAnchor.BOTTOM_RIGHT; } else if (verticalAlignment == VerticalAlignment.CENTER) { anchor = TextBlockAnchor.BOTTOM_CENTER; } else if (verticalAlignment == VerticalAlignment.BOTTOM) { anchor = TextBlockAnchor.BOTTOM_LEFT; } } this.content.draw(g2, x, y, anchor, x, y, -Math.PI / 2.0); } /** * Tests this title for equality with another object. * * @param obj the object ({@code null} permitted). * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof TextTitle)) { return false; } TextTitle that = (TextTitle) obj; if (!Objects.equals(this.text, that.text)) { return false; } if (!Objects.equals(this.font, that.font)) { return false; } if (!PaintUtils.equal(this.paint, that.paint)) { return false; } if (!Objects.equals(this.textAlignment, that.textAlignment)) { return false; } if (!PaintUtils.equal(this.backgroundPaint, that.backgroundPaint)) { return false; } if (this.maximumLinesToDisplay != that.maximumLinesToDisplay) { return false; } if (this.expandToFitSpace != that.expandToFitSpace) { return false; } if (!Objects.equals(this.toolTipText, that.toolTipText)) { return false; } if (!Objects.equals(this.urlText, that.urlText)) { return false; } if (!Objects.equals(this.content, that.content)) { return false; } if (!that.canEqual(this)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof TextTitle); } /** * Returns a hash code. * * @return A hash code. */ @Override public int hashCode() { int hash = super.hashCode(); // equals calls superclass, hashCode must also hash = 83 * hash + Objects.hashCode(this.text); hash = 83 * hash + Objects.hashCode(this.font); hash = 83 * hash + Objects.hashCode(this.textAlignment); hash = 83 * hash + HashUtils.hashCodeForPaint(this.paint); hash = 83 * hash + HashUtils.hashCodeForPaint(this.backgroundPaint); hash = 83 * hash + Objects.hashCode(this.toolTipText); hash = 83 * hash + Objects.hashCode(this.urlText); hash = 83 * hash + Objects.hashCode(this.content); hash = 83 * hash + (this.expandToFitSpace ? 1 : 0); hash = 83 * hash + this.maximumLinesToDisplay; return hash; } /** * Returns a clone of this object. * * @return A clone. * * @throws CloneNotSupportedException never. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); SerialUtils.writePaint(this.paint, stream); SerialUtils.writePaint(this.backgroundPaint, stream); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.paint = SerialUtils.readPaint(stream); this.backgroundPaint = SerialUtils.readPaint(stream); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/title/Title.java000066400000000000000000000356711463604235500261370ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------- * Title.java * ---------- * (C) Copyright 2000-present, by David Berry and Contributors. * * Original Author: David Berry; * Contributor(s): David Gilbert; * Nicolas Brodu; * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.chart.title; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Objects; import javax.swing.event.EventListenerList; import org.jfree.chart.block.AbstractBlock; import org.jfree.chart.block.Block; import org.jfree.chart.event.TitleChangeEvent; import org.jfree.chart.event.TitleChangeListener; import org.jfree.chart.ui.HorizontalAlignment; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.ui.VerticalAlignment; import org.jfree.chart.util.Args; /** * The base class for all chart titles. A chart can have multiple titles, * appearing at the top, bottom, left or right of the chart. *

* Concrete implementations of this class will render text and images, and * hence do the actual work of drawing titles. */ public abstract class Title extends AbstractBlock implements Block, Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -6675162505277817221L; /** The default title position. */ public static final RectangleEdge DEFAULT_POSITION = RectangleEdge.TOP; /** The default horizontal alignment. */ public static final HorizontalAlignment DEFAULT_HORIZONTAL_ALIGNMENT = HorizontalAlignment.CENTER; /** The default vertical alignment. */ public static final VerticalAlignment DEFAULT_VERTICAL_ALIGNMENT = VerticalAlignment.CENTER; /** Default title padding. */ public static final RectangleInsets DEFAULT_PADDING = new RectangleInsets( 1, 1, 1, 1); /** * A flag that controls whether or not the title is visible. */ public boolean visible; /** The title position. */ private RectangleEdge position; /** The horizontal alignment of the title content. */ private HorizontalAlignment horizontalAlignment; /** The vertical alignment of the title content. */ private VerticalAlignment verticalAlignment; /** Storage for registered change listeners. */ private transient EventListenerList listenerList; /** * A flag that can be used to temporarily disable the listener mechanism. */ private boolean notify; /** * Creates a new title, using default attributes where necessary. */ protected Title() { this(Title.DEFAULT_POSITION, Title.DEFAULT_HORIZONTAL_ALIGNMENT, Title.DEFAULT_VERTICAL_ALIGNMENT, Title.DEFAULT_PADDING); } /** * Creates a new title, using default attributes where necessary. * * @param position the position of the title ({@code null} not * permitted). * @param horizontalAlignment the horizontal alignment of the title * ({@code null} not permitted). * @param verticalAlignment the vertical alignment of the title * ({@code null} not permitted). */ protected Title(RectangleEdge position, HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment) { this(position, horizontalAlignment, verticalAlignment, Title.DEFAULT_PADDING); } /** * Creates a new title. * * @param position the position of the title ({@code null} not * permitted). * @param horizontalAlignment the horizontal alignment of the title (LEFT, * CENTER or RIGHT, {@code null} not * permitted). * @param verticalAlignment the vertical alignment of the title (TOP, * MIDDLE or BOTTOM, {@code null} not * permitted). * @param padding the amount of space to leave around the outside of the * title ({@code null} not permitted). */ protected Title(RectangleEdge position, HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment, RectangleInsets padding) { Args.nullNotPermitted(position, "position"); Args.nullNotPermitted(horizontalAlignment, "horizontalAlignment"); Args.nullNotPermitted(verticalAlignment, "verticalAlignment"); Args.nullNotPermitted(padding, "padding"); this.visible = true; this.position = position; this.horizontalAlignment = horizontalAlignment; this.verticalAlignment = verticalAlignment; setPadding(padding); this.listenerList = new EventListenerList(); this.notify = true; } /** * Returns a flag that controls whether or not the title should be * drawn. The default value is {@code true}. * * @return A boolean. * * @see #setVisible(boolean) */ public boolean isVisible() { return this.visible; } /** * Sets a flag that controls whether or not the title should be drawn, and * sends a {@link TitleChangeEvent} to all registered listeners. * * @param visible the new flag value. * * @see #isVisible() */ public void setVisible(boolean visible) { this.visible = visible; notifyListeners(new TitleChangeEvent(this)); } /** * Returns the position of the title. * * @return The title position (never {@code null}). */ public RectangleEdge getPosition() { return this.position; } /** * Sets the position for the title and sends a {@link TitleChangeEvent} to * all registered listeners. * * @param position the position ({@code null} not permitted). */ public void setPosition(RectangleEdge position) { Args.nullNotPermitted(position, "position"); if (this.position != position) { this.position = position; notifyListeners(new TitleChangeEvent(this)); } } /** * Returns the horizontal alignment of the title. * * @return The horizontal alignment (never {@code null}). */ public HorizontalAlignment getHorizontalAlignment() { return this.horizontalAlignment; } /** * Sets the horizontal alignment for the title and sends a * {@link TitleChangeEvent} to all registered listeners. * * @param alignment the horizontal alignment ({@code null} not * permitted). */ public void setHorizontalAlignment(HorizontalAlignment alignment) { Args.nullNotPermitted(alignment, "alignment"); if (this.horizontalAlignment != alignment) { this.horizontalAlignment = alignment; notifyListeners(new TitleChangeEvent(this)); } } /** * Returns the vertical alignment of the title. * * @return The vertical alignment (never {@code null}). */ public VerticalAlignment getVerticalAlignment() { return this.verticalAlignment; } /** * Sets the vertical alignment for the title, and notifies any registered * listeners of the change. * * @param alignment the new vertical alignment (TOP, MIDDLE or BOTTOM, * {@code null} not permitted). */ public void setVerticalAlignment(VerticalAlignment alignment) { Args.nullNotPermitted(alignment, "alignment"); if (this.verticalAlignment != alignment) { this.verticalAlignment = alignment; notifyListeners(new TitleChangeEvent(this)); } } /** * Returns the flag that indicates whether or not the notification * mechanism is enabled. * * @return The flag. */ public boolean getNotify() { return this.notify; } /** * Sets the flag that indicates whether or not the notification mechanism * is enabled. There are certain situations (such as cloning) where you * want to turn notification off temporarily. * * @param flag the new value of the flag. */ public void setNotify(boolean flag) { this.notify = flag; if (flag) { notifyListeners(new TitleChangeEvent(this)); } } /** * Draws the title on a Java 2D graphics device (such as the screen or a * printer). * * @param g2 the graphics device. * @param area the area allocated for the title (subclasses should not * draw outside this area). */ @Override public abstract void draw(Graphics2D g2, Rectangle2D area); /** * Returns a clone of the title. *

* One situation when this is useful is when editing the title properties - * you can edit a clone, and then it is easier to cancel the changes if * necessary. * * @return A clone of the title. * * @throws CloneNotSupportedException not thrown by this class, but it may * be thrown by subclasses. */ @Override public Object clone() throws CloneNotSupportedException { Title duplicate = (Title) super.clone(); duplicate.listenerList = new EventListenerList(); // RectangleInsets is immutable => same reference in clone OK return duplicate; } /** * Registers an object for notification of changes to the title. * * @param listener the object that is being registered. */ public void addChangeListener(TitleChangeListener listener) { this.listenerList.add(TitleChangeListener.class, listener); } /** * Unregisters an object for notification of changes to the chart title. * * @param listener the object that is being unregistered. */ public void removeChangeListener(TitleChangeListener listener) { this.listenerList.remove(TitleChangeListener.class, listener); } /** * Notifies all registered listeners that the chart title has changed in * some way. * * @param event an object that contains information about the change to * the title. */ protected void notifyListeners(TitleChangeEvent event) { if (this.notify) { Object[] listeners = this.listenerList.getListenerList(); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == TitleChangeListener.class) { ((TitleChangeListener) listeners[i + 1]).titleChanged( event); } } } } /** * Tests an object for equality with this title. * * @param obj the object ({@code null} not permitted). * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof Title)) { return false; } Title that = (Title) obj; if (this.visible != that.visible) { return false; } if (!Objects.equals(this.position, that.position)) { return false; } if (!Objects.equals(this.horizontalAlignment, that.horizontalAlignment)) { return false; } if (!Objects.equals(this.verticalAlignment, that.verticalAlignment)) { return false; } if (this.notify != that.notify) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof Title); } /** * Returns a hashcode for the title. * * @return The hashcode. */ @Override public int hashCode() { int hash = super.hashCode(); // equals calls superclass, hashCode must also hash = 97 * hash + (this.visible ? 1 : 0); hash = 97 * hash + Objects.hashCode(this.position); hash = 97 * hash + Objects.hashCode(this.horizontalAlignment); hash = 97 * hash + Objects.hashCode(this.verticalAlignment); hash = 97 * hash + (this.notify ? 1 : 0); return hash; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.listenerList = new EventListenerList(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/title/package.html000066400000000000000000000002401463604235500264540ustar00rootroot00000000000000 Classes used to display chart titles and subtitles. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ui/000077500000000000000000000000001463604235500234735ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ui/Align.java000066400000000000000000000104401463604235500253670ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.ui; import java.awt.geom.Rectangle2D; /** * A utility class for aligning rectangles. */ public final class Align { /** Center alignment. */ public static final int CENTER = 0x00; /** Top alignment. */ public static final int TOP = 0x01; /** Bottom alignment. */ public static final int BOTTOM = 0x02; /** Left alignment. */ public static final int LEFT = 0x04; /** Right alignment. */ public static final int RIGHT = 0x08; /** Top/Left alignment. */ public static final int TOP_LEFT = TOP | LEFT; /** Top/Right alignment. */ public static final int TOP_RIGHT = TOP | RIGHT; /** Bottom/Left alignment. */ public static final int BOTTOM_LEFT = BOTTOM | LEFT; /** Bottom/Right alignment. */ public static final int BOTTOM_RIGHT = BOTTOM | RIGHT; /** Horizontal fit. */ public static final int FIT_HORIZONTAL = LEFT | RIGHT; /** Vertical fit. */ public static final int FIT_VERTICAL = TOP | BOTTOM; /** Complete fit. */ public static final int FIT = FIT_HORIZONTAL | FIT_VERTICAL; /** North alignment (same as TOP). */ public static final int NORTH = TOP; /** South alignment (same as BOTTOM). */ public static final int SOUTH = BOTTOM; /** West alignment (same as LEFT). */ public static final int WEST = LEFT; /** East alignment (same as RIGHT). */ public static final int EAST = RIGHT; /** North/West alignment (same as TOP_LEFT). */ public static final int NORTH_WEST = NORTH | WEST; /** North/East alignment (same as TOP_RIGHT). */ public static final int NORTH_EAST = NORTH | EAST; /** South/West alignment (same as BOTTOM_LEFT). */ public static final int SOUTH_WEST = SOUTH | WEST; /** South/East alignment (same as BOTTOM_RIGHT). */ public static final int SOUTH_EAST = SOUTH | EAST; /** * Private constructor. */ private Align() { super(); } /** * Aligns one rectangle ({@code rect}) relative to another rectangle ({@code frame}). * * @param rect the rectangle to be aligned ({@code null} not permitted). * @param frame the reference frame ({@code null} not permitted). * @param align the alignment code. */ public static void align(Rectangle2D rect, Rectangle2D frame, int align) { double x = frame.getCenterX() - rect.getWidth() / 2.0; double y = frame.getCenterY() - rect.getHeight() / 2.0; double w = rect.getWidth(); double h = rect.getHeight(); if ((align & FIT_VERTICAL) == FIT_VERTICAL) { h = frame.getHeight(); } if ((align & FIT_HORIZONTAL) == FIT_HORIZONTAL) { w = frame.getWidth(); } if ((align & TOP) == TOP) { y = frame.getMinY(); } if ((align & BOTTOM) == BOTTOM) { y = frame.getMaxY() - h; } if ((align & LEFT) == LEFT) { x = frame.getX(); } if ((align & RIGHT) == RIGHT) { x = frame.getMaxX() - w; } rect.setRect(x, y, w, h); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ui/ApplicationFrame.java000066400000000000000000000072231463604235500275600ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.ui; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import javax.swing.JFrame; /** * A base class for creating the main frame for simple applications. The frame * listens for window closing events, and responds by shutting down the JVM. * This is OK for small demo applications...for more serious applications, * you'll want to use something more robust. */ public class ApplicationFrame extends JFrame implements WindowListener { /** * Constructs a new application frame. * * @param title the frame title. */ public ApplicationFrame(String title) { super(title); addWindowListener(this); } /** * Listens for the main window closing, and shuts down the application. * * @param event information about the window event. */ @Override public void windowClosing(WindowEvent event) { if (event.getWindow() == this) { dispose(); System.exit(0); } } /** * Required for WindowListener interface, but not used by this class. * * @param event information about the window event. */ @Override public void windowClosed(WindowEvent event) { // ignore } /** * Required for WindowListener interface, but not used by this class. * * @param event information about the window event. */ @Override public void windowActivated(WindowEvent event) { // ignore } /** * Required for WindowListener interface, but not used by this class. * * @param event information about the window event. */ @Override public void windowDeactivated(WindowEvent event) { // ignore } /** * Required for WindowListener interface, but not used by this class. * * @param event information about the window event. */ @Override public void windowDeiconified(WindowEvent event) { // ignore } /** * Required for WindowListener interface, but not used by this class. * * @param event information about the window event. */ @Override public void windowIconified(WindowEvent event) { // ignore } /** * Required for WindowListener interface, but not used by this class. * * @param event information about the window event. */ @Override public void windowOpened(WindowEvent event) { // ignore } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ui/Drawable.java000066400000000000000000000031671463604235500260660ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.ui; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; /** * An interface for an object that can draw itself within an area on a * {@code Graphics2D}. */ public interface Drawable { /** * Draws the object. * * @param g2 the graphics device. * @param area the area inside which the object should be drawn. */ void draw(Graphics2D g2, Rectangle2D area); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ui/FontChooserPanel.java000066400000000000000000000155271463604235500275610ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.ui; import java.awt.BorderLayout; import java.awt.Font; import java.awt.GraphicsEnvironment; import java.awt.GridLayout; import java.util.ResourceBundle; import javax.swing.BorderFactory; import javax.swing.JCheckBox; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.ListModel; import org.jfree.chart.util.ResourceBundleWrapper; /** * A panel for choosing a font from the available system fonts - still a bit of * a hack at the moment, but good enough for demonstration applications. */ public class FontChooserPanel extends JPanel { /** The font sizes that can be selected. */ public static final String[] SIZES = {"9", "10", "11", "12", "14", "16", "18", "20", "22", "24", "28", "36", "48", "72"}; /** The list of fonts. */ private JList fontlist; /** The list of sizes. */ private JList sizelist; /** The checkbox that indicates whether the font is bold. */ private JCheckBox bold; /** The checkbox that indicates whether or not the font is italic. */ private JCheckBox italic; /** The resourceBundle for the localization. */ protected static ResourceBundle localizationResources = ResourceBundleWrapper.getBundle("org.jfree.chart.ui.LocalizationBundle"); /** * Standard constructor - builds a FontChooserPanel initialised with the * specified font. * * @param font the initial font to display. */ public FontChooserPanel(Font font) { final GraphicsEnvironment g = GraphicsEnvironment.getLocalGraphicsEnvironment(); final String[] fonts = g.getAvailableFontFamilyNames(); setLayout(new BorderLayout()); final JPanel right = new JPanel(new BorderLayout()); final JPanel fontPanel = new JPanel(new BorderLayout()); fontPanel.setBorder(BorderFactory.createTitledBorder( BorderFactory.createEtchedBorder(), localizationResources.getString("Font"))); this.fontlist = new JList(fonts); final JScrollPane fontpane = new JScrollPane(this.fontlist); fontpane.setBorder(BorderFactory.createEtchedBorder()); fontPanel.add(fontpane); add(fontPanel); final JPanel sizePanel = new JPanel(new BorderLayout()); sizePanel.setBorder(BorderFactory.createTitledBorder( BorderFactory.createEtchedBorder(), localizationResources.getString("Size"))); this.sizelist = new JList(SIZES); final JScrollPane sizepane = new JScrollPane(this.sizelist); sizepane.setBorder(BorderFactory.createEtchedBorder()); sizePanel.add(sizepane); final JPanel attributes = new JPanel(new GridLayout(1, 2)); this.bold = new JCheckBox(localizationResources.getString("Bold")); this.italic = new JCheckBox(localizationResources.getString("Italic")); attributes.add(this.bold); attributes.add(this.italic); attributes.setBorder(BorderFactory.createTitledBorder( BorderFactory.createEtchedBorder(), localizationResources.getString("Attributes"))); right.add(sizePanel, BorderLayout.CENTER); right.add(attributes, BorderLayout.SOUTH); add(right, BorderLayout.EAST); setSelectedFont(font); } /** * Returns a Font object representing the selection in the panel. * * @return the font. */ public Font getSelectedFont() { return new Font(getSelectedName(), getSelectedStyle(), getSelectedSize()); } /** * Returns the selected name. * * @return the name. */ public String getSelectedName() { return (String) this.fontlist.getSelectedValue(); } /** * Returns the selected style. * * @return the style. */ public int getSelectedStyle() { if (this.bold.isSelected() && this.italic.isSelected()) { return Font.BOLD + Font.ITALIC; } if (this.bold.isSelected()) { return Font.BOLD; } if (this.italic.isSelected()) { return Font.ITALIC; } else { return Font.PLAIN; } } /** * Returns the selected size. * * @return the size. */ public int getSelectedSize() { final String selected = (String) this.sizelist.getSelectedValue(); if (selected != null) { return Integer.parseInt(selected); } else { return 10; } } /** * Initializes the contents of the dialog from the given font * object. * * @param font the font from which to read the properties. */ public void setSelectedFont (Font font) { if (font == null) { throw new NullPointerException(); } this.bold.setSelected(font.isBold()); this.italic.setSelected(font.isItalic()); final String fontName = font.getName(); ListModel model = this.fontlist.getModel(); this.fontlist.clearSelection(); for (int i = 0; i < model.getSize(); i++) { if (fontName.equals(model.getElementAt(i))) { this.fontlist.setSelectedIndex(i); break; } } final String fontSize = String.valueOf(font.getSize()); model = this.sizelist.getModel(); this.sizelist.clearSelection(); for (int i = 0; i < model.getSize(); i++) { if (fontSize.equals(model.getElementAt(i))) { this.sizelist.setSelectedIndex(i); break; } } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ui/FontDisplayField.java000066400000000000000000000056131463604235500275430ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.ui; import java.awt.Font; import java.util.ResourceBundle; import javax.swing.JTextField; import org.jfree.chart.util.ResourceBundleWrapper; /** * A field for displaying a font selection. The display field itself is * read-only, to the developer must provide another mechanism to allow the * user to change the font. */ public class FontDisplayField extends JTextField { /** The current font. */ private Font displayFont; /** The resourceBundle for the localization. */ protected static final ResourceBundle localizationResources = ResourceBundleWrapper.getBundle("org.jfree.chart.ui.LocalizationBundle"); /** * Standard constructor - builds a FontDescriptionField initialised with * the specified font. * * @param font the font. */ public FontDisplayField(Font font) { super(""); setDisplayFont(font); setEnabled(false); } /** * Returns the current font. * * @return the font. */ public Font getDisplayFont() { return this.displayFont; } /** * Sets the font. * * @param font the font. */ public void setDisplayFont(Font font) { this.displayFont = font; setText(fontToString(this.displayFont)); } /** * Returns a string representation of the specified font. * * @param font the font. * * @return a string describing the font. */ private String fontToString(Font font) { if (font != null) { return font.getFontName() + ", " + font.getSize(); } else { return localizationResources.getString("No_Font_Selected"); } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ui/GradientPaintTransformType.java000066400000000000000000000105121463604235500316240ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.ui; import java.io.ObjectStreamException; import java.io.Serializable; /** * Represents a type of transform for a {@code GradientPaint}. */ public final class GradientPaintTransformType implements Serializable { /** For serialization. */ private static final long serialVersionUID = 8331561784933982450L; /** Vertical. */ public static final GradientPaintTransformType VERTICAL = new GradientPaintTransformType("GradientPaintTransformType.VERTICAL"); /** Horizontal. */ public static final GradientPaintTransformType HORIZONTAL = new GradientPaintTransformType( "GradientPaintTransformType.HORIZONTAL"); /** Center/vertical. */ public static final GradientPaintTransformType CENTER_VERTICAL = new GradientPaintTransformType( "GradientPaintTransformType.CENTER_VERTICAL"); /** Center/horizontal. */ public static final GradientPaintTransformType CENTER_HORIZONTAL = new GradientPaintTransformType( "GradientPaintTransformType.CENTER_HORIZONTAL"); /** The name. */ private String name; /** * Private constructor. * * @param name the name. */ private GradientPaintTransformType(String name) { this.name = name; } /** * Returns a string representing the object. * * @return The string. */ @Override public String toString() { return this.name; } /** * Returns {@code true} if this object is equal to the specified * object, and {@code false} otherwise. * * @param o the other object. * * @return A boolean. */ @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof GradientPaintTransformType)) { return false; } final GradientPaintTransformType t = (GradientPaintTransformType) o; if (!this.name.equals(t.name)) { return false; } return true; } /** * Returns a hash code value for the object. * * @return the hashcode */ @Override public int hashCode() { return this.name.hashCode(); } /** * Ensures that serialization returns the unique instances. * * @return The object. * * @throws ObjectStreamException if there is a problem. */ private Object readResolve() throws ObjectStreamException { GradientPaintTransformType result = null; if (this.equals(GradientPaintTransformType.HORIZONTAL)) { result = GradientPaintTransformType.HORIZONTAL; } else if (this.equals(GradientPaintTransformType.VERTICAL)) { result = GradientPaintTransformType.VERTICAL; } else if (this.equals(GradientPaintTransformType.CENTER_HORIZONTAL)) { result = GradientPaintTransformType.CENTER_HORIZONTAL; } else if (this.equals(GradientPaintTransformType.CENTER_VERTICAL)) { result = GradientPaintTransformType.CENTER_VERTICAL; } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ui/GradientPaintTransformer.java000066400000000000000000000035641463604235500313220ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.ui; import java.awt.GradientPaint; import java.awt.Shape; /** * The interface for a class that can transform a {@code GradientPaint} to * fit an arbitrary shape. */ public interface GradientPaintTransformer { /** * Transforms a {@code GradientPaint} instance to fit some target * shape. Classes that implement this method typically return a new * instance of {@code GradientPaint}. * * @param paint the original paint (not {@code null}). * @param target the reference area (not {@code null}). * * @return A transformed paint. */ GradientPaint transform(GradientPaint paint, Shape target); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ui/HorizontalAlignment.java000066400000000000000000000074571463604235500303430ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.ui; import java.io.ObjectStreamException; import java.io.Serializable; /** * An enumeration of the horizontal alignment types ({@code LEFT}, * {@code RIGHT} and {@code CENTER}). */ public final class HorizontalAlignment implements Serializable { /** For serialization. */ private static final long serialVersionUID = -8249740987565309567L; /** Left alignment. */ public static final HorizontalAlignment LEFT = new HorizontalAlignment("HorizontalAlignment.LEFT"); /** Right alignment. */ public static final HorizontalAlignment RIGHT = new HorizontalAlignment("HorizontalAlignment.RIGHT"); /** Center alignment. */ public static final HorizontalAlignment CENTER = new HorizontalAlignment("HorizontalAlignment.CENTER"); /** The name. */ private String name; /** * Private constructor. * * @param name the name. */ private HorizontalAlignment(String name) { this.name = name; } /** * Returns a string representing the object. * * @return The string. */ @Override public String toString() { return this.name; } /** * Returns {@code true} if this object is equal to the specified * object, and {@code false} otherwise. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof HorizontalAlignment)) { return false; } final HorizontalAlignment that = (HorizontalAlignment) obj; if (!this.name.equals(that.name)) { return false; } return true; } /** * Returns a hash code value for the object. * * @return The hashcode */ @Override public int hashCode() { return this.name.hashCode(); } /** * Ensures that serialization returns the unique instances. * * @return The object. * * @throws ObjectStreamException if there is a problem. */ private Object readResolve() throws ObjectStreamException { HorizontalAlignment result = null; if (this.equals(HorizontalAlignment.LEFT)) { result = HorizontalAlignment.LEFT; } else if (this.equals(HorizontalAlignment.RIGHT)) { result = HorizontalAlignment.RIGHT; } else if (this.equals(HorizontalAlignment.CENTER)) { result = HorizontalAlignment.CENTER; } return result; } }jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ui/LCBLayout.java000066400000000000000000000211761463604235500261430ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.ui; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Insets; import java.awt.LayoutManager; import java.io.Serializable; /** * Specialised layout manager for a grid of components. */ public class LCBLayout implements LayoutManager, Serializable { /** For serialization. */ private static final long serialVersionUID = -2531780832406163833L; /** A constant for the number of columns in the layout. */ private static final int COLUMNS = 3; /** Tracks the column widths. */ private int[] colWidth; /** Tracks the row heights. */ private int[] rowHeight; /** The gap between each label and component. */ private int labelGap; /** The gap between each component and button. */ private int buttonGap; /** The gap between rows. */ private int vGap; /** * Creates a new LCBLayout with the specified maximum number of rows. * * @param maxrows the maximum number of rows. */ public LCBLayout(int maxrows) { this.labelGap = 10; this.buttonGap = 6; this.vGap = 2; this.colWidth = new int[COLUMNS]; this.rowHeight = new int[maxrows]; } /** * Returns the preferred size using this layout manager. * * @param parent the parent. * * @return the preferred size using this layout manager. */ @Override public Dimension preferredLayoutSize(Container parent) { synchronized (parent.getTreeLock()) { Insets insets = parent.getInsets(); int ncomponents = parent.getComponentCount(); int nrows = ncomponents / COLUMNS; for (int c = 0; c < COLUMNS; c++) { for (int r = 0; r < nrows; r++) { Component component = parent.getComponent(r * COLUMNS + c); Dimension d = component.getPreferredSize(); if (this.colWidth[c] < d.width) { this.colWidth[c] = d.width; } if (this.rowHeight[r] < d.height) { this.rowHeight[r] = d.height; } } } int totalHeight = this.vGap * (nrows - 1); for (int r = 0; r < nrows; r++) { totalHeight = totalHeight + this.rowHeight[r]; } int totalWidth = this.colWidth[0] + this.labelGap + this.colWidth[1] + this.buttonGap + this.colWidth[2]; return new Dimension( insets.left + insets.right + totalWidth + this.labelGap + this.buttonGap, insets.top + insets.bottom + totalHeight + this.vGap ); } } /** * Returns the minimum size using this layout manager. * * @param parent the parent. * * @return the minimum size using this layout manager. */ @Override public Dimension minimumLayoutSize(Container parent) { synchronized (parent.getTreeLock()) { Insets insets = parent.getInsets(); int ncomponents = parent.getComponentCount(); int nrows = ncomponents / COLUMNS; for (int c = 0; c < COLUMNS; c++) { for (int r = 0; r < nrows; r++) { Component component = parent.getComponent(r * COLUMNS + c); Dimension d = component.getMinimumSize(); if (this.colWidth[c] < d.width) { this.colWidth[c] = d.width; } if (this.rowHeight[r] < d.height) { this.rowHeight[r] = d.height; } } } int totalHeight = this.vGap * (nrows - 1); for (int r = 0; r < nrows; r++) { totalHeight = totalHeight + this.rowHeight[r]; } int totalWidth = this.colWidth[0] + this.labelGap + this.colWidth[1] + this.buttonGap + this.colWidth[2]; return new Dimension( insets.left + insets.right + totalWidth + this.labelGap + this.buttonGap, insets.top + insets.bottom + totalHeight + this.vGap ); } } /** * Lays out the components. * * @param parent the parent. */ @Override public void layoutContainer(Container parent) { synchronized (parent.getTreeLock()) { Insets insets = parent.getInsets(); int ncomponents = parent.getComponentCount(); int nrows = ncomponents / COLUMNS; for (int c = 0; c < COLUMNS; c++) { for (int r = 0; r < nrows; r++) { Component component = parent.getComponent(r * COLUMNS + c); Dimension d = component.getPreferredSize(); if (this.colWidth[c] < d.width) { this.colWidth[c] = d.width; } if (this.rowHeight[r] < d.height) { this.rowHeight[r] = d.height; } } } int totalHeight = this.vGap * (nrows - 1); for (int r = 0; r < nrows; r++) { totalHeight = totalHeight + this.rowHeight[r]; } int totalWidth = this.colWidth[0] + this.colWidth[1] + this.colWidth[2]; // adjust the width of the second column to use up all of parent int available = parent.getWidth() - insets.left - insets.right - this.labelGap - this.buttonGap; this.colWidth[1] = this.colWidth[1] + (available - totalWidth); // *** DO THE LAYOUT *** int x = insets.left; for (int c = 0; c < COLUMNS; c++) { int y = insets.top; for (int r = 0; r < nrows; r++) { int i = r * COLUMNS + c; if (i < ncomponents) { Component component = parent.getComponent(i); Dimension d = component.getPreferredSize(); int h = d.height; int adjust = (this.rowHeight[r] - h) / 2; parent.getComponent(i).setBounds(x, y + adjust, this.colWidth[c], h); } y = y + this.rowHeight[r] + this.vGap; } x = x + this.colWidth[c]; if (c == 0) { x = x + this.labelGap; } if (c == 1) { x = x + this.buttonGap; } } } } /** * Not used. * * @param comp the component. */ public void addLayoutComponent(Component comp) { // not used } /** * Not used. * * @param comp the component. */ @Override public void removeLayoutComponent(Component comp) { // not used } /** * Not used. * * @param name the component name. * @param comp the component. */ @Override public void addLayoutComponent(String name, Component comp) { // not used } /** * Not used. * * @param name the component name. * @param comp the component. */ public void removeLayoutComponent(String name, Component comp) { // not used } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ui/Layer.java000066400000000000000000000034541463604235500254200ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.ui; /** * Used to indicate either the foreground or background layer. */ public enum Layer { /** Foreground. */ FOREGROUND("Layer.FOREGROUND"), /** Background. */ BACKGROUND("Layer.BACKGROUND"); /** The name. */ private String name; /** * Private constructor. * * @param name the name. */ Layer(final String name) { this.name = name; } /** * Returns a string representing the object. * * @return The string. */ @Override public String toString() { return this.name; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ui/LengthAdjustmentType.java000066400000000000000000000073261463604235500304700ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.ui; import java.io.ObjectStreamException; import java.io.Serializable; /** * Represents the three options for adjusting a length: expand, contract, and * no change. */ public final class LengthAdjustmentType implements Serializable { /** For serialization. */ private static final long serialVersionUID = -6097408511380545010L; /** NO_CHANGE. */ public static final LengthAdjustmentType NO_CHANGE = new LengthAdjustmentType("NO_CHANGE"); /** EXPAND. */ public static final LengthAdjustmentType EXPAND = new LengthAdjustmentType("EXPAND"); /** CONTRACT. */ public static final LengthAdjustmentType CONTRACT = new LengthAdjustmentType("CONTRACT"); /** The name. */ private String name; /** * Private constructor. * * @param name the name. */ private LengthAdjustmentType(String name) { this.name = name; } /** * Returns a string representing the object. * * @return The string. */ @Override public String toString() { return this.name; } /** * Returns {@code true} if this object is equal to the specified * object, and {@code false} otherwise. * * @param obj the other object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof LengthAdjustmentType)) { return false; } final LengthAdjustmentType that = (LengthAdjustmentType) obj; if (!this.name.equals(that.name)) { return false; } return true; } /** * Returns a hash code value for the object. * * @return The hashcode */ @Override public int hashCode() { return this.name.hashCode(); } /** * Ensures that serialization returns the unique instances. * * @return The object. * * @throws ObjectStreamException if there is a problem. */ private Object readResolve() throws ObjectStreamException { if (this.equals(LengthAdjustmentType.NO_CHANGE)) { return LengthAdjustmentType.NO_CHANGE; } else if (this.equals(LengthAdjustmentType.EXPAND)) { return LengthAdjustmentType.EXPAND; } else if (this.equals(LengthAdjustmentType.CONTRACT)) { return LengthAdjustmentType.CONTRACT; } return null; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ui/NumberCellRenderer.java000066400000000000000000000056161463604235500300650ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.ui; import java.awt.Component; import java.text.NumberFormat; import javax.swing.JTable; import javax.swing.SwingConstants; import javax.swing.table.DefaultTableCellRenderer; /** * A table cell renderer that formats numbers with right alignment in each cell. */ public class NumberCellRenderer extends DefaultTableCellRenderer { /** * Default constructor - builds a renderer that right justifies the * contents of a table cell. */ public NumberCellRenderer() { super(); setHorizontalAlignment(SwingConstants.RIGHT); } /** * Returns itself as the renderer. Supports the TableCellRenderer interface. * * @param table the table. * @param value the data to be rendered. * @param isSelected a boolean that indicates whether or not the cell is * selected. * @param hasFocus a boolean that indicates whether or not the cell has * the focus. * @param row the (zero-based) row index. * @param column the (zero-based) column index. * * @return the component that can render the contents of the cell. */ @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { setFont(null); NumberFormat nf = NumberFormat.getNumberInstance(); if (value != null) { setText(nf.format(value)); } else { setText(""); } if (isSelected) { setBackground(table.getSelectionBackground()); } else { setBackground(null); } return this; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ui/PaintSample.java000066400000000000000000000063071463604235500265610ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.ui; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Insets; import java.awt.Paint; import java.awt.geom.Rectangle2D; import javax.swing.JComponent; /** * A panel that displays a paint sample. */ public class PaintSample extends JComponent { /** The paint. */ private Paint paint; /** The preferred size of the component. */ private Dimension preferredSize; /** * Standard constructor - builds a paint sample. * * @param paint the paint to display. */ public PaintSample(Paint paint) { this.paint = paint; this.preferredSize = new Dimension(80, 12); } /** * Returns the current Paint object being displayed in the panel. * * @return the paint. */ public Paint getPaint() { return this.paint; } /** * Sets the Paint object being displayed in the panel. * * @param paint the paint. */ public void setPaint(Paint paint) { this.paint = paint; repaint(); } /** * Returns the preferred size of the component. * * @return the preferred size. */ @Override public Dimension getPreferredSize() { return this.preferredSize; } /** * Fills the component with the current Paint. * * @param g the graphics device. */ @Override public void paintComponent(Graphics g) { final Graphics2D g2 = (Graphics2D) g; final Dimension size = getSize(); final Insets insets = getInsets(); final double xx = insets.left; final double yy = insets.top; final double ww = size.getWidth() - insets.left - insets.right - 1; final double hh = size.getHeight() - insets.top - insets.bottom - 1; final Rectangle2D area = new Rectangle2D.Double(xx, yy, ww, hh); g2.setPaint(this.paint); g2.fill(area); g2.setPaint(Color.BLACK); g2.draw(area); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ui/RectangleAnchor.java000066400000000000000000000134771463604235500274110ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.ui; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import org.jfree.chart.util.Args; /** * Used to indicate an anchor point for a rectangle. */ public enum RectangleAnchor { /** Center. */ CENTER("RectangleAnchor.CENTER"), /** Top. */ TOP("RectangleAnchor.TOP"), /** Top-Left. */ TOP_LEFT("RectangleAnchor.TOP_LEFT"), /** Top-Right. */ TOP_RIGHT("RectangleAnchor.TOP_RIGHT"), /** Bottom. */ BOTTOM("RectangleAnchor.BOTTOM"), /** Bottom-Left. */ BOTTOM_LEFT("RectangleAnchor.BOTTOM_LEFT"), /** Bottom-Right. */ BOTTOM_RIGHT("RectangleAnchor.BOTTOM_RIGHT"), /** Left. */ LEFT("RectangleAnchor.LEFT"), /** Right. */ RIGHT("RectangleAnchor.RIGHT"); /** The name. */ private final String name; /** * Private constructor. * * @param name the name. */ RectangleAnchor(String name) { this.name = name; } /** * Returns the anchor point relative to the specified rectangle. * * @param rectangle the rectangle (null not permitted). * * @return The anchor point (never null). */ public Point2D getAnchorPoint(Rectangle2D rectangle) { Args.nullNotPermitted(rectangle, "rectangle"); Point2D result = new Point2D.Double(); if (this == RectangleAnchor.CENTER) { result.setLocation(rectangle.getCenterX(), rectangle.getCenterY()); } else if (this == RectangleAnchor.TOP) { result.setLocation(rectangle.getCenterX(), rectangle.getMinY()); } else if (this == RectangleAnchor.BOTTOM) { result.setLocation(rectangle.getCenterX(), rectangle.getMaxY()); } else if (this == RectangleAnchor.LEFT) { result.setLocation(rectangle.getMinX(), rectangle.getCenterY()); } else if (this == RectangleAnchor.RIGHT) { result.setLocation(rectangle.getMaxX(), rectangle.getCenterY()); } else if (this == RectangleAnchor.TOP_LEFT) { result.setLocation(rectangle.getMinX(), rectangle.getMinY()); } else if (this == RectangleAnchor.TOP_RIGHT) { result.setLocation(rectangle.getMaxX(), rectangle.getMinY()); } else if (this == RectangleAnchor.BOTTOM_LEFT) { result.setLocation(rectangle.getMinX(), rectangle.getMaxY()); } else if (this == RectangleAnchor.BOTTOM_RIGHT) { result.setLocation(rectangle.getMaxX(), rectangle.getMaxY()); } return result; } /** * Returns a string representing the object. * * @return The string. */ @Override public String toString() { return this.name; } /** * Creates a new rectangle with the specified dimensions that is aligned to * the given anchor point {@code (anchorX, anchorY)}. * * @param dimensions the dimensions ({@code null} not permitted). * @param anchorX the x-anchor. * @param anchorY the y-anchor. * @param anchor the anchor ({@code null} not permitted). * * @return A rectangle. */ public static Rectangle2D createRectangle(Size2D dimensions, double anchorX, double anchorY, RectangleAnchor anchor) { Rectangle2D result = null; double w = dimensions.getWidth(); double h = dimensions.getHeight(); if (anchor == RectangleAnchor.CENTER) { result = new Rectangle2D.Double(anchorX - w / 2.0, anchorY - h / 2.0, w, h); } else if (anchor == RectangleAnchor.TOP) { result = new Rectangle2D.Double(anchorX - w / 2.0, anchorY, w, h); } else if (anchor == RectangleAnchor.BOTTOM) { result = new Rectangle2D.Double(anchorX - w / 2.0, anchorY - h, w, h); } else if (anchor == RectangleAnchor.LEFT) { result = new Rectangle2D.Double(anchorX, anchorY - h / 2.0, w, h); } else if (anchor == RectangleAnchor.RIGHT) { result = new Rectangle2D.Double(anchorX - w, anchorY - h / 2.0, w, h); } else if (anchor == RectangleAnchor.TOP_LEFT) { result = new Rectangle2D.Double(anchorX, anchorY, w, h); } else if (anchor == RectangleAnchor.TOP_RIGHT) { result = new Rectangle2D.Double(anchorX - w, anchorY, w, h); } else if (anchor == RectangleAnchor.BOTTOM_LEFT) { result = new Rectangle2D.Double(anchorX, anchorY - h, w, h); } else if (anchor == RectangleAnchor.BOTTOM_RIGHT) { result = new Rectangle2D.Double(anchorX - w, anchorY - h, w, h); } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ui/RectangleEdge.java000066400000000000000000000135311463604235500270320ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.ui; import java.awt.geom.Rectangle2D; import java.io.ObjectStreamException; import java.io.Serializable; /** * Used to indicate the edge of a rectangle. */ public final class RectangleEdge implements Serializable { /** For serialization. */ private static final long serialVersionUID = -7400988293691093548L; /** Top. */ public static final RectangleEdge TOP = new RectangleEdge("RectangleEdge.TOP"); /** Bottom. */ public static final RectangleEdge BOTTOM = new RectangleEdge("RectangleEdge.BOTTOM"); /** Left. */ public static final RectangleEdge LEFT = new RectangleEdge("RectangleEdge.LEFT"); /** Right. */ public static final RectangleEdge RIGHT = new RectangleEdge("RectangleEdge.RIGHT"); /** The name. */ private String name; /** * Private constructor. * * @param name the name. */ private RectangleEdge(String name) { this.name = name; } /** * Returns a string representing the object. * * @return The string. */ @Override public String toString() { return this.name; } /** * Returns {@code true} if this object is equal to the specified * object, and {@code false} otherwise. * * @param o the other object. * * @return A boolean. */ @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof RectangleEdge)) { return false; } final RectangleEdge order = (RectangleEdge) o; if (!this.name.equals(order.name)) { return false; } return true; } /** * Returns a hash code value for the object. * * @return the hashcode */ @Override public int hashCode() { return this.name.hashCode(); } /** * Returns {@code true} if the edge is {@code TOP} or * {@code BOTTOM}, and {@code false} otherwise. * * @param edge the edge. * * @return A boolean. */ public static boolean isTopOrBottom(RectangleEdge edge) { return (edge == RectangleEdge.TOP || edge == RectangleEdge.BOTTOM); } /** * Returns {@code true} if the edge is {@code LEFT} or * {@code RIGHT}, and {@code false} otherwise. * * @param edge the edge. * * @return A boolean. */ public static boolean isLeftOrRight(RectangleEdge edge) { return (edge == RectangleEdge.LEFT || edge == RectangleEdge.RIGHT); } /** * Returns the opposite edge. * * @param edge an edge. * * @return The opposite edge. */ public static RectangleEdge opposite(RectangleEdge edge) { RectangleEdge result = null; if (edge == RectangleEdge.TOP) { result = RectangleEdge.BOTTOM; } else if (edge == RectangleEdge.BOTTOM) { result = RectangleEdge.TOP; } else if (edge == RectangleEdge.LEFT) { result = RectangleEdge.RIGHT; } else if (edge == RectangleEdge.RIGHT) { result = RectangleEdge.LEFT; } return result; } /** * Returns the x or y coordinate of the specified edge. * * @param rectangle the rectangle. * @param edge the edge. * * @return The coordinate. */ public static double coordinate(Rectangle2D rectangle, RectangleEdge edge) { double result = 0.0; if (edge == RectangleEdge.TOP) { result = rectangle.getMinY(); } else if (edge == RectangleEdge.BOTTOM) { result = rectangle.getMaxY(); } else if (edge == RectangleEdge.LEFT) { result = rectangle.getMinX(); } else if (edge == RectangleEdge.RIGHT) { result = rectangle.getMaxX(); } return result; } /** * Ensures that serialization returns the unique instances. * * @return The object. * * @throws ObjectStreamException if there is a problem. */ private Object readResolve() throws ObjectStreamException { RectangleEdge result = null; if (this.equals(RectangleEdge.TOP)) { result = RectangleEdge.TOP; } else if (this.equals(RectangleEdge.BOTTOM)) { result = RectangleEdge.BOTTOM; } else if (this.equals(RectangleEdge.LEFT)) { result = RectangleEdge.LEFT; } else if (this.equals(RectangleEdge.RIGHT)) { result = RectangleEdge.RIGHT; } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ui/RectangleInsets.java000066400000000000000000000376751463604235500274520ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.ui; import java.awt.geom.Rectangle2D; import java.io.Serializable; import org.jfree.chart.util.UnitType; /** * Represents the insets for a rectangle, specified in absolute or relative * terms. This class is immutable. */ public class RectangleInsets implements Serializable { /** For serialization. */ private static final long serialVersionUID = 1902273207559319996L; /** * A useful constant representing zero insets. */ public static final RectangleInsets ZERO_INSETS = new RectangleInsets( UnitType.ABSOLUTE, 0.0, 0.0, 0.0, 0.0); /** Absolute or relative units. */ private UnitType unitType; /** The top insets. */ private double top; /** The left insets. */ private double left; /** The bottom insets. */ private double bottom; /** The right insets. */ private double right; /** * Creates a new instance with all insets initialised to {@code 1.0}. */ public RectangleInsets() { this(1.0, 1.0, 1.0, 1.0); } /** * Creates a new instance with the specified insets (as 'absolute' units). * * @param top the top insets. * @param left the left insets. * @param bottom the bottom insets. * @param right the right insets. */ public RectangleInsets(double top, double left, double bottom, double right) { this(UnitType.ABSOLUTE, top, left, bottom, right); } /** * Creates a new instance. * * @param unitType absolute or relative units ({@code null} not * permitted). * @param top the top insets. * @param left the left insets. * @param bottom the bottom insets. * @param right the right insets. */ public RectangleInsets(UnitType unitType, double top, double left, double bottom, double right) { if (unitType == null) { throw new IllegalArgumentException("Null 'unitType' argument."); } this.unitType = unitType; this.top = top; this.bottom = bottom; this.left = left; this.right = right; } /** * Returns the unit type (absolute or relative). This specifies whether * the insets are measured as Java2D units or percentages. * * @return The unit type (never {@code null}). */ public UnitType getUnitType() { return this.unitType; } /** * Returns the top insets. * * @return The top insets. */ public double getTop() { return this.top; } /** * Returns the bottom insets. * * @return The bottom insets. */ public double getBottom() { return this.bottom; } /** * Returns the left insets. * * @return The left insets. */ public double getLeft() { return this.left; } /** * Returns the right insets. * * @return The right insets. */ public double getRight() { return this.right; } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof RectangleInsets)) { return false; } final RectangleInsets that = (RectangleInsets) obj; if (that.unitType != this.unitType) { return false; } if (this.left != that.left) { return false; } if (this.right != that.right) { return false; } if (this.top != that.top) { return false; } if (this.bottom != that.bottom) { return false; } return true; } /** * Returns a hash code for the object. * * @return A hash code. */ @Override public int hashCode() { int result; long temp; result = (this.unitType != null ? this.unitType.hashCode() : 0); temp = this.top != +0.0d ? Double.doubleToLongBits(this.top) : 0L; result = 29 * result + (int) (temp ^ (temp >>> 32)); temp = this.bottom != +0.0d ? Double.doubleToLongBits(this.bottom) : 0L; result = 29 * result + (int) (temp ^ (temp >>> 32)); temp = this.left != +0.0d ? Double.doubleToLongBits(this.left) : 0L; result = 29 * result + (int) (temp ^ (temp >>> 32)); temp = this.right != +0.0d ? Double.doubleToLongBits(this.right) : 0L; result = 29 * result + (int) (temp ^ (temp >>> 32)); return result; } /** * Returns a textual representation of this instance, useful for debugging * purposes. * * @return A string representing this instance. */ @Override public String toString() { return "RectangleInsets[t=" + this.top + ",l=" + this.left + ",b=" + this.bottom + ",r=" + this.right + "]"; } /** * Creates an adjusted rectangle using the supplied rectangle, the insets * specified by this instance, and the horizontal and vertical * adjustment types. * * @param base the base rectangle ({@code null} not permitted). * @param horizontal the horizontal adjustment type ({@code null} not * permitted). * @param vertical the vertical adjustment type ({@code null} not * permitted). * * @return The inset rectangle. */ public Rectangle2D createAdjustedRectangle(Rectangle2D base, LengthAdjustmentType horizontal, LengthAdjustmentType vertical) { if (base == null) { throw new IllegalArgumentException("Null 'base' argument."); } double x = base.getX(); double y = base.getY(); double w = base.getWidth(); double h = base.getHeight(); if (horizontal == LengthAdjustmentType.EXPAND) { final double leftOutset = calculateLeftOutset(w); x = x - leftOutset; w = w + leftOutset + calculateRightOutset(w); } else if (horizontal == LengthAdjustmentType.CONTRACT) { final double leftMargin = calculateLeftInset(w); x = x + leftMargin; w = w - leftMargin - calculateRightInset(w); } if (vertical == LengthAdjustmentType.EXPAND) { final double topMargin = calculateTopOutset(h); y = y - topMargin; h = h + topMargin + calculateBottomOutset(h); } else if (vertical == LengthAdjustmentType.CONTRACT) { final double topMargin = calculateTopInset(h); y = y + topMargin; h = h - topMargin - calculateBottomInset(h); } return new Rectangle2D.Double(x, y, w, h); } /** * Creates an 'inset' rectangle. * * @param base the base rectangle ({@code null} not permitted). * * @return The inset rectangle. */ public Rectangle2D createInsetRectangle(Rectangle2D base) { return createInsetRectangle(base, true, true); } /** * Creates an 'inset' rectangle. * * @param base the base rectangle ({@code null} not permitted). * @param horizontal apply horizontal insets? * @param vertical apply vertical insets? * * @return The inset rectangle. */ public Rectangle2D createInsetRectangle(Rectangle2D base, boolean horizontal, boolean vertical) { if (base == null) { throw new IllegalArgumentException("Null 'base' argument."); } double topMargin = 0.0; double bottomMargin = 0.0; if (vertical) { topMargin = calculateTopInset(base.getHeight()); bottomMargin = calculateBottomInset(base.getHeight()); } double leftMargin = 0.0; double rightMargin = 0.0; if (horizontal) { leftMargin = calculateLeftInset(base.getWidth()); rightMargin = calculateRightInset(base.getWidth()); } return new Rectangle2D.Double(base.getX() + leftMargin, base.getY() + topMargin, base.getWidth() - leftMargin - rightMargin, base.getHeight() - topMargin - bottomMargin); } /** * Creates an outset rectangle. * * @param base the base rectangle ({@code null} not permitted). * * @return An outset rectangle. */ public Rectangle2D createOutsetRectangle(Rectangle2D base) { return createOutsetRectangle(base, true, true); } /** * Creates an outset rectangle. * * @param base the base rectangle ({@code null} not permitted). * @param horizontal apply horizontal insets? * @param vertical apply vertical insets? * * @return An outset rectangle. */ public Rectangle2D createOutsetRectangle(Rectangle2D base, boolean horizontal, boolean vertical) { if (base == null) { throw new IllegalArgumentException("Null 'base' argument."); } double topMargin = 0.0; double bottomMargin = 0.0; if (vertical) { topMargin = calculateTopOutset(base.getHeight()); bottomMargin = calculateBottomOutset(base.getHeight()); } double leftMargin = 0.0; double rightMargin = 0.0; if (horizontal) { leftMargin = calculateLeftOutset(base.getWidth()); rightMargin = calculateRightOutset(base.getWidth()); } return new Rectangle2D.Double(base.getX() - leftMargin, base.getY() - topMargin, base.getWidth() + leftMargin + rightMargin, base.getHeight() + topMargin + bottomMargin); } /** * Returns the top margin. * * @param height the height of the base rectangle. * * @return The top margin (in Java2D units). */ public double calculateTopInset(double height) { double result = this.top; if (this.unitType == UnitType.RELATIVE) { result = (this.top * height); } return result; } /** * Returns the top margin. * * @param height the height of the base rectangle. * * @return The top margin (in Java2D units). */ public double calculateTopOutset(double height) { double result = this.top; if (this.unitType == UnitType.RELATIVE) { result = (height / (1 - this.top - this.bottom)) * this.top; } return result; } /** * Returns the bottom margin. * * @param height the height of the base rectangle. * * @return The bottom margin (in Java2D units). */ public double calculateBottomInset(double height) { double result = this.bottom; if (this.unitType == UnitType.RELATIVE) { result = (this.bottom * height); } return result; } /** * Returns the bottom margin. * * @param height the height of the base rectangle. * * @return The bottom margin (in Java2D units). */ public double calculateBottomOutset(double height) { double result = this.bottom; if (this.unitType == UnitType.RELATIVE) { result = (height / (1 - this.top - this.bottom)) * this.bottom; } return result; } /** * Returns the left margin. * * @param width the width of the base rectangle. * * @return The left margin (in Java2D units). */ public double calculateLeftInset(double width) { double result = this.left; if (this.unitType == UnitType.RELATIVE) { result = (this.left * width); } return result; } /** * Returns the left margin. * * @param width the width of the base rectangle. * * @return The left margin (in Java2D units). */ public double calculateLeftOutset(double width) { double result = this.left; if (this.unitType == UnitType.RELATIVE) { result = (width / (1 - this.left - this.right)) * this.left; } return result; } /** * Returns the right margin. * * @param width the width of the base rectangle. * * @return The right margin (in Java2D units). */ public double calculateRightInset(double width) { double result = this.right; if (this.unitType == UnitType.RELATIVE) { result = (this.right * width); } return result; } /** * Returns the right margin. * * @param width the width of the base rectangle. * * @return The right margin (in Java2D units). */ public double calculateRightOutset(double width) { double result = this.right; if (this.unitType == UnitType.RELATIVE) { result = (width / (1 - this.left - this.right)) * this.right; } return result; } /** * Trims the given width to allow for the insets. * * @param width the width. * * @return The trimmed width. */ public double trimWidth(double width) { return width - calculateLeftInset(width) - calculateRightInset(width); } /** * Extends the given width to allow for the insets. * * @param width the width. * * @return The extended width. */ public double extendWidth(double width) { return width + calculateLeftOutset(width) + calculateRightOutset(width); } /** * Trims the given height to allow for the insets. * * @param height the height. * * @return The trimmed height. */ public double trimHeight(double height) { return height - calculateTopInset(height) - calculateBottomInset(height); } /** * Extends the given height to allow for the insets. * * @param height the height. * * @return The extended height. */ public double extendHeight(double height) { return height + calculateTopOutset(height) + calculateBottomOutset(height); } /** * Shrinks the given rectangle by the amount of these insets. * * @param area the area ({@code null} not permitted). */ public void trim(Rectangle2D area) { double w = area.getWidth(); double h = area.getHeight(); double l = calculateLeftInset(w); double r = calculateRightInset(w); double t = calculateTopInset(h); double b = calculateBottomInset(h); area.setRect(area.getX() + l, area.getY() + t, w - l - r, h - t - b); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ui/Size2D.java000066400000000000000000000104401463604235500254350ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.ui; import java.io.Serializable; import org.jfree.chart.util.PublicCloneable; /** * A simple class for representing the dimensions of an object. It would be * better to use {@code Dimension2D}, but this class is broken on various * JDK releases (particularly JDK 1.3.1, refer to bugs 4189446 and 4976448 on * the Java bug parade). */ public class Size2D implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 2558191683786418168L; /** The width. */ public double width; /** The height. */ public double height; /** * Creates a new instance with zero width and height. */ public Size2D() { this(0.0, 0.0); } /** * Creates a new instance with the specified width and height. * * @param width the width. * @param height the height. */ public Size2D(double width, double height) { this.width = width; this.height = height; } /** * Returns the width. * * @return The width. */ public double getWidth() { return this.width; } /** * Sets the width. * * @param width the width. */ public void setWidth(double width) { this.width = width; } /** * Returns the height. * * @return The height. */ public double getHeight() { return this.height; } /** * Sets the height. * * @param height the height. */ public void setHeight(double height) { this.height = height; } /** * Returns a string representation of this instance, mostly used for * debugging purposes. * * @return A string. */ @Override public String toString() { return "Size2D[width=" + this.width + ", height=" + this.height + "]"; } /** * Compares this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof Size2D)) { return false; } Size2D that = (Size2D) obj; if (this.width != that.width) { return false; } if (this.height != that.height) { return false; } return true; } @Override public int hashCode() { int hash = 5; hash = 29 * hash + (int) (Double.doubleToLongBits(this.width) ^ (Double.doubleToLongBits(this.width) >>> 32)); hash = 29 * hash + (int) (Double.doubleToLongBits(this.height) ^ (Double.doubleToLongBits(this.height) >>> 32)); return hash; } /** * Returns a clone of this object. * * @return A clone. * * @throws CloneNotSupportedException if the object cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ui/StandardGradientPaintTransformer.java000066400000000000000000000134651463604235500330040ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.ui; import java.awt.GradientPaint; import java.awt.Shape; import java.awt.geom.Rectangle2D; import java.io.Serializable; import org.jfree.chart.util.PublicCloneable; /** * Transforms a {@code GradientPaint} to range over the width of a target * shape. Instances of this class are immutable. */ public class StandardGradientPaintTransformer implements GradientPaintTransformer, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -8155025776964678320L; /** The transform type. */ private GradientPaintTransformType type; /** * Creates a new transformer with the type * {@link GradientPaintTransformType#VERTICAL}. */ public StandardGradientPaintTransformer() { this(GradientPaintTransformType.VERTICAL); } /** * Creates a new transformer with the specified type. * * @param type the transform type ({@code null} not permitted). */ public StandardGradientPaintTransformer( final GradientPaintTransformType type) { if (type == null) { throw new IllegalArgumentException("Null 'type' argument."); } this.type = type; } /** * Returns the type of transform. * * @return The type of transform (never {@code null}). */ public GradientPaintTransformType getType() { return this.type; } /** * Transforms a {@code GradientPaint} instance to fit the specified * {@code target} shape. * * @param paint the original paint ({@code null} not permitted). * @param target the target shape ({@code null} not permitted). * * @return The transformed paint. */ @Override public GradientPaint transform(GradientPaint paint, Shape target) { GradientPaint result = paint; Rectangle2D bounds = target.getBounds2D(); if (this.type.equals(GradientPaintTransformType.VERTICAL)) { result = new GradientPaint((float) bounds.getCenterX(), (float) bounds.getMinY(), paint.getColor1(), (float) bounds.getCenterX(), (float) bounds.getMaxY(), paint.getColor2()); } else if (this.type.equals(GradientPaintTransformType.HORIZONTAL)) { result = new GradientPaint((float) bounds.getMinX(), (float) bounds.getCenterY(), paint.getColor1(), (float) bounds.getMaxX(), (float) bounds.getCenterY(), paint.getColor2()); } else if (this.type.equals( GradientPaintTransformType.CENTER_HORIZONTAL)) { result = new GradientPaint((float) bounds.getCenterX(), (float) bounds.getCenterY(), paint.getColor2(), (float) bounds.getMaxX(), (float) bounds.getCenterY(), paint.getColor1(), true); } else if (this.type.equals(GradientPaintTransformType.CENTER_VERTICAL)) { result = new GradientPaint((float) bounds.getCenterX(), (float) bounds.getMinY(), paint.getColor1(), (float) bounds.getCenterX(), (float) bounds.getCenterY(), paint.getColor2(), true); } return result; } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof StandardGradientPaintTransformer)) { return false; } StandardGradientPaintTransformer that = (StandardGradientPaintTransformer) obj; if (this.type != that.type) { return false; } return true; } /** * Returns a clone of the transformer. Note that instances of this class * are immutable, so cloning an instance isn't really necessary. * * @return A clone. * * @throws CloneNotSupportedException not thrown by this class, but * subclasses (if any) might. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Returns a hash code for this object. * * @return A hash code. */ @Override public int hashCode() { return (this.type != null ? this.type.hashCode() : 0); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ui/StrokeChooserPanel.java000066400000000000000000000064241463604235500301160ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.ui; import java.awt.BorderLayout; import java.awt.Stroke; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.DefaultComboBoxModel; import javax.swing.JComboBox; import javax.swing.JPanel; /** * A component for choosing a stroke from a list of available strokes. This * class needs work. */ public class StrokeChooserPanel extends JPanel { /** A combo for selecting the stroke. */ private JComboBox selector; /** * Creates a panel containing a combo-box that allows the user to select * one stroke from a list of available strokes. * * @param current the current stroke sample. * @param available an array of 'available' stroke samples. */ public StrokeChooserPanel(StrokeSample current, StrokeSample[] available) { setLayout(new BorderLayout()); // we've changed the behaviour here to populate the combo box // with Stroke objects directly - ideally we'd change the signature // of the constructor too...maybe later. DefaultComboBoxModel model = new DefaultComboBoxModel(); for (int i = 0; i < available.length; i++) { model.addElement(available[i].getStroke()); } this.selector = new JComboBox(model); this.selector.setSelectedItem(current.getStroke()); this.selector.setRenderer(new StrokeSample(null)); add(this.selector); // Changes due to focus problems!! DZ this.selector.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent evt) { getSelector().transferFocus(); } }); } /** * Returns the selector component. * * @return Returns the selector. */ protected final JComboBox getSelector() { return this.selector; } /** * Returns the selected stroke. * * @return The selected stroke (possibly {@code null}). */ public Stroke getSelectedStroke() { return (Stroke) this.selector.getSelectedItem(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ui/StrokeSample.java000066400000000000000000000116021463604235500267470ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.ui; import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Insets; import java.awt.RenderingHints; import java.awt.Stroke; import java.awt.geom.Ellipse2D; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import javax.swing.JComponent; import javax.swing.JList; import javax.swing.ListCellRenderer; /** * A panel that displays a stroke sample. */ public class StrokeSample extends JComponent implements ListCellRenderer { /** The stroke being displayed (may be null). */ private Stroke stroke; /** The preferred size of the component. */ private Dimension preferredSize; /** * Creates a StrokeSample for the specified stroke. * * @param stroke the sample stroke ({@code null} permitted). */ public StrokeSample(Stroke stroke) { this.stroke = stroke; this.preferredSize = new Dimension(80, 18); setPreferredSize(this.preferredSize); } /** * Returns the current Stroke object being displayed. * * @return The stroke (possibly {@code null}). */ public Stroke getStroke() { return this.stroke; } /** * Sets the stroke object being displayed and repaints the component. * * @param stroke the stroke ({@code null} permitted). */ public void setStroke(Stroke stroke) { this.stroke = stroke; repaint(); } /** * Returns the preferred size of the component. * * @return the preferred size of the component. */ @Override public Dimension getPreferredSize() { return this.preferredSize; } /** * Draws a line using the sample stroke. * * @param g the graphics device. */ @Override public void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D) g; g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); Dimension size = getSize(); Insets insets = getInsets(); double xx = insets.left; double yy = insets.top; double ww = size.getWidth() - insets.left - insets.right; double hh = size.getHeight() - insets.top - insets.bottom; // calculate point one Point2D one = new Point2D.Double(xx + 6, yy + hh / 2); // calculate point two Point2D two = new Point2D.Double(xx + ww - 6, yy + hh / 2); // draw a circle at point one Ellipse2D circle1 = new Ellipse2D.Double(one.getX() - 5, one.getY() - 5, 10, 10); Ellipse2D circle2 = new Ellipse2D.Double(two.getX() - 6, two.getY() - 5, 10, 10); // draw a circle at point two g2.draw(circle1); g2.fill(circle1); g2.draw(circle2); g2.fill(circle2); // draw a line connecting the points Line2D line = new Line2D.Double(one, two); if (this.stroke != null) { g2.setStroke(this.stroke); g2.draw(line); } } /** * Returns a list cell renderer for the stroke, so the sample can be * displayed in a list or combo. * * @param list the list. * @param value the value. * @param index the index. * @param isSelected selected? * @param cellHasFocus focussed? * * @return the component for rendering. */ @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { if (value instanceof Stroke) { setStroke((Stroke) value); } else { setStroke(null); } return this; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ui/TextAnchor.java000066400000000000000000000220461463604235500264210ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.ui; import java.io.ObjectStreamException; import java.io.Serializable; /** * Used to indicate the position of an anchor point for a text string. This is * frequently used to align a string to a fixed point in some coordinate space. */ public final class TextAnchor implements Serializable { /** For serialization. */ private static final long serialVersionUID = 8219158940496719660L; /** Top/left. */ public static final TextAnchor TOP_LEFT = new TextAnchor("TextAnchor.TOP_LEFT"); /** Top/center. */ public static final TextAnchor TOP_CENTER = new TextAnchor("TextAnchor.TOP_CENTER"); /** Top/right. */ public static final TextAnchor TOP_RIGHT = new TextAnchor("TextAnchor.TOP_RIGHT"); /** Half-ascent/left. */ public static final TextAnchor HALF_ASCENT_LEFT = new TextAnchor("TextAnchor.HALF_ASCENT_LEFT"); /** Half-ascent/center. */ public static final TextAnchor HALF_ASCENT_CENTER = new TextAnchor("TextAnchor.HALF_ASCENT_CENTER"); /** Half-ascent/right. */ public static final TextAnchor HALF_ASCENT_RIGHT = new TextAnchor("TextAnchor.HALF_ASCENT_RIGHT"); /** Middle/left. */ public static final TextAnchor CENTER_LEFT = new TextAnchor("TextAnchor.CENTER_LEFT"); /** Middle/center. */ public static final TextAnchor CENTER = new TextAnchor("TextAnchor.CENTER"); /** Middle/right. */ public static final TextAnchor CENTER_RIGHT = new TextAnchor("TextAnchor.CENTER_RIGHT"); /** Baseline/left. */ public static final TextAnchor BASELINE_LEFT = new TextAnchor("TextAnchor.BASELINE_LEFT"); /** Baseline/center. */ public static final TextAnchor BASELINE_CENTER = new TextAnchor("TextAnchor.BASELINE_CENTER"); /** Baseline/right. */ public static final TextAnchor BASELINE_RIGHT = new TextAnchor("TextAnchor.BASELINE_RIGHT"); /** Bottom/left. */ public static final TextAnchor BOTTOM_LEFT = new TextAnchor("TextAnchor.BOTTOM_LEFT"); /** Bottom/center. */ public static final TextAnchor BOTTOM_CENTER = new TextAnchor("TextAnchor.BOTTOM_CENTER"); /** Bottom/right. */ public static final TextAnchor BOTTOM_RIGHT = new TextAnchor("TextAnchor.BOTTOM_RIGHT"); /** The name. */ private String name; /** * Private constructor. * * @param name the name. */ private TextAnchor(String name) { this.name = name; } /** * Returns {@code true} if the anchor is a left-side anchor, and * {@code false} otherwise. * * @return A boolean. */ public boolean isLeft() { return this == BASELINE_LEFT || this == BOTTOM_LEFT || this == CENTER_LEFT || this == HALF_ASCENT_LEFT || this == TOP_LEFT; } /** * Returns {@code true} if the anchor is a right-side anchor, and * {@code false} otherwise. * * @return A boolean. */ public boolean isRight() { return this == BASELINE_RIGHT || this == BOTTOM_RIGHT || this == CENTER_RIGHT || this == HALF_ASCENT_RIGHT || this == TOP_RIGHT; } /** * Returns {@code true} if the anchor is a center anchor, and * {@code false} otherwise. * * @return A boolean. */ public boolean isHorizontalCenter() { return this == BASELINE_CENTER || this == BOTTOM_CENTER || this == CENTER || this == HALF_ASCENT_CENTER || this == TOP_CENTER; } /** * Returns {@code true} if the anchor is a top anchor, and * {@code false} otherwise. * * @return A boolean. */ public boolean isTop() { return this == TOP_LEFT || this == TOP_CENTER || this == TOP_RIGHT; } /** * Returns {@code true} if the anchor is a bottom anchor, and * {@code false} otherwise. * * @return A boolean. */ public boolean isBottom() { return this == BOTTOM_LEFT || this == BOTTOM_CENTER || this == BOTTOM_RIGHT; } /** * Returns {@code true} if the anchor is a baseline anchor, and * {@code false} otherwise. * * @return A boolean. */ public boolean isBaseline() { return this == BASELINE_LEFT || this == BASELINE_CENTER || this == BASELINE_RIGHT; } /** * Returns {@code true} if the anchor is a half-ascent anchor, and * {@code false} otherwise. * * @return A boolean. */ public boolean isHalfAscent() { return this == HALF_ASCENT_LEFT || this == HALF_ASCENT_CENTER || this == HALF_ASCENT_RIGHT; } /** * Returns {@code true} if the anchor is a half-ascent anchor, and * {@code false} otherwise. * * @return A boolean. */ public boolean isVerticalCenter() { return this == CENTER_LEFT || this == CENTER || this == CENTER_RIGHT; } /** * Returns a string representing the object. * * @return The string. */ @Override public String toString() { return this.name; } /** * Returns {@code true} if this object is equal to the specified * object, and {@code false} otherwise. * * @param o the other object. * * @return A boolean. */ @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof TextAnchor)) { return false; } TextAnchor order = (TextAnchor) o; if (!this.name.equals(order.name)) { return false; } return true; } /** * Returns a hash code value for the object. * * @return The hashcode */ @Override public int hashCode() { return this.name.hashCode(); } /** * Ensures that serialization returns the unique instances. * * @return The object. * * @throws ObjectStreamException if there is a problem. */ private Object readResolve() throws ObjectStreamException { TextAnchor result = null; if (this.equals(TextAnchor.TOP_LEFT)) { result = TextAnchor.TOP_LEFT; } else if (this.equals(TextAnchor.TOP_CENTER)) { result = TextAnchor.TOP_CENTER; } else if (this.equals(TextAnchor.TOP_RIGHT)) { result = TextAnchor.TOP_RIGHT; } else if (this.equals(TextAnchor.BOTTOM_LEFT)) { result = TextAnchor.BOTTOM_LEFT; } else if (this.equals(TextAnchor.BOTTOM_CENTER)) { result = TextAnchor.BOTTOM_CENTER; } else if (this.equals(TextAnchor.BOTTOM_RIGHT)) { result = TextAnchor.BOTTOM_RIGHT; } else if (this.equals(TextAnchor.BASELINE_LEFT)) { result = TextAnchor.BASELINE_LEFT; } else if (this.equals(TextAnchor.BASELINE_CENTER)) { result = TextAnchor.BASELINE_CENTER; } else if (this.equals(TextAnchor.BASELINE_RIGHT)) { result = TextAnchor.BASELINE_RIGHT; } else if (this.equals(TextAnchor.CENTER_LEFT)) { result = TextAnchor.CENTER_LEFT; } else if (this.equals(TextAnchor.CENTER)) { result = TextAnchor.CENTER; } else if (this.equals(TextAnchor.CENTER_RIGHT)) { result = TextAnchor.CENTER_RIGHT; } else if (this.equals(TextAnchor.HALF_ASCENT_LEFT)) { result = TextAnchor.HALF_ASCENT_LEFT; } else if (this.equals(TextAnchor.HALF_ASCENT_CENTER)) { result = TextAnchor.HALF_ASCENT_CENTER; } else if (this.equals(TextAnchor.HALF_ASCENT_RIGHT)) { result = TextAnchor.HALF_ASCENT_RIGHT; } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ui/UIUtils.java000066400000000000000000000150341463604235500256770ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.ui; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Container; import java.awt.Dialog; import java.awt.Dimension; import java.awt.Font; import java.awt.Rectangle; import java.awt.Window; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.table.TableColumn; import javax.swing.table.TableModel; /** * A collection of utility methods relating to user interfaces. */ public class UIUtils { private UIUtils() { } /** * Positions the specified frame in the middle of the screen. * * @param frame the frame to be centered on the screen. */ public static void centerFrameOnScreen(Window frame) { positionFrameOnScreen(frame, 0.5, 0.5); } /** * Positions the specified frame at a relative position in the screen, where 50% is considered * to be the center of the screen. * * @param frame the frame. * @param horizontalPercent the relative horizontal position of the frame (0.0 to 1.0, * where 0.5 is the center of the screen). * @param verticalPercent the relative vertical position of the frame (0.0 to 1.0, where * 0.5 is the center of the screen). */ public static void positionFrameOnScreen(Window frame, double horizontalPercent, double verticalPercent) { Rectangle s = frame.getGraphicsConfiguration().getBounds(); Dimension f = frame.getSize(); int w = Math.max(s.width - f.width, 0); int h = Math.max(s.height - f.height, 0); int x = (int) (horizontalPercent * w) + s.x; int y = (int) (verticalPercent * h) + s.y; frame.setBounds(x, y, f.width, f.height); } /** * Positions the specified frame at a random location on the screen while ensuring that the * entire frame is visible (provided that the frame is smaller than the screen). * * @param frame the frame. */ public static void positionFrameRandomly(Window frame) { positionFrameOnScreen(frame, Math.random(), Math.random()); } /** * Positions the specified dialog within its parent. * * @param dialog the dialog to be positioned on the screen. */ public static void centerDialogInParent(Dialog dialog) { positionDialogRelativeToParent(dialog, 0.5, 0.5); } /** * Positions the specified dialog at a position relative to its parent. * * @param dialog the dialog to be positioned. * @param horizontalPercent the relative location. * @param verticalPercent the relative location. */ public static void positionDialogRelativeToParent(Dialog dialog, double horizontalPercent, double verticalPercent) { Container parent = dialog.getParent(); if (parent == null) { centerFrameOnScreen(dialog); return; } Dimension d = dialog.getSize(); Dimension p = parent.getSize(); int baseX = parent.getX(); int baseY = parent.getY(); int x = baseX + (int) (horizontalPercent * p.width); int y = baseY + (int) (verticalPercent * p.height); // make sure the dialog fits completely on the screen... Rectangle s = parent.getGraphicsConfiguration().getBounds(); Rectangle r = new Rectangle(x, y, d.width, d.height); dialog.setBounds(r.intersection(s)); } /** * Creates a panel that contains a table based on the specified table model. * * @param model the table model to use when constructing the table. * * @return The panel. */ public static JPanel createTablePanel(TableModel model) { JPanel panel = new JPanel(new BorderLayout()); JTable table = new JTable(model); for (int columnIndex = 0; columnIndex < model.getColumnCount(); columnIndex++) { TableColumn column = table.getColumnModel().getColumn(columnIndex); Class c = model.getColumnClass(columnIndex); if (c.equals(Number.class)) { column.setCellRenderer(new NumberCellRenderer()); } } panel.add(new JScrollPane(table)); return panel; } /** * Creates a label with a specific font. * * @param text the text for the label. * @param font the font. * * @return The label. */ public static JLabel createJLabel(String text, Font font) { JLabel result = new JLabel(text); result.setFont(font); return result; } /** * Creates a label with a specific font and color. * * @param text the text for the label. * @param font the font. * @param color the color. * * @return The label. */ public static JLabel createJLabel(String text, Font font, Color color) { JLabel result = new JLabel(text); result.setFont(font); result.setForeground(color); return result; } /** * Creates a {@link JButton}. * * @param label the label. * @param font the font. * * @return The button. */ public static JButton createJButton(String label, Font font) { JButton result = new JButton(label); result.setFont(font); return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ui/VerticalAlignment.java000066400000000000000000000074011463604235500277500ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.ui; import java.io.ObjectStreamException; import java.io.Serializable; /** * An enumeration of the vertical alignment types ({@code TOP}, * {@code BOTTOM} and {@code CENTER}). */ public final class VerticalAlignment implements Serializable { /** For serialization. */ private static final long serialVersionUID = 7272397034325429853L; /** Top alignment. */ public static final VerticalAlignment TOP = new VerticalAlignment("VerticalAlignment.TOP"); /** Bottom alignment. */ public static final VerticalAlignment BOTTOM = new VerticalAlignment("VerticalAlignment.BOTTOM"); /** Center alignment. */ public static final VerticalAlignment CENTER = new VerticalAlignment("VerticalAlignment.CENTER"); /** The name. */ private final String name; /** * Private constructor. * * @param name the name. */ private VerticalAlignment(String name) { this.name = name; } /** * Returns a string representing the object. * * @return the string. */ @Override public String toString() { return this.name; } /** * Returns {@code true} if this object is equal to the specified * object, and {@code false} otherwise. * * @param o the other object. * * @return a boolean. */ @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof VerticalAlignment)) { return false; } VerticalAlignment alignment = (VerticalAlignment) o; if (!this.name.equals(alignment.name)) { return false; } return true; } /** * Returns a hash code value for the object. * * @return the hashcode */ @Override public int hashCode() { return this.name.hashCode(); } /** * Ensures that serialization returns the unique instances. * * @return The object. * * @throws ObjectStreamException if there is a problem. */ private Object readResolve() throws ObjectStreamException { if (this.equals(VerticalAlignment.TOP)) { return VerticalAlignment.TOP; } else if (this.equals(VerticalAlignment.BOTTOM)) { return VerticalAlignment.BOTTOM; } else if (this.equals(VerticalAlignment.CENTER)) { return VerticalAlignment.CENTER; } else { return null; // this should never happen } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/ui/package.html000066400000000000000000000002341463604235500257530ustar00rootroot00000000000000 Utility classes that relate to user interface items. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/urls/000077500000000000000000000000001463604235500240435ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/urls/CategoryURLGenerator.java000066400000000000000000000044631463604235500307240ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * CategoryURLGenerator.java * ------------------------- * (C) Copyright 2002-present, by Richard Atkinson and Contributors. * * Original Author: Richard Atkinson; * Contributors: David Gilbert; * */ package org.jfree.chart.urls; import org.jfree.data.category.CategoryDataset; /** * A URL generator for items in a {@link CategoryDataset}. */ public interface CategoryURLGenerator { /** * Returns a URL for one item in a dataset. As a guideline, the URL * should be valid within the context of an XHTML 1.0 document. Classes * that implement this interface are responsible for correctly escaping * any text that is derived from the dataset, as this may be user-specified * and could pose a security risk. * * @param dataset the dataset. * @param series the series (zero-based index). * @param category the category. * * @return A string containing the URL. */ String generateURL(CategoryDataset dataset, int series, int category); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/urls/CustomCategoryURLGenerator.java000066400000000000000000000133221463604235500321110ustar00rootroot00000000000000/* ====================================== * JFreeChart : a free Java chart library * ====================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------- * CustomCategoryURLGenerator.java * ------------------------------- * (C) Copyright 2008-present, by Diego Pierangeli and Contributors. * * Original Author: Diego Pierangeli; * Contributors: David Gilbert; * */ package org.jfree.chart.urls; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.category.CategoryDataset; /** * A custom URL generator. */ public class CustomCategoryURLGenerator implements CategoryURLGenerator, Cloneable, PublicCloneable, Serializable { /** Storage for the URLs. */ private ArrayList urlSeries = new ArrayList(); /** * Default constructor. */ public CustomCategoryURLGenerator() { super(); } /** * Returns the number of URL lists stored by the renderer. * * @return The list count. */ public int getListCount() { return this.urlSeries.size(); } /** * Returns the number of URLs in a given list. * * @param list the list index (zero based). * * @return The URL count. */ public int getURLCount(int list) { int result = 0; List urls = (List) this.urlSeries.get(list); if (urls != null) { result = urls.size(); } return result; } /** * Returns the URL for an item. * * @param series the series index. * @param item the item index. * * @return The URL (possibly {@code null}). */ public String getURL(int series, int item) { String result = null; if (series < getListCount()) { List urls = (List) this.urlSeries.get(series); if (urls != null) { if (item < urls.size()) { result = (String) urls.get(item); } } } return result; } /** * Generates a URL. * * @param dataset the dataset (ignored in this implementation). * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return A string containing the URL (possibly {@code null}). */ @Override public String generateURL(CategoryDataset dataset, int series, int item) { return getURL(series, item); } /** * Adds a list of URLs. * * @param urls the list of URLs ({@code null} permitted). */ public void addURLSeries(List urls) { List listToAdd = null; if (urls != null) { listToAdd = new java.util.ArrayList(urls); } this.urlSeries.add(listToAdd); } /** * Tests if this object is equal to another. * * @param obj the other object. * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof CustomCategoryURLGenerator)) { return false; } CustomCategoryURLGenerator generator = (CustomCategoryURLGenerator) obj; int listCount = getListCount(); if (listCount != generator.getListCount()) { return false; } for (int series = 0; series < listCount; series++) { int urlCount = getURLCount(series); if (urlCount != generator.getURLCount(series)) { return false; } for (int item = 0; item < urlCount; item++) { String u1 = getURL(series, item); String u2 = generator.getURL(series, item); if (u1 != null) { if (!u1.equals(u2)) { return false; } } else { if (u2 != null) { return false; } } } } return true; } /** * Returns a new generator that is a copy of, and independent from, this * generator. * * @return A clone. * * @throws CloneNotSupportedException if there is a problem with cloning. */ @Override public Object clone() throws CloneNotSupportedException { CustomCategoryURLGenerator clone = (CustomCategoryURLGenerator) super.clone(); clone.urlSeries = new java.util.ArrayList(this.urlSeries); return clone; } }jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/urls/CustomPieURLGenerator.java000066400000000000000000000150071463604235500310530ustar00rootroot00000000000000/* ====================================== * JFreeChart : a free Java chart library * ====================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * CustomPieURLGenerator.java * -------------------------- * (C) Copyright 2004-present, by David Basten and Contributors. * * Original Author: David Basten; * Contributors: -; * */ package org.jfree.chart.urls; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.jfree.chart.plot.MultiplePiePlot; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.general.PieDataset; /** * A custom URL generator for pie charts. */ public class CustomPieURLGenerator implements PieURLGenerator, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 7100607670144900503L; /** Storage for the URLs. */ private ArrayList urls; /** * Creates a new {@code CustomPieURLGenerator} instance, initially * empty. Call {@link #addURLs(Map)} to specify the URL fragments to be * used. */ public CustomPieURLGenerator() { this.urls = new ArrayList(); } /** * Generates a URL fragment. * * @param dataset the dataset (ignored). * @param key the item key. * @param pieIndex the pie index. * * @return A string containing the generated URL. * * @see #getURL(Comparable, int) */ @Override public String generateURL(PieDataset dataset, Comparable key, int pieIndex) { return getURL(key, pieIndex); } /** * Returns the number of URL maps stored by the renderer. * * @return The list count. * * @see #addURLs(Map) */ public int getListCount() { return this.urls.size(); } /** * Returns the number of URLs in a given map (specified by its position * in the map list). * * @param list the list index (zero based). * * @return The URL count. * * @see #getListCount() */ public int getURLCount(int list) { int result = 0; Map urlMap = (Map) this.urls.get(list); if (urlMap != null) { result = urlMap.size(); } return result; } /** * Returns the URL for a section in the specified map. * * @param key the key. * @param mapIndex the map index. * * @return The URL. */ public String getURL(Comparable key, int mapIndex) { String result = null; if (mapIndex < getListCount()) { Map urlMap = (Map) this.urls.get(mapIndex); if (urlMap != null) { result = (String) urlMap.get(key); } } return result; } /** * Adds a map containing {@code (key, URL)} mappings where each * {@code key} is an instance of {@code Comparable} * (corresponding to the key for an item in a pie dataset) and each * {@code URL} is a {@code String} representing a URL fragment. *

* The map is appended to an internal list...you can add multiple maps * if you are working with, say, a {@link MultiplePiePlot}. * * @param urlMap the URLs ({@code null} permitted). */ public void addURLs(Map urlMap) { this.urls.add(urlMap); } /** * Tests if this object is equal to another. * * @param o the other object. * * @return A boolean. */ @Override public boolean equals(Object o) { if (o == this) { return true; } if (o instanceof CustomPieURLGenerator) { CustomPieURLGenerator generator = (CustomPieURLGenerator) o; if (getListCount() != generator.getListCount()) { return false; } Set keySet; for (int pieItem = 0; pieItem < getListCount(); pieItem++) { if (getURLCount(pieItem) != generator.getURLCount(pieItem)) { return false; } keySet = ((HashMap) this.urls.get(pieItem)).keySet(); String key; for (Iterator i = keySet.iterator(); i.hasNext();) { key = (String) i.next(); if (!getURL(key, pieItem).equals( generator.getURL(key, pieItem))) { return false; } } } return true; } return false; } /** * Returns a clone of the generator. * * @return A clone. * * @throws CloneNotSupportedException if cloning is not supported. */ @Override public Object clone() throws CloneNotSupportedException { CustomPieURLGenerator urlGen = new CustomPieURLGenerator(); Map map; Map newMap; String key; for (Iterator i = this.urls.iterator(); i.hasNext();) { map = (Map) i.next(); newMap = new HashMap(); for (Iterator j = map.keySet().iterator(); j.hasNext();) { key = (String) j.next(); newMap.put(key, map.get(key)); } urlGen.addURLs(newMap); } return urlGen; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/urls/CustomXYURLGenerator.java000066400000000000000000000134751463604235500307050ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * CustomXYURLGenerator.java * ------------------------- * (C) Copyright 2002-present, by Richard Atkinson and Contributors. * * Original Author: Richard Atkinson; * Contributors: David Gilbert; * */ package org.jfree.chart.urls; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.xy.XYDataset; /** * A custom URL generator. */ public class CustomXYURLGenerator implements XYURLGenerator, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -8565933356596551832L; /** Storage for the URLs. */ private ArrayList urlSeries = new ArrayList(); /** * Default constructor. */ public CustomXYURLGenerator() { super(); } /** * Returns the number of URL lists stored by the renderer. * * @return The list count. */ public int getListCount() { return this.urlSeries.size(); } /** * Returns the number of URLs in a given list. * * @param list the list index (zero based). * * @return The URL count. */ public int getURLCount(int list) { int result = 0; List urls = (List) this.urlSeries.get(list); if (urls != null) { result = urls.size(); } return result; } /** * Returns the URL for an item. * * @param series the series index. * @param item the item index. * * @return The URL (possibly {@code null}). */ public String getURL(int series, int item) { String result = null; if (series < getListCount()) { List urls = (List) this.urlSeries.get(series); if (urls != null) { if (item < urls.size()) { result = (String) urls.get(item); } } } return result; } /** * Generates a URL. * * @param dataset the dataset. * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return A string containing the URL (possibly {@code null}). */ @Override public String generateURL(XYDataset dataset, int series, int item) { return getURL(series, item); } /** * Adds a list of URLs. * * @param urls the list of URLs ({@code null} permitted, the list * is copied). */ public void addURLSeries(List urls) { List listToAdd = null; if (urls != null) { listToAdd = new java.util.ArrayList(urls); } this.urlSeries.add(listToAdd); } /** * Tests this generator for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof CustomXYURLGenerator)) { return false; } CustomXYURLGenerator that = (CustomXYURLGenerator) obj; int listCount = getListCount(); if (listCount != that.getListCount()) { return false; } for (int series = 0; series < listCount; series++) { int urlCount = getURLCount(series); if (urlCount != that.getURLCount(series)) { return false; } for (int item = 0; item < urlCount; item++) { String u1 = getURL(series, item); String u2 = that.getURL(series, item); if (u1 != null) { if (!u1.equals(u2)) { return false; } } else { if (u2 != null) { return false; } } } } return true; } /** * Returns a new generator that is a copy of, and independent from, this * generator. * * @return A clone. * * @throws CloneNotSupportedException if there is a problem with cloning. */ @Override public Object clone() throws CloneNotSupportedException { CustomXYURLGenerator clone = (CustomXYURLGenerator) super.clone(); clone.urlSeries = new java.util.ArrayList(this.urlSeries); return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/urls/PieURLGenerator.java000066400000000000000000000052671463604235500276670ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * PieURLGenerator.java * -------------------- * (C) Copyright 2002-present, by Richard Atkinson and Contributors. * * Original Author: Richard Atkinson; * Contributors: David Gilbert; * */ package org.jfree.chart.urls; import org.jfree.data.general.PieDataset; /** * Interface for a URL generator for plots that use data from a * {@link PieDataset}. Classes that implement this interface: *

    *
  • are responsible for correctly escaping any text that is derived from the * dataset, as this may be user-specified and could pose a security * risk;
  • *
  • should be either (a) immutable, or (b) cloneable via the * {@code PublicCloneable} interface (defined in the JCommon class * library). This provides a mechanism for the referring plot to clone * the generator if necessary.
  • *
*/ public interface PieURLGenerator { /** * Generates a URL for one item in a {@link PieDataset}. As a guideline, * the URL should be valid within the context of an XHTML 1.0 document. * * @param dataset the dataset ({@code null} not permitted). * @param key the item key ({@code null} not permitted). * @param pieIndex the pie index (differentiates between pies in a * 'multi' pie chart). * * @return A string containing the URL. */ String generateURL(PieDataset dataset, Comparable key, int pieIndex); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/urls/StandardCategoryURLGenerator.java000066400000000000000000000155031463604235500324020ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------------- * StandardCategoryURLGenerator.java * --------------------------------- * (C) Copyright 2002-present, by Richard Atkinson and Contributors. * * Original Author: Richard Atkinson; * Contributors: David Gilbert; * Cleland Early; * */ package org.jfree.chart.urls; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.Objects; import org.jfree.chart.util.Args; import org.jfree.data.category.CategoryDataset; /** * A URL generator that can be assigned to a * {@link org.jfree.chart.renderer.category.CategoryItemRenderer}. */ public class StandardCategoryURLGenerator implements CategoryURLGenerator, Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 2276668053074881909L; /** Prefix to the URL */ private String prefix = "index.html"; /** Series parameter name to go in each URL */ private String seriesParameterName = "series"; /** Category parameter name to go in each URL */ private String categoryParameterName = "category"; /** * Creates a new generator with default settings. */ public StandardCategoryURLGenerator() { super(); } /** * Constructor that overrides default prefix to the URL. * * @param prefix the prefix to the URL ({@code null} not permitted). */ public StandardCategoryURLGenerator(String prefix) { Args.nullNotPermitted(prefix, "prefix"); this.prefix = prefix; } /** * Constructor that overrides all the defaults. * * @param prefix the prefix to the URL ({@code null} not permitted). * @param seriesParameterName the name of the series parameter to go in * each URL ({@code null} not permitted). * @param categoryParameterName the name of the category parameter to go in * each URL ({@code null} not permitted). */ public StandardCategoryURLGenerator(String prefix, String seriesParameterName, String categoryParameterName) { Args.nullNotPermitted(prefix, "prefix"); Args.nullNotPermitted(seriesParameterName, "seriesParameterName"); Args.nullNotPermitted(categoryParameterName, "categoryParameterName"); this.prefix = prefix; this.seriesParameterName = seriesParameterName; this.categoryParameterName = categoryParameterName; } /** * Generates a URL for a particular item within a series. * * @param dataset the dataset. * @param series the series index (zero-based). * @param category the category index (zero-based). * * @return The generated URL. */ @Override public String generateURL(CategoryDataset dataset, int series, int category) { String url = this.prefix; Comparable seriesKey = dataset.getRowKey(series); Comparable categoryKey = dataset.getColumnKey(category); boolean firstParameter = !url.contains("?"); url += firstParameter ? "?" : "&"; try { url += this.seriesParameterName + "=" + URLEncoder.encode( seriesKey.toString(), "UTF-8"); url += "&" + this.categoryParameterName + "=" + URLEncoder.encode(categoryKey.toString(), "UTF-8"); } catch (UnsupportedEncodingException ex) { throw new RuntimeException(ex); // this won't happen :) } return url; } /** * Returns an independent copy of the URL generator. * * @return A clone. * * @throws CloneNotSupportedException not thrown by this class, but * subclasses (if any) might. */ @Override public Object clone() throws CloneNotSupportedException { // all attributes are immutable, so we can just return the super.clone() // FIXME: in fact, the generator itself is immutable, so cloning is // not necessary return super.clone(); } /** * Tests the generator for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof StandardCategoryURLGenerator)) { return false; } StandardCategoryURLGenerator that = (StandardCategoryURLGenerator) obj; if (!Objects.equals(this.prefix, that.prefix)) { return false; } if (!Objects.equals(this.seriesParameterName, that.seriesParameterName)) { return false; } if (!Objects.equals(this.categoryParameterName, that.categoryParameterName)) { return false; } return true; } /** * Returns a hash code. * * @return A hash code. */ @Override public int hashCode() { int result; result = (this.prefix != null ? this.prefix.hashCode() : 0); result = 29 * result + (this.seriesParameterName != null ? this.seriesParameterName.hashCode() : 0); result = 29 * result + (this.categoryParameterName != null ? this.categoryParameterName.hashCode() : 0); return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/urls/StandardPieURLGenerator.java000066400000000000000000000126551463604235500313470ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------- * StandardPieURLGenerator.java * ---------------------------- * (C) Copyright 2002-present, by Richard Atkinson and Contributors. * * Original Author: Richard Atkinson; * Contributors: David Gilbert; * */ package org.jfree.chart.urls; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.Objects; import org.jfree.chart.util.Args; import org.jfree.data.general.PieDataset; /** * A URL generator for pie charts. Instances of this class are immutable. */ public class StandardPieURLGenerator implements PieURLGenerator, Serializable { /** For serialization. */ private static final long serialVersionUID = 1626966402065883419L; /** The prefix. */ private String prefix = "index.html"; /** The category parameter name. */ private String categoryParamName = "category"; /** The pie index parameter name. */ private String indexParamName = "pieIndex"; /** * Default constructor. */ public StandardPieURLGenerator() { this("index.html"); } /** * Creates a new generator. * * @param prefix the prefix ({@code null} not permitted). */ public StandardPieURLGenerator(String prefix) { this(prefix, "category"); } /** * Creates a new generator. * * @param prefix the prefix ({@code null} not permitted). * @param categoryParamName the category parameter name ({@code null} not * permitted). */ public StandardPieURLGenerator(String prefix, String categoryParamName) { this(prefix, categoryParamName, "pieIndex"); } /** * Creates a new generator. * * @param prefix the prefix ({@code null} not permitted). * @param categoryParamName the category parameter name ({@code null} not * permitted). * @param indexParamName the index parameter name ({@code null} permitted). */ public StandardPieURLGenerator(String prefix, String categoryParamName, String indexParamName) { Args.nullNotPermitted(prefix, "prefix"); Args.nullNotPermitted(categoryParamName, "categoryParamName"); this.prefix = prefix; this.categoryParamName = categoryParamName; this.indexParamName = indexParamName; } /** * Generates a URL. * * @param dataset the dataset (ignored). * @param key the item key ({@code null} not permitted). * @param pieIndex the pie index. * * @return A string containing the generated URL. */ @Override public String generateURL(PieDataset dataset, Comparable key, int pieIndex) { String url = this.prefix; try { if (url.contains("?")) { url += "&" + this.categoryParamName + "=" + URLEncoder.encode(key.toString(), "UTF-8"); } else { url += "?" + this.categoryParamName + "=" + URLEncoder.encode(key.toString(), "UTF-8"); } if (this.indexParamName != null) { url += "&" + this.indexParamName + "=" + pieIndex; } } catch (UnsupportedEncodingException e) { // this won't happen :) throw new RuntimeException(e); } return url; } /** * Tests if this object is equal to another. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof StandardPieURLGenerator)) { return false; } StandardPieURLGenerator that = (StandardPieURLGenerator) obj; if (!this.prefix.equals(that.prefix)) { return false; } if (!this.categoryParamName.equals(that.categoryParamName)) { return false; } if (!Objects.equals(this.indexParamName, that.indexParamName)) { return false; } return true; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/urls/StandardXYURLGenerator.java000066400000000000000000000135001463604235500311600ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * StandardXYURLGenerator.java * --------------------------- * (C) Copyright 2002-present, by Richard Atkinson and Contributors. * * Original Author: Richard Atkinson; * Contributors: David Gilbert; * */ package org.jfree.chart.urls; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.util.Args; import org.jfree.data.xy.XYDataset; /** * A URL generator. */ public class StandardXYURLGenerator implements XYURLGenerator, Serializable { /** For serialization. */ private static final long serialVersionUID = -1771624523496595382L; /** The default prefix. */ public static final String DEFAULT_PREFIX = "index.html"; /** The default series parameter. */ public static final String DEFAULT_SERIES_PARAMETER = "series"; /** The default item parameter. */ public static final String DEFAULT_ITEM_PARAMETER = "item"; /** Prefix to the URL */ private String prefix; /** Series parameter name to go in each URL */ private String seriesParameterName; /** Item parameter name to go in each URL */ private String itemParameterName; /** * Creates a new default generator. This constructor is equivalent to * calling {@code StandardXYURLGenerator("index.html", "series", "item");}. */ public StandardXYURLGenerator() { this(DEFAULT_PREFIX, DEFAULT_SERIES_PARAMETER, DEFAULT_ITEM_PARAMETER); } /** * Creates a new generator with the specified prefix. This constructor * is equivalent to calling * {@code StandardXYURLGenerator(prefix, "series", "item");}. * * @param prefix the prefix to the URL ({@code null} not permitted). */ public StandardXYURLGenerator(String prefix) { this(prefix, DEFAULT_SERIES_PARAMETER, DEFAULT_ITEM_PARAMETER); } /** * Constructor that overrides all the defaults * * @param prefix the prefix to the URL ({@code null} not permitted). * @param seriesParameterName the name of the series parameter to go in * each URL ({@code null} not permitted). * @param itemParameterName the name of the item parameter to go in each * URL ({@code null} not permitted). */ public StandardXYURLGenerator(String prefix, String seriesParameterName, String itemParameterName) { Args.nullNotPermitted(prefix, "prefix"); Args.nullNotPermitted(seriesParameterName, "seriesParameterName"); Args.nullNotPermitted(itemParameterName, "itemParameterName"); this.prefix = prefix; this.seriesParameterName = seriesParameterName; this.itemParameterName = itemParameterName; } /** * Generates a URL for a particular item within a series. * * @param dataset the dataset. * @param series the series number (zero-based index). * @param item the item number (zero-based index). * * @return The generated URL. */ @Override public String generateURL(XYDataset dataset, int series, int item) { // TODO: URLEncode? String url = this.prefix; boolean firstParameter = !url.contains("?"); url += firstParameter ? "?" : "&"; url += this.seriesParameterName + "=" + series + "&" + this.itemParameterName + "=" + item; return url; } /** * Tests this generator for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof StandardXYURLGenerator)) { return false; } StandardXYURLGenerator that = (StandardXYURLGenerator) obj; if (!Objects.equals(that.prefix, this.prefix)) { return false; } if (!Objects.equals(that.seriesParameterName, this.seriesParameterName)) { return false; } if (!Objects.equals(that.itemParameterName, this.itemParameterName)) { return false; } return true; } @Override public int hashCode() { int hash = 7; hash = 79 * hash + Objects.hashCode(this.prefix); hash = 79 * hash + Objects.hashCode(this.seriesParameterName); hash = 79 * hash + Objects.hashCode(this.itemParameterName); return hash; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/urls/StandardXYZURLGenerator.java000066400000000000000000000041501463604235500313130ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------- * StandardXYZURLGenerator.java * ---------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributors: -; * */ package org.jfree.chart.urls; import org.jfree.data.xy.XYZDataset; /** * A URL generator. */ public class StandardXYZURLGenerator extends StandardXYURLGenerator implements XYZURLGenerator { /** * Generates a URL for a particular item within a series. * * @param dataset the dataset. * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return A string containing the generated URL. */ @Override public String generateURL(XYZDataset dataset, int series, int item) { return super.generateURL(dataset, series, item); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/urls/TimeSeriesURLGenerator.java000066400000000000000000000152721463604235500312200ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * TimeSeriesURLGenerator.java * --------------------------- * (C) Copyright 2002-present, by Richard Atkinson and Contributors. * * Original Author: Richard Atkinson; * Contributors: David Gilbert; * */ package org.jfree.chart.urls; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.text.DateFormat; import java.util.Date; import org.jfree.chart.util.Args; import org.jfree.data.xy.XYDataset; /** * A URL generator for time series charts. */ public class TimeSeriesURLGenerator implements XYURLGenerator, Serializable { /** For serialization. */ private static final long serialVersionUID = -9122773175671182445L; /** A formatter for the date. */ private DateFormat dateFormat = DateFormat.getInstance(); /** Prefix to the URL */ private String prefix = "index.html"; /** Name to use to identify the series */ private String seriesParameterName = "series"; /** Name to use to identify the item */ private String itemParameterName = "item"; /** * Default constructor. */ public TimeSeriesURLGenerator() { super(); } /** * Construct TimeSeriesURLGenerator overriding defaults. * * @param dateFormat a formatter for the date ({@code null} not * permitted). * @param prefix the prefix of the URL ({@code null} not permitted). * @param seriesParameterName the name of the series parameter in the URL * ({@code null} not permitted). * @param itemParameterName the name of the item parameter in the URL * ({@code null} not permitted). */ public TimeSeriesURLGenerator(DateFormat dateFormat, String prefix, String seriesParameterName, String itemParameterName) { Args.nullNotPermitted(dateFormat, "dateFormat"); Args.nullNotPermitted(prefix, "prefix"); Args.nullNotPermitted(seriesParameterName, "seriesParameterName"); Args.nullNotPermitted(itemParameterName, "itemParameterName"); this.dateFormat = (DateFormat) dateFormat.clone(); this.prefix = prefix; this.seriesParameterName = seriesParameterName; this.itemParameterName = itemParameterName; } /** * Returns a clone of the date format assigned to this URL generator. * * @return The date format (never {@code null}). */ public DateFormat getDateFormat() { return (DateFormat) this.dateFormat.clone(); } /** * Returns the prefix string. * * @return The prefix string (never {@code null}). */ public String getPrefix() { return this.prefix; } /** * Returns the series parameter name. * * @return The series parameter name (never {@code null}). */ public String getSeriesParameterName() { return this.seriesParameterName; } /** * Returns the item parameter name. * * @return The item parameter name (never {@code null}). */ public String getItemParameterName() { return this.itemParameterName; } /** * Generates a URL for a particular item within a series. * * @param dataset the dataset ({@code null} not permitted). * @param series the series number (zero-based index). * @param item the item number (zero-based index). * * @return The generated URL. */ @Override public String generateURL(XYDataset dataset, int series, int item) { String result = this.prefix; boolean firstParameter = !result.contains("?"); Comparable seriesKey = dataset.getSeriesKey(series); if (seriesKey != null) { result += firstParameter ? "?" : "&"; try { result += this.seriesParameterName + "=" + URLEncoder.encode( seriesKey.toString(), "UTF-8"); } catch (UnsupportedEncodingException ex) { throw new RuntimeException(ex); } firstParameter = false; } long x = (long) dataset.getXValue(series, item); String xValue = this.dateFormat.format(new Date(x)); result += firstParameter ? "?" : "&"; try { result += this.itemParameterName + "=" + URLEncoder.encode(xValue, "UTF-8"); } catch (UnsupportedEncodingException ex) { throw new RuntimeException(ex); } return result; } /** * Tests this generator for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof TimeSeriesURLGenerator)) { return false; } TimeSeriesURLGenerator that = (TimeSeriesURLGenerator) obj; if (!this.dateFormat.equals(that.dateFormat)) { return false; } if (!this.itemParameterName.equals(that.itemParameterName)) { return false; } if (!this.prefix.equals(that.prefix)) { return false; } if (!this.seriesParameterName.equals(that.seriesParameterName)) { return false; } return true; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/urls/XYURLGenerator.java000066400000000000000000000045431463604235500275060ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * XYURLGenerator.java * ------------------- * (C) Copyright 2002-present, by Richard Atkinson and Contributors. * * Original Author: Richard Atkinson; * Contributors: David Gilbert; * */ package org.jfree.chart.urls; import org.jfree.data.xy.XYDataset; /** * Interface for a URL generator for plots that uses data from an * {@link XYDataset}. Classes that implement this interface are responsible * for correctly escaping any text that is derived from the dataset, as this * may be user-specified and could pose a security risk. */ public interface XYURLGenerator { /** * Generates a URL for a particular item within a series. As a guideline, * the URL should be valid within the context of an XHTML 1.0 document. * * @param dataset the dataset ({@code null} not permitted). * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return A string containing the generated URL (possibly * {@code null}). */ String generateURL(XYDataset dataset, int series, int item); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/urls/XYZURLGenerator.java000066400000000000000000000045071463604235500276400ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * XYZURLGenerator.java * -------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributors: -; * */ package org.jfree.chart.urls; import org.jfree.data.xy.XYZDataset; /** * Interface for a URL generator for plots that uses data from an * {@link XYZDataset}. Classes that implement this interface are responsible * for correctly escaping any text that is derived from the dataset, as this * may be user-specified and could pose a security risk. */ public interface XYZURLGenerator extends XYURLGenerator { /** * Generates a URL for a particular item within a series. As a guideline, * the URL should be valid within the context of an XHTML 1.0 document. * * @param dataset the dataset ({@code null} not permitted). * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return A string containing the generated URL. */ String generateURL(XYZDataset dataset, int series, int item); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/urls/package.html000066400000000000000000000002551463604235500263260ustar00rootroot00000000000000 Classes for adding URLS to charts for HTML image map generation. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/000077500000000000000000000000001463604235500240335ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/AbstractObjectList.java000066400000000000000000000164571463604235500304410ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.util; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Arrays; import java.util.Objects; /** * A list of objects that can grow as required. */ public class AbstractObjectList implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 7789833772597351595L; /** The default initial capacity of the list. */ public static final int DEFAULT_INITIAL_CAPACITY = 8; /** Storage for the objects. */ private transient Object[] objects; /** The current list size. */ private int size = 0; /** The default increment. */ private int increment = DEFAULT_INITIAL_CAPACITY; /** * Creates a new list with the default initial capacity. */ protected AbstractObjectList() { this(DEFAULT_INITIAL_CAPACITY); } /** * Creates a new list. * * @param initialCapacity the initial capacity. */ protected AbstractObjectList(int initialCapacity) { this (initialCapacity, initialCapacity); } /** * Creates a new list. * * @param initialCapacity the initial capacity. * @param increment the increment. */ protected AbstractObjectList(int initialCapacity, int increment) { this.objects = new Object[initialCapacity]; this.increment = increment; } /** * Returns the object at the specified index, if there is one, or * {@code null}. * * @param index the object index. * * @return The object or {@code null}. */ protected Object get(int index) { Object result = null; if (index >= 0 && index < this.size) { result = this.objects[index]; } return result; } /** * Sets an object reference (overwriting any existing object). * * @param index the object index. * @param object the object ({@code null} permitted). */ protected void set(int index, Object object) { if (index < 0) { throw new IllegalArgumentException("Requires index >= 0."); } if (index >= this.objects.length) { Object[] enlarged = new Object[index + this.increment]; System.arraycopy(this.objects, 0, enlarged, 0, this.objects.length); this.objects = enlarged; } this.objects[index] = object; this.size = Math.max(this.size, index + 1); } /** * Clears the list. */ public void clear() { Arrays.fill(this.objects, null); this.size = 0; } /** * Returns the size of the list. * * @return The size of the list. */ public int size() { return this.size; } /** * Returns the index of the specified object, or -1 if the object is not in * the list. * * @param object the object. * * @return The index or -1. */ protected int indexOf(Object object) { for (int index = 0; index < this.size; index++) { if (this.objects[index] == object) { return (index); } } return -1; } /** * Tests this list for equality with another object. * * @param obj the object to test. * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (obj == this) { return true; } if (!(obj instanceof AbstractObjectList)) { return false; } final AbstractObjectList other = (AbstractObjectList) obj; final int listSize = size(); for (int i = 0; i < listSize; i++) { if (!Objects.equals(get(i), other.get(i))) { return false; } } return true; } /** * Returns a hash code value for the object. * * @return the hashcode */ @Override public int hashCode() { return Arrays.hashCode(objects); } /** * Clones the list of objects. The objects in the list are not cloned, so * this is method makes a 'shallow' copy of the list. * * @return A clone. * * @throws CloneNotSupportedException if an item in the list does not * support cloning. */ @Override public Object clone() throws CloneNotSupportedException { final AbstractObjectList clone = (AbstractObjectList) super.clone(); if (this.objects != null) { clone.objects = new Object[this.objects.length]; System.arraycopy( this.objects, 0, clone.objects, 0, this.objects.length ); } return clone; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); final int count = size(); stream.writeInt(count); for (int i = 0; i < count; i++) { final Object object = get(i); if (object != null && object instanceof Serializable) { stream.writeInt(i); stream.writeObject(object); } else { stream.writeInt(-1); } } } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.objects = new Object[this.size]; final int count = stream.readInt(); for (int i = 0; i < count; i++) { final int index = stream.readInt(); if (index != -1) { set(index, stream.readObject()); } } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/Args.java000066400000000000000000000103431463604235500255730ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------- * Args.java * --------- * (C) Copyright 2011-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.util; /** * A utility class for checking method arguments. */ public class Args { /** * Throws an {@code IllegalArgumentException} if the supplied * {@code param} is {@code null}. * * @param param the parameter to check ({@code null} permitted). * @param name the name of the parameter (to use in the exception message * if {@code param} is {@code null}). * * @throws IllegalArgumentException if {@code param} is * {@code null}. */ public static void nullNotPermitted(Object param, String name) { if (param == null) { throw new IllegalArgumentException("Null '" + name + "' argument."); } } /** * Throws an {@code IllegalArgumentException} if {@code value} is negative. * * @param value the value. * @param name the parameter name (for use in the exception message). */ public static void requireNonNegative(int value, String name) { if (value < 0) { throw new IllegalArgumentException("Require '" + name + "' (" + value + ") to be non-negative."); } } /** * Throws an {@code IllegalArgumentException} if {@code value} is negative. * * @param value the value. * @param name the parameter name (for use in the exception message). */ public static void requireNonNegative(double value, String name) { if (value < 0) { throw new IllegalArgumentException("Require '" + name + "' (" + value + ") to be non-negative."); } } /** * Checks that the value falls within the specified range and, if it does * not, throws an {@code IllegalArgumentException}. * * @param value the value. * @param name the parameter name. * @param lowerBound the lower bound of the permitted range. * @param upperBound the upper bound fo the permitted range. */ public static void requireInRange(int value, String name, int lowerBound, int upperBound) { if (value < lowerBound || value > upperBound) { throw new IllegalArgumentException("Require '" + name + "' (" + value + ") to be in the range " + lowerBound + " to " + upperBound); } } /** * Checks the supplied value is finite (neither infinite nor NaN) and * throws an {@code IllegalArgumentException} if the requirement is not * met. * * @param value the value. * @param name the parameter name (for use in the exception message). * * @since 1.5.4 */ public static void requireFinite(double value, String name) { if (!Double.isFinite(value)) { throw new IllegalArgumentException("Require '" + name + "' (" + value + ") to be finite."); } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/ArrayUtils.java000066400000000000000000000131141463604235500267750ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * ArrayUtils.java * --------------- * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributors: -; */ package org.jfree.chart.util; import java.util.Arrays; /** * Utility methods for working with arrays. */ public class ArrayUtils { /** * Private constructor prevents object creation. */ private ArrayUtils() { } /** * Clones a two dimensional array of floats. * * @param array the array. * * @return A clone of the array. */ public static float[][] clone(float[][] array) { if (array == null) { return null; } float[][] result = new float[array.length][]; System.arraycopy(array, 0, result, 0, array.length); for (int i = 0; i < array.length; i++) { float[] child = array[i]; float[] copychild = new float[child.length]; System.arraycopy(child, 0, copychild, 0, child.length); result[i] = copychild; } return result; } /** * Returns {@code true} if all the references in {@code array1} * are equal to all the references in {@code array2} (two * {@code null} references are considered equal for this test). * * @param array1 the first array ({@code null} permitted). * @param array2 the second array ({@code null} permitted). * * @return A boolean. */ public static boolean equalReferencesInArrays(Object[] array1, Object[] array2) { if (array1 == null) { return (array2 == null); } if (array2 == null) { return false; } if (array1.length != array2.length) { return false; } for (int i = 0; i < array1.length; i++) { if (array1[i] == null) { if (array2[i] != null) { return false; } } if (array2[i] == null) { if (array1[i] != null) { return false; } } if (array1[i] != array2[i]) { return false; } } return true; } /** * Tests two float arrays for equality. * * @param array1 the first array ({@code null} permitted). * @param array2 the second arrray ({@code null} permitted). * * @return A boolean. */ public static boolean equal(float[][] array1, float[][] array2) { if (array1 == null) { return (array2 == null); } if (array2 == null) { return false; } if (array1.length != array2.length) { return false; } for (int i = 0; i < array1.length; i++) { if (!Arrays.equals(array1[i], array2[i])) { return false; } } return true; } /** * Returns {@code true} if any two items in the array are equal to * one another. Any {@code null} values in the array are ignored. * * @param array the array to check. * * @return A boolean. */ public static boolean hasDuplicateItems(Object[] array) { for (int i = 0; i < array.length; i++) { for (int j = 0; j < i; j++) { Object o1 = array[i]; Object o2 = array[j]; if (o1 != null && o2 != null) { if (o1.equals(o2)) { return true; } } } } return false; } /** * Compares the initial elements of two arrays. * * @param a1 array 1. * @param a2 array 2. * * @return An integer showing the relative ordering. */ public static int compareVersionArrays (Comparable[] a1, Comparable[] a2) { int length = Math.min (a1.length, a2.length); for (int i = 0; i < length; i++) { Comparable o1 = a1[i]; Comparable o2 = a2[i]; if (o1 == null && o2 == null) { // cannot decide .. continue; } if (o1 == null) { return 1; } if (o2 == null) { return -1; } int retval = o1.compareTo(o2); if (retval != 0) { return retval; } } return 0; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/AttrStringUtils.java000066400000000000000000000267251463604235500300340ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * AttrStringUtils.java * -------------------- * (C) Copyright 2013-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.util; import java.awt.Graphics2D; import java.awt.font.TextLayout; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.text.AttributedString; import org.jfree.chart.ui.TextAnchor; /** * Some {@code AttributedString} utilities. */ public class AttrStringUtils { private AttrStringUtils() { // no need to instantiate this class } /** * Returns the bounds for the attributed string. * * @param text the attributed string ({@code null} not permitted). * @param g2 the graphics target ({@code null} not permitted). * * @return The bounds (never {@code null}). */ public static Rectangle2D getTextBounds(AttributedString text, Graphics2D g2) { TextLayout tl = new TextLayout(text.getIterator(), g2.getFontRenderContext()); return tl.getBounds(); } /** * Draws the attributed string at {@code (x, y)}, rotated by the * specified angle about {@code (x, y)}. * * @param text the attributed string ({@code null} not permitted). * @param g2 the graphics output target. * @param angle the angle. * @param x the x-coordinate. * @param y the y-coordinate. */ public static void drawRotatedString(AttributedString text, Graphics2D g2, double angle, float x, float y) { drawRotatedString(text, g2, x, y, angle, x, y); } /** * Draws the attributed string at {@code (textX, textY)}, rotated by * the specified angle about {@code (rotateX, rotateY)}. * * @param text the attributed string ({@code null} not permitted). * @param g2 the graphics output target. * @param textX the x-coordinate for the text. * @param textY the y-coordinate for the text. * @param angle the rotation angle (in radians). * @param rotateX the x-coordinate for the rotation point. * @param rotateY the y-coordinate for the rotation point. */ public static void drawRotatedString(AttributedString text, Graphics2D g2, float textX, float textY, double angle, float rotateX, float rotateY) { Args.nullNotPermitted(text, "text"); AffineTransform saved = g2.getTransform(); AffineTransform rotate = AffineTransform.getRotateInstance(angle, rotateX, rotateY); g2.transform(rotate); TextLayout tl = new TextLayout(text.getIterator(), g2.getFontRenderContext()); tl.draw(g2, textX, textY); g2.setTransform(saved); } /** * Draws the string anchored to {@code (x, y)}, rotated by the * specified angle about {@code (rotationX, rotationY)}. * * @param text the text ({@code null} not permitted). * @param g2 the graphics target. * @param x the x-coordinate for the text location. * @param y the y-coordinate for the text location. * @param textAnchor the text anchor point. * @param angle the rotation (in radians). * @param rotationX the x-coordinate for the rotation point. * @param rotationY the y-coordinate for the rotation point. */ public static void drawRotatedString(AttributedString text, Graphics2D g2, float x, float y, TextAnchor textAnchor, double angle, float rotationX, float rotationY) { Args.nullNotPermitted(text, "text"); float[] textAdj = deriveTextBoundsAnchorOffsets(g2, text, textAnchor, null); drawRotatedString(text, g2, x + textAdj[0], y + textAdj[1], angle, rotationX, rotationY); } /** * Draws a rotated string. * * @param text the text to draw. * @param g2 the graphics target. * @param x the x-coordinate for the text location. * @param y the y-coordinate for the text location. * @param textAnchor the text anchor point. * @param angle the rotation (in radians). * @param rotationAnchor the rotation anchor point. */ public static void drawRotatedString(AttributedString text, Graphics2D g2, float x, float y, TextAnchor textAnchor, double angle, TextAnchor rotationAnchor) { Args.nullNotPermitted(text, "text"); float[] textAdj = deriveTextBoundsAnchorOffsets(g2, text, textAnchor, null); float[] rotateAdj = deriveRotationAnchorOffsets(g2, text, rotationAnchor); drawRotatedString(text, g2, x + textAdj[0], y + textAdj[1], angle, x + textAdj[0] + rotateAdj[0], y + textAdj[1] + rotateAdj[1]); } private static float[] deriveTextBoundsAnchorOffsets(Graphics2D g2, AttributedString text, TextAnchor anchor, Rectangle2D textBounds) { TextLayout layout = new TextLayout(text.getIterator(), g2.getFontRenderContext()); Rectangle2D bounds = layout.getBounds(); float[] result = new float[3]; float ascent = layout.getAscent(); result[2] = -ascent; float halfAscent = ascent / 2.0f; float descent = layout.getDescent(); float leading = layout.getLeading(); float xAdj = 0.0f; float yAdj = 0.0f; if (isHorizontalCenter(anchor)) { xAdj = (float) -bounds.getWidth() / 2.0f; } else if (isHorizontalRight(anchor)) { xAdj = (float) -bounds.getWidth(); } if (isTop(anchor)) { //yAdj = -descent - leading + (float) bounds.getHeight(); yAdj = (float) bounds.getHeight(); } else if (isHalfAscent(anchor)) { yAdj = halfAscent; } else if (isHalfHeight(anchor)) { yAdj = -descent - leading + (float) (bounds.getHeight() / 2.0); } else if (isBaseline(anchor)) { yAdj = 0.0f; } else if (isBottom(anchor)) { yAdj = -descent - leading; } if (textBounds != null) { textBounds.setRect(bounds); } result[0] = xAdj; result[1] = yAdj; return result; } /** * A utility method that calculates the rotation anchor offsets for a * string. These offsets are relative to the text starting coordinate * (BASELINE_LEFT). * * @param g2 the graphics device. * @param text the text. * @param anchor the anchor point. * * @return The offsets. */ private static float[] deriveRotationAnchorOffsets(Graphics2D g2, AttributedString text, TextAnchor anchor) { float[] result = new float[2]; TextLayout layout = new TextLayout(text.getIterator(), g2.getFontRenderContext()); Rectangle2D bounds = layout.getBounds(); float ascent = layout.getAscent(); float halfAscent = ascent / 2.0f; float descent = layout.getDescent(); float leading = layout.getLeading(); float xAdj = 0.0f; float yAdj = 0.0f; if (isHorizontalLeft(anchor)) { xAdj = 0.0f; } else if (isHorizontalCenter(anchor)) { xAdj = (float) bounds.getWidth() / 2.0f; } else if (isHorizontalRight(anchor)) { xAdj = (float) bounds.getWidth(); } if (isTop(anchor)) { yAdj = descent + leading - (float) bounds.getHeight(); } else if (isHalfHeight(anchor)) { yAdj = descent + leading - (float) (bounds.getHeight() / 2.0); } else if (isHalfAscent(anchor)) { yAdj = -halfAscent; } else if (isBaseline(anchor)) { yAdj = 0.0f; } else if (isBottom(anchor)) { yAdj = descent + leading; } result[0] = xAdj; result[1] = yAdj; return result; } private static boolean isTop(TextAnchor anchor) { return anchor.equals(TextAnchor.TOP_LEFT) || anchor.equals(TextAnchor.TOP_CENTER) || anchor.equals(TextAnchor.TOP_RIGHT); } private static boolean isBaseline(TextAnchor anchor) { return anchor.equals(TextAnchor.BASELINE_LEFT) || anchor.equals(TextAnchor.BASELINE_CENTER) || anchor.equals(TextAnchor.BASELINE_RIGHT); } private static boolean isHalfAscent(TextAnchor anchor) { return anchor.equals(TextAnchor.HALF_ASCENT_LEFT) || anchor.equals(TextAnchor.HALF_ASCENT_CENTER) || anchor.equals(TextAnchor.HALF_ASCENT_RIGHT); } private static boolean isHalfHeight(TextAnchor anchor) { return anchor.equals(TextAnchor.CENTER_LEFT) || anchor.equals(TextAnchor.CENTER) || anchor.equals(TextAnchor.CENTER_RIGHT); } private static boolean isBottom(TextAnchor anchor) { return anchor.equals(TextAnchor.BOTTOM_LEFT) || anchor.equals(TextAnchor.BOTTOM_CENTER) || anchor.equals(TextAnchor.BOTTOM_RIGHT); } private static boolean isHorizontalLeft(TextAnchor anchor) { return anchor.equals(TextAnchor.TOP_LEFT) || anchor.equals(TextAnchor.CENTER_LEFT) || anchor.equals(TextAnchor.HALF_ASCENT_LEFT) || anchor.equals(TextAnchor.BASELINE_LEFT) || anchor.equals(TextAnchor.BOTTOM_LEFT); } private static boolean isHorizontalCenter(TextAnchor anchor) { return anchor.equals(TextAnchor.TOP_CENTER) || anchor.equals(TextAnchor.CENTER) || anchor.equals(TextAnchor.HALF_ASCENT_CENTER) || anchor.equals(TextAnchor.BASELINE_CENTER) || anchor.equals(TextAnchor.BOTTOM_CENTER); } private static boolean isHorizontalRight(TextAnchor anchor) { return anchor.equals(TextAnchor.TOP_RIGHT) || anchor.equals(TextAnchor.CENTER_RIGHT) || anchor.equals(TextAnchor.HALF_ASCENT_RIGHT) || anchor.equals(TextAnchor.BASELINE_RIGHT) || anchor.equals(TextAnchor.BOTTOM_RIGHT); } }jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/BooleanList.java000066400000000000000000000053301463604235500271120ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * BooleanList.java * ---------------- * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributors: -; */ package org.jfree.chart.util; /** * A list of {@code Boolean} objects. */ public class BooleanList extends AbstractObjectList { /** For serialization. */ private static final long serialVersionUID = -8543170333219422042L; /** * Creates a new list. */ public BooleanList() { } /** * Returns a {@link Boolean} from the list. * * @param index the index (zero-based). * * @return a {@link Boolean} from the list. */ public Boolean getBoolean(int index) { return (Boolean) get(index); } /** * Sets the value for an item in the list. The list is expanded if * necessary. * * @param index the index (zero-based). * @param b the boolean. */ public void setBoolean(int index, Boolean b) { set(index, b); } /** * Tests the list for equality with another object (typically also a list). * * @param o the other object. * * @return A boolean. */ @Override public boolean equals(Object o) { if (o instanceof BooleanList) { return super.equals(o); } return false; } /** * Returns a hash code value for the object. * * @return the hashcode */ @Override public int hashCode() { return super.hashCode(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/CloneUtils.java000066400000000000000000000114721463604235500267640ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * CloneUtils.java * --------------- * (C) Copyright 2014-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.util; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Utilities for cloning. */ public class CloneUtils { /** * Returns a clone of the specified object, if it can be cloned, otherwise * throws a {@code CloneNotSupportedException}. * * @param object the object to clone ({@code null} not permitted). * @return A clone of the specified object. * @throws CloneNotSupportedException if the object cannot be cloned. */ public static Object clone(Object object) throws CloneNotSupportedException { if (object == null) { throw new IllegalArgumentException("Null 'object' argument."); } if (object instanceof PublicCloneable) { PublicCloneable pc = (PublicCloneable) object; return pc.clone(); } else { try { Method method = object.getClass().getMethod("clone", (Class[]) null); if (Modifier.isPublic(method.getModifiers())) { return method.invoke(object, (Object[]) null); } } catch (NoSuchMethodException e) { throw new CloneNotSupportedException("Object without clone() method is impossible."); } catch (IllegalAccessException e) { throw new CloneNotSupportedException("Object.clone(): unable to call method."); } catch (InvocationTargetException e) { throw new CloneNotSupportedException("Object without clone() method is impossible."); } } throw new CloneNotSupportedException("Failed to clone."); } /** * Returns a list containing cloned copies of the items in the source * list. * * @param source the source list ({@code null} not permitted). * * @return A new list. */ public static List cloneList(List source) { Args.nullNotPermitted(source, "source"); List result = new ArrayList(); for (Object obj: source) { if (obj == null) { result.add(null); } else if (obj.getClass() == String.class) { result.add(obj); } else { try { result.add(ObjectUtils.clone(obj)); } catch (CloneNotSupportedException ex) { throw new RuntimeException(ex); } } } return result; } /** * Returns a new map that contains the same keys and cloned copied of the * values. * * @param source the source map ({@code null} not permitted). * * @return A new map. */ public static Map cloneMapValues(Map source) { Args.nullNotPermitted(source, "source"); Map result = new HashMap(); for (Object key : source.keySet()) { Object value = source.get(key); if (value != null) { try { result.put(key, ObjectUtils.clone(value)); } catch (CloneNotSupportedException ex) { throw new RuntimeException(ex); } } else { result.put(key, null); } } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/DefaultShadowGenerator.java000066400000000000000000000226501463604235500313040ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * DefaultShadowGenerator.java * --------------------------- * (C) Copyright 2009-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.util; import java.awt.Color; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.awt.image.DataBufferInt; import java.io.Serializable; import org.jfree.chart.HashUtils; /** * A default implementation of the {@link ShadowGenerator} interface, based on * code in a * blog * post by Romain Guy. */ public class DefaultShadowGenerator implements ShadowGenerator, Serializable { private static final long serialVersionUID = 2732993885591386064L; /** The shadow size. */ private int shadowSize; /** The shadow color. */ private Color shadowColor; /** The shadow opacity. */ private float shadowOpacity; /** The shadow offset angle (in radians). */ private double angle; /** The shadow offset distance (in Java2D units). */ private int distance; /** * Creates a new instance with default attributes. */ public DefaultShadowGenerator() { this(5, Color.BLACK, 0.5f, 5, -Math.PI / 4); } /** * Creates a new instance with the specified attributes. * * @param size the shadow size. * @param color the shadow color. * @param opacity the shadow opacity. * @param distance the shadow offset distance. * @param angle the shadow offset angle (in radians). */ public DefaultShadowGenerator(int size, Color color, float opacity, int distance, double angle) { Args.nullNotPermitted(color, "color"); this.shadowSize = size; this.shadowColor = color; this.shadowOpacity = opacity; this.distance = distance; this.angle = angle; } /** * Returns the shadow size. * * @return The shadow size. */ public int getShadowSize() { return this.shadowSize; } /** * Returns the shadow color. * * @return The shadow color (never {@code null}). */ public Color getShadowColor() { return this.shadowColor; } /** * Returns the shadow opacity. * * @return The shadow opacity. */ public float getShadowOpacity() { return this.shadowOpacity; } /** * Returns the shadow offset distance. * * @return The shadow offset distance (in Java2D units). */ public int getDistance() { return this.distance; } /** * Returns the shadow offset angle (in radians). * * @return The angle (in radians). */ public double getAngle() { return this.angle; } /** * Calculates the x-offset for drawing the shadow image relative to the * source. * * @return The x-offset. */ @Override public int calculateOffsetX() { return (int) (Math.cos(this.angle) * this.distance) - this.shadowSize; } /** * Calculates the y-offset for drawing the shadow image relative to the * source. * * @return The y-offset. */ @Override public int calculateOffsetY() { return -(int) (Math.sin(this.angle) * this.distance) - this.shadowSize; } /** * Creates and returns an image containing the drop shadow for the * specified source image. * * @param source the source image. * * @return A new image containing the shadow. */ @Override public BufferedImage createDropShadow(BufferedImage source) { BufferedImage subject = new BufferedImage( source.getWidth() + this.shadowSize * 2, source.getHeight() + this.shadowSize * 2, BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = subject.createGraphics(); g2.drawImage(source, null, this.shadowSize, this.shadowSize); g2.dispose(); applyShadow(subject); return subject; } /** * Applies a shadow to the image. * * @param image the image. */ protected void applyShadow(BufferedImage image) { int dstWidth = image.getWidth(); int dstHeight = image.getHeight(); int left = (this.shadowSize - 1) >> 1; int right = this.shadowSize - left; int xStart = left; int xStop = dstWidth - right; int yStart = left; int yStop = dstHeight - right; int shadowRgb = this.shadowColor.getRGB() & 0x00FFFFFF; int[] aHistory = new int[this.shadowSize]; int historyIdx; int aSum; int[] dataBuffer = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); int lastPixelOffset = right * dstWidth; float sumDivider = this.shadowOpacity / this.shadowSize; // horizontal pass for (int y = 0, bufferOffset = 0; y < dstHeight; y++, bufferOffset = y * dstWidth) { aSum = 0; historyIdx = 0; for (int x = 0; x < this.shadowSize; x++, bufferOffset++) { int a = dataBuffer[bufferOffset] >>> 24; aHistory[x] = a; aSum += a; } bufferOffset -= right; for (int x = xStart; x < xStop; x++, bufferOffset++) { int a = (int) (aSum * sumDivider); dataBuffer[bufferOffset] = a << 24 | shadowRgb; // substract the oldest pixel from the sum aSum -= aHistory[historyIdx]; // get the lastest pixel a = dataBuffer[bufferOffset + right] >>> 24; aHistory[historyIdx] = a; aSum += a; if (++historyIdx >= this.shadowSize) { historyIdx -= this.shadowSize; } } } // vertical pass for (int x = 0, bufferOffset = 0; x < dstWidth; x++, bufferOffset = x) { aSum = 0; historyIdx = 0; for (int y = 0; y < this.shadowSize; y++, bufferOffset += dstWidth) { int a = dataBuffer[bufferOffset] >>> 24; aHistory[y] = a; aSum += a; } bufferOffset -= lastPixelOffset; for (int y = yStart; y < yStop; y++, bufferOffset += dstWidth) { int a = (int) (aSum * sumDivider); dataBuffer[bufferOffset] = a << 24 | shadowRgb; // substract the oldest pixel from the sum aSum -= aHistory[historyIdx]; // get the lastest pixel a = dataBuffer[bufferOffset + lastPixelOffset] >>> 24; aHistory[historyIdx] = a; aSum += a; if (++historyIdx >= this.shadowSize) { historyIdx -= this.shadowSize; } } } } /** * Tests this object for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return The object. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof DefaultShadowGenerator)) { return false; } DefaultShadowGenerator that = (DefaultShadowGenerator) obj; if (this.shadowSize != that.shadowSize) { return false; } if (!this.shadowColor.equals(that.shadowColor)) { return false; } if (this.shadowOpacity != that.shadowOpacity) { return false; } if (this.distance != that.distance) { return false; } if (this.angle != that.angle) { return false; } return true; } /** * Returns a hash code for this instance. * * @return The hash code. */ @Override public int hashCode() { int hash = HashUtils.hashCode(17, this.shadowSize); hash = HashUtils.hashCode(hash, this.shadowColor); hash = HashUtils.hashCode(hash, this.shadowOpacity); hash = HashUtils.hashCode(hash, this.distance); hash = HashUtils.hashCode(hash, this.angle); return hash; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/DirectionalGradientPaintTransformer.java000066400000000000000000000124631463604235500340360ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------------------- * DirectionalGradientPaintTransformer.java * ---------------------------------------- * (C) Copyright 2013-present, by Peter Kolb and Contributors. * * Original Author: Peter Kolb; * Contributor(s): David Gilbert; * */ package org.jfree.chart.util; import java.awt.GradientPaint; import java.awt.geom.Rectangle2D; import java.awt.Shape; import org.jfree.chart.ui.GradientPaintTransformer; /** * Transforms a {@code GradientPaint} to range over the width of a target * shape. The orientation of the resulting {@code GradientPaint} * depend on the coordinates of the original paint: * *
    *
  • If the original paint starts at 0,0 and ends at a point 0, y != 0, * the resulting paint will have a vertical orientation. *
  • If the original paint starts at 0,0 and ends at a point x !=0, 0, * the resulting paint will have a horizontal orientation. *
  • If the original paint starts at 0,0 and ends at a point x != 0, y != 0, * the resulting paint will have a diagonal orientation from the upper left to * the lower right edge. Lines of equal color will have a 45 ∞ angle, * pointing upwards from left to right. *
  • If the original paint starts at a point x != 0, y != 0, * the resulting paint will have a diagonal orientation from the lower left to * the upper right edge. Lines of equal color will have a 45 ∞ angle, * pointing downwards from left to right. *
*

In all cases, the cyclic flag of the original paint will be taken into * account.

* * @author Peter Kolb */ public class DirectionalGradientPaintTransformer implements GradientPaintTransformer { /** * Default constructor. */ public DirectionalGradientPaintTransformer() { super(); } /** * Transforms a {@code GradientPaint} instance to fit some target * shape. * * @param paint the original paint (not {@code null}). * @param target the reference area (not {@code null}). * * @return A transformed paint. */ @Override public GradientPaint transform(GradientPaint paint, Shape target) { //get the coordinates of the original GradientPaint final double px1 = paint.getPoint1().getX(); final double py1 = paint.getPoint1().getY(); final double px2 = paint.getPoint2().getX(); final double py2 = paint.getPoint2().getY(); //get the coordinates of the shape that is to be filled final Rectangle2D bounds = target.getBounds(); final float bx = (float)bounds.getX(); final float by = (float)bounds.getY(); final float bw = (float)bounds.getWidth(); final float bh = (float)bounds.getHeight(); //reserve variables to store the coordinates of the resulting GradientPaint float rx1, ry1, rx2, ry2; if (px1 == 0 && py1 == 0) { //start point is upper left corner rx1 = bx; ry1 = by; if (px2 != 0.0f && py2 != 0.0f) { //end point is lower right corner --> diagonal gradient float offset = (paint.isCyclic()) ? (bw + bh) / 4.0f : (bw + bh) / 2.0f ; rx2 = bx + offset; ry2 = by + offset; } else { //end point is either lower left corner --> vertical gradient //or end point is upper right corner --> horizontal gradient rx2 = (px2 == 0) ? rx1 : (paint.isCyclic() ? (rx1 + bw / 2.0f) : (rx1 + bw)); ry2 = (py2 == 0) ? ry1 : (paint.isCyclic() ? (ry1 + bh / 2.0f) : (ry1 + bh)); } } else { //start point is lower left right corner --> diagonal gradient rx1 = bx; ry1 = by + bh; float offset = (paint.isCyclic()) ? (bw + bh) / 4.0f : (bw + bh) / 2.0f; rx2 = bx + offset; ry2 = by + bh - offset; } return new GradientPaint(rx1, ry1, paint.getColor1(), rx2, ry2, paint.getColor2(), paint.isCyclic()); } }jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/ExportUtils.java000066400000000000000000000235001463604235500272000ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * ExportUtils.java * ---------------- * (C) Copyright 2014-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.util; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import javax.imageio.ImageIO; import org.jfree.chart.ui.Drawable; /** * Utility functions for exporting charts to SVG and PDF format. */ public class ExportUtils { /** * Returns {@code true} if JFreeSVG is on the classpath, and * {@code false} otherwise. The JFreeSVG library can be found at * http://www.jfree.org/jfreesvg/ * * @return A boolean. */ public static boolean isJFreeSVGAvailable() { Class svgClass = null; try { svgClass = Class.forName("org.jfree.svg.SVGGraphics2D"); } catch (ClassNotFoundException e) { // see if there is maybe an older version of JFreeSVG (different package name) try { svgClass = Class.forName("org.jfree.graphics2d.svg.SVGGraphics2D"); } catch (ClassNotFoundException e2) { // svgClass will be null so the function will return false } } return (svgClass != null); } /** * Returns {@code true} if OrsonPDF (or JFreePDF) is on the classpath, and * {@code false} otherwise. The OrsonPDF library can be found at * https://github.com/jfree/orsonpdf. JFreePDF is a modular version of * the same library, requiring Java 11 or later. Since JFreeChart might * be used in a modular context, this function has been modified (in version * 1.5.3) to detect JFreePDF also. * * @return A boolean. */ public static boolean isOrsonPDFAvailable() { Class pdfDocumentClass = null; try { pdfDocumentClass = Class.forName("com.orsonpdf.PDFDocument"); } catch (ClassNotFoundException e) { // check also for JFreePDF, which is the new modular version of OrsonPDF try { pdfDocumentClass = Class.forName("org.jfree.pdf.PDFDocument"); } catch (ClassNotFoundException e2) { // pdfDocumentClass will be null so the function will return false } } return (pdfDocumentClass != null); } /** * Writes the current content to the specified file in SVG format. This * will only work when the JFreeSVG library is found on the classpath. * Reflection is used to ensure there is no compile-time dependency on * JFreeSVG. * * @param drawable the drawable ({@code null} not permitted). * @param w the chart width. * @param h the chart height. * @param file the output file ({@code null} not permitted). */ public static void writeAsSVG(Drawable drawable, int w, int h, File file) { if (!ExportUtils.isJFreeSVGAvailable()) { throw new IllegalStateException("JFreeSVG is not present on the classpath."); } Args.nullNotPermitted(drawable, "drawable"); Args.nullNotPermitted(file, "file"); try { Class svg2Class; try { svg2Class = Class.forName("org.jfree.graphics2d.svg.SVGGraphics2D"); } catch (ClassNotFoundException ex) { svg2Class = Class.forName("org.jfree.svg.SVGGraphics2D"); } Constructor c1 = svg2Class.getConstructor(int.class, int.class); Graphics2D svg2 = (Graphics2D) c1.newInstance(w, h); Rectangle2D drawArea = new Rectangle2D.Double(0, 0, w, h); drawable.draw(svg2, drawArea); Class svgUtilsClass; try { svgUtilsClass = Class.forName("org.jfree.graphics2d.svg.SVGUtils"); } catch (ClassNotFoundException ex) { svgUtilsClass = Class.forName("org.jfree.svg.SVGUtils"); } Method m1 = svg2Class.getMethod("getSVGElement", (Class[]) null); String element = (String) m1.invoke(svg2, (Object[]) null); Method m2 = svgUtilsClass.getMethod("writeToSVG", File.class, String.class); m2.invoke(svgUtilsClass, file, element); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException ex) { throw new RuntimeException(ex); } } /** * Writes a {@link Drawable} to the specified file in PDF format. This * will only work when the OrsonPDF library is found on the classpath. * Reflection is used to ensure there is no compile-time dependency on * OrsonPDF. * * @param drawable the drawable ({@code null} not permitted). * @param w the chart width. * @param h the chart height. * @param file the output file ({@code null} not permitted). */ public static void writeAsPDF(Drawable drawable, int w, int h, File file) { if (!ExportUtils.isOrsonPDFAvailable()) { throw new IllegalStateException("Neither OrsonPDF nor JFreePDF is present on the classpath."); } Args.nullNotPermitted(drawable, "drawable"); Args.nullNotPermitted(file, "file"); try { Class pdfDocClass; try { pdfDocClass = Class.forName("com.orsonpdf.PDFDocument"); } catch (ClassNotFoundException e) { pdfDocClass = Class.forName("org.jfree.pdf.PDFDocument"); } Object pdfDoc = pdfDocClass.getDeclaredConstructor().newInstance(); Method m = pdfDocClass.getMethod("createPage", Rectangle2D.class); Rectangle2D rect = new Rectangle(w, h); Object page = m.invoke(pdfDoc, rect); Method m2 = page.getClass().getMethod("getGraphics2D"); Graphics2D g2 = (Graphics2D) m2.invoke(page); Rectangle2D drawArea = new Rectangle2D.Double(0, 0, w, h); drawable.draw(g2, drawArea); Method m3 = pdfDocClass.getMethod("writeToFile", File.class); m3.invoke(pdfDoc, file); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException ex) { throw new RuntimeException(ex); } } /** * Writes the current content to the specified file in PNG format. * * @param drawable the drawable ({@code null} not permitted). * @param w the chart width. * @param h the chart height. * @param file the output file ({@code null} not permitted). * * @throws FileNotFoundException if the file is not found. * @throws IOException if there is an I/O problem. */ public static void writeAsPNG(Drawable drawable, int w, int h, File file) throws FileNotFoundException, IOException { BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = image.createGraphics(); drawable.draw(g2, new Rectangle(w, h)); try (OutputStream out = new BufferedOutputStream(new FileOutputStream(file))) { ImageIO.write(image, "png", out); } } /** * Writes the current content to the specified file in JPEG format. * * @param drawable the drawable ({@code null} not permitted). * @param w the chart width. * @param h the chart height. * @param file the output file ({@code null} not permitted). * * @throws FileNotFoundException if the file is not found. * @throws IOException if there is an I/O problem. */ public static void writeAsJPEG(Drawable drawable, int w, int h, File file) throws FileNotFoundException, IOException { BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); drawable.draw(g2, new Rectangle(w, h)); try (OutputStream out = new BufferedOutputStream(new FileOutputStream(file))) { ImageIO.write(image, "jpg", out); } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/GeomUtil.java000066400000000000000000000142141463604235500264250ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------- * GeomUtil.java * ------------- * (C) Copyright 2021-present, by Yuri Blankenstein and Contributors. * * Original Author: Yuri Blankenstein (for ESI TNO); * */ package org.jfree.chart.util; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.Line2D; import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.util.ArrayList; /** * Some utility methods for working with geometry in Java2D. */ public final class GeomUtil { private GeomUtil() { // Empty for utility classes } /** * For each line in {@code lines}, calculates its intersection point with * {@code lineA}, possibly no intersection point exists (i.e. parallel * lines). * * @param lineA line to calculate the intersection point for. * @param lines lines to calculate the intersection points with. * @return all intersections points between {@code lineA} and {@code lines}. * @see #calculateIntersectionPoint(Line2D, Line2D) */ public static Point2D[] calculateIntersectionPoints(Line2D lineA, Line2D... lines) { ArrayList intersectionPoints = new ArrayList<>( lines.length); for (Line2D lineB : lines) { if (lineA.intersectsLine(lineB)) { // Why does Java have the tester method, but not the method to // get the point itself :S intersectionPoints.add(calculateIntersectionPoint(lineA, lineB)); } } return intersectionPoints.toArray(new Point2D[0]); } /** * Calculates the intersection point of {@code lineA} with {@code lineB}, * possibly {@code null} if no intersection point exists (i.e. parallel * lines). * * @param lineA the first line for the calculation * @param lineB the second line for the calculation * @return the intersection point of {@code lineA} with {@code lineB}, * possibly {@code null} if no intersection point exists */ public static Point2D calculateIntersectionPoint(Line2D lineA, Line2D lineB) { double x1 = lineA.getX1(); double y1 = lineA.getY1(); double x2 = lineA.getX2(); double y2 = lineA.getY2(); double x3 = lineB.getX1(); double y3 = lineB.getY1(); double x4 = lineB.getX2(); double y4 = lineB.getY2(); Point2D p = null; double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); if (d != 0) { double xi = ((x3 - x4) * (x1 * y2 - y1 * x2) - (x1 - x2) * (x3 * y4 - y3 * x4)) / d; double yi = ((y3 - y4) * (x1 * y2 - y1 * x2) - (y1 - y2) * (x3 * y4 - y3 * x4)) / d; p = new Point2D.Double(xi, yi); } return p; } /** * Returns all {@link PathIterator#SEG_LINETO line segments} building up a * {@code shape}. * * @param shape a shape that is built up of {@link PathIterator#SEG_LINETO} * elements. * @param at an optional {@code AffineTransform} to be applied to the * coordinates as they are returned in the iteration, or * {@code null} if untransformed coordinates are desired * @return all {@link PathIterator#SEG_LINETO line segments} building up the * {@code shape} * @throws IllegalArgumentException if {@code shape} contains non-straight * line segments (i.e. * {@link PathIterator#SEG_CUBICTO} or * {@link PathIterator#SEG_QUADTO}) */ public static Line2D[] getLines(Shape shape, AffineTransform at) throws IllegalArgumentException { ArrayList lines = new ArrayList<>(); Point2D first = null; Point2D current = null; double[] coords = new double[6]; for (PathIterator pathIterator = shape.getPathIterator(at); !pathIterator.isDone(); pathIterator.next()) { switch (pathIterator.currentSegment(coords)) { case PathIterator.SEG_MOVETO: current = new Point2D.Double(coords[0], coords[1]); break; case PathIterator.SEG_LINETO: Point2D to = new Point2D.Double(coords[0], coords[1]); lines.add(new Line2D.Double(current, to)); current = to; break; case PathIterator.SEG_CLOSE: lines.add(new Line2D.Double(current, first)); current = first; break; default: throw new IllegalArgumentException( "Shape contains non-straight line segments"); } if (null == first) first = current; } return lines.toArray(new Line2D[0]); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/HMSNumberFormat.java000066400000000000000000000072511463604235500276540ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * HMSNumberFormat.java * -------------------- * (C) Copyright 2013-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.util; import java.text.DecimalFormat; import java.text.FieldPosition; import java.text.NumberFormat; import java.text.ParsePosition; /** * A custom number formatter that formats numbers (in seconds) as HH:MM:SS. * Created in response to: * * http://stackoverflow.com/questions/19028908/jfreechart-need-to-customize-y-axis-just-for-printing */ public class HMSNumberFormat extends NumberFormat { private NumberFormat formatter = new DecimalFormat("00"); /** * Creates a new instance. */ public HMSNumberFormat() { // nothing to do } /** * Formats the specified number as a string of the form HH:MM:SS. The * decimal fraction is ignored. * * @param number the number to format. * @param toAppendTo the buffer to append to (ignored here). * @param pos the field position (ignored here). * * @return The string buffer. */ @Override public StringBuffer format(double number, StringBuffer toAppendTo, FieldPosition pos) { return format((long) number, toAppendTo, pos); } /** * Formats the specified number as a string of the form HH:MM:SS. * * @param number the number to format. * @param toAppendTo the buffer to append to (ignored here). * @param pos the field position (ignored here). * * @return The string buffer. */ @Override public StringBuffer format(long number, StringBuffer toAppendTo, FieldPosition pos) { StringBuffer sb = new StringBuffer(); long hours = number / 3600; sb.append(this.formatter.format(hours)).append(":"); long remaining = number - (hours * 3600); long minutes = remaining / 60; sb.append(this.formatter.format(minutes)).append(":"); long seconds = remaining - (minutes * 60); sb.append(this.formatter.format(seconds)); return sb; } /** * Parsing is not implemented, so this method always returns * {@code null}. * * @param source ignored. * @param parsePosition ignored. * * @return Always {@code null}. */ @Override public Number parse (String source, ParsePosition parsePosition) { return null; // don't bother with parsing } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/HexNumberFormat.java000066400000000000000000000113471463604235500277520ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * HexNumberFormat.java * -------------------- * (C) Copyright 2007-present, by Richard West and Contributors. * * Original Author: Richard West, Advanced Micro Devices, Inc.; * Contributor(s): David Gilbert; * */ package org.jfree.chart.util; import java.text.FieldPosition; import java.text.NumberFormat; import java.text.ParsePosition; /** * A custom number formatter that formats numbers as hexadecimal strings. * There are some limitations, so be careful using this class. */ public class HexNumberFormat extends NumberFormat { /** Number of hexadecimal digits for a byte. */ public static final int BYTE = 2; /** Number of hexadecimal digits for a word. */ public static final int WORD = 4; /** Number of hexadecimal digits for a double word. */ public static final int DWORD = 8; /** Number of hexadecimal digits for a quad word. */ public static final int QWORD = 16; /** The number of digits (shorter strings will be left padded). */ private int m_numDigits = DWORD; /** * Creates a new instance with 8 digits. */ public HexNumberFormat() { this(DWORD); } /** * Creates a new instance with the specified number of digits. * @param digits the digits. */ public HexNumberFormat(int digits) { super(); this.m_numDigits = digits; } /** * Returns the number of digits. * * @return The number of digits. */ public final int getNumberOfDigits() { return this.m_numDigits; } /** * Sets the number of digits. * * @param digits the number of digits. */ public void setNumberOfDigits(int digits) { this.m_numDigits = digits; } /** * Formats the specified number as a hexadecimal string. The decimal * fraction is ignored. * * @param number the number to format. * @param toAppendTo the buffer to append to (ignored here). * @param pos the field position (ignored here). * * @return The string buffer. */ @Override public StringBuffer format(double number, StringBuffer toAppendTo, FieldPosition pos) { return format((long) number, toAppendTo, pos); } /** * Formats the specified number as a hexadecimal string. The decimal * fraction is ignored. * * @param number the number to format. * @param toAppendTo the buffer to append to (ignored here). * @param pos the field position (ignored here). * * @return The string buffer. */ @Override public StringBuffer format(long number, StringBuffer toAppendTo, FieldPosition pos) { String l_hex = Long.toHexString(number).toUpperCase(); int l_pad = this.m_numDigits - l_hex.length(); l_pad = (0 < l_pad) ? l_pad : 0; StringBuffer l_extended = new StringBuffer("0x"); for (int i = 0; i < l_pad; i++) { l_extended.append(0); } l_extended.append(l_hex); return l_extended; } /** * Parsing is not implemented, so this method always returns * {@code null}. * * @param source ignored. * @param parsePosition ignored. * * @return Always {@code null}. */ @Override public Number parse (String source, ParsePosition parsePosition) { return null; // don't bother with parsing } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/LineUtils.java000066400000000000000000000143151463604235500266120ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * LineUtils.java * -------------- * (C) Copyright 2008-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.util; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; /** * Some utility methods for {@link Line2D} objects. */ public class LineUtils { /** * Clips the specified line to the given rectangle. If any of the line * coordinates is not finite, then the method returns {@code false} and * the line is not modified. * * @param line the line ({@code null} not permitted). * @param rect the clipping rectangle ({@code null} not permitted). * * @return {@code true} if the clipped line is visible, and * {@code false} otherwise. */ public static boolean clipLine(Line2D line, Rectangle2D rect) { double x1 = line.getX1(); double y1 = line.getY1(); double x2 = line.getX2(); double y2 = line.getY2(); // check that the line can be worked with (bug#223) if ((!Double.isFinite(x1) || !Double.isFinite(y1)) || !Double.isFinite(x2) || !Double.isFinite(y2)) { return false; } double minX = rect.getMinX(); double maxX = rect.getMaxX(); double minY = rect.getMinY(); double maxY = rect.getMaxY(); int f1 = rect.outcode(x1, y1); int f2 = rect.outcode(x2, y2); while ((f1 | f2) != 0) { if ((f1 & f2) != 0) { return false; } double dx = (x2 - x1); double dy = (y2 - y1); // update (x1, y1), (x2, y2) and f1 and f2 using intersections // then recheck if (f1 != 0) { // first point is outside, so we update it against one of the // four sides then continue if ((f1 & Rectangle2D.OUT_LEFT) == Rectangle2D.OUT_LEFT && dx != 0.0) { y1 = y1 + (minX - x1) * dy / dx; x1 = minX; } else if ((f1 & Rectangle2D.OUT_RIGHT) == Rectangle2D.OUT_RIGHT && dx != 0.0) { y1 = y1 + (maxX - x1) * dy / dx; x1 = maxX; } else if ((f1 & Rectangle2D.OUT_BOTTOM) == Rectangle2D.OUT_BOTTOM && dy != 0.0) { x1 = x1 + (maxY - y1) * dx / dy; y1 = maxY; } else if ((f1 & Rectangle2D.OUT_TOP) == Rectangle2D.OUT_TOP && dy != 0.0) { x1 = x1 + (minY - y1) * dx / dy; y1 = minY; } f1 = rect.outcode(x1, y1); } else if (f2 != 0) { // second point is outside, so we update it against one of the // four sides then continue if ((f2 & Rectangle2D.OUT_LEFT) == Rectangle2D.OUT_LEFT && dx != 0.0) { y2 = y2 + (minX - x2) * dy / dx; x2 = minX; } else if ((f2 & Rectangle2D.OUT_RIGHT) == Rectangle2D.OUT_RIGHT && dx != 0.0) { y2 = y2 + (maxX - x2) * dy / dx; x2 = maxX; } else if ((f2 & Rectangle2D.OUT_BOTTOM) == Rectangle2D.OUT_BOTTOM && dy != 0.0) { x2 = x2 + (maxY - y2) * dx / dy; y2 = maxY; } else if ((f2 & Rectangle2D.OUT_TOP) == Rectangle2D.OUT_TOP && dy != 0.0) { x2 = x2 + (minY - y2) * dx / dy; y2 = minY; } f2 = rect.outcode(x2, y2); } } line.setLine(x1, y1, x2, y2); return true; // the line is visible - if it wasn't, we'd have // returned false from within the while loop above } /** * Creates a new line by extending an existing line. * * @param line the line ({@code null} not permitted). * @param startPercent the amount to extend the line at the start point * end. * @param endPercent the amount to extend the line at the end point end. * * @return A new line. */ public static Line2D extendLine(Line2D line, double startPercent, double endPercent) { Args.nullNotPermitted(line, "line"); double x1 = line.getX1(); double x2 = line.getX2(); double deltaX = x2 - x1; double y1 = line.getY1(); double y2 = line.getY2(); double deltaY = y2 - y1; x1 = x1 - (startPercent * deltaX); y1 = y1 - (startPercent * deltaY); x2 = x2 + (endPercent * deltaX); y2 = y2 + (endPercent * deltaY); return new Line2D.Double(x1, y1, x2, y2); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/LogFormat.java000066400000000000000000000160051463604235500265720ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * LogFormat.java * -------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.util; import java.text.DecimalFormat; import java.text.FieldPosition; import java.text.NumberFormat; import java.text.ParsePosition; /** * A number formatter for logarithmic values. This formatter does not support * parsing. */ public class LogFormat extends NumberFormat { /** The log base value. */ private double base; /** The natural logarithm of the base value. */ private double baseLog; /** The label for the log base (for example, "e"). */ private String baseLabel; /** * The label for the power symbol. */ private String powerLabel; /** A flag that controls whether or not the base is shown. */ private boolean showBase; /** The number formatter for the exponent. */ private NumberFormat formatter = new DecimalFormat("0.0#"); /** * Creates a new instance using base 10. */ public LogFormat() { this(10.0, "10", true); } /** * Creates a new instance. * * @param base the base. * @param baseLabel the base label ({@code null} not permitted). * @param showBase a flag that controls whether or not the base value is * shown. */ public LogFormat(double base, String baseLabel, boolean showBase) { this(base, baseLabel, "^", showBase); } /** * Creates a new instance. * * @param base the base. * @param baseLabel the base label ({@code null} not permitted). * @param powerLabel the power label ({@code null} not permitted). * @param showBase a flag that controls whether or not the base value is * shown. */ public LogFormat(double base, String baseLabel, String powerLabel, boolean showBase) { Args.nullNotPermitted(baseLabel, "baseLabel"); Args.nullNotPermitted(powerLabel, "powerLabel"); this.base = base; this.baseLog = Math.log(this.base); this.baseLabel = baseLabel; this.showBase = showBase; this.powerLabel = powerLabel; } /** * Returns the number format used for the exponent. * * @return The number format (never {@code null}). */ public NumberFormat getExponentFormat() { return (NumberFormat) this.formatter.clone(); } /** * Sets the number format used for the exponent. * * @param format the formatter ({@code null} not permitted). */ public void setExponentFormat(NumberFormat format) { Args.nullNotPermitted(format, "format"); this.formatter = format; } /** * Calculates the log of a given value. * * @param value the value. * * @return The log of the value. */ private double calculateLog(double value) { return Math.log(value) / this.baseLog; } /** * Returns a formatted representation of the specified number. * * @param number the number. * @param toAppendTo the string buffer to append to. * @param pos the position. * * @return A string buffer containing the formatted value. */ @Override public StringBuffer format(double number, StringBuffer toAppendTo, FieldPosition pos) { StringBuffer result = new StringBuffer(); if (this.showBase) { result.append(this.baseLabel); result.append(this.powerLabel); } result.append(this.formatter.format(calculateLog(number))); return result; } /** * Formats the specified number as a hexadecimal string. The decimal * fraction is ignored. * * @param number the number to format. * @param toAppendTo the buffer to append to (ignored here). * @param pos the field position (ignored here). * * @return The string buffer. */ @Override public StringBuffer format(long number, StringBuffer toAppendTo, FieldPosition pos) { StringBuffer result = new StringBuffer(); if (this.showBase) { result.append(this.baseLabel); result.append(this.powerLabel); } result.append(this.formatter.format(calculateLog(number))); return result; } /** * Parsing is not implemented, so this method always returns * {@code null}. * * @param source ignored. * @param parsePosition ignored. * * @return Always {@code null}. */ @Override public Number parse (String source, ParsePosition parsePosition) { return null; // don't bother with parsing } /** * Tests this formatter for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof LogFormat)) { return false; } LogFormat that = (LogFormat) obj; if (this.base != that.base) { return false; } if (!this.baseLabel.equals(that.baseLabel)) { return false; } if (this.baseLog != that.baseLog) { return false; } if (this.showBase != that.showBase) { return false; } if (!this.formatter.equals(that.formatter)) { return false; } return super.equals(obj); } /** * Returns a clone of this instance. * * @return A clone. */ @Override public Object clone() { LogFormat clone = (LogFormat) super.clone(); clone.formatter = (NumberFormat) this.formatter.clone(); return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/ObjectList.java000066400000000000000000000064521463604235500267470ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * ObjectList.java * --------------- * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributors: -; */ package org.jfree.chart.util; /** * A list of objects that can grow as required. *

* When cloning, the objects in the list are NOT cloned, only the references. */ public class ObjectList extends AbstractObjectList { /** * Default constructor. */ public ObjectList() { } /** * Creates a new list. * * @param initialCapacity the initial capacity. */ public ObjectList(int initialCapacity) { super(initialCapacity); } // NOTE: the methods below look redundant, but their purpose is to provide public // access to the the get(), set() and indexOf() methods defined in the // AbstractObjectList class, for this class only. For other classes // (e.g. PaintList, ShapeList etc) we don't want the Object versions of these // methods to be visible in the public API. /** * Returns the object at the specified index, if there is one, or {@code null}. * * @param index the object index. * * @return The object or {@code null}. */ @Override public Object get(int index) { return super.get(index); } /** * Sets an object reference (overwriting any existing object). * * @param index the object index. * @param object the object ({@code null} permitted). */ @Override public void set(int index, Object object) { super.set(index, object); } /** * Returns the index of the specified object, or -1 if the object is not in the list. * * @param object the object. * * @return The index or -1. */ @Override public int indexOf(Object object) { return super.indexOf(object); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/ObjectUtils.java000066400000000000000000000126171463604235500271340ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.util; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Collection; import java.util.Iterator; /** * A collection of useful static utility methods for handling classes and object * instantiation. */ public final class ObjectUtils { /** * Default constructor - private. */ private ObjectUtils() { } /** * Returns {@code true} if the two objects are equal OR both * {@code null}. * * @param o1 object 1 ({@code null} permitted). * @param o2 object 2 ({@code null} permitted). * * @return {@code true} or {@code false}. * * @deprecated Use Objects.equals() from the JDK. */ public static boolean equal(Object o1, Object o2) { if (o1 == o2) { return true; } if (o1 != null) { return o1.equals(o2); } else { return false; } } /** * Returns a hash code for an object, or zero if the object is * {@code null}. * * @param object the object ({@code null} permitted). * @return The object's hash code (or zero if the object is * {@code null}). */ public static int hashCode(Object object) { int result = 0; if (object != null) { result = object.hashCode(); } return result; } /** * Returns a clone of the specified object, if it can be cloned, otherwise * throws a CloneNotSupportedException. * * @param object the object to clone ({@code null} not permitted). * @return A clone of the specified object. * @throws CloneNotSupportedException if the object cannot be cloned. */ public static Object clone(Object object) throws CloneNotSupportedException { if (object == null) { throw new IllegalArgumentException("Null 'object' argument."); } if (object instanceof PublicCloneable) { PublicCloneable pc = (PublicCloneable) object; return pc.clone(); } else { try { Method method = object.getClass().getMethod("clone", (Class[]) null); if (Modifier.isPublic(method.getModifiers())) { return method.invoke(object, (Object[]) null); } } catch (NoSuchMethodException e) { throw new CloneNotSupportedException("Object without clone() method is impossible."); } catch (IllegalAccessException e) { throw new CloneNotSupportedException("Object.clone(): unable to call method."); } catch (InvocationTargetException e) { throw new CloneNotSupportedException("Object without clone() method is impossible."); } } throw new CloneNotSupportedException("Failed to clone."); } /** * Returns a new collection containing clones of all the items in the * specified collection. * * @param collection the collection ({@code null} not permitted). * @return A new collection containing clones of all the items in the * specified collection. * @throws CloneNotSupportedException if any of the items in the collection * cannot be cloned. */ public static Collection deepClone(Collection collection) throws CloneNotSupportedException { if (collection == null) { throw new IllegalArgumentException("Null 'collection' argument."); } // all JDK-Collections are cloneable ... // and if the collection is not clonable, then we should throw // a CloneNotSupportedException anyway ... Collection result = (Collection) ObjectUtils.clone(collection); result.clear(); Iterator iterator = collection.iterator(); while (iterator.hasNext()) { Object item = iterator.next(); if (item != null) { result.add(clone(item)); } else { result.add(null); } } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/PaintAlpha.java000066400000000000000000000350061463604235500267230ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Java is a trademark or registered trademark of Sun Microsystems, Inc. * in the United States and other countries.] * * --------------- * PaintAlpha.java * --------------- * (C) Copyright 2011-present, by DaveLaw and Contributors. * * Original Author: DaveLaw (dave ATT davelaw D0TT de); * Contributor(s): David Gilbert; * */ package org.jfree.chart.util; import java.awt.Color; import java.awt.GradientPaint; import java.awt.LinearGradientPaint; import java.awt.Paint; import java.awt.RadialGradientPaint; import java.awt.TexturePaint; import java.awt.image.BufferedImage; import java.awt.image.IndexColorModel; import java.awt.image.WritableRaster; import java.util.Hashtable; /** * This class contains static methods for the manipulation * of objects of type {@code Paint} *

* The intention is to honour the alpha-channel in the process. * {@code PaintAlpha} was originally conceived to improve the * rendering of 3D Shapes with transparent colours and to allow * invisible bars by making them completely transparent. *

* Previously {@link Color#darker()} was used for this, * which always returns an opaque colour. *

* Additionally there are methods to control the behaviour and * in particular a {@link PaintAlpha#cloneImage(BufferedImage) cloneImage(..)} * method which is needed to darken objects of type {@link TexturePaint}. * * @author DaveLaw */ public class PaintAlpha { // TODO Revert to SVN revision 2469 in JFreeChart 1.0.16 // (MultipleGradientPaint's / JDK issues) // TODO THEN: change visibility of ALL darker(...) Methods EXCEPT // darker(Paint) to private! /** * Multiplier for the {@code darker} Methods.
* (taken from {@link java.awt.Color}.FACTOR) */ private static final double FACTOR = 0.7; private static boolean legacyAlpha = false; /** * Per default {@code PaintAlpha} will try to honour alpha-channel * information. In the past this was not the case. * If you wish legacy functionality for your application you can request * this here. * * @param legacyAlpha boolean * * @return the previous setting */ public static boolean setLegacyAlpha(boolean legacyAlpha) { boolean old = PaintAlpha.legacyAlpha; PaintAlpha.legacyAlpha = legacyAlpha; return old; } /** * Create a new (if possible, darker) {@code Paint} of the same Type. * If the Type is not supported, the original {@code Paint} is returned. *

* @param paint a {@code Paint} implementation * (e.g. {@link Color}, {@link GradientPaint}, {@link TexturePaint},..) *

* @return a (usually new, see above) {@code Paint} */ public static Paint darker(Paint paint) { if (paint instanceof Color) { return darker((Color) paint); } if (legacyAlpha) { /* * Legacy? Just return the original Paint. * (this corresponds EXACTLY to how Paints used to be darkened) */ return paint; } if (paint instanceof GradientPaint) { return darker((GradientPaint) paint); } if (paint instanceof LinearGradientPaint) { return darkerLinearGradientPaint((LinearGradientPaint) paint); } if (paint instanceof RadialGradientPaint) { return darkerRadialGradientPaint((RadialGradientPaint) paint); } if (paint instanceof TexturePaint) { try { return darkerTexturePaint((TexturePaint) paint); } catch (Exception e) { /* * Lots can go wrong while fiddling with Images, Color Models * & such! If anything at all goes awry, just return the original * TexturePaint. (TexturePaint's are immutable anyway, so no harm * done) */ return paint; } } return paint; } /** * Similar to {@link Color#darker()}. *

* The essential difference is that this method * maintains the alpha-channel unchanged
* * @param paint a {@code Color} * * @return a darker version of the {@code Color} */ private static Color darker(Color paint) { return new Color( (int)(paint.getRed () * FACTOR), (int)(paint.getGreen() * FACTOR), (int)(paint.getBlue () * FACTOR), paint.getAlpha()); } /** * Create a new {@code GradientPaint} with its colors darkened. * * @param paint the gradient paint ({@code null} not permitted). * * @return a darker version of the {@code GradientPaint} */ private static GradientPaint darker(GradientPaint paint) { return new GradientPaint( paint.getPoint1(), darker(paint.getColor1()), paint.getPoint2(), darker(paint.getColor2()), paint.isCyclic()); } /** * Create a new Gradient with its colours darkened. * * @param paint a {@code LinearGradientPaint} * * @return a darker version of the {@code LinearGradientPaint} */ private static Paint darkerLinearGradientPaint(LinearGradientPaint paint) { final Color[] paintColors = paint.getColors(); for (int i = 0; i < paintColors.length; i++) { paintColors[i] = darker(paintColors[i]); } return new LinearGradientPaint(paint.getStartPoint(), paint.getEndPoint(), paint.getFractions(), paintColors, paint.getCycleMethod(), paint.getColorSpace(), paint.getTransform()); } /** * Create a new Gradient with its colours darkened. * * @param paint a {@code RadialGradientPaint} * * @return a darker version of the {@code RadialGradientPaint} */ private static Paint darkerRadialGradientPaint(RadialGradientPaint paint) { final Color[] paintColors = paint.getColors(); for (int i = 0; i < paintColors.length; i++) { paintColors[i] = darker(paintColors[i]); } return new RadialGradientPaint(paint.getCenterPoint(), paint.getRadius(), paint.getFocusPoint(), paint.getFractions(), paintColors, paint.getCycleMethod(), paint.getColorSpace(), paint.getTransform()); } /** * Create a new {@code TexturePaint} with its colors darkened. *

* This entails cloning the underlying {@code BufferedImage}, * then darkening each color-pixel individually! * * @param paint a {@code TexturePaint} * * @return a darker version of the {@code TexturePaint} */ private static TexturePaint darkerTexturePaint(TexturePaint paint) { /** * Color Models with pre-multiplied Alpha tested OK without any * special logic * * BufferedImage.TYPE_INT_ARGB_PRE: // Type 03: tested OK 2011.02.27 * BufferedImage.TYPE_4BYTE_ABGR_PRE: // Type 07: tested OK 2011.02.27 */ if (paint.getImage().getColorModel().isAlphaPremultiplied()) { /* Placeholder */ } BufferedImage img = cloneImage(paint.getImage()); WritableRaster ras = img.copyData(null); final int miX = ras.getMinX(); final int miY = ras.getMinY(); final int maY = ras.getMinY() + ras.getHeight(); final int wid = ras.getWidth(); /**/ int[] pix = new int[wid * img.getSampleModel().getNumBands()]; /* (pix-buffer is large enough for all pixels of one row) */ /** * Indexed Color Models (sort of a Palette) CANNOT be simply * multiplied (the pixel-value is just an index into the Palette). * * Fortunately, IndexColorModel.getComponents(..) resolves the colors. * The resolved colors can then be multiplied by our FACTOR. * IndexColorModel.getDataElement(..) then tries to map the computed * color to the "nearest" in the Palette. * * It is quite possible that the "nearest" color is the ORIGINAL * color! In the worst case, the returned Image will be identical to * the original. * * Applies to following Image Types: * * BufferedImage.TYPE_BYTE_BINARY: // Type 12: tested OK 2011.02.27 * BufferedImage.TYPE_BYTE_INDEXED: // Type 13: tested OK 2011.02.27 */ if (img.getColorModel() instanceof IndexColorModel) { int[] nco = new int[4]; // RGB (+ optional Alpha which we leave // unchanged) for (int y = miY; y < maY; y++) { pix = ras.getPixels(miX, y, wid, 1, pix); for (int p = 0; p < pix.length; p++) { nco = img.getColorModel().getComponents(pix[p], nco, 0); nco[0] *= FACTOR; // Red nco[1] *= FACTOR; // Green nco[2] *= FACTOR; // Blue. Now map computed colour to // nearest in Palette... pix[p] = img.getColorModel().getDataElement(nco, 0); } /**/ ras.setPixels(miX, y, wid, 1, pix); } img.setData(ras); return new TexturePaint(img, paint.getAnchorRect()); } /** * For the other 2 Color Models, java.awt.image.ComponentColorModel and * java.awt.image.DirectColorModel, the order of subpixels returned by * ras.getPixels(..) was observed to correspond to the following... */ if (img.getSampleModel().getNumBands() == 4) { /** * The following Image Types have an Alpha-channel which we will * leave unchanged: * * BufferedImage.TYPE_INT_ARGB: // Type 02: tested OK 2011.02.27 * BufferedImage.TYPE_4BYTE_ABGR: // Type 06: tested OK 2011.02.27 */ for (int y = miY; y < maY; y++) { pix = ras.getPixels(miX, y, wid, 1, pix); for (int p = 0; p < pix.length;) { pix[p] = (int)(pix[p++] * FACTOR); // Red pix[p] = (int)(pix[p++] * FACTOR); // Green pix[p] = (int)(pix[p++] * FACTOR); // Blue /* Ignore alpha-channel -> */p++; } /**/ ras.setPixels(miX, y, wid, 1, pix); } img.setData(ras); return new TexturePaint(img, paint.getAnchorRect()); } else { for (int y = miY; y < maY; y++) { pix = ras.getPixels(miX, y, wid, 1, pix); for (int p = 0; p < pix.length; p++) { pix[p] = (int)(pix[p] * FACTOR); } /**/ ras.setPixels(miX, y, wid, 1, pix); } img.setData(ras); return new TexturePaint(img, paint.getAnchorRect()); /** * Above, we multiplied every pixel by our FACTOR because the * applicable Image Types consist only of color or grey channels: * * BufferedImage.TYPE_INT_RGB: // Type 01: tested OK 2011.02.27 * BufferedImage.TYPE_INT_BGR: // Type 04: tested OK 2011.02.27 * BufferedImage.TYPE_3BYTE_BGR: // Type 05: tested OK 2011.02.27 * BufferedImage.TYPE_BYTE_GRAY: // Type 10: tested OK 2011.02.27 * BufferedImage.TYPE_USHORT_GRAY: // Type 11: tested OK 2011.02.27 * BufferedImage.TYPE_USHORT_565_RGB: // Type 08: tested OK 2011.02.27 * BufferedImage.TYPE_USHORT_555_RGB: // Type 09: tested OK 2011.02.27 * * Note: as ras.getPixels(..) returned colours in the order R, G, B, A (optional) * for both TYPE_4BYTE_ABGR & TYPE_3BYTE_BGR, * it is assumed that TYPE_INT_BGR will behave similarly. */ } } /** * Clone a {@link BufferedImage}. *

* Note: when constructing the clone, the original Color Model Object is * reused.
That keeps things simple and should not be a problem, as all * known Color Models
* ({@link java.awt.image.IndexColorModel IndexColorModel}, * {@link java.awt.image.DirectColorModel DirectColorModel}, * {@link java.awt.image.ComponentColorModel ComponentColorModel}) are * immutable. * * @param image original BufferedImage to clone * * @return a new BufferedImage reusing the original's Color Model and * containing a clone of its pixels */ public static BufferedImage cloneImage(BufferedImage image) { WritableRaster rin = image.getRaster(); WritableRaster ras = rin.createCompatibleWritableRaster(); /**/ ras.setRect(rin); // <- this is the code that actually COPIES the pixels /* * Buffered Images may have properties, but NEVER disclose them! * Nevertheless, just in case someone implements getPropertyNames() * one day... */ Hashtable props = null; String[] propNames = image.getPropertyNames(); if (propNames != null) { // ALWAYS null props = new Hashtable(); for (int i = 0; i < propNames.length; i++) { props.put(propNames[i], image.getProperty(propNames[i])); } } return new BufferedImage(image.getColorModel(), ras, image.isAlphaPremultiplied(), props); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/PaintList.java000066400000000000000000000106521463604235500266110ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * PaintList.java * -------------- * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributors: -; */ package org.jfree.chart.util; import java.awt.Paint; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; /** * A table of {@link Paint} objects. */ public class PaintList extends AbstractObjectList { private static final long serialVersionUID = -708669381577938219L; /** * Creates a new list. */ public PaintList() { super(); } /** * Returns a {@link Paint} object from the list. * * @param index the index (zero-based). * * @return The object. */ public Paint getPaint(int index) { return (Paint) get(index); } /** * Sets the {@link Paint} for an item in the list. The list is expanded if necessary. * * @param index the index (zero-based). * @param paint the {@link Paint}. */ public void setPaint(int index, Paint paint) { set(index, paint); } /** * Tests the list for equality with another object (typically also a list). * * @param obj the other object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (obj == this) { return true; } if (!(obj instanceof PaintList)) { return false; } PaintList that = (PaintList) obj; int listSize = size(); for (int i = 0; i < listSize; i++) { if (!PaintUtils.equal(getPaint(i), that.getPaint(i))) { return false; } } return true; } /** * Returns a hash code value for the object. * * @return the hashcode */ @Override public int hashCode() { return super.hashCode(); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); int count = size(); stream.writeInt(count); for (int i = 0; i < count; i++) { Paint paint = getPaint(i); if (paint != null) { stream.writeInt(i); SerialUtils.writePaint(paint, stream); } else { stream.writeInt(-1); } } } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); int count = stream.readInt(); for (int i = 0; i < count; i++) { int index = stream.readInt(); if (index != -1) { setPaint(index, SerialUtils.readPaint(stream)); } } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/PaintUtils.java000066400000000000000000000161211463604235500267730ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * PaintUtils.java * --------------- * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributors: -; */ package org.jfree.chart.util; import java.awt.Color; import java.awt.GradientPaint; import java.awt.LinearGradientPaint; import java.awt.Paint; import java.awt.RadialGradientPaint; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Arrays; /** * Utility code that relates to {@code Paint} objects. */ public class PaintUtils { /** * Private constructor prevents object creation. */ private PaintUtils() { } /** * Returns {@code true} if the two {@code Paint} objects are equal * OR both {@code null}. This method handles * {@code GradientPaint}, {@code LinearGradientPaint} and * {@code RadialGradientPaint} as a special cases, since those classes do * not override the {@code equals()} method. * * @param p1 paint 1 ({@code null} permitted). * @param p2 paint 2 ({@code null} permitted). * * @return A boolean. */ public static boolean equal(Paint p1, Paint p2) { if (p1 == p2) { return true; } // handle cases where either or both arguments are null if (p1 == null) { return (p2 == null); } if (p2 == null) { return false; } // handle GradientPaint as a special case... if (p1 instanceof GradientPaint && p2 instanceof GradientPaint) { GradientPaint gp1 = (GradientPaint) p1; GradientPaint gp2 = (GradientPaint) p2; return gp1.getColor1().equals(gp2.getColor1()) && gp1.getColor2().equals(gp2.getColor2()) && gp1.getPoint1().equals(gp2.getPoint1()) && gp1.getPoint2().equals(gp2.getPoint2()) && gp1.isCyclic() == gp2.isCyclic() && gp1.getTransparency() == gp1.getTransparency(); } else if (p1 instanceof LinearGradientPaint && p2 instanceof LinearGradientPaint) { LinearGradientPaint lgp1 = (LinearGradientPaint) p1; LinearGradientPaint lgp2 = (LinearGradientPaint) p2; return lgp1.getStartPoint().equals(lgp2.getStartPoint()) && lgp1.getEndPoint().equals(lgp2.getEndPoint()) && Arrays.equals(lgp1.getFractions(), lgp2.getFractions()) && Arrays.equals(lgp1.getColors(), lgp2.getColors()) && lgp1.getCycleMethod() == lgp2.getCycleMethod() && lgp1.getColorSpace() == lgp2.getColorSpace() && lgp1.getTransform().equals(lgp2.getTransform()); } else if (p1 instanceof RadialGradientPaint && p2 instanceof RadialGradientPaint) { RadialGradientPaint rgp1 = (RadialGradientPaint) p1; RadialGradientPaint rgp2 = (RadialGradientPaint) p2; return rgp1.getCenterPoint().equals(rgp2.getCenterPoint()) && rgp1.getRadius() == rgp2.getRadius() && rgp1.getFocusPoint().equals(rgp2.getFocusPoint()) && Arrays.equals(rgp1.getFractions(), rgp2.getFractions()) && Arrays.equals(rgp1.getColors(), rgp2.getColors()) && rgp1.getCycleMethod() == rgp2.getCycleMethod() && rgp1.getColorSpace() == rgp2.getColorSpace() && rgp1.getTransform().equals(rgp2.getTransform()); } else { return p1.equals(p2); } } /** * Converts a color into a string. If the color is equal to one of the * defined constant colors, that name is returned instead. Otherwise the * color is returned as hex-string. * * @param c the color. * @return the string for this color. */ public static String colorToString(Color c) { try { Field[] fields = Color.class.getFields(); for (int i = 0; i < fields.length; i++) { Field f = fields[i]; if (Modifier.isPublic(f.getModifiers()) && Modifier.isFinal(f.getModifiers()) && Modifier.isStatic(f.getModifiers())) { final String name = f.getName(); final Object oColor = f.get(null); if (oColor instanceof Color) { if (c.equals(oColor)) { return name; } } } } } catch (Exception e) { // } // no defined constant color, so this must be a user defined color final String color = Integer.toHexString(c.getRGB() & 0x00ffffff); final StringBuilder retval = new StringBuilder(7); retval.append("#"); final int fillUp = 6 - color.length(); for (int i = 0; i < fillUp; i++) { retval.append("0"); } retval.append(color); return retval.toString(); } /** * Converts a given string into a color. * * @param value the string, either a name or a hex-string. * @return the color. */ public static Color stringToColor(String value) { if (value == null) { return Color.BLACK; } try { // get color by hex or octal value return Color.decode(value); } catch (NumberFormatException nfe) { // if we can't decode lets try to get it by name try { // try to get a color by name using reflection final Field f = Color.class.getField(value); return (Color) f.get(null); } catch (Exception ce) { // if we can't get any color return black return Color.BLACK; } } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/PublicCloneable.java000066400000000000000000000040531463604235500277230ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * PublicCloneable.java * -------------------- * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributors: -; */ package org.jfree.chart.util; /** * An interface that exposes the clone() method. In order to support the * cloning of {@link org.jfree.chart.JFreeChart} instances, it is advisable to implement this * interface for custom plots, renderers and other chart components. If * this interface is not implemented, cloning will still be attempted via * reflection. */ public interface PublicCloneable extends Cloneable { /** * Returns a clone of the object. * * @return A clone. * * @throws CloneNotSupportedException if cloning is not supported for some reason. */ Object clone() throws CloneNotSupportedException; } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/RelativeDateFormat.java000066400000000000000000000374621463604235500304340ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * RelativeDateFormat.java * ----------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Michael Siemer; * */ package org.jfree.chart.util; import java.text.DateFormat; import java.text.DecimalFormat; import java.text.FieldPosition; import java.text.NumberFormat; import java.text.ParsePosition; import java.util.Date; import java.util.GregorianCalendar; /** * A formatter that formats dates to show the elapsed time relative to some * base date. */ public class RelativeDateFormat extends DateFormat { /** The base milliseconds for the elapsed time calculation. */ private long baseMillis; /** * A flag that controls whether or not a zero day count is displayed. */ private boolean showZeroDays; /** * A flag that controls whether or not a zero hour count is displayed. */ private boolean showZeroHours; /** * A formatter for the day count (most likely not critical until the * day count exceeds 999). */ private NumberFormat dayFormatter; /** * A prefix prepended to the start of the format if the relative date is * positive. */ private String positivePrefix; /** * A string appended after the day count. */ private String daySuffix; /** * A formatter for the hours. */ private NumberFormat hourFormatter; /** * A string appended after the hours. */ private String hourSuffix; /** * A formatter for the minutes. */ private NumberFormat minuteFormatter; /** * A string appended after the minutes. */ private String minuteSuffix; /** * A formatter for the seconds (and milliseconds). */ private NumberFormat secondFormatter; /** * A string appended after the seconds. */ private String secondSuffix; /** * A constant for the number of milliseconds in one hour. */ private static final long MILLISECONDS_IN_ONE_HOUR = 60 * 60 * 1000L; /** * A constant for the number of milliseconds in one day. */ private static final long MILLISECONDS_IN_ONE_DAY = 24 * MILLISECONDS_IN_ONE_HOUR; /** * Creates a new instance with base milliseconds set to zero. */ public RelativeDateFormat() { this(0L); } /** * Creates a new instance. * * @param time the date/time ({@code null} not permitted). */ public RelativeDateFormat(Date time) { this(time.getTime()); } /** * Creates a new instance. * * @param baseMillis the time zone ({@code null} not permitted). */ public RelativeDateFormat(long baseMillis) { super(); this.baseMillis = baseMillis; this.showZeroDays = false; this.showZeroHours = true; this.positivePrefix = ""; this.dayFormatter = NumberFormat.getNumberInstance(); this.daySuffix = "d"; this.hourFormatter = NumberFormat.getNumberInstance(); this.hourSuffix = "h"; this.minuteFormatter = NumberFormat.getNumberInstance(); this.minuteSuffix = "m"; this.secondFormatter = NumberFormat.getNumberInstance(); this.secondFormatter.setMaximumFractionDigits(3); this.secondFormatter.setMinimumFractionDigits(3); this.secondSuffix = "s"; // we don't use the calendar or numberFormat fields, but equals(Object) // is failing without them being non-null this.calendar = new GregorianCalendar(); this.numberFormat = new DecimalFormat("0"); } /** * Returns the base date/time used to calculate the elapsed time for * display. * * @return The base date/time in milliseconds since 1-Jan-1970. * * @see #setBaseMillis(long) */ public long getBaseMillis() { return this.baseMillis; } /** * Sets the base date/time used to calculate the elapsed time for display. * This should be specified in milliseconds using the same encoding as * {@code java.util.Date}. * * @param baseMillis the base date/time in milliseconds. * * @see #getBaseMillis() */ public void setBaseMillis(long baseMillis) { this.baseMillis = baseMillis; } /** * Returns the flag that controls whether or not zero day counts are * shown in the formatted output. * * @return The flag. * * @see #setShowZeroDays(boolean) */ public boolean getShowZeroDays() { return this.showZeroDays; } /** * Sets the flag that controls whether or not zero day counts are shown * in the formatted output. * * @param show the flag. * * @see #getShowZeroDays() */ public void setShowZeroDays(boolean show) { this.showZeroDays = show; } /** * Returns the flag that controls whether or not zero hour counts are * shown in the formatted output. * * @return The flag. * * @see #setShowZeroHours(boolean) */ public boolean getShowZeroHours() { return this.showZeroHours; } /** * Sets the flag that controls whether or not zero hour counts are shown * in the formatted output. * * @param show the flag. * * @see #getShowZeroHours() */ public void setShowZeroHours(boolean show) { this.showZeroHours = show; } /** * Returns the string that is prepended to the format if the relative time * is positive. * * @return The string (never {@code null}). * * @see #setPositivePrefix(String) */ public String getPositivePrefix() { return this.positivePrefix; } /** * Sets the string that is prepended to the format if the relative time is * positive. * * @param prefix the prefix ({@code null} not permitted). * * @see #getPositivePrefix() */ public void setPositivePrefix(String prefix) { Args.nullNotPermitted(prefix, "prefix"); this.positivePrefix = prefix; } /** * Sets the formatter for the days. * * @param formatter the formatter ({@code null} not permitted). */ public void setDayFormatter(NumberFormat formatter) { Args.nullNotPermitted(formatter, "formatter"); this.dayFormatter = formatter; } /** * Returns the string that is appended to the day count. * * @return The string. * * @see #setDaySuffix(String) */ public String getDaySuffix() { return this.daySuffix; } /** * Sets the string that is appended to the day count. * * @param suffix the suffix ({@code null} not permitted). * * @see #getDaySuffix() */ public void setDaySuffix(String suffix) { Args.nullNotPermitted(suffix, "suffix"); this.daySuffix = suffix; } /** * Sets the formatter for the hours. * * @param formatter the formatter ({@code null} not permitted). */ public void setHourFormatter(NumberFormat formatter) { Args.nullNotPermitted(formatter, "formatter"); this.hourFormatter = formatter; } /** * Returns the string that is appended to the hour count. * * @return The string. * * @see #setHourSuffix(String) */ public String getHourSuffix() { return this.hourSuffix; } /** * Sets the string that is appended to the hour count. * * @param suffix the suffix ({@code null} not permitted). * * @see #getHourSuffix() */ public void setHourSuffix(String suffix) { Args.nullNotPermitted(suffix, "suffix"); this.hourSuffix = suffix; } /** * Sets the formatter for the minutes. * * @param formatter the formatter ({@code null} not permitted). */ public void setMinuteFormatter(NumberFormat formatter) { Args.nullNotPermitted(formatter, "formatter"); this.minuteFormatter = formatter; } /** * Returns the string that is appended to the minute count. * * @return The string. * * @see #setMinuteSuffix(String) */ public String getMinuteSuffix() { return this.minuteSuffix; } /** * Sets the string that is appended to the minute count. * * @param suffix the suffix ({@code null} not permitted). * * @see #getMinuteSuffix() */ public void setMinuteSuffix(String suffix) { Args.nullNotPermitted(suffix, "suffix"); this.minuteSuffix = suffix; } /** * Returns the string that is appended to the second count. * * @return The string. * * @see #setSecondSuffix(String) */ public String getSecondSuffix() { return this.secondSuffix; } /** * Sets the string that is appended to the second count. * * @param suffix the suffix ({@code null} not permitted). * * @see #getSecondSuffix() */ public void setSecondSuffix(String suffix) { Args.nullNotPermitted(suffix, "suffix"); this.secondSuffix = suffix; } /** * Sets the formatter for the seconds and milliseconds. * * @param formatter the formatter ({@code null} not permitted). */ public void setSecondFormatter(NumberFormat formatter) { Args.nullNotPermitted(formatter, "formatter"); this.secondFormatter = formatter; } /** * Formats the given date as the amount of elapsed time (relative to the * base date specified in the constructor). * * @param date the date. * @param toAppendTo the string buffer. * @param fieldPosition the field position. * * @return The formatted date. */ @Override public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) { long currentMillis = date.getTime(); long elapsed = currentMillis - this.baseMillis; String signPrefix; if (elapsed < 0) { elapsed *= -1L; signPrefix = "-"; } else { signPrefix = this.positivePrefix; } long days = elapsed / MILLISECONDS_IN_ONE_DAY; elapsed = elapsed - (days * MILLISECONDS_IN_ONE_DAY); long hours = elapsed / MILLISECONDS_IN_ONE_HOUR; elapsed = elapsed - (hours * MILLISECONDS_IN_ONE_HOUR); long minutes = elapsed / 60000L; elapsed = elapsed - (minutes * 60000L); double seconds = elapsed / 1000.0; toAppendTo.append(signPrefix); if (days != 0 || this.showZeroDays) { toAppendTo.append(this.dayFormatter.format(days)) .append(getDaySuffix()); } if (hours != 0 || this.showZeroHours) { toAppendTo.append(this.hourFormatter.format(hours)) .append(getHourSuffix()); } toAppendTo.append(this.minuteFormatter.format(minutes)) .append(getMinuteSuffix()); toAppendTo.append(this.secondFormatter.format(seconds)) .append(getSecondSuffix()); return toAppendTo; } /** * Parses the given string (not implemented). * * @param source the date string. * @param pos the parse position. * * @return {@code null}, as this method has not been implemented. */ @Override public Date parse(String source, ParsePosition pos) { return null; } /** * Tests this formatter for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof RelativeDateFormat)) { return false; } if (!super.equals(obj)) { return false; } RelativeDateFormat that = (RelativeDateFormat) obj; if (this.baseMillis != that.baseMillis) { return false; } if (this.showZeroDays != that.showZeroDays) { return false; } if (this.showZeroHours != that.showZeroHours) { return false; } if (!this.positivePrefix.equals(that.positivePrefix)) { return false; } if (!this.daySuffix.equals(that.daySuffix)) { return false; } if (!this.hourSuffix.equals(that.hourSuffix)) { return false; } if (!this.minuteSuffix.equals(that.minuteSuffix)) { return false; } if (!this.secondSuffix.equals(that.secondSuffix)) { return false; } if (!this.dayFormatter.equals(that.dayFormatter)) { return false; } if (!this.hourFormatter.equals(that.hourFormatter)) { return false; } if (!this.minuteFormatter.equals(that.minuteFormatter)) { return false; } if (!this.secondFormatter.equals(that.secondFormatter)) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = 193; result = 37 * result + (int) (this.baseMillis ^ (this.baseMillis >>> 32)); result = 37 * result + this.positivePrefix.hashCode(); result = 37 * result + this.daySuffix.hashCode(); result = 37 * result + this.hourSuffix.hashCode(); result = 37 * result + this.minuteSuffix.hashCode(); result = 37 * result + this.secondSuffix.hashCode(); result = 37 * result + this.secondFormatter.hashCode(); return result; } /** * Returns a clone of this instance. * * @return A clone. */ @Override public Object clone() { RelativeDateFormat clone = (RelativeDateFormat) super.clone(); clone.dayFormatter = (NumberFormat) this.dayFormatter.clone(); clone.secondFormatter = (NumberFormat) this.secondFormatter.clone(); return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/ResourceBundleWrapper.java000066400000000000000000000132701463604235500311630ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * ResourceBundleWrapper.java * -------------------------- * (C)opyright 2008-present, by Jess Thrysoee and Contributors. * * Original Author: Jess Thrysoee; * Contributor(s): David Gilbert; * */ package org.jfree.chart.util; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.ResourceBundle; /** * Wrapper of ResourceBundle.getBundle() methods. This wrapper is introduced to * avoid a dramatic performance penalty by superfluous resource (and classes * loaded by Class.forName) lookups on web server in applets. * *

 * public class AppletC extends javax.swing.JApplet {
 *    public void init() {
 *       ResourceBundleWrapper.removeCodeBase(getCodeBase(),
 *               (URLClassLoader) getClass().getClassLoader());
 *    ...
 * 
* * @see * Bug ID: 4243379 * @see * Bug ID: 4668479 */ public class ResourceBundleWrapper { /** * A special class loader with no code base lookup. This field may be * {@code null} (the field is only initialised if removeCodeBase() is * called from an applet). */ private static URLClassLoader noCodeBaseClassLoader; /** * Private constructor. */ private ResourceBundleWrapper() { // all methods are static, no need to instantiate } /** * Instantiate a {@link URLClassLoader} for resource lookups where the * codeBase URL is removed. This method is typically called from an * applet's init() method. If this method is never called, the * {@code getBundle()} methods map to the standard * {@link ResourceBundle} lookup methods. * * @param codeBase the codeBase URL. * @param urlClassLoader the class loader. */ public static void removeCodeBase(URL codeBase, URLClassLoader urlClassLoader) { List urlsNoBase = new ArrayList(); URL[] urls = urlClassLoader.getURLs(); for (int i = 0; i < urls.length; i++) { if (!urls[i].sameFile(codeBase)) { urlsNoBase.add(urls[i]); } } // substitute the filtered URL list URL[] urlsNoBaseArray = (URL[]) urlsNoBase.toArray(new URL[0]); noCodeBaseClassLoader = URLClassLoader.newInstance(urlsNoBaseArray); } /** * Finds and returns the specified resource bundle. * * @param baseName the base name. * * @return The resource bundle. */ public static ResourceBundle getBundle(String baseName) { // the noCodeBaseClassLoader is configured by a call to the // removeCodeBase() method, typically in the init() method of an // applet... if (noCodeBaseClassLoader != null) { return ResourceBundle.getBundle(baseName, Locale.getDefault(), noCodeBaseClassLoader); } else { // standard ResourceBundle behaviour return ResourceBundle.getBundle(baseName); } } /** * Finds and returns the specified resource bundle. * * @param baseName the base name. * @param locale the locale. * * @return The resource bundle. */ public static ResourceBundle getBundle(String baseName, Locale locale) { // the noCodeBaseClassLoader is configured by a call to the // removeCodeBase() method, typically in the init() method of an // applet... if (noCodeBaseClassLoader != null) { return ResourceBundle.getBundle(baseName, locale, noCodeBaseClassLoader); } else { // standard ResourceBundle behaviour return ResourceBundle.getBundle(baseName, locale); } } /** * Maps directly to {@code ResourceBundle.getBundle(baseName, locale, * loader)}. * * @param baseName the base name. * @param locale the locale. * @param loader the class loader. * * @return The resource bundle. */ public static ResourceBundle getBundle(String baseName, Locale locale, ClassLoader loader) { return ResourceBundle.getBundle(baseName, locale, loader); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/Rotation.java000066400000000000000000000045441463604235500265040ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * */ package org.jfree.chart.util; /** * Represents a direction of rotation ({@code CLOCKWISE} or * {@code ANTICLOCKWISE}). */ public enum Rotation { /** Clockwise. */ CLOCKWISE("Rotation.CLOCKWISE", -1.0), /** The reverse order renders the primary dataset first. */ ANTICLOCKWISE("Rotation.ANTICLOCKWISE", 1.0); /** The name. */ private String name; /** * The factor (-1.0 for {@code CLOCKWISE} and 1.0 for * {@code ANTICLOCKWISE}). */ private double factor; /** * Private constructor. * * @param name the name. * @param factor the rotation factor. */ Rotation(String name, double factor) { this.name = name; this.factor = factor; } /** * Returns a string representing the object. * * @return the string (never {@code null}). */ @Override public String toString() { return this.name; } /** * Returns the rotation factor, which is -1.0 for {@code CLOCKWISE} * and 1.0 for {@code ANTICLOCKWISE}. * * @return the rotation factor. */ public double getFactor() { return this.factor; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/SerialUtils.java000066400000000000000000000550331463604235500271440ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * SerialUtils.java * ---------------- * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributors: -; */ package org.jfree.chart.util; import java.awt.AlphaComposite; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Composite; import java.awt.GradientPaint; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Arc2D; import java.awt.geom.Ellipse2D; import java.awt.geom.GeneralPath; import java.awt.geom.Line2D; import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.text.AttributedCharacterIterator; import java.text.AttributedString; import java.text.CharacterIterator; import java.util.HashMap; import java.util.Map; /** * A class containing useful utility methods relating to serialization. */ public class SerialUtils { /** * Private constructor prevents object creation. */ private SerialUtils() { } /** * Returns {@code true} if a class implements {@code Serializable} * and {@code false} otherwise. * * @param c the class. * * @return A boolean. */ public static boolean isSerializable(Class c) { return (Serializable.class.isAssignableFrom(c)); } /** * Reads a {@code Paint} object that has been serialised by the * {@link #writePaint(Paint, ObjectOutputStream)} method. * * @param stream the input stream ({@code null} not permitted). * * @return The paint object (possibly {@code null}). * * @throws IOException if there is an I/O problem. * @throws ClassNotFoundException if there is a problem loading a class. */ public static Paint readPaint(ObjectInputStream stream) throws IOException, ClassNotFoundException { if (stream == null) { throw new IllegalArgumentException("Null 'stream' argument."); } Paint result = null; boolean isNull = stream.readBoolean(); if (!isNull) { final Class c = (Class) stream.readObject(); if (isSerializable(c)) { result = (Paint) stream.readObject(); } else if (c.equals(GradientPaint.class)) { float x1 = stream.readFloat(); float y1 = stream.readFloat(); Color c1 = (Color) stream.readObject(); float x2 = stream.readFloat(); float y2 = stream.readFloat(); Color c2 = (Color) stream.readObject(); boolean isCyclic = stream.readBoolean(); result = new GradientPaint(x1, y1, c1, x2, y2, c2, isCyclic); } } return result; } /** * Serialises a {@code Paint} object. * * @param paint the paint object ({@code null} permitted). * @param stream the output stream ({@code null} not permitted). * * @throws IOException if there is an I/O error. */ public static void writePaint(Paint paint, ObjectOutputStream stream) throws IOException { if (stream == null) { throw new IllegalArgumentException("Null 'stream' argument."); } if (paint != null) { stream.writeBoolean(false); stream.writeObject(paint.getClass()); if (paint instanceof Serializable) { stream.writeObject(paint); } else if (paint instanceof GradientPaint) { final GradientPaint gp = (GradientPaint) paint; stream.writeFloat((float) gp.getPoint1().getX()); stream.writeFloat((float) gp.getPoint1().getY()); stream.writeObject(gp.getColor1()); stream.writeFloat((float) gp.getPoint2().getX()); stream.writeFloat((float) gp.getPoint2().getY()); stream.writeObject(gp.getColor2()); stream.writeBoolean(gp.isCyclic()); } } else { stream.writeBoolean(true); } } /** * Reads a {@code Stroke} object that has been serialised by the * {@link #writeStroke(Stroke, ObjectOutputStream)} method. * * @param stream the input stream ({@code null} not permitted). * * @return The stroke object (possibly {@code null}). * * @throws IOException if there is an I/O problem. * @throws ClassNotFoundException if there is a problem loading a class. */ public static Stroke readStroke(ObjectInputStream stream) throws IOException, ClassNotFoundException { if (stream == null) { throw new IllegalArgumentException("Null 'stream' argument."); } Stroke result = null; boolean isNull = stream.readBoolean(); if (!isNull) { Class c = (Class) stream.readObject(); if (c.equals(BasicStroke.class)) { float width = stream.readFloat(); int cap = stream.readInt(); int join = stream.readInt(); float miterLimit = stream.readFloat(); float[] dash = (float[]) stream.readObject(); float dashPhase = stream.readFloat(); result = new BasicStroke(width, cap, join, miterLimit, dash, dashPhase); } else { result = (Stroke) stream.readObject(); } } return result; } /** * Serialises a {@code Stroke} object. This code handles the * {@code BasicStroke} class which is the only {@code Stroke} * implementation provided by the JDK (and isn't directly * {@code Serializable}). * * @param stroke the stroke object ({@code null} permitted). * @param stream the output stream ({@code null} not permitted). * * @throws IOException if there is an I/O error. */ public static void writeStroke(Stroke stroke, ObjectOutputStream stream) throws IOException { if (stream == null) { throw new IllegalArgumentException("Null 'stream' argument."); } if (stroke != null) { stream.writeBoolean(false); if (stroke instanceof BasicStroke) { BasicStroke s = (BasicStroke) stroke; stream.writeObject(BasicStroke.class); stream.writeFloat(s.getLineWidth()); stream.writeInt(s.getEndCap()); stream.writeInt(s.getLineJoin()); stream.writeFloat(s.getMiterLimit()); stream.writeObject(s.getDashArray()); stream.writeFloat(s.getDashPhase()); } else { stream.writeObject(stroke.getClass()); stream.writeObject(stroke); } } else { stream.writeBoolean(true); } } /** * Reads a {@code Composite} object that has been serialised by the * {@link #writeComposite(Composite, ObjectOutputStream)} * method. * * @param stream the input stream ({@code null} not permitted). * * @return The composite object (possibly {@code null}). * * @throws IOException if there is an I/O problem. * @throws ClassNotFoundException if there is a problem loading a class. */ public static Composite readComposite(ObjectInputStream stream) throws IOException, ClassNotFoundException { if (stream == null) { throw new IllegalArgumentException("Null 'stream' argument."); } Composite result = null; boolean isNull = stream.readBoolean(); if (!isNull) { Class c = (Class) stream.readObject(); if (isSerializable(c)) { result = (Composite) stream.readObject(); } else if (c.equals(AlphaComposite.class)) { int rule = stream.readInt(); float alpha = stream.readFloat(); result = AlphaComposite.getInstance(rule, alpha); } } return result; } /** * Serialises a {@code Composite} object. * * @param composite the composite object ({@code null} permitted). * @param stream the output stream ({@code null} not permitted). * * @throws IOException if there is an I/O error. */ public static void writeComposite(Composite composite, ObjectOutputStream stream) throws IOException { if (stream == null) { throw new IllegalArgumentException("Null 'stream' argument."); } if (composite != null) { stream.writeBoolean(false); stream.writeObject(composite.getClass()); if (composite instanceof Serializable) { stream.writeObject(composite); } else if (composite instanceof AlphaComposite) { AlphaComposite ac = (AlphaComposite) composite; stream.writeInt(ac.getRule()); stream.writeFloat(ac.getAlpha()); } } else { stream.writeBoolean(true); } } /** * Reads a {@code Shape} object that has been serialised by the * {@link #writeShape(Shape, ObjectOutputStream)} method. * * @param stream the input stream ({@code null} not permitted). * * @return The shape object (possibly {@code null}). * * @throws IOException if there is an I/O problem. * @throws ClassNotFoundException if there is a problem loading a class. */ public static Shape readShape(ObjectInputStream stream) throws IOException, ClassNotFoundException { if (stream == null) { throw new IllegalArgumentException("Null 'stream' argument."); } Shape result = null; boolean isNull = stream.readBoolean(); if (!isNull) { Class c = (Class) stream.readObject(); if (c.equals(Line2D.class)) { double x1 = stream.readDouble(); double y1 = stream.readDouble(); double x2 = stream.readDouble(); double y2 = stream.readDouble(); result = new Line2D.Double(x1, y1, x2, y2); } else if (c.equals(Rectangle2D.class)) { double x = stream.readDouble(); double y = stream.readDouble(); double w = stream.readDouble(); double h = stream.readDouble(); result = new Rectangle2D.Double(x, y, w, h); } else if (c.equals(Ellipse2D.class)) { double x = stream.readDouble(); double y = stream.readDouble(); double w = stream.readDouble(); double h = stream.readDouble(); result = new Ellipse2D.Double(x, y, w, h); } else if (c.equals(Arc2D.class)) { double x = stream.readDouble(); double y = stream.readDouble(); double w = stream.readDouble(); double h = stream.readDouble(); double as = stream.readDouble(); // Angle Start double ae = stream.readDouble(); // Angle Extent int at = stream.readInt(); // Arc type result = new Arc2D.Double(x, y, w, h, as, ae, at); } else if (c.equals(GeneralPath.class)) { GeneralPath gp = new GeneralPath(); float[] args = new float[6]; boolean hasNext = stream.readBoolean(); while (!hasNext) { int type = stream.readInt(); for (int i = 0; i < 6; i++) { args[i] = stream.readFloat(); } switch (type) { case PathIterator.SEG_MOVETO : gp.moveTo(args[0], args[1]); break; case PathIterator.SEG_LINETO : gp.lineTo(args[0], args[1]); break; case PathIterator.SEG_CUBICTO : gp.curveTo(args[0], args[1], args[2], args[3], args[4], args[5]); break; case PathIterator.SEG_QUADTO : gp.quadTo(args[0], args[1], args[2], args[3]); break; case PathIterator.SEG_CLOSE : gp.closePath(); break; default : throw new RuntimeException( "JFreeChart - No path exists"); } gp.setWindingRule(stream.readInt()); hasNext = stream.readBoolean(); } result = gp; } else { result = (Shape) stream.readObject(); } } return result; } /** * Serialises a {@code Shape} object. * * @param shape the shape object ({@code null} permitted). * @param stream the output stream ({@code null} not permitted). * * @throws IOException if there is an I/O error. */ public static void writeShape(Shape shape, ObjectOutputStream stream) throws IOException { if (stream == null) { throw new IllegalArgumentException("Null 'stream' argument."); } if (shape != null) { stream.writeBoolean(false); if (shape instanceof Line2D) { final Line2D line = (Line2D) shape; stream.writeObject(Line2D.class); stream.writeDouble(line.getX1()); stream.writeDouble(line.getY1()); stream.writeDouble(line.getX2()); stream.writeDouble(line.getY2()); } else if (shape instanceof Rectangle2D) { final Rectangle2D rectangle = (Rectangle2D) shape; stream.writeObject(Rectangle2D.class); stream.writeDouble(rectangle.getX()); stream.writeDouble(rectangle.getY()); stream.writeDouble(rectangle.getWidth()); stream.writeDouble(rectangle.getHeight()); } else if (shape instanceof Ellipse2D) { final Ellipse2D ellipse = (Ellipse2D) shape; stream.writeObject(Ellipse2D.class); stream.writeDouble(ellipse.getX()); stream.writeDouble(ellipse.getY()); stream.writeDouble(ellipse.getWidth()); stream.writeDouble(ellipse.getHeight()); } else if (shape instanceof Arc2D) { final Arc2D arc = (Arc2D) shape; stream.writeObject(Arc2D.class); stream.writeDouble(arc.getX()); stream.writeDouble(arc.getY()); stream.writeDouble(arc.getWidth()); stream.writeDouble(arc.getHeight()); stream.writeDouble(arc.getAngleStart()); stream.writeDouble(arc.getAngleExtent()); stream.writeInt(arc.getArcType()); } else if (shape instanceof GeneralPath) { stream.writeObject(GeneralPath.class); final PathIterator pi = shape.getPathIterator(null); final float[] args = new float[6]; stream.writeBoolean(pi.isDone()); while (!pi.isDone()) { final int type = pi.currentSegment(args); stream.writeInt(type); // TODO: could write this to only stream the values // required for the segment type for (int i = 0; i < 6; i++) { stream.writeFloat(args[i]); } stream.writeInt(pi.getWindingRule()); pi.next(); stream.writeBoolean(pi.isDone()); } } else { stream.writeObject(shape.getClass()); stream.writeObject(shape); } } else { stream.writeBoolean(true); } } /** * Reads a {@code Point2D} object that has been serialised by the * {@link #writePoint2D(Point2D, ObjectOutputStream)} method. * * @param stream the input stream ({@code null} not permitted). * * @return The point object (possibly {@code null}). * * @throws IOException if there is an I/O problem. */ public static Point2D readPoint2D(ObjectInputStream stream) throws IOException { if (stream == null) { throw new IllegalArgumentException("Null 'stream' argument."); } Point2D result = null; boolean isNull = stream.readBoolean(); if (!isNull) { double x = stream.readDouble(); double y = stream.readDouble(); result = new Point2D.Double(x, y); } return result; } /** * Serialises a {@code Point2D} object. * * @param p the point object ({@code null} permitted). * @param stream the output stream ({@code null} not permitted). * * @throws IOException if there is an I/O error. */ public static void writePoint2D(Point2D p, ObjectOutputStream stream) throws IOException { if (stream == null) { throw new IllegalArgumentException("Null 'stream' argument."); } if (p != null) { stream.writeBoolean(false); stream.writeDouble(p.getX()); stream.writeDouble(p.getY()); } else { stream.writeBoolean(true); } } /** * Reads a {@code AttributedString} object that has been serialised by * the {@link #writeAttributedString(AttributedString, * ObjectOutputStream)} method. * * @param stream the input stream ({@code null} not permitted). * * @return The attributed string object (possibly {@code null}). * * @throws IOException if there is an I/O problem. * @throws ClassNotFoundException if there is a problem loading a class. */ public static AttributedString readAttributedString( ObjectInputStream stream) throws IOException, ClassNotFoundException { if (stream == null) { throw new IllegalArgumentException("Null 'stream' argument."); } AttributedString result = null; final boolean isNull = stream.readBoolean(); if (!isNull) { // read string and attributes then create result String plainStr = (String) stream.readObject(); result = new AttributedString(plainStr); char c = stream.readChar(); int start = 0; while (c != CharacterIterator.DONE) { int limit = stream.readInt(); Map atts = (Map) stream.readObject(); result.addAttributes(atts, start, limit); start = limit; c = stream.readChar(); } } return result; } /** * Serialises an {@code AttributedString} object. * * @param as the attributed string object ({@code null} permitted). * @param stream the output stream ({@code null} not permitted). * * @throws IOException if there is an I/O error. */ public static void writeAttributedString(AttributedString as, ObjectOutputStream stream) throws IOException { if (stream == null) { throw new IllegalArgumentException("Null 'stream' argument."); } if (as != null) { stream.writeBoolean(false); AttributedCharacterIterator aci = as.getIterator(); // build a plain string from aci // then write the string StringBuffer plainStr = new StringBuffer(); char current = aci.first(); while (current != CharacterIterator.DONE) { plainStr = plainStr.append(current); current = aci.next(); } stream.writeObject(plainStr.toString()); // then write the attributes and limits for each run current = aci.first(); int begin = aci.getBeginIndex(); while (current != CharacterIterator.DONE) { // write the current character - when the reader sees that this // is not CharacterIterator.DONE, it will know to read the // run limits and attributes stream.writeChar(current); // now write the limit, adjusted as if beginIndex is zero int limit = aci.getRunLimit(); stream.writeInt(limit - begin); // now write the attribute set Map atts = new HashMap(aci.getAttributes()); stream.writeObject(atts); current = aci.setIndex(limit); } // write a character that signals to the reader that all runs // are done... stream.writeChar(CharacterIterator.DONE); } else { // write a flag that indicates a null stream.writeBoolean(true); } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/ShadowGenerator.java000066400000000000000000000043521463604235500277760ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * ShadowGenerator.java * -------------------- * (C) Copyright 2009-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.util; import java.awt.image.BufferedImage; /** * An interface that defines the API for a shadow generator. Some plot * classes use this to create drop shadows. */ public interface ShadowGenerator { /** * Creates and returns an image containing the drop shadow for the * specified source image. * * @param source the source image. * * @return A new image containing the shadow. */ BufferedImage createDropShadow(BufferedImage source); /** * Calculates the x-offset for drawing the shadow image relative to the * source. * * @return The x-offset. */ int calculateOffsetX(); /** * Calculates the y-offset for drawing the shadow image relative to the * source. * * @return The y-offset. */ int calculateOffsetY(); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/ShapeList.java000066400000000000000000000112001463604235500265640ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * ShapeList.java * -------------- * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributors: -; */ package org.jfree.chart.util; import java.awt.Shape; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; /** * A table of {@link Shape} objects. */ public class ShapeList extends AbstractObjectList { /** * Creates a new list. */ public ShapeList() { super(); } /** * Returns a {@link Shape} object from the list. * * @param index the index (zero-based). * * @return The object. */ public Shape getShape(int index) { return (Shape) get(index); } /** * Sets the {@link Shape} for an item in the list. The list is expanded * if necessary. * * @param index the index (zero-based). * @param shape the {@link Shape}. */ public void setShape(int index, Shape shape) { set(index, shape); } /** * Returns an independent copy of the list. * * @return A clone. * * @throws CloneNotSupportedException if an item in the list does not * support cloning. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Tests the list for equality with another object (typically also a list). * * @param obj the other object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof ShapeList)) { return false; } ShapeList that = (ShapeList) obj; int listSize = size(); for (int i = 0; i < listSize; i++) { if (!ShapeUtils.equal((Shape) get(i), (Shape) that.get(i))) { return false; } } return true; } /** * Returns a hash code value for the object. * * @return the hashcode */ @Override public int hashCode() { return super.hashCode(); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); final int count = size(); stream.writeInt(count); for (int i = 0; i < count; i++) { final Shape shape = getShape(i); if (shape != null) { stream.writeInt(i); SerialUtils.writeShape(shape, stream); } else { stream.writeInt(-1); } } } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); final int count = stream.readInt(); for (int i = 0; i < count; i++) { final int index = stream.readInt(); if (index != -1) { setShape(index, SerialUtils.readShape(stream)); } } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/ShapeUtils.java000066400000000000000000000445371463604235500267740ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * ShapeUtils.java * --------------- * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributors: -; */ package org.jfree.chart.util; import java.awt.Graphics2D; import java.awt.Polygon; import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.Arc2D; import java.awt.geom.Ellipse2D; import java.awt.geom.GeneralPath; import java.awt.geom.Line2D; import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.Arrays; import java.util.Objects; import org.jfree.chart.ui.RectangleAnchor; /** * Utility methods for {@link Shape} objects. */ public class ShapeUtils { /** * Prevents instantiation. */ private ShapeUtils() { } /** * Returns a clone of the specified shape, or {@code null}. At the * current time, this method supports cloning for instances of * {@code Line2D}, {@code RectangularShape}, {@code Area} * and {@code GeneralPath}. *

* {@code RectangularShape} includes {@code Arc2D}, * {@code Ellipse2D}, {@code Rectangle2D}, * {@code RoundRectangle2D}. * * @param shape the shape to clone ({@code null} permitted, * returns {@code null}). * * @return A clone or {@code null}. */ public static Shape clone(Shape shape) { if (shape instanceof Cloneable) { try { return (Shape) ObjectUtils.clone(shape); } catch (CloneNotSupportedException cnse) { } } final Shape result = null; return result; } /** * Tests two shapes for equality. If both shapes are {@code null}, * this method will return {@code true}. *

* In the current implementation, the following shapes are supported: * {@code Ellipse2D}, {@code Line2D} and {@code Rectangle2D} * (implicit). * * @param s1 the first shape ({@code null} permitted). * @param s2 the second shape ({@code null} permitted). * * @return A boolean. */ public static boolean equal(Shape s1, Shape s2) { if (s1 instanceof Line2D && s2 instanceof Line2D) { return equal((Line2D) s1, (Line2D) s2); } else if (s1 instanceof Ellipse2D && s2 instanceof Ellipse2D) { return equal((Ellipse2D) s1, (Ellipse2D) s2); } else if (s1 instanceof Arc2D && s2 instanceof Arc2D) { return equal((Arc2D) s1, (Arc2D) s2); } else if (s1 instanceof Polygon && s2 instanceof Polygon) { return equal((Polygon) s1, (Polygon) s2); } else if (s1 instanceof GeneralPath && s2 instanceof GeneralPath) { return equal((GeneralPath) s1, (GeneralPath) s2); } else { // this will handle Rectangle2D... return Objects.equals(s1, s2); } } /** * Compares two lines are returns {@code true} if they are equal or * both {@code null}. * * @param l1 the first line ({@code null} permitted). * @param l2 the second line ({@code null} permitted). * * @return A boolean. */ public static boolean equal(Line2D l1, Line2D l2) { if (l1 == null) { return (l2 == null); } if (l2 == null) { return false; } if (!l1.getP1().equals(l2.getP1())) { return false; } if (!l1.getP2().equals(l2.getP2())) { return false; } return true; } /** * Compares two ellipses and returns {@code true} if they are equal or * both {@code null}. * * @param e1 the first ellipse ({@code null} permitted). * @param e2 the second ellipse ({@code null} permitted). * * @return A boolean. */ public static boolean equal(Ellipse2D e1, Ellipse2D e2) { if (e1 == null) { return (e2 == null); } if (e2 == null) { return false; } if (!e1.getFrame().equals(e2.getFrame())) { return false; } return true; } /** * Compares two arcs and returns {@code true} if they are equal or * both {@code null}. * * @param a1 the first arc ({@code null} permitted). * @param a2 the second arc ({@code null} permitted). * * @return A boolean. */ public static boolean equal(Arc2D a1, Arc2D a2) { if (a1 == null) { return (a2 == null); } if (a2 == null) { return false; } if (!a1.getFrame().equals(a2.getFrame())) { return false; } if (a1.getAngleStart() != a2.getAngleStart()) { return false; } if (a1.getAngleExtent() != a2.getAngleExtent()) { return false; } if (a1.getArcType() != a2.getArcType()) { return false; } return true; } /** * Tests two polygons for equality. If both are {@code null} this * method returns {@code true}. * * @param p1 polygon 1 ({@code null} permitted). * @param p2 polygon 2 ({@code null} permitted). * * @return A boolean. */ public static boolean equal(Polygon p1, Polygon p2) { if (p1 == null) { return (p2 == null); } if (p2 == null) { return false; } if (p1.npoints != p2.npoints) { return false; } if (!Arrays.equals(p1.xpoints, p2.xpoints)) { return false; } if (!Arrays.equals(p1.ypoints, p2.ypoints)) { return false; } return true; } /** * Tests two polygons for equality. If both are {@code null} this * method returns {@code true}. * * @param p1 path 1 ({@code null} permitted). * @param p2 path 2 ({@code null} permitted). * * @return A boolean. */ public static boolean equal(GeneralPath p1, GeneralPath p2) { if (p1 == null) { return (p2 == null); } if (p2 == null) { return false; } if (p1.getWindingRule() != p2.getWindingRule()) { return false; } PathIterator iterator1 = p1.getPathIterator(null); PathIterator iterator2 = p2.getPathIterator(null); double[] d1 = new double[6]; double[] d2 = new double[6]; boolean done = iterator1.isDone() && iterator2.isDone(); while (!done) { if (iterator1.isDone() != iterator2.isDone()) { return false; } int seg1 = iterator1.currentSegment(d1); int seg2 = iterator2.currentSegment(d2); if (seg1 != seg2) { return false; } if (!Arrays.equals(d1, d2)) { return false; } iterator1.next(); iterator2.next(); done = iterator1.isDone() && iterator2.isDone(); } return true; } /** * Creates and returns a translated shape. * * @param shape the shape ({@code null} not permitted). * @param transX the x translation (in Java2D space). * @param transY the y translation (in Java2D space). * * @return The translated shape. */ public static Shape createTranslatedShape(Shape shape, double transX, double transY) { if (shape == null) { throw new IllegalArgumentException("Null 'shape' argument."); } final AffineTransform transform = AffineTransform.getTranslateInstance( transX, transY); return transform.createTransformedShape(shape); } /** * Translates a shape to a new location such that the anchor point * (relative to the rectangular bounds of the shape) aligns with the * specified (x, y) coordinate in Java2D space. * * @param shape the shape ({@code null} not permitted). * @param anchor the anchor ({@code null} not permitted). * @param locationX the x-coordinate (in Java2D space). * @param locationY the y-coordinate (in Java2D space). * * @return A new and translated shape. */ public static Shape createTranslatedShape(Shape shape, RectangleAnchor anchor, double locationX, double locationY) { if (shape == null) { throw new IllegalArgumentException("Null 'shape' argument."); } if (anchor == null) { throw new IllegalArgumentException("Null 'anchor' argument."); } Point2D anchorPoint = anchor.getAnchorPoint(shape.getBounds2D()); final AffineTransform transform = AffineTransform.getTranslateInstance( locationX - anchorPoint.getX(), locationY - anchorPoint.getY()); return transform.createTransformedShape(shape); } /** * Rotates a shape about the specified coordinates. * * @param base the shape ({@code null} permitted, returns * {@code null}). * @param angle the angle (in radians). * @param x the x coordinate for the rotation point (in Java2D space). * @param y the y coordinate for the rotation point (in Java2D space). * * @return the rotated shape. */ public static Shape rotateShape(Shape base, double angle, float x, float y) { if (base == null) { return null; } final AffineTransform rotate = AffineTransform.getRotateInstance( angle, x, y); final Shape result = rotate.createTransformedShape(base); return result; } /** * Draws a shape with the specified rotation about {@code (x, y)}. * * @param g2 the graphics device ({@code null} not permitted). * @param shape the shape ({@code null} not permitted). * @param angle the angle (in radians). * @param x the x coordinate for the rotation point. * @param y the y coordinate for the rotation point. */ public static void drawRotatedShape(Graphics2D g2, Shape shape, double angle, float x, float y) { AffineTransform saved = g2.getTransform(); AffineTransform rotate = AffineTransform.getRotateInstance(angle, x, y); g2.transform(rotate); g2.draw(shape); g2.setTransform(saved); } /** A useful constant used internally. */ private static final float SQRT2 = (float) Math.pow(2.0, 0.5); /** * Creates a diagonal cross shape. * * @param l the length of each 'arm'. * @param t the thickness. * * @return A diagonal cross shape. */ public static Shape createDiagonalCross(float l, float t) { final GeneralPath p0 = new GeneralPath(); p0.moveTo(-l - t, -l + t); p0.lineTo(-l + t, -l - t); p0.lineTo(0.0f, -t * SQRT2); p0.lineTo(l - t, -l - t); p0.lineTo(l + t, -l + t); p0.lineTo(t * SQRT2, 0.0f); p0.lineTo(l + t, l - t); p0.lineTo(l - t, l + t); p0.lineTo(0.0f, t * SQRT2); p0.lineTo(-l + t, l + t); p0.lineTo(-l - t, l - t); p0.lineTo(-t * SQRT2, 0.0f); p0.closePath(); return p0; } /** * Creates a diagonal cross shape. * * @param l the length of each 'arm'. * @param t the thickness. * * @return A diagonal cross shape. */ public static Shape createRegularCross(float l, float t) { final GeneralPath p0 = new GeneralPath(); p0.moveTo(-l, t); p0.lineTo(-t, t); p0.lineTo(-t, l); p0.lineTo(t, l); p0.lineTo(t, t); p0.lineTo(l, t); p0.lineTo(l, -t); p0.lineTo(t, -t); p0.lineTo(t, -l); p0.lineTo(-t, -l); p0.lineTo(-t, -t); p0.lineTo(-l, -t); p0.closePath(); return p0; } /** * Creates a diamond shape. * * @param s the size factor (equal to half the height of the diamond). * * @return A diamond shape. */ public static Shape createDiamond(float s) { final GeneralPath p0 = new GeneralPath(); p0.moveTo(0.0f, -s); p0.lineTo(s, 0.0f); p0.lineTo(0.0f, s); p0.lineTo(-s, 0.0f); p0.closePath(); return p0; } /** * Creates a triangle shape that points upwards. * * @param s the size factor (equal to half the height of the triangle). * * @return A triangle shape. */ public static Shape createUpTriangle(float s) { final GeneralPath p0 = new GeneralPath(); p0.moveTo(0.0f, -s); p0.lineTo(s, s); p0.lineTo(-s, s); p0.closePath(); return p0; } /** * Creates a triangle shape that points downwards. * * @param s the size factor (equal to half the height of the triangle). * * @return A triangle shape. */ public static Shape createDownTriangle(float s) { final GeneralPath p0 = new GeneralPath(); p0.moveTo(0.0f, s); p0.lineTo(s, -s); p0.lineTo(-s, -s); p0.closePath(); return p0; } /** * Creates a region surrounding a line segment by 'widening' the line * segment. A typical use for this method is the creation of a * 'clickable' region for a line that is displayed on-screen. * * @param line the line ({@code null} not permitted). * @param width the width of the region. * * @return A region that surrounds the line. */ public static Shape createLineRegion(Line2D line, float width) { final GeneralPath result = new GeneralPath(); final float x1 = (float) line.getX1(); final float x2 = (float) line.getX2(); final float y1 = (float) line.getY1(); final float y2 = (float) line.getY2(); if ((x2 - x1) != 0.0) { final double theta = Math.atan((y2 - y1) / (x2 - x1)); final float dx = (float) Math.sin(theta) * width; final float dy = (float) Math.cos(theta) * width; result.moveTo(x1 - dx, y1 + dy); result.lineTo(x1 + dx, y1 - dy); result.lineTo(x2 + dx, y2 - dy); result.lineTo(x2 - dx, y2 + dy); result.closePath(); } else { // special case, vertical line result.moveTo(x1 - width / 2.0f, y1); result.lineTo(x1 + width / 2.0f, y1); result.lineTo(x2 + width / 2.0f, y2); result.lineTo(x2 - width / 2.0f, y2); result.closePath(); } return result; } /** * Returns a point based on (x, y) but constrained to be within the bounds * of a given rectangle. * * @param x the x-coordinate. * @param y the y-coordinate. * @param area the constraining rectangle ({@code null} not * permitted). * * @return A point within the rectangle. * * @throws NullPointerException if {@code area} is {@code null}. */ public static Point2D getPointInRectangle(double x, double y, Rectangle2D area) { x = Math.max(area.getMinX(), Math.min(x, area.getMaxX())); y = Math.max(area.getMinY(), Math.min(y, area.getMaxY())); return new Point2D.Double(x, y); } /** * Checks, whether the given rectangle1 fully contains rectangle 2 * (even if rectangle 2 has a height or width of zero!). * * @param rect1 the first rectangle. * @param rect2 the second rectangle. * * @return A boolean. */ public static boolean contains(Rectangle2D rect1, Rectangle2D rect2) { final double x0 = rect1.getX(); final double y0 = rect1.getY(); final double x = rect2.getX(); final double y = rect2.getY(); final double w = rect2.getWidth(); final double h = rect2.getHeight(); return ((x >= x0) && (y >= y0) && ((x + w) <= (x0 + rect1.getWidth())) && ((y + h) <= (y0 + rect1.getHeight()))); } /** * Checks, whether the given rectangle1 fully contains rectangle 2 * (even if rectangle 2 has a height or width of zero!). * * @param rect1 the first rectangle. * @param rect2 the second rectangle. * * @return A boolean. */ public static boolean intersects(Rectangle2D rect1, Rectangle2D rect2) { final double x0 = rect1.getX(); final double y0 = rect1.getY(); final double x = rect2.getX(); final double width = rect2.getWidth(); final double y = rect2.getY(); final double height = rect2.getHeight(); return (x + width >= x0 && y + height >= y0 && x <= x0 + rect1.getWidth() && y <= y0 + rect1.getHeight()); } /** * Returns {@code true} if the specified point (x, y) falls within or * on the boundary of the specified rectangle. * * @param rect the rectangle ({@code null} not permitted). * @param x the x-coordinate. * @param y the y-coordinate. * * @return A boolean. */ public static boolean isPointInRect(Rectangle2D rect, double x, double y) { return (x >= rect.getMinX() && x <= rect.getMaxX() && y >= rect.getMinY() && y <= rect.getMaxY()); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/SortOrder.java000066400000000000000000000040101463604235500266140ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * SortOrder.java * -------------- * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributors: -; */ package org.jfree.chart.util; /** * Defines tokens used to indicate sorting order (ascending or descending). */ public enum SortOrder { /** Ascending order. */ ASCENDING("SortOrder.ASCENDING"), /** Descending order. */ DESCENDING("SortOrder.DESCENDING"); /** The name. */ private final String name; /** * Private constructor. * * @param name the name. */ SortOrder(String name) { this.name = name; } /** * Returns a string representing the object. * * @return The string. */ @Override public String toString() { return this.name; } }jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/StringUtils.java000066400000000000000000000057401463604235500271730ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * StringUtils.java * ---------------- * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributors: -; */ package org.jfree.chart.util; /** * String utilities. */ public class StringUtils { /** * Private constructor prevents object creation. */ private StringUtils() { } /** * Helper functions to query a strings start portion. The comparison is case insensitive. * * @param base the base string. * @param start the starting text. * * @return true, if the string starts with the given starting text. */ public static boolean startsWithIgnoreCase(String base, String start) { if (base.length() < start.length()) { return false; } return base.regionMatches(true, 0, start, 0, start.length()); } /** * Helper functions to query a strings end portion. The comparison is case insensitive. * * @param base the base string. * @param end the ending text. * * @return true, if the string ends with the given ending text. */ public static boolean endsWithIgnoreCase(String base, String end) { if (base.length() < end.length()) { return false; } return base.regionMatches(true, base.length() - end.length(), end, 0, end.length()); } /** * Queries the system properties for the line separator. If access * to the System properties is forbidden, the UNIX default is returned. * * @return the line separator. */ public static String getLineSeparator() { try { return System.getProperty("line.separator", "\n"); } catch (Exception e) { return "\n"; } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/StrokeList.java000066400000000000000000000107131463604235500270030ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * StrokeList.java * --------------- * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributors: -; */ package org.jfree.chart.util; import java.awt.Stroke; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; /** * A table of {@link Stroke} objects. */ public class StrokeList extends AbstractObjectList { /** * Creates a new list. */ public StrokeList() { super(); } /** * Returns a {@link Stroke} object from the list. * * @param index the index (zero-based). * * @return The object. */ public Stroke getStroke(int index) { return (Stroke) get(index); } /** * Sets the {@link Stroke} for an item in the list. The list is expanded if necessary. * * @param index the index (zero-based). * @param stroke the {@link Stroke}. */ public void setStroke(int index, Stroke stroke) { set(index, stroke); } /** * Returns an independent copy of the list. * * @return A clone. * * @throws CloneNotSupportedException if an item in the list cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Tests the list for equality with another object (typically also a list). * * @param o the other object. * * @return A boolean. */ @Override public boolean equals(Object o) { if (o == null) { return false; } if (o == this) { return true; } if (o instanceof StrokeList) { return super.equals(o); } return false; } /** * Returns a hash code value for the object. * * @return the hashcode */ @Override public int hashCode() { return super.hashCode(); } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); final int count = size(); stream.writeInt(count); for (int i = 0; i < count; i++) { final Stroke stroke = getStroke(i); if (stroke != null) { stream.writeInt(i); SerialUtils.writeStroke(stroke, stream); } else { stream.writeInt(-1); } } } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); int count = stream.readInt(); for (int i = 0; i < count; i++) { int index = stream.readInt(); if (index != -1) { setStroke(index, SerialUtils.readStroke(stream)); } } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/TableOrder.java000066400000000000000000000037701463604235500267300ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * TableOrder.java * --------------- * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributors: -; */ package org.jfree.chart.util; /** * Used to indicate the processing order for a table (by row or by column). */ public enum TableOrder { /** By row. */ BY_ROW("TableOrder.BY_ROW"), /** By column. */ BY_COLUMN("TableOrder.BY_COLUMN"); /** The name. */ private final String name; /** * Private constructor. * * @param name the name. */ TableOrder(String name) { this.name = name; } /** * Returns a string representing the object. * * @return The string. */ @Override public String toString() { return this.name; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/UnitType.java000066400000000000000000000037241463604235500264650ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------- * UnitType.java * ------------- * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributors: -; */ package org.jfree.chart.util; /** * Used to indicate absolute or relative units. */ public enum UnitType { /** Absolute. */ ABSOLUTE("UnitType.ABSOLUTE"), /** Relative. */ RELATIVE("UnitType.RELATIVE"); /** The name. */ private final String name; /** * Private constructor. * * @param name the name. */ UnitType(String name) { this.name = name; } /** * Returns a string representing the object. * * @return The string. */ @Override public String toString() { return this.name; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/XYCoordinateType.java000066400000000000000000000075571463604235500301260ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * XYCoordinateType.java * --------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.util; import java.io.ObjectStreamException; import java.io.Serializable; /** * Represents several possible interpretations for an (x, y) coordinate. */ public final class XYCoordinateType implements Serializable { /** The (x, y) coordinates represent a point in the data space. */ public static final XYCoordinateType DATA = new XYCoordinateType("XYCoordinateType.DATA"); /** * The (x, y) coordinates represent a relative position in the data space. * In this case, the values should be in the range (0.0 to 1.0). */ public static final XYCoordinateType RELATIVE = new XYCoordinateType("XYCoordinateType.RELATIVE"); /** * The (x, y) coordinates represent indices in a dataset. * In this case, the values should be in the range (0.0 to 1.0). */ public static final XYCoordinateType INDEX = new XYCoordinateType("XYCoordinateType.INDEX"); /** The name. */ private String name; /** * Private constructor. * * @param name the name. */ private XYCoordinateType(String name) { this.name = name; } /** * Returns a string representing the object. * * @return The string. */ @Override public String toString() { return this.name; } /** * Returns {@code true} if this object is equal to the specified * object, and {@code false} otherwise. * * @param obj the other object. * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof XYCoordinateType)) { return false; } XYCoordinateType order = (XYCoordinateType) obj; if (!this.name.equals(order.toString())) { return false; } return true; } /** * Ensures that serialization returns the unique instances. * * @return The object. * * @throws ObjectStreamException if there is a problem. */ private Object readResolve() throws ObjectStreamException { if (this.equals(XYCoordinateType.DATA)) { return XYCoordinateType.DATA; } else if (this.equals(XYCoordinateType.RELATIVE)) { return XYCoordinateType.RELATIVE; } else if (this.equals(XYCoordinateType.INDEX)) { return XYCoordinateType.INDEX; } return null; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/chart/util/package.html000066400000000000000000000002201463604235500263060ustar00rootroot00000000000000 Utility classes used by JFreeChart. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/000077500000000000000000000000001463604235500226665ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/ComparableObjectItem.java000066400000000000000000000124161463604235500275500ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * ComparableObjectItem.java * ------------------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.util.Args; /** * Represents one (Comparable, Object) data item for use in a * {@link ComparableObjectSeries}. */ public class ComparableObjectItem implements Cloneable, Comparable, Serializable { /** For serialization. */ private static final long serialVersionUID = 2751513470325494890L; /** The x-value. */ private Comparable x; /** The y-value. */ private Object obj; /** * Constructs a new data item. * * @param x the x-value ({@code null} NOT permitted). * @param y the y-value ({@code null} permitted). */ public ComparableObjectItem(Comparable x, Object y) { Args.nullNotPermitted(x, "x"); this.x = x; this.obj = y; } /** * Returns the x-value. * * @return The x-value (never {@code null}). */ protected Comparable getComparable() { return this.x; } /** * Returns the y-value. * * @return The y-value (possibly {@code null}). */ protected Object getObject() { return this.obj; } /** * Sets the y-value for this data item. Note that there is no * corresponding method to change the x-value. * * @param y the new y-value ({@code null} permitted). */ protected void setObject(Object y) { this.obj = y; } /** * Returns an integer indicating the order of this object relative to * another object. *

* For the order we consider only the x-value: * negative == "less-than", zero == "equal", positive == "greater-than". * * @param o1 the object being compared to. * * @return An integer indicating the order of this data pair object * relative to another object. */ @Override public int compareTo(Object o1) { int result; // CASE 1 : Comparing to another ComparableObjectItem object // --------------------------------------------------------- if (o1 instanceof ComparableObjectItem) { ComparableObjectItem that = (ComparableObjectItem) o1; return this.x.compareTo(that.x); } // CASE 2 : Comparing to a general object // --------------------------------------------- else { // consider these to be ordered after general objects result = 1; } return result; } /** * Returns a clone of this object. * * @return A clone. * * @throws CloneNotSupportedException not thrown by this class, but * subclasses may differ. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Tests if this object is equal to another. * * @param obj the object to test against for equality ({@code null} * permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof ComparableObjectItem)) { return false; } ComparableObjectItem that = (ComparableObjectItem) obj; if (!this.x.equals(that.x)) { return false; } if (!Objects.equals(this.obj, that.obj)) { return false; } return true; } /** * Returns a hash code. * * @return A hash code. */ @Override public int hashCode() { int result; result = this.x.hashCode(); result = 29 * result + (this.obj != null ? this.obj.hashCode() : 0); return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/ComparableObjectSeries.java000066400000000000000000000363141463604235500301070ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * ComparableObjectSeries.java * --------------------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data; import java.io.Serializable; import java.util.Collections; import java.util.List; import java.util.Objects; import org.jfree.chart.util.Args; import org.jfree.chart.util.CloneUtils; import org.jfree.data.general.Series; import org.jfree.data.general.SeriesChangeEvent; import org.jfree.data.general.SeriesException; /** * A (possibly ordered) list of (Comparable, Object) data items. */ public class ComparableObjectSeries extends Series implements Cloneable, Serializable { /** Storage for the data items in the series. */ protected List data; /** The maximum number of items for the series. */ private int maximumItemCount = Integer.MAX_VALUE; /** A flag that controls whether the items are automatically sorted. */ private boolean autoSort; /** A flag that controls whether or not duplicate x-values are allowed. */ private boolean allowDuplicateXValues; /** * Creates a new empty series. By default, items added to the series will * be sorted into ascending order by x-value, and duplicate x-values will * be allowed (these defaults can be modified with another constructor. * * @param key the series key ({@code null} not permitted). */ public ComparableObjectSeries(Comparable key) { this(key, true, true); } /** * Constructs a new series that contains no data. You can specify * whether or not duplicate x-values are allowed for the series. * * @param key the series key ({@code null} not permitted). * @param autoSort a flag that controls whether or not the items in the * series are sorted. * @param allowDuplicateXValues a flag that controls whether duplicate * x-values are allowed. */ public ComparableObjectSeries(Comparable key, boolean autoSort, boolean allowDuplicateXValues) { super(key); this.data = new java.util.ArrayList(); this.autoSort = autoSort; this.allowDuplicateXValues = allowDuplicateXValues; } /** * Returns the flag that controls whether the items in the series are * automatically sorted. There is no setter for this flag, it must be * defined in the series constructor. * * @return A boolean. */ public boolean getAutoSort() { return this.autoSort; } /** * Returns a flag that controls whether duplicate x-values are allowed. * This flag can only be set in the constructor. * * @return A boolean. */ public boolean getAllowDuplicateXValues() { return this.allowDuplicateXValues; } /** * Returns the number of items in the series. * * @return The item count. */ @Override public int getItemCount() { return this.data.size(); } /** * Returns the maximum number of items that will be retained in the series. * The default value is {@code Integer.MAX_VALUE}. * * @return The maximum item count. * @see #setMaximumItemCount(int) */ public int getMaximumItemCount() { return this.maximumItemCount; } /** * Sets the maximum number of items that will be retained in the series. * If you add a new item to the series such that the number of items will * exceed the maximum item count, then the first element in the series is * automatically removed, ensuring that the maximum item count is not * exceeded. *

* Typically this value is set before the series is populated with data, * but if it is applied later, it may cause some items to be removed from * the series (in which case a {@link SeriesChangeEvent} will be sent to * all registered listeners. * * @param maximum the maximum number of items for the series. */ public void setMaximumItemCount(int maximum) { this.maximumItemCount = maximum; boolean dataRemoved = false; while (this.data.size() > maximum) { this.data.remove(0); dataRemoved = true; } if (dataRemoved) { fireSeriesChanged(); } } /** * Adds new data to the series and sends a {@link SeriesChangeEvent} to * all registered listeners. *

* Throws an exception if the x-value is a duplicate AND the * allowDuplicateXValues flag is false. * * @param x the x-value ({@code null} not permitted). * @param y the y-value ({@code null} permitted). */ protected void add(Comparable x, Object y) { // argument checking delegated... add(x, y, true); } /** * Adds new data to the series and, if requested, sends a * {@link SeriesChangeEvent} to all registered listeners. *

* Throws an exception if the x-value is a duplicate AND the * allowDuplicateXValues flag is false. * * @param x the x-value ({@code null} not permitted). * @param y the y-value ({@code null} permitted). * @param notify a flag the controls whether or not a * {@link SeriesChangeEvent} is sent to all registered * listeners. */ protected void add(Comparable x, Object y, boolean notify) { // delegate argument checking to XYDataItem... ComparableObjectItem item = new ComparableObjectItem(x, y); add(item, notify); } /** * Adds a data item to the series and, if requested, sends a * {@link SeriesChangeEvent} to all registered listeners. * * @param item the (x, y) item ({@code null} not permitted). * @param notify a flag that controls whether or not a * {@link SeriesChangeEvent} is sent to all registered * listeners. */ protected void add(ComparableObjectItem item, boolean notify) { Args.nullNotPermitted(item, "item"); if (this.autoSort) { int index = Collections.binarySearch(this.data, item); if (index < 0) { this.data.add(-index - 1, item); } else { if (this.allowDuplicateXValues) { // need to make sure we are adding *after* any duplicates int size = this.data.size(); while (index < size && item.compareTo(this.data.get(index)) == 0) { index++; } if (index < this.data.size()) { this.data.add(index, item); } else { this.data.add(item); } } else { throw new SeriesException("X-value already exists."); } } } else { if (!this.allowDuplicateXValues) { // can't allow duplicate values, so we need to check whether // there is an item with the given x-value already int index = indexOf(item.getComparable()); if (index >= 0) { throw new SeriesException("X-value already exists."); } } this.data.add(item); } if (getItemCount() > this.maximumItemCount) { this.data.remove(0); } if (notify) { fireSeriesChanged(); } } /** * Returns the index of the item with the specified x-value, or a negative * index if the series does not contain an item with that x-value. Be * aware that for an unsorted series, the index is found by iterating * through all items in the series. * * @param x the x-value ({@code null} not permitted). * * @return The index. */ public int indexOf(Comparable x) { if (this.autoSort) { return Collections.binarySearch(this.data, new ComparableObjectItem( x, null)); } else { for (int i = 0; i < this.data.size(); i++) { ComparableObjectItem item = (ComparableObjectItem) this.data.get(i); if (item.getComparable().equals(x)) { return i; } } return -1; } } /** * Updates an item in the series. * * @param x the x-value ({@code null} not permitted). * @param y the y-value ({@code null} permitted). * * @throws SeriesException if there is no existing item with the specified * x-value. */ protected void update(Comparable x, Object y) { int index = indexOf(x); if (index < 0) { throw new SeriesException("No observation for x = " + x); } else { ComparableObjectItem item = getDataItem(index); item.setObject(y); fireSeriesChanged(); } } /** * Updates the value of an item in the series and sends a * {@link SeriesChangeEvent} to all registered listeners. * * @param index the item (zero based index). * @param y the new value ({@code null} permitted). */ protected void updateByIndex(int index, Object y) { ComparableObjectItem item = getDataItem(index); item.setObject(y); fireSeriesChanged(); } /** * Return the data item with the specified index. * * @param index the index. * * @return The data item with the specified index. */ protected ComparableObjectItem getDataItem(int index) { return (ComparableObjectItem) this.data.get(index); } /** * Deletes a range of items from the series and sends a * {@link SeriesChangeEvent} to all registered listeners. * * @param start the start index (zero-based). * @param end the end index (zero-based). */ protected void delete(int start, int end) { if (end >= start) { this.data.subList(start, end + 1).clear(); } fireSeriesChanged(); } /** * Removes all data items from the series and, unless the series is * already empty, sends a {@link SeriesChangeEvent} to all registered * listeners. */ public void clear() { if (this.data.size() > 0) { this.data.clear(); fireSeriesChanged(); } } /** * Removes the item at the specified index and sends a * {@link SeriesChangeEvent} to all registered listeners. * * @param index the index. * * @return The item removed. */ protected ComparableObjectItem remove(int index) { ComparableObjectItem result = (ComparableObjectItem) this.data.remove( index); fireSeriesChanged(); return result; } /** * Removes the item with the specified x-value and sends a * {@link SeriesChangeEvent} to all registered listeners. * * @param x the x-value. * @return The item removed. */ public ComparableObjectItem remove(Comparable x) { return remove(indexOf(x)); } /** * Tests this series for equality with an arbitrary object. * * @param obj the object to test against for equality * ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof ComparableObjectSeries)) { return false; } if (!super.equals(obj)) { return false; } ComparableObjectSeries that = (ComparableObjectSeries) obj; if (this.maximumItemCount != that.maximumItemCount) { return false; } if (this.autoSort != that.autoSort) { return false; } if (this.allowDuplicateXValues != that.allowDuplicateXValues) { return false; } if (!Objects.equals(this.data, that.data)) { return false; } return true; } /** * Returns a hash code. * * @return A hash code. */ @Override public int hashCode() { int result = super.hashCode(); // it is too slow to look at every data item, so let's just look at // the first, middle and last items... int count = getItemCount(); if (count > 0) { ComparableObjectItem item = getDataItem(0); result = 29 * result + item.hashCode(); } if (count > 1) { ComparableObjectItem item = getDataItem(count - 1); result = 29 * result + item.hashCode(); } if (count > 2) { ComparableObjectItem item = getDataItem(count / 2); result = 29 * result + item.hashCode(); } result = 29 * result + this.maximumItemCount; result = 29 * result + (this.autoSort ? 1 : 0); result = 29 * result + (this.allowDuplicateXValues ? 1 : 0); return result; } /** * Returns a clone of the series. * * @return A clone of the series. * * @throws CloneNotSupportedException if there is a cloning problem. */ @Override @SuppressWarnings("unchecked") public Object clone() throws CloneNotSupportedException { ComparableObjectSeries clone = (ComparableObjectSeries) super.clone(); clone.data = CloneUtils.cloneList(this.data); return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/DataUtils.java000066400000000000000000000216431463604235500254310ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * DataUtils.java * -------------- * (C) Copyright 2003-present, by David Gilbert and contributors. * * Original Author: David Gilbert; * Contributor(s): Peter Kolb (patch 2511330); * */ package org.jfree.data; import java.util.Arrays; import org.jfree.chart.util.Args; import org.jfree.data.general.DatasetUtils; /** * Utility methods for use with some of the data classes (but not the datasets, * see {@link DatasetUtils}). */ public abstract class DataUtils { /** * Tests two arrays for equality. To be considered equal, the arrays must * have exactly the same dimensions, and the values in each array must also * match (two values that qre both NaN or both INF are considered equal * in this test). * * @param a the first array ({@code null} permitted). * @param b the second array ({@code null} permitted). * * @return A boolean. */ public static boolean equal(double[][] a, double[][] b) { if (a == null) { return (b == null); } if (b == null) { return false; // already know 'a' isn't null } if (a.length != b.length) { return false; } for (int i = 0; i < a.length; i++) { if (!Arrays.equals(a[i], b[i])) { return false; } } return true; } /** * Returns a clone of the specified array. * * @param source the source array ({@code null} not permitted). * * @return A clone of the array. */ public static double[][] clone(double[][] source) { Args.nullNotPermitted(source, "source"); double[][] clone = new double[source.length][]; for (int i = 0; i < source.length; i++) { if (source[i] != null) { double[] row = new double[source[i].length]; System.arraycopy(source[i], 0, row, 0, source[i].length); clone[i] = row; } } return clone; } /** * Returns the total of the values in one column of the supplied data * table. * * @param data the table of values ({@code null} not permitted). * @param column the column index (zero-based). * * @return The total of the values in the specified column. */ public static double calculateColumnTotal(Values2D data, int column) { Args.nullNotPermitted(data, "data"); double total = 0.0; int rowCount = data.getRowCount(); for (int r = 0; r < rowCount; r++) { Number n = data.getValue(r, column); if (n != null) { total += n.doubleValue(); } } return total; } /** * Returns the total of the values in one column of the supplied data * table by taking only the row numbers in the array into account. * * @param data the table of values ({@code null} not permitted). * @param column the column index (zero-based). * @param validRows the array with valid rows (zero-based). * * @return The total of the valid values in the specified column. */ public static double calculateColumnTotal(Values2D data, int column, int[] validRows) { Args.nullNotPermitted(data, "data"); double total = 0.0; int rowCount = data.getRowCount(); for (int v = 0; v < validRows.length; v++) { int row = validRows[v]; if (row < rowCount) { Number n = data.getValue(row, column); if (n != null) { total += n.doubleValue(); } } } return total; } /** * Returns the total of the values in one row of the supplied data * table. * * @param data the table of values ({@code null} not permitted). * @param row the row index (zero-based). * * @return The total of the values in the specified row. */ public static double calculateRowTotal(Values2D data, int row) { Args.nullNotPermitted(data, "data"); double total = 0.0; int columnCount = data.getColumnCount(); for (int c = 0; c < columnCount; c++) { Number n = data.getValue(row, c); if (n != null) { total += n.doubleValue(); } } return total; } /** * Returns the total of the values in one row of the supplied data * table by taking only the column numbers in the array into account. * * @param data the table of values ({@code null} not permitted). * @param row the row index (zero-based). * @param validCols the array with valid cols (zero-based). * * @return The total of the valid values in the specified row. */ public static double calculateRowTotal(Values2D data, int row, int[] validCols) { Args.nullNotPermitted(data, "data"); double total = 0.0; int colCount = data.getColumnCount(); for (int v = 0; v < validCols.length; v++) { int col = validCols[v]; if (col < colCount) { Number n = data.getValue(row, col); if (n != null) { total += n.doubleValue(); } } } return total; } /** * Constructs an array of {@code Number} objects from an array of * {@code double} primitives. * * @param data the data ({@code null} not permitted). * * @return An array of {@code double}. */ public static Number[] createNumberArray(double[] data) { Args.nullNotPermitted(data, "data"); Number[] result = new Number[data.length]; for (int i = 0; i < data.length; i++) { result[i] = data[i]; } return result; } /** * Constructs an array of arrays of {@code Number} objects from a * corresponding structure containing {@code double} primitives. * * @param data the data ({@code null} not permitted). * * @return An array of {@code double}. */ public static Number[][] createNumberArray2D(double[][] data) { Args.nullNotPermitted(data, "data"); int l1 = data.length; Number[][] result = new Number[l1][]; for (int i = 0; i < l1; i++) { result[i] = createNumberArray(data[i]); } return result; } /** * Returns a {@link KeyedValues} instance that contains the cumulative * percentage values for the data in another {@link KeyedValues} instance. *

* The percentages are values between 0.0 and 1.0 (where 1.0 = 100%). * * @param data the data ({@code null} not permitted). * * @return The cumulative percentages. */ public static KeyedValues getCumulativePercentages(KeyedValues data) { Args.nullNotPermitted(data, "data"); DefaultKeyedValues result = new DefaultKeyedValues(); double total = 0.0; for (int i = 0; i < data.getItemCount(); i++) { Number v = data.getValue(i); if (v != null) { total = total + v.doubleValue(); } } double runningTotal = 0.0; for (int i = 0; i < data.getItemCount(); i++) { Number v = data.getValue(i); if (v != null) { runningTotal = runningTotal + v.doubleValue(); } result.addValue(data.getKey(i), runningTotal / total); } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/DefaultKeyedValue.java000066400000000000000000000113401463604235500270730ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * DefaultKeyedValue.java * ---------------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (generics for bug fix to PiePlot); * */ package org.jfree.data; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; /** * A (key, value) pair. This class provides a default implementation of the * {@link KeyedValue} interface. * * @param the key type ({@code String} is a good default). */ public class DefaultKeyedValue> implements KeyedValue, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -7388924517460437712L; /** The key. */ private final K key; /** The value. */ private Number value; /** * Creates a new (key, value) item. * * @param key the key (should be immutable, {@code null} not permitted). * @param value the value ({@code null} permitted). */ public DefaultKeyedValue(K key, Number value) { Args.nullNotPermitted(key, "key"); this.key = key; this.value = value; } /** * Returns the key. * * @return The key (never {@code null}). */ @Override public K getKey() { return this.key; } /** * Returns the value. * * @return The value (possibly {@code null}). */ @Override public Number getValue() { return this.value; } /** * Sets the value. * * @param value the value ({@code null} permitted). */ public synchronized void setValue(Number value) { this.value = value; } /** * Tests this key-value pair for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof DefaultKeyedValue)) { return false; } DefaultKeyedValue that = (DefaultKeyedValue) obj; if (!this.key.equals(that.key)) { return false; } return Objects.equals(this.value, that.value); } /** * Returns a hash code. * * @return A hash code. */ @Override public int hashCode() { int result; result = (this.key != null ? this.key.hashCode() : 0); result = 29 * result + (this.value != null ? this.value.hashCode() : 0); return result; } /** * Returns a clone. It is assumed that both the key and value are * immutable objects, so only the references are cloned, not the objects * themselves. * * @return A clone. * * @throws CloneNotSupportedException Not thrown by this class, but * subclasses (if any) might. */ @Override public Object clone() throws CloneNotSupportedException { return (DefaultKeyedValue) super.clone(); } /** * Returns a string representing this instance, primarily useful for * debugging. * * @return A string. */ @Override public String toString() { return "(" + this.key.toString() + ", " + this.value.toString() + ")"; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/DefaultKeyedValues.java000066400000000000000000000325441463604235500272670ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * DefaultKeyedValues.java * ----------------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Thomas Morgner; * Tracy Hiltbrand (generics for bug fix to PiePlot); * */ package org.jfree.data; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SortOrder; /** * An ordered list of (key, value) items. This class provides a default * implementation of the {@link KeyedValues} interface. * * @param the key type ({@code String} is a good default). */ public class DefaultKeyedValues> implements KeyedValues, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 8468154364608194797L; /** Storage for the keys. */ private List keys; /** Storage for the values. */ private List values; /** * Contains (key, Integer) mappings, where the Integer is the index for * the key in the list. */ private Map indexMap; /** * Creates a new collection (initially empty). */ public DefaultKeyedValues() { this.keys = new ArrayList<>(); this.values = new ArrayList<>(); this.indexMap = new HashMap<>(); } /** * Returns the number of items (values) in the collection. * * @return The item count. */ @Override public int getItemCount() { return this.indexMap.size(); } /** * Returns a value. * * @param item the item of interest (zero-based index). * * @return The value (possibly {@code null}). * * @throws IndexOutOfBoundsException if {@code item} is out of bounds. */ @Override public Number getValue(int item) { return this.values.get(item); } /** * Returns a key. * * @param index the item index (zero-based). * * @return The row key. * * @throws IndexOutOfBoundsException if {@code item} is out of bounds. */ @Override public K getKey(int index) { return this.keys.get(index); } /** * Returns the index for a given key. * * @param key the key ({@code null} not permitted). * * @return The index, or {@code -1} if the key is not recognised. * * @throws IllegalArgumentException if {@code key} is * {@code null}. */ @Override public int getIndex(K key) { Args.nullNotPermitted(key, "key"); final Integer i = this.indexMap.get(key); if (i == null) { return -1; // key not found } return i; } /** * Returns the keys for the values in the collection. * * @return The keys (never {@code null}). */ @Override public List getKeys() { return new ArrayList<>(this.keys); } /** * Returns the value for a given key. * * @param key the key ({@code null} not permitted). * * @return The value (possibly {@code null}). * * @throws UnknownKeyException if the key is not recognised. * * @see #getValue(int) */ @Override public Number getValue(K key) { int index = getIndex(key); if (index < 0) { throw new UnknownKeyException("Key not found: " + key); } return getValue(index); } /** * Updates an existing value, or adds a new value to the collection. * * @param key the key ({@code null} not permitted). * @param value the value. * * @see #addValue(Comparable, Number) */ public void addValue(K key, double value) { addValue(key, Double.valueOf(value)); } /** * Adds a new value to the collection, or updates an existing value. * This method passes control directly to the * {@link #setValue(Comparable, Number)} method. * * @param key the key ({@code null} not permitted). * @param value the value ({@code null} permitted). */ public void addValue(K key, Number value) { setValue(key, value); } /** * Updates an existing value, or adds a new value to the collection. * * @param key the key ({@code null} not permitted). * @param value the value. */ public void setValue(K key, double value) { setValue(key, Double.valueOf(value)); } /** * Updates an existing value, or adds a new value to the collection. * * @param key the key ({@code null} not permitted). * @param value the value ({@code null} permitted). */ public void setValue(K key, Number value) { Args.nullNotPermitted(key, "key"); int keyIndex = getIndex(key); if (keyIndex >= 0) { this.keys.set(keyIndex, key); this.values.set(keyIndex, value); } else { this.keys.add(key); this.values.add(value); this.indexMap.put(key, this.keys.size() - 1); } } /** * Inserts a new value at the specified position in the dataset or, if * there is an existing item with the specified key, updates the value * for that item and moves it to the specified position. * * @param position the position (in the range 0 to getItemCount()). * @param key the key ({@code null} not permitted). * @param value the value. */ public void insertValue(int position, K key, double value) { insertValue(position, key, Double.valueOf(value)); } /** * Inserts a new value at the specified position in the dataset or, if * there is an existing item with the specified key, updates the value * for that item and moves it to the specified position. * * @param position the position (in the range 0 to getItemCount()). * @param key the key ({@code null} not permitted). * @param value the value ({@code null} permitted). */ public void insertValue(int position, K key, Number value) { if (position < 0 || position > getItemCount()) { throw new IllegalArgumentException("'position' out of bounds."); } Args.nullNotPermitted(key, "key"); int pos = getIndex(key); if (pos == position) { this.keys.set(pos, key); this.values.set(pos, value); } else { if (pos >= 0) { this.keys.remove(pos); this.values.remove(pos); } this.keys.add(position, key); this.values.add(position, value); rebuildIndex(); } } /** * Rebuilds the key to indexed-position mapping after an positioned insert * or a remove operation. */ private void rebuildIndex () { this.indexMap.clear(); for (int i = 0; i < this.keys.size(); i++) { final K key = this.keys.get(i); this.indexMap.put(key, i); } } /** * Removes a value from the collection. * * @param index the index of the item to remove (in the range * {@code 0} to {@code getItemCount() -1}). * * @throws IndexOutOfBoundsException if {@code index} is not within * the specified range. */ public void removeValue(int index) { this.keys.remove(index); this.values.remove(index); rebuildIndex(); } /** * Removes a value from the collection. * * @param key the item key ({@code null} not permitted). * * @throws IllegalArgumentException if {@code key} is * {@code null}. * @throws UnknownKeyException if {@code key} is not recognised. */ public void removeValue(K key) { int index = getIndex(key); if (index < 0) { throw new UnknownKeyException("The key (" + key + ") is not recognised."); } removeValue(index); } /** * Clears all values from the collection. */ public void clear() { this.keys.clear(); this.values.clear(); this.indexMap.clear(); } /** * Sorts the items in the list by key. * * @param order the sort order ({@code null} not permitted). */ public void sortByKeys(SortOrder order) { final int size = this.keys.size(); final DefaultKeyedValue[] data = new DefaultKeyedValue[size]; for (int i = 0; i < size; i++) { data[i] = new DefaultKeyedValue(this.keys.get(i), this.values.get(i)); } Comparator comparator = new KeyedValueComparator( KeyedValueComparatorType.BY_KEY, order); Arrays.sort(data, comparator); clear(); for (int i = 0; i < data.length; i++) { final DefaultKeyedValue value = data[i]; addValue(value.getKey(), value.getValue()); } } /** * Sorts the items in the list by value. If the list contains * {@code null} values, they will sort to the end of the list, * irrespective of the sort order. * * @param order the sort order ({@code null} not permitted). */ public void sortByValues(SortOrder order) { final int size = this.keys.size(); final DefaultKeyedValue[] data = new DefaultKeyedValue[size]; for (int i = 0; i < size; i++) { data[i] = new DefaultKeyedValue((Comparable) this.keys.get(i), (Number) this.values.get(i)); } Comparator comparator = new KeyedValueComparator( KeyedValueComparatorType.BY_VALUE, order); Arrays.sort(data, comparator); clear(); for (int i = 0; i < data.length; i++) { final DefaultKeyedValue value = data[i]; addValue(value.getKey(), value.getValue()); } } /** * Tests if this object is equal to another. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof KeyedValues)) { return false; } KeyedValues that = (KeyedValues) obj; int count = getItemCount(); if (count != that.getItemCount()) { return false; } for (int i = 0; i < count; i++) { Comparable k1 = getKey(i); Comparable k2 = that.getKey(i); if (!k1.equals(k2)) { return false; } Number v1 = getValue(i); Number v2 = that.getValue(i); if (v1 == null) { if (v2 != null) { return false; } } else { if (!v1.equals(v2)) { return false; } } } return true; } /** * Returns a hash code. * * @return A hash code. */ @Override public int hashCode() { return (this.keys != null ? this.keys.hashCode() : 0); } /** * Returns a clone. * * @return A clone. * * @throws CloneNotSupportedException this class will not throw this * exception, but subclasses might. */ @Override public Object clone() throws CloneNotSupportedException { DefaultKeyedValues clone = (DefaultKeyedValues) super.clone(); clone.keys = new ArrayList<>(this.keys); clone.values = new ArrayList<>(this.values); clone.indexMap = new HashMap(this.indexMap); return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/DefaultKeyedValues2D.java000066400000000000000000000415351463604235500274550ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * DefaultKeyedValues2D.java * ------------------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Andreas Schroeder; * */ package org.jfree.data; import java.io.Serializable; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; /** * A data structure that stores zero, one or many values, where each value * is associated with two keys (a 'row' key and a 'column' key). The keys * should be (a) instances of {@link Comparable} and (b) immutable. */ public class DefaultKeyedValues2D implements KeyedValues2D, PublicCloneable, Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -5514169970951994748L; /** The row keys. */ private List rowKeys; /** The column keys. */ private List columnKeys; /** The row data. */ private List rows; /** If the row keys should be sorted by their comparable order. */ private final boolean sortRowKeys; /** * Creates a new instance (initially empty). */ public DefaultKeyedValues2D() { this(false); } /** * Creates a new instance (initially empty). * * @param sortRowKeys if the row keys should be sorted. */ public DefaultKeyedValues2D(boolean sortRowKeys) { this.rowKeys = new java.util.ArrayList(); this.columnKeys = new java.util.ArrayList(); this.rows = new java.util.ArrayList(); this.sortRowKeys = sortRowKeys; } /** * Returns the row count. * * @return The row count. * * @see #getColumnCount() */ @Override public int getRowCount() { return this.rowKeys.size(); } /** * Returns the column count. * * @return The column count. * * @see #getRowCount() */ @Override public int getColumnCount() { return this.columnKeys.size(); } /** * Returns the value for a given row and column. * * @param row the row index. * @param column the column index. * * @return The value. * * @see #getValue(Comparable, Comparable) */ @Override public Number getValue(int row, int column) { Number result = null; DefaultKeyedValues rowData = (DefaultKeyedValues) this.rows.get(row); if (rowData != null) { Comparable columnKey = (Comparable) this.columnKeys.get(column); // the row may not have an entry for this key, in which case the // return value is null int index = rowData.getIndex(columnKey); if (index >= 0) { result = rowData.getValue(index); } } return result; } /** * Returns the key for a given row. * * @param row the row index (in the range 0 to {@link #getRowCount()} - 1). * * @return The row key. * * @see #getRowIndex(Comparable) * @see #getColumnKey(int) */ @Override public Comparable getRowKey(int row) { return (Comparable) this.rowKeys.get(row); } /** * Returns the row index for a given key. * * @param key the key ({@code null} not permitted). * * @return The row index. * * @see #getRowKey(int) * @see #getColumnIndex(Comparable) */ @Override public int getRowIndex(Comparable key) { Args.nullNotPermitted(key, "key"); if (this.sortRowKeys) { return Collections.binarySearch(this.rowKeys, key); } else { return this.rowKeys.indexOf(key); } } /** * Returns the row keys in an unmodifiable list. * * @return The row keys. * * @see #getColumnKeys() */ @Override public List getRowKeys() { return Collections.unmodifiableList(this.rowKeys); } /** * Returns the key for a given column. * * @param column the column (in the range 0 to {@link #getColumnCount()} * - 1). * * @return The key. * * @see #getColumnIndex(Comparable) * @see #getRowKey(int) */ @Override public Comparable getColumnKey(int column) { return (Comparable) this.columnKeys.get(column); } /** * Returns the column index for a given key. * * @param key the key ({@code null} not permitted). * * @return The column index. * * @see #getColumnKey(int) * @see #getRowIndex(Comparable) */ @Override public int getColumnIndex(Comparable key) { Args.nullNotPermitted(key, "key"); return this.columnKeys.indexOf(key); } /** * Returns the column keys in an unmodifiable list. * * @return The column keys. * * @see #getRowKeys() */ @Override public List getColumnKeys() { return Collections.unmodifiableList(this.columnKeys); } /** * Returns the value for the given row and column keys. This method will * throw an {@link UnknownKeyException} if either key is not defined in the * data structure. * * @param rowKey the row key ({@code null} not permitted). * @param columnKey the column key ({@code null} not permitted). * * @return The value (possibly {@code null}). * * @see #addValue(Number, Comparable, Comparable) * @see #removeValue(Comparable, Comparable) */ @Override public Number getValue(Comparable rowKey, Comparable columnKey) { Args.nullNotPermitted(rowKey, "rowKey"); Args.nullNotPermitted(columnKey, "columnKey"); // check that the column key is defined in the 2D structure if (!(this.columnKeys.contains(columnKey))) { throw new UnknownKeyException("Unrecognised columnKey: " + columnKey); } // now fetch the row data - need to bear in mind that the row // structure may not have an entry for the column key, but that we // have already checked that the key is valid for the 2D structure int row = getRowIndex(rowKey); if (row >= 0) { DefaultKeyedValues rowData = (DefaultKeyedValues) this.rows.get(row); int col = rowData.getIndex(columnKey); return (col >= 0 ? rowData.getValue(col) : null); } else { throw new UnknownKeyException("Unrecognised rowKey: " + rowKey); } } /** * Adds a value to the table. Performs the same function as * #setValue(Number, Comparable, Comparable). * * @param value the value ({@code null} permitted). * @param rowKey the row key ({@code null} not permitted). * @param columnKey the column key ({@code null} not permitted). * * @see #setValue(Number, Comparable, Comparable) * @see #removeValue(Comparable, Comparable) */ public void addValue(Number value, Comparable rowKey, Comparable columnKey) { // defer argument checking setValue(value, rowKey, columnKey); } /** * Adds or updates a value. * * @param value the value ({@code null} permitted). * @param rowKey the row key ({@code null} not permitted). * @param columnKey the column key ({@code null} not permitted). * * @see #addValue(Number, Comparable, Comparable) * @see #removeValue(Comparable, Comparable) */ public void setValue(Number value, Comparable rowKey, Comparable columnKey) { DefaultKeyedValues row; int rowIndex = getRowIndex(rowKey); if (rowIndex >= 0) { row = (DefaultKeyedValues) this.rows.get(rowIndex); } else { row = new DefaultKeyedValues(); if (this.sortRowKeys) { rowIndex = -rowIndex - 1; this.rowKeys.add(rowIndex, rowKey); this.rows.add(rowIndex, row); } else { this.rowKeys.add(rowKey); this.rows.add(row); } } row.setValue(columnKey, value); int columnIndex = this.columnKeys.indexOf(columnKey); if (columnIndex < 0) { this.columnKeys.add(columnKey); } } /** * Removes a value from the table by setting it to {@code null}. If * all the values in the specified row and/or column are now * {@code null}, the row and/or column is removed from the table. * * @param rowKey the row key ({@code null} not permitted). * @param columnKey the column key ({@code null} not permitted). * * @see #addValue(Number, Comparable, Comparable) */ public void removeValue(Comparable rowKey, Comparable columnKey) { setValue(null, rowKey, columnKey); // 1. check whether the row is now empty. boolean allNull = true; int rowIndex = getRowIndex(rowKey); DefaultKeyedValues row = (DefaultKeyedValues) this.rows.get(rowIndex); for (int item = 0, itemCount = row.getItemCount(); item < itemCount; item++) { if (row.getValue(item) != null) { allNull = false; break; } } if (allNull) { this.rowKeys.remove(rowIndex); this.rows.remove(rowIndex); } // 2. check whether the column is now empty. allNull = true; //int columnIndex = getColumnIndex(columnKey); for (int item = 0, itemCount = this.rows.size(); item < itemCount; item++) { row = (DefaultKeyedValues) this.rows.get(item); int columnIndex = row.getIndex(columnKey); if (columnIndex >= 0 && row.getValue(columnIndex) != null) { allNull = false; break; } } if (allNull) { for (int item = 0, itemCount = this.rows.size(); item < itemCount; item++) { row = (DefaultKeyedValues) this.rows.get(item); int columnIndex = row.getIndex(columnKey); if (columnIndex >= 0) { row.removeValue(columnIndex); } } this.columnKeys.remove(columnKey); } } /** * Removes a row. * * @param rowIndex the row index. * * @see #removeRow(Comparable) * @see #removeColumn(int) */ public void removeRow(int rowIndex) { this.rowKeys.remove(rowIndex); this.rows.remove(rowIndex); } /** * Removes a row from the table. * * @param rowKey the row key ({@code null} not permitted). * * @see #removeRow(int) * @see #removeColumn(Comparable) * * @throws UnknownKeyException if {@code rowKey} is not defined in the * table. */ public void removeRow(Comparable rowKey) { Args.nullNotPermitted(rowKey, "rowKey"); int index = getRowIndex(rowKey); if (index >= 0) { removeRow(index); } else { throw new UnknownKeyException("Unknown key: " + rowKey); } } /** * Removes a column. * * @param columnIndex the column index. * * @see #removeColumn(Comparable) * @see #removeRow(int) */ public void removeColumn(int columnIndex) { Comparable columnKey = getColumnKey(columnIndex); removeColumn(columnKey); } /** * Removes a column from the table. * * @param columnKey the column key ({@code null} not permitted). * * @throws UnknownKeyException if the table does not contain a column with * the specified key. * @throws IllegalArgumentException if {@code columnKey} is * {@code null}. * * @see #removeColumn(int) * @see #removeRow(Comparable) */ public void removeColumn(Comparable columnKey) { Args.nullNotPermitted(columnKey, "columnKey"); if (!this.columnKeys.contains(columnKey)) { throw new UnknownKeyException("Unknown key: " + columnKey); } Iterator iterator = this.rows.iterator(); while (iterator.hasNext()) { DefaultKeyedValues rowData = (DefaultKeyedValues) iterator.next(); int index = rowData.getIndex(columnKey); if (index >= 0) { rowData.removeValue(columnKey); } } this.columnKeys.remove(columnKey); } /** * Clears all the data and associated keys. */ public void clear() { this.rowKeys.clear(); this.columnKeys.clear(); this.rows.clear(); } /** * Tests if this object is equal to another. * * @param o the other object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object o) { if (o == null) { return false; } if (o == this) { return true; } if (!(o instanceof KeyedValues2D)) { return false; } KeyedValues2D kv2D = (KeyedValues2D) o; if (!getRowKeys().equals(kv2D.getRowKeys())) { return false; } if (!getColumnKeys().equals(kv2D.getColumnKeys())) { return false; } int rowCount = getRowCount(); if (rowCount != kv2D.getRowCount()) { return false; } int colCount = getColumnCount(); if (colCount != kv2D.getColumnCount()) { return false; } for (int r = 0; r < rowCount; r++) { for (int c = 0; c < colCount; c++) { Number v1 = getValue(r, c); Number v2 = kv2D.getValue(r, c); if (v1 == null) { if (v2 != null) { return false; } } else { if (!v1.equals(v2)) { return false; } } } } return true; } /** * Returns a hash code. * * @return A hash code. */ @Override public int hashCode() { int result; result = this.rowKeys.hashCode(); result = 29 * result + this.columnKeys.hashCode(); result = 29 * result + this.rows.hashCode(); return result; } /** * Returns a clone. * * @return A clone. * * @throws CloneNotSupportedException this class will not throw this * exception, but subclasses (if any) might. */ @Override public Object clone() throws CloneNotSupportedException { DefaultKeyedValues2D clone = (DefaultKeyedValues2D) super.clone(); // for the keys, a shallow copy should be fine because keys // should be immutable... clone.columnKeys = new java.util.ArrayList(this.columnKeys); clone.rowKeys = new java.util.ArrayList(this.rowKeys); // but the row data requires a deep copy clone.rows = (List) ObjectUtils.deepClone(this.rows); return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/DomainInfo.java000066400000000000000000000055371463604235500255660ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * DomainInfo.java * --------------- * (C) Copyright 2000-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data; /** * An interface (optional) that can be implemented by a dataset to assist in * determining the minimum and maximum values. If not present, * {@link org.jfree.data.general.DatasetUtils} will iterate over all the * values in the dataset to get the bounds. */ public interface DomainInfo { /** * Returns the minimum x-value in the dataset. * * @param includeInterval a flag that determines whether or not the * x-interval is taken into account. * * @return The minimum value or {@code Double.NaN} if there are no * values. */ double getDomainLowerBound(boolean includeInterval); /** * Returns the maximum x-value in the dataset. * * @param includeInterval a flag that determines whether or not the * x-interval is taken into account. * * @return The maximum value or {@code Double.NaN} if there are no * values. */ double getDomainUpperBound(boolean includeInterval); /** * Returns the range of the values in this dataset's domain. * * @param includeInterval a flag that determines whether or not the * x-interval is taken into account. * * @return The range (or {@code null} if the dataset contains no * values). */ Range getDomainBounds(boolean includeInterval); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/DomainOrder.java000066400000000000000000000075401463604235500257420ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * DomainOrder.java * ---------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data; import java.io.ObjectStreamException; import java.io.Serializable; /** * Used to indicate sorting order if any (ascending, descending or none). */ public final class DomainOrder implements Serializable { /** For serialization. */ private static final long serialVersionUID = 4902774943512072627L; /** No order. */ public static final DomainOrder NONE = new DomainOrder("DomainOrder.NONE"); /** Ascending order. */ public static final DomainOrder ASCENDING = new DomainOrder("DomainOrder.ASCENDING"); /** Descending order. */ public static final DomainOrder DESCENDING = new DomainOrder("DomainOrder.DESCENDING"); /** The name. */ private String name; /** * Private constructor. * * @param name the name. */ private DomainOrder(String name) { this.name = name; } /** * Returns a string representing the object. * * @return The string. */ @Override public String toString() { return this.name; } /** * Returns {@code true} if this object is equal to the specified * object, and {@code false} otherwise. * * @param obj the other object. * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof DomainOrder)) { return false; } DomainOrder that = (DomainOrder) obj; if (!this.name.equals(that.toString())) { return false; } return true; } /** * Returns a hash code value for the object. * * @return The hashcode */ @Override public int hashCode() { return this.name.hashCode(); } /** * Ensures that serialization returns the unique instances. * * @return The object. * * @throws ObjectStreamException if there is a problem. */ private Object readResolve() throws ObjectStreamException { if (this.equals(DomainOrder.ASCENDING)) { return DomainOrder.ASCENDING; } else if (this.equals(DomainOrder.DESCENDING)) { return DomainOrder.DESCENDING; } else if (this.equals(DomainOrder.NONE)) { return DomainOrder.NONE; } return null; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/ItemKey.java000066400000000000000000000031731463604235500251040ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------ * ItemKey.java * ------------ * (C) Copyright 2014-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data; /** * A key that references a single data item in a dataset. */ public interface ItemKey { /** * Returns a JSON formatted string representing the key. * * @return A JSON formatted string. */ String toJSONString(); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/KeyToGroupMap.java000066400000000000000000000231261463604235500262430ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * KeyToGroupMap.java * ------------------ * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data; import java.io.Serializable; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; /** * A class that maps keys (instances of {@code Comparable}) to groups. */ public class KeyToGroupMap implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -2228169345475318082L; /** The default group. */ private Comparable defaultGroup; /** The groups. */ private List groups; /** A mapping between keys and groups. */ private Map keyToGroupMap; /** * Creates a new map with a default group named 'Default Group'. */ public KeyToGroupMap() { this("Default Group"); } /** * Creates a new map with the specified default group. * * @param defaultGroup the default group ({@code null} not permitted). */ public KeyToGroupMap(Comparable defaultGroup) { Args.nullNotPermitted(defaultGroup, "defaultGroup"); this.defaultGroup = defaultGroup; this.groups = new ArrayList(); this.keyToGroupMap = new HashMap(); } /** * Returns the number of groups in the map. * * @return The number of groups in the map. */ public int getGroupCount() { return this.groups.size() + 1; } /** * Returns a list of the groups (always including the default group) in the * map. The returned list is independent of the map, so altering the list * will have no effect. * * @return The groups (never {@code null}). */ public List getGroups() { List result = new ArrayList(); result.add(this.defaultGroup); Iterator iterator = this.groups.iterator(); while (iterator.hasNext()) { Comparable group = (Comparable) iterator.next(); if (!result.contains(group)) { result.add(group); } } return result; } /** * Returns the index for the group. * * @param group the group. * * @return The group index (or -1 if the group is not represented within * the map). */ public int getGroupIndex(Comparable group) { int result = this.groups.indexOf(group); if (result < 0) { if (this.defaultGroup.equals(group)) { result = 0; } } else { result = result + 1; } return result; } /** * Returns the group that a key is mapped to. * * @param key the key ({@code null} not permitted). * * @return The group (never {@code null}, returns the default group if * there is no mapping for the specified key). */ public Comparable getGroup(Comparable key) { Args.nullNotPermitted(key, "key"); Comparable result = this.defaultGroup; Comparable group = (Comparable) this.keyToGroupMap.get(key); if (group != null) { result = group; } return result; } /** * Maps a key to a group. * * @param key the key ({@code null} not permitted). * @param group the group ({@code null} permitted, clears any * existing mapping). */ public void mapKeyToGroup(Comparable key, Comparable group) { Args.nullNotPermitted(key, "key"); Comparable currentGroup = getGroup(key); if (!currentGroup.equals(this.defaultGroup)) { if (!currentGroup.equals(group)) { int count = getKeyCount(currentGroup); if (count == 1) { this.groups.remove(currentGroup); } } } if (group == null) { this.keyToGroupMap.remove(key); } else { if (!this.groups.contains(group)) { if (!this.defaultGroup.equals(group)) { this.groups.add(group); } } this.keyToGroupMap.put(key, group); } } /** * Returns the number of keys mapped to the specified group. This method * won't always return an accurate result for the default group, since * explicit mappings are not required for this group. * * @param group the group ({@code null} not permitted). * * @return The key count. */ public int getKeyCount(Comparable group) { Args.nullNotPermitted(group, "group"); int result = 0; Iterator iterator = this.keyToGroupMap.values().iterator(); while (iterator.hasNext()) { Comparable g = (Comparable) iterator.next(); if (group.equals(g)) { result++; } } return result; } /** * Tests the map for equality against an arbitrary object. * * @param obj the object to test against ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof KeyToGroupMap)) { return false; } KeyToGroupMap that = (KeyToGroupMap) obj; if (!Objects.equals(this.defaultGroup, that.defaultGroup)) { return false; } if (!this.keyToGroupMap.equals(that.keyToGroupMap)) { return false; } return true; } /** * Returns a clone of the map. * * @return A clone. * * @throws CloneNotSupportedException if there is a problem cloning the * map. */ @Override public Object clone() throws CloneNotSupportedException { KeyToGroupMap result = (KeyToGroupMap) super.clone(); result.defaultGroup = (Comparable) KeyToGroupMap.clone(this.defaultGroup); result.groups = (List) KeyToGroupMap.clone(this.groups); result.keyToGroupMap = (Map) KeyToGroupMap.clone(this.keyToGroupMap); return result; } /** * Attempts to clone the specified object using reflection. * * @param object the object ({@code null} permitted). * * @return The cloned object, or the original object if cloning failed. */ private static Object clone(Object object) { if (object == null) { return null; } Class c = object.getClass(); Object result = null; try { Method m = c.getMethod("clone", (Class[]) null); if (Modifier.isPublic(m.getModifiers())) { try { result = m.invoke(object, (Object[]) null); } catch (Exception e) { e.printStackTrace(); } } } catch (NoSuchMethodException e) { result = object; } return result; } /** * Returns a clone of the list. * * @param list the list. * * @return A clone of the list. * * @throws CloneNotSupportedException if the list could not be cloned. */ private static Collection clone(Collection list) throws CloneNotSupportedException { Collection result = null; if (list != null) { try { List clone = (List) list.getClass().newInstance(); Iterator iterator = list.iterator(); while (iterator.hasNext()) { clone.add(KeyToGroupMap.clone(iterator.next())); } result = clone; } catch (Exception e) { throw new CloneNotSupportedException("Exception."); } } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/KeyedObject.java000066400000000000000000000077051463604235500257320ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * KeyedObject.java * ---------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.util.PublicCloneable; /** * A (key, object) pair. */ public class KeyedObject implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 2677930479256885863L; /** The key. */ private Comparable key; /** The object. */ private Object object; /** * Creates a new (key, object) pair. * * @param key the key. * @param object the object ({@code null} permitted). */ public KeyedObject(Comparable key, Object object) { this.key = key; this.object = object; } /** * Returns the key. * * @return The key. */ public Comparable getKey() { return this.key; } /** * Returns the object. * * @return The object (possibly {@code null}). */ public Object getObject() { return this.object; } /** * Sets the object. * * @param object the object ({@code null} permitted). */ public void setObject(Object object) { this.object = object; } /** * Returns a clone of this object. It is assumed that the key is an * immutable object, so it is not deep-cloned. The object is deep-cloned * if it implements {@link PublicCloneable}, otherwise a shallow clone is * made. * * @return A clone. * * @throws CloneNotSupportedException if there is a problem cloning. */ @Override public Object clone() throws CloneNotSupportedException { KeyedObject clone = (KeyedObject) super.clone(); if (this.object instanceof PublicCloneable) { PublicCloneable pc = (PublicCloneable) this.object; clone.object = pc.clone(); } return clone; } /** * Tests if this object is equal to another. * * @param obj the other object. * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof KeyedObject)) { return false; } KeyedObject that = (KeyedObject) obj; if (!Objects.equals(this.key, that.key)) { return false; } if (!Objects.equals(this.object, that.object)) { return false; } return true; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/KeyedObjects.java000066400000000000000000000237601463604235500261140ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * KeyedObjects.java * ----------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data; import java.io.Serializable; import java.util.Iterator; import java.util.List; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; /** * A collection of (key, object) pairs. */ public class KeyedObjects implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 1321582394193530984L; /** Storage for the data. */ private List data; /** * Creates a new collection (initially empty). */ public KeyedObjects() { this.data = new java.util.ArrayList(); } /** * Returns the number of items (values) in the collection. * * @return The item count. */ public int getItemCount() { return this.data.size(); } /** * Returns an object from the list. * * @param item the item index (zero-based). * * @return The object (possibly {@code null}). * * @throws IndexOutOfBoundsException if {@code item} is out of bounds. */ public Object getObject(int item) { Object result = null; KeyedObject kobj = (KeyedObject) this.data.get(item); if (kobj != null) { result = kobj.getObject(); } return result; } /** * Returns the key at the specified position in the list. * * @param index the item index (zero-based). * * @return The row key. * * @throws IndexOutOfBoundsException if {@code item} is out of bounds. * * @see #getIndex(Comparable) */ public Comparable getKey(int index) { Comparable result = null; KeyedObject item = (KeyedObject) this.data.get(index); if (item != null) { result = item.getKey(); } return result; } /** * Returns the index for a given key, or {@code -1}. * * @param key the key ({@code null} not permitted). * * @return The index, or {@code -1} if the key is unrecognised. * * @see #getKey(int) */ public int getIndex(Comparable key) { Args.nullNotPermitted(key, "key"); int i = 0; Iterator iterator = this.data.iterator(); while (iterator.hasNext()) { KeyedObject ko = (KeyedObject) iterator.next(); if (ko.getKey().equals(key)) { return i; } i++; } return -1; } /** * Returns a list containing all the keys in the list. * * @return The keys (never {@code null}). */ public List getKeys() { List result = new java.util.ArrayList(); Iterator iterator = this.data.iterator(); while (iterator.hasNext()) { KeyedObject ko = (KeyedObject) iterator.next(); result.add(ko.getKey()); } return result; } /** * Returns the object for a given key. If the key is not recognised, the * method should return {@code null}. * * @param key the key. * * @return The object (possibly {@code null}). * * @see #addObject(Comparable, Object) */ public Object getObject(Comparable key) { int index = getIndex(key); if (index < 0) { throw new UnknownKeyException("The key (" + key + ") is not recognised."); } return getObject(index); } /** * Adds a new object to the collection, or overwrites an existing object. * This is the same as the {@link #setObject(Comparable, Object)} method. * * @param key the key. * @param object the object. * * @see #getObject(Comparable) */ public void addObject(Comparable key, Object object) { setObject(key, object); } /** * Replaces an existing object, or adds a new object to the collection. * This is the same as the {@link #addObject(Comparable, Object)} * method. * * @param key the key ({@code null} not permitted). * @param object the object. * * @see #getObject(Comparable) */ public void setObject(Comparable key, Object object) { int keyIndex = getIndex(key); if (keyIndex >= 0) { KeyedObject ko = (KeyedObject) this.data.get(keyIndex); ko.setObject(object); } else { KeyedObject ko = new KeyedObject(key, object); this.data.add(ko); } } /** * Inserts a new value at the specified position in the dataset or, if * there is an existing item with the specified key, updates the value * for that item and moves it to the specified position. * * @param position the position (in the range {@code 0} to * {@code getItemCount()}). * @param key the key ({@code null} not permitted). * @param value the value ({@code null} permitted). */ public void insertValue(int position, Comparable key, Object value) { if (position < 0 || position > this.data.size()) { throw new IllegalArgumentException("'position' out of bounds."); } Args.nullNotPermitted(key, "key"); int pos = getIndex(key); if (pos >= 0) { this.data.remove(pos); } KeyedObject item = new KeyedObject(key, value); if (position <= this.data.size()) { this.data.add(position, item); } else { this.data.add(item); } } /** * Removes a value from the collection. * * @param index the index of the item to remove. * * @see #removeValue(Comparable) */ public void removeValue(int index) { this.data.remove(index); } /** * Removes a value from the collection. * * @param key the key ({@code null} not permitted). * * @see #removeValue(int) * * @throws UnknownKeyException if the key is not recognised. */ public void removeValue(Comparable key) { // defer argument checking int index = getIndex(key); if (index < 0) { throw new UnknownKeyException("The key (" + key.toString() + ") is not recognised."); } removeValue(index); } /** * Clears all values from the collection. */ public void clear() { this.data.clear(); } /** * Returns a clone of this object. Keys in the list should be immutable * and are not cloned. Objects in the list are cloned only if they * implement {@link PublicCloneable}. * * @return A clone. * * @throws CloneNotSupportedException if there is a problem cloning. */ @Override public Object clone() throws CloneNotSupportedException { KeyedObjects clone = (KeyedObjects) super.clone(); clone.data = new java.util.ArrayList(); Iterator iterator = this.data.iterator(); while (iterator.hasNext()) { KeyedObject ko = (KeyedObject) iterator.next(); clone.data.add(ko.clone()); } return clone; } /** * Tests this object for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof KeyedObjects)) { return false; } KeyedObjects that = (KeyedObjects) obj; int count = getItemCount(); if (count != that.getItemCount()) { return false; } for (int i = 0; i < count; i++) { Comparable k1 = getKey(i); Comparable k2 = that.getKey(i); if (!k1.equals(k2)) { return false; } Object o1 = getObject(i); Object o2 = that.getObject(i); if (o1 == null) { if (o2 != null) { return false; } } else { if (!o1.equals(o2)) { return false; } } } return true; } /** * Returns a hash code. * * @return A hash code. */ @Override public int hashCode() { return (this.data != null ? this.data.hashCode() : 0); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/KeyedObjects2D.java000066400000000000000000000362321463604235500263000ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * KeyedObject2D.java * ------------------ * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data; import java.io.Serializable; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.jfree.chart.util.Args; /** * A data structure that stores zero, one or many objects, where each object is * associated with two keys (a 'row' key and a 'column' key). */ public class KeyedObjects2D implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -1015873563138522374L; /** The row keys. */ private List rowKeys; /** The column keys. */ private List columnKeys; /** The row data. */ private List rows; /** * Creates a new instance (initially empty). */ public KeyedObjects2D() { this.rowKeys = new java.util.ArrayList(); this.columnKeys = new java.util.ArrayList(); this.rows = new java.util.ArrayList(); } /** * Returns the row count. * * @return The row count. * * @see #getColumnCount() */ public int getRowCount() { return this.rowKeys.size(); } /** * Returns the column count. * * @return The column count. * * @see #getRowCount() */ public int getColumnCount() { return this.columnKeys.size(); } /** * Returns the object for a given row and column. * * @param row the row index (in the range 0 to getRowCount() - 1). * @param column the column index (in the range 0 to getColumnCount() - 1). * * @return The object (possibly {@code null}). * * @see #getObject(Comparable, Comparable) */ public Object getObject(int row, int column) { Object result = null; KeyedObjects rowData = (KeyedObjects) this.rows.get(row); if (rowData != null) { Comparable columnKey = (Comparable) this.columnKeys.get(column); if (columnKey != null) { int index = rowData.getIndex(columnKey); if (index >= 0) { result = rowData.getObject(columnKey); } } } return result; } /** * Returns the key for a given row. * * @param row the row index (zero based). * * @return The row index. * * @see #getRowIndex(Comparable) */ public Comparable getRowKey(int row) { return (Comparable) this.rowKeys.get(row); } /** * Returns the row index for a given key, or {@code -1} if the key * is not recognised. * * @param key the key ({@code null} not permitted). * * @return The row index. * * @see #getRowKey(int) */ public int getRowIndex(Comparable key) { Args.nullNotPermitted(key, "key"); return this.rowKeys.indexOf(key); } /** * Returns the row keys. * * @return The row keys (never {@code null}). * * @see #getRowKeys() */ public List getRowKeys() { return Collections.unmodifiableList(this.rowKeys); } /** * Returns the key for a given column. * * @param column the column. * * @return The key. * * @see #getColumnIndex(Comparable) */ public Comparable getColumnKey(int column) { return (Comparable) this.columnKeys.get(column); } /** * Returns the column index for a given key, or {@code -1} if the key * is not recognised. * * @param key the key ({@code null} not permitted). * * @return The column index. * * @see #getColumnKey(int) */ public int getColumnIndex(Comparable key) { Args.nullNotPermitted(key, "key"); return this.columnKeys.indexOf(key); } /** * Returns the column keys. * * @return The column keys (never {@code null}). * * @see #getRowKeys() */ public List getColumnKeys() { return Collections.unmodifiableList(this.columnKeys); } /** * Returns the object for the given row and column keys. * * @param rowKey the row key ({@code null} not permitted). * @param columnKey the column key ({@code null} not permitted). * * @return The object (possibly {@code null}). * * @throws IllegalArgumentException if {@code rowKey} or * {@code columnKey} is {@code null}. * @throws UnknownKeyException if {@code rowKey} or * {@code columnKey} is not recognised. */ public Object getObject(Comparable rowKey, Comparable columnKey) { Args.nullNotPermitted(rowKey, "rowKey"); Args.nullNotPermitted(columnKey, "columnKey"); int row = this.rowKeys.indexOf(rowKey); if (row < 0) { throw new UnknownKeyException("Row key (" + rowKey + ") not recognised."); } int column = this.columnKeys.indexOf(columnKey); if (column < 0) { throw new UnknownKeyException("Column key (" + columnKey + ") not recognised."); } KeyedObjects rowData = (KeyedObjects) this.rows.get(row); int index = rowData.getIndex(columnKey); if (index >= 0) { return rowData.getObject(index); } else { return null; } } /** * Adds an object to the table. Performs the same function as setObject(). * * @param object the object. * @param rowKey the row key ({@code null} not permitted). * @param columnKey the column key ({@code null} not permitted). */ public void addObject(Object object, Comparable rowKey, Comparable columnKey) { setObject(object, rowKey, columnKey); } /** * Adds or updates an object. * * @param object the object. * @param rowKey the row key ({@code null} not permitted). * @param columnKey the column key ({@code null} not permitted). */ public void setObject(Object object, Comparable rowKey, Comparable columnKey) { Args.nullNotPermitted(rowKey, "rowKey"); Args.nullNotPermitted(columnKey, "columnKey"); KeyedObjects row; int rowIndex = this.rowKeys.indexOf(rowKey); if (rowIndex >= 0) { row = (KeyedObjects) this.rows.get(rowIndex); } else { this.rowKeys.add(rowKey); row = new KeyedObjects(); this.rows.add(row); } row.setObject(columnKey, object); int columnIndex = this.columnKeys.indexOf(columnKey); if (columnIndex < 0) { this.columnKeys.add(columnKey); } } /** * Removes an object from the table by setting it to {@code null}. If * all the objects in the specified row and/or column are now * {@code null}, the row and/or column is removed from the table. * * @param rowKey the row key ({@code null} not permitted). * @param columnKey the column key ({@code null} not permitted). * * @see #addObject(Object, Comparable, Comparable) */ public void removeObject(Comparable rowKey, Comparable columnKey) { int rowIndex = getRowIndex(rowKey); if (rowIndex < 0) { throw new UnknownKeyException("Row key (" + rowKey + ") not recognised."); } int columnIndex = getColumnIndex(columnKey); if (columnIndex < 0) { throw new UnknownKeyException("Column key (" + columnKey + ") not recognised."); } setObject(null, rowKey, columnKey); // 1. check whether the row is now empty. boolean allNull = true; KeyedObjects row = (KeyedObjects) this.rows.get(rowIndex); for (int item = 0, itemCount = row.getItemCount(); item < itemCount; item++) { if (row.getObject(item) != null) { allNull = false; break; } } if (allNull) { this.rowKeys.remove(rowIndex); this.rows.remove(rowIndex); } // 2. check whether the column is now empty. allNull = true; for (int item = 0, itemCount = this.rows.size(); item < itemCount; item++) { row = (KeyedObjects) this.rows.get(item); int colIndex = row.getIndex(columnKey); if (colIndex >= 0 && row.getObject(colIndex) != null) { allNull = false; break; } } if (allNull) { for (int item = 0, itemCount = this.rows.size(); item < itemCount; item++) { row = (KeyedObjects) this.rows.get(item); int colIndex = row.getIndex(columnKey); if (colIndex >= 0) { row.removeValue(colIndex); } } this.columnKeys.remove(columnKey); } } /** * Removes an entire row from the table. * * @param rowIndex the row index. * * @see #removeColumn(int) */ public void removeRow(int rowIndex) { this.rowKeys.remove(rowIndex); this.rows.remove(rowIndex); } /** * Removes an entire row from the table. * * @param rowKey the row key ({@code null} not permitted). * * @throws UnknownKeyException if {@code rowKey} is not recognised. * * @see #removeColumn(Comparable) */ public void removeRow(Comparable rowKey) { int index = getRowIndex(rowKey); if (index < 0) { throw new UnknownKeyException("Row key (" + rowKey + ") not recognised."); } removeRow(index); } /** * Removes an entire column from the table. * * @param columnIndex the column index. * * @see #removeRow(int) */ public void removeColumn(int columnIndex) { Comparable columnKey = getColumnKey(columnIndex); removeColumn(columnKey); } /** * Removes an entire column from the table. * * @param columnKey the column key ({@code null} not permitted). * * @throws UnknownKeyException if {@code rowKey} is not recognised. * * @see #removeRow(Comparable) */ public void removeColumn(Comparable columnKey) { int index = getColumnIndex(columnKey); if (index < 0) { throw new UnknownKeyException("Column key (" + columnKey + ") not recognised."); } Iterator iterator = this.rows.iterator(); while (iterator.hasNext()) { KeyedObjects rowData = (KeyedObjects) iterator.next(); int i = rowData.getIndex(columnKey); if (i >= 0) { rowData.removeValue(i); } } this.columnKeys.remove(columnKey); } /** * Clears all the data and associated keys. */ public void clear() { this.rowKeys.clear(); this.columnKeys.clear(); this.rows.clear(); } /** * Tests this object for equality with an arbitrary object. * * @param obj the object to test ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof KeyedObjects2D)) { return false; } KeyedObjects2D that = (KeyedObjects2D) obj; if (!getRowKeys().equals(that.getRowKeys())) { return false; } if (!getColumnKeys().equals(that.getColumnKeys())) { return false; } int rowCount = getRowCount(); if (rowCount != that.getRowCount()) { return false; } int colCount = getColumnCount(); if (colCount != that.getColumnCount()) { return false; } for (int r = 0; r < rowCount; r++) { for (int c = 0; c < colCount; c++) { Object v1 = getObject(r, c); Object v2 = that.getObject(r, c); if (v1 == null) { if (v2 != null) { return false; } } else { if (!v1.equals(v2)) { return false; } } } } return true; } /** * Returns a hashcode for this object. * * @return A hashcode. */ @Override public int hashCode() { int result; result = this.rowKeys.hashCode(); result = 29 * result + this.columnKeys.hashCode(); result = 29 * result + this.rows.hashCode(); return result; } /** * Returns a clone. * * @return A clone. * * @throws CloneNotSupportedException this class will not throw this * exception, but subclasses (if any) might. */ @Override public Object clone() throws CloneNotSupportedException { KeyedObjects2D clone = (KeyedObjects2D) super.clone(); clone.columnKeys = new java.util.ArrayList(this.columnKeys); clone.rowKeys = new java.util.ArrayList(this.rowKeys); clone.rows = new java.util.ArrayList(this.rows.size()); Iterator iterator = this.rows.iterator(); while (iterator.hasNext()) { KeyedObjects row = (KeyedObjects) iterator.next(); clone.rows.add(row.clone()); } return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/KeyedValue.java000066400000000000000000000034551463604235500255760ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * KeyedValue.java * --------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data; /** * A (key, value) pair. * * @param the key type. * * @see DefaultKeyedValue */ public interface KeyedValue> extends Value { /** * Returns the key associated with the value. The key returned by this * method should be immutable. * * @return The key (never {@code null}). */ K getKey(); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/KeyedValueComparator.java000066400000000000000000000122041463604235500276160ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * KeyedValueComparator.java * ------------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data; import java.io.Serializable; import java.util.Comparator; import org.jfree.chart.util.Args; import org.jfree.chart.util.SortOrder; /** * A utility class that can compare and order two {@link KeyedValue} instances * and sort them into ascending or descending order by key or by value. */ public class KeyedValueComparator implements Comparator, Serializable { /** The comparator type. */ private KeyedValueComparatorType type; /** The sort order. */ private SortOrder order; /** * Creates a new comparator. * * @param type the type ({@code BY_KEY} or {@code BY_VALUE}, * {@code null} not permitted). * @param order the order ({@code null} not permitted). */ public KeyedValueComparator(KeyedValueComparatorType type, SortOrder order) { Args.nullNotPermitted(type, "type"); Args.nullNotPermitted(order, "order"); this.type = type; this.order = order; } /** * Returns the type. * * @return The type (never {@code null}). */ public KeyedValueComparatorType getType() { return this.type; } /** * Returns the sort order. * * @return The sort order (never {@code null}). */ public SortOrder getOrder() { return this.order; } /** * Compares two {@link KeyedValue} instances and returns an * {@code int} that indicates the relative order of the two objects. * * @param o1 object 1. * @param o2 object 2. * * @return An int indicating the relative order of the objects. */ @Override public int compare(Object o1, Object o2) { if (o2 == null) { return -1; } if (o1 == null) { return 1; } int result; KeyedValue kv1 = (KeyedValue) o1; KeyedValue kv2 = (KeyedValue) o2; if (this.type == KeyedValueComparatorType.BY_KEY) { if (this.order.equals(SortOrder.ASCENDING)) { result = kv1.getKey().compareTo(kv2.getKey()); } else if (this.order.equals(SortOrder.DESCENDING)) { result = kv2.getKey().compareTo(kv1.getKey()); } else { throw new IllegalArgumentException("Unrecognised sort order."); } } else if (this.type == KeyedValueComparatorType.BY_VALUE) { Number n1 = kv1.getValue(); Number n2 = kv2.getValue(); if (n2 == null) { return -1; } if (n1 == null) { return 1; } double d1 = n1.doubleValue(); double d2 = n2.doubleValue(); if (this.order.equals(SortOrder.ASCENDING)) { if (d1 > d2) { result = 1; } else if (d1 < d2) { result = -1; } else { result = 0; } } else if (this.order.equals(SortOrder.DESCENDING)) { if (d1 > d2) { result = -1; } else if (d1 < d2) { result = 1; } else { result = 0; } } else { throw new IllegalArgumentException("Unrecognised sort order."); } } else { throw new IllegalArgumentException("Unrecognised type."); } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/KeyedValueComparatorType.java000066400000000000000000000063341463604235500304670ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------- * KeyedValueComparatorType.java * ----------------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data; import java.io.Serializable; /** * Used to indicate the type of a {@link KeyedValueComparator} : 'by key' or * 'by value'. */ public final class KeyedValueComparatorType implements Serializable { /** An object representing 'by key' sorting. */ public static final KeyedValueComparatorType BY_KEY = new KeyedValueComparatorType("KeyedValueComparatorType.BY_KEY"); /** An object representing 'by value' sorting. */ public static final KeyedValueComparatorType BY_VALUE = new KeyedValueComparatorType("KeyedValueComparatorType.BY_VALUE"); /** The name. */ private String name; /** * Private constructor. * * @param name the name. */ private KeyedValueComparatorType(String name) { this.name = name; } /** * Returns a string representing the object. * * @return The string. */ @Override public String toString() { return this.name; } /** * Returns {@code true} if this object is equal to the specified * object, and {@code false} otherwise. * * @param o the other object. * * @return A boolean. */ @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof KeyedValueComparatorType)) { return false; } KeyedValueComparatorType type = (KeyedValueComparatorType) o; if (!this.name.equals(type.name)) { return false; } return true; } /** * Returns a hash code. * * @return A hash code. */ @Override public int hashCode() { return this.name.hashCode(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/KeyedValues.java000066400000000000000000000063531463604235500257610ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * KeyedValues.java * ---------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (generics for bug fix to PiePlot); * */ package org.jfree.data; import java.util.List; /** * An ordered list of (key, value) items where the keys are unique and * non-{@code null}. * * @param the key type ({@code String} is a good default). * * @see Values * @see DefaultKeyedValues */ public interface KeyedValues> extends Values { /** * Returns the key associated with the item at a given position. Note * that some implementations allow re-ordering of the data items, so the * result may be transient. * * @param index the item index (in the range {@code 0} to * {@code getItemCount() - 1}). * * @return The key (never {@code null}). * * @throws IndexOutOfBoundsException if {@code index} is not in the * specified range. */ K getKey(int index); /** * Returns the index for a given key. * * @param key the key ({@code null} not permitted). * * @return The index, or {@code -1} if the key is unrecognised. * * @throws IllegalArgumentException if {@code key} is {@code null}. */ int getIndex(K key); /** * Returns the keys for the values in the collection. Note that you can * access the values in this collection by key or by index. For this * reason, the key order is important - this method should return the keys * in order. The returned list may be unmodifiable. * * @return The keys (never {@code null}). */ List getKeys(); /** * Returns the value for a given key. * * @param key the key. * * @return The value (possibly {@code null}). * * @throws UnknownKeyException if the key is not recognised. */ Number getValue(K key); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/KeyedValues2D.java000066400000000000000000000064201463604235500261420ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * KeyedValues2D.java * ------------------ * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data; import java.util.List; /** * An extension of the {@link Values2D} interface where a unique key is * associated with the row and column indices. */ public interface KeyedValues2D extends Values2D { /** * Returns the row key for a given index. * * @param row the row index (zero-based). * * @return The row key. * * @throws IndexOutOfBoundsException if {@code row} is out of bounds. */ Comparable getRowKey(int row); /** * Returns the row index for a given key. * * @param key the row key. * * @return The row index, or {@code -1} if the key is unrecognised. */ int getRowIndex(Comparable key); /** * Returns the row keys. * * @return The keys. */ List getRowKeys(); /** * Returns the column key for a given index. * * @param column the column index (zero-based). * * @return The column key. * * @throws IndexOutOfBoundsException if {@code row} is out of bounds. */ Comparable getColumnKey(int column); /** * Returns the column index for a given key. * * @param key the column key. * * @return The column index, or {@code -1} if the key is unrecognised. */ int getColumnIndex(Comparable key); /** * Returns the column keys. * * @return The keys. */ List getColumnKeys(); /** * Returns the value associated with the specified keys. * * @param rowKey the row key ({@code null} not permitted). * @param columnKey the column key ({@code null} not permitted). * * @return The value. * * @throws UnknownKeyException if either key is not recognised. */ Number getValue(Comparable rowKey, Comparable columnKey); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/KeyedValues2DItemKey.java000066400000000000000000000110501463604235500274250ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * KeyedValues2DItemKey.java * ------------------------- * (C) Copyright 2014-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data; import java.io.Serializable; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.Args; /** * An object that references one data item in a {@link KeyedValues2D} data * structure. Instances of this class are immutable (subject to the caller * using series, row and column keys that are immutable). * * @param the row key type. * @param the column key type. */ public class KeyedValues2DItemKey, C extends Comparable> implements ItemKey, Comparable>, Serializable { /** The row key. */ R rowKey; /** The column key. */ C columnKey; /** * Creates a new instance. * * @param rowKey the row key ({@code null} not permitted). * @param columnKey the column key ({@code null} not permitted). */ public KeyedValues2DItemKey(R rowKey, C columnKey) { Args.nullNotPermitted(rowKey, "rowKey"); Args.nullNotPermitted(columnKey, "columnKey"); this.rowKey = rowKey; this.columnKey = columnKey; } /** * Returns the row key. * * @return The row key (never {@code null}). */ public R getRowKey() { return this.rowKey; } /** * Returns the column key. * * @return The column key (never {@code null}). */ public C getColumnKey() { return this.columnKey; } @Override public int compareTo(KeyedValues2DItemKey key) { int result = this.rowKey.compareTo(key.rowKey); if (result == 0) { result = this.columnKey.compareTo(key.columnKey); } return result; } /** * Tests this key for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof KeyedValues2DItemKey)) { return false; } KeyedValues2DItemKey that = (KeyedValues2DItemKey) obj; if (!this.rowKey.equals(that.rowKey)) { return false; } if (!this.columnKey.equals(that.columnKey)) { return false; } return true; } @Override public int hashCode() { int hash = 3; hash = 17 * hash + ObjectUtils.hashCode(this.rowKey); hash = 17 * hash + ObjectUtils.hashCode(this.columnKey); return hash; } @Override public String toJSONString() { StringBuilder sb = new StringBuilder(); sb.append("{\"rowKey\": \"").append(this.rowKey.toString()); sb.append("\", "); sb.append("\"columnKey\": \"").append(this.columnKey.toString()); sb.append("\"}"); return sb.toString(); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Values2DItemKey[row="); sb.append(rowKey.toString()).append(",column="); sb.append(columnKey.toString()); sb.append("]"); return sb.toString(); } }jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/KeyedValuesItemKey.java000066400000000000000000000061161463604235500272460ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * KeyedValuesItemKey.java * ----------------------- * (C) Copyright 2014-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.data; import java.io.Serializable; import org.jfree.chart.util.Args; /** * A key that references one item in a {@link KeyedValues} data structure. */ public class KeyedValuesItemKey implements ItemKey, Serializable { /** The key for the item. */ Comparable key; /** * Creates a new instance. * * @param key the key ({@code null} not permitted). */ public KeyedValuesItemKey(Comparable key) { Args.nullNotPermitted(key, "key"); this.key = key; } /** * Returns the key. * * @return The key (never {@code null}). */ public Comparable getKey() { return this.key; } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} not permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof KeyedValuesItemKey)) { return false; } KeyedValuesItemKey that = (KeyedValuesItemKey) obj; if (!this.key.equals(that.key)) { return false; } return true; } @Override public String toJSONString() { StringBuilder sb = new StringBuilder(); sb.append("{\"key\": \"").append(this.key.toString()).append("\"}"); return sb.toString(); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("KeyedValuesItemKey["); sb.append(this.key.toString()); sb.append("]"); return sb.toString(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/Range.java000066400000000000000000000327731463604235500246010ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------- * Range.java * ---------- * (C) Copyright 2002-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Chuanhao Chiu; * Bill Kelemen; * Nicolas Brodu; * Sergei Ivanov; * Tracy Hiltbrand (equals complies with EqualsVerifier); * */ package org.jfree.data; import java.io.Serializable; import org.jfree.chart.util.Args; /** * Represents an immutable range of values. */ public strictfp class Range implements Serializable { /** For serialization. */ private static final long serialVersionUID = -906333695431863380L; /** The lower bound of the range. */ private double lower; /** The upper bound of the range. */ private double upper; /** * Creates a new range. * * @param lower the lower bound (must be <= upper bound). * @param upper the upper bound (must be >= lower bound). */ public Range(double lower, double upper) { if (lower > upper) { String msg = "Range(double, double): require lower (" + lower + ") <= upper (" + upper + ")."; throw new IllegalArgumentException(msg); } this.lower = lower; this.upper = upper; } /** * Returns the lower bound for the range. * * @return The lower bound. */ public double getLowerBound() { return this.lower; } /** * Returns the upper bound for the range. * * @return The upper bound. */ public double getUpperBound() { return this.upper; } /** * Returns the length of the range. * * @return The length. */ public double getLength() { return this.upper - this.lower; } /** * Returns the central value for the range. * * @return The central value. */ public double getCentralValue() { return this.lower / 2.0 + this.upper / 2.0; } /** * Returns {@code true} if the range contains the specified value and * {@code false} otherwise. * * @param value the value to lookup. * * @return {@code true} if the range contains the specified value. */ public boolean contains(double value) { return (value >= this.lower && value <= this.upper); } /** * Returns {@code true} if the range intersects with the specified * range, and {@code false} otherwise. * * @param b0 the lower bound (should be <= b1). * @param b1 the upper bound (should be >= b0). * * @return A boolean. */ public boolean intersects(double b0, double b1) { if (b0 <= this.lower) { return (b1 > this.lower); } else { return (b0 < this.upper && b1 >= b0); } } /** * Returns {@code true} if the range intersects with the specified * range, and {@code false} otherwise. * * @param range another range ({@code null} not permitted). * * @return A boolean. */ public boolean intersects(Range range) { return intersects(range.getLowerBound(), range.getUpperBound()); } /** * Returns the value within the range that is closest to the specified * value. * * @param value the value. * * @return The constrained value. */ public double constrain(double value) { if (contains(value)) { return value; } if (value > this.upper) { return this.upper; } if (value < this.lower) { return this.lower; } return value; // covers Double.NaN } /** * Creates a new range by combining two existing ranges. *

* Note that: *

    *
  • either range can be {@code null}, in which case the other * range is returned;
  • *
  • if both ranges are {@code null} the return value is * {@code null}.
  • *
* * @param range1 the first range ({@code null} permitted). * @param range2 the second range ({@code null} permitted). * * @return A new range (possibly {@code null}). */ public static Range combine(Range range1, Range range2) { if (range1 == null) { return range2; } if (range2 == null) { return range1; } double l = Math.min(range1.getLowerBound(), range2.getLowerBound()); double u = Math.max(range1.getUpperBound(), range2.getUpperBound()); return new Range(l, u); } /** * Returns a new range that spans both {@code range1} and * {@code range2}. This method has a special handling to ignore * Double.NaN values. * * @param range1 the first range ({@code null} permitted). * @param range2 the second range ({@code null} permitted). * * @return A new range (possibly {@code null}). */ public static Range combineIgnoringNaN(Range range1, Range range2) { if (range1 == null) { if (range2 != null && range2.isNaNRange()) { return null; } return range2; } if (range2 == null) { if (range1.isNaNRange()) { return null; } return range1; } double l = min(range1.getLowerBound(), range2.getLowerBound()); double u = max(range1.getUpperBound(), range2.getUpperBound()); if (Double.isNaN(l) && Double.isNaN(u)) { return null; } return new Range(l, u); } /** * Returns the minimum value. If either value is NaN, the other value is * returned. If both are NaN, NaN is returned. * * @param d1 value 1. * @param d2 value 2. * * @return The minimum of the two values. */ private static double min(double d1, double d2) { if (Double.isNaN(d1)) { return d2; } if (Double.isNaN(d2)) { return d1; } return Math.min(d1, d2); } private static double max(double d1, double d2) { if (Double.isNaN(d1)) { return d2; } if (Double.isNaN(d2)) { return d1; } return Math.max(d1, d2); } /** * Returns a range that includes all the values in the specified * {@code range} AND the specified {@code value}. * * @param range the range ({@code null} permitted). * @param value the value that must be included. * * @return A range. */ public static Range expandToInclude(Range range, double value) { if (range == null) { return new Range(value, value); } if (value < range.getLowerBound()) { return new Range(value, range.getUpperBound()); } else if (value > range.getUpperBound()) { return new Range(range.getLowerBound(), value); } else { return range; } } /** * Creates a new range by adding margins to an existing range. * * @param range the range ({@code null} not permitted). * @param lowerMargin the lower margin (expressed as a percentage of the * range length). * @param upperMargin the upper margin (expressed as a percentage of the * range length). * * @return The expanded range. */ public static Range expand(Range range, double lowerMargin, double upperMargin) { Args.nullNotPermitted(range, "range"); double length = range.getLength(); double lower = range.getLowerBound() - length * lowerMargin; double upper = range.getUpperBound() + length * upperMargin; if (lower > upper) { lower = lower / 2.0 + upper / 2.0; upper = lower; } return new Range(lower, upper); } /** * Shifts the range by the specified amount. * * @param base the base range ({@code null} not permitted). * @param delta the shift amount. * * @return A new range. */ public static Range shift(Range base, double delta) { return shift(base, delta, false); } /** * Shifts the range by the specified amount. * * @param base the base range ({@code null} not permitted). * @param delta the shift amount. * @param allowZeroCrossing a flag that determines whether or not the * bounds of the range are allowed to cross * zero after adjustment. * * @return A new range. */ public static Range shift(Range base, double delta, boolean allowZeroCrossing) { Args.nullNotPermitted(base, "base"); if (allowZeroCrossing) { return new Range(base.getLowerBound() + delta, base.getUpperBound() + delta); } else { return new Range(shiftWithNoZeroCrossing(base.getLowerBound(), delta), shiftWithNoZeroCrossing(base.getUpperBound(), delta)); } } /** * Returns the given {@code value} adjusted by {@code delta} but * with a check to prevent the result from crossing {@code 0.0}. * * @param value the value. * @param delta the adjustment. * * @return The adjusted value. */ private static double shiftWithNoZeroCrossing(double value, double delta) { if (value > 0.0) { return Math.max(value + delta, 0.0); } else if (value < 0.0) { return Math.min(value + delta, 0.0); } else { return value + delta; } } /** * Scales the range by the specified factor. * * @param base the base range ({@code null} not permitted). * @param factor the scaling factor (must be non-negative). * * @return A new range. */ public static Range scale(Range base, double factor) { Args.nullNotPermitted(base, "base"); if (factor < 0) { throw new IllegalArgumentException("Negative 'factor' argument."); } return new Range(base.getLowerBound() * factor, base.getUpperBound() * factor); } /** * Tests this object for equality with an arbitrary object. * * @param obj the object to test against ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (!(obj instanceof Range)) { return false; } Range range = (Range) obj; if (Double.doubleToLongBits(this.lower) != Double.doubleToLongBits(range.lower)) { return false; } if (Double.doubleToLongBits(this.upper) != Double.doubleToLongBits(range.upper)) { return false; } return true; } /** * Returns {@code true} if both the lower and upper bounds are * {@code Double.NaN}, and {@code false} otherwise. * * @return A boolean. */ public boolean isNaNRange() { return Double.isNaN(this.lower) && Double.isNaN(this.upper); } /** * Returns a hash code. * * @return A hash code. */ @Override public int hashCode() { int result; long temp; temp = Double.doubleToLongBits(this.lower); result = (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(this.upper); result = 29 * result + (int) (temp ^ (temp >>> 32)); return result; } /** * Returns a string representation of this Range. * * @return A String "Range[lower,upper]" where lower=lower range and * upper=upper range. */ @Override public String toString() { return ("Range[" + this.lower + "," + this.upper + "]"); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/RangeInfo.java000066400000000000000000000051671463604235500254120ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * RangeInfo.java * -------------- * (C) Copyright 2000-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data; /** * An interface (optional) that can be implemented by a dataset to assist in * determining the minimum and maximum values. See also {@link DomainInfo}. */ public interface RangeInfo { /** * Returns the minimum y-value in the dataset. * * @param includeInterval a flag that determines whether or not the * y-interval is taken into account. * * @return The minimum value. */ double getRangeLowerBound(boolean includeInterval); /** * Returns the maximum y-value in the dataset. * * @param includeInterval a flag that determines whether or not the * y-interval is taken into account. * * @return The maximum value. */ double getRangeUpperBound(boolean includeInterval); /** * Returns the range of the values in this dataset's range. * * @param includeInterval a flag that determines whether or not the * y-interval is taken into account. * * @return The range (or {@code null} if the dataset contains no * values). */ Range getRangeBounds(boolean includeInterval); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/RangeType.java000066400000000000000000000075201463604235500254330ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * RangeType.java * -------------- * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; */ package org.jfree.data; import java.io.ObjectStreamException; import java.io.Serializable; /** * Used to indicate the type of range to display on an axis (full, positive or * negative). */ public final class RangeType implements Serializable { /** For serialization. */ private static final long serialVersionUID = -9073319010650549239L; /** Full range (positive and negative). */ public static final RangeType FULL = new RangeType("RangeType.FULL"); /** Positive range. */ public static final RangeType POSITIVE = new RangeType("RangeType.POSITIVE"); /** Negative range. */ public static final RangeType NEGATIVE = new RangeType("RangeType.NEGATIVE"); /** The name. */ private String name; /** * Private constructor. * * @param name the name. */ private RangeType(String name) { this.name = name; } /** * Returns a string representing the object. * * @return The string. */ @Override public String toString() { return this.name; } /** * Returns {@code true} if this object is equal to the specified * object, and {@code false} otherwise. * * @param obj the other object. * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof RangeType)) { return false; } RangeType that = (RangeType) obj; if (!this.name.equals(that.toString())) { return false; } return true; } /** * Returns a hash code value for the object. * * @return The hashcode */ @Override public int hashCode() { return this.name.hashCode(); } /** * Ensures that serialization returns the unique instances. * * @return The object. * * @throws ObjectStreamException if there is a problem. */ private Object readResolve() throws ObjectStreamException { if (this.equals(RangeType.FULL)) { return RangeType.FULL; } else if (this.equals(RangeType.POSITIVE)) { return RangeType.POSITIVE; } else if (this.equals(RangeType.NEGATIVE)) { return RangeType.NEGATIVE; } return null; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/UnknownKeyException.java000066400000000000000000000034411463604235500275220ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * UnknownKeyException.java * ------------------------ * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data; /** * An exception that indicates an unknown key value. */ public class UnknownKeyException extends IllegalArgumentException { /** * Creates a new exception. * * @param message a message describing the exception. */ public UnknownKeyException(String message) { super(message); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/Value.java000066400000000000000000000032041463604235500246040ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------- * Value.java * ---------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data; /** * A general purpose interface for accessing a value. */ public interface Value { /** * Returns the value. * * @return The value (possibly {@code null}). */ Number getValue(); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/Values.java000066400000000000000000000041051463604235500247700ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------- * Values.java * ----------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data; /** * An interface through which (single-dimension) data values can be accessed. */ public interface Values { /** * Returns the number of items (values) in the collection. * * @return The item count (possibly zero). */ int getItemCount(); /** * Returns the value with the specified index. * * @param index the item index (in the range {@code 0} to * {@code getItemCount() -1}). * * @return The value (possibly {@code null}). * * @throws IndexOutOfBoundsException if {@code index} is not in the * specified range. */ Number getValue(int index); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/Values2D.java000066400000000000000000000042731463604235500251640ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------- * Values2D.java * ------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; */ package org.jfree.data; /** * A general purpose interface that can be used to access a table of values. */ public interface Values2D { /** * Returns the number of rows in the table. * * @return The row count. */ int getRowCount(); /** * Returns the number of columns in the table. * * @return The column count. */ int getColumnCount(); /** * Returns a value from the table. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The value (possibly {@code null}). * * @throws IndexOutOfBoundsException if the {@code row} * or {@code column} is out of bounds. */ Number getValue(int row, int column); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/category/000077500000000000000000000000001463604235500245035ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/category/CategoryDataset.java000066400000000000000000000036201463604235500304320ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * CategoryDataset.java * -------------------- * (C) Copyright 2000-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.category; import org.jfree.data.KeyedValues2D; import org.jfree.data.general.Dataset; /** * The interface for a dataset with one or more series, and values associated * with categories. *

* The categories are represented by {@code Comparable} instance, with the * category label being provided by the {@code toString()} method. */ public interface CategoryDataset extends KeyedValues2D, Dataset { // no additional methods required } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/category/CategoryRangeInfo.java000066400000000000000000000041641463604235500307210ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * CategoryRangeInfo.java * ---------------------- * (C) Copyright 2009-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.category; import java.util.List; import org.jfree.data.Range; /** * An interface that can (optionally) be implemented by a dataset to assist in * determining the minimum and maximum y-values. */ public interface CategoryRangeInfo { /** * Returns the range of the values in this dataset's range. * * @param visibleSeriesKeys the keys of the visible series. * @param includeInterval a flag that determines whether or not the * y-interval is taken into account. * * @return The range (or {@code null} if the dataset contains no * values). */ Range getRangeBounds(List visibleSeriesKeys, boolean includeInterval); }jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/category/CategoryToPieDataset.java000066400000000000000000000237041463604235500314000ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * CategoryToPieDataset.java * ------------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Christian W. Zuckschwerdt; * */ package org.jfree.data.category; import java.util.Collections; import java.util.List; import org.jfree.chart.util.Args; import org.jfree.chart.util.TableOrder; import org.jfree.data.general.AbstractDataset; import org.jfree.data.general.DatasetChangeEvent; import org.jfree.data.general.DatasetChangeListener; import org.jfree.data.general.PieDataset; /** * A {@link PieDataset} implementation that obtains its data from one row or * column of a {@link CategoryDataset}. */ public class CategoryToPieDataset extends AbstractDataset implements PieDataset, DatasetChangeListener { /** For serialization. */ static final long serialVersionUID = 5516396319762189617L; /** The source. */ private final CategoryDataset source; /** The extract type. */ private final TableOrder extract; /** The row or column index. */ private final int index; /** * An adaptor class that converts any {@link CategoryDataset} into a * {@link PieDataset}, by taking the values from a single row or column. *

* If {@code source} is {@code null}, the created dataset will * be empty. * * @param source the source dataset ({@code null} permitted). * @param extract extract data from rows or columns? ({@code null} * not permitted). * @param index the row or column index. */ public CategoryToPieDataset(CategoryDataset source, TableOrder extract, int index) { Args.nullNotPermitted(extract, "extract"); this.source = source; if (this.source != null) { this.source.addChangeListener(this); } this.extract = extract; this.index = index; } /** * Returns the underlying dataset. * * @return The underlying dataset (possibly {@code null}). */ public CategoryDataset getUnderlyingDataset() { return this.source; } /** * Returns the extract type, which determines whether data is read from * one row or one column of the underlying dataset. * * @return The extract type. */ public TableOrder getExtractType() { return this.extract; } /** * Returns the index of the row or column from which to extract the data. * * @return The extract index. */ public int getExtractIndex() { return this.index; } /** * Returns the number of items (values) in the collection. If the * underlying dataset is {@code null}, this method returns zero. * * @return The item count. */ @Override public int getItemCount() { int result = 0; if (this.source != null) { if (this.extract == TableOrder.BY_ROW) { result = this.source.getColumnCount(); } else if (this.extract == TableOrder.BY_COLUMN) { result = this.source.getRowCount(); } } return result; } /** * Returns a value from the dataset. * * @param item the item index (zero-based). * * @return The value (possibly {@code null}). * * @throws IndexOutOfBoundsException if {@code item} is not in the * range {@code 0} to {@code getItemCount() -1}. */ @Override public Number getValue(int item) { Number result = null; if (item < 0 || item >= getItemCount()) { // this will include the case where the underlying dataset is null throw new IndexOutOfBoundsException( "The 'item' index is out of bounds."); } if (this.extract == TableOrder.BY_ROW) { result = this.source.getValue(this.index, item); } else if (this.extract == TableOrder.BY_COLUMN) { result = this.source.getValue(item, this.index); } return result; } /** * Returns the key at the specified index. * * @param index the item index (in the range {@code 0} to * {@code getItemCount() -1}). * * @return The key. * * @throws IndexOutOfBoundsException if {@code index} is not in the * specified range. */ @Override public Comparable getKey(int index) { Comparable result = null; if (index < 0 || index >= getItemCount()) { // this includes the case where the underlying dataset is null throw new IndexOutOfBoundsException("Invalid 'index': " + index); } if (this.extract == TableOrder.BY_ROW) { result = this.source.getColumnKey(index); } else if (this.extract == TableOrder.BY_COLUMN) { result = this.source.getRowKey(index); } return result; } /** * Returns the index for a given key, or {@code -1} if there is no * such key. * * @param key the key. * * @return The index for the key, or {@code -1}. */ @Override public int getIndex(Comparable key) { int result = -1; if (this.source != null) { if (this.extract == TableOrder.BY_ROW) { result = this.source.getColumnIndex(key); } else if (this.extract == TableOrder.BY_COLUMN) { result = this.source.getRowIndex(key); } } return result; } /** * Returns the keys for the dataset. *

* If the underlying dataset is {@code null}, this method returns an * empty list. * * @return The keys. */ @Override public List getKeys() { List result = Collections.EMPTY_LIST; if (this.source != null) { if (this.extract == TableOrder.BY_ROW) { result = this.source.getColumnKeys(); } else if (this.extract == TableOrder.BY_COLUMN) { result = this.source.getRowKeys(); } } return result; } /** * Returns the value for a given key. If the key is not recognised, the * method should return {@code null} (but note that {@code null} * can be associated with a valid key also). * * @param key the key. * * @return The value (possibly {@code null}). */ @Override public Number getValue(Comparable key) { Number result = null; int keyIndex = getIndex(key); if (keyIndex != -1) { if (this.extract == TableOrder.BY_ROW) { result = this.source.getValue(this.index, keyIndex); } else if (this.extract == TableOrder.BY_COLUMN) { result = this.source.getValue(keyIndex, this.index); } } return result; } /** * Sends a {@link DatasetChangeEvent} to all registered listeners, with * this (not the underlying) dataset as the source. * * @param event the event (ignored, a new event with this dataset as the * source is sent to the listeners). */ @Override public void datasetChanged(DatasetChangeEvent event) { fireDatasetChanged(); } /** * Tests this dataset for equality with an arbitrary object, returning * {@code true} if {@code obj} is a dataset containing the same * keys and values in the same order as this dataset. * * @param obj the object to test ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof PieDataset)) { return false; } PieDataset that = (PieDataset) obj; int count = getItemCount(); if (that.getItemCount() != count) { return false; } for (int i = 0; i < count; i++) { Comparable k1 = getKey(i); Comparable k2 = that.getKey(i); if (!k1.equals(k2)) { return false; } Number v1 = getValue(i); Number v2 = that.getValue(i); if (v1 == null) { if (v2 != null) { return false; } } else { if (!v1.equals(v2)) { return false; } } } return true; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/category/DefaultCategoryDataset.java000066400000000000000000000307401463604235500317420ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * DefaultCategoryDataset.java * --------------------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.category; import java.io.Serializable; import java.util.List; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.DefaultKeyedValues2D; import org.jfree.data.UnknownKeyException; import org.jfree.data.general.AbstractDataset; import org.jfree.data.general.DatasetChangeEvent; /** * A default implementation of the {@link CategoryDataset} interface. */ public class DefaultCategoryDataset extends AbstractDataset implements CategoryDataset, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -8168173757291644622L; /** A storage structure for the data. */ private DefaultKeyedValues2D data; /** * Creates a new (empty) dataset. */ public DefaultCategoryDataset() { this.data = new DefaultKeyedValues2D(); } /** * Returns the number of rows in the table. * * @return The row count. * * @see #getColumnCount() */ @Override public int getRowCount() { return this.data.getRowCount(); } /** * Returns the number of columns in the table. * * @return The column count. * * @see #getRowCount() */ @Override public int getColumnCount() { return this.data.getColumnCount(); } /** * Returns a value from the table. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The value (possibly {@code null}). * * @see #addValue(Number, Comparable, Comparable) * @see #removeValue(Comparable, Comparable) */ @Override public Number getValue(int row, int column) { return this.data.getValue(row, column); } /** * Returns the key for the specified row. * * @param row the row index (zero-based). * * @return The row key. * * @see #getRowIndex(Comparable) * @see #getRowKeys() * @see #getColumnKey(int) */ @Override public Comparable getRowKey(int row) { return this.data.getRowKey(row); } /** * Returns the row index for a given key. * * @param key the row key ({@code null} not permitted). * * @return The row index. * * @see #getRowKey(int) */ @Override public int getRowIndex(Comparable key) { // defer null argument check return this.data.getRowIndex(key); } /** * Returns the row keys. * * @return The keys. * * @see #getRowKey(int) */ @Override public List getRowKeys() { return this.data.getRowKeys(); } /** * Returns a column key. * * @param column the column index (zero-based). * * @return The column key. * * @see #getColumnIndex(Comparable) */ @Override public Comparable getColumnKey(int column) { return this.data.getColumnKey(column); } /** * Returns the column index for a given key. * * @param key the column key ({@code null} not permitted). * * @return The column index. * * @see #getColumnKey(int) */ @Override public int getColumnIndex(Comparable key) { // defer null argument check return this.data.getColumnIndex(key); } /** * Returns the column keys. * * @return The keys. * * @see #getColumnKey(int) */ @Override public List getColumnKeys() { return this.data.getColumnKeys(); } /** * Returns the value for a pair of keys. * * @param rowKey the row key ({@code null} not permitted). * @param columnKey the column key ({@code null} not permitted). * * @return The value (possibly {@code null}). * * @throws UnknownKeyException if either key is not defined in the dataset. * * @see #addValue(Number, Comparable, Comparable) */ @Override public Number getValue(Comparable rowKey, Comparable columnKey) { return this.data.getValue(rowKey, columnKey); } /** * Adds a value to the table. Performs the same function as setValue(). * * @param value the value. * @param rowKey the row key. * @param columnKey the column key. * * @see #getValue(Comparable, Comparable) * @see #removeValue(Comparable, Comparable) */ public void addValue(Number value, Comparable rowKey, Comparable columnKey) { this.data.addValue(value, rowKey, columnKey); fireDatasetChanged(); } /** * Adds a value to the table. * * @param value the value. * @param rowKey the row key. * @param columnKey the column key. * * @see #getValue(Comparable, Comparable) */ public void addValue(double value, Comparable rowKey, Comparable columnKey) { addValue(Double.valueOf(value), rowKey, columnKey); } /** * Adds or updates a value in the table and sends a * {@link DatasetChangeEvent} to all registered listeners. * * @param value the value ({@code null} permitted). * @param rowKey the row key ({@code null} not permitted). * @param columnKey the column key ({@code null} not permitted). * * @see #getValue(Comparable, Comparable) */ public void setValue(Number value, Comparable rowKey, Comparable columnKey) { this.data.setValue(value, rowKey, columnKey); fireDatasetChanged(); } /** * Adds or updates a value in the table and sends a * {@link DatasetChangeEvent} to all registered listeners. * * @param value the value. * @param rowKey the row key ({@code null} not permitted). * @param columnKey the column key ({@code null} not permitted). * * @see #getValue(Comparable, Comparable) */ public void setValue(double value, Comparable rowKey, Comparable columnKey) { setValue(Double.valueOf(value), rowKey, columnKey); } /** * Adds the specified value to an existing value in the dataset (if the * existing value is {@code null}, it is treated as if it were 0.0). * * @param value the value. * @param rowKey the row key ({@code null} not permitted). * @param columnKey the column key ({@code null} not permitted). * * @throws UnknownKeyException if either key is not defined in the dataset. */ public void incrementValue(double value, Comparable rowKey, Comparable columnKey) { double existing = 0.0; Number n = getValue(rowKey, columnKey); if (n != null) { existing = n.doubleValue(); } setValue(existing + value, rowKey, columnKey); } /** * Removes a value from the dataset and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param rowKey the row key. * @param columnKey the column key. * * @see #addValue(Number, Comparable, Comparable) */ public void removeValue(Comparable rowKey, Comparable columnKey) { this.data.removeValue(rowKey, columnKey); fireDatasetChanged(); } /** * Removes a row from the dataset and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param rowIndex the row index. * * @see #removeColumn(int) */ public void removeRow(int rowIndex) { this.data.removeRow(rowIndex); fireDatasetChanged(); } /** * Removes a row from the dataset and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param rowKey the row key. * * @see #removeColumn(Comparable) */ public void removeRow(Comparable rowKey) { this.data.removeRow(rowKey); fireDatasetChanged(); } /** * Removes a column from the dataset and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param columnIndex the column index. * * @see #removeRow(int) */ public void removeColumn(int columnIndex) { this.data.removeColumn(columnIndex); fireDatasetChanged(); } /** * Removes a column from the dataset and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param columnKey the column key ({@code null} not permitted). * * @see #removeRow(Comparable) * * @throws UnknownKeyException if {@code columnKey} is not defined * in the dataset. */ public void removeColumn(Comparable columnKey) { this.data.removeColumn(columnKey); fireDatasetChanged(); } /** * Clears all data from the dataset and sends a {@link DatasetChangeEvent} * to all registered listeners. */ public void clear() { this.data.clear(); fireDatasetChanged(); } /** * Tests this dataset for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof CategoryDataset)) { return false; } CategoryDataset that = (CategoryDataset) obj; if (!getRowKeys().equals(that.getRowKeys())) { return false; } if (!getColumnKeys().equals(that.getColumnKeys())) { return false; } int rowCount = getRowCount(); int colCount = getColumnCount(); for (int r = 0; r < rowCount; r++) { for (int c = 0; c < colCount; c++) { Number v1 = getValue(r, c); Number v2 = that.getValue(r, c); if (v1 == null) { if (v2 != null) { return false; } } else if (!v1.equals(v2)) { return false; } } } return true; } /** * Returns a hash code for the dataset. * * @return A hash code. */ @Override public int hashCode() { return this.data.hashCode(); } /** * Returns a clone of the dataset. * * @return A clone. * * @throws CloneNotSupportedException if there is a problem cloning the * dataset. */ @Override public Object clone() throws CloneNotSupportedException { DefaultCategoryDataset clone = (DefaultCategoryDataset) super.clone(); clone.data = (DefaultKeyedValues2D) this.data.clone(); return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/category/DefaultIntervalCategoryDataset.java000066400000000000000000000644731463604235500334610ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------------- * DefaultIntervalCategoryDataset.java * ----------------------------------- * (C) Copyright 2002-present, by Jeremy Bowman and Contributors. * * Original Author: Jeremy Bowman; * Contributor(s): David Gilbert; * */ package org.jfree.data.category; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.ResourceBundle; import org.jfree.chart.util.Args; import org.jfree.chart.util.ResourceBundleWrapper; import org.jfree.data.DataUtils; import org.jfree.data.UnknownKeyException; import org.jfree.data.general.AbstractSeriesDataset; /** * A convenience class that provides a default implementation of the * {@link IntervalCategoryDataset} interface. *

* The standard constructor accepts data in a two dimensional array where the * first dimension is the series, and the second dimension is the category. */ public class DefaultIntervalCategoryDataset extends AbstractSeriesDataset implements IntervalCategoryDataset { /** The series keys. */ private Comparable[] seriesKeys; /** The category keys. */ private Comparable[] categoryKeys; /** Storage for the start value data. */ private Number[][] startData; /** Storage for the end value data. */ private Number[][] endData; /** * Creates a new dataset using the specified data values and automatically * generated series and category keys. * * @param starts the starting values for the intervals ({@code null} * not permitted). * @param ends the ending values for the intervals ({@code null} not * permitted). */ public DefaultIntervalCategoryDataset(double[][] starts, double[][] ends) { this(DataUtils.createNumberArray2D(starts), DataUtils.createNumberArray2D(ends)); } /** * Constructs a dataset and populates it with data from the array. *

* The arrays are indexed as data[series][category]. Series and category * names are automatically generated - you can change them using the * {@link #setSeriesKeys(Comparable[])} and * {@link #setCategoryKeys(Comparable[])} methods. * * @param starts the start values data. * @param ends the end values data. */ public DefaultIntervalCategoryDataset(Number[][] starts, Number[][] ends) { this(null, null, starts, ends); } /** * Constructs a DefaultIntervalCategoryDataset, populates it with data * from the arrays, and uses the supplied names for the series. *

* Category names are generated automatically ("Category 1", "Category 2", * etc). * * @param seriesNames the series names (if {@code null}, series names * will be generated automatically). * @param starts the start values data, indexed as data[series][category]. * @param ends the end values data, indexed as data[series][category]. */ public DefaultIntervalCategoryDataset(String[] seriesNames, Number[][] starts, Number[][] ends) { this(seriesNames, null, starts, ends); } /** * Constructs a DefaultIntervalCategoryDataset, populates it with data * from the arrays, and uses the supplied names for the series and the * supplied objects for the categories. * * @param seriesKeys the series keys (if {@code null}, series keys * will be generated automatically). * @param categoryKeys the category keys (if {@code null}, category * keys will be generated automatically). * @param starts the start values data, indexed as data[series][category]. * @param ends the end values data, indexed as data[series][category]. */ public DefaultIntervalCategoryDataset(Comparable[] seriesKeys, Comparable[] categoryKeys, Number[][] starts, Number[][] ends) { this.startData = starts; this.endData = ends; if (starts != null && ends != null) { String baseName = "org.jfree.data.resources.DataPackageResources"; ResourceBundle resources = ResourceBundleWrapper.getBundle( baseName); int seriesCount = starts.length; if (seriesCount != ends.length) { String errMsg = "DefaultIntervalCategoryDataset: the number " + "of series in the start value dataset does " + "not match the number of series in the end " + "value dataset."; throw new IllegalArgumentException(errMsg); } if (seriesCount > 0) { // set up the series names... if (seriesKeys != null) { if (seriesKeys.length != seriesCount) { throw new IllegalArgumentException( "The number of series keys does not " + "match the number of series in the data."); } this.seriesKeys = seriesKeys; } else { String prefix = resources.getString( "series.default-prefix") + " "; this.seriesKeys = generateKeys(seriesCount, prefix); } // set up the category names... int categoryCount = starts[0].length; if (categoryCount != ends[0].length) { String errMsg = "DefaultIntervalCategoryDataset: the " + "number of categories in the start value " + "dataset does not match the number of " + "categories in the end value dataset."; throw new IllegalArgumentException(errMsg); } if (categoryKeys != null) { if (categoryKeys.length != categoryCount) { throw new IllegalArgumentException( "The number of category keys does not match " + "the number of categories in the data."); } this.categoryKeys = categoryKeys; } else { String prefix = resources.getString( "categories.default-prefix") + " "; this.categoryKeys = generateKeys(categoryCount, prefix); } } else { this.seriesKeys = new Comparable[0]; this.categoryKeys = new Comparable[0]; } } } /** * Returns the number of series in the dataset (possibly zero). * * @return The number of series in the dataset. * * @see #getRowCount() * @see #getCategoryCount() */ @Override public int getSeriesCount() { int result = 0; if (this.startData != null) { result = this.startData.length; } return result; } /** * Returns a series index. * * @param seriesKey the series key. * * @return The series index. * * @see #getRowIndex(Comparable) * @see #getSeriesKey(int) */ public int getSeriesIndex(Comparable seriesKey) { int result = -1; for (int i = 0; i < this.seriesKeys.length; i++) { if (seriesKey.equals(this.seriesKeys[i])) { result = i; break; } } return result; } /** * Returns the name of the specified series. * * @param series the index of the required series (zero-based). * * @return The name of the specified series. * * @see #getSeriesIndex(Comparable) */ @Override public Comparable getSeriesKey(int series) { if ((series >= getSeriesCount()) || (series < 0)) { throw new IllegalArgumentException("No such series : " + series); } return this.seriesKeys[series]; } /** * Sets the names of the series in the dataset. * * @param seriesKeys the new keys ({@code null} not permitted, the * length of the array must match the number of series in the * dataset). * * @see #setCategoryKeys(Comparable[]) */ public void setSeriesKeys(Comparable[] seriesKeys) { Args.nullNotPermitted(seriesKeys, "seriesKeys"); if (seriesKeys.length != getSeriesCount()) { throw new IllegalArgumentException( "The number of series keys does not match the data."); } this.seriesKeys = seriesKeys; fireDatasetChanged(); } /** * Returns the number of categories in the dataset. * * @return The number of categories in the dataset. * * @see #getColumnCount() */ public int getCategoryCount() { int result = 0; if (this.startData != null) { if (getSeriesCount() > 0) { result = this.startData[0].length; } } return result; } /** * Returns a list of the categories in the dataset. This method supports * the {@link CategoryDataset} interface. * * @return A list of the categories in the dataset. * * @see #getRowKeys() */ @Override public List getColumnKeys() { // the CategoryDataset interface expects a list of categories, but // we've stored them in an array... if (this.categoryKeys == null) { return new ArrayList(); } else { return Collections.unmodifiableList(Arrays.asList( this.categoryKeys)); } } /** * Sets the categories for the dataset. * * @param categoryKeys an array of objects representing the categories in * the dataset. * * @see #getRowKeys() * @see #setSeriesKeys(Comparable[]) */ public void setCategoryKeys(Comparable[] categoryKeys) { Args.nullNotPermitted(categoryKeys, "categoryKeys"); if (categoryKeys.length != getCategoryCount()) { throw new IllegalArgumentException( "The number of categories does not match the data."); } for (int i = 0; i < categoryKeys.length; i++) { if (categoryKeys[i] == null) { throw new IllegalArgumentException( "DefaultIntervalCategoryDataset.setCategoryKeys(): " + "null category not permitted."); } } this.categoryKeys = categoryKeys; fireDatasetChanged(); } /** * Returns the data value for one category in a series. *

* This method is part of the CategoryDataset interface. Not particularly * meaningful for this class...returns the end value. * * @param series The required series (zero based index). * @param category The required category. * * @return The data value for one category in a series (null possible). * * @see #getEndValue(Comparable, Comparable) */ @Override public Number getValue(Comparable series, Comparable category) { int seriesIndex = getSeriesIndex(series); if (seriesIndex < 0) { throw new UnknownKeyException("Unknown 'series' key."); } int itemIndex = getColumnIndex(category); if (itemIndex < 0) { throw new UnknownKeyException("Unknown 'category' key."); } return getValue(seriesIndex, itemIndex); } /** * Returns the data value for one category in a series. *

* This method is part of the CategoryDataset interface. Not particularly * meaningful for this class...returns the end value. * * @param series the required series (zero based index). * @param category the required category. * * @return The data value for one category in a series (null possible). * * @see #getEndValue(int, int) */ @Override public Number getValue(int series, int category) { return getEndValue(series, category); } /** * Returns the start data value for one category in a series. * * @param series the required series. * @param category the required category. * * @return The start data value for one category in a series * (possibly {@code null}). * * @see #getStartValue(int, int) */ @Override public Number getStartValue(Comparable series, Comparable category) { int seriesIndex = getSeriesIndex(series); if (seriesIndex < 0) { throw new UnknownKeyException("Unknown 'series' key."); } int itemIndex = getColumnIndex(category); if (itemIndex < 0) { throw new UnknownKeyException("Unknown 'category' key."); } return getStartValue(seriesIndex, itemIndex); } /** * Returns the start data value for one category in a series. * * @param series the required series (zero based index). * @param category the required category. * * @return The start data value for one category in a series * (possibly {@code null}). * * @see #getStartValue(Comparable, Comparable) */ @Override public Number getStartValue(int series, int category) { // check arguments... if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException( "DefaultIntervalCategoryDataset.getValue(): " + "series index out of range."); } if ((category < 0) || (category >= getCategoryCount())) { throw new IllegalArgumentException( "DefaultIntervalCategoryDataset.getValue(): " + "category index out of range."); } // fetch the value... return this.startData[series][category]; } /** * Returns the end data value for one category in a series. * * @param series the required series. * @param category the required category. * * @return The end data value for one category in a series (null possible). * * @see #getEndValue(int, int) */ @Override public Number getEndValue(Comparable series, Comparable category) { int seriesIndex = getSeriesIndex(series); if (seriesIndex < 0) { throw new UnknownKeyException("Unknown 'series' key."); } int itemIndex = getColumnIndex(category); if (itemIndex < 0) { throw new UnknownKeyException("Unknown 'category' key."); } return getEndValue(seriesIndex, itemIndex); } /** * Returns the end data value for one category in a series. * * @param series the required series (zero based index). * @param category the required category. * * @return The end data value for one category in a series (null possible). * * @see #getEndValue(Comparable, Comparable) */ @Override public Number getEndValue(int series, int category) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException( "DefaultIntervalCategoryDataset.getValue(): " + "series index out of range."); } if ((category < 0) || (category >= getCategoryCount())) { throw new IllegalArgumentException( "DefaultIntervalCategoryDataset.getValue(): " + "category index out of range."); } return this.endData[series][category]; } /** * Sets the start data value for one category in a series. * * @param series the series (zero-based index). * @param category the category. * * @param value The value. * * @see #setEndValue(int, Comparable, Number) */ public void setStartValue(int series, Comparable category, Number value) { // does the series exist? if ((series < 0) || (series > getSeriesCount() - 1)) { throw new IllegalArgumentException( "DefaultIntervalCategoryDataset.setValue: " + "series outside valid range."); } // is the category valid? int categoryIndex = getCategoryIndex(category); if (categoryIndex < 0) { throw new IllegalArgumentException( "DefaultIntervalCategoryDataset.setValue: " + "unrecognised category."); } // update the data... this.startData[series][categoryIndex] = value; fireDatasetChanged(); } /** * Sets the end data value for one category in a series. * * @param series the series (zero-based index). * @param category the category. * * @param value the value. * * @see #setStartValue(int, Comparable, Number) */ public void setEndValue(int series, Comparable category, Number value) { // does the series exist? if ((series < 0) || (series > getSeriesCount() - 1)) { throw new IllegalArgumentException( "DefaultIntervalCategoryDataset.setValue: " + "series outside valid range."); } // is the category valid? int categoryIndex = getCategoryIndex(category); if (categoryIndex < 0) { throw new IllegalArgumentException( "DefaultIntervalCategoryDataset.setValue: " + "unrecognised category."); } // update the data... this.endData[series][categoryIndex] = value; fireDatasetChanged(); } /** * Returns the index for the given category. * * @param category the category ({@code null} not permitted). * * @return The index. * * @see #getColumnIndex(Comparable) */ public int getCategoryIndex(Comparable category) { int result = -1; for (int i = 0; i < this.categoryKeys.length; i++) { if (category.equals(this.categoryKeys[i])) { result = i; break; } } return result; } /** * Generates an array of keys, by appending a space plus an integer * (starting with 1) to the supplied prefix string. * * @param count the number of keys required. * @param prefix the name prefix. * * @return An array of prefixN with N = { 1 .. count}. */ private Comparable[] generateKeys(int count, String prefix) { Comparable[] result = new Comparable[count]; String name; for (int i = 0; i < count; i++) { name = prefix + (i + 1); result[i] = name; } return result; } /** * Returns a column key. * * @param column the column index. * * @return The column key. * * @see #getRowKey(int) */ @Override public Comparable getColumnKey(int column) { return this.categoryKeys[column]; } /** * Returns a column index. * * @param columnKey the column key ({@code null} not permitted). * * @return The column index. * * @see #getCategoryIndex(Comparable) */ @Override public int getColumnIndex(Comparable columnKey) { Args.nullNotPermitted(columnKey, "columnKey"); return getCategoryIndex(columnKey); } /** * Returns a row index. * * @param rowKey the row key. * * @return The row index. * * @see #getSeriesIndex(Comparable) */ @Override public int getRowIndex(Comparable rowKey) { return getSeriesIndex(rowKey); } /** * Returns a list of the series in the dataset. This method supports the * {@link CategoryDataset} interface. * * @return A list of the series in the dataset. * * @see #getColumnKeys() */ @Override public List getRowKeys() { // the CategoryDataset interface expects a list of series, but // we've stored them in an array... if (this.seriesKeys == null) { return new java.util.ArrayList(); } else { return Collections.unmodifiableList(Arrays.asList(this.seriesKeys)); } } /** * Returns the name of the specified series. * * @param row the index of the required row/series (zero-based). * * @return The name of the specified series. * * @see #getColumnKey(int) */ @Override public Comparable getRowKey(int row) { if ((row >= getRowCount()) || (row < 0)) { throw new IllegalArgumentException( "The 'row' argument is out of bounds."); } return this.seriesKeys[row]; } /** * Returns the number of categories in the dataset. This method is part of * the {@link CategoryDataset} interface. * * @return The number of categories in the dataset. * * @see #getCategoryCount() * @see #getRowCount() */ @Override public int getColumnCount() { return this.categoryKeys.length; } /** * Returns the number of series in the dataset (possibly zero). * * @return The number of series in the dataset. * * @see #getSeriesCount() * @see #getColumnCount() */ @Override public int getRowCount() { return this.seriesKeys.length; } /** * Tests this dataset for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof DefaultIntervalCategoryDataset)) { return false; } DefaultIntervalCategoryDataset that = (DefaultIntervalCategoryDataset) obj; if (!Arrays.equals(this.seriesKeys, that.seriesKeys)) { return false; } if (!Arrays.equals(this.categoryKeys, that.categoryKeys)) { return false; } if (!equal(this.startData, that.startData)) { return false; } if (!equal(this.endData, that.endData)) { return false; } // seem to be the same... return true; } /** * Returns a clone of this dataset. * * @return A clone. * * @throws CloneNotSupportedException if there is a problem cloning the * dataset. */ @Override public Object clone() throws CloneNotSupportedException { DefaultIntervalCategoryDataset clone = (DefaultIntervalCategoryDataset) super.clone(); clone.categoryKeys = (Comparable[]) this.categoryKeys.clone(); clone.seriesKeys = (Comparable[]) this.seriesKeys.clone(); clone.startData = clone(this.startData); clone.endData = clone(this.endData); return clone; } /** * Tests two double[][] arrays for equality. * * @param array1 the first array ({@code null} permitted). * @param array2 the second arrray ({@code null} permitted). * * @return A boolean. */ private static boolean equal(Number[][] array1, Number[][] array2) { if (array1 == null) { return (array2 == null); } if (array2 == null) { return false; } if (array1.length != array2.length) { return false; } for (int i = 0; i < array1.length; i++) { if (!Arrays.equals(array1[i], array2[i])) { return false; } } return true; } /** * Clones a two dimensional array of {@code Number} objects. * * @param array the array ({@code null} not permitted). * * @return A clone of the array. */ private static Number[][] clone(Number[][] array) { Args.nullNotPermitted(array, "array"); Number[][] result = new Number[array.length][]; for (int i = 0; i < array.length; i++) { Number[] child = array[i]; Number[] copychild = new Number[child.length]; System.arraycopy(child, 0, copychild, 0, child.length); result[i] = copychild; } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/category/IntervalCategoryDataset.java000066400000000000000000000062511463604235500321420ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------- * IntervalCategoryDataset.java * ---------------------------- * (C) Copyright 2002-present, by Eduard Martinescu and Contributors. * * Original Author: Eduard Martinescu; * Contributor(s): David Gilbert; * */ package org.jfree.data.category; /** * A category dataset that defines a value range for each series/category * combination. */ public interface IntervalCategoryDataset extends CategoryDataset { /** * Returns the start value for the interval for a given series and category. * * @param series the series (zero-based index). * @param category the category (zero-based index). * * @return The start value (possibly {@code null}). * * @see #getEndValue(int, int) */ Number getStartValue(int series, int category); /** * Returns the start value for the interval for a given series and category. * * @param series the series key. * @param category the category key. * * @return The start value (possibly {@code null}). * * @see #getEndValue(Comparable, Comparable) */ Number getStartValue(Comparable series, Comparable category); /** * Returns the end value for the interval for a given series and category. * * @param series the series (zero-based index). * @param category the category (zero-based index). * * @return The end value (possibly {@code null}). * * @see #getStartValue(int, int) */ Number getEndValue(int series, int category); /** * Returns the end value for the interval for a given series and category. * * @param series the series key. * @param category the category key. * * @return The end value (possibly {@code null}). * * @see #getStartValue(Comparable, Comparable) */ Number getEndValue(Comparable series, Comparable category); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/category/SlidingCategoryDataset.java000066400000000000000000000251401463604235500317450ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * SlidingCategoryDataset.java * --------------------------- * (C) Copyright 2008-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.category; import java.util.Collections; import java.util.List; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.UnknownKeyException; import org.jfree.data.general.AbstractDataset; import org.jfree.data.general.DatasetChangeEvent; /** * A {@link CategoryDataset} implementation that presents a subset of the * categories in an underlying dataset. The index of the first "visible" * category can be modified, which provides a means of "sliding" through * the categories in the underlying dataset. */ public class SlidingCategoryDataset extends AbstractDataset implements CategoryDataset { /** The underlying dataset. */ private CategoryDataset underlying; /** The index of the first category to present. */ private int firstCategoryIndex; /** The maximum number of categories to present. */ private int maximumCategoryCount; /** * Creates a new instance. * * @param underlying the underlying dataset ({@code null} not * permitted). * @param firstColumn the index of the first visible column from the * underlying dataset. * @param maxColumns the maximumColumnCount. */ public SlidingCategoryDataset(CategoryDataset underlying, int firstColumn, int maxColumns) { this.underlying = underlying; this.firstCategoryIndex = firstColumn; this.maximumCategoryCount = maxColumns; } /** * Returns the underlying dataset that was supplied to the constructor. * * @return The underlying dataset (never {@code null}). */ public CategoryDataset getUnderlyingDataset() { return this.underlying; } /** * Returns the index of the first visible category. * * @return The index. * * @see #setFirstCategoryIndex(int) */ public int getFirstCategoryIndex() { return this.firstCategoryIndex; } /** * Sets the index of the first category that should be used from the * underlying dataset, and sends a {@link DatasetChangeEvent} to all * registered listeners. * * @param first the index. * * @see #getFirstCategoryIndex() */ public void setFirstCategoryIndex(int first) { if (first < 0 || first >= this.underlying.getColumnCount()) { throw new IllegalArgumentException("Invalid index."); } this.firstCategoryIndex = first; fireDatasetChanged(); } /** * Returns the maximum category count. * * @return The maximum category count. * * @see #setMaximumCategoryCount(int) */ public int getMaximumCategoryCount() { return this.maximumCategoryCount; } /** * Sets the maximum category count and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param max the maximum. * * @see #getMaximumCategoryCount() */ public void setMaximumCategoryCount(int max) { if (max < 0) { throw new IllegalArgumentException("Requires 'max' >= 0."); } this.maximumCategoryCount = max; fireDatasetChanged(); } /** * Returns the index of the last column for this dataset, or -1. * * @return The index. */ private int lastCategoryIndex() { if (this.maximumCategoryCount == 0) { return -1; } return Math.min(this.firstCategoryIndex + this.maximumCategoryCount, this.underlying.getColumnCount()) - 1; } /** * Returns the index for the specified column key. * * @param key the key. * * @return The column index, or -1 if the key is not recognised. */ @Override public int getColumnIndex(Comparable key) { int index = this.underlying.getColumnIndex(key); if (index >= this.firstCategoryIndex && index <= lastCategoryIndex()) { return index - this.firstCategoryIndex; } return -1; // we didn't find the key } /** * Returns the column key for a given index. * * @param column the column index (zero-based). * * @return The column key. * * @throws IndexOutOfBoundsException if {@code row} is out of bounds. */ @Override public Comparable getColumnKey(int column) { return this.underlying.getColumnKey(column + this.firstCategoryIndex); } /** * Returns the column keys. * * @return The keys. * * @see #getColumnKey(int) */ @Override public List getColumnKeys() { List result = new java.util.ArrayList(); int last = lastCategoryIndex(); for (int i = this.firstCategoryIndex; i <= last; i++) { result.add(this.underlying.getColumnKey(i)); } return Collections.unmodifiableList(result); } /** * Returns the row index for a given key. * * @param key the row key. * * @return The row index, or {@code -1} if the key is unrecognised. */ @Override public int getRowIndex(Comparable key) { return this.underlying.getRowIndex(key); } /** * Returns the row key for a given index. * * @param row the row index (zero-based). * * @return The row key. * * @throws IndexOutOfBoundsException if {@code row} is out of bounds. */ @Override public Comparable getRowKey(int row) { return this.underlying.getRowKey(row); } /** * Returns the row keys. * * @return The keys. */ @Override public List getRowKeys() { return this.underlying.getRowKeys(); } /** * Returns the value for a pair of keys. * * @param rowKey the row key ({@code null} not permitted). * @param columnKey the column key ({@code null} not permitted). * * @return The value (possibly {@code null}). * * @throws UnknownKeyException if either key is not defined in the dataset. */ @Override public Number getValue(Comparable rowKey, Comparable columnKey) { int r = getRowIndex(rowKey); int c = getColumnIndex(columnKey); if (c != -1) { return this.underlying.getValue(r, c + this.firstCategoryIndex); } else { throw new UnknownKeyException("Unknown columnKey: " + columnKey); } } /** * Returns the number of columns in the table. * * @return The column count. */ @Override public int getColumnCount() { int last = lastCategoryIndex(); if (last == -1) { return 0; } else { return Math.max(last - this.firstCategoryIndex + 1, 0); } } /** * Returns the number of rows in the table. * * @return The row count. */ @Override public int getRowCount() { return this.underlying.getRowCount(); } /** * Returns a value from the table. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The value (possibly {@code null}). */ @Override public Number getValue(int row, int column) { return this.underlying.getValue(row, column + this.firstCategoryIndex); } /** * Tests this {@code SlidingCategoryDataset} for equality with an * arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof SlidingCategoryDataset)) { return false; } SlidingCategoryDataset that = (SlidingCategoryDataset) obj; if (this.firstCategoryIndex != that.firstCategoryIndex) { return false; } if (this.maximumCategoryCount != that.maximumCategoryCount) { return false; } if (!this.underlying.equals(that.underlying)) { return false; } return true; } /** * Returns an independent copy of the dataset. Note that: *

    *
  • the underlying dataset is only cloned if it implements the * {@link PublicCloneable} interface;
  • *
  • the listeners registered with this dataset are not carried over to * the cloned dataset.
  • *
* * @return An independent copy of the dataset. * * @throws CloneNotSupportedException if the dataset cannot be cloned for * any reason. */ @Override public Object clone() throws CloneNotSupportedException { SlidingCategoryDataset clone = (SlidingCategoryDataset) super.clone(); if (this.underlying instanceof PublicCloneable) { PublicCloneable pc = (PublicCloneable) this.underlying; clone.underlying = (CategoryDataset) pc.clone(); } return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/category/package.html000066400000000000000000000003241463604235500267630ustar00rootroot00000000000000 A package containing the {@link org.jfree.data.category.CategoryDataset} interface and related classes. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/flow/000077500000000000000000000000001463604235500236355ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/flow/DefaultFlowDataset.java000066400000000000000000000311201463604235500302170ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * DefaultFlowDataset.java * ----------------------- * (C) Copyright 2022-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.flow; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import org.jfree.chart.util.Args; import org.jfree.chart.util.CloneUtils; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.general.AbstractDataset; /** * A dataset representing flows between source and destination nodes. * * @param the type for the keys used to identify sources and destinations * (instances should be immutable, {@code String} is a good default choice). * * @since 1.5.3 */ public class DefaultFlowDataset> extends AbstractDataset implements FlowDataset, PublicCloneable, Serializable { /** * The nodes at each stage. The list will have N+1 entries, where N is * the number of stages - the last entry contains the destination nodes for * the final stage. */ private List> nodes; /** Node properties. */ private Map> nodeProperties; /** Storage for the flows. */ private Map, Number> flows; /** Flow properties. */ private Map> flowProperties; /** * Creates a new dataset that is initially empty. */ public DefaultFlowDataset() { this.nodes = new ArrayList<>(); this.nodes.add(new ArrayList<>()); this.nodes.add(new ArrayList<>()); this.nodeProperties = new HashMap<>(); this.flows = new HashMap<>(); this.flowProperties = new HashMap<>(); } /** * Returns a list of the source nodes for the specified stage. * * @param stage the stage (0 to {@code getStageCount() - 1}). * * @return A list of source nodes (possibly empty but never {@code null}). */ @Override public List getSources(int stage) { return new ArrayList<>(this.nodes.get(stage)); } /** * Returns a list of the destination nodes for the specified stage. * * @param stage the stage (0 to {@code getStageCount() - 1}). * * @return A list of destination nodes (possibly empty but never {@code null}). */ @Override public List getDestinations(int stage) { return new ArrayList<>(this.nodes.get(stage + 1)); } /** * Returns the set of keys for all the nodes in the dataset. * * @return The set of keys for all the nodes in the dataset (possibly empty * but never {@code null}). */ @Override public Set> getAllNodes() { Set> result = new HashSet<>(); for (int s = 0; s <= this.getStageCount(); s++) { for (K key : this.getSources(s)) { result.add(new NodeKey<>(s, key)); } } return result; } /** * Returns the value of a property, if specified, for the specified node. * * @param nodeKey the node key ({@code null} not permitted). * @param propertyKey the node key ({@code null} not permitted). * * @return The property value, or {@code null}. */ @Override public Object getNodeProperty(NodeKey nodeKey, String propertyKey) { Map props = this.nodeProperties.get(nodeKey); if (props != null) { return props.get(propertyKey); } return null; } /** * Sets a property for the specified node and notifies registered listeners * that the dataset has changed. * * @param nodeKey the node key ({@code null} not permitted). * @param propertyKey the property key ({@code null} not permitted). * @param value the property value. */ public void setNodeProperty(NodeKey nodeKey, String propertyKey, Object value) { Map props = this.nodeProperties.computeIfAbsent(nodeKey, k -> new HashMap<>()); props.put(propertyKey, value); fireDatasetChanged(); } /** * Returns the flow between a source node and a destination node at a * specified stage. This must be 0 or greater. The dataset can return * {@code null} to represent an unknown value. * * @param stage the stage index (0 to {@code getStageCount()} - 1). * @param source the source ({@code null} not permitted). * @param destination the destination ({@code null} not permitted). * * @return The flow (zero or greater, possibly {@code null}). */ @Override public Number getFlow(int stage, K source, K destination) { return this.flows.get(new FlowKey<>(stage, source, destination)); } /** * Sets the flow between a source node and a destination node at the * specified stage. A new stage will be added if {@code stage} is equal * to {@code getStageCount()}. * * @param stage the stage (0 to {@code getStageCount()}. * @param source the source ({@code null} not permitted). * @param destination the destination ({@code null} not permitted). * @param flow the flow (0 or greater). */ public void setFlow(int stage, K source, K destination, double flow) { Args.requireInRange(stage, "stage", 0, getStageCount()); Args.nullNotPermitted(source, "source"); Args.nullNotPermitted(destination, "destination"); if (stage > this.nodes.size() - 2) { this.nodes.add(new ArrayList<>()); } if (!getSources(stage).contains(source)) { this.nodes.get(stage).add(source); } if (!getDestinations(stage).contains(destination)) { this.nodes.get(stage + 1).add(destination); } this.flows.put(new FlowKey<>(stage, source, destination), flow); fireDatasetChanged(); } /** * Returns the value of a property, if specified, for the specified flow. * * @param flowKey flowKey ({@code null} not permitted). * * @return The property value, or {@code null}. */ @Override public Object getFlowProperty(FlowKey flowKey, String propertyKey) { Map props = this.flowProperties.get(flowKey); if (props != null) { return props.get(propertyKey); } return null; } /** * Sets a property for the specified flow and notifies registered listeners * that the dataset has changed. * * @param flowKey the node key ({@code null} not permitted). * @param propertyKey the property key ({@code null} not permitted). * @param value the property value. */ public void setFlowProperty(FlowKey flowKey, String propertyKey, Object value) { Map props = this.flowProperties.computeIfAbsent(flowKey, k -> new HashMap<>()); props.put(propertyKey, value); fireDatasetChanged(); } /** * Returns the number of flow stages. A flow dataset always has one or * more stages, so this method will return {@code 1} even for an empty * dataset (one with no sources, destinations or flows defined). * * @return The number of flow stages. */ @Override public int getStageCount() { return this.nodes.size() - 1; } /** * Returns a set of keys for all the flows in the dataset. * * @return A set. */ @Override public Set> getAllFlows() { return new HashSet<>(this.flows.keySet()); } /** * Returns a list of flow keys for all the flows coming into this node. * * @param nodeKey the node key ({@code null} not permitted). * * @return A list of flow keys (possibly empty but never {@code null}). */ public List> getInFlows(NodeKey nodeKey) { Args.nullNotPermitted(nodeKey, "nodeKey"); if (nodeKey.getStage() == 0) { return Collections.EMPTY_LIST; } List> result = new ArrayList<>(); for (FlowKey flowKey : this.flows.keySet()) { if (flowKey.getStage() == nodeKey.getStage() - 1 && flowKey.getDestination().equals(nodeKey.getNode())) { result.add(flowKey); } } return result; } /** * Returns a list of flow keys for all the flows going out of this node. * * @param nodeKey the node key ({@code null} not permitted). * * @return A list of flow keys (possibly empty but never {@code null}). */ public List getOutFlows(NodeKey nodeKey) { Args.nullNotPermitted(nodeKey, "nodeKey"); if (nodeKey.getStage() == this.getStageCount()) { return Collections.EMPTY_LIST; } List result = new ArrayList<>(); for (FlowKey flowKey : this.flows.keySet()) { if (flowKey.getStage() == nodeKey.getStage() && flowKey.getSource().equals(nodeKey.getNode())) { result.add(flowKey); } } return result; } /** * Returns a clone of the dataset. * * @return A clone of the dataset. * * @throws CloneNotSupportedException if there is a problem with cloning. */ @Override public Object clone() throws CloneNotSupportedException { DefaultFlowDataset clone = (DefaultFlowDataset) super.clone(); clone.flows = new HashMap<>(this.flows); clone.nodes = new ArrayList<>(); for (List list : nodes) { clone.nodes.add((List) CloneUtils.cloneList(list)); } return clone; } /** * Tests this dataset for equality with an arbitrary object. This method * will return {@code true} if the object implements the * {@link FlowDataset} and defines the exact same set of nodes and flows * as this dataset. * * @param obj the object to test equality against ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof FlowDataset)) { return false; } final FlowDataset other = (FlowDataset) obj; if (other.getStageCount() != getStageCount()) { return false; } for (int stage = 0; stage < getStageCount(); stage++) { if (!Objects.equals(other.getSources(stage), getSources(stage))) { return false; } if (!Objects.equals(other.getDestinations(stage), getDestinations(stage))) { return false; } for (K source : getSources(stage)) { for (K destination : getDestinations(stage)) { if (!Objects.equals(other.getFlow(stage, source, destination), getFlow(stage, source, destination))) { return false; } } } } return true; } @Override public int hashCode() { int hash = 3; hash = 89 * hash + Objects.hashCode(getSources(0)); hash = 89 * hash + Objects.hashCode(getDestinations(getStageCount() - 1)); return hash; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/flow/FlowDataset.java000066400000000000000000000114341463604235500267200ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * FlowDataset.java * ---------------- * (C) Copyright 2022-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.flow; import java.util.List; import java.util.Set; import org.jfree.data.general.Dataset; /** * A dataset representing value flows from a set of source nodes to a set of * destination nodes. The number of source nodes does not need to match the * number of destination nodes. *

* The dataset can represent flows in one or many stages. In the simple case, * the flows are defined in one stage, going directly from a source node to * the final destination node. In a multi-stage dataset there will be groups * of intermediate nodes and the flows are defined stage-by-stage from the * source to the destination. * * @param the type for the keys used to identify sources and destinations * (instances should be immutable, {@code String} is a good default choice). * * @since 1.5.3 */ public interface FlowDataset> extends Dataset { /** * Returns the number of flow stages (never less than one). * * @return The number of flow stages. */ int getStageCount(); /** * Returns a list of the sources at the specified stage. * * @param stage the stage index (0 to {@code getStageCount()} - 1). * * @return A list of the sources at the specified stage (never {@code null}). */ List getSources(int stage); /** * Returns a list of the destinations at the specified stage. For a * multi-stage dataset, the destinations at stage N are the same as the * sources at stage N+1. * * @param stage the stage index (0 to {@code getStageCount()} - 1). * * @return A list of the sources at the specified stage (never {@code null}). */ List getDestinations(int stage); /** * Returns the set of keys for all the nodes in the dataset. * * @return The set of keys for all the nodes in the dataset (possibly empty but never {@code null}). */ Set> getAllNodes(); /** * Returns the value of a property, if specified, for the specified node. * * @param nodeKey the node key ({@code null} not permitted). * @param propertyKey the node key ({@code null} not permitted). * * @return The property value, or {@code null}. */ Object getNodeProperty(NodeKey nodeKey, String propertyKey); /** * Returns the flow between a source node and a destination node at a * specified stage. This must be 0 or greater. The dataset can return * {@code null} to represent an unknown value. * * @param stage the stage index (0 to {@code getStageCount()} - 1). * @param source the source ({@code null} not permitted). * @param destination the destination ({@code null} not permitted). * * @return The flow (zero or greater, possibly {@code null}). */ Number getFlow(int stage, K source, K destination); /** * Returns a set of keys for all the flows in the dataset. * * @return A set. */ Set> getAllFlows(); /** * Returns the value of a property, if specified, for the specified flow. * * @param flowKey the flow key ({@code null} not permitted). * @param propertyKey the property key ({@code null} not permitted). * * @return The property value, or {@code null}. */ Object getFlowProperty(FlowKey flowKey, String propertyKey); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/flow/FlowDatasetUtils.java000066400000000000000000000166671463604235500277560ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * FlowDatasetUtils.java * --------------------- * (C) Copyright 2022-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.flow; import java.util.List; import org.jfree.chart.util.Args; /** * Utility methods related to {@link FlowDataset}. * * @since 1.5.3 */ public class FlowDatasetUtils { private FlowDatasetUtils() { // no need for instantiation } /** * Returns the total inflow for the specified node (a destination node for * the specified stage). * * @param the type for the flow identifiers. * @param dataset the dataset ({@code null} not permitted). * @param node the node ({@code null} not permitted). * @param stage the stage. * * @return The total inflow volume. */ public static > double calculateInflow(FlowDataset dataset, K node, int stage) { Args.nullNotPermitted(dataset, "dataset"); Args.nullNotPermitted(node, "node"); Args.requireInRange(stage, "stage", 0, dataset.getStageCount()); if (stage == 0) { return 0.0; // there are no inflows for stage 0 } double inflow = 0.0; List sourceKeys = dataset.getSources(stage - 1); for (K key : sourceKeys) { Number n = dataset.getFlow(stage - 1, key, node); if (n != null) { inflow = inflow + n.doubleValue(); } } return inflow; } /** * Returns the total outflow for the specified node (a source node for the * specified stage). * * @param the type for the flow identifiers. * @param dataset the dataset ({@code null} not permitted). * @param source the source node ({@code null} not permitted). * @param stage the stage. * * @return The total outflow volume. */ public static > double calculateOutflow(FlowDataset dataset, K source, int stage) { Args.nullNotPermitted(dataset, "dataset"); Args.nullNotPermitted(source, "source"); Args.requireInRange(stage, "stage", 0, dataset.getStageCount()); if (stage == dataset.getStageCount()) { return 0.0; // there are no outflows for the last stage } double outflow = 0.0; List destinationKeys = dataset.getDestinations(stage); for (K key : destinationKeys) { Number n = dataset.getFlow(stage, source, key); if (n != null) { outflow = outflow + n.doubleValue(); } } return outflow; } /** * Returns the total flow from all sources to all destinations at the * specified stage. * * @param the type for the flow identifiers. * @param dataset the dataset ({@code null} not permitted). * @param stage the stage. * * @return The total flow. */ public static > double calculateTotalFlow(FlowDataset dataset, int stage) { Args.nullNotPermitted(dataset, "dataset"); double total = 0.0; for (K source : dataset.getSources(stage)) { for (K destination : dataset.getDestinations(stage)) { Number flow = dataset.getFlow(stage, source, destination); if (flow != null) { total = total + flow.doubleValue(); } } } return total; } /** * Returns {@code true} if any of the nodes in the dataset have a property * 'selected' with the value {@code Boolean.TRUE}, and * {@code false} otherwise. * * @param the type for the node identifiers. * @param dataset the dataset ({@code null} not permitted). * * @return A boolean. */ public static > boolean hasNodeSelections(FlowDataset dataset) { Args.nullNotPermitted(dataset, "dataset"); for (int stage = 0; stage < dataset.getStageCount() + 1; stage++) { // '+1' to include final destination nodes for (K source : dataset.getSources(stage)) { NodeKey nodeKey = new NodeKey<>(stage, source); if (Boolean.TRUE.equals(dataset.getNodeProperty(nodeKey, NodeKey.SELECTED_PROPERTY_KEY))) { return true; } } } return false; } /** * Returns the number of selected nodes. * * @param the type for the node keys. * @param dataset the dataset ({@code null} not permitted). * * @return The number of selected nodes. */ public static > int selectedNodeCount(FlowDataset dataset) { Args.nullNotPermitted(dataset, "dataset"); int result = 0; for (int stage = 0; stage < dataset.getStageCount() + 1; stage++) { // '+1' to include final destination nodes for (K source : dataset.getSources(stage)) { NodeKey nodeKey = new NodeKey<>(stage, source); if (Boolean.TRUE.equals(dataset.getNodeProperty(nodeKey, NodeKey.SELECTED_PROPERTY_KEY))) { result++; } } } return result; } /** * Returns {@code true} if any of the flows in the dataset have a property * 'selected' with the value {@code Boolean.TRUE}, and * {@code false} otherwise. * * @param the type for the flow keys. * @param dataset the dataset ({@code null} not permitted). * * @return A boolean. */ public static > boolean hasFlowSelections(FlowDataset dataset) { Args.nullNotPermitted(dataset, "dataset"); for (int s = 0; s < dataset.getStageCount(); s++) { for (K source : dataset.getSources(s)) { for (K destination : dataset.getDestinations(s)) { FlowKey flowKey = new FlowKey<>(s, source, destination); if (Boolean.TRUE.equals(dataset.getFlowProperty(flowKey, FlowKey.SELECTED_PROPERTY_KEY))) { return true; } } } } return false; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/flow/FlowKey.java000066400000000000000000000114761463604235500260710ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------ * FlowKey.java * ------------ * (C) Copyright 2022-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.flow; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; /** * A key that identifies a flow within a dataset. * * @param the type for the keys used to identify sources and destinations * ({@code String} is a good default choice). * * @since 1.5.3 */ public class FlowKey> implements PublicCloneable, Serializable { /** * The key for a flow property that, if defined (at the dataset level), * contains a {@code Boolean} value for the selection status of the flow. */ public static final String SELECTED_PROPERTY_KEY = "selected"; /** The stage. */ private final int stage; /* The source node. */ private final K source; /* The destination node. */ private final K destination; /** * Creates a new instance. * * @param stage the stage. * @param source the source identifier ({@code null} not permitted). * @param destination the destination identifier ({@code null} not permitted). */ public FlowKey(int stage, K source, K destination) { Args.nullNotPermitted(source, "source"); Args.nullNotPermitted(destination, "destination"); this.stage = stage; this.source = source; this.destination = destination; } /** * Returns the stage number for the flow. * * @return The stage number. */ public int getStage() { return this.stage; } /** * Returns the source identifier. * * @return The source identifier (never {@code null}). */ public K getSource() { return source; } /** * Returns the destination identifier. * * @return The destination identifier (never {@code null}). */ public K getDestination() { return destination; } /** * Returns a string representation of this instance, primarily for * debugging purposes. * * @return A string. */ @Override public String toString() { return "[FlowKey: " + this.stage + ", " + this.source + " -> " + this.destination + "]"; } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final FlowKey other = (FlowKey) obj; if (this.stage != other.stage) { return false; } if (!Objects.equals(this.source, other.source)) { return false; } if (!Objects.equals(this.destination, other.destination)) { return false; } return true; } /** * Returns a hashcode for this instance. * * @return A hashcode. */ @Override public int hashCode() { int hash = 5; hash = 67 * hash + this.stage; hash = 67 * hash + Objects.hashCode(this.source); hash = 67 * hash + Objects.hashCode(this.destination); return hash; } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/flow/NodeKey.java000066400000000000000000000102651463604235500260420ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------ * NodeKey.java * ------------ * (C) Copyright 2022-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.flow; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; /** * A key that identifies a node in a {@link FlowDataset}. Instances of this * class are immutable. * * @param the type for the keys used to identify sources and destinations * ({@code String} is a good default choice). * * @since 1.5.3 */ public class NodeKey > implements PublicCloneable, Serializable { /** * The key for a node property that, if defined (at the dataset level), * contains a {@code Boolean} value for the selection status of the node. */ public static final String SELECTED_PROPERTY_KEY = "selected"; /** The stage. */ private final int stage; /* The source node. */ private final K node; /** * Creates a new key referencing a node in a {@link FlowDataset}. * * @param stage the stage. * @param node the node key. */ public NodeKey(int stage, K node) { Args.nullNotPermitted(node, "node"); this.stage = stage; this.node = node; } /** * Returns the stage number. * * @return The stage number. */ public int getStage() { return this.stage; } /** * Returns the identifier for the node. * * @return The identifier for the node (never {@code null}). */ public K getNode() { return this.node; } /** * Returns a string representation of this instance, primarily for * debugging purposes. * * @return A string. */ @Override public String toString() { return "[NodeKey: " + stage + ", " + node + "]"; } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final NodeKey other = (NodeKey) obj; if (this.stage != other.stage) { return false; } if (!Objects.equals(this.node, other.node)) { return false; } return true; } /** * Returns a hashcode for this instance. * * @return A hashcode. */ @Override public int hashCode() { int hash = 3; hash = 53 * hash + this.stage; hash = 53 * hash + Objects.hashCode(this.node); return hash; } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } }jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/flow/package-info.java000066400000000000000000000001551463604235500270250ustar00rootroot00000000000000/** * Data interfaces and classes for flow plots (a type of Sankey chart). */ package org.jfree.data.flow; jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/function/000077500000000000000000000000001463604235500245135ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/function/Function2D.java000066400000000000000000000033451463604235500273360ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * Function2D.java * --------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.function; /** * A function of the form {@code y = f(x)}. */ public interface Function2D { /** * Returns the value of the function ('y') for a given input ('x'). * * @param x the x-value. * * @return The function value. */ double getValue(double x); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/function/LineFunction2D.java000066400000000000000000000066711463604235500301530ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * LineFunction2D.java * ------------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.function; import java.io.Serializable; import org.jfree.chart.HashUtils; /** * A function in the form y = a + bx. */ public class LineFunction2D implements Function2D, Serializable { /** The intercept. */ private double a; /** The slope of the line. */ private double b; /** * Constructs a new line function. * * @param a the intercept. * @param b the slope. */ public LineFunction2D(double a, double b) { this.a = a; this.b = b; } /** * Returns the 'a' coefficient that was specified in the constructor. * * @return The 'a' coefficient. */ public double getIntercept() { return this.a; } /** * Returns the 'b' coefficient that was specified in the constructor. * * @return The 'b' coefficient. */ public double getSlope() { return this.b; } /** * Returns the function value. * * @param x the x-value. * * @return The value. */ @Override public double getValue(double x) { return this.a + this.b * x; } /** * Tests this function for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (!(obj instanceof LineFunction2D)) { return false; } LineFunction2D that = (LineFunction2D) obj; if (this.a != that.a) { return false; } if (this.b != that.b) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = 29; result = HashUtils.hashCode(result, this.a); result = HashUtils.hashCode(result, this.b); return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/function/NormalDistributionFunction2D.java000066400000000000000000000101511463604235500331000ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------------- * NormalDistributionFunction2D.java * --------------------------------- * (C)opyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.function; import java.io.Serializable; import org.jfree.chart.HashUtils; /** * A normal distribution function. See * http://en.wikipedia.org/wiki/Normal_distribution. */ public class NormalDistributionFunction2D implements Function2D, Serializable { /** The mean. */ private double mean; /** The standard deviation. */ private double std; /** Precomputed factor for the function value. */ private double factor; /** Precomputed denominator for the function value. */ private double denominator; /** * Constructs a new normal distribution function. * * @param mean the mean. * @param std the standard deviation (> 0). */ public NormalDistributionFunction2D(double mean, double std) { if (std <= 0) { throw new IllegalArgumentException("Requires 'std' > 0."); } this.mean = mean; this.std = std; // calculate constant values this.factor = 1 / (std * Math.sqrt(2.0 * Math.PI)); this.denominator = 2 * std * std; } /** * Returns the mean for the function. * * @return The mean. */ public double getMean() { return this.mean; } /** * Returns the standard deviation for the function. * * @return The standard deviation. */ public double getStandardDeviation() { return this.std; } /** * Returns the function value. * * @param x the x-value. * * @return The value. */ @Override public double getValue(double x) { double z = x - this.mean; return this.factor * Math.exp(-z * z / this.denominator); } /** * Tests this function for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (!(obj instanceof NormalDistributionFunction2D)) { return false; } NormalDistributionFunction2D that = (NormalDistributionFunction2D) obj; if (this.mean != that.mean) { return false; } if (this.std != that.std) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = 29; result = HashUtils.hashCode(result, this.mean); result = HashUtils.hashCode(result, this.std); return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/function/PolynomialFunction2D.java000066400000000000000000000072261463604235500314040ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * PolynomialFunction2D.java * ------------------------- * (C) Copyright 2009-present, by David Gilbert. * * Original Author: Peter Kolb; * Contributor(s): David Gilbert; * */ package org.jfree.data.function; import java.io.Serializable; import java.util.Arrays; import org.jfree.chart.HashUtils; import org.jfree.chart.util.Args; /** * A function in the form {@code y = a0 + a1 * x + a2 * x^2 + ... + an * * x^n}. Instances of this class are immutable. */ public class PolynomialFunction2D implements Function2D, Serializable { /** The coefficients. */ private double[] coefficients; /** * Constructs a new polynomial function {@code y = a0 + a1 * x + a2 * x^2 + * ... + an * x^n} * * @param coefficients an array with the coefficients [a0, a1, ..., an] * ({@code null} not permitted). */ public PolynomialFunction2D(double[] coefficients) { Args.nullNotPermitted(coefficients, "coefficients"); this.coefficients = (double[]) coefficients.clone(); } /** * Returns a copy of the coefficients array that was specified in the * constructor. * * @return The coefficients array. */ public double[] getCoefficients() { return (double[]) this.coefficients.clone(); } /** * Returns the order of the polynomial. * * @return The order. */ public int getOrder() { return this.coefficients.length - 1; } /** * Returns the function value. * * @param x the x-value. * * @return The value. */ @Override public double getValue(double x) { double y = 0; for(int i = 0; i < coefficients.length; i++){ y += coefficients[i] * Math.pow(x, i); } return y; } /** * Tests this function for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (!(obj instanceof PolynomialFunction2D)) { return false; } PolynomialFunction2D that = (PolynomialFunction2D) obj; return Arrays.equals(this.coefficients, that.coefficients); } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { return HashUtils.hashCodeForDoubleArray(this.coefficients); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/function/PowerFunction2D.java000066400000000000000000000067611463604235500303600ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * PowerFunction2D.java * -------------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.function; import java.io.Serializable; import org.jfree.chart.HashUtils; /** * A function of the form y = a * x ^ b. */ public class PowerFunction2D implements Function2D, Serializable { /** The 'a' coefficient. */ private double a; /** The 'b' coefficient. */ private double b; /** * Creates a new power function. * * @param a the 'a' coefficient. * @param b the 'b' coefficient. */ public PowerFunction2D(double a, double b) { this.a = a; this.b = b; } /** * Returns the 'a' coefficient that was specified in the constructor. * * @return The 'a' coefficient. */ public double getA() { return this.a; } /** * Returns the 'b' coefficient that was specified in the constructor. * * @return The 'b' coefficient. */ public double getB() { return this.b; } /** * Returns the value of the function for a given input ('x'). * * @param x the x-value. * * @return The value. */ @Override public double getValue(double x) { return this.a * Math.pow(x, this.b); } /** * Tests this function for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (!(obj instanceof PowerFunction2D)) { return false; } PowerFunction2D that = (PowerFunction2D) obj; if (this.a != that.a) { return false; } if (this.b != that.b) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = 29; result = HashUtils.hashCode(result, this.a); result = HashUtils.hashCode(result, this.b); return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/function/package.html000066400000000000000000000002211463604235500267670ustar00rootroot00000000000000 Representation for simple functions. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/gantt/000077500000000000000000000000001463604235500240035ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/gantt/GanttCategoryDataset.java000066400000000000000000000133531463604235500307340ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * GanttCategoryDataset.java * ------------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.gantt; import org.jfree.data.category.IntervalCategoryDataset; /** * An extension of the {@link IntervalCategoryDataset} interface that adds * support for multiple sub-intervals. */ public interface GanttCategoryDataset extends IntervalCategoryDataset { /** * Returns the percent complete for a given item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The percent complete. * * @see #getPercentComplete(Comparable, Comparable) */ Number getPercentComplete(int row, int column); /** * Returns the percent complete for a given item. * * @param rowKey the row key. * @param columnKey the column key. * * @return The percent complete. * * @see #getPercentComplete(int, int) */ Number getPercentComplete(Comparable rowKey, Comparable columnKey); /** * Returns the number of sub-intervals for a given item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The sub-interval count. * * @see #getSubIntervalCount(Comparable, Comparable) */ int getSubIntervalCount(int row, int column); /** * Returns the number of sub-intervals for a given item. * * @param rowKey the row key. * @param columnKey the column key. * * @return The sub-interval count. * * @see #getSubIntervalCount(int, int) */ int getSubIntervalCount(Comparable rowKey, Comparable columnKey); /** * Returns the start value of a sub-interval for a given item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * @param subinterval the sub-interval index (zero-based). * * @return The start value (possibly {@code null}). * * @see #getEndValue(int, int, int) */ Number getStartValue(int row, int column, int subinterval); /** * Returns the start value of a sub-interval for a given item. * * @param rowKey the row key. * @param columnKey the column key. * @param subinterval the sub-interval. * * @return The start value (possibly {@code null}). * * @see #getEndValue(Comparable, Comparable, int) */ Number getStartValue(Comparable rowKey, Comparable columnKey, int subinterval); /** * Returns the end value of a sub-interval for a given item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * @param subinterval the sub-interval. * * @return The end value (possibly {@code null}). * * @see #getStartValue(int, int, int) */ Number getEndValue(int row, int column, int subinterval); /** * Returns the end value of a sub-interval for a given item. * * @param rowKey the row key. * @param columnKey the column key. * @param subinterval the sub-interval. * * @return The end value (possibly {@code null}). * * @see #getStartValue(Comparable, Comparable, int) */ Number getEndValue(Comparable rowKey, Comparable columnKey, int subinterval); /** * Returns the percentage complete value of a sub-interval for a given item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * @param subinterval the sub-interval. * * @return The percent complete value (possibly {@code null}). * * @see #getPercentComplete(Comparable, Comparable, int) */ Number getPercentComplete(int row, int column, int subinterval); /** * Returns the percentage complete value of a sub-interval for a given item. * * @param rowKey the row key. * @param columnKey the column key. * @param subinterval the sub-interval. * * @return The percent complete value (possibly {@code null}). * * @see #getPercentComplete(int, int, int) */ Number getPercentComplete(Comparable rowKey, Comparable columnKey, int subinterval); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/gantt/SlidingGanttCategoryDataset.java000066400000000000000000000506301463604235500322450ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------------- * SlidingGanttCategoryDataset.java * -------------------------------- * (C) Copyright 2008-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.data.gantt; import java.util.Collections; import java.util.List; import java.util.Objects; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.UnknownKeyException; import org.jfree.data.general.AbstractDataset; import org.jfree.data.general.DatasetChangeEvent; /** * A {@link GanttCategoryDataset} implementation that presents a subset of the * categories in an underlying dataset. The index of the first "visible" * category can be modified, which provides a means of "sliding" through * the categories in the underlying dataset. */ public class SlidingGanttCategoryDataset extends AbstractDataset implements GanttCategoryDataset { /** The underlying dataset. */ private GanttCategoryDataset underlying; /** The index of the first category to present. */ private int firstCategoryIndex; /** The maximum number of categories to present. */ private int maximumCategoryCount; /** * Creates a new instance. * * @param underlying the underlying dataset ({@code null} not * permitted). * @param firstColumn the index of the first visible column from the * underlying dataset. * @param maxColumns the maximumColumnCount. */ public SlidingGanttCategoryDataset(GanttCategoryDataset underlying, int firstColumn, int maxColumns) { this.underlying = underlying; this.firstCategoryIndex = firstColumn; this.maximumCategoryCount = maxColumns; } /** * Returns the underlying dataset that was supplied to the constructor. * * @return The underlying dataset (never {@code null}). */ public GanttCategoryDataset getUnderlyingDataset() { return this.underlying; } /** * Returns the index of the first visible category. * * @return The index. * * @see #setFirstCategoryIndex(int) */ public int getFirstCategoryIndex() { return this.firstCategoryIndex; } /** * Sets the index of the first category that should be used from the * underlying dataset, and sends a {@link DatasetChangeEvent} to all * registered listeners. * * @param first the index. * * @see #getFirstCategoryIndex() */ public void setFirstCategoryIndex(int first) { if (first < 0 || first >= this.underlying.getColumnCount()) { throw new IllegalArgumentException("Invalid index."); } this.firstCategoryIndex = first; fireDatasetChanged(); } /** * Returns the maximum category count. * * @return The maximum category count. * * @see #setMaximumCategoryCount(int) */ public int getMaximumCategoryCount() { return this.maximumCategoryCount; } /** * Sets the maximum category count and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param max the maximum. * * @see #getMaximumCategoryCount() */ public void setMaximumCategoryCount(int max) { if (max < 0) { throw new IllegalArgumentException("Requires 'max' >= 0."); } this.maximumCategoryCount = max; fireDatasetChanged(); } /** * Returns the index of the last column for this dataset, or -1. * * @return The index. */ private int lastCategoryIndex() { if (this.maximumCategoryCount == 0) { return -1; } return Math.min(this.firstCategoryIndex + this.maximumCategoryCount, this.underlying.getColumnCount()) - 1; } /** * Returns the index for the specified column key. * * @param key the key. * * @return The column index, or -1 if the key is not recognised. */ @Override public int getColumnIndex(Comparable key) { int index = this.underlying.getColumnIndex(key); if (index >= this.firstCategoryIndex && index <= lastCategoryIndex()) { return index - this.firstCategoryIndex; } return -1; // we didn't find the key } /** * Returns the column key for a given index. * * @param column the column index (zero-based). * * @return The column key. * * @throws IndexOutOfBoundsException if {@code row} is out of bounds. */ @Override public Comparable getColumnKey(int column) { return this.underlying.getColumnKey(column + this.firstCategoryIndex); } /** * Returns the column keys. * * @return The keys. * * @see #getColumnKey(int) */ @Override public List getColumnKeys() { List result = new java.util.ArrayList(); int last = lastCategoryIndex(); for (int i = this.firstCategoryIndex; i < last; i++) { result.add(this.underlying.getColumnKey(i)); } return Collections.unmodifiableList(result); } /** * Returns the row index for a given key. * * @param key the row key. * * @return The row index, or {@code -1} if the key is unrecognised. */ @Override public int getRowIndex(Comparable key) { return this.underlying.getRowIndex(key); } /** * Returns the row key for a given index. * * @param row the row index (zero-based). * * @return The row key. * * @throws IndexOutOfBoundsException if {@code row} is out of bounds. */ @Override public Comparable getRowKey(int row) { return this.underlying.getRowKey(row); } /** * Returns the row keys. * * @return The keys. */ @Override public List getRowKeys() { return this.underlying.getRowKeys(); } /** * Returns the value for a pair of keys. * * @param rowKey the row key ({@code null} not permitted). * @param columnKey the column key ({@code null} not permitted). * * @return The value (possibly {@code null}). * * @throws UnknownKeyException if either key is not defined in the dataset. */ @Override public Number getValue(Comparable rowKey, Comparable columnKey) { int r = getRowIndex(rowKey); int c = getColumnIndex(columnKey); if (c != -1) { return this.underlying.getValue(r, c + this.firstCategoryIndex); } else { throw new UnknownKeyException("Unknown columnKey: " + columnKey); } } /** * Returns the number of columns in the table. * * @return The column count. */ @Override public int getColumnCount() { int last = lastCategoryIndex(); if (last == -1) { return 0; } else { return Math.max(last - this.firstCategoryIndex + 1, 0); } } /** * Returns the number of rows in the table. * * @return The row count. */ @Override public int getRowCount() { return this.underlying.getRowCount(); } /** * Returns a value from the table. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The value (possibly {@code null}). */ @Override public Number getValue(int row, int column) { return this.underlying.getValue(row, column + this.firstCategoryIndex); } /** * Returns the percent complete for a given item. * * @param rowKey the row key. * @param columnKey the column key. * * @return The percent complete. */ @Override public Number getPercentComplete(Comparable rowKey, Comparable columnKey) { int r = getRowIndex(rowKey); int c = getColumnIndex(columnKey); if (c != -1) { return this.underlying.getPercentComplete(r, c + this.firstCategoryIndex); } else { throw new UnknownKeyException("Unknown columnKey: " + columnKey); } } /** * Returns the percentage complete value of a sub-interval for a given item. * * @param rowKey the row key. * @param columnKey the column key. * @param subinterval the sub-interval. * * @return The percent complete value (possibly {@code null}). * * @see #getPercentComplete(int, int, int) */ @Override public Number getPercentComplete(Comparable rowKey, Comparable columnKey, int subinterval) { int r = getRowIndex(rowKey); int c = getColumnIndex(columnKey); if (c != -1) { return this.underlying.getPercentComplete(r, c + this.firstCategoryIndex, subinterval); } else { throw new UnknownKeyException("Unknown columnKey: " + columnKey); } } /** * Returns the end value of a sub-interval for a given item. * * @param rowKey the row key. * @param columnKey the column key. * @param subinterval the sub-interval. * * @return The end value (possibly {@code null}). * * @see #getStartValue(Comparable, Comparable, int) */ @Override public Number getEndValue(Comparable rowKey, Comparable columnKey, int subinterval) { int r = getRowIndex(rowKey); int c = getColumnIndex(columnKey); if (c != -1) { return this.underlying.getEndValue(r, c + this.firstCategoryIndex, subinterval); } else { throw new UnknownKeyException("Unknown columnKey: " + columnKey); } } /** * Returns the end value of a sub-interval for a given item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * @param subinterval the sub-interval. * * @return The end value (possibly {@code null}). * * @see #getStartValue(int, int, int) */ @Override public Number getEndValue(int row, int column, int subinterval) { return this.underlying.getEndValue(row, column + this.firstCategoryIndex, subinterval); } /** * Returns the percent complete for a given item. * * @param series the row index (zero-based). * @param category the column index (zero-based). * * @return The percent complete. */ @Override public Number getPercentComplete(int series, int category) { return this.underlying.getPercentComplete(series, category + this.firstCategoryIndex); } /** * Returns the percentage complete value of a sub-interval for a given item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * @param subinterval the sub-interval. * * @return The percent complete value (possibly {@code null}). * * @see #getPercentComplete(Comparable, Comparable, int) */ @Override public Number getPercentComplete(int row, int column, int subinterval) { return this.underlying.getPercentComplete(row, column + this.firstCategoryIndex, subinterval); } /** * Returns the start value of a sub-interval for a given item. * * @param rowKey the row key. * @param columnKey the column key. * @param subinterval the sub-interval. * * @return The start value (possibly {@code null}). * * @see #getEndValue(Comparable, Comparable, int) */ @Override public Number getStartValue(Comparable rowKey, Comparable columnKey, int subinterval) { int r = getRowIndex(rowKey); int c = getColumnIndex(columnKey); if (c != -1) { return this.underlying.getStartValue(r, c + this.firstCategoryIndex, subinterval); } else { throw new UnknownKeyException("Unknown columnKey: " + columnKey); } } /** * Returns the start value of a sub-interval for a given item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * @param subinterval the sub-interval index (zero-based). * * @return The start value (possibly {@code null}). * * @see #getEndValue(int, int, int) */ @Override public Number getStartValue(int row, int column, int subinterval) { return this.underlying.getStartValue(row, column + this.firstCategoryIndex, subinterval); } /** * Returns the number of sub-intervals for a given item. * * @param rowKey the row key. * @param columnKey the column key. * * @return The sub-interval count. * * @see #getSubIntervalCount(int, int) */ @Override public int getSubIntervalCount(Comparable rowKey, Comparable columnKey) { int r = getRowIndex(rowKey); int c = getColumnIndex(columnKey); if (c != -1) { return this.underlying.getSubIntervalCount(r, c + this.firstCategoryIndex); } else { throw new UnknownKeyException("Unknown columnKey: " + columnKey); } } /** * Returns the number of sub-intervals for a given item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The sub-interval count. * * @see #getSubIntervalCount(Comparable, Comparable) */ @Override public int getSubIntervalCount(int row, int column) { return this.underlying.getSubIntervalCount(row, column + this.firstCategoryIndex); } /** * Returns the start value for the interval for a given series and category. * * @param rowKey the series key. * @param columnKey the category key. * * @return The start value (possibly {@code null}). * * @see #getEndValue(Comparable, Comparable) */ @Override public Number getStartValue(Comparable rowKey, Comparable columnKey) { int r = getRowIndex(rowKey); int c = getColumnIndex(columnKey); if (c != -1) { return this.underlying.getStartValue(r, c + this.firstCategoryIndex); } else { throw new UnknownKeyException("Unknown columnKey: " + columnKey); } } /** * Returns the start value for the interval for a given series and category. * * @param row the series (zero-based index). * @param column the category (zero-based index). * * @return The start value (possibly {@code null}). * * @see #getEndValue(int, int) */ @Override public Number getStartValue(int row, int column) { return this.underlying.getStartValue(row, column + this.firstCategoryIndex); } /** * Returns the end value for the interval for a given series and category. * * @param rowKey the series key. * @param columnKey the category key. * * @return The end value (possibly {@code null}). * * @see #getStartValue(Comparable, Comparable) */ @Override public Number getEndValue(Comparable rowKey, Comparable columnKey) { int r = getRowIndex(rowKey); int c = getColumnIndex(columnKey); if (c != -1) { return this.underlying.getEndValue(r, c + this.firstCategoryIndex); } else { throw new UnknownKeyException("Unknown columnKey: " + columnKey); } } /** * Returns the end value for the interval for a given series and category. * * @param series the series (zero-based index). * @param category the category (zero-based index). * * @return The end value (possibly {@code null}). */ @Override public Number getEndValue(int series, int category) { return this.underlying.getEndValue(series, category + this.firstCategoryIndex); } /** * Tests this {@code SlidingGanttCategoryDataset} instance for equality * with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof SlidingGanttCategoryDataset)) { return false; } SlidingGanttCategoryDataset that = (SlidingGanttCategoryDataset) obj; if (this.firstCategoryIndex != that.firstCategoryIndex) { return false; } if (this.maximumCategoryCount != that.maximumCategoryCount) { return false; } if (!Objects.equals(this.underlying, that.underlying)) { return false; } if (!that.canEqual(this)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof SlidingGanttCategoryDataset); } @Override public int hashCode() { int hash = super.hashCode(); // equals calls superclass, hashCode must also hash = 23 * hash + Objects.hashCode(this.underlying); hash = 23 * hash + this.firstCategoryIndex; hash = 23 * hash + this.maximumCategoryCount; return hash; } /** * Returns an independent copy of the dataset. Note that: *
    *
  • the underlying dataset is only cloned if it implements the * {@link PublicCloneable} interface;
  • *
  • the listeners registered with this dataset are not carried over to * the cloned dataset.
  • *
* * @return An independent copy of the dataset. * * @throws CloneNotSupportedException if the dataset cannot be cloned for * any reason. */ @Override public Object clone() throws CloneNotSupportedException { SlidingGanttCategoryDataset clone = (SlidingGanttCategoryDataset) super.clone(); if (this.underlying instanceof PublicCloneable) { PublicCloneable pc = (PublicCloneable) this.underlying; clone.underlying = (GanttCategoryDataset) pc.clone(); } return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/gantt/Task.java000066400000000000000000000176111463604235500255560ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------- * Task.java * --------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.data.gantt; import java.io.Serializable; import java.util.Date; import java.util.List; import java.util.Objects; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.time.SimpleTimePeriod; import org.jfree.data.time.TimePeriod; /** * A simple representation of a task. The task has a description and a * duration. You can add sub-tasks to the task. */ public class Task implements Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 1094303785346988894L; /** The task description. */ private String description; /** The time period for the task (estimated or actual). */ private TimePeriod duration; /** The percent complete ({@code null} is permitted). */ private Double percentComplete; /** Storage for the sub-tasks (if any). */ private List subtasks; /** * Creates a new task. * * @param description the task description ({@code null} not * permitted). * @param duration the task duration ({@code null} permitted). */ public Task(String description, TimePeriod duration) { Args.nullNotPermitted(description, "description"); this.description = description; this.duration = duration; this.percentComplete = null; this.subtasks = new java.util.ArrayList(); } /** * Creates a new task. * * @param description the task description ({@code null} not * permitted). * @param start the start date ({@code null} not permitted). * @param end the end date ({@code null} not permitted). */ public Task(String description, Date start, Date end) { this(description, new SimpleTimePeriod(start, end)); } /** * Returns the task description. * * @return The task description (never {@code null}). */ public String getDescription() { return this.description; } /** * Sets the task description. * * @param description the description ({@code null} not permitted). */ public void setDescription(String description) { Args.nullNotPermitted(description, "description"); this.description = description; } /** * Returns the duration (actual or estimated) of the task. * * @return The task duration (possibly {@code null}). */ public TimePeriod getDuration() { return this.duration; } /** * Sets the task duration (actual or estimated). * * @param duration the duration ({@code null} permitted). */ public void setDuration(TimePeriod duration) { this.duration = duration; } /** * Returns the percentage complete for this task. * * @return The percentage complete (possibly {@code null}). */ public Double getPercentComplete() { return this.percentComplete; } /** * Sets the percentage complete for the task. * * @param percent the percentage ({@code null} permitted). */ public void setPercentComplete(Double percent) { this.percentComplete = percent; } /** * Sets the percentage complete for the task. * * @param percent the percentage. */ public void setPercentComplete(double percent) { setPercentComplete(Double.valueOf(percent)); } /** * Adds a sub-task to the task. * * @param subtask the subtask ({@code null} not permitted). */ public void addSubtask(Task subtask) { Args.nullNotPermitted(subtask, "subtask"); this.subtasks.add(subtask); } /** * Removes a sub-task from the task. * * @param subtask the subtask. */ public void removeSubtask(Task subtask) { this.subtasks.remove(subtask); } /** * Returns the sub-task count. * * @return The sub-task count. */ public int getSubtaskCount() { return this.subtasks.size(); } /** * Returns a sub-task. * * @param index the index. * * @return The sub-task. */ public Task getSubtask(int index) { return (Task) this.subtasks.get(index); } /** * Tests this object for equality with an arbitrary object. * * @param object the other object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object object) { if (object == this) { return true; } if (!(object instanceof Task)) { return false; } Task that = (Task) object; if (!Objects.equals(this.description, that.description)) { return false; } if (!Objects.equals(this.duration, that.duration)) { return false; } if (!Objects.equals(this.percentComplete, that.percentComplete)) { return false; } if (!Objects.equals(this.subtasks, that.subtasks)) { return false; } if (!that.canEqual(this)) { return false; } return true; } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof Task); } @Override public int hashCode() { int hash = 3; hash = 31 * hash + Objects.hashCode(this.description); hash = 31 * hash + Objects.hashCode(this.duration); hash = 31 * hash + Objects.hashCode(this.percentComplete); hash = 31 * hash + Objects.hashCode(this.subtasks); return hash; } /** * Returns a clone of the task. * * @return A clone. * * @throws CloneNotSupportedException never thrown by this class, but * subclasses may not support cloning. */ @Override public Object clone() throws CloneNotSupportedException { Task clone = (Task) super.clone(); return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/gantt/TaskSeries.java000066400000000000000000000141771463604235500267350ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * TaskSeries.java * --------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.data.gantt; import java.util.Collections; import java.util.List; import java.util.Objects; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.Args; import org.jfree.data.general.Series; /** * A series that contains zero, one or many {@link Task} objects. *

* This class is used as a building block for the {@link TaskSeriesCollection} * class that can be used to construct basic Gantt charts. */ public class TaskSeries extends Series { /** Storage for the tasks in the series. */ private List tasks; /** * Constructs a new series with the specified name. * * @param name the series name ({@code null} not permitted). */ public TaskSeries(String name) { super(name); this.tasks = new java.util.ArrayList(); } /** * Adds a task to the series and sends a * {@link org.jfree.data.general.SeriesChangeEvent} to all registered * listeners. * * @param task the task ({@code null} not permitted). */ public void add(Task task) { Args.nullNotPermitted(task, "task"); this.tasks.add(task); fireSeriesChanged(); } /** * Removes a task from the series and sends * a {@link org.jfree.data.general.SeriesChangeEvent} * to all registered listeners. * * @param task the task. */ public void remove(Task task) { this.tasks.remove(task); fireSeriesChanged(); } /** * Removes all tasks from the series and sends * a {@link org.jfree.data.general.SeriesChangeEvent} * to all registered listeners. */ public void removeAll() { this.tasks.clear(); fireSeriesChanged(); } /** * Returns the number of items in the series. * * @return The item count. */ @Override public int getItemCount() { return this.tasks.size(); } /** * Returns a task from the series. * * @param index the task index (zero-based). * * @return The task. */ public Task get(int index) { return (Task) this.tasks.get(index); } /** * Returns the task in the series that has the specified description. * * @param description the name ({@code null} not permitted). * * @return The task (possibly {@code null}). */ public Task get(String description) { Task result = null; int count = this.tasks.size(); for (int i = 0; i < count; i++) { Task t = (Task) this.tasks.get(i); if (t.getDescription().equals(description)) { result = t; break; } } return result; } /** * Returns an unmodifialble list of the tasks in the series. * * @return The tasks. */ public List getTasks() { return Collections.unmodifiableList(this.tasks); } /** * Tests this object for equality with an arbitrary object. * * @param obj the object to test against ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof TaskSeries)) { return false; } TaskSeries that = (TaskSeries) obj; if (!Objects.equals(this.tasks, that.tasks)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof TaskSeries); } @Override public int hashCode() { int hash = super.hashCode(); // equals calls superclass, hashCode must also hash = 29 * hash + java.util.Objects.hashCode(this.tasks); return hash; } /** * Returns an independent copy of this series. * * @return A clone of the series. * * @throws CloneNotSupportedException if there is some problem cloning * the dataset. */ @Override public Object clone() throws CloneNotSupportedException { TaskSeries clone = (TaskSeries) super.clone(); clone.tasks = (List) ObjectUtils.deepClone(this.tasks); return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/gantt/TaskSeriesCollection.java000066400000000000000000000543551463604235500307530ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * TaskSeriesCollection.java * ------------------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Thomas Schuster; * Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.data.gantt; import org.jfree.chart.util.Args; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.general.AbstractSeriesDataset; import org.jfree.data.general.SeriesChangeEvent; import org.jfree.data.time.TimePeriod; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.List; import java.util.Objects; /** * A collection of {@link TaskSeries} objects. This class provides one * implementation of the {@link GanttCategoryDataset} interface. */ public class TaskSeriesCollection extends AbstractSeriesDataset implements GanttCategoryDataset, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -2065799050738449903L; /** * Storage for aggregate task keys (the task description is used as the * key). */ private List keys; /** Storage for the series. */ private List data; /** * Default constructor. */ public TaskSeriesCollection() { this.keys = new java.util.ArrayList(); this.data = new java.util.ArrayList(); } /** * Returns a series from the collection. * * @param key the series key ({@code null} not permitted). * * @return The series. */ public TaskSeries getSeries(Comparable key) { if (key == null) { throw new NullPointerException("Null 'key' argument."); } TaskSeries result = null; int index = getRowIndex(key); if (index >= 0) { result = getSeries(index); } return result; } /** * Returns a series from the collection. * * @param series the series index (zero-based). * * @return The series. */ public TaskSeries getSeries(int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds"); } return (TaskSeries) this.data.get(series); } /** * Returns the number of series in the collection. * * @return The series count. */ @Override public int getSeriesCount() { return getRowCount(); } /** * Returns the name of a series. * * @param series the series index (zero-based). * * @return The name of a series. */ @Override public Comparable getSeriesKey(int series) { TaskSeries ts = (TaskSeries) this.data.get(series); return ts.getKey(); } /** * Returns the number of rows (series) in the collection. * * @return The series count. */ @Override public int getRowCount() { return this.data.size(); } /** * Returns the row keys. In this case, each series is a key. * * @return The row keys. */ @Override public List getRowKeys() { return this.data; } /** * Returns the number of column in the dataset. * * @return The column count. */ @Override public int getColumnCount() { return this.keys.size(); } /** * Returns a list of the column keys in the dataset. * * @return The category list. */ @Override public List getColumnKeys() { return this.keys; } /** * Returns a column key. * * @param index the column index. * * @return The column key. */ @Override public Comparable getColumnKey(int index) { return (Comparable) this.keys.get(index); } /** * Returns the column index for a column key. * * @param columnKey the column key ({@code null} not permitted). * * @return The column index. */ @Override public int getColumnIndex(Comparable columnKey) { Args.nullNotPermitted(columnKey, "columnKey"); return this.keys.indexOf(columnKey); } /** * Returns the row index for the given row key. * * @param rowKey the row key. * * @return The index. */ @Override public int getRowIndex(Comparable rowKey) { int result = -1; int count = this.data.size(); for (int i = 0; i < count; i++) { TaskSeries s = (TaskSeries) this.data.get(i); if (s.getKey().equals(rowKey)) { result = i; break; } } return result; } /** * Returns the key for a row. * * @param index the row index (zero-based). * * @return The key. */ @Override public Comparable getRowKey(int index) { TaskSeries series = (TaskSeries) this.data.get(index); return series.getKey(); } /** * Adds a series to the dataset and sends a * {@link org.jfree.data.general.DatasetChangeEvent} to all registered * listeners. * * @param series the series ({@code null} not permitted). */ public void add(TaskSeries series) { Args.nullNotPermitted(series, "series"); this.data.add(series); series.addChangeListener(this); // look for any keys that we don't already know about... for (Object o : series.getTasks()) { Task task = (Task) o; String key = task.getDescription(); int index = this.keys.indexOf(key); if (index < 0) { this.keys.add(key); } } fireDatasetChanged(); } /** * Removes a series from the collection and sends * a {@link org.jfree.data.general.DatasetChangeEvent} * to all registered listeners. * * @param series the series. */ public void remove(TaskSeries series) { Args.nullNotPermitted(series, "series"); if (this.data.contains(series)) { series.removeChangeListener(this); this.data.remove(series); fireDatasetChanged(); } } /** * Removes a series from the collection and sends * a {@link org.jfree.data.general.DatasetChangeEvent} * to all registered listeners. * * @param series the series (zero based index). */ public void remove(int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException( "TaskSeriesCollection.remove(): index outside valid range."); } // fetch the series, remove the change listener, then remove the series. TaskSeries ts = (TaskSeries) this.data.get(series); ts.removeChangeListener(this); this.data.remove(series); fireDatasetChanged(); } /** * Removes all the series from the collection and sends * a {@link org.jfree.data.general.DatasetChangeEvent} * to all registered listeners. */ public void removeAll() { // deregister the collection as a change listener to each series in // the collection. for (Object item : this.data) { TaskSeries series = (TaskSeries) item; series.removeChangeListener(this); } // remove all the series from the collection and notify listeners. this.data.clear(); fireDatasetChanged(); } /** * Returns the value for an item. * * @param rowKey the row key. * @param columnKey the column key. * * @return The item value. */ @Override public Number getValue(Comparable rowKey, Comparable columnKey) { return getStartValue(rowKey, columnKey); } /** * Returns the value for a task. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The start value. */ @Override public Number getValue(int row, int column) { return getStartValue(row, column); } /** * Returns the start value for a task. This is a date/time value, measured * in milliseconds since 1-Jan-1970. * * @param rowKey the series. * @param columnKey the category. * * @return The start value (possibly {@code null}). */ @Override public Number getStartValue(Comparable rowKey, Comparable columnKey) { Number result = null; int row = getRowIndex(rowKey); TaskSeries series = (TaskSeries) this.data.get(row); Task task = series.get(columnKey.toString()); if (task != null) { TimePeriod duration = task.getDuration(); if (duration != null) { result = duration.getStart().getTime(); } } return result; } /** * Returns the start value for a task. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The start value. */ @Override public Number getStartValue(int row, int column) { Comparable rowKey = getRowKey(row); Comparable columnKey = getColumnKey(column); return getStartValue(rowKey, columnKey); } /** * Returns the end value for a task. This is a date/time value, measured * in milliseconds since 1-Jan-1970. * * @param rowKey the series. * @param columnKey the category. * * @return The end value (possibly {@code null}). */ @Override public Number getEndValue(Comparable rowKey, Comparable columnKey) { Number result = null; int row = getRowIndex(rowKey); TaskSeries series = (TaskSeries) this.data.get(row); Task task = series.get(columnKey.toString()); if (task != null) { TimePeriod duration = task.getDuration(); if (duration != null) { result = duration.getEnd().getTime(); } } return result; } /** * Returns the end value for a task. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The end value. */ @Override public Number getEndValue(int row, int column) { Comparable rowKey = getRowKey(row); Comparable columnKey = getColumnKey(column); return getEndValue(rowKey, columnKey); } /** * Returns the percent complete for a given item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The percent complete (possibly {@code null}). */ @Override public Number getPercentComplete(int row, int column) { Comparable rowKey = getRowKey(row); Comparable columnKey = getColumnKey(column); return getPercentComplete(rowKey, columnKey); } /** * Returns the percent complete for a given item. * * @param rowKey the row key. * @param columnKey the column key. * * @return The percent complete. */ @Override public Number getPercentComplete(Comparable rowKey, Comparable columnKey) { Number result = null; int row = getRowIndex(rowKey); TaskSeries series = (TaskSeries) this.data.get(row); Task task = series.get(columnKey.toString()); if (task != null) { result = task.getPercentComplete(); } return result; } /** * Returns the number of sub-intervals for a given item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The sub-interval count. */ @Override public int getSubIntervalCount(int row, int column) { Comparable rowKey = getRowKey(row); Comparable columnKey = getColumnKey(column); return getSubIntervalCount(rowKey, columnKey); } /** * Returns the number of sub-intervals for a given item. * * @param rowKey the row key. * @param columnKey the column key. * * @return The sub-interval count. */ @Override public int getSubIntervalCount(Comparable rowKey, Comparable columnKey) { int result = 0; int row = getRowIndex(rowKey); TaskSeries series = (TaskSeries) this.data.get(row); Task task = series.get(columnKey.toString()); if (task != null) { result = task.getSubtaskCount(); } return result; } /** * Returns the start value of a sub-interval for a given item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * @param subinterval the sub-interval index (zero-based). * * @return The start value (possibly {@code null}). */ @Override public Number getStartValue(int row, int column, int subinterval) { Comparable rowKey = getRowKey(row); Comparable columnKey = getColumnKey(column); return getStartValue(rowKey, columnKey, subinterval); } /** * Returns the start value of a sub-interval for a given item. * * @param rowKey the row key. * @param columnKey the column key. * @param subinterval the subinterval. * * @return The start value (possibly {@code null}). */ @Override public Number getStartValue(Comparable rowKey, Comparable columnKey, int subinterval) { Number result = null; int row = getRowIndex(rowKey); TaskSeries series = (TaskSeries) this.data.get(row); Task task = series.get(columnKey.toString()); if (task != null) { Task sub = task.getSubtask(subinterval); if (sub != null) { TimePeriod duration = sub.getDuration(); result = duration.getStart().getTime(); } } return result; } /** * Returns the end value of a sub-interval for a given item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * @param subinterval the subinterval. * * @return The end value (possibly {@code null}). */ @Override public Number getEndValue(int row, int column, int subinterval) { Comparable rowKey = getRowKey(row); Comparable columnKey = getColumnKey(column); return getEndValue(rowKey, columnKey, subinterval); } /** * Returns the end value of a sub-interval for a given item. * * @param rowKey the row key. * @param columnKey the column key. * @param subinterval the subinterval. * * @return The end value (possibly {@code null}). */ @Override public Number getEndValue(Comparable rowKey, Comparable columnKey, int subinterval) { Number result = null; int row = getRowIndex(rowKey); TaskSeries series = (TaskSeries) this.data.get(row); Task task = series.get(columnKey.toString()); if (task != null) { Task sub = task.getSubtask(subinterval); if (sub != null) { TimePeriod duration = sub.getDuration(); result = duration.getEnd().getTime(); } } return result; } /** * Returns the percentage complete value of a sub-interval for a given item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * @param subinterval the sub-interval. * * @return The percent complete value (possibly {@code null}). */ @Override public Number getPercentComplete(int row, int column, int subinterval) { Comparable rowKey = getRowKey(row); Comparable columnKey = getColumnKey(column); return getPercentComplete(rowKey, columnKey, subinterval); } /** * Returns the percentage complete value of a sub-interval for a given item. * * @param rowKey the row key. * @param columnKey the column key. * @param subinterval the sub-interval. * * @return The percent complete value (possibly {@code null}). */ @Override public Number getPercentComplete(Comparable rowKey, Comparable columnKey, int subinterval) { Number result = null; int row = getRowIndex(rowKey); TaskSeries series = (TaskSeries) this.data.get(row); Task task = series.get(columnKey.toString()); if (task != null) { Task sub = task.getSubtask(subinterval); if (sub != null) { result = sub.getPercentComplete(); } } return result; } /** * Called when a series belonging to the dataset changes. * * @param event information about the change. */ @Override public void seriesChanged(SeriesChangeEvent event) { refreshKeys(); fireDatasetChanged(); } /** * Refreshes the keys. */ private void refreshKeys() { this.keys.clear(); for (int i = 0; i < getSeriesCount(); i++) { TaskSeries series = (TaskSeries) this.data.get(i); // look for any keys that we don't already know about... for (Object o : series.getTasks()) { Task task = (Task) o; String key = task.getDescription(); int index = this.keys.indexOf(key); if (index < 0) { this.keys.add(key); } } } } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof TaskSeriesCollection)) { return false; } TaskSeriesCollection that = (TaskSeriesCollection) obj; if (!Objects.equals(this.data, that.data)) { return false; } if (!Objects.equals(this.keys, that.keys)) { return false; } if (!that.canEqual(this)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof TaskSeriesCollection); } @Override public int hashCode() { int hash = super.hashCode(); // equals calls superclass, hashCode must also hash = 79 * hash + Objects.hashCode(this.data); hash = 79 * hash + Objects.hashCode(this.keys); return hash; } /** * Returns an independent copy of this dataset. * * @return A clone of the dataset. * * @throws CloneNotSupportedException if there is some problem cloning * the dataset. */ @Override public Object clone() throws CloneNotSupportedException { TaskSeriesCollection clone = (TaskSeriesCollection) super.clone(); clone.data = (List) ObjectUtils.deepClone(this.data); clone.keys = new java.util.ArrayList(this.keys); return clone; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); for (Object item : this.data) { TaskSeries series = (TaskSeries) item; series.addChangeListener(this); } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/gantt/XYTaskDataset.java000066400000000000000000000356611463604235500273520ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * XYTaskDataset.java * ------------------ * (C) Copyright 2008-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.data.gantt; import java.util.Date; import java.util.Objects; import org.jfree.chart.axis.SymbolAxis; import org.jfree.chart.renderer.xy.XYBarRenderer; import org.jfree.chart.util.Args; import org.jfree.data.general.DatasetChangeEvent; import org.jfree.data.general.DatasetChangeListener; import org.jfree.data.time.TimePeriod; import org.jfree.data.xy.AbstractXYDataset; import org.jfree.data.xy.IntervalXYDataset; /** * A dataset implementation that wraps a {@link TaskSeriesCollection} and * presents it as an {@link IntervalXYDataset}, allowing a set of tasks to * be displayed using an {@link XYBarRenderer} (and usually a * {@link SymbolAxis}). This is a very specialised dataset implementation * ---before using it, you should take some time to understand the use-cases * that it is designed for. */ public class XYTaskDataset extends AbstractXYDataset implements IntervalXYDataset, DatasetChangeListener { /** The underlying tasks. */ private TaskSeriesCollection underlying; /** The series interval width (typically 0.0 < w <= 1.0). */ private double seriesWidth; /** A flag that controls whether or not the data values are transposed. */ private boolean transposed; /** * Creates a new dataset based on the supplied collection of tasks. * * @param tasks the underlying dataset ({@code null} not permitted). */ public XYTaskDataset(TaskSeriesCollection tasks) { Args.nullNotPermitted(tasks, "tasks"); this.underlying = tasks; this.seriesWidth = 0.8; this.underlying.addChangeListener(this); } /** * Returns the underlying task series collection that was supplied to the * constructor. * * @return The underlying collection (never {@code null}). */ public TaskSeriesCollection getTasks() { return this.underlying; } /** * Returns the width of the interval for each series this dataset. * * @return The width of the series interval. * * @see #setSeriesWidth(double) */ public double getSeriesWidth() { return this.seriesWidth; } /** * Sets the series interval width and sends a {@link DatasetChangeEvent} to * all registered listeners. * * @param w the width. * * @see #getSeriesWidth() */ public void setSeriesWidth(double w) { if (w <= 0.0) { throw new IllegalArgumentException("Requires 'w' > 0.0."); } this.seriesWidth = w; fireDatasetChanged(); } /** * Returns a flag that indicates whether or not the dataset is transposed. * The default is {@code false} which means the x-values are integers * corresponding to the series indices, and the y-values are millisecond * values corresponding to the task date/time intervals. If the flag * is set to {@code true}, the x and y-values are reversed. * * @return The flag. * * @see #setTransposed(boolean) */ public boolean isTransposed() { return this.transposed; } /** * Sets the flag that controls whether or not the dataset is transposed * and sends a {@link DatasetChangeEvent} to all registered listeners. * * @param transposed the new flag value. * * @see #isTransposed() */ public void setTransposed(boolean transposed) { this.transposed = transposed; fireDatasetChanged(); } /** * Returns the number of series in the dataset. * * @return The series count. */ @Override public int getSeriesCount() { return this.underlying.getSeriesCount(); } /** * Returns the name of a series. * * @param series the series index (zero-based). * * @return The name of a series. */ @Override public Comparable getSeriesKey(int series) { return this.underlying.getSeriesKey(series); } /** * Returns the number of items (tasks) in the specified series. * * @param series the series index (zero-based). * * @return The item count. */ @Override public int getItemCount(int series) { return this.underlying.getSeries(series).getItemCount(); } /** * Returns the x-value (as a double primitive) for an item within a series. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public double getXValue(int series, int item) { if (!this.transposed) { return getSeriesValue(series); } else { return getItemValue(series, item); } } /** * Returns the starting date/time for the specified item (task) in the * given series, measured in milliseconds since 1-Jan-1970 (as in * java.util.Date). * * @param series the series index. * @param item the item (or task) index. * * @return The start date/time. */ @Override public double getStartXValue(int series, int item) { if (!this.transposed) { return getSeriesStartValue(series); } else { return getItemStartValue(series, item); } } /** * Returns the ending date/time for the specified item (task) in the * given series, measured in milliseconds since 1-Jan-1970 (as in * java.util.Date). * * @param series the series index. * @param item the item (or task) index. * * @return The end date/time. */ @Override public double getEndXValue(int series, int item) { if (!this.transposed) { return getSeriesEndValue(series); } else { return getItemEndValue(series, item); } } /** * Returns the x-value for the specified series. * * @param series the series index. * @param item the item index. * * @return The x-value (in milliseconds). */ @Override public Number getX(int series, int item) { return getXValue(series, item); } /** * Returns the starting date/time for the specified item (task) in the * given series, measured in milliseconds since 1-Jan-1970 (as in * java.util.Date). * * @param series the series index. * @param item the item (or task) index. * * @return The start date/time. */ @Override public Number getStartX(int series, int item) { return getStartXValue(series, item); } /** * Returns the ending date/time for the specified item (task) in the * given series, measured in milliseconds since 1-Jan-1970 (as in * java.util.Date). * * @param series the series index. * @param item the item (or task) index. * * @return The end date/time. */ @Override public Number getEndX(int series, int item) { return getEndXValue(series, item); } /** * Returns the y-value (as a double primitive) for an item within a series. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public double getYValue(int series, int item) { if (!this.transposed) { return getItemValue(series, item); } else { return getSeriesValue(series); } } /** * Returns the starting value of the y-interval for an item in the * given series. * * @param series the series index. * @param item the item (or task) index. * * @return The y-interval start. */ @Override public double getStartYValue(int series, int item) { if (!this.transposed) { return getItemStartValue(series, item); } else { return getSeriesStartValue(series); } } /** * Returns the ending value of the y-interval for an item in the * given series. * * @param series the series index. * @param item the item (or task) index. * * @return The y-interval end. */ @Override public double getEndYValue(int series, int item) { if (!this.transposed) { return getItemEndValue(series, item); } else { return getSeriesEndValue(series); } } /** * Returns the y-value for the specified series/item. In this * implementation, we return the series index as the y-value (this means * that every item in the series has a constant integer value). * * @param series the series index. * @param item the item index. * * @return The y-value. */ @Override public Number getY(int series, int item) { return getYValue(series, item); } /** * Returns the starting value of the y-interval for an item in the * given series. * * @param series the series index. * @param item the item (or task) index. * * @return The y-interval start. */ @Override public Number getStartY(int series, int item) { return getStartYValue(series, item); } /** * Returns the ending value of the y-interval for an item in the * given series. * * @param series the series index. * @param item the item (or task) index. * * @return The y-interval end. */ @Override public Number getEndY(int series, int item) { return getEndYValue(series, item); } private double getSeriesValue(int series) { return series; } private double getSeriesStartValue(int series) { return series - this.seriesWidth / 2.0; } private double getSeriesEndValue(int series) { return series + this.seriesWidth / 2.0; } private double getItemValue(int series, int item) { TaskSeries s = this.underlying.getSeries(series); Task t = s.get(item); TimePeriod duration = t.getDuration(); Date start = duration.getStart(); Date end = duration.getEnd(); return (start.getTime() + end.getTime()) / 2.0; } private double getItemStartValue(int series, int item) { TaskSeries s = this.underlying.getSeries(series); Task t = s.get(item); TimePeriod duration = t.getDuration(); Date start = duration.getStart(); return start.getTime(); } private double getItemEndValue(int series, int item) { TaskSeries s = this.underlying.getSeries(series); Task t = s.get(item); TimePeriod duration = t.getDuration(); Date end = duration.getEnd(); return end.getTime(); } /** * Receives a change event from the underlying dataset and responds by * firing a change event for this dataset. * * @param event the event. */ @Override public void datasetChanged(DatasetChangeEvent event) { fireDatasetChanged(); } /** * Tests this dataset for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYTaskDataset)) { return false; } XYTaskDataset that = (XYTaskDataset) obj; if (Double.doubleToLongBits(this.seriesWidth) != Double.doubleToLongBits(that.seriesWidth)) { return false; } if (this.transposed != that.transposed) { return false; } if (!Objects.equals(this.underlying, that.underlying)) { return false; } if (!that.canEqual(this)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof XYTaskDataset); } @Override public int hashCode() { int hash = super.hashCode(); // equals calls superclass, hashCode must also hash = 97 * hash + Objects.hashCode(this.underlying); hash = 97 * hash + (int) (Double.doubleToLongBits(this.seriesWidth) ^ (Double.doubleToLongBits(this.seriesWidth) >>> 32)); hash = 97 * hash + (this.transposed ? 1 : 0); return hash; } /** * Returns a clone of this dataset. * * @return A clone of this dataset. * * @throws CloneNotSupportedException if there is a problem cloning. */ @Override public Object clone() throws CloneNotSupportedException { XYTaskDataset clone = (XYTaskDataset) super.clone(); clone.underlying = (TaskSeriesCollection) this.underlying.clone(); return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/gantt/package.html000066400000000000000000000002321463604235500262610ustar00rootroot00000000000000 Data interfaces and classes for Gantt charts. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/general/000077500000000000000000000000001463604235500243035ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/general/AbstractDataset.java000066400000000000000000000246501463604235500302260ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * AbstractDataset.java * -------------------- * (C)opyright 2000-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Nicolas Brodu (for Astrium and EADS Corporate Research * Center); Tracy Hiltbrand (added equals/canEqual/hashCode); * */ package org.jfree.data.general; import java.io.IOException; import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.ObjectInputValidation; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Arrays; import java.util.EventListener; import java.util.List; import java.util.Objects; import javax.swing.event.EventListenerList; import org.jfree.chart.util.Args; /** * An abstract implementation of the {@link Dataset} interface, containing a * mechanism for registering change listeners. */ public abstract class AbstractDataset implements Dataset, Cloneable, Serializable, ObjectInputValidation { /** For serialization. */ private static final long serialVersionUID = 1918768939869230744L; /** The group that the dataset belongs to. */ private DatasetGroup group; /** Storage for registered change listeners. */ private transient EventListenerList listenerList; /** * A flag that can be used to temporarily suppress dataset change event * notifications. */ private boolean notify; @Override public int hashCode() { int hash = 3; hash = 29 * hash + Objects.hashCode(this.group); hash = 29 * hash + (this.notify ? 1 : 0); return hash; } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof AbstractDataset); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof AbstractDataset)) { return false; } AbstractDataset that = (AbstractDataset) obj; if (this.notify != that.notify) { return false; } if (!Objects.equals(this.group, that.group)) { return false; } if (!that.canEqual(this)) { return false; } return true; } /** * Constructs a dataset. By default, the dataset is assigned to its own * group. */ protected AbstractDataset() { this.group = new DatasetGroup(); this.listenerList = new EventListenerList(); this.notify = true; } /** * Returns the dataset group for the dataset. * * @return The group (never {@code null}). * * @see #setGroup(DatasetGroup) */ @Override public DatasetGroup getGroup() { return this.group; } /** * Sets the dataset group for the dataset. * * @param group the group ({@code null} not permitted). * * @see #getGroup() */ @Override public void setGroup(DatasetGroup group) { Args.nullNotPermitted(group, "group"); this.group = group; } /** * Returns the value of the notify flag. The default value is * {@code true}. If this is {@code false}, calls to the * {@link #fireDatasetChanged()} method will NOT trigger a dataset * change event. * * @return A boolean. */ public boolean getNotify() { return this.notify; } /** * Sets the notify flag, which controls whether or not the {@link #fireDatasetChanged()} * method notifies listeners. Setting this flag to {@code true} will * trigger a {@code DatasetChangeEvent} because there may be * queued up changes. * * @param notify the new flag value. */ public void setNotify(boolean notify) { this.notify = notify; if (notify) { fireDatasetChanged(); } } /** * Registers an object to receive notification of changes to the dataset. * * @param listener the object to register. * * @see #removeChangeListener(DatasetChangeListener) */ @Override public void addChangeListener(DatasetChangeListener listener) { this.listenerList.add(DatasetChangeListener.class, listener); } /** * Deregisters an object so that it no longer receives notification of * changes to the dataset. * * @param listener the object to deregister. * * @see #addChangeListener(DatasetChangeListener) */ @Override public void removeChangeListener(DatasetChangeListener listener) { this.listenerList.remove(DatasetChangeListener.class, listener); } /** * Returns {@code true} if the specified object is registered with * the dataset as a listener. Most applications won't need to call this * method, it exists mainly for use by unit testing code. * * @param listener the listener. * * @return A boolean. * * @see #addChangeListener(DatasetChangeListener) * @see #removeChangeListener(DatasetChangeListener) */ public boolean hasListener(EventListener listener) { List list = Arrays.asList(this.listenerList.getListenerList()); return list.contains(listener); } /** * Notifies all registered listeners that the dataset has changed, * provided that the {@code notify} flag has not been set to * {@code false}. * * @see #addChangeListener(DatasetChangeListener) */ protected void fireDatasetChanged() { if (this.notify) { notifyListeners(new DatasetChangeEvent(this, this)); } } /** * Notifies all registered listeners that the dataset has changed. * * @param event contains information about the event that triggered the * notification. * * @see #addChangeListener(DatasetChangeListener) * @see #removeChangeListener(DatasetChangeListener) */ protected void notifyListeners(DatasetChangeEvent event) { Object[] listeners = this.listenerList.getListenerList(); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == DatasetChangeListener.class) { ((DatasetChangeListener) listeners[i + 1]).datasetChanged( event); } } } /** * Returns a clone of the dataset. The cloned dataset will NOT include the * {@link DatasetChangeListener} references that have been registered with * this dataset. * * @return A clone. * * @throws CloneNotSupportedException if the dataset does not support * cloning. */ @Override public Object clone() throws CloneNotSupportedException { AbstractDataset clone = (AbstractDataset) super.clone(); clone.listenerList = new EventListenerList(); return clone; } /** * Handles serialization. * * @param stream the output stream. * * @throws IOException if there is an I/O problem. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); } /** * Restores a serialized object. * * @param stream the input stream. * * @throws IOException if there is an I/O problem. * @throws ClassNotFoundException if there is a problem loading a class. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.listenerList = new EventListenerList(); stream.registerValidation(this, 10); // see comments about priority of // 10 in validateObject() } /** * Validates the object. We use this opportunity to call listeners who have * registered during the deserialization process, as listeners are not * serialized. This method is called by the serialization system after the * entire graph is read. * * This object has registered itself to the system with a priority of 10. * Other callbacks may register with a higher priority number to be called * before this object, or with a lower priority number to be called after * the listeners were notified. * * All listeners are supposed to have register by now, either in their * readObject or validateObject methods. Notify them that this dataset has * changed. * * @exception InvalidObjectException If the object cannot validate itself. */ @Override public void validateObject() throws InvalidObjectException { fireDatasetChanged(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/general/AbstractSeriesDataset.java000066400000000000000000000065641463604235500314050ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * AbstractSeriesDataset.java * -------------------------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.general; import java.io.Serializable; /** * An abstract implementation of the {@link SeriesDataset} interface, * containing a mechanism for registering change listeners. */ public abstract class AbstractSeriesDataset extends AbstractDataset implements SeriesDataset, SeriesChangeListener, Serializable { /** For serialization. */ private static final long serialVersionUID = -6074996219705033171L; /** * Creates a new dataset. */ protected AbstractSeriesDataset() { super(); } /** * Returns the number of series in the dataset. * * @return The series count. */ @Override public abstract int getSeriesCount(); /** * Returns the key for a series. *

* If {@code series} is not within the specified range, the * implementing method should throw an {@link IndexOutOfBoundsException} * (preferred) or an {@link IllegalArgumentException}. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * * @return The series key. */ @Override public abstract Comparable getSeriesKey(int series); /** * Returns the index of the named series, or -1. * * @param seriesKey the series key ({@code null} permitted). * * @return The index. */ @Override public int indexOf(Comparable seriesKey) { int seriesCount = getSeriesCount(); for (int s = 0; s < seriesCount; s++) { if (getSeriesKey(s).equals(seriesKey)) { return s; } } return -1; } /** * Called when a series belonging to the dataset changes. * * @param event information about the change. */ @Override public void seriesChanged(SeriesChangeEvent event) { fireDatasetChanged(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/general/Dataset.java000066400000000000000000000051271463604235500265400ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------ * Dataset.java * ------------ * (C) Copyright 2000-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.general; /** * The base interface for data sets. *

* All datasets are required to support the {@link DatasetChangeEvent} * mechanism by allowing listeners to register and receive notification of any * changes to the dataset. *

* In addition, all datasets must belong to one (and only one) * {@link DatasetGroup}. The group object maintains a reader-writer lock * which provides synchronised access to the datasets in multi-threaded code. */ public interface Dataset { /** * Registers an object for notification of changes to the dataset. * * @param listener the object to register. */ void addChangeListener(DatasetChangeListener listener); /** * Deregisters an object for notification of changes to the dataset. * * @param listener the object to deregister. */ void removeChangeListener(DatasetChangeListener listener); /** * Returns the dataset group. * * @return The dataset group. */ DatasetGroup getGroup(); /** * Sets the dataset group. * * @param group the dataset group. */ void setGroup(DatasetGroup group); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/general/DatasetChangeEvent.java000066400000000000000000000051431463604235500306460ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * DatasetChangeEvent.java * ----------------------- * (C) Copyright 2000-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.general; /** * A change event that encapsulates information about a change to a dataset. */ public class DatasetChangeEvent extends java.util.EventObject { /** * The dataset that generated the change event. */ private Dataset dataset; /** * Constructs a new event. The source is either the dataset or the * {@link org.jfree.chart.plot.Plot} class. The dataset can be * {@code null} (in this case the source will be the * {@link org.jfree.chart.plot.Plot} class). * * @param source the source of the event. * @param dataset the dataset that generated the event ({@code null} * permitted). */ public DatasetChangeEvent(Object source, Dataset dataset) { super(source); this.dataset = dataset; } /** * Returns the dataset that generated the event. Note that the dataset * may be {@code null} since adding a {@code null} dataset to a * plot will generated a change event. * * @return The dataset (possibly {@code null}). */ public Dataset getDataset() { return this.dataset; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/general/DatasetChangeListener.java000066400000000000000000000035741463604235500313600ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * DatasetChangeListener.java * -------------------------- * (C) Copyright 2000-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.general; import java.util.EventListener; /** * The interface that must be supported by classes that wish to receive * notification of changes to a dataset. */ public interface DatasetChangeListener extends EventListener { /** * Receives notification of an dataset change event. * * @param event information about the event. */ void datasetChanged(DatasetChangeEvent event); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/general/DatasetGroup.java000066400000000000000000000066511463604235500275600ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * DatasetGroup.java * ----------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.data.general; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.util.Args; /** * A class that is used to group datasets (currently not used for any specific * purpose). */ public class DatasetGroup implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -3640642179674185688L; /** The group id. */ private String id; /** * Constructs a new group. */ public DatasetGroup() { super(); this.id = "NOID"; } /** * Creates a new group with the specified id. * * @param id the identification for the group. */ public DatasetGroup(String id) { Args.nullNotPermitted(id, "id"); this.id = id; } /** * Returns the identification string for this group. * * @return The identification string. */ public String getID() { return this.id; } /** * Clones the group. * * @return A clone. * * @throws CloneNotSupportedException not by this class. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof DatasetGroup)) { return false; } DatasetGroup that = (DatasetGroup) obj; if (!Objects.equals(this.id, that.id)) { return false; } return true; } @Override public int hashCode() { int hash = 3; hash = 41 * hash + Objects.hashCode(this.id); return hash; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/general/DatasetUtils.java000066400000000000000000002523571463604235500275720ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * DatasetUtils.java * ----------------- * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Andrzej Porebski (bug fix); * Jonathan Nash (bug fix); * Richard Atkinson; * Andreas Schroeder; * Rafal Skalny (patch 1925366); * Jerome David (patch 2131001); * Peter Kolb (patch 2791407); * Martin Hoeller (patch 2952086); * */ package org.jfree.data.general; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.jfree.chart.util.ArrayUtils; import org.jfree.chart.util.Args; import org.jfree.data.DomainInfo; import org.jfree.data.DomainOrder; import org.jfree.data.KeyToGroupMap; import org.jfree.data.KeyedValues; import org.jfree.data.Range; import org.jfree.data.RangeInfo; import org.jfree.data.category.CategoryDataset; import org.jfree.data.category.CategoryRangeInfo; import org.jfree.data.category.DefaultCategoryDataset; import org.jfree.data.category.IntervalCategoryDataset; import org.jfree.data.function.Function2D; import org.jfree.data.statistics.BoxAndWhiskerCategoryDataset; import org.jfree.data.statistics.BoxAndWhiskerXYDataset; import org.jfree.data.statistics.MultiValueCategoryDataset; import org.jfree.data.statistics.StatisticalCategoryDataset; import org.jfree.data.xy.IntervalXYDataset; import org.jfree.data.xy.OHLCDataset; import org.jfree.data.xy.TableXYDataset; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYDomainInfo; import org.jfree.data.xy.XYRangeInfo; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.jfree.data.xy.XYZDataset; /** * A collection of useful static methods relating to datasets. */ public final class DatasetUtils { /** * Private constructor for non-instanceability. */ private DatasetUtils() { // now try to instantiate this ;-) } /** * Calculates the total of all the values in a {@link PieDataset}. If * the dataset contains negative or {@code null} values, they are * ignored. * * @param dataset the dataset ({@code null} not permitted). * * @return The total. */ public static double calculatePieDatasetTotal(PieDataset dataset) { Args.nullNotPermitted(dataset, "dataset"); List keys = dataset.getKeys(); double totalValue = 0; Iterator iterator = keys.iterator(); while (iterator.hasNext()) { Comparable current = (Comparable) iterator.next(); if (current != null) { Number value = dataset.getValue(current); double v = 0.0; if (value != null) { v = value.doubleValue(); } if (v > 0) { totalValue = totalValue + v; } } } return totalValue; } /** * Creates a pie dataset from a table dataset by taking all the values * for a single row. * * @param dataset the dataset ({@code null} not permitted). * @param rowKey the row key. * * @return A pie dataset. */ public static PieDataset createPieDatasetForRow(CategoryDataset dataset, Comparable rowKey) { int row = dataset.getRowIndex(rowKey); return createPieDatasetForRow(dataset, row); } /** * Creates a pie dataset from a table dataset by taking all the values * for a single row. * * @param dataset the dataset ({@code null} not permitted). * @param row the row (zero-based index). * * @return A pie dataset. */ public static PieDataset createPieDatasetForRow(CategoryDataset dataset, int row) { DefaultPieDataset result = new DefaultPieDataset(); int columnCount = dataset.getColumnCount(); for (int current = 0; current < columnCount; current++) { Comparable columnKey = dataset.getColumnKey(current); result.setValue(columnKey, dataset.getValue(row, current)); } return result; } /** * Creates a pie dataset from a table dataset by taking all the values * for a single column. * * @param dataset the dataset ({@code null} not permitted). * @param columnKey the column key. * * @return A pie dataset. */ public static PieDataset createPieDatasetForColumn(CategoryDataset dataset, Comparable columnKey) { int column = dataset.getColumnIndex(columnKey); return createPieDatasetForColumn(dataset, column); } /** * Creates a pie dataset from a {@link CategoryDataset} by taking all the * values for a single column. * * @param dataset the dataset ({@code null} not permitted). * @param column the column (zero-based index). * * @return A pie dataset. */ public static PieDataset createPieDatasetForColumn(CategoryDataset dataset, int column) { DefaultPieDataset result = new DefaultPieDataset(); int rowCount = dataset.getRowCount(); for (int i = 0; i < rowCount; i++) { Comparable rowKey = dataset.getRowKey(i); result.setValue(rowKey, dataset.getValue(i, column)); } return result; } /** * Creates a new pie dataset based on the supplied dataset, but modified * by aggregating all the low value items (those whose value is lower * than the {@code percentThreshold}) into a single item with the * key "Other". * * @param source the source dataset ({@code null} not permitted). * @param key a new key for the aggregated items ({@code null} not * permitted). * @param minimumPercent the percent threshold. * * @return The pie dataset with (possibly) aggregated items. */ public static PieDataset createConsolidatedPieDataset(PieDataset source, Comparable key, double minimumPercent) { return DatasetUtils.createConsolidatedPieDataset(source, key, minimumPercent, 2); } /** * Creates a new pie dataset based on the supplied dataset, but modified * by aggregating all the low value items (those whose value is lower * than the {@code percentThreshold}) into a single item. The * aggregated items are assigned the specified key. Aggregation only * occurs if there are at least {@code minItems} items to aggregate. * * @param source the source dataset ({@code null} not permitted). * @param key the key to represent the aggregated items. * @param minimumPercent the percent threshold (ten percent is 0.10). * @param minItems only aggregate low values if there are at least this * many. * * @return The pie dataset with (possibly) aggregated items. */ public static PieDataset createConsolidatedPieDataset(PieDataset source, Comparable key, double minimumPercent, int minItems) { DefaultPieDataset result = new DefaultPieDataset(); double total = DatasetUtils.calculatePieDatasetTotal(source); // Iterate and find all keys below threshold percentThreshold List keys = source.getKeys(); ArrayList otherKeys = new ArrayList(); Iterator iterator = keys.iterator(); while (iterator.hasNext()) { Comparable currentKey = (Comparable) iterator.next(); Number dataValue = source.getValue(currentKey); if (dataValue != null) { double value = dataValue.doubleValue(); if (value / total < minimumPercent) { otherKeys.add(currentKey); } } } // Create new dataset with keys above threshold percentThreshold iterator = keys.iterator(); double otherValue = 0; while (iterator.hasNext()) { Comparable currentKey = (Comparable) iterator.next(); Number dataValue = source.getValue(currentKey); if (dataValue != null) { if (otherKeys.contains(currentKey) && otherKeys.size() >= minItems) { // Do not add key to dataset otherValue += dataValue.doubleValue(); } else { // Add key to dataset result.setValue(currentKey, dataValue); } } } // Add other category if applicable if (otherKeys.size() >= minItems) { result.setValue(key, otherValue); } return result; } /** * Creates a {@link CategoryDataset} that contains a copy of the data in an * array (instances of {@code double} are created to represent the * data items). *

* Row and column keys are created by appending 0, 1, 2, ... to the * supplied prefixes. * * @param rowKeyPrefix the row key prefix. * @param columnKeyPrefix the column key prefix. * @param data the data. * * @return The dataset. */ public static CategoryDataset createCategoryDataset(String rowKeyPrefix, String columnKeyPrefix, double[][] data) { DefaultCategoryDataset result = new DefaultCategoryDataset(); for (int r = 0; r < data.length; r++) { String rowKey = rowKeyPrefix + (r + 1); for (int c = 0; c < data[r].length; c++) { String columnKey = columnKeyPrefix + (c + 1); result.addValue(data[r][c], rowKey, columnKey); } } return result; } /** * Creates a {@link CategoryDataset} that contains a copy of the data in * an array. *

* Row and column keys are created by appending 0, 1, 2, ... to the * supplied prefixes. * * @param rowKeyPrefix the row key prefix. * @param columnKeyPrefix the column key prefix. * @param data the data. * * @return The dataset. */ public static CategoryDataset createCategoryDataset(String rowKeyPrefix, String columnKeyPrefix, Number[][] data) { DefaultCategoryDataset result = new DefaultCategoryDataset(); for (int r = 0; r < data.length; r++) { String rowKey = rowKeyPrefix + (r + 1); for (int c = 0; c < data[r].length; c++) { String columnKey = columnKeyPrefix + (c + 1); result.addValue(data[r][c], rowKey, columnKey); } } return result; } /** * Creates a {@link CategoryDataset} that contains a copy of the data in * an array (instances of {@code double} are created to represent the * data items). *

* Row and column keys are taken from the supplied arrays. * * @param rowKeys the row keys ({@code null} not permitted). * @param columnKeys the column keys ({@code null} not permitted). * @param data the data. * * @return The dataset. */ public static CategoryDataset createCategoryDataset(Comparable[] rowKeys, Comparable[] columnKeys, double[][] data) { Args.nullNotPermitted(rowKeys, "rowKeys"); Args.nullNotPermitted(columnKeys, "columnKeys"); if (ArrayUtils.hasDuplicateItems(rowKeys)) { throw new IllegalArgumentException("Duplicate items in 'rowKeys'."); } if (ArrayUtils.hasDuplicateItems(columnKeys)) { throw new IllegalArgumentException( "Duplicate items in 'columnKeys'."); } if (rowKeys.length != data.length) { throw new IllegalArgumentException( "The number of row keys does not match the number of rows in " + "the data array."); } int columnCount = 0; for (int r = 0; r < data.length; r++) { columnCount = Math.max(columnCount, data[r].length); } if (columnKeys.length != columnCount) { throw new IllegalArgumentException( "The number of column keys does not match the number of " + "columns in the data array."); } // now do the work... DefaultCategoryDataset result = new DefaultCategoryDataset(); for (int r = 0; r < data.length; r++) { Comparable rowKey = rowKeys[r]; for (int c = 0; c < data[r].length; c++) { Comparable columnKey = columnKeys[c]; result.addValue(data[r][c], rowKey, columnKey); } } return result; } /** * Creates a {@link CategoryDataset} by copying the data from the supplied * {@link KeyedValues} instance. * * @param rowKey the row key ({@code null} not permitted). * @param rowData the row data ({@code null} not permitted). * * @return A dataset. */ public static CategoryDataset createCategoryDataset(Comparable rowKey, KeyedValues rowData) { Args.nullNotPermitted(rowKey, "rowKey"); Args.nullNotPermitted(rowData, "rowData"); DefaultCategoryDataset result = new DefaultCategoryDataset(); for (int i = 0; i < rowData.getItemCount(); i++) { result.addValue(rowData.getValue(i), rowKey, rowData.getKey(i)); } return result; } /** * Creates an {@link XYDataset} by sampling the specified function over a * fixed range. * * @param f the function ({@code null} not permitted). * @param start the start value for the range. * @param end the end value for the range. * @param samples the number of sample points (must be > 1). * @param seriesKey the key to give the resulting series ({@code null} not * permitted). * * @return A dataset. */ public static XYDataset sampleFunction2D(Function2D f, double start, double end, int samples, Comparable seriesKey) { // defer argument checking XYSeries series = sampleFunction2DToSeries(f, start, end, samples, seriesKey); XYSeriesCollection collection = new XYSeriesCollection(series); return collection; } /** * Creates an {@link XYSeries} by sampling the specified function over a * fixed range. * * @param f the function ({@code null} not permitted). * @param start the start value for the range. * @param end the end value for the range. * @param samples the number of sample points (must be > 1). * @param seriesKey the key to give the resulting series * ({@code null} not permitted). * * @return A series. */ public static XYSeries sampleFunction2DToSeries(Function2D f, double start, double end, int samples, Comparable seriesKey) { Args.nullNotPermitted(f, "f"); Args.nullNotPermitted(seriesKey, "seriesKey"); if (start >= end) { throw new IllegalArgumentException("Requires 'start' < 'end'."); } if (samples < 2) { throw new IllegalArgumentException("Requires 'samples' > 1"); } XYSeries series = new XYSeries(seriesKey); double step = (end - start) / (samples - 1); for (int i = 0; i < samples; i++) { double x = start + (step * i); series.add(x, f.getValue(x)); } return series; } /** * Returns {@code true} if the dataset is empty (or {@code null}), * and {@code false} otherwise. * * @param dataset the dataset ({@code null} permitted). * * @return A boolean. */ public static boolean isEmptyOrNull(PieDataset dataset) { if (dataset == null) { return true; } int itemCount = dataset.getItemCount(); if (itemCount == 0) { return true; } for (int item = 0; item < itemCount; item++) { Number y = dataset.getValue(item); if (y != null) { double yy = y.doubleValue(); if (yy > 0.0) { return false; } } } return true; } /** * Returns {@code true} if the dataset is empty (or {@code null}), * and {@code false} otherwise. * * @param dataset the dataset ({@code null} permitted). * * @return A boolean. */ public static boolean isEmptyOrNull(CategoryDataset dataset) { if (dataset == null) { return true; } int rowCount = dataset.getRowCount(); int columnCount = dataset.getColumnCount(); if (rowCount == 0 || columnCount == 0) { return true; } for (int r = 0; r < rowCount; r++) { for (int c = 0; c < columnCount; c++) { if (dataset.getValue(r, c) != null) { return false; } } } return true; } /** * Returns {@code true} if the dataset is empty (or {@code null}), * and {@code false} otherwise. * * @param dataset the dataset ({@code null} permitted). * * @return A boolean. */ public static boolean isEmptyOrNull(XYDataset dataset) { if (dataset != null) { for (int s = 0; s < dataset.getSeriesCount(); s++) { if (dataset.getItemCount(s) > 0) { return false; } } } return true; } /** * Returns the range of values in the domain (x-values) of a dataset. * * @param dataset the dataset ({@code null} not permitted). * * @return The range of values (possibly {@code null}). */ public static Range findDomainBounds(XYDataset dataset) { return findDomainBounds(dataset, true); } /** * Returns the range of values in the domain (x-values) of a dataset. * * @param dataset the dataset ({@code null} not permitted). * @param includeInterval determines whether or not the x-interval is taken * into account (only applies if the dataset is an * {@link IntervalXYDataset}). * * @return The range of values (possibly {@code null}). */ public static Range findDomainBounds(XYDataset dataset, boolean includeInterval) { Args.nullNotPermitted(dataset, "dataset"); Range result; // if the dataset implements DomainInfo, life is easier if (dataset instanceof DomainInfo) { DomainInfo info = (DomainInfo) dataset; result = info.getDomainBounds(includeInterval); } else { result = iterateDomainBounds(dataset, includeInterval); } return result; } /** * Returns the bounds of the x-values in the specified {@code dataset} * taking into account only the visible series and including any x-interval * if requested. * * @param dataset the dataset ({@code null} not permitted). * @param visibleSeriesKeys the visible series keys ({@code null} * not permitted). * @param includeInterval include the x-interval (if any)? * * @return The bounds (or {@code null} if the dataset contains no values. */ public static Range findDomainBounds(XYDataset dataset, List visibleSeriesKeys, boolean includeInterval) { Args.nullNotPermitted(dataset, "dataset"); Range result; if (dataset instanceof XYDomainInfo) { XYDomainInfo info = (XYDomainInfo) dataset; result = info.getDomainBounds(visibleSeriesKeys, includeInterval); } else { result = iterateToFindDomainBounds(dataset, visibleSeriesKeys, includeInterval); } return result; } /** * Iterates over the items in an {@link XYDataset} to find * the range of x-values. If the dataset is an instance of * {@link IntervalXYDataset}, the starting and ending x-values * will be used for the bounds calculation. * * @param dataset the dataset ({@code null} not permitted). * * @return The range (possibly {@code null}). */ public static Range iterateDomainBounds(XYDataset dataset) { return iterateDomainBounds(dataset, true); } /** * Iterates over the items in an {@link XYDataset} to find * the range of x-values. * * @param dataset the dataset ({@code null} not permitted). * @param includeInterval a flag that determines, for an * {@link IntervalXYDataset}, whether the x-interval or just the * x-value is used to determine the overall range. * * @return The range (possibly {@code null}). */ public static Range iterateDomainBounds(XYDataset dataset, boolean includeInterval) { Args.nullNotPermitted(dataset, "dataset"); double minimum = Double.POSITIVE_INFINITY; double maximum = Double.NEGATIVE_INFINITY; int seriesCount = dataset.getSeriesCount(); double lvalue, uvalue; if (includeInterval && dataset instanceof IntervalXYDataset) { IntervalXYDataset intervalXYData = (IntervalXYDataset) dataset; for (int series = 0; series < seriesCount; series++) { int itemCount = dataset.getItemCount(series); for (int item = 0; item < itemCount; item++) { double value = intervalXYData.getXValue(series, item); lvalue = intervalXYData.getStartXValue(series, item); uvalue = intervalXYData.getEndXValue(series, item); if (!Double.isNaN(value)) { minimum = Math.min(minimum, value); maximum = Math.max(maximum, value); } if (!Double.isNaN(lvalue)) { minimum = Math.min(minimum, lvalue); maximum = Math.max(maximum, lvalue); } if (!Double.isNaN(uvalue)) { minimum = Math.min(minimum, uvalue); maximum = Math.max(maximum, uvalue); } } } } else { for (int series = 0; series < seriesCount; series++) { int itemCount = dataset.getItemCount(series); for (int item = 0; item < itemCount; item++) { lvalue = dataset.getXValue(series, item); uvalue = lvalue; if (!Double.isNaN(lvalue)) { minimum = Math.min(minimum, lvalue); maximum = Math.max(maximum, uvalue); } } } } if (minimum > maximum) { return null; } else { return new Range(minimum, maximum); } } /** * Returns the range of values in the range for the dataset. * * @param dataset the dataset ({@code null} not permitted). * * @return The range (possibly {@code null}). */ public static Range findRangeBounds(CategoryDataset dataset) { return findRangeBounds(dataset, true); } /** * Returns the range of values in the range for the dataset. * * @param dataset the dataset ({@code null} not permitted). * @param includeInterval a flag that determines whether or not the * y-interval is taken into account. * * @return The range (possibly {@code null}). */ public static Range findRangeBounds(CategoryDataset dataset, boolean includeInterval) { Args.nullNotPermitted(dataset, "dataset"); Range result; if (dataset instanceof RangeInfo) { RangeInfo info = (RangeInfo) dataset; result = info.getRangeBounds(includeInterval); } else { result = iterateRangeBounds(dataset, includeInterval); } return result; } /** * Finds the bounds of the y-values in the specified dataset, including * only those series that are listed in visibleSeriesKeys. * * @param dataset the dataset ({@code null} not permitted). * @param visibleSeriesKeys the keys for the visible series * ({@code null} not permitted). * @param includeInterval include the y-interval (if the dataset has a * y-interval). * * @return The data bounds. */ public static Range findRangeBounds(CategoryDataset dataset, List visibleSeriesKeys, boolean includeInterval) { Args.nullNotPermitted(dataset, "dataset"); Range result; if (dataset instanceof CategoryRangeInfo) { CategoryRangeInfo info = (CategoryRangeInfo) dataset; result = info.getRangeBounds(visibleSeriesKeys, includeInterval); } else { result = iterateToFindRangeBounds(dataset, visibleSeriesKeys, includeInterval); } return result; } /** * Returns the range of values in the range for the dataset. This method * is the partner for the {@link #findDomainBounds(XYDataset)} method. * * @param dataset the dataset ({@code null} not permitted). * * @return The range (possibly {@code null}). */ public static Range findRangeBounds(XYDataset dataset) { return findRangeBounds(dataset, true); } /** * Returns the range of values in the range for the dataset. This method * is the partner for the {@link #findDomainBounds(XYDataset, boolean)} * method. * * @param dataset the dataset ({@code null} not permitted). * @param includeInterval a flag that determines whether or not the * y-interval is taken into account. * * @return The range (possibly {@code null}). */ public static Range findRangeBounds(XYDataset dataset, boolean includeInterval) { Args.nullNotPermitted(dataset, "dataset"); Range result; if (dataset instanceof RangeInfo) { RangeInfo info = (RangeInfo) dataset; result = info.getRangeBounds(includeInterval); } else { result = iterateRangeBounds(dataset, includeInterval); } return result; } /** * Finds the bounds of the y-values in the specified dataset, including * only those series that are listed in visibleSeriesKeys, and those items * whose x-values fall within the specified range. * * @param dataset the dataset ({@code null} not permitted). * @param visibleSeriesKeys the keys for the visible series * ({@code null} not permitted). * @param xRange the x-range ({@code null} not permitted). * @param includeInterval include the y-interval (if the dataset has a * y-interval). * * @return The data bounds. */ public static Range findRangeBounds(XYDataset dataset, List visibleSeriesKeys, Range xRange, boolean includeInterval) { Args.nullNotPermitted(dataset, "dataset"); Range result; if (dataset instanceof XYRangeInfo) { XYRangeInfo info = (XYRangeInfo) dataset; result = info.getRangeBounds(visibleSeriesKeys, xRange, includeInterval); } else { result = iterateToFindRangeBounds(dataset, visibleSeriesKeys, xRange, includeInterval); } return result; } /** * Iterates over the data item of the category dataset to find * the range bounds. * * @param dataset the dataset ({@code null} not permitted). * * @return The range (possibly {@code null}). */ public static Range iterateRangeBounds(CategoryDataset dataset) { return iterateRangeBounds(dataset, true); } /** * Iterates over the data item of the category dataset to find * the range bounds. * * @param dataset the dataset ({@code null} not permitted). * @param includeInterval a flag that determines whether or not the * y-interval is taken into account. * * @return The range (possibly {@code null}). */ public static Range iterateRangeBounds(CategoryDataset dataset, boolean includeInterval) { double minimum = Double.POSITIVE_INFINITY; double maximum = Double.NEGATIVE_INFINITY; int rowCount = dataset.getRowCount(); int columnCount = dataset.getColumnCount(); if (includeInterval && dataset instanceof IntervalCategoryDataset) { // handle the special case where the dataset has y-intervals that // we want to measure IntervalCategoryDataset icd = (IntervalCategoryDataset) dataset; Number value, lvalue, uvalue; for (int row = 0; row < rowCount; row++) { for (int column = 0; column < columnCount; column++) { value = icd.getValue(row, column); double v; if ((value != null) && !Double.isNaN(v = value.doubleValue())) { minimum = Math.min(v, minimum); maximum = Math.max(v, maximum); } lvalue = icd.getStartValue(row, column); if (lvalue != null && !Double.isNaN(v = lvalue.doubleValue())) { minimum = Math.min(v, minimum); maximum = Math.max(v, maximum); } uvalue = icd.getEndValue(row, column); if (uvalue != null && !Double.isNaN(v = uvalue.doubleValue())) { minimum = Math.min(v, minimum); maximum = Math.max(v, maximum); } } } } else { // handle the standard case (plain CategoryDataset) for (int row = 0; row < rowCount; row++) { for (int column = 0; column < columnCount; column++) { Number value = dataset.getValue(row, column); if (value != null) { double v = value.doubleValue(); if (!Double.isNaN(v)) { minimum = Math.min(minimum, v); maximum = Math.max(maximum, v); } } } } } if (minimum == Double.POSITIVE_INFINITY) { return null; } else { return new Range(minimum, maximum); } } /** * Iterates over the data item of the category dataset to find * the range bounds. * * @param dataset the dataset ({@code null} not permitted). * @param includeInterval a flag that determines whether or not the * y-interval is taken into account. * @param visibleSeriesKeys the visible series keys. * * @return The range (possibly {@code null}). */ public static Range iterateToFindRangeBounds(CategoryDataset dataset, List visibleSeriesKeys, boolean includeInterval) { Args.nullNotPermitted(dataset, "dataset"); Args.nullNotPermitted(visibleSeriesKeys, "visibleSeriesKeys"); double minimum = Double.POSITIVE_INFINITY; double maximum = Double.NEGATIVE_INFINITY; int columnCount = dataset.getColumnCount(); if (includeInterval && dataset instanceof BoxAndWhiskerCategoryDataset) { // handle special case of BoxAndWhiskerDataset BoxAndWhiskerCategoryDataset bx = (BoxAndWhiskerCategoryDataset) dataset; Iterator iterator = visibleSeriesKeys.iterator(); while (iterator.hasNext()) { Comparable seriesKey = (Comparable) iterator.next(); int series = dataset.getRowIndex(seriesKey); int itemCount = dataset.getColumnCount(); for (int item = 0; item < itemCount; item++) { Number lvalue = bx.getMinRegularValue(series, item); if (lvalue == null) { lvalue = bx.getValue(series, item); } Number uvalue = bx.getMaxRegularValue(series, item); if (uvalue == null) { uvalue = bx.getValue(series, item); } if (lvalue != null) { minimum = Math.min(minimum, lvalue.doubleValue()); } if (uvalue != null) { maximum = Math.max(maximum, uvalue.doubleValue()); } } } } else if (includeInterval && dataset instanceof IntervalCategoryDataset) { // handle the special case where the dataset has y-intervals that // we want to measure IntervalCategoryDataset icd = (IntervalCategoryDataset) dataset; Number lvalue, uvalue; Iterator iterator = visibleSeriesKeys.iterator(); while (iterator.hasNext()) { Comparable seriesKey = (Comparable) iterator.next(); int series = dataset.getRowIndex(seriesKey); for (int column = 0; column < columnCount; column++) { lvalue = icd.getStartValue(series, column); uvalue = icd.getEndValue(series, column); if (lvalue != null && !Double.isNaN(lvalue.doubleValue())) { minimum = Math.min(minimum, lvalue.doubleValue()); } if (uvalue != null && !Double.isNaN(uvalue.doubleValue())) { maximum = Math.max(maximum, uvalue.doubleValue()); } } } } else if (includeInterval && dataset instanceof MultiValueCategoryDataset) { // handle the special case where the dataset has y-intervals that // we want to measure MultiValueCategoryDataset mvcd = (MultiValueCategoryDataset) dataset; Iterator iterator = visibleSeriesKeys.iterator(); while (iterator.hasNext()) { Comparable seriesKey = (Comparable) iterator.next(); int series = dataset.getRowIndex(seriesKey); for (int column = 0; column < columnCount; column++) { List values = mvcd.getValues(series, column); Iterator valueIterator = values.iterator(); while (valueIterator.hasNext()) { Object o = valueIterator.next(); if (o instanceof Number){ double v = ((Number) o).doubleValue(); if (!Double.isNaN(v)){ minimum = Math.min(minimum, v); maximum = Math.max(maximum, v); } } } } } } else if (includeInterval && dataset instanceof StatisticalCategoryDataset) { // handle the special case where the dataset has y-intervals that // we want to measure StatisticalCategoryDataset scd = (StatisticalCategoryDataset) dataset; Iterator iterator = visibleSeriesKeys.iterator(); while (iterator.hasNext()) { Comparable seriesKey = (Comparable) iterator.next(); int series = dataset.getRowIndex(seriesKey); for (int column = 0; column < columnCount; column++) { Number meanN = scd.getMeanValue(series, column); if (meanN != null) { double std = 0.0; Number stdN = scd.getStdDevValue(series, column); if (stdN != null) { std = stdN.doubleValue(); if (Double.isNaN(std)) { std = 0.0; } } double mean = meanN.doubleValue(); if (!Double.isNaN(mean)) { minimum = Math.min(minimum, mean - std); maximum = Math.max(maximum, mean + std); } } } } } else { // handle the standard case (plain CategoryDataset) Iterator iterator = visibleSeriesKeys.iterator(); while (iterator.hasNext()) { Comparable seriesKey = (Comparable) iterator.next(); int series = dataset.getRowIndex(seriesKey); for (int column = 0; column < columnCount; column++) { Number value = dataset.getValue(series, column); if (value != null) { double v = value.doubleValue(); if (!Double.isNaN(v)) { minimum = Math.min(minimum, v); maximum = Math.max(maximum, v); } } } } } if (minimum == Double.POSITIVE_INFINITY) { return null; } else { return new Range(minimum, maximum); } } /** * Iterates over the data item of the xy dataset to find * the range bounds. * * @param dataset the dataset ({@code null} not permitted). * * @return The range (possibly {@code null}). */ public static Range iterateRangeBounds(XYDataset dataset) { return iterateRangeBounds(dataset, true); } /** * Iterates over the data items of the xy dataset to find * the range bounds. * * @param dataset the dataset ({@code null} not permitted). * @param includeInterval a flag that determines, for an * {@link IntervalXYDataset}, whether the y-interval or just the * y-value is used to determine the overall range. * * @return The range (possibly {@code null}). */ public static Range iterateRangeBounds(XYDataset dataset, boolean includeInterval) { double minimum = Double.POSITIVE_INFINITY; double maximum = Double.NEGATIVE_INFINITY; int seriesCount = dataset.getSeriesCount(); // handle three cases by dataset type if (includeInterval && dataset instanceof IntervalXYDataset) { // handle special case of IntervalXYDataset IntervalXYDataset ixyd = (IntervalXYDataset) dataset; for (int series = 0; series < seriesCount; series++) { int itemCount = dataset.getItemCount(series); for (int item = 0; item < itemCount; item++) { double value = ixyd.getYValue(series, item); double lvalue = ixyd.getStartYValue(series, item); double uvalue = ixyd.getEndYValue(series, item); if (!Double.isNaN(value)) { minimum = Math.min(minimum, value); maximum = Math.max(maximum, value); } if (!Double.isNaN(lvalue)) { minimum = Math.min(minimum, lvalue); maximum = Math.max(maximum, lvalue); } if (!Double.isNaN(uvalue)) { minimum = Math.min(minimum, uvalue); maximum = Math.max(maximum, uvalue); } } } } else if (includeInterval && dataset instanceof OHLCDataset) { // handle special case of OHLCDataset OHLCDataset ohlc = (OHLCDataset) dataset; for (int series = 0; series < seriesCount; series++) { int itemCount = dataset.getItemCount(series); for (int item = 0; item < itemCount; item++) { double lvalue = ohlc.getLowValue(series, item); double uvalue = ohlc.getHighValue(series, item); if (!Double.isNaN(lvalue)) { minimum = Math.min(minimum, lvalue); } if (!Double.isNaN(uvalue)) { maximum = Math.max(maximum, uvalue); } } } } else { // standard case - plain XYDataset for (int series = 0; series < seriesCount; series++) { int itemCount = dataset.getItemCount(series); for (int item = 0; item < itemCount; item++) { double value = dataset.getYValue(series, item); if (!Double.isNaN(value)) { minimum = Math.min(minimum, value); maximum = Math.max(maximum, value); } } } } if (minimum == Double.POSITIVE_INFINITY) { return null; } else { return new Range(minimum, maximum); } } /** * Returns the range of values in the z-dimension for the dataset. This * method is the partner for the {@link #findRangeBounds(XYDataset)} * and {@link #findDomainBounds(XYDataset)} methods. * * @param dataset the dataset ({@code null} not permitted). * * @return The range (possibly {@code null}). */ public static Range findZBounds(XYZDataset dataset) { return findZBounds(dataset, true); } /** * Returns the range of values in the z-dimension for the dataset. This * method is the partner for the * {@link #findRangeBounds(XYDataset, boolean)} and * {@link #findDomainBounds(XYDataset, boolean)} methods. * * @param dataset the dataset ({@code null} not permitted). * @param includeInterval a flag that determines whether or not the * z-interval is taken into account. * * @return The range (possibly {@code null}). */ public static Range findZBounds(XYZDataset dataset, boolean includeInterval) { Args.nullNotPermitted(dataset, "dataset"); Range result = iterateZBounds(dataset, includeInterval); return result; } /** * Finds the bounds of the z-values in the specified dataset, including * only those series that are listed in visibleSeriesKeys, and those items * whose x-values fall within the specified range. * * @param dataset the dataset ({@code null} not permitted). * @param visibleSeriesKeys the keys for the visible series * ({@code null} not permitted). * @param xRange the x-range ({@code null} not permitted). * @param includeInterval include the z-interval (if the dataset has a * z-interval). * * @return The data bounds. */ public static Range findZBounds(XYZDataset dataset, List visibleSeriesKeys, Range xRange, boolean includeInterval) { Args.nullNotPermitted(dataset, "dataset"); Range result = iterateToFindZBounds(dataset, visibleSeriesKeys, xRange, includeInterval); return result; } /** * Iterates over the data item of the xyz dataset to find * the z-dimension bounds. * * @param dataset the dataset ({@code null} not permitted). * * @return The range (possibly {@code null}). */ public static Range iterateZBounds(XYZDataset dataset) { return iterateZBounds(dataset, true); } /** * Iterates over the data items of the xyz dataset to find * the z-dimension bounds. * * @param dataset the dataset ({@code null} not permitted). * @param includeInterval include the z-interval (if the dataset has a * z-interval. * * @return The range (possibly {@code null}). */ public static Range iterateZBounds(XYZDataset dataset, boolean includeInterval) { double minimum = Double.POSITIVE_INFINITY; double maximum = Double.NEGATIVE_INFINITY; int seriesCount = dataset.getSeriesCount(); for (int series = 0; series < seriesCount; series++) { int itemCount = dataset.getItemCount(series); for (int item = 0; item < itemCount; item++) { double value = dataset.getZValue(series, item); if (!Double.isNaN(value)) { minimum = Math.min(minimum, value); maximum = Math.max(maximum, value); } } } if (minimum == Double.POSITIVE_INFINITY) { return null; } else { return new Range(minimum, maximum); } } /** * Returns the range of x-values in the specified dataset for the * data items belonging to the visible series. * * @param dataset the dataset ({@code null} not permitted). * @param visibleSeriesKeys the visible series keys ({@code null} not * permitted). * @param includeInterval a flag that determines whether or not the * y-interval for the dataset is included (this only applies if the * dataset is an instance of IntervalXYDataset). * * @return The x-range (possibly {@code null}). */ public static Range iterateToFindDomainBounds(XYDataset dataset, List visibleSeriesKeys, boolean includeInterval) { Args.nullNotPermitted(dataset, "dataset"); Args.nullNotPermitted(visibleSeriesKeys, "visibleSeriesKeys"); double minimum = Double.POSITIVE_INFINITY; double maximum = Double.NEGATIVE_INFINITY; if (includeInterval && dataset instanceof IntervalXYDataset) { // handle special case of IntervalXYDataset IntervalXYDataset ixyd = (IntervalXYDataset) dataset; Iterator iterator = visibleSeriesKeys.iterator(); while (iterator.hasNext()) { Comparable seriesKey = (Comparable) iterator.next(); int series = dataset.indexOf(seriesKey); int itemCount = dataset.getItemCount(series); for (int item = 0; item < itemCount; item++) { double xvalue = ixyd.getXValue(series, item); double lvalue = ixyd.getStartXValue(series, item); double uvalue = ixyd.getEndXValue(series, item); if (!Double.isNaN(xvalue)) { minimum = Math.min(minimum, xvalue); maximum = Math.max(maximum, xvalue); } if (!Double.isNaN(lvalue)) { minimum = Math.min(minimum, lvalue); } if (!Double.isNaN(uvalue)) { maximum = Math.max(maximum, uvalue); } } } } else { // standard case - plain XYDataset Iterator iterator = visibleSeriesKeys.iterator(); while (iterator.hasNext()) { Comparable seriesKey = (Comparable) iterator.next(); int series = dataset.indexOf(seriesKey); int itemCount = dataset.getItemCount(series); for (int item = 0; item < itemCount; item++) { double x = dataset.getXValue(series, item); if (!Double.isNaN(x)) { minimum = Math.min(minimum, x); maximum = Math.max(maximum, x); } } } } if (minimum == Double.POSITIVE_INFINITY) { return null; } else { return new Range(minimum, maximum); } } /** * Returns the range of y-values in the specified dataset for the * data items belonging to the visible series and with x-values in the * given range. * * @param dataset the dataset ({@code null} not permitted). * @param visibleSeriesKeys the visible series keys ({@code null} not * permitted). * @param xRange the x-range ({@code null} not permitted). * @param includeInterval a flag that determines whether or not the * y-interval for the dataset is included (this only applies if the * dataset is an instance of IntervalXYDataset). * * @return The y-range (possibly {@code null}). */ public static Range iterateToFindRangeBounds(XYDataset dataset, List visibleSeriesKeys, Range xRange, boolean includeInterval) { Args.nullNotPermitted(dataset, "dataset"); Args.nullNotPermitted(visibleSeriesKeys, "visibleSeriesKeys"); Args.nullNotPermitted(xRange, "xRange"); double minimum = Double.POSITIVE_INFINITY; double maximum = Double.NEGATIVE_INFINITY; // handle three cases by dataset type if (includeInterval && dataset instanceof OHLCDataset) { // handle special case of OHLCDataset OHLCDataset ohlc = (OHLCDataset) dataset; Iterator iterator = visibleSeriesKeys.iterator(); while (iterator.hasNext()) { Comparable seriesKey = (Comparable) iterator.next(); int series = dataset.indexOf(seriesKey); int itemCount = dataset.getItemCount(series); for (int item = 0; item < itemCount; item++) { double x = ohlc.getXValue(series, item); if (xRange.contains(x)) { double lvalue = ohlc.getLowValue(series, item); double uvalue = ohlc.getHighValue(series, item); if (!Double.isNaN(lvalue)) { minimum = Math.min(minimum, lvalue); } if (!Double.isNaN(uvalue)) { maximum = Math.max(maximum, uvalue); } } } } } else if (includeInterval && dataset instanceof BoxAndWhiskerXYDataset) { // handle special case of BoxAndWhiskerXYDataset BoxAndWhiskerXYDataset bx = (BoxAndWhiskerXYDataset) dataset; Iterator iterator = visibleSeriesKeys.iterator(); while (iterator.hasNext()) { Comparable seriesKey = (Comparable) iterator.next(); int series = dataset.indexOf(seriesKey); int itemCount = dataset.getItemCount(series); for (int item = 0; item < itemCount; item++) { double x = bx.getXValue(series, item); if (xRange.contains(x)) { Number lvalue = bx.getMinRegularValue(series, item); Number uvalue = bx.getMaxRegularValue(series, item); if (lvalue != null) { minimum = Math.min(minimum, lvalue.doubleValue()); } if (uvalue != null) { maximum = Math.max(maximum, uvalue.doubleValue()); } } } } } else if (includeInterval && dataset instanceof IntervalXYDataset) { // handle special case of IntervalXYDataset IntervalXYDataset ixyd = (IntervalXYDataset) dataset; Iterator iterator = visibleSeriesKeys.iterator(); while (iterator.hasNext()) { Comparable seriesKey = (Comparable) iterator.next(); int series = dataset.indexOf(seriesKey); int itemCount = dataset.getItemCount(series); for (int item = 0; item < itemCount; item++) { double x = ixyd.getXValue(series, item); if (xRange.contains(x)) { double yvalue = ixyd.getYValue(series, item); double lvalue = ixyd.getStartYValue(series, item); double uvalue = ixyd.getEndYValue(series, item); if (!Double.isNaN(yvalue)) { minimum = Math.min(minimum, yvalue); maximum = Math.max(maximum, yvalue); } if (!Double.isNaN(lvalue)) { minimum = Math.min(minimum, lvalue); } if (!Double.isNaN(uvalue)) { maximum = Math.max(maximum, uvalue); } } } } } else { // standard case - plain XYDataset Iterator iterator = visibleSeriesKeys.iterator(); while (iterator.hasNext()) { Comparable seriesKey = (Comparable) iterator.next(); int series = dataset.indexOf(seriesKey); int itemCount = dataset.getItemCount(series); for (int item = 0; item < itemCount; item++) { double x = dataset.getXValue(series, item); double y = dataset.getYValue(series, item); if (xRange.contains(x)) { if (!Double.isNaN(y)) { minimum = Math.min(minimum, y); maximum = Math.max(maximum, y); } } } } } if (minimum == Double.POSITIVE_INFINITY) { return null; } else { return new Range(minimum, maximum); } } /** * Returns the range of z-values in the specified dataset for the * data items belonging to the visible series and with x-values in the * given range. * * @param dataset the dataset ({@code null} not permitted). * @param visibleSeriesKeys the visible series keys ({@code null} not * permitted). * @param xRange the x-range ({@code null} not permitted). * @param includeInterval a flag that determines whether or not the * z-interval for the dataset is included (this only applies if the * dataset has an interval, which is currently not supported). * * @return The y-range (possibly {@code null}). */ public static Range iterateToFindZBounds(XYZDataset dataset, List visibleSeriesKeys, Range xRange, boolean includeInterval) { Args.nullNotPermitted(dataset, "dataset"); Args.nullNotPermitted(visibleSeriesKeys, "visibleSeriesKeys"); Args.nullNotPermitted(xRange, "xRange"); double minimum = Double.POSITIVE_INFINITY; double maximum = Double.NEGATIVE_INFINITY; Iterator iterator = visibleSeriesKeys.iterator(); while (iterator.hasNext()) { Comparable seriesKey = (Comparable) iterator.next(); int series = dataset.indexOf(seriesKey); int itemCount = dataset.getItemCount(series); for (int item = 0; item < itemCount; item++) { double x = dataset.getXValue(series, item); double z = dataset.getZValue(series, item); if (xRange.contains(x)) { if (!Double.isNaN(z)) { minimum = Math.min(minimum, z); maximum = Math.max(maximum, z); } } } } if (minimum == Double.POSITIVE_INFINITY) { return null; } else { return new Range(minimum, maximum); } } /** * Finds the minimum domain (or X) value for the specified dataset. This * is easy if the dataset implements the {@link DomainInfo} interface (a * good idea if there is an efficient way to determine the minimum value). * Otherwise, it involves iterating over the entire data-set. *

* Returns {@code null} if all the data values in the dataset are * {@code null}. * * @param dataset the dataset ({@code null} not permitted). * * @return The minimum value (possibly {@code null}). */ public static Number findMinimumDomainValue(XYDataset dataset) { Args.nullNotPermitted(dataset, "dataset"); Number result; // if the dataset implements DomainInfo, life is easy if (dataset instanceof DomainInfo) { DomainInfo info = (DomainInfo) dataset; return info.getDomainLowerBound(true); } else { double minimum = Double.POSITIVE_INFINITY; int seriesCount = dataset.getSeriesCount(); for (int series = 0; series < seriesCount; series++) { int itemCount = dataset.getItemCount(series); for (int item = 0; item < itemCount; item++) { double value; if (dataset instanceof IntervalXYDataset) { IntervalXYDataset intervalXYData = (IntervalXYDataset) dataset; value = intervalXYData.getStartXValue(series, item); } else { value = dataset.getXValue(series, item); } if (!Double.isNaN(value)) { minimum = Math.min(minimum, value); } } } if (minimum == Double.POSITIVE_INFINITY) { result = null; } else { result = minimum; } } return result; } /** * Returns the maximum domain value for the specified dataset. This is * easy if the dataset implements the {@link DomainInfo} interface (a good * idea if there is an efficient way to determine the maximum value). * Otherwise, it involves iterating over the entire data-set. Returns * {@code null} if all the data values in the dataset are * {@code null}. * * @param dataset the dataset ({@code null} not permitted). * * @return The maximum value (possibly {@code null}). */ public static Number findMaximumDomainValue(XYDataset dataset) { Args.nullNotPermitted(dataset, "dataset"); Number result; // if the dataset implements DomainInfo, life is easy if (dataset instanceof DomainInfo) { DomainInfo info = (DomainInfo) dataset; return info.getDomainUpperBound(true); } // hasn't implemented DomainInfo, so iterate... else { double maximum = Double.NEGATIVE_INFINITY; int seriesCount = dataset.getSeriesCount(); for (int series = 0; series < seriesCount; series++) { int itemCount = dataset.getItemCount(series); for (int item = 0; item < itemCount; item++) { double value; if (dataset instanceof IntervalXYDataset) { IntervalXYDataset intervalXYData = (IntervalXYDataset) dataset; value = intervalXYData.getEndXValue(series, item); } else { value = dataset.getXValue(series, item); } if (!Double.isNaN(value)) { maximum = Math.max(maximum, value); } } } if (maximum == Double.NEGATIVE_INFINITY) { result = null; } else { result = maximum; } } return result; } /** * Returns the minimum range value for the specified dataset. This is * easy if the dataset implements the {@link RangeInfo} interface (a good * idea if there is an efficient way to determine the minimum value). * Otherwise, it involves iterating over the entire data-set. Returns * {@code null} if all the data values in the dataset are * {@code null}. * * @param dataset the dataset ({@code null} not permitted). * * @return The minimum value (possibly {@code null}). */ public static Number findMinimumRangeValue(CategoryDataset dataset) { Args.nullNotPermitted(dataset, "dataset"); if (dataset instanceof RangeInfo) { RangeInfo info = (RangeInfo) dataset; return info.getRangeLowerBound(true); } // hasn't implemented RangeInfo, so we'll have to iterate... else { double minimum = Double.POSITIVE_INFINITY; int seriesCount = dataset.getRowCount(); int itemCount = dataset.getColumnCount(); for (int series = 0; series < seriesCount; series++) { for (int item = 0; item < itemCount; item++) { Number value; if (dataset instanceof IntervalCategoryDataset) { IntervalCategoryDataset icd = (IntervalCategoryDataset) dataset; value = icd.getStartValue(series, item); } else { value = dataset.getValue(series, item); } if (value != null) { minimum = Math.min(minimum, value.doubleValue()); } } } if (minimum == Double.POSITIVE_INFINITY) { return null; } else { return minimum; } } } /** * Returns the minimum range value for the specified dataset. This is * easy if the dataset implements the {@link RangeInfo} interface (a good * idea if there is an efficient way to determine the minimum value). * Otherwise, it involves iterating over the entire data-set. Returns * {@code null} if all the data values in the dataset are * {@code null}. * * @param dataset the dataset ({@code null} not permitted). * * @return The minimum value (possibly {@code null}). */ public static Number findMinimumRangeValue(XYDataset dataset) { Args.nullNotPermitted(dataset, "dataset"); // work out the minimum value... if (dataset instanceof RangeInfo) { RangeInfo info = (RangeInfo) dataset; return info.getRangeLowerBound(true); } // hasn't implemented RangeInfo, so we'll have to iterate... else { double minimum = Double.POSITIVE_INFINITY; int seriesCount = dataset.getSeriesCount(); for (int series = 0; series < seriesCount; series++) { int itemCount = dataset.getItemCount(series); for (int item = 0; item < itemCount; item++) { double value; if (dataset instanceof IntervalXYDataset) { IntervalXYDataset intervalXYData = (IntervalXYDataset) dataset; value = intervalXYData.getStartYValue(series, item); } else if (dataset instanceof OHLCDataset) { OHLCDataset highLowData = (OHLCDataset) dataset; value = highLowData.getLowValue(series, item); } else { value = dataset.getYValue(series, item); } if (!Double.isNaN(value)) { minimum = Math.min(minimum, value); } } } if (minimum == Double.POSITIVE_INFINITY) { return null; } else { return minimum; } } } /** * Returns the maximum range value for the specified dataset. This is easy * if the dataset implements the {@link RangeInfo} interface (a good idea * if there is an efficient way to determine the maximum value). * Otherwise, it involves iterating over the entire data-set. Returns * {@code null} if all the data values are {@code null}. * * @param dataset the dataset ({@code null} not permitted). * * @return The maximum value (possibly {@code null}). */ public static Number findMaximumRangeValue(CategoryDataset dataset) { Args.nullNotPermitted(dataset, "dataset"); // work out the minimum value... if (dataset instanceof RangeInfo) { RangeInfo info = (RangeInfo) dataset; return info.getRangeUpperBound(true); } // hasn't implemented RangeInfo, so we'll have to iterate... else { double maximum = Double.NEGATIVE_INFINITY; int seriesCount = dataset.getRowCount(); int itemCount = dataset.getColumnCount(); for (int series = 0; series < seriesCount; series++) { for (int item = 0; item < itemCount; item++) { Number value; if (dataset instanceof IntervalCategoryDataset) { IntervalCategoryDataset icd = (IntervalCategoryDataset) dataset; value = icd.getEndValue(series, item); } else { value = dataset.getValue(series, item); } if (value != null) { maximum = Math.max(maximum, value.doubleValue()); } } } if (maximum == Double.NEGATIVE_INFINITY) { return null; } else { return maximum; } } } /** * Returns the maximum range value for the specified dataset. This is * easy if the dataset implements the {@link RangeInfo} interface (a good * idea if there is an efficient way to determine the maximum value). * Otherwise, it involves iterating over the entire data-set. Returns * {@code null} if all the data values are {@code null}. * * @param dataset the dataset ({@code null} not permitted). * * @return The maximum value (possibly {@code null}). */ public static Number findMaximumRangeValue(XYDataset dataset) { Args.nullNotPermitted(dataset, "dataset"); // work out the minimum value... if (dataset instanceof RangeInfo) { RangeInfo info = (RangeInfo) dataset; return info.getRangeUpperBound(true); } // hasn't implemented RangeInfo, so we'll have to iterate... else { double maximum = Double.NEGATIVE_INFINITY; int seriesCount = dataset.getSeriesCount(); for (int series = 0; series < seriesCount; series++) { int itemCount = dataset.getItemCount(series); for (int item = 0; item < itemCount; item++) { double value; if (dataset instanceof IntervalXYDataset) { IntervalXYDataset intervalXYData = (IntervalXYDataset) dataset; value = intervalXYData.getEndYValue(series, item); } else if (dataset instanceof OHLCDataset) { OHLCDataset highLowData = (OHLCDataset) dataset; value = highLowData.getHighValue(series, item); } else { value = dataset.getYValue(series, item); } if (!Double.isNaN(value)) { maximum = Math.max(maximum, value); } } } if (maximum == Double.NEGATIVE_INFINITY) { return null; } else { return maximum; } } } /** * Returns the minimum and maximum values for the dataset's range * (y-values), assuming that the series in one category are stacked. * * @param dataset the dataset ({@code null} not permitted). * * @return The range ({@code null} if the dataset contains no values). */ public static Range findStackedRangeBounds(CategoryDataset dataset) { return findStackedRangeBounds(dataset, 0.0); } /** * Returns the minimum and maximum values for the dataset's range * (y-values), assuming that the series in one category are stacked. * * @param dataset the dataset ({@code null} not permitted). * @param base the base value for the bars. * * @return The range ({@code null} if the dataset contains no values). */ public static Range findStackedRangeBounds(CategoryDataset dataset, double base) { Args.nullNotPermitted(dataset, "dataset"); Range result = null; double minimum = Double.POSITIVE_INFINITY; double maximum = Double.NEGATIVE_INFINITY; int categoryCount = dataset.getColumnCount(); for (int item = 0; item < categoryCount; item++) { double positive = base; double negative = base; int seriesCount = dataset.getRowCount(); for (int series = 0; series < seriesCount; series++) { Number number = dataset.getValue(series, item); if (number != null) { double value = number.doubleValue(); if (value > 0.0) { positive = positive + value; } if (value < 0.0) { negative = negative + value; // '+', remember value is negative } } } minimum = Math.min(minimum, negative); maximum = Math.max(maximum, positive); } if (minimum <= maximum) { result = new Range(minimum, maximum); } return result; } /** * Returns the minimum and maximum values for the dataset's range * (y-values), assuming that the series in one category are stacked. * * @param dataset the dataset. * @param map a structure that maps series to groups. * * @return The value range ({@code null} if the dataset contains no * values). */ public static Range findStackedRangeBounds(CategoryDataset dataset, KeyToGroupMap map) { Args.nullNotPermitted(dataset, "dataset"); boolean hasValidData = false; Range result = null; // create an array holding the group indices for each series... int[] groupIndex = new int[dataset.getRowCount()]; for (int i = 0; i < dataset.getRowCount(); i++) { groupIndex[i] = map.getGroupIndex(map.getGroup( dataset.getRowKey(i))); } // minimum and maximum for each group... int groupCount = map.getGroupCount(); double[] minimum = new double[groupCount]; double[] maximum = new double[groupCount]; int categoryCount = dataset.getColumnCount(); for (int item = 0; item < categoryCount; item++) { double[] positive = new double[groupCount]; double[] negative = new double[groupCount]; int seriesCount = dataset.getRowCount(); for (int series = 0; series < seriesCount; series++) { Number number = dataset.getValue(series, item); if (number != null) { hasValidData = true; double value = number.doubleValue(); if (value > 0.0) { positive[groupIndex[series]] = positive[groupIndex[series]] + value; } if (value < 0.0) { negative[groupIndex[series]] = negative[groupIndex[series]] + value; // '+', remember value is negative } } } for (int g = 0; g < groupCount; g++) { minimum[g] = Math.min(minimum[g], negative[g]); maximum[g] = Math.max(maximum[g], positive[g]); } } if (hasValidData) { for (int j = 0; j < groupCount; j++) { result = Range.combine(result, new Range(minimum[j], maximum[j])); } } return result; } /** * Returns the minimum value in the dataset range, assuming that values in * each category are "stacked". * * @param dataset the dataset ({@code null} not permitted). * * @return The minimum value. * * @see #findMaximumStackedRangeValue(CategoryDataset) */ public static Number findMinimumStackedRangeValue(CategoryDataset dataset) { Args.nullNotPermitted(dataset, "dataset"); Number result = null; boolean hasValidData = false; double minimum = 0.0; int categoryCount = dataset.getColumnCount(); for (int item = 0; item < categoryCount; item++) { double total = 0.0; int seriesCount = dataset.getRowCount(); for (int series = 0; series < seriesCount; series++) { Number number = dataset.getValue(series, item); if (number != null) { hasValidData = true; double value = number.doubleValue(); if (value < 0.0) { total = total + value; // '+', remember value is negative } } } minimum = Math.min(minimum, total); } if (hasValidData) { result = minimum; } return result; } /** * Returns the maximum value in the dataset range, assuming that values in * each category are "stacked". * * @param dataset the dataset ({@code null} not permitted). * * @return The maximum value (possibly {@code null}). * * @see #findMinimumStackedRangeValue(CategoryDataset) */ public static Number findMaximumStackedRangeValue(CategoryDataset dataset) { Args.nullNotPermitted(dataset, "dataset"); Number result = null; boolean hasValidData = false; double maximum = 0.0; int categoryCount = dataset.getColumnCount(); for (int item = 0; item < categoryCount; item++) { double total = 0.0; int seriesCount = dataset.getRowCount(); for (int series = 0; series < seriesCount; series++) { Number number = dataset.getValue(series, item); if (number != null) { hasValidData = true; double value = number.doubleValue(); if (value > 0.0) { total = total + value; } } } maximum = Math.max(maximum, total); } if (hasValidData) { result = maximum; } return result; } /** * Returns the minimum and maximum values for the dataset's range, * assuming that the series are stacked. * * @param dataset the dataset ({@code null} not permitted). * * @return The range ([0.0, 0.0] if the dataset contains no values). */ public static Range findStackedRangeBounds(TableXYDataset dataset) { return findStackedRangeBounds(dataset, 0.0); } /** * Returns the minimum and maximum values for the dataset's range, * assuming that the series are stacked, using the specified base value. * * @param dataset the dataset ({@code null} not permitted). * @param base the base value. * * @return The range ({@code null} if the dataset contains no values). */ public static Range findStackedRangeBounds(TableXYDataset dataset, double base) { Args.nullNotPermitted(dataset, "dataset"); double minimum = base; double maximum = base; for (int itemNo = 0; itemNo < dataset.getItemCount(); itemNo++) { double positive = base; double negative = base; int seriesCount = dataset.getSeriesCount(); for (int seriesNo = 0; seriesNo < seriesCount; seriesNo++) { double y = dataset.getYValue(seriesNo, itemNo); if (!Double.isNaN(y)) { if (y > 0.0) { positive += y; } else { negative += y; } } } if (positive > maximum) { maximum = positive; } if (negative < minimum) { minimum = negative; } } if (minimum <= maximum) { return new Range(minimum, maximum); } else { return null; } } /** * Calculates the total for the y-values in all series for a given item * index. * * @param dataset the dataset. * @param item the item index. * * @return The total. */ public static double calculateStackTotal(TableXYDataset dataset, int item) { double total = 0.0; int seriesCount = dataset.getSeriesCount(); for (int s = 0; s < seriesCount; s++) { double value = dataset.getYValue(s, item); if (!Double.isNaN(value)) { total = total + value; } } return total; } /** * Calculates the range of values for a dataset where each item is the * running total of the items for the current series. * * @param dataset the dataset ({@code null} not permitted). * * @return The range. * * @see #findRangeBounds(CategoryDataset) */ public static Range findCumulativeRangeBounds(CategoryDataset dataset) { Args.nullNotPermitted(dataset, "dataset"); boolean allItemsNull = true; // we'll set this to false if there is at // least one non-null data item... double minimum = 0.0; double maximum = 0.0; for (int row = 0; row < dataset.getRowCount(); row++) { double runningTotal = 0.0; for (int column = 0; column <= dataset.getColumnCount() - 1; column++) { Number n = dataset.getValue(row, column); if (n != null) { allItemsNull = false; double value = n.doubleValue(); if (!Double.isNaN(value)) { runningTotal = runningTotal + value; minimum = Math.min(minimum, runningTotal); maximum = Math.max(maximum, runningTotal); } } } } if (!allItemsNull) { return new Range(minimum, maximum); } else { return null; } } /** * Returns the interpolated value of y that corresponds to the specified * x-value in the given series. If the x-value falls outside the range of * x-values for the dataset, this method returns {@code Double.NaN}. * * @param dataset the dataset ({@code null} not permitted). * @param series the series index. * @param x the x-value. * * @return The y value. */ public static double findYValue(XYDataset dataset, int series, double x) { // delegate null check on dataset int[] indices = findItemIndicesForX(dataset, series, x); if (indices[0] == -1) { return Double.NaN; } if (indices[0] == indices[1]) { return dataset.getYValue(series, indices[0]); } double x0 = dataset.getXValue(series, indices[0]); double x1 = dataset.getXValue(series, indices[1]); double y0 = dataset.getYValue(series, indices[0]); double y1 = dataset.getYValue(series, indices[1]); return y0 + (y1 - y0) * (x - x0) / (x1 - x0); } /** * Finds the indices of the the items in the dataset that span the * specified x-value. There are three cases for the return value: *

    *
  • there is an exact match for the x-value at index i * (returns {@code int[] {i, i}});
  • *
  • the x-value falls between two (adjacent) items at index i and i+1 * (returns {@code int[] {i, i+1}});
  • *
  • the x-value falls outside the domain bounds, in which case the * method returns {@code int[] {-1, -1}}.
  • *
* @param dataset the dataset ({@code null} not permitted). * @param series the series index. * @param x the x-value. * * @return The indices of the two items that span the x-value. * * @see #findYValue(org.jfree.data.xy.XYDataset, int, double) */ public static int[] findItemIndicesForX(XYDataset dataset, int series, double x) { Args.nullNotPermitted(dataset, "dataset"); int itemCount = dataset.getItemCount(series); if (itemCount == 0) { return new int[] {-1, -1}; } if (itemCount == 1) { if (x == dataset.getXValue(series, 0)) { return new int[] {0, 0}; } else { return new int[] {-1, -1}; } } if (dataset.getDomainOrder() == DomainOrder.ASCENDING) { int low = 0; int high = itemCount - 1; double lowValue = dataset.getXValue(series, low); if (lowValue > x) { return new int[] {-1, -1}; } if (lowValue == x) { return new int[] {low, low}; } double highValue = dataset.getXValue(series, high); if (highValue < x) { return new int[] {-1, -1}; } if (highValue == x) { return new int[] {high, high}; } int mid = (low + high) / 2; while (high - low > 1) { double midV = dataset.getXValue(series, mid); if (x == midV) { return new int[] {mid, mid}; } if (midV < x) { low = mid; } else { high = mid; } mid = (low + high) / 2; } return new int[] {low, high}; } else if (dataset.getDomainOrder() == DomainOrder.DESCENDING) { int high = 0; int low = itemCount - 1; double lowValue = dataset.getXValue(series, low); if (lowValue > x) { return new int[] {-1, -1}; } double highValue = dataset.getXValue(series, high); if (highValue < x) { return new int[] {-1, -1}; } int mid = (low + high) / 2; while (high - low > 1) { double midV = dataset.getXValue(series, mid); if (x == midV) { return new int[] {mid, mid}; } if (midV < x) { low = mid; } else { high = mid; } mid = (low + high) / 2; } return new int[] {low, high}; } else { // we don't know anything about the ordering of the x-values, // so we iterate until we find the first crossing of x (if any) // we know there are at least 2 items in the series at this point double prev = dataset.getXValue(series, 0); if (x == prev) { return new int[] {0, 0}; // exact match on first item } for (int i = 1; i < itemCount; i++) { double next = dataset.getXValue(series, i); if (x == next) { return new int[] {i, i}; // exact match } if ((x > prev && x < next) || (x < prev && x > next)) { return new int[] {i - 1, i}; // spanning match } } return new int[] {-1, -1}; // no crossing of x } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/general/DefaultHeatMapDataset.java000066400000000000000000000241021463604235500312770ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * DefaultHeatMapDataset.java * -------------------------- * (C) Copyright 2009-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.general; import java.io.Serializable; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.DataUtils; /** * A default implementation of the {@link HeatMapDataset} interface. */ public class DefaultHeatMapDataset extends AbstractDataset implements HeatMapDataset, Cloneable, PublicCloneable, Serializable { /** The number of samples in this dataset for the x-dimension. */ private int xSamples; /** The number of samples in this dataset for the y-dimension. */ private int ySamples; /** The minimum x-value in the dataset. */ private double minX; /** The maximum x-value in the dataset. */ private double maxX; /** The minimum y-value in the dataset. */ private double minY; /** The maximum y-value in the dataset. */ private double maxY; /** Storage for the z-values. */ private double[][] zValues; /** * Creates a new dataset where all the z-values are initially 0. This is * a fixed size array of z-values. * * @param xSamples the number of x-values. * @param ySamples the number of y-values * @param minX the minimum x-value in the dataset. * @param maxX the maximum x-value in the dataset. * @param minY the minimum y-value in the dataset. * @param maxY the maximum y-value in the dataset. */ public DefaultHeatMapDataset(int xSamples, int ySamples, double minX, double maxX, double minY, double maxY) { if (xSamples < 1) { throw new IllegalArgumentException("Requires 'xSamples' > 0"); } if (ySamples < 1) { throw new IllegalArgumentException("Requires 'ySamples' > 0"); } if (Double.isInfinite(minX) || Double.isNaN(minX)) { throw new IllegalArgumentException("'minX' cannot be INF or NaN."); } if (Double.isInfinite(maxX) || Double.isNaN(maxX)) { throw new IllegalArgumentException("'maxX' cannot be INF or NaN."); } if (Double.isInfinite(minY) || Double.isNaN(minY)) { throw new IllegalArgumentException("'minY' cannot be INF or NaN."); } if (Double.isInfinite(maxY) || Double.isNaN(maxY)) { throw new IllegalArgumentException("'maxY' cannot be INF or NaN."); } this.xSamples = xSamples; this.ySamples = ySamples; this.minX = minX; this.maxX = maxX; this.minY = minY; this.maxY = maxY; this.zValues = new double[xSamples][]; for (int x = 0; x < xSamples; x++) { this.zValues[x] = new double[ySamples]; } } /** * Returns the number of x values across the width of the dataset. The * values are evenly spaced between {@link #getMinimumXValue()} and * {@link #getMaximumXValue()}. * * @return The number of x-values (always > 0). */ @Override public int getXSampleCount() { return this.xSamples; } /** * Returns the number of y values (or samples) for the dataset. The * values are evenly spaced between {@link #getMinimumYValue()} and * {@link #getMaximumYValue()}. * * @return The number of y-values (always > 0). */ @Override public int getYSampleCount() { return this.ySamples; } /** * Returns the lowest x-value represented in this dataset. A requirement * of this interface is that this method must never return infinite or * Double.NAN values. * * @return The lowest x-value represented in this dataset. */ @Override public double getMinimumXValue() { return this.minX; } /** * Returns the highest x-value represented in this dataset. A requirement * of this interface is that this method must never return infinite or * Double.NAN values. * * @return The highest x-value represented in this dataset. */ @Override public double getMaximumXValue() { return this.maxX; } /** * Returns the lowest y-value represented in this dataset. A requirement * of this interface is that this method must never return infinite or * Double.NAN values. * * @return The lowest y-value represented in this dataset. */ @Override public double getMinimumYValue() { return this.minY; } /** * Returns the highest y-value represented in this dataset. A requirement * of this interface is that this method must never return infinite or * Double.NAN values. * * @return The highest y-value represented in this dataset. */ @Override public double getMaximumYValue() { return this.maxY; } /** * A convenience method that returns the x-value for the given index. * * @param xIndex the xIndex. * * @return The x-value. */ @Override public double getXValue(int xIndex) { double x = this.minX + (this.maxX - this.minX) * (xIndex / (double) this.xSamples); return x; } /** * A convenience method that returns the y-value for the given index. * * @param yIndex the yIndex. * * @return The y-value. */ @Override public double getYValue(int yIndex) { double y = this.minY + (this.maxY - this.minY) * (yIndex / (double) this.ySamples); return y; } /** * Returns the z-value at the specified sample position in the dataset. * For a missing or unknown value, this method should return Double.NAN. * * @param xIndex the position of the x sample in the dataset. * @param yIndex the position of the y sample in the dataset. * * @return The z-value. */ @Override public double getZValue(int xIndex, int yIndex) { return this.zValues[xIndex][yIndex]; } /** * Returns the z-value at the specified sample position in the dataset. * In this implementation, where the underlying values are stored in an * array of double primitives, you should avoid using this method and * use {@link #getZValue(int, int)} instead. * * @param xIndex the position of the x sample in the dataset. * @param yIndex the position of the y sample in the dataset. * * @return The z-value. */ @Override public Number getZ(int xIndex, int yIndex) { return getZValue(xIndex, yIndex); } /** * Updates a z-value in the dataset and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param xIndex the x-index. * @param yIndex the y-index. * @param z the new z-value. */ public void setZValue(int xIndex, int yIndex, double z) { setZValue(xIndex, yIndex, z, true); } /** * Updates a z-value in the dataset and, if requested, sends a * {@link DatasetChangeEvent} to all registered listeners. * * @param xIndex the x-index. * @param yIndex the y-index. * @param z the new z-value. * @param notify notify listeners? */ public void setZValue(int xIndex, int yIndex, double z, boolean notify) { this.zValues[xIndex][yIndex] = z; if (notify) { fireDatasetChanged(); } } /** * Tests this dataset for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof DefaultHeatMapDataset)) { return false; } DefaultHeatMapDataset that = (DefaultHeatMapDataset) obj; if (this.xSamples != that.xSamples) { return false; } if (this.ySamples != that.ySamples) { return false; } if (this.minX != that.minX) { return false; } if (this.maxX != that.maxX) { return false; } if (this.minY != that.minY) { return false; } if (this.maxY != that.maxY) { return false; } if (!DataUtils.equal(this.zValues, that.zValues)) { return false; } // can't find any differences return true; } /** * Returns an independent copy of this dataset. * * @return A clone. * * @throws java.lang.CloneNotSupportedException if there is a problem * cloning. */ @Override public Object clone() throws CloneNotSupportedException { DefaultHeatMapDataset clone = (DefaultHeatMapDataset) super.clone(); clone.zValues = DataUtils.clone(this.zValues); return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/general/DefaultKeyedValueDataset.java000066400000000000000000000130561463604235500320240ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------- * DefaultKeyedValueDataset.java * ----------------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.general; import java.io.Serializable; import java.util.Objects; import org.jfree.data.DefaultKeyedValue; import org.jfree.data.KeyedValue; /** * A default implementation of the {@link KeyedValueDataset} interface. */ public class DefaultKeyedValueDataset extends AbstractDataset implements KeyedValueDataset, Serializable { /** For serialization. */ private static final long serialVersionUID = -8149484339560406750L; /** Storage for the data. */ private KeyedValue data; /** * Constructs a new dataset, initially empty. */ public DefaultKeyedValueDataset() { this(null); } /** * Creates a new dataset with the specified initial value. * * @param key the key. * @param value the value ({@code null} permitted). */ public DefaultKeyedValueDataset(Comparable key, Number value) { this(new DefaultKeyedValue(key, value)); } /** * Creates a new dataset that uses the data from a {@link KeyedValue} * instance. * * @param data the data ({@code null} permitted). */ public DefaultKeyedValueDataset(KeyedValue data) { this.data = data; } /** * Returns the key associated with the value, or {@code null} if the * dataset has no data item. * * @return The key. */ @Override public Comparable getKey() { Comparable result = null; if (this.data != null) { result = this.data.getKey(); } return result; } /** * Returns the value. * * @return The value (possibly {@code null}). */ @Override public Number getValue() { Number result = null; if (this.data != null) { result = this.data.getValue(); } return result; } /** * Updates the value. * * @param value the new value ({@code null} permitted). */ public void updateValue(Number value) { if (this.data == null) { throw new RuntimeException("updateValue: can't update null."); } setValue(this.data.getKey(), value); } /** * Sets the value for the dataset and sends a {@link DatasetChangeEvent} to * all registered listeners. * * @param key the key. * @param value the value ({@code null} permitted). */ public void setValue(Comparable key, Number value) { this.data = new DefaultKeyedValue(key, value); notifyListeners(new DatasetChangeEvent(this, this)); } /** * Tests this dataset for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof KeyedValueDataset)) { return false; } KeyedValueDataset that = (KeyedValueDataset) obj; if (this.data == null) { if (that.getKey() != null || that.getValue() != null) { return false; } return true; } if (!Objects.equals(this.data.getKey(), that.getKey())) { return false; } if (!Objects.equals(this.data.getValue(), that.getValue())) { return false; } return true; } /** * Returns a hash code. * * @return A hash code. */ @Override public int hashCode() { return (this.data != null ? this.data.hashCode() : 0); } /** * Creates a clone of the dataset. * * @return A clone. * * @throws CloneNotSupportedException This class will not throw this * exception, but subclasses (if any) might. */ @Override public Object clone() throws CloneNotSupportedException { DefaultKeyedValueDataset clone = (DefaultKeyedValueDataset) super.clone(); return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/general/DefaultKeyedValues2DDataset.java000066400000000000000000000036511463604235500323750ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------------- * DefaultKeyedValues2DDataset.java * -------------------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.general; import java.io.Serializable; import org.jfree.data.category.DefaultCategoryDataset; /** * A default implementation of the {@link KeyedValues2DDataset} interface. * */ public class DefaultKeyedValues2DDataset extends DefaultCategoryDataset implements KeyedValues2DDataset, Serializable { /** For serialization. */ private static final long serialVersionUID = 4288210771905990424L; // no new methods } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/general/DefaultKeyedValuesDataset.java000066400000000000000000000035371463604235500322120ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------ * DefaultKeyedValuesDataset.java * ------------------------------ * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.general; /** * A default implementation of the {@link KeyedValuesDataset} interface. * This is an alias for {@link DefaultPieDataset}. */ public class DefaultKeyedValuesDataset extends DefaultPieDataset implements KeyedValuesDataset { /** For serialization. */ private static final long serialVersionUID = 306264413152815781L; // no new methods } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/general/DefaultPieDataset.java000066400000000000000000000245161463604235500305060ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * DefaultPieDataset.java * ---------------------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Sam (oldman); * Tracy Hiltbrand (generics for bug fix to PiePlot); */ package org.jfree.data.general; import java.io.Serializable; import java.util.Collections; import java.util.List; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.SortOrder; import org.jfree.data.DefaultKeyedValues; import org.jfree.data.KeyedValues; import org.jfree.data.UnknownKeyException; /** * A default implementation of the {@link PieDataset} interface. * * @param Key type for PieDataset */ public class DefaultPieDataset> extends AbstractDataset implements PieDataset, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 2904745139106540618L; /** Storage for the data. */ private DefaultKeyedValues data; /** * Constructs a new dataset, initially empty. */ public DefaultPieDataset() { this.data = new DefaultKeyedValues<>(); } /** * Creates a new dataset by copying data from a {@link KeyedValues} * instance. * * @param source the data ({@code null} not permitted). */ public DefaultPieDataset(KeyedValues source) { Args.nullNotPermitted(source, "source"); this.data = new DefaultKeyedValues<>(); for (int i = 0; i < source.getItemCount(); i++) { this.data.addValue(source.getKey(i), source.getValue(i)); } } /** * Returns the number of items in the dataset. * * @return The item count. */ @Override public int getItemCount() { return this.data.getItemCount(); } /** * Returns the categories in the dataset. The returned list is * unmodifiable. * * @return The categories in the dataset. */ @Override public List getKeys() { return Collections.unmodifiableList(this.data.getKeys()); } /** * Returns the key for the specified item, or {@code null}. * * @param item the item index (in the range {@code 0} to * {@code getItemCount() - 1}). * * @return The key, or {@code null}. * * @throws IndexOutOfBoundsException if {@code item} is not in the * specified range. */ @Override public K getKey(int item) { return this.data.getKey(item); } /** * Returns the index for a key, or -1 if the key is not recognised. * * @param key the key ({@code null} not permitted). * * @return The index, or {@code -1} if the key is unrecognised. * * @throws IllegalArgumentException if {@code key} is * {@code null}. */ @Override public int getIndex(K key) { return this.data.getIndex(key); } /** * Returns a value. * * @param item the value index. * * @return The value (possibly {@code null}). */ @Override public Number getValue(int item) { return this.data.getValue(item); } /** * Returns the data value associated with a key. * * @param key the key ({@code null} not permitted). * * @return The value (possibly {@code null}). * * @throws UnknownKeyException if the key is not recognised. */ @Override public Number getValue(K key) { Args.nullNotPermitted(key, "key"); return this.data.getValue(key); } /** * Sets the data value for a key and sends a {@link DatasetChangeEvent} to * all registered listeners. * * @param key the key ({@code null} not permitted). * @param value the value. * * @throws IllegalArgumentException if {@code key} is * {@code null}. */ public void setValue(K key, Number value) { this.data.setValue(key, value); fireDatasetChanged(); } /** * Sets the data value for a key and sends a {@link DatasetChangeEvent} to * all registered listeners. * * @param key the key ({@code null} not permitted). * @param value the value. * * @throws IllegalArgumentException if {@code key} is * {@code null}. */ public void setValue(K key, double value) { setValue(key, Double.valueOf(value)); } /** * Inserts a new value at the specified position in the dataset or, if * there is an existing item with the specified key, updates the value * for that item and moves it to the specified position. After the change * is made, this methods sends a {@link DatasetChangeEvent} to all * registered listeners. * * @param position the position (in the range 0 to getItemCount()). * @param key the key ({@code null} not permitted). * @param value the value ({@code null} permitted). */ public void insertValue(int position, K key, double value) { insertValue(position, key, Double.valueOf(value)); } /** * Inserts a new value at the specified position in the dataset or, if * there is an existing item with the specified key, updates the value * for that item and moves it to the specified position. After the change * is made, this methods sends a {@link DatasetChangeEvent} to all * registered listeners. * * @param position the position (in the range 0 to getItemCount()). * @param key the key ({@code null} not permitted). * @param value the value ({@code null} permitted). */ public void insertValue(int position, K key, Number value) { this.data.insertValue(position, key, value); fireDatasetChanged(); } /** * Removes an item from the dataset and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param key the key ({@code null} not permitted). * * @throws IllegalArgumentException if {@code key} is * {@code null}. */ public void remove(K key) { this.data.removeValue(key); fireDatasetChanged(); } /** * Clears all data from this dataset and sends a {@link DatasetChangeEvent} * to all registered listeners (unless the dataset was already empty). */ public void clear() { if (getItemCount() > 0) { this.data.clear(); fireDatasetChanged(); } } /** * Sorts the dataset's items by key and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param order the sort order ({@code null} not permitted). */ public void sortByKeys(SortOrder order) { this.data.sortByKeys(order); fireDatasetChanged(); } /** * Sorts the dataset's items by value and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param order the sort order ({@code null} not permitted). */ public void sortByValues(SortOrder order) { this.data.sortByValues(order); fireDatasetChanged(); } /** * Tests if this object is equal to another. * * @param obj the other object. * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof PieDataset)) { return false; } @SuppressWarnings("unchecked") PieDataset that = (PieDataset) obj; int count = getItemCount(); if (that.getItemCount() != count) { return false; } for (int i = 0; i < count; i++) { K k1 = getKey(i); K k2 = that.getKey(i); if (!k1.equals(k2)) { return false; } Number v1 = getValue(i); Number v2 = that.getValue(i); if (v1 == null) { if (v2 != null) { return false; } } else { if (!v1.equals(v2)) { return false; } } } return true; } /** * Returns a hash code. * * @return A hash code. */ @Override public int hashCode() { return this.data.hashCode(); } /** * Returns a clone of the dataset. * * @return A clone. * * @throws CloneNotSupportedException This class will not throw this * exception, but subclasses (if any) might. */ @Override @SuppressWarnings("unchecked") public Object clone() throws CloneNotSupportedException { DefaultPieDataset clone = (DefaultPieDataset) super.clone(); clone.data = (DefaultKeyedValues) this.data.clone(); return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/general/DefaultValueDataset.java000066400000000000000000000074651463604235500310510ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * DefaultValueDataset.java * ------------------------ * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.general; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.util.PublicCloneable; /** * A dataset that stores a single value (that is possibly {@code null}). * This class provides a default implementation of the {@link ValueDataset} * interface. */ public class DefaultValueDataset extends AbstractDataset implements ValueDataset, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 8137521217249294891L; /** The value. */ private Number value; /** * Constructs a new dataset, initially empty. */ public DefaultValueDataset() { this(null); } /** * Creates a new dataset with the specified value. * * @param value the value. */ public DefaultValueDataset(double value) { this(Double.valueOf(value)); } /** * Creates a new dataset with the specified value. * * @param value the initial value ({@code null} permitted). */ public DefaultValueDataset(Number value) { super(); this.value = value; } /** * Returns the value. * * @return The value (possibly {@code null}). */ @Override public Number getValue() { return this.value; } /** * Sets the value and sends a {@link DatasetChangeEvent} to all registered * listeners. * * @param value the new value ({@code null} permitted). */ public void setValue(Number value) { this.value = value; notifyListeners(new DatasetChangeEvent(this, this)); } /** * Tests this dataset for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj instanceof ValueDataset) { ValueDataset vd = (ValueDataset) obj; return Objects.equals(this.value, vd.getValue()); } return false; } /** * Returns a hash code. * * @return A hash code. */ @Override public int hashCode() { return (this.value != null ? this.value.hashCode() : 0); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/general/HeatMapDataset.java000066400000000000000000000120041463604235500277700ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * HeatMapDataset.java * ------------------- * (C) Copyright 2009-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.general; /** * A dataset that represents a rectangular grid of (x, y, z) values. The x * and y values appear at regular intervals in the dataset, while the z-values * can take any value (including {@code null} for unknown values). */ public interface HeatMapDataset { /** * Returns the number of x values across the width of the dataset. The * values are evenly spaced between {@link #getMinimumXValue()} and * {@link #getMaximumXValue()}. * * @return The number of x-values (always > 0). */ int getXSampleCount(); /** * Returns the number of y values (or samples) for the dataset. The * values are evenly spaced between {@link #getMinimumYValue()} and * {@link #getMaximumYValue()}. * * @return The number of y-values (always > 0). */ int getYSampleCount(); /** * Returns the lowest x-value represented in this dataset. A requirement * of this interface is that this method must never return infinite or * Double.NAN values. * * @return The lowest x-value represented in this dataset. */ double getMinimumXValue(); /** * Returns the highest x-value represented in this dataset. A requirement * of this interface is that this method must never return infinite or * Double.NAN values. * * @return The highest x-value represented in this dataset. */ double getMaximumXValue(); /** * Returns the lowest y-value represented in this dataset. A requirement * of this interface is that this method must never return infinite or * Double.NAN values. * * @return The lowest y-value represented in this dataset. */ double getMinimumYValue(); /** * Returns the highest y-value represented in this dataset. A requirement * of this interface is that this method must never return infinite or * Double.NAN values. * * @return The highest y-value represented in this dataset. */ double getMaximumYValue(); /** * A convenience method that returns the x-value for the given index. * * @param xIndex the xIndex. * * @return The x-value. */ double getXValue(int xIndex); /** * A convenience method that returns the y-value for the given index. * * @param yIndex the yIndex. * * @return The y-value. */ double getYValue(int yIndex); /** * Returns the z-value at the specified sample position in the dataset. * For a missing or unknown value, this method should return Double.NAN. * * @param xIndex the position of the x sample in the dataset. * @param yIndex the position of the y sample in the dataset. * * @return The z-value. */ double getZValue(int xIndex, int yIndex); /** * Returns the z-value at the specified sample position in the dataset. * This method can return {@code null} to indicate a missing/unknown * value. *

* Bear in mind that the class implementing this interface may * store its data using primitives rather than objects, so calling this * method may require a new {@code Number} object to be allocated... * for this reason, it is generally preferable to use the * {@link #getZValue(int, int)} method unless you *know* that the dataset * implementation stores the z-values using objects. * * @param xIndex the position of the x sample in the dataset. * @param yIndex the position of the y sample in the dataset. * * @return The z-value (possibly {@code null}). */ Number getZ(int xIndex, int yIndex); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/general/HeatMapUtils.java000066400000000000000000000110431463604235500275050ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * HeatMapUtils.java * ----------------- * (C) Copyright 2009-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.general; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.image.BufferedImage; import org.jfree.chart.renderer.PaintScale; import org.jfree.chart.util.Args; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; /** * A utility class for the {@link HeatMapDataset}. */ public abstract class HeatMapUtils { /** * Returns a dataset containing one series that holds a copy of the (x, z) * data from one row (y-index) of the specified dataset. * * @param dataset the dataset ({@code null} not permitted). * @param row the row (y) index. * @param seriesName the series name/key ({@code null} not permitted). * * @return The dataset. */ public static XYDataset extractRowFromHeatMapDataset(HeatMapDataset dataset, int row, Comparable seriesName) { XYSeries series = new XYSeries(seriesName); int cols = dataset.getXSampleCount(); for (int c = 0; c < cols; c++) { series.add(dataset.getXValue(c), dataset.getZValue(c, row)); } XYSeriesCollection result = new XYSeriesCollection(series); return result; } /** * Returns a dataset containing one series that holds a copy of the (y, z) * data from one column (x-index) of the specified dataset. * * @param dataset the dataset ({@code null} not permitted). * @param column the column (x) index. * @param seriesName the series name ({@code null} not permitted). * * @return The dataset. */ public static XYDataset extractColumnFromHeatMapDataset( HeatMapDataset dataset, int column, Comparable seriesName) { XYSeries series = new XYSeries(seriesName); int rows = dataset.getYSampleCount(); for (int r = 0; r < rows; r++) { series.add(dataset.getYValue(r), dataset.getZValue(column, r)); } XYSeriesCollection result = new XYSeriesCollection(series); return result; } /** * Creates an image that displays the values from the specified dataset. * * @param dataset the dataset ({@code null} not permitted). * @param paintScale the paint scale for the z-values ({@code null} * not permitted). * * @return A buffered image. */ public static BufferedImage createHeatMapImage(HeatMapDataset dataset, PaintScale paintScale) { Args.nullNotPermitted(dataset, "dataset"); Args.nullNotPermitted(paintScale, "paintScale"); int xCount = dataset.getXSampleCount(); int yCount = dataset.getYSampleCount(); BufferedImage image = new BufferedImage(xCount, yCount, BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = image.createGraphics(); for (int xIndex = 0; xIndex < xCount; xIndex++) { for (int yIndex = 0; yIndex < yCount; yIndex++) { double z = dataset.getZValue(xIndex, yIndex); Paint p = paintScale.getPaint(z); g2.setPaint(p); g2.fillRect(xIndex, yCount - yIndex - 1, 1, 1); } } return image; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/general/KeyedValueDataset.java000066400000000000000000000032171463604235500305150ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * KeyedValueDataset.java * ---------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.general; import org.jfree.data.KeyedValue; /** * A dataset containing a single value. */ public interface KeyedValueDataset extends KeyedValue, Dataset { // no new methods required } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/general/KeyedValues2DDataset.java000066400000000000000000000034161463604235500310670ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * KeyedValues2DDataset.java * ----------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.general; import org.jfree.data.category.CategoryDataset; /** * A dataset containing (key, value) data items. This dataset is equivalent * to a {@link CategoryDataset} and is included for completeness only. */ public interface KeyedValues2DDataset extends CategoryDataset { // no new methods required } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/general/KeyedValuesDataset.java000066400000000000000000000031561463604235500307020ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * KeyedValuesDataset.java * ----------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.general; /** * A dataset containing (key, value) data items. */ public interface KeyedValuesDataset extends PieDataset { // no new methods required } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/general/PieDataset.java000066400000000000000000000037671463604235500272060ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * PieDataset.java * --------------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Sam (oldman); * Tracy Hiltbrand (generics for bug fix to PiePlot); * */ package org.jfree.data.general; import org.jfree.data.KeyedValues; /** * A general purpose dataset where values are associated with keys. As the * name suggests, you can use this dataset to supply data for pie charts (refer * to the {@link org.jfree.chart.plot.PiePlot} class). * * @param Key type for KeyedValues */ public interface PieDataset> extends KeyedValues, Dataset { // no new methods added. } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/general/Series.java000066400000000000000000000333141463604235500264040ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------- * Series.java * ----------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand (equals/hashCode comply with EqualsVerifier); * */ package org.jfree.data.general; import org.jfree.chart.util.Args; import javax.swing.event.EventListenerList; import java.beans.*; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Objects; /** * Base class representing a data series. Subclasses are left to implement the * actual data structures. *

* The series has two properties ("Key" and "Description") for which you can * register a {@code PropertyChangeListener}. *

* You can also register a {@link SeriesChangeListener} to receive notification * of changes to the series data. */ public abstract class Series implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -6906561437538683581L; /** The key for the series. */ private Comparable key; /** A description of the series. */ private String description; /** Storage for registered change listeners. */ private transient EventListenerList listeners; /** Object to support property change notification. */ private transient PropertyChangeSupport propertyChangeSupport; /** Object to support property change notification. */ private transient VetoableChangeSupport vetoableChangeSupport; /** A flag that controls whether changes are notified. */ private boolean notify; /** * Creates a new series with the specified key. * * @param key the series key ({@code null} not permitted). */ protected Series(Comparable key) { this(key, null); } /** * Creates a new series with the specified key and description. * * @param key the series key ({@code null} NOT permitted). * @param description the series description ({@code null} permitted). */ protected Series(Comparable key, String description) { Args.nullNotPermitted(key, "key"); this.key = key; this.description = description; this.listeners = new EventListenerList(); this.propertyChangeSupport = new PropertyChangeSupport(this); this.vetoableChangeSupport = new VetoableChangeSupport(this); this.notify = true; } /** * Returns the key for the series. * * @return The series key (never {@code null}). * * @see #setKey(Comparable) */ public Comparable getKey() { return this.key; } /** * Sets the key for the series and sends a {@code VetoableChangeEvent} * (with the property name "Key") to all registered listeners. For * backwards compatibility, this method also fires a regular * {@code PropertyChangeEvent}. If the key change is vetoed this * method will throw an IllegalArgumentException. * * This implementation is not very robust when cloning or deserialising * series collections, so you should not rely upon it for that purpose. * In future releases, the series key will be made immutable. * * @param key the key ({@code null} not permitted). * * @see #getKey() * @deprecated In future releases the series key will be immutable. */ @Deprecated public void setKey(Comparable key) { Args.nullNotPermitted(key, "key"); Comparable old = this.key; try { // if this series belongs to a dataset, the dataset might veto the // change if it results in two series within the dataset having the // same key this.vetoableChangeSupport.fireVetoableChange("Key", old, key); this.key = key; // prior to 1.0.14, we just fired a PropertyChange - so we need to // keep doing this this.propertyChangeSupport.firePropertyChange("Key", old, key); } catch (PropertyVetoException e) { throw new IllegalArgumentException(e.getMessage()); } } /** * Returns a description of the series. * * @return The series description (possibly {@code null}). * * @see #setDescription(String) */ public String getDescription() { return this.description; } /** * Sets the description of the series and sends a * {@code PropertyChangeEvent} to all registered listeners. * * @param description the description ({@code null} permitted). * * @see #getDescription() */ public void setDescription(String description) { String old = this.description; this.description = description; this.propertyChangeSupport.firePropertyChange("Description", old, description); } /** * Returns the flag that controls whether or not change events are sent to * registered listeners. * * @return A boolean. * * @see #setNotify(boolean) */ public boolean getNotify() { return this.notify; } /** * Sets the flag that controls whether or not change events are sent to * registered listeners. * * @param notify the new value of the flag. * * @see #getNotify() */ public void setNotify(boolean notify) { if (this.notify != notify) { this.notify = notify; fireSeriesChanged(); } } /** * Returns {@code true} if the series contains no data items, and * {@code false} otherwise. * * @return A boolean. */ public boolean isEmpty() { return (getItemCount() == 0); } /** * Returns the number of data items in the series. * * @return The number of data items in the series. */ public abstract int getItemCount(); /** * Returns a clone of the series. *

* Notes: *

    *
  • No need to clone the name or description, since String object is * immutable.
  • *
  • We set the listener list to empty, since the listeners did not * register with the clone.
  • *
  • Same applies to the PropertyChangeSupport instance.
  • *
* * @return A clone of the series. * * @throws CloneNotSupportedException not thrown by this class, but * subclasses may differ. */ @Override public Object clone() throws CloneNotSupportedException { Series clone = (Series) super.clone(); clone.listeners = new EventListenerList(); clone.propertyChangeSupport = new PropertyChangeSupport(clone); clone.vetoableChangeSupport = new VetoableChangeSupport(clone); return clone; } /** * Tests the series for equality with another object. * * @param obj the object ({@code null} permitted). * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof Series)) { return false; } Series that = (Series) obj; if (!Objects.equals(this.key, that.key)) { return false; } if (!Objects.equals(this.description, that.description)) { return false; } if (!that.canEqual(this)) { return false; } return true; } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ public boolean canEqual(Object other) { // fix the "equals not symmetric" problem return (other instanceof Series); } /** * Returns a hash code. * * @return A hash code. */ @Override public int hashCode() { int hash = 5; hash = 53 * hash + Objects.hashCode(this.key); hash = 53 * hash + Objects.hashCode(this.description); return hash; } /** * Registers an object with this series, to receive notification whenever * the series changes. *

* Objects being registered must implement the {@link SeriesChangeListener} * interface. * * @param listener the listener to register. */ public void addChangeListener(SeriesChangeListener listener) { this.listeners.add(SeriesChangeListener.class, listener); } /** * Deregisters an object, so that it not longer receives notification * whenever the series changes. * * @param listener the listener to deregister. */ public void removeChangeListener(SeriesChangeListener listener) { this.listeners.remove(SeriesChangeListener.class, listener); } /** * General method for signalling to registered listeners that the series * has been changed. */ public void fireSeriesChanged() { if (this.notify) { notifyListeners(new SeriesChangeEvent(this)); } } /** * Sends a change event to all registered listeners. * * @param event contains information about the event that triggered the * notification. */ protected void notifyListeners(SeriesChangeEvent event) { Object[] listenerList = this.listeners.getListenerList(); for (int i = listenerList.length - 2; i >= 0; i -= 2) { if (listenerList[i] == SeriesChangeListener.class) { ((SeriesChangeListener) listenerList[i + 1]).seriesChanged( event); } } } /** * Adds a property change listener to the series. * * @param listener the listener. */ public void addPropertyChangeListener(PropertyChangeListener listener) { this.propertyChangeSupport.addPropertyChangeListener(listener); } /** * Removes a property change listener from the series. * * @param listener the listener. */ public void removePropertyChangeListener(PropertyChangeListener listener) { this.propertyChangeSupport.removePropertyChangeListener(listener); } /** * Fires a property change event. * * @param property the property key. * @param oldValue the old value. * @param newValue the new value. */ protected void firePropertyChange(String property, Object oldValue, Object newValue) { this.propertyChangeSupport.firePropertyChange(property, oldValue, newValue); } /** * Adds a vetoable property change listener to the series. * * @param listener the listener. */ public void addVetoableChangeListener(VetoableChangeListener listener) { this.vetoableChangeSupport.addVetoableChangeListener(listener); } /** * Removes a vetoable property change listener from the series. * * @param listener the listener. */ public void removeVetoableChangeListener(VetoableChangeListener listener) { this.vetoableChangeSupport.removeVetoableChangeListener(listener); } /** * Fires a vetoable property change event. * * @param property the property key. * @param oldValue the old value. * @param newValue the new value. * * @throws PropertyVetoException if the change was vetoed. */ protected void fireVetoableChange(String property, Object oldValue, Object newValue) throws PropertyVetoException { this.vetoableChangeSupport.fireVetoableChange(property, oldValue, newValue); } /** * Provides serialization support. * * @param stream the output stream ({@code null} not permitted). * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); } /** * Provides serialization support. * * @param stream the input stream ({@code null} not permitted). * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); this.listeners = new EventListenerList(); this.propertyChangeSupport = new PropertyChangeSupport(this); this.vetoableChangeSupport = new VetoableChangeSupport(this); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/general/SeriesChangeEvent.java000066400000000000000000000037071463604235500305170ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * SeriesChangeEvent.java * ---------------------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.general; import java.io.Serializable; import java.util.EventObject; /** * An event with details of a change to a series. */ public class SeriesChangeEvent extends EventObject implements Serializable { /** For serialization. */ private static final long serialVersionUID = 1593866085210089052L; /** * Constructs a new event. * * @param source the source of the change event. */ public SeriesChangeEvent(Object source) { super(source); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/general/SeriesChangeListener.java000066400000000000000000000035121463604235500312150ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * SeriesChangeListener.java * ------------------------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.general; import java.util.EventListener; /** * Methods for receiving notification of changes to a data series. */ public interface SeriesChangeListener extends EventListener { /** * Called when an observed series changes in some way. * * @param event information about the change. */ void seriesChanged(SeriesChangeEvent event); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/general/SeriesDataset.java000066400000000000000000000051431463604235500277110ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * SeriesDataset.java * ------------------ * (C) Copyright 2000-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.general; import org.jfree.data.category.CategoryDataset; import org.jfree.data.xy.IntervalXYDataset; import org.jfree.data.xy.IntervalXYZDataset; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYZDataset; /** * The interface for a dataset consisting of one or many series of data. * * @see CategoryDataset * @see IntervalXYDataset * @see IntervalXYZDataset * @see XYDataset * @see XYZDataset */ public interface SeriesDataset extends Dataset { /** * Returns the number of series in the dataset. * * @return The series count. */ int getSeriesCount(); /** * Returns the key for a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * * @return The key for the series. */ Comparable getSeriesKey(int series); /** * Returns the index of the series with the specified key, or -1 if there * is no such series in the dataset. * * @param seriesKey the series key ({@code null} permitted). * * @return The index, or -1. */ int indexOf(Comparable seriesKey); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/general/SeriesException.java000066400000000000000000000036721463604235500302670ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * SeriesException.java * -------------------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.general; import java.io.Serializable; /** * A general purpose exception class for data series. */ public class SeriesException extends RuntimeException implements Serializable { /** For serialization. */ private static final long serialVersionUID = -3667048387550852940L; /** * Constructs a new series exception. * * @param message a message describing the exception. */ public SeriesException(String message) { super(message); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/general/ValueDataset.java000066400000000000000000000032131463604235500275270ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * ValueDataset.java * ----------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.general; import org.jfree.data.Value; /** * An interface for a dataset that returns a single value. */ public interface ValueDataset extends Value, Dataset { // no additional methods required } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/general/WaferMapDataset.java000066400000000000000000000203041463604235500301550ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * WaferMapDataset.java * -------------------- * (C) Copyright 2003-present, by Robert Redburn and Contributors. * * Original Author: Robert Redburn; * Contributor(s): David Gilbert; * */ package org.jfree.data.general; import java.util.Set; import java.util.TreeSet; import org.jfree.data.DefaultKeyedValues2D; /** * A dataset that can be used with the {@link org.jfree.chart.plot.WaferMapPlot} * class. */ public class WaferMapDataset extends AbstractDataset { /** * Storage structure for the data values (row key is chipx, column is * chipy) */ private DefaultKeyedValues2D data; /** wafer x dimension */ private int maxChipX; /** wafer y dimension */ private int maxChipY; /** space to draw between chips */ private double chipSpace; /** maximum value in this dataset */ private Double maxValue; /** minimum value in this dataset */ private Double minValue; /** default chip spacing */ private static final double DEFAULT_CHIP_SPACE = 1d; /** * Creates a new dataset using the default chipspace. * * @param maxChipX the wafer x-dimension. * @param maxChipY the wafer y-dimension. */ public WaferMapDataset(int maxChipX, int maxChipY) { this(maxChipX, maxChipY, null); } /** * Creates a new dataset. * * @param maxChipX the wafer x-dimension. * @param maxChipY the wafer y-dimension. * @param chipSpace the space between chips. */ public WaferMapDataset(int maxChipX, int maxChipY, Number chipSpace) { this.maxValue = Double.NEGATIVE_INFINITY; this.minValue = Double.POSITIVE_INFINITY; this.data = new DefaultKeyedValues2D(); this.maxChipX = maxChipX; this.maxChipY = maxChipY; if (chipSpace == null) { this.chipSpace = DEFAULT_CHIP_SPACE; } else { this.chipSpace = chipSpace.doubleValue(); } } /** * Sets a value in the dataset. * * @param value the value. * @param chipx the x-index for the chip. * @param chipy the y-index for the chip. */ public void addValue(Number value, Comparable chipx, Comparable chipy) { setValue(value, chipx, chipy); } /** * Adds a value to the dataset. * * @param v the value. * @param x the x-index. * @param y the y-index. */ public void addValue(int v, int x, int y) { setValue(v, x, y); } /** * Sets a value in the dataset and updates min and max value entries. * * @param value the value. * @param chipx the x-index. * @param chipy the y-index. */ public void setValue(Number value, Comparable chipx, Comparable chipy) { this.data.setValue(value, chipx, chipy); if (isMaxValue(value)) { this.maxValue = value.doubleValue(); } if (isMinValue(value)) { this.minValue = value.doubleValue(); } } /** * Returns the number of unique values. * * @return The number of unique values. */ public int getUniqueValueCount() { return getUniqueValues().size(); } /** * Returns the set of unique values. * * @return The set of unique values. */ public Set getUniqueValues() { Set unique = new TreeSet(); //step through all the values and add them to the hash for (int r = 0; r < this.data.getRowCount(); r++) { for (int c = 0; c < this.data.getColumnCount(); c++) { Number value = this.data.getValue(r, c); if (value != null) { unique.add(value); } } } return unique; } /** * Returns the data value for a chip. * * @param chipx the x-index. * @param chipy the y-index. * * @return The data value. */ public Number getChipValue(int chipx, int chipy) { return getChipValue(Integer.valueOf(chipx), Integer.valueOf(chipy)); } /** * Returns the value for a given chip x and y or null. * * @param chipx the x-index. * @param chipy the y-index. * * @return The data value. */ public Number getChipValue(Comparable chipx, Comparable chipy) { int rowIndex = this.data.getRowIndex(chipx); if (rowIndex < 0) { return null; } int colIndex = this.data.getColumnIndex(chipy); if (colIndex < 0) { return null; } return this.data.getValue(rowIndex, colIndex); } /** * Tests to see if the passed value is larger than the stored maxvalue. * * @param check the number to check. * * @return A boolean. */ public boolean isMaxValue(Number check) { if (check.doubleValue() > this.maxValue) { return true; } return false; } /** * Tests to see if the passed value is smaller than the stored minvalue. * * @param check the number to check. * * @return A boolean. */ public boolean isMinValue(Number check) { if (check.doubleValue() < this.minValue) { return true; } return false; } /** * Returns the maximum value stored in the dataset. * * @return The maximum value. */ public Number getMaxValue() { return this.maxValue; } /** * Returns the minimum value stored in the dataset. * * @return The minimum value. */ public Number getMinValue() { return this.minValue; } /** * Returns the wafer x-dimension. * * @return The number of chips in the x-dimension. */ public int getMaxChipX() { return this.maxChipX; } /** * Sets wafer x dimension. * * @param maxChipX the number of chips in the x-dimension. */ public void setMaxChipX(int maxChipX) { this.maxChipX = maxChipX; } /** * Returns the number of chips in the y-dimension. * * @return The number of chips. */ public int getMaxChipY() { return this.maxChipY; } /** * Sets the number of chips in the y-dimension. * * @param maxChipY the number of chips. */ public void setMaxChipY(int maxChipY) { this.maxChipY = maxChipY; } /** * Returns the space to draw between chips. * * @return The space. */ public double getChipSpace() { return this.chipSpace; } /** * Sets the space to draw between chips. * * @param space the space. */ public void setChipSpace(double space) { this.chipSpace = space; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/general/package.html000066400000000000000000000002111463604235500265560ustar00rootroot00000000000000 Data interfaces and classes. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/io/000077500000000000000000000000001463604235500232755ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/io/CSV.java000066400000000000000000000151701463604235500245770ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------- * CSV.java * -------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.io; import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; import java.util.List; import org.jfree.data.category.CategoryDataset; import org.jfree.data.category.DefaultCategoryDataset; /** * A utility class for reading {@link CategoryDataset} data from a CSV file. * This initial version is very basic, and won't handle errors in the data * file very gracefully. */ public class CSV { /** The field delimiter. */ private char fieldDelimiter; /** The text delimiter. */ private char textDelimiter; /** * Creates a new CSV reader where the field delimiter is a comma, and the * text delimiter is a double-quote. */ public CSV() { this(',', '"'); } /** * Creates a new reader with the specified field and text delimiters. * * @param fieldDelimiter the field delimiter (usually a comma, semi-colon, * colon, tab or space). * @param textDelimiter the text delimiter (usually a single or double * quote). */ public CSV(char fieldDelimiter, char textDelimiter) { this.fieldDelimiter = fieldDelimiter; this.textDelimiter = textDelimiter; } /** * Reads a {@link CategoryDataset} from a CSV file or input source. * * @param in the input source. * * @return A category dataset. * * @throws IOException if there is an I/O problem. */ public CategoryDataset readCategoryDataset(Reader in) throws IOException { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); BufferedReader reader = new BufferedReader(in); List columnKeys = null; int lineIndex = 0; String line = reader.readLine(); while (line != null) { if (lineIndex == 0) { // first line contains column keys columnKeys = extractColumnKeys(line); } else { // remaining lines contain a row key and data values extractRowKeyAndData(line, dataset, columnKeys); } line = reader.readLine(); lineIndex++; } return dataset; } /** * Extracts the column keys from a string. * * @param line a line from the input file. * * @return A list of column keys. */ private List extractColumnKeys(String line) { List keys = new java.util.ArrayList(); int fieldIndex = 0; int start = 0; for (int i = 0; i < line.length(); i++) { if (line.charAt(i) == this.fieldDelimiter) { if (fieldIndex > 0) { // first field is ignored, since // column 0 is for row keys String key = line.substring(start, i); keys.add(removeStringDelimiters(key)); } start = i + 1; fieldIndex++; } } String key = line.substring(start); keys.add(removeStringDelimiters(key)); return keys; } /** * Extracts the row key and data for a single line from the input source. * * @param line the line from the input source. * @param dataset the dataset to be populated. * @param columnKeys the column keys. */ private void extractRowKeyAndData(String line, DefaultCategoryDataset dataset, List columnKeys) { Comparable rowKey = null; int fieldIndex = 0; int start = 0; for (int i = 0; i < line.length(); i++) { if (line.charAt(i) == this.fieldDelimiter) { if (fieldIndex == 0) { // first field contains the row key String key = line.substring(start, i); rowKey = removeStringDelimiters(key); } else { // remaining fields contain values Double value = Double.valueOf( removeStringDelimiters(line.substring(start, i)) ); dataset.addValue( value, rowKey, (Comparable) columnKeys.get(fieldIndex - 1) ); } start = i + 1; fieldIndex++; } } Double value = Double.valueOf( removeStringDelimiters(line.substring(start)) ); dataset.addValue( value, rowKey, (Comparable) columnKeys.get(fieldIndex - 1) ); } /** * Removes the string delimiters from a key (as well as any white space * outside the delimiters). * * @param key the key (including delimiters). * * @return The key without delimiters. */ private String removeStringDelimiters(String key) { String k = key.trim(); if (k.charAt(0) == this.textDelimiter) { k = k.substring(1); } if (k.charAt(k.length() - 1) == this.textDelimiter) { k = k.substring(0, k.length() - 1); } return k; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/io/package.html000066400000000000000000000002341463604235500255550ustar00rootroot00000000000000 Miscellaneous support for input/output of data. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/jdbc/000077500000000000000000000000001463604235500235705ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/jdbc/JDBCCategoryDataset.java000066400000000000000000000251631463604235500301500ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * JDBCCategoryDataset.java * ------------------------ * (C) Copyright 2002-present, by Bryan Scott and Contributors. * * Original Author: Bryan Scott; Andy; * Contributor(s): David Gilbert; * Thomas Morgner; * */ package org.jfree.data.jdbc; import java.sql.Connection; import java.sql.Date; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; import java.sql.Types; import org.jfree.data.category.CategoryDataset; import org.jfree.data.category.DefaultCategoryDataset; /** * A {@link CategoryDataset} implementation over a database JDBC result set. * The dataset is populated via a call to {@link #executeQuery(String)} with * the string SQL query. The SQL query must return at least two columns. The * first column will be the category name and remaining columns values (each * column represents a series). Subsequent calls to * {@link #executeQuery(String)} will refresh the dataset. *

* The database connection is read-only and no write back facility exists. *

* NOTE: Many people have found this class too restrictive in general use. * For the greatest flexibility, please consider writing your own code to read * data from a{@code ResultSet} and populate a {@link DefaultCategoryDataset} * directly. */ public class JDBCCategoryDataset extends DefaultCategoryDataset { /** For serialization. */ static final long serialVersionUID = -3080395327918844965L; /** The database connection. */ private transient Connection connection; /** * A flag the controls whether or not the table is transposed. The default * is 'true' because this provides the behaviour described in the * documentation. */ private boolean transpose = true; /** * Creates a new dataset with a database connection. * * @param url the URL of the database connection. * @param driverName the database driver class name. * @param user the database user. * @param passwd the database user's password. * * @throws ClassNotFoundException if the driver cannot be found. * @throws SQLException if there is an error obtaining a connection to the * database. */ public JDBCCategoryDataset(String url, String driverName, String user, String passwd) throws ClassNotFoundException, SQLException { Class.forName(driverName); this.connection = DriverManager.getConnection(url, user, passwd); } /** * Create a new dataset with the given database connection. * * @param connection the database connection. */ public JDBCCategoryDataset(Connection connection) { if (connection == null) { throw new NullPointerException("A connection must be supplied."); } this.connection = connection; } /** * Creates a new dataset with the given database connection, and executes * the supplied query to populate the dataset. * * @param connection the connection. * @param query the query. * * @throws SQLException if there is a problem executing the query. */ public JDBCCategoryDataset(Connection connection, String query) throws SQLException { this(connection); executeQuery(query); } /** * Returns a flag that controls whether or not the table values are * transposed when added to the dataset. * * @return A boolean. */ public boolean getTranspose() { return this.transpose; } /** * Sets a flag that controls whether or not the table values are transposed * when added to the dataset. * * @param transpose the flag. */ public void setTranspose(boolean transpose) { this.transpose = transpose; } /** * Populates the dataset by executing the supplied query against the * existing database connection. If no connection exists then no action * is taken. *

* The results from the query are extracted and cached locally, thus * applying an upper limit on how many rows can be retrieved successfully. * * @param query the query. * * @throws SQLException if there is a problem executing the query. */ public void executeQuery(String query) throws SQLException { executeQuery(this.connection, query); } /** * Populates the dataset by executing the supplied query against the * existing database connection. If no connection exists then no action * is taken. *

* The results from the query are extracted and cached locally, thus * applying an upper limit on how many rows can be retrieved successfully. * * @param con the connection. * @param query the query. * * @throws SQLException if there is a problem executing the query. */ public void executeQuery(Connection con, String query) throws SQLException { Statement statement = null; ResultSet resultSet = null; try { statement = con.createStatement(); resultSet = statement.executeQuery(query); ResultSetMetaData metaData = resultSet.getMetaData(); int columnCount = metaData.getColumnCount(); if (columnCount < 2) { throw new SQLException( "JDBCCategoryDataset.executeQuery() : insufficient columns " + "returned from the database."); } // Remove any previous old data int i = getRowCount(); while (--i >= 0) { removeRow(i); } while (resultSet.next()) { // first column contains the row key... Comparable rowKey = resultSet.getString(1); for (int column = 2; column <= columnCount; column++) { Comparable columnKey = metaData.getColumnName(column); int columnType = metaData.getColumnType(column); switch (columnType) { case Types.TINYINT: case Types.SMALLINT: case Types.INTEGER: case Types.BIGINT: case Types.FLOAT: case Types.DOUBLE: case Types.DECIMAL: case Types.NUMERIC: case Types.REAL: { Number value = (Number) resultSet.getObject(column); if (this.transpose) { setValue(value, columnKey, rowKey); } else { setValue(value, rowKey, columnKey); } break; } case Types.DATE: case Types.TIME: case Types.TIMESTAMP: { Date date = (Date) resultSet.getObject(column); Number value = date.getTime(); if (this.transpose) { setValue(value, columnKey, rowKey); } else { setValue(value, rowKey, columnKey); } break; } case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: { String string = (String) resultSet.getObject(column); try { Number value = Double.valueOf(string); if (this.transpose) { setValue(value, columnKey, rowKey); } else { setValue(value, rowKey, columnKey); } } catch (NumberFormatException e) { // suppress (value defaults to null) } break; } default: // not a value, can't use it (defaults to null) break; } } } fireDatasetChanged(); } finally { if (resultSet != null) { try { resultSet.close(); } catch (Exception e) { // report this? } } if (statement != null) { try { statement.close(); } catch (Exception e) { // report this? } } } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/jdbc/JDBCPieDataset.java000066400000000000000000000175661463604235500271200ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * JDBCPieDataset.java * ------------------- * (C) Copyright 2002-present, by Bryan Scott and Contributors. * * Original Author: Bryan Scott; Andy * Contributor(s): David Gilbert; * Thomas Morgner; * */ package org.jfree.data.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; import java.sql.Timestamp; import java.sql.Types; import org.jfree.data.general.DefaultPieDataset; import org.jfree.data.general.PieDataset; /** * A {@link PieDataset} that reads data from a database via JDBC. *

* A query should be supplied that returns data in two columns, the first * containing VARCHAR data, and the second containing numerical data. The * data is cached in-memory and can be refreshed at any time. */ public class JDBCPieDataset extends DefaultPieDataset { /** For serialization. */ static final long serialVersionUID = -8753216855496746108L; /** The database connection. */ private transient Connection connection; /** * Creates a new JDBCPieDataset and establishes a new database connection. * * @param url the URL of the database connection. * @param driverName the database driver class name. * @param user the database user. * @param password the database users password. * * @throws ClassNotFoundException if the driver cannot be found. * @throws SQLException if there is a problem obtaining a database * connection. */ public JDBCPieDataset(String url, String driverName, String user, String password) throws SQLException, ClassNotFoundException { Class.forName(driverName); this.connection = DriverManager.getConnection(url, user, password); } /** * Creates a new JDBCPieDataset using a pre-existing database connection. *

* The dataset is initially empty, since no query has been supplied yet. * * @param con the database connection. */ public JDBCPieDataset(Connection con) { if (con == null) { throw new NullPointerException("A connection must be supplied."); } this.connection = con; } /** * Creates a new JDBCPieDataset using a pre-existing database connection. *

* The dataset is initialised with the supplied query. * * @param con the database connection. * @param query the database connection. * * @throws SQLException if there is a problem executing the query. */ public JDBCPieDataset(Connection con, String query) throws SQLException { this(con); executeQuery(query); } /** * ExecuteQuery will attempt execute the query passed to it against the * existing database connection. If no connection exists then no action * is taken. * The results from the query are extracted and cached locally, thus * applying an upper limit on how many rows can be retrieved successfully. * * @param query the query to be executed. * * @throws SQLException if there is a problem executing the query. */ public void executeQuery(String query) throws SQLException { executeQuery(this.connection, query); } /** * ExecuteQuery will attempt execute the query passed to it against the * existing database connection. If no connection exists then no action * is taken. * The results from the query are extracted and cached locally, thus * applying an upper limit on how many rows can be retrieved successfully. * * @param query the query to be executed * @param con the connection the query is to be executed against * * @throws SQLException if there is a problem executing the query. */ public void executeQuery(Connection con, String query) throws SQLException { Statement statement = null; ResultSet resultSet = null; try { statement = con.createStatement(); resultSet = statement.executeQuery(query); ResultSetMetaData metaData = resultSet.getMetaData(); int columnCount = metaData.getColumnCount(); if (columnCount != 2) { throw new SQLException( "Invalid sql generated. PieDataSet requires 2 columns only" ); } int columnType = metaData.getColumnType(2); double value; while (resultSet.next()) { Comparable key = resultSet.getString(1); switch (columnType) { case Types.NUMERIC: case Types.REAL: case Types.INTEGER: case Types.DOUBLE: case Types.FLOAT: case Types.DECIMAL: case Types.BIGINT: value = resultSet.getDouble(2); setValue(key, value); break; case Types.DATE: case Types.TIME: case Types.TIMESTAMP: Timestamp date = resultSet.getTimestamp(2); value = date.getTime(); setValue(key, value); break; default: System.err.println( "JDBCPieDataset - unknown data type"); break; } } fireDatasetChanged(); } finally { if (resultSet != null) { try { resultSet.close(); } catch (Exception e) { System.err.println("JDBCPieDataset: swallowing exception."); } } if (statement != null) { try { statement.close(); } catch (Exception e) { System.err.println("JDBCPieDataset: swallowing exception."); } } } } /** * Close the database connection */ public void close() { try { this.connection.close(); } catch (Exception e) { System.err.println("JdbcXYDataset: swallowing exception."); } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/jdbc/JDBCXYDataset.java000066400000000000000000000421421463604235500267270ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * JDBCXYDataset.java * ------------------ * (C) Copyright 2002-present, by Bryan Scott and Contributors. * * Original Author: Bryan Scott; * Contributor(s): David Gilbert; * Eric Alexander; * */ package org.jfree.data.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; import java.sql.Types; import java.util.ArrayList; import java.util.Date; import org.jfree.data.Range; import org.jfree.data.RangeInfo; import org.jfree.data.general.Dataset; import org.jfree.data.xy.AbstractXYDataset; import org.jfree.data.xy.TableXYDataset; import org.jfree.data.xy.XYDataset; /** * This class provides an {@link XYDataset} implementation over a database * JDBC result set. The dataset is populated via a call to executeQuery with * the string sql query. The sql query must return at least two columns. * The first column will be the x-axis and remaining columns y-axis values. * executeQuery can be called a number of times. * * The database connection is read-only and no write back facility exists. */ public class JDBCXYDataset extends AbstractXYDataset implements XYDataset, TableXYDataset, RangeInfo { /** The database connection. */ private transient Connection connection; /** Column names. */ private String[] columnNames = {}; /** Rows. */ private ArrayList rows; /** The maximum y value of the returned result set */ private double maxValue = 0.0; /** The minimum y value of the returned result set */ private double minValue = 0.0; /** Is this dataset a timeseries ? */ private boolean isTimeSeries = false; /** * Creates a new JDBCXYDataset (initially empty) with no database * connection. */ private JDBCXYDataset() { this.rows = new ArrayList(); } /** * Creates a new dataset (initially empty) and establishes a new database * connection. * * @param url URL of the database connection. * @param driverName the database driver class name. * @param user the database user. * @param password the database user's password. * * @throws ClassNotFoundException if the driver cannot be found. * @throws SQLException if there is a problem connecting to the database. */ public JDBCXYDataset(String url, String driverName, String user, String password) throws SQLException, ClassNotFoundException { this(); Class.forName(driverName); this.connection = DriverManager.getConnection(url, user, password); } /** * Creates a new dataset (initially empty) using the specified database * connection. * * @param con the database connection. * * @throws SQLException if there is a problem connecting to the database. */ public JDBCXYDataset(Connection con) throws SQLException { this(); this.connection = con; } /** * Creates a new dataset using the specified database connection, and * populates it using data obtained with the supplied query. * * @param con the connection. * @param query the SQL query. * * @throws SQLException if there is a problem executing the query. */ public JDBCXYDataset(Connection con, String query) throws SQLException { this(con); executeQuery(query); } /** * Returns {@code true} if the dataset represents time series data, * and {@code false} otherwise. * * @return A boolean. */ public boolean isTimeSeries() { return this.isTimeSeries; } /** * Sets a flag that indicates whether or not the data represents a time * series. * * @param timeSeries the new value of the flag. */ public void setTimeSeries(boolean timeSeries) { this.isTimeSeries = timeSeries; } /** * ExecuteQuery will attempt execute the query passed to it against the * existing database connection. If no connection exists then no action * is taken. * * The results from the query are extracted and cached locally, thus * applying an upper limit on how many rows can be retrieved successfully. * * @param query the query to be executed. * * @throws SQLException if there is a problem executing the query. */ public void executeQuery(String query) throws SQLException { executeQuery(this.connection, query); } /** * ExecuteQuery will attempt execute the query passed to it against the * provided database connection. If connection is null then no action is * taken. * * The results from the query are extracted and cached locally, thus * applying an upper limit on how many rows can be retrieved successfully. * * @param query the query to be executed. * @param con the connection the query is to be executed against. * * @throws SQLException if there is a problem executing the query. */ public void executeQuery(Connection con, String query) throws SQLException { if (con == null) { throw new SQLException( "There is no database to execute the query." ); } ResultSet resultSet = null; Statement statement = null; try { statement = con.createStatement(); resultSet = statement.executeQuery(query); ResultSetMetaData metaData = resultSet.getMetaData(); int numberOfColumns = metaData.getColumnCount(); int numberOfValidColumns = 0; int [] columnTypes = new int[numberOfColumns]; for (int column = 0; column < numberOfColumns; column++) { try { int type = metaData.getColumnType(column + 1); switch (type) { case Types.NUMERIC: case Types.REAL: case Types.INTEGER: case Types.DOUBLE: case Types.FLOAT: case Types.DECIMAL: case Types.BIT: case Types.DATE: case Types.TIME: case Types.TIMESTAMP: case Types.BIGINT: case Types.SMALLINT: ++numberOfValidColumns; columnTypes[column] = type; break; default: columnTypes[column] = Types.NULL; break; } } catch (SQLException e) { columnTypes[column] = Types.NULL; throw e; } } if (numberOfValidColumns <= 1) { throw new SQLException( "Not enough valid columns where generated by query." ); } /// First column is X data this.columnNames = new String[numberOfValidColumns - 1]; /// Get the column names and cache them. int currentColumn = 0; for (int column = 1; column < numberOfColumns; column++) { if (columnTypes[column] != Types.NULL) { this.columnNames[currentColumn] = metaData.getColumnLabel(column + 1); ++currentColumn; } } // Might need to add, to free memory from any previous result sets if (this.rows != null) { for (int column = 0; column < this.rows.size(); column++) { ArrayList row = (ArrayList) this.rows.get(column); row.clear(); } this.rows.clear(); } // Are we working with a time series. switch (columnTypes[0]) { case Types.DATE: case Types.TIME: case Types.TIMESTAMP: this.isTimeSeries = true; break; default : this.isTimeSeries = false; break; } // Get all rows. // rows = new ArrayList(); while (resultSet.next()) { ArrayList newRow = new ArrayList(); for (int column = 0; column < numberOfColumns; column++) { Object xObject = resultSet.getObject(column + 1); switch (columnTypes[column]) { case Types.NUMERIC: case Types.REAL: case Types.INTEGER: case Types.DOUBLE: case Types.FLOAT: case Types.DECIMAL: case Types.BIGINT: case Types.SMALLINT: newRow.add(xObject); break; case Types.DATE: case Types.TIME: case Types.TIMESTAMP: newRow.add(((Date) xObject).getTime()); break; case Types.NULL: break; default: System.err.println("Unknown data"); columnTypes[column] = Types.NULL; break; } } this.rows.add(newRow); } /// a kludge to make everything work when no rows returned if (this.rows.isEmpty()) { ArrayList newRow = new ArrayList(); for (int column = 0; column < numberOfColumns; column++) { if (columnTypes[column] != Types.NULL) { newRow.add(0); } } this.rows.add(newRow); } /// Determine max and min values. if (this.rows.size() < 1) { this.maxValue = 0.0; this.minValue = 0.0; } else { this.maxValue = Double.NEGATIVE_INFINITY; this.minValue = Double.POSITIVE_INFINITY; for (int rowNum = 0; rowNum < this.rows.size(); ++rowNum) { ArrayList row = (ArrayList) this.rows.get(rowNum); for (int column = 1; column < numberOfColumns; column++) { Object testValue = row.get(column); if (testValue != null) { double test = ((Number) testValue).doubleValue(); if (test < this.minValue) { this.minValue = test; } if (test > this.maxValue) { this.maxValue = test; } } } } } fireDatasetChanged(); // Tell the listeners a new table has arrived. } finally { if (resultSet != null) { try { resultSet.close(); } catch (Exception e) { // TODO: is this a good idea? } } if (statement != null) { try { statement.close(); } catch (Exception e) { // TODO: is this a good idea? } } } } /** * Returns the x-value for the specified series and item. The * implementation is responsible for ensuring that the x-values are * presented in ascending order. * * @param seriesIndex the series (zero-based index). * @param itemIndex the item (zero-based index). * * @return The x-value * * @see XYDataset */ @Override public Number getX(int seriesIndex, int itemIndex) { ArrayList row = (ArrayList) this.rows.get(itemIndex); return (Number) row.get(0); } /** * Returns the y-value for the specified series and item. * * @param seriesIndex the series (zero-based index). * @param itemIndex the item (zero-based index). * * @return The yValue value * * @see XYDataset */ @Override public Number getY(int seriesIndex, int itemIndex) { ArrayList row = (ArrayList) this.rows.get(itemIndex); return (Number) row.get(seriesIndex + 1); } /** * Returns the number of items in the specified series. * * @param seriesIndex the series (zero-based index). * * @return The itemCount value * * @see XYDataset */ @Override public int getItemCount(int seriesIndex) { return this.rows.size(); } /** * Returns the number of items in all series. This method is defined by * the {@link TableXYDataset} interface. * * @return The item count. */ @Override public int getItemCount() { return getItemCount(0); } /** * Returns the number of series in the dataset. * * @return The seriesCount value * * @see XYDataset * @see Dataset */ @Override public int getSeriesCount() { return this.columnNames.length; } /** * Returns the key for the specified series. * * @param seriesIndex the series (zero-based index). * * @return The seriesName value * * @see XYDataset * @see Dataset */ @Override public Comparable getSeriesKey(int seriesIndex) { if ((seriesIndex < this.columnNames.length) && (this.columnNames[seriesIndex] != null)) { return this.columnNames[seriesIndex]; } else { return ""; } } /** * Close the database connection */ public void close() { try { this.connection.close(); } catch (Exception e) { System.err.println("JdbcXYDataset: swallowing exception."); } } /** * Returns the minimum y-value in the dataset. * * @param includeInterval a flag that determines whether or not the * y-interval is taken into account. * * @return The minimum value. */ @Override public double getRangeLowerBound(boolean includeInterval) { return this.minValue; } /** * Returns the maximum y-value in the dataset. * * @param includeInterval a flag that determines whether or not the * y-interval is taken into account. * * @return The maximum value. */ @Override public double getRangeUpperBound(boolean includeInterval) { return this.maxValue; } /** * Returns the range of the values in this dataset's range. * * @param includeInterval a flag that determines whether or not the * y-interval is taken into account. * * @return The range. */ @Override public Range getRangeBounds(boolean includeInterval) { return new Range(this.minValue, this.maxValue); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/jdbc/package.html000066400000000000000000000002461463604235500260530ustar00rootroot00000000000000 Dataset classes that fetch data from a database via JDBC. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/json/000077500000000000000000000000001463604235500236375ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/json/JSONUtils.java000066400000000000000000000153261463604235500263030ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * JSONUtils.java * -------------- * (C) Copyright 2014-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.json; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; import java.util.Iterator; import java.util.List; import org.jfree.chart.util.Args; import org.jfree.data.KeyedValues; import org.jfree.data.KeyedValues2D; import org.jfree.data.category.CategoryDataset; import org.jfree.data.general.PieDataset; import org.jfree.data.json.impl.JSONValue; /** * A utility class that can read and write data in specific JSON formats. */ public class JSONUtils { /** * Returns a string containing the data in JSON format. The format is * an array of arrays, where each sub-array represents one data value. * The sub-array should contain two items, first the item key as a string * and second the item value as a number. For example: * {@code [["Key A", 1.0], ["Key B", 2.0]]} *

* Note that this method can be used with instances of {@link PieDataset}. * * @param data the data ({@code null} not permitted). * * @return A string in JSON format. */ public static String writeKeyedValues(KeyedValues data) { Args.nullNotPermitted(data, "data"); StringWriter sw = new StringWriter(); try { writeKeyedValues(data, sw); } catch (IOException ex) { throw new RuntimeException(ex); } return sw.toString(); } /** * Writes the data in JSON format to the supplied writer. *

* Note that this method can be used with instances of {@link PieDataset}. * * @param data the data ({@code null} not permitted). * @param writer the writer ({@code null} not permitted). * * @throws IOException if there is an I/O problem. */ public static void writeKeyedValues(KeyedValues data, Writer writer) throws IOException { Args.nullNotPermitted(data, "data"); Args.nullNotPermitted(writer, "writer"); writer.write("["); boolean first = true; Iterator iterator = data.getKeys().iterator(); while (iterator.hasNext()) { Comparable key = (Comparable) iterator.next(); if (!first) { writer.write(", "); } else { first = false; } writer.write("["); writer.write(JSONValue.toJSONString(key.toString())); writer.write(", "); writer.write(JSONValue.toJSONString(data.getValue(key))); writer.write("]"); } writer.write("]"); } /** * Returns a string containing the data in JSON format. The format is... *

* Note that this method can be used with instances of * {@link CategoryDataset}. * * @param data the data ({@code null} not permitted). * * @return A string in JSON format. */ public static String writeKeyedValues2D(KeyedValues2D data) { Args.nullNotPermitted(data, "data"); StringWriter sw = new StringWriter(); try { writeKeyedValues2D(data, sw); } catch (IOException ex) { throw new RuntimeException(ex); } return sw.toString(); } /** * Writes the data in JSON format to the supplied writer. *

* Note that this method can be used with instances of * {@link CategoryDataset}. * * @param data the data ({@code null} not permitted). * @param writer the writer ({@code null} not permitted). * * @throws IOException if there is an I/O problem. */ public static void writeKeyedValues2D(KeyedValues2D data, Writer writer) throws IOException { Args.nullNotPermitted(data, "data"); Args.nullNotPermitted(writer, "writer"); List> columnKeys = data.getColumnKeys(); List> rowKeys = data.getRowKeys(); writer.write("{"); if (!columnKeys.isEmpty()) { writer.write("\"columnKeys\": ["); boolean first = true; for (Comparable columnKey : columnKeys) { if (!first) { writer.write(", "); } else { first = false; } writer.write(JSONValue.toJSONString(columnKey.toString())); } writer.write("]"); } if (!rowKeys.isEmpty()) { writer.write(", \"rows\": ["); boolean firstRow = true; for (Comparable rowKey : rowKeys) { if (!firstRow) { writer.write(", ["); } else { writer.write("["); firstRow = false; } // write the row data writer.write(JSONValue.toJSONString(rowKey.toString())); writer.write(", ["); boolean first = true; for (Comparable columnKey : columnKeys) { if (!first) { writer.write(", "); } else { first = false; } writer.write(JSONValue.toJSONString(data.getValue(rowKey, columnKey))); } writer.write("]]"); } writer.write("]"); } writer.write("}"); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/json/impl/000077500000000000000000000000001463604235500246005ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/json/impl/JSONArray.java000066400000000000000000000107341463604235500272200ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * JSON.simple * ----------- * The code in this file originates from the JSON.simple project by * FangYidong: * * https://code.google.com/p/json-simple/ * * which is licensed under the Apache Software License version 2.0. * * It has been modified locally and repackaged under * org.jfree.data.json.impl.* to avoid conflicts with any other version that * may be present on the classpath. * */ package org.jfree.data.json.impl; import java.io.IOException; import java.io.Writer; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * A JSON array. *

* This class is for internal use by JFreeChart, it is not * part of the supported API and you should not call it directly. If you need * JSON support in your project you should include JSON.simple * (https://code.google.com/p/json-simple/) or some other JSON library directly * in your project. */ public class JSONArray extends ArrayList implements List, JSONAware, JSONStreamAware { private static final long serialVersionUID = 3957988303675231981L; /** * Encode a list into JSON text and write it to out. * If this list is also a {@link JSONStreamAware} or a {@link JSONAware}, * {@code JSONStreamAware} and {@code JSONAware} specific * behaviours will be ignored at this top level. * * @see org.jfree.data.json.impl.JSONValue#writeJSONString(Object, Writer) * * @param list the list ({@code null} permitted). * @param out the output writer ({@code null} not permitted). * * @throws IOException if there is an I/O problem. */ public static void writeJSONString(List list, Writer out) throws IOException { if (list == null) { out.write("null"); return; } boolean first = true; Iterator iter = list.iterator(); out.write('['); while (iter.hasNext()) { if (first) { first = false; } else { out.write(','); } Object value = iter.next(); if (value == null) { out.write("null"); continue; } JSONValue.writeJSONString(value, out); } out.write(']'); } /** * Writes this array to the specified output writer. * * @param out the output writer ({@code null} not permitted). * * @throws IOException if there is an I/O problem. */ @Override public void writeJSONString(Writer out) throws IOException { writeJSONString(this, out); } /** * Convert a list to JSON text. The result is a JSON array. * If this list is also a {@link JSONAware}, {@link JSONAware} specific * behaviours will be omitted at this top level. * * @see org.jfree.data.json.impl.JSONValue#toJSONString(Object) * * @param list the list ({@code null} permitted). * * @return JSON text, or "null" if list is null. */ public static String toJSONString(List list){ if (list == null) { return "null"; } boolean first = true; StringBuilder sb = new StringBuilder(); Iterator iter = list.iterator(); sb.append('['); while (iter.hasNext()) { if (first) { first = false; } else { sb.append(','); } Object value = iter.next(); if (value == null) { sb.append("null"); continue; } sb.append(JSONValue.toJSONString(value)); } sb.append(']'); return sb.toString(); } /** * Returns a JSON string representation of this list. * * @return A string. */ @Override public String toJSONString(){ return toJSONString(this); } /** * Returns a string representation of this list. * * @return A string. */ @Override public String toString() { return toJSONString(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/json/impl/JSONAware.java000066400000000000000000000025571463604235500272050ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * JSON.simple * ----------- * The code in this file originates from the JSON.simple project by * FangYidong: * * https://code.google.com/p/json-simple/ * * which is licensed under the Apache Software License version 2.0. * * It has been modified locally and repackaged under * org.jfree.data.json.impl.* to avoid conflicts with any other version that * may be present on the classpath. * */ package org.jfree.data.json.impl; /** * Classes that support customized output of JSON text shall implement this * interface. *

* This class is for internal use by JFreeChart, it is not * part of the supported API and you should not call it directly. If you need * JSON support in your project you should include JSON.simple * (https://code.google.com/p/json-simple/) or some other JSON library directly * in your project. */ public interface JSONAware { /** * Returns a JSON string representing the object. * * @return A JSON string. */ String toJSONString(); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/json/impl/JSONObject.java000066400000000000000000000132421463604235500273450ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * JSON.simple * ----------- * The code in this file originates from the JSON.simple project by * FangYidong: * * https://code.google.com/p/json-simple/ * * which is licensed under the Apache Software License version 2.0. * * It has been modified locally and repackaged under * org.jfree.data.json.impl.* to avoid conflicts with any other version that * may be present on the classpath. * */ package org.jfree.data.json.impl; import java.io.IOException; import java.io.Writer; import java.util.HashMap; import java.util.Iterator; import java.util.Map; /** * A JSON object. Key value pairs are unordered. *

* This class is for internal use by JFreeChart, it is not * part of the supported API and you should not call it directly. If you need * JSON support in your project you should include JSON.simple * (https://code.google.com/p/json-simple/) or some other JSON library directly * in your project. */ public class JSONObject extends HashMap implements Map, JSONAware, JSONStreamAware { private static final long serialVersionUID = -503443796854799292L; /** * Encode a map into JSON text and write it to out. * If this map is also a {@link JSONAware} or {@link JSONStreamAware}, * {@code JSONAware} or {@code JSONStreamAware} specific * behaviours will be ignored at this top level. * * @see org.jfree.data.json.impl.JSONValue#writeJSONString(Object, Writer) * * @param map the map to write ({@code null} permitted). * @param out the output writer ({@code null} not permitted). * * @throws IOException if there is an I/O problem. */ public static void writeJSONString(Map map, Writer out) throws IOException { if (map == null) { out.write("null"); return; } boolean first = true; Iterator iter = map.entrySet().iterator(); out.write('{'); while (iter.hasNext()) { if (first) { first = false; } else { out.write(','); } Map.Entry entry = (Map.Entry) iter.next(); out.write('\"'); out.write(JSONValue.escape(String.valueOf(entry.getKey()))); out.write('\"'); out.write(':'); JSONValue.writeJSONString(entry.getValue(), out); } out.write('}'); } /** * Writes a JSON string representing this object instance to the specified * output writer. * * @param out the output writer ({@code null} not permitted). * * @throws IOException if there is an I/O problem. */ @Override public void writeJSONString(Writer out) throws IOException { writeJSONString(this, out); } /** * Convert a map to JSON text. The result is a JSON object. * If this map is also a {@link JSONAware}, {@code JSONAware} specific * behaviours will be omitted at this top level. * * @see org.jfree.data.json.impl.JSONValue#toJSONString(Object) * * @param map the map ({@code null} permitted). * * @return JSON text, or "null" if map is null. */ public static String toJSONString(Map map){ if (map == null) { return "null"; } StringBuffer sb = new StringBuffer(); boolean first = true; Iterator iter = map.entrySet().iterator(); sb.append('{'); while (iter.hasNext()) { if (first) { first = false; } else { sb.append(','); } Map.Entry entry = (Map.Entry) iter.next(); toJSONString(String.valueOf(entry.getKey()), entry.getValue(), sb); } sb.append('}'); return sb.toString(); } /** * Returns a JSON string representing this object. * * @return A JSON string. */ @Override public String toJSONString(){ return toJSONString(this); } /** * Writes a key and value to a JSON string. * * @param key the key ({@code null} permitted). * @param value the value ({@code null} permitted). * @param sb a string buffer ({@code null} not permitted). * * @return A JSON string fragment representing the key and value. */ private static String toJSONString(String key, Object value, StringBuffer sb) { sb.append('\"'); if (key == null) { sb.append("null"); } else { JSONValue.escape(key, sb); } sb.append('\"').append(':'); sb.append(JSONValue.toJSONString(value)); return sb.toString(); } /** * Returns a string representation of this object. * * @return A string. */ @Override public String toString(){ return toJSONString(); } /** * Returns a JSON string fragment containing the key and value. * * @param key the key ({@code null} permitted). * @param value the value ({@code null} permitted). * * @return A JSON string fragment. */ public static String toString(String key, Object value){ StringBuffer sb = new StringBuffer(); toJSONString(key, value, sb); return sb.toString(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/json/impl/JSONStreamAware.java000066400000000000000000000030751463604235500303550ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * JSON.simple * ----------- * The code in this file originates from the JSON.simple project by * FangYidong: * * https://code.google.com/p/json-simple/ * * which is licensed under the Apache Software License version 2.0. * * It has been modified locally and repackaged under * org.jfree.data.json.impl.* to avoid conflicts with any other version that * may be present on the classpath. * */ package org.jfree.data.json.impl; import java.io.IOException; import java.io.Writer; /** * Beans that support customized output of JSON text to a writer shall * implement this interface. * @author FangYidong<fangyidong@yahoo.com.cn> *

* This class is for internal use by JFreeChart, it is not * part of the supported API and you should not call it directly. If you need * JSON support in your project you should include JSON.simple * (https://code.google.com/p/json-simple/) or some other JSON library directly * in your project. */ public interface JSONStreamAware { /** * write JSON string to out. * * @param out the output writer. * * @throws IOException if there is an I/O problem. */ void writeJSONString(Writer out) throws IOException; } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/json/impl/JSONValue.java000066400000000000000000000242031463604235500272120ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * JSON.simple * ----------- * The code in this file originates from the JSON.simple project by * FangYidong: * * https://code.google.com/p/json-simple/ * * which is licensed under the Apache Software License version 2.0. * * It has been modified locally and repackaged under * org.jfree.data.json.impl.* to avoid conflicts with any other version that * may be present on the classpath. * */ package org.jfree.data.json.impl; import java.io.IOException; import java.io.Writer; import java.util.List; import java.util.Map; /** * Utility methods for JSON values. *

* This class is for internal use by JFreeChart, it is not * part of the supported API and you should not call it directly. If you need * JSON support in your project you should include JSON.simple * (https://code.google.com/p/json-simple/) or some other JSON library directly * in your project. */ public class JSONValue { // /** // * Parse JSON text into java object from the input source. // * Please use parseWithException() if you don't want to ignore the // * exception. // * // * @see com.orsoncharts.util.json.parser.JSONParser#parse(Reader) // * @see #parseWithException(Reader) // * // * @param in the input reader. // * @return Instance of the following: // * com.orsoncharts.util.json.JSONObject, // * com.orsoncharts.util.json.JSONArray, // * java.lang.String, // * java.lang.Number, // * java.lang.Boolean, // * null // */ // public static Object parse(Reader in){ // try { // JSONParser parser = new JSONParser(); // return parser.parse(in); // } // catch (Exception e) { // return null; // } // } // // /** // * Parses an object from a string. // * // * @param s the string. // * // * @return An object. // */ // public static Object parse(String s){ // StringReader in = new StringReader(s); // return parse(in); // } // // /** // * Parse JSON text into java object from the input source. // * // * @see com.orsoncharts.util.json.parser.JSONParser // * // * @param in the input reader ({@code null} not permitted). // * // * @return Instance of the following: // * com.orsoncharts.util.json.JSONObject, // * com.orsoncharts.util.json.JSONArray, // * java.lang.String, // * java.lang.Number, // * java.lang.Boolean, // * null // * // * @throws IOException if there is an I/O problem. // * @throws ParseException if there is a parsing problem. // */ // public static Object parseWithException(Reader in) throws IOException, // ParseException{ // JSONParser parser = new JSONParser(); // return parser.parse(in); // } // // /** // * Parses an object from a JSON string. // * // * @param s the string. // * // * @return An object. // * // * @throws ParseException if there is a parsing problem. // */ // public static Object parseWithException(String s) throws ParseException{ // JSONParser parser = new JSONParser(); // return parser.parse(s); // } /** * Encode an object into JSON text and write it to out. *

* If this object is a {@code Map} or a {@code List}, and it's * also a {@link JSONStreamAware} or a {@link JSONAware}, * {@code JSONStreamAware} or {@code JSONAware} will be * considered firstly. *

* DO NOT call this method from writeJSONString(Writer) of a class that * implements both JSONStreamAware and (Map or List) with * "this" as the first parameter, use JSONObject.writeJSONString(Map, * Writer) or JSONArray.writeJSONString(List, Writer) instead. * * @see org.jfree.data.json.impl.JSONObject#writeJSONString(Map, Writer) * @see org.jfree.data.json.impl.JSONArray#writeJSONString(List, Writer) * * @param value the value. * @param out the output writer. * @throws IOException if there is an I/O problem. */ public static void writeJSONString(Object value, Writer out) throws IOException { if (value == null) { out.write("null"); return; } if (value instanceof String) { out.write('\"'); out.write(escape((String) value)); out.write('\"'); return; } if (value instanceof Double) { if(((Double) value).isInfinite() || ((Double) value).isNaN()) { out.write("null"); } else { out.write(value.toString()); } return; } if (value instanceof Float) { if (((Float) value).isInfinite() || ((Float) value).isNaN()) { out.write("null"); } else { out.write(value.toString()); } return; } if (value instanceof Number) { out.write(value.toString()); return; } if (value instanceof Boolean) { out.write(value.toString()); return; } if ((value instanceof JSONStreamAware)) { ((JSONStreamAware) value).writeJSONString(out); return; } if ((value instanceof JSONAware)) { out.write(((JSONAware) value).toJSONString()); return; } if (value instanceof Map) { JSONObject.writeJSONString((Map) value, out); return; } if (value instanceof List) { JSONArray.writeJSONString((List) value, out); return; } out.write(value.toString()); } /** * Convert an object to JSON text. *

* If this object is a Map or a List, and it's also a JSONAware, JSONAware * will be considered firstly. *

* DO NOT call this method from toJSONString() of a class that implements * both JSONAware and Map or List with * "this" as the parameter, use JSONObject.toJSONString(Map) or * JSONArray.toJSONString(List) instead. * * @see org.jfree.data.json.impl.JSONObject#toJSONString(Map) * @see org.jfree.data.json.impl.JSONArray#toJSONString(List) * * @param value the value. * @return JSON text, or "null" if value is null or it's an NaN or an INF * number. */ public static String toJSONString(Object value){ if (value == null) { return "null"; } if (value instanceof String) { return "\"" + escape((String) value) + "\""; } if (value instanceof Double){ if(((Double) value).isInfinite() || ((Double) value).isNaN()) { return "null"; } else { return value.toString(); } } if (value instanceof Float) { if (((Float) value).isInfinite() || ((Float) value).isNaN()) { return "null"; } else { return value.toString(); } } if (value instanceof Number) { return value.toString(); } if (value instanceof Boolean) { return value.toString(); } if ((value instanceof JSONAware)) { return ((JSONAware) value).toJSONString(); } if (value instanceof Map) { return JSONObject.toJSONString((Map) value); } if (value instanceof List) { return JSONArray.toJSONString((List) value); } return value.toString(); } /** * Escape quotes, \, /, \r, \n, \b, \f, \t and other control characters * (U+0000 through U+001F). * * @param s the string to be escaped ({@code null} permitted). * * @return A string. */ public static String escape(String s) { if (s == null) { return null; } StringBuffer sb = new StringBuffer(); escape(s, sb); return sb.toString(); } /** * @param s - Must not be null. * @param sb */ static void escape(String s, StringBuffer sb) { for(int i = 0; i < s.length(); i++) { char ch = s.charAt(i); switch(ch){ case '"': sb.append("\\\""); break; case '\\': sb.append("\\\\"); break; case '\b': sb.append("\\b"); break; case '\f': sb.append("\\f"); break; case '\n': sb.append("\\n"); break; case '\r': sb.append("\\r"); break; case '\t': sb.append("\\t"); break; case '/': sb.append("\\/"); break; default: //Reference: http://www.unicode.org/versions/Unicode5.1.0/ if ((ch >= '\u0000' && ch <= '\u001F') || (ch >= '\u007F' && ch <= '\u009F') || (ch >= '\u2000' && ch <= '\u20FF')) { String ss = Integer.toHexString(ch); sb.append("\\u"); for (int k = 0; k < 4 - ss.length(); k++) { sb.append('0'); } sb.append(ss.toUpperCase()); } else { sb.append(ch); } } }//for } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/json/impl/package-info.java000066400000000000000000000001361463604235500277670ustar00rootroot00000000000000/** * Utility classes for JSON, for internal use only. */ package org.jfree.data.json.impl; jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/json/package-info.java000066400000000000000000000001401463604235500270210ustar00rootroot00000000000000/** * Utilities for reading/writing data to/from JSON format. */ package org.jfree.data.json; jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/package.html000066400000000000000000000002571463604235500251530ustar00rootroot00000000000000 The base package for classes that represent various types of data. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/resources/000077500000000000000000000000001463604235500247005ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/resources/DataPackageResources.java000066400000000000000000000041141463604235500315630ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * DataPackageResources.java * ------------------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.resources; import java.util.ListResourceBundle; /** * A resource bundle that stores all the items that might need localisation. * */ public class DataPackageResources extends ListResourceBundle { /** * Returns the array of strings in the resource bundle. * * @return The localised resources. */ @Override public Object[][] getContents() { return CONTENTS; } /** The resources to be localised. */ private static final Object[][] CONTENTS = { {"series.default-prefix", "Series"}, {"categories.default-prefix", "Category"}, }; } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/resources/DataPackageResources_de.java000066400000000000000000000041621463604235500322360ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------- * DataPackageResources_de.java * ---------------------------- * (C) Copyright 2002-present, by David Gilbert and Contributors. * * Original Author: Thomas Meier; * Contributor(s): David Gilbert; * */ package org.jfree.data.resources; import java.util.ListResourceBundle; /** * A resource bundle that stores all the items that might need localisation. */ public class DataPackageResources_de extends ListResourceBundle { /** * Returns the array of strings in the resource bundle. * * @return The localised resources. */ @Override public Object[][] getContents() { return CONTENTS; } /** The resources to be localised. */ private static final Object[][] CONTENTS = { {"series.default-prefix", "Reihen"}, {"categories.default-prefix", "Kategorien"}, }; } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/resources/DataPackageResources_es.java000066400000000000000000000041721463604235500322560ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------- * DataPackageResources_es.java * ---------------------------- * (C) Copyright 2002-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Hans-Jurgen Greiner; * */ package org.jfree.data.resources; import java.util.ListResourceBundle; /** * A resource bundle that stores all the items that might need localisation. */ public class DataPackageResources_es extends ListResourceBundle { /** * Returns the array of strings in the resource bundle. * * @return The localised resources. */ @Override public Object[][] getContents() { return CONTENTS; } /** The resources to be localised. */ private static final Object[][] CONTENTS = { {"series.default-prefix", "Series"}, {"categories.default-prefix", "Categor?a"}, }; } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/resources/DataPackageResources_fr.java000066400000000000000000000040421463604235500322520ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------- * DataPackageResources_fr.java * ---------------------------- * * Original Author: Anthony Boulestreau; * Contributor(s): -; * */ package org.jfree.data.resources; import java.util.ListResourceBundle; /** * A resource bundle that stores all the items that might need localisation. */ public class DataPackageResources_fr extends ListResourceBundle { /** * Returns the array of strings in the resource bundle. * * @return The localised resources. */ @Override public Object[][] getContents() { return CONTENTS; } /** The resources to be localised. */ private static final Object[][] CONTENTS = { {"series.default-prefix", "S?ries"}, {"categories.default-prefix", "Cat?gorie"}, }; } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/resources/DataPackageResources_pl.java000066400000000000000000000042241463604235500322600ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------- * DataPackageResources_pl.java * ---------------------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * Polish translation: Krzysztof Pa? (kpaz@samorzad.pw.edu.pl) * */ package org.jfree.data.resources; import java.util.ListResourceBundle; /** * A resource bundle that stores all the items that might need localisation. */ public class DataPackageResources_pl extends ListResourceBundle { /** * Returns the array of strings in the resource bundle. * * @return The localised resources. */ @Override public Object[][] getContents() { return CONTENTS; } /** The resources to be localised. */ private static final Object[][] CONTENTS = { {"series.default-prefix", "Serie"}, {"categories.default-prefix", "Kategorie"}, }; } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/resources/DataPackageResources_ru.java000066400000000000000000000040371463604235500322750ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------- * DataPackageResources_ru.java * ---------------------------- * * Original Author: Sergey Bondarenko; * Contributor(s): -; * */ package org.jfree.data.resources; import java.util.ListResourceBundle; /** * A resource bundle that stores all the items that might need localisation. */ public class DataPackageResources_ru extends ListResourceBundle { /** * Returns the array of strings in the resource bundle. * * @return The localised resources. */ @Override public Object[][] getContents() { return CONTENTS; } /** The resources to be localised. */ private static final Object[][] CONTENTS = { {"series.default-prefix", "?????"}, {"categories.default-prefix", "?????????"}, }; } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/resources/package.html000066400000000000000000000002421463604235500271570ustar00rootroot00000000000000 Resource bundles for items that require localisation. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/statistics/000077500000000000000000000000001463604235500250605ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/statistics/BoxAndWhiskerCalculator.java000066400000000000000000000201461463604235500324500ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------- * BoxAndWhiskerCalculator.java * ---------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.statistics; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.jfree.chart.util.Args; /** * A utility class that calculates the mean, median, quartiles Q1 and Q3, plus * a list of outlier values...all from an arbitrary list of * {@code Number} objects. */ public abstract class BoxAndWhiskerCalculator { /** * Calculates the statistics required for a {@link BoxAndWhiskerItem} * from a list of {@code Number} objects. Any items in the list * that are {@code null}, not an instance of {@code Number}, or * equivalent to {@code Double.NaN}, will be ignored. * * @param values a list of numbers (a {@code null} list is not * permitted). * * @return A box-and-whisker item. */ public static BoxAndWhiskerItem calculateBoxAndWhiskerStatistics( List values) { return calculateBoxAndWhiskerStatistics(values, true); } /** * Calculates the statistics required for a {@link BoxAndWhiskerItem} * from a list of {@code Number} objects. Any items in the list * that are {@code null}, not an instance of {@code Number}, or * equivalent to {@code Double.NaN}, will be ignored. * * @param values a list of numbers (a {@code null} list is not * permitted). * @param stripNullAndNaNItems a flag that controls the handling of null * and NaN items. * * @return A box-and-whisker item. */ public static BoxAndWhiskerItem calculateBoxAndWhiskerStatistics( List values, boolean stripNullAndNaNItems) { Args.nullNotPermitted(values, "values"); List vlist; if (stripNullAndNaNItems) { vlist = new ArrayList(values.size()); Iterator iterator = values.listIterator(); while (iterator.hasNext()) { Object obj = iterator.next(); if (obj instanceof Number) { Number n = (Number) obj; double v = n.doubleValue(); if (!Double.isNaN(v)) { vlist.add(n); } } } } else { vlist = values; } Collections.sort(vlist); double mean = Statistics.calculateMean(vlist, false); double median = Statistics.calculateMedian(vlist, false); double q1 = calculateQ1(vlist); double q3 = calculateQ3(vlist); double interQuartileRange = q3 - q1; double upperOutlierThreshold = q3 + (interQuartileRange * 1.5); double lowerOutlierThreshold = q1 - (interQuartileRange * 1.5); double upperFaroutThreshold = q3 + (interQuartileRange * 2.0); double lowerFaroutThreshold = q1 - (interQuartileRange * 2.0); double minRegularValue = Double.POSITIVE_INFINITY; double maxRegularValue = Double.NEGATIVE_INFINITY; double minOutlier = Double.POSITIVE_INFINITY; double maxOutlier = Double.NEGATIVE_INFINITY; List outliers = new ArrayList(); Iterator iterator = vlist.iterator(); while (iterator.hasNext()) { Number number = (Number) iterator.next(); double value = number.doubleValue(); if (value > upperOutlierThreshold) { outliers.add(number); if (value > maxOutlier && value <= upperFaroutThreshold) { maxOutlier = value; } } else if (value < lowerOutlierThreshold) { outliers.add(number); if (value < minOutlier && value >= lowerFaroutThreshold) { minOutlier = value; } } else { minRegularValue = Math.min(minRegularValue, value); maxRegularValue = Math.max(maxRegularValue, value); } minOutlier = Math.min(minOutlier, minRegularValue); maxOutlier = Math.max(maxOutlier, maxRegularValue); } return new BoxAndWhiskerItem(mean, median, q1, q3, minRegularValue, maxRegularValue, minOutlier, maxOutlier, outliers); } /** * Calculates the first quartile for a list of numbers in ascending order. * If the items in the list are not in ascending order, the result is * unspecified. If the list contains items that are {@code null}, not * an instance of {@code Number}, or equivalent to * {@code Double.NaN}, the result is unspecified. * * @param values the numbers in ascending order ({@code null} not * permitted). * * @return The first quartile. */ public static double calculateQ1(List values) { Args.nullNotPermitted(values, "values"); double result = Double.NaN; int count = values.size(); if (count > 0) { if (count % 2 == 1) { if (count > 1) { result = Statistics.calculateMedian(values, 0, count / 2); } else { result = Statistics.calculateMedian(values, 0, 0); } } else { result = Statistics.calculateMedian(values, 0, count / 2 - 1); } } return result; } /** * Calculates the third quartile for a list of numbers in ascending order. * If the items in the list are not in ascending order, the result is * unspecified. If the list contains items that are {@code null}, not * an instance of {@code Number}, or equivalent to * {@code Double.NaN}, the result is unspecified. * * @param values the list of values ({@code null} not permitted). * * @return The third quartile. */ public static double calculateQ3(List values) { Args.nullNotPermitted(values, "values"); double result = Double.NaN; int count = values.size(); if (count > 0) { if (count % 2 == 1) { if (count > 1) { result = Statistics.calculateMedian(values, count / 2, count - 1); } else { result = Statistics.calculateMedian(values, 0, 0); } } else { result = Statistics.calculateMedian(values, count / 2, count - 1); } } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/statistics/BoxAndWhiskerCategoryDataset.java000066400000000000000000000155521463604235500334470ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------------- * BoxAndWhiskerCategoryDataset.java * --------------------------------- * (C) Copyright 2003-present, by David Browning and Contributors. * * Original Author: David Browning (for Australian Institute of Marine * Science); * Contributor(s): -; * */ package org.jfree.data.statistics; import java.util.List; import org.jfree.data.category.CategoryDataset; /** * A category dataset that defines various medians, outliers and an average * value for each item. */ public interface BoxAndWhiskerCategoryDataset extends CategoryDataset { /** * Returns the mean value for an item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The mean value. */ Number getMeanValue(int row, int column); /** * Returns the average value for an item. * * @param rowKey the row key. * @param columnKey the columnKey. * * @return The average value. */ Number getMeanValue(Comparable rowKey, Comparable columnKey); /** * Returns the median value for an item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The median value. */ Number getMedianValue(int row, int column); /** * Returns the median value for an item. * * @param rowKey the row key. * @param columnKey the columnKey. * * @return The median value. */ Number getMedianValue(Comparable rowKey, Comparable columnKey); /** * Returns the q1median value for an item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The q1median value. */ Number getQ1Value(int row, int column); /** * Returns the q1median value for an item. * * @param rowKey the row key. * @param columnKey the columnKey. * * @return The q1median value. */ Number getQ1Value(Comparable rowKey, Comparable columnKey); /** * Returns the q3median value for an item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The q3median value. */ Number getQ3Value(int row, int column); /** * Returns the q3median value for an item. * * @param rowKey the row key. * @param columnKey the columnKey. * * @return The q3median value. */ Number getQ3Value(Comparable rowKey, Comparable columnKey); /** * Returns the minimum regular (non-outlier) value for an item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The minimum regular value. */ Number getMinRegularValue(int row, int column); /** * Returns the minimum regular (non-outlier) value for an item. * * @param rowKey the row key. * @param columnKey the columnKey. * * @return The minimum regular value. */ Number getMinRegularValue(Comparable rowKey, Comparable columnKey); /** * Returns the maximum regular (non-outlier) value for an item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The maximum regular value. */ Number getMaxRegularValue(int row, int column); /** * Returns the maximum regular (non-outlier) value for an item. * * @param rowKey the row key. * @param columnKey the columnKey. * * @return The maximum regular value. */ Number getMaxRegularValue(Comparable rowKey, Comparable columnKey); /** * Returns the minimum outlier (non-farout) for an item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The minimum outlier. */ Number getMinOutlier(int row, int column); /** * Returns the minimum outlier (non-farout) for an item. * * @param rowKey the row key. * @param columnKey the columnKey. * * @return The minimum outlier. */ Number getMinOutlier(Comparable rowKey, Comparable columnKey); /** * Returns the maximum outlier (non-farout) for an item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The maximum outlier. */ Number getMaxOutlier(int row, int column); /** * Returns the maximum outlier (non-farout) for an item. * * @param rowKey the row key. * @param columnKey the columnKey. * * @return The maximum outlier. */ Number getMaxOutlier(Comparable rowKey, Comparable columnKey); /** * Returns a list of outlier values for an item. The list may be empty, * but should never be {@code null}. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return A list of outliers for an item. */ List getOutliers(int row, int column); /** * Returns a list of outlier values for an item. The list may be empty, * but should never be {@code null}. * * @param rowKey the row key. * @param columnKey the columnKey. * * @return A list of outlier values for an item. */ List getOutliers(Comparable rowKey, Comparable columnKey); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/statistics/BoxAndWhiskerItem.java000066400000000000000000000206421463604235500312560ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * BoxAndWhiskerItem.java * ---------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.statistics; import java.io.Serializable; import java.util.Collections; import java.util.List; import java.util.Objects; /** * Represents one data item within a box-and-whisker dataset. Instances of * this class are immutable. */ public class BoxAndWhiskerItem implements Serializable { /** For serialization. */ private static final long serialVersionUID = 7329649623148167423L; /** The mean. */ private final Number mean; /** The median. */ private final Number median; /** The first quarter. */ private final Number q1; /** The third quarter. */ private final Number q3; /** The minimum regular value. */ private final Number minRegularValue; /** The maximum regular value. */ private final Number maxRegularValue; /** The minimum outlier. */ private final Number minOutlier; /** The maximum outlier. */ private final Number maxOutlier; /** The outliers. */ private final List outliers; /** * Creates a new box-and-whisker item. * * @param mean the mean ({@code null} permitted). * @param median the median ({@code null} permitted). * @param q1 the first quartile ({@code null} permitted). * @param q3 the third quartile ({@code null} permitted). * @param minRegularValue the minimum regular value ({@code null} * permitted). * @param maxRegularValue the maximum regular value ({@code null} * permitted). * @param minOutlier the minimum outlier ({@code null} permitted). * @param maxOutlier the maximum outlier ({@code null} permitted). * @param outliers the outliers ({@code null} permitted). */ public BoxAndWhiskerItem(Number mean, Number median, Number q1, Number q3, Number minRegularValue, Number maxRegularValue, Number minOutlier, Number maxOutlier, List outliers) { this.mean = mean; this.median = median; this.q1 = q1; this.q3 = q3; this.minRegularValue = minRegularValue; this.maxRegularValue = maxRegularValue; this.minOutlier = minOutlier; this.maxOutlier = maxOutlier; this.outliers = outliers; } /** * Creates a new box-and-whisker item. * * @param mean the mean. * @param median the median * @param q1 the first quartile. * @param q3 the third quartile. * @param minRegularValue the minimum regular value. * @param maxRegularValue the maximum regular value. * @param minOutlier the minimum outlier value. * @param maxOutlier the maximum outlier value. * @param outliers a list of the outliers. */ public BoxAndWhiskerItem(double mean, double median, double q1, double q3, double minRegularValue, double maxRegularValue, double minOutlier, double maxOutlier, List outliers) { // pass values to other constructor this(Double.valueOf(mean), Double.valueOf(median), Double.valueOf(q1), Double.valueOf(q3), Double.valueOf(minRegularValue), Double.valueOf(maxRegularValue), Double.valueOf(minOutlier), Double.valueOf(maxOutlier), outliers); } /** * Returns the mean. * * @return The mean (possibly {@code null}). */ public Number getMean() { return this.mean; } /** * Returns the median. * * @return The median (possibly {@code null}). */ public Number getMedian() { return this.median; } /** * Returns the first quartile. * * @return The first quartile (possibly {@code null}). */ public Number getQ1() { return this.q1; } /** * Returns the third quartile. * * @return The third quartile (possibly {@code null}). */ public Number getQ3() { return this.q3; } /** * Returns the minimum regular value. * * @return The minimum regular value (possibly {@code null}). */ public Number getMinRegularValue() { return this.minRegularValue; } /** * Returns the maximum regular value. * * @return The maximum regular value (possibly {@code null}). */ public Number getMaxRegularValue() { return this.maxRegularValue; } /** * Returns the minimum outlier. * * @return The minimum outlier (possibly {@code null}). */ public Number getMinOutlier() { return this.minOutlier; } /** * Returns the maximum outlier. * * @return The maximum outlier (possibly {@code null}). */ public Number getMaxOutlier() { return this.maxOutlier; } /** * Returns a list of outliers. * * @return A list of outliers (possibly {@code null}). */ public List getOutliers() { if (this.outliers == null) { return null; } return Collections.unmodifiableList(this.outliers); } /** * Returns a string representation of this instance, primarily for * debugging purposes. * * @return A string representation of this instance. */ @Override public String toString() { return super.toString() + "[mean=" + this.mean + ",median=" + this.median + ",q1=" + this.q1 + ",q3=" + this.q3 + "]"; } /** * Tests this object for equality with an arbitrary object. * * @param obj the object to test against ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof BoxAndWhiskerItem)) { return false; } BoxAndWhiskerItem that = (BoxAndWhiskerItem) obj; if (!Objects.equals(this.mean, that.mean)) { return false; } if (!Objects.equals(this.median, that.median)) { return false; } if (!Objects.equals(this.q1, that.q1)) { return false; } if (!Objects.equals(this.q3, that.q3)) { return false; } if (!Objects.equals(this.minRegularValue, that.minRegularValue)) { return false; } if (!Objects.equals(this.maxRegularValue, that.maxRegularValue)) { return false; } if (!Objects.equals(this.minOutlier, that.minOutlier)) { return false; } if (!Objects.equals(this.maxOutlier, that.maxOutlier)) { return false; } if (!Objects.equals(this.outliers, that.outliers)) { return false; } return true; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/statistics/BoxAndWhiskerXYDataset.java000066400000000000000000000132161463604235500322250ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * BoxAndWhiskerXYDataset.java * --------------------------- * (C) Copyright 2003-present, by David Browning and Contributors. * * Original Author: David Browning (for Australian Institute of Marine * Science); * Contributor(s): David Gilbert; * */ package org.jfree.data.statistics; import java.util.List; import org.jfree.data.xy.XYDataset; /** * An interface that defines data in the form of (x, max, min, average, median) * tuples. *

* Example: JFreeChart uses this interface to obtain data for AIMS * max-min-average-median plots. */ public interface BoxAndWhiskerXYDataset extends XYDataset { /** * Returns the mean for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The mean for the specified series and item. */ Number getMeanValue(int series, int item); /** * Returns the median-value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The median-value for the specified series and item. */ Number getMedianValue(int series, int item); /** * Returns the Q1 median-value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The Q1 median-value for the specified series and item. */ Number getQ1Value(int series, int item); /** * Returns the Q3 median-value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The Q3 median-value for the specified series and item. */ Number getQ3Value(int series, int item); /** * Returns the min-value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The min-value for the specified series and item. */ Number getMinRegularValue(int series, int item); /** * Returns the max-value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The max-value for the specified series and item. */ Number getMaxRegularValue(int series, int item); /** * Returns the minimum value which is not a farout. * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return A {@code Number} representing the maximum non-farout value. */ Number getMinOutlier(int series, int item); /** * Returns the maximum value which is not a farout, ie Q3 + (interquartile * range * farout coefficient). * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return A {@code Number} representing the maximum non-farout value. */ Number getMaxOutlier(int series, int item); /** * Returns a list of outliers for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The list of outliers for the specified series and item * (possibly {@code null}). */ List getOutliers(int series, int item); /** * Returns the value used as the outlier coefficient. The outlier * coefficient gives an indication of the degree of certainty in an * unskewed distribution. Increasing the coefficient increases the number * of values included. Currently only used to ensure farout coefficient * is greater than the outlier coefficient * * @return A {@code double} representing the value used to calculate * outliers */ double getOutlierCoefficient(); /** * Returns the value used as the farout coefficient. The farout coefficient * allows the calculation of which values will be off the graph. * * @return A {@code double} representing the value used to calculate * farouts */ double getFaroutCoefficient(); } DefaultBoxAndWhiskerCategoryDataset.java000066400000000000000000000657431463604235500347040ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/statistics/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------------------- * DefaultBoxAndWhiskerCategoryDataset.java * ---------------------------------------- * (C) Copyright 2003-present, by David Browning and Contributors. * * Original Author: David Browning (for Australian Institute of Marine * Science); * Contributor(s): David Gilbert; * */ package org.jfree.data.statistics; import java.util.List; import java.util.Objects; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.KeyedObjects2D; import org.jfree.data.Range; import org.jfree.data.RangeInfo; import org.jfree.data.general.AbstractDataset; import org.jfree.data.general.DatasetChangeEvent; /** * A convenience class that provides a default implementation of the * {@link BoxAndWhiskerCategoryDataset} interface. */ public class DefaultBoxAndWhiskerCategoryDataset extends AbstractDataset implements BoxAndWhiskerCategoryDataset, RangeInfo, PublicCloneable { /** Storage for the data. */ protected KeyedObjects2D data; /** The minimum range value. */ private double minimumRangeValue; /** The row index for the cell that the minimum range value comes from. */ private int minimumRangeValueRow; /** * The column index for the cell that the minimum range value comes from. */ private int minimumRangeValueColumn; /** The maximum range value. */ private double maximumRangeValue; /** The row index for the cell that the maximum range value comes from. */ private int maximumRangeValueRow; /** * The column index for the cell that the maximum range value comes from. */ private int maximumRangeValueColumn; /** * Creates a new dataset. */ public DefaultBoxAndWhiskerCategoryDataset() { this.data = new KeyedObjects2D(); this.minimumRangeValue = Double.NaN; this.minimumRangeValueRow = -1; this.minimumRangeValueColumn = -1; this.maximumRangeValue = Double.NaN; this.maximumRangeValueRow = -1; this.maximumRangeValueColumn = -1; } /** * Adds a list of values relating to one box-and-whisker entity to the * table. The various median values are calculated. * * @param list a collection of values from which the various medians will * be calculated. * @param rowKey the row key ({@code null} not permitted). * @param columnKey the column key ({@code null} not permitted). * * @see #add(BoxAndWhiskerItem, Comparable, Comparable) */ public void add(List list, Comparable rowKey, Comparable columnKey) { BoxAndWhiskerItem item = BoxAndWhiskerCalculator .calculateBoxAndWhiskerStatistics(list); add(item, rowKey, columnKey); } /** * Adds a list of values relating to one Box and Whisker entity to the * table. The various median values are calculated. * * @param item a box and whisker item ({@code null} not permitted). * @param rowKey the row key ({@code null} not permitted). * @param columnKey the column key ({@code null} not permitted). * * @see #add(List, Comparable, Comparable) */ public void add(BoxAndWhiskerItem item, Comparable rowKey, Comparable columnKey) { this.data.addObject(item, rowKey, columnKey); // update cached min and max values int r = this.data.getRowIndex(rowKey); int c = this.data.getColumnIndex(columnKey); if ((this.maximumRangeValueRow == r && this.maximumRangeValueColumn == c) || (this.minimumRangeValueRow == r && this.minimumRangeValueColumn == c)) { updateBounds(); } else { double minval = Double.NaN; if (item.getMinOutlier() != null) { minval = item.getMinOutlier().doubleValue(); } double maxval = Double.NaN; if (item.getMaxOutlier() != null) { maxval = item.getMaxOutlier().doubleValue(); } if (Double.isNaN(this.maximumRangeValue)) { this.maximumRangeValue = maxval; this.maximumRangeValueRow = r; this.maximumRangeValueColumn = c; } else if (maxval > this.maximumRangeValue) { this.maximumRangeValue = maxval; this.maximumRangeValueRow = r; this.maximumRangeValueColumn = c; } if (Double.isNaN(this.minimumRangeValue)) { this.minimumRangeValue = minval; this.minimumRangeValueRow = r; this.minimumRangeValueColumn = c; } else if (minval < this.minimumRangeValue) { this.minimumRangeValue = minval; this.minimumRangeValueRow = r; this.minimumRangeValueColumn = c; } } fireDatasetChanged(); } /** * Removes an item from the dataset and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param rowKey the row key ({@code null} not permitted). * @param columnKey the column key ({@code null} not permitted). * * @see #add(BoxAndWhiskerItem, Comparable, Comparable) */ public void remove(Comparable rowKey, Comparable columnKey) { // defer null argument checks int r = getRowIndex(rowKey); int c = getColumnIndex(columnKey); this.data.removeObject(rowKey, columnKey); // if this cell held a maximum and/or minimum value, we'll need to // update the cached bounds... if ((this.maximumRangeValueRow == r && this.maximumRangeValueColumn == c) || (this.minimumRangeValueRow == r && this.minimumRangeValueColumn == c)) { updateBounds(); } fireDatasetChanged(); } /** * Removes a row from the dataset and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param rowIndex the row index. * * @see #removeColumn(int) */ public void removeRow(int rowIndex) { this.data.removeRow(rowIndex); updateBounds(); fireDatasetChanged(); } /** * Removes a row from the dataset and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param rowKey the row key. * * @see #removeColumn(Comparable) */ public void removeRow(Comparable rowKey) { this.data.removeRow(rowKey); updateBounds(); fireDatasetChanged(); } /** * Removes a column from the dataset and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param columnIndex the column index. * * @see #removeRow(int) */ public void removeColumn(int columnIndex) { this.data.removeColumn(columnIndex); updateBounds(); fireDatasetChanged(); } /** * Removes a column from the dataset and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param columnKey the column key. * * @see #removeRow(Comparable) */ public void removeColumn(Comparable columnKey) { this.data.removeColumn(columnKey); updateBounds(); fireDatasetChanged(); } /** * Clears all data from the dataset and sends a {@link DatasetChangeEvent} * to all registered listeners. */ public void clear() { this.data.clear(); updateBounds(); fireDatasetChanged(); } /** * Return an item from within the dataset. * * @param row the row index. * @param column the column index. * * @return The item. */ public BoxAndWhiskerItem getItem(int row, int column) { return (BoxAndWhiskerItem) this.data.getObject(row, column); } /** * Returns the value for an item. * * @param row the row index. * @param column the column index. * * @return The value. * * @see #getMedianValue(int, int) * @see #getValue(Comparable, Comparable) */ @Override public Number getValue(int row, int column) { return getMedianValue(row, column); } /** * Returns the value for an item. * * @param rowKey the row key. * @param columnKey the columnKey. * * @return The value. * * @see #getMedianValue(Comparable, Comparable) * @see #getValue(int, int) */ @Override public Number getValue(Comparable rowKey, Comparable columnKey) { return getMedianValue(rowKey, columnKey); } /** * Returns the mean value for an item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The mean value. * * @see #getItem(int, int) */ @Override public Number getMeanValue(int row, int column) { Number result = null; BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject(row, column); if (item != null) { result = item.getMean(); } return result; } /** * Returns the mean value for an item. * * @param rowKey the row key. * @param columnKey the column key. * * @return The mean value. * * @see #getItem(int, int) */ @Override public Number getMeanValue(Comparable rowKey, Comparable columnKey) { Number result = null; BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( rowKey, columnKey); if (item != null) { result = item.getMean(); } return result; } /** * Returns the median value for an item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The median value. * * @see #getItem(int, int) */ @Override public Number getMedianValue(int row, int column) { Number result = null; BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject(row, column); if (item != null) { result = item.getMedian(); } return result; } /** * Returns the median value for an item. * * @param rowKey the row key. * @param columnKey the columnKey. * * @return The median value. * * @see #getItem(int, int) */ @Override public Number getMedianValue(Comparable rowKey, Comparable columnKey) { Number result = null; BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( rowKey, columnKey); if (item != null) { result = item.getMedian(); } return result; } /** * Returns the first quartile value. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The first quartile value. * * @see #getItem(int, int) */ @Override public Number getQ1Value(int row, int column) { Number result = null; BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( row, column); if (item != null) { result = item.getQ1(); } return result; } /** * Returns the first quartile value. * * @param rowKey the row key. * @param columnKey the column key. * * @return The first quartile value. * * @see #getItem(int, int) */ @Override public Number getQ1Value(Comparable rowKey, Comparable columnKey) { Number result = null; BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( rowKey, columnKey); if (item != null) { result = item.getQ1(); } return result; } /** * Returns the third quartile value. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The third quartile value. * * @see #getItem(int, int) */ @Override public Number getQ3Value(int row, int column) { Number result = null; BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( row, column); if (item != null) { result = item.getQ3(); } return result; } /** * Returns the third quartile value. * * @param rowKey the row key. * @param columnKey the column key. * * @return The third quartile value. * * @see #getItem(int, int) */ @Override public Number getQ3Value(Comparable rowKey, Comparable columnKey) { Number result = null; BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( rowKey, columnKey); if (item != null) { result = item.getQ3(); } return result; } /** * Returns the column index for a given key. * * @param key the column key ({@code null} not permitted). * * @return The column index. * * @see #getColumnKey(int) */ @Override public int getColumnIndex(Comparable key) { return this.data.getColumnIndex(key); } /** * Returns a column key. * * @param column the column index (zero-based). * * @return The column key. * * @see #getColumnIndex(Comparable) */ @Override public Comparable getColumnKey(int column) { return this.data.getColumnKey(column); } /** * Returns the column keys. * * @return The keys. * * @see #getRowKeys() */ @Override public List getColumnKeys() { return this.data.getColumnKeys(); } /** * Returns the row index for a given key. * * @param key the row key ({@code null} not permitted). * * @return The row index. * * @see #getRowKey(int) */ @Override public int getRowIndex(Comparable key) { // defer null argument check return this.data.getRowIndex(key); } /** * Returns a row key. * * @param row the row index (zero-based). * * @return The row key. * * @see #getRowIndex(Comparable) */ @Override public Comparable getRowKey(int row) { return this.data.getRowKey(row); } /** * Returns the row keys. * * @return The keys. * * @see #getColumnKeys() */ @Override public List getRowKeys() { return this.data.getRowKeys(); } /** * Returns the number of rows in the table. * * @return The row count. * * @see #getColumnCount() */ @Override public int getRowCount() { return this.data.getRowCount(); } /** * Returns the number of columns in the table. * * @return The column count. * * @see #getRowCount() */ @Override public int getColumnCount() { return this.data.getColumnCount(); } /** * Returns the minimum y-value in the dataset. * * @param includeInterval a flag that determines whether or not the * y-interval is taken into account. * * @return The minimum value. * * @see #getRangeUpperBound(boolean) */ @Override public double getRangeLowerBound(boolean includeInterval) { return this.minimumRangeValue; } /** * Returns the maximum y-value in the dataset. * * @param includeInterval a flag that determines whether or not the * y-interval is taken into account. * * @return The maximum value. * * @see #getRangeLowerBound(boolean) */ @Override public double getRangeUpperBound(boolean includeInterval) { return this.maximumRangeValue; } /** * Returns the range of the values in this dataset's range. * * @param includeInterval a flag that determines whether or not the * y-interval is taken into account. * * @return The range. */ @Override public Range getRangeBounds(boolean includeInterval) { return new Range(this.minimumRangeValue, this.maximumRangeValue); } /** * Returns the minimum regular (non outlier) value for an item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The minimum regular value. * * @see #getItem(int, int) */ @Override public Number getMinRegularValue(int row, int column) { Number result = null; BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( row, column); if (item != null) { result = item.getMinRegularValue(); } return result; } /** * Returns the minimum regular (non outlier) value for an item. * * @param rowKey the row key. * @param columnKey the column key. * * @return The minimum regular value. * * @see #getItem(int, int) */ @Override public Number getMinRegularValue(Comparable rowKey, Comparable columnKey) { Number result = null; BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( rowKey, columnKey); if (item != null) { result = item.getMinRegularValue(); } return result; } /** * Returns the maximum regular (non outlier) value for an item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The maximum regular value. * * @see #getItem(int, int) */ @Override public Number getMaxRegularValue(int row, int column) { Number result = null; BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( row, column); if (item != null) { result = item.getMaxRegularValue(); } return result; } /** * Returns the maximum regular (non outlier) value for an item. * * @param rowKey the row key. * @param columnKey the column key. * * @return The maximum regular value. * * @see #getItem(int, int) */ @Override public Number getMaxRegularValue(Comparable rowKey, Comparable columnKey) { Number result = null; BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( rowKey, columnKey); if (item != null) { result = item.getMaxRegularValue(); } return result; } /** * Returns the minimum outlier (non farout) value for an item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The minimum outlier. * * @see #getItem(int, int) */ @Override public Number getMinOutlier(int row, int column) { Number result = null; BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( row, column); if (item != null) { result = item.getMinOutlier(); } return result; } /** * Returns the minimum outlier (non farout) value for an item. * * @param rowKey the row key. * @param columnKey the column key. * * @return The minimum outlier. * * @see #getItem(int, int) */ @Override public Number getMinOutlier(Comparable rowKey, Comparable columnKey) { Number result = null; BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( rowKey, columnKey); if (item != null) { result = item.getMinOutlier(); } return result; } /** * Returns the maximum outlier (non farout) value for an item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The maximum outlier. * * @see #getItem(int, int) */ @Override public Number getMaxOutlier(int row, int column) { Number result = null; BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( row, column); if (item != null) { result = item.getMaxOutlier(); } return result; } /** * Returns the maximum outlier (non farout) value for an item. * * @param rowKey the row key. * @param columnKey the column key. * * @return The maximum outlier. * * @see #getItem(int, int) */ @Override public Number getMaxOutlier(Comparable rowKey, Comparable columnKey) { Number result = null; BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( rowKey, columnKey); if (item != null) { result = item.getMaxOutlier(); } return result; } /** * Returns a list of outlier values for an item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return A list of outlier values. * * @see #getItem(int, int) */ @Override public List getOutliers(int row, int column) { List result = null; BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( row, column); if (item != null) { result = item.getOutliers(); } return result; } /** * Returns a list of outlier values for an item. * * @param rowKey the row key. * @param columnKey the column key. * * @return A list of outlier values. * * @see #getItem(int, int) */ @Override public List getOutliers(Comparable rowKey, Comparable columnKey) { List result = null; BoxAndWhiskerItem item = (BoxAndWhiskerItem) this.data.getObject( rowKey, columnKey); if (item != null) { result = item.getOutliers(); } return result; } /** * Resets the cached bounds, by iterating over the entire dataset to find * the current bounds. */ private void updateBounds() { this.minimumRangeValue = Double.NaN; this.minimumRangeValueRow = -1; this.minimumRangeValueColumn = -1; this.maximumRangeValue = Double.NaN; this.maximumRangeValueRow = -1; this.maximumRangeValueColumn = -1; int rowCount = getRowCount(); int columnCount = getColumnCount(); for (int r = 0; r < rowCount; r++) { for (int c = 0; c < columnCount; c++) { BoxAndWhiskerItem item = getItem(r, c); if (item != null) { Number min = item.getMinOutlier(); if (min != null) { double minv = min.doubleValue(); if (!Double.isNaN(minv)) { if (minv < this.minimumRangeValue || Double.isNaN( this.minimumRangeValue)) { this.minimumRangeValue = minv; this.minimumRangeValueRow = r; this.minimumRangeValueColumn = c; } } } Number max = item.getMaxOutlier(); if (max != null) { double maxv = max.doubleValue(); if (!Double.isNaN(maxv)) { if (maxv > this.maximumRangeValue || Double.isNaN( this.maximumRangeValue)) { this.maximumRangeValue = maxv; this.maximumRangeValueRow = r; this.maximumRangeValueColumn = c; } } } } } } } /** * Tests this dataset for equality with an arbitrary object. * * @param obj the object to test against ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj instanceof DefaultBoxAndWhiskerCategoryDataset) { DefaultBoxAndWhiskerCategoryDataset dataset = (DefaultBoxAndWhiskerCategoryDataset) obj; return Objects.equals(this.data, dataset.data); } return false; } /** * Returns a clone of this dataset. * * @return A clone. * * @throws CloneNotSupportedException if cloning is not possible. */ @Override public Object clone() throws CloneNotSupportedException { DefaultBoxAndWhiskerCategoryDataset clone = (DefaultBoxAndWhiskerCategoryDataset) super.clone(); clone.data = (KeyedObjects2D) this.data.clone(); return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/statistics/DefaultBoxAndWhiskerXYDataset.java000066400000000000000000000430551463604235500335360ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------------- * DefaultBoxAndWhiskerXYDataset.java * ---------------------------------- * (C) Copyright 2003-present, by David Browning and Contributors. * * Original Author: David Browning (for Australian Institute of Marine * Science); * Contributor(s): David Gilbert; * */ package org.jfree.data.statistics; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Objects; import org.jfree.data.Range; import org.jfree.data.RangeInfo; import org.jfree.data.general.DatasetChangeEvent; import org.jfree.data.xy.AbstractXYDataset; /** * A simple implementation of the {@link BoxAndWhiskerXYDataset} interface. * This dataset implementation can hold only one series. */ public class DefaultBoxAndWhiskerXYDataset extends AbstractXYDataset implements BoxAndWhiskerXYDataset, RangeInfo { /** The series key. */ private Comparable seriesKey; /** Storage for the dates. */ private List dates; /** Storage for the box and whisker statistics. */ private List items; /** The minimum range value. */ private Number minimumRangeValue; /** The maximum range value. */ private Number maximumRangeValue; /** The range of values. */ private Range rangeBounds; /** * The coefficient used to calculate outliers. Tukey's default value is * 1.5 (see EDA) Any value which is greater than Q3 + (interquartile range * * outlier coefficient) is considered to be an outlier. Can be altered * if the data is particularly skewed. */ private double outlierCoefficient = 1.5; /** * The coefficient used to calculate farouts. Tukey's default value is 2 * (see EDA) Any value which is greater than Q3 + (interquartile range * * farout coefficient) is considered to be a farout. Can be altered if the * data is particularly skewed. */ private double faroutCoefficient = 2.0; /** * Constructs a new box and whisker dataset. *

* The current implementation allows only one series in the dataset. * This may be extended in a future version. * * @param seriesKey the key for the series. */ public DefaultBoxAndWhiskerXYDataset(Comparable seriesKey) { this.seriesKey = seriesKey; this.dates = new ArrayList(); this.items = new ArrayList(); this.minimumRangeValue = null; this.maximumRangeValue = null; this.rangeBounds = null; } /** * Returns the value used as the outlier coefficient. The outlier * coefficient gives an indication of the degree of certainty in an * unskewed distribution. Increasing the coefficient increases the number * of values included. Currently only used to ensure farout coefficient is * greater than the outlier coefficient * * @return A {@code double} representing the value used to calculate * outliers. * * @see #setOutlierCoefficient(double) */ @Override public double getOutlierCoefficient() { return this.outlierCoefficient; } /** * Sets the value used as the outlier coefficient * * @param outlierCoefficient being a {@code double} representing the * value used to calculate outliers. * * @see #getOutlierCoefficient() */ public void setOutlierCoefficient(double outlierCoefficient) { this.outlierCoefficient = outlierCoefficient; } /** * Returns the value used as the farout coefficient. The farout coefficient * allows the calculation of which values will be off the graph. * * @return A {@code double} representing the value used to calculate * farouts. * * @see #setFaroutCoefficient(double) */ @Override public double getFaroutCoefficient() { return this.faroutCoefficient; } /** * Sets the value used as the farouts coefficient. The farout coefficient * must b greater than the outlier coefficient. * * @param faroutCoefficient being a {@code double} representing the * value used to calculate farouts. * * @see #getFaroutCoefficient() */ public void setFaroutCoefficient(double faroutCoefficient) { if (faroutCoefficient > getOutlierCoefficient()) { this.faroutCoefficient = faroutCoefficient; } else { throw new IllegalArgumentException("Farout value must be greater " + "than the outlier value, which is currently set at: (" + getOutlierCoefficient() + ")"); } } /** * Returns the number of series in the dataset. *

* This implementation only allows one series. * * @return The number of series. */ @Override public int getSeriesCount() { return 1; } /** * Returns the number of items in the specified series. * * @param series the index (zero-based) of the series. * * @return The number of items in the specified series. */ @Override public int getItemCount(int series) { return this.dates.size(); } /** * Adds an item to the dataset and sends a {@link DatasetChangeEvent} to * all registered listeners. * * @param date the date ({@code null} not permitted). * @param item the item ({@code null} not permitted). */ public void add(Date date, BoxAndWhiskerItem item) { this.dates.add(date); this.items.add(item); if (this.minimumRangeValue == null) { this.minimumRangeValue = item.getMinRegularValue(); } else { if (item.getMinRegularValue().doubleValue() < this.minimumRangeValue.doubleValue()) { this.minimumRangeValue = item.getMinRegularValue(); } } if (this.maximumRangeValue == null) { this.maximumRangeValue = item.getMaxRegularValue(); } else { if (item.getMaxRegularValue().doubleValue() > this.maximumRangeValue.doubleValue()) { this.maximumRangeValue = item.getMaxRegularValue(); } } this.rangeBounds = new Range(this.minimumRangeValue.doubleValue(), this.maximumRangeValue.doubleValue()); fireDatasetChanged(); } /** * Returns the name of the series stored in this dataset. * * @param i the index of the series. Currently ignored. * * @return The name of this series. */ @Override public Comparable getSeriesKey(int i) { return this.seriesKey; } /** * Return an item from within the dataset. * * @param series the series index (ignored, since this dataset contains * only one series). * @param item the item within the series (zero-based index) * * @return The item. */ public BoxAndWhiskerItem getItem(int series, int item) { return (BoxAndWhiskerItem) this.items.get(item); } /** * Returns the x-value for one item in a series. *

* The value returned is a Long object generated from the underlying Date * object. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The x-value. */ @Override public Number getX(int series, int item) { return ((Date) this.dates.get(item)).getTime(); } /** * Returns the x-value for one item in a series, as a Date. *

* This method is provided for convenience only. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The x-value as a Date. */ public Date getXDate(int series, int item) { return (Date) this.dates.get(item); } /** * Returns the y-value for one item in a series. *

* This method (from the XYDataset interface) is mapped to the * getMeanValue() method. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The y-value. */ @Override public Number getY(int series, int item) { return getMeanValue(series, item); } /** * Returns the mean for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The mean for the specified series and item. */ @Override public Number getMeanValue(int series, int item) { Number result = null; BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this.items.get(item); if (stats != null) { result = stats.getMean(); } return result; } /** * Returns the median-value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The median-value for the specified series and item. */ @Override public Number getMedianValue(int series, int item) { Number result = null; BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this.items.get(item); if (stats != null) { result = stats.getMedian(); } return result; } /** * Returns the Q1 median-value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The Q1 median-value for the specified series and item. */ @Override public Number getQ1Value(int series, int item) { Number result = null; BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this.items.get(item); if (stats != null) { result = stats.getQ1(); } return result; } /** * Returns the Q3 median-value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The Q3 median-value for the specified series and item. */ @Override public Number getQ3Value(int series, int item) { Number result = null; BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this.items.get(item); if (stats != null) { result = stats.getQ3(); } return result; } /** * Returns the min-value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The min-value for the specified series and item. */ @Override public Number getMinRegularValue(int series, int item) { Number result = null; BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this.items.get(item); if (stats != null) { result = stats.getMinRegularValue(); } return result; } /** * Returns the max-value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The max-value for the specified series and item. */ @Override public Number getMaxRegularValue(int series, int item) { Number result = null; BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this.items.get(item); if (stats != null) { result = stats.getMaxRegularValue(); } return result; } /** * Returns the minimum value which is not a farout. * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return A {@code Number} representing the maximum non-farout value. */ @Override public Number getMinOutlier(int series, int item) { Number result = null; BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this.items.get(item); if (stats != null) { result = stats.getMinOutlier(); } return result; } /** * Returns the maximum value which is not a farout, ie Q3 + (interquartile * range * farout coefficient). * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return A {@code Number} representing the maximum non-farout value. */ @Override public Number getMaxOutlier(int series, int item) { Number result = null; BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this.items.get(item); if (stats != null) { result = stats.getMaxOutlier(); } return result; } /** * Returns a list of outliers for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The list of outliers for the specified series and item * (possibly {@code null}). */ @Override public List getOutliers(int series, int item) { List result = null; BoxAndWhiskerItem stats = (BoxAndWhiskerItem) this.items.get(item); if (stats != null) { result = stats.getOutliers(); } return result; } /** * Returns the minimum y-value in the dataset. * * @param includeInterval a flag that determines whether or not the * y-interval is taken into account. * * @return The minimum value. */ @Override public double getRangeLowerBound(boolean includeInterval) { double result = Double.NaN; if (this.minimumRangeValue != null) { result = this.minimumRangeValue.doubleValue(); } return result; } /** * Returns the maximum y-value in the dataset. * * @param includeInterval a flag that determines whether or not the * y-interval is taken into account. * * @return The maximum value. */ @Override public double getRangeUpperBound(boolean includeInterval) { double result = Double.NaN; if (this.maximumRangeValue != null) { result = this.maximumRangeValue.doubleValue(); } return result; } /** * Returns the range of the values in this dataset's range. * * @param includeInterval a flag that determines whether or not the * y-interval is taken into account. * * @return The range. */ @Override public Range getRangeBounds(boolean includeInterval) { return this.rangeBounds; } /** * Tests this dataset for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof DefaultBoxAndWhiskerXYDataset)) { return false; } DefaultBoxAndWhiskerXYDataset that = (DefaultBoxAndWhiskerXYDataset) obj; if (!Objects.equals(this.seriesKey, that.seriesKey)) { return false; } if (!this.dates.equals(that.dates)) { return false; } if (!this.items.equals(that.items)) { return false; } return true; } /** * Returns a clone of the plot. * * @return A clone. * * @throws CloneNotSupportedException if the cloning is not supported. */ @Override public Object clone() throws CloneNotSupportedException { DefaultBoxAndWhiskerXYDataset clone = (DefaultBoxAndWhiskerXYDataset) super.clone(); clone.dates = new java.util.ArrayList(this.dates); clone.items = new java.util.ArrayList(this.items); return clone; } } DefaultMultiValueCategoryDataset.java000066400000000000000000000270561463604235500342560ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/statistics/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------------- * DefaultMultiValueCategoryDataset.java * ------------------------------------- * (C) Copyright 2007-present, by David Forslund and Contributors. * * Original Author: David Forslund; * Contributor(s): David Gilbert; * */ package org.jfree.data.statistics; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.KeyedObjects2D; import org.jfree.data.Range; import org.jfree.data.RangeInfo; import org.jfree.data.general.AbstractDataset; import org.jfree.data.general.DatasetChangeEvent; /** * A category dataset that defines multiple values for each item. */ public class DefaultMultiValueCategoryDataset extends AbstractDataset implements MultiValueCategoryDataset, RangeInfo, PublicCloneable { /** * Storage for the data. */ protected KeyedObjects2D data; /** * The minimum range value. */ private Number minimumRangeValue; /** * The maximum range value. */ private Number maximumRangeValue; /** * The range of values. */ private Range rangeBounds; /** * Creates a new dataset. */ public DefaultMultiValueCategoryDataset() { this.data = new KeyedObjects2D(); this.minimumRangeValue = null; this.maximumRangeValue = null; this.rangeBounds = new Range(0.0, 0.0); } /** * Adds a list of values to the dataset ({@code null} and Double.NaN * items are automatically removed) and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param values a list of values ({@code null} not permitted). * @param rowKey the row key ({@code null} not permitted). * @param columnKey the column key ({@code null} not permitted). */ public void add(List values, Comparable rowKey, Comparable columnKey) { Args.nullNotPermitted(values, "values"); Args.nullNotPermitted(rowKey, "rowKey"); Args.nullNotPermitted(columnKey, "columnKey"); List vlist = new ArrayList(values.size()); Iterator iterator = values.listIterator(); while (iterator.hasNext()) { Object obj = iterator.next(); if (obj instanceof Number) { Number n = (Number) obj; double v = n.doubleValue(); if (!Double.isNaN(v)) { vlist.add(n); } } } Collections.sort(vlist); this.data.addObject(vlist, rowKey, columnKey); if (vlist.size() > 0) { double maxval = Double.NEGATIVE_INFINITY; double minval = Double.POSITIVE_INFINITY; for (int i = 0; i < vlist.size(); i++) { Number n = (Number) vlist.get(i); double v = n.doubleValue(); minval = Math.min(minval, v); maxval = Math.max(maxval, v); } // update the cached range values... if (this.maximumRangeValue == null) { this.maximumRangeValue = maxval; } else if (maxval > this.maximumRangeValue.doubleValue()) { this.maximumRangeValue = maxval; } if (this.minimumRangeValue == null) { this.minimumRangeValue = minval; } else if (minval < this.minimumRangeValue.doubleValue()) { this.minimumRangeValue = minval; } this.rangeBounds = new Range(this.minimumRangeValue.doubleValue(), this.maximumRangeValue.doubleValue()); } fireDatasetChanged(); } /** * Returns a list (possibly empty) of the values for the specified item. * The returned list should be unmodifiable. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The list of values. */ @Override public List getValues(int row, int column) { List values = (List) this.data.getObject(row, column); if (values != null) { return Collections.unmodifiableList(values); } else { return Collections.EMPTY_LIST; } } /** * Returns a list (possibly empty) of the values for the specified item. * The returned list should be unmodifiable. * * @param rowKey the row key ({@code null} not permitted). * @param columnKey the column key ({@code null} not permitted). * * @return The list of values. */ @Override public List getValues(Comparable rowKey, Comparable columnKey) { return Collections.unmodifiableList((List) this.data.getObject(rowKey, columnKey)); } /** * Returns the average value for the specified item. * * @param row the row key. * @param column the column key. * * @return The average value. */ @Override public Number getValue(Comparable row, Comparable column) { List l = (List) this.data.getObject(row, column); double average = 0.0d; int count = 0; if (l != null && l.size() > 0) { for (int i = 0; i < l.size(); i++) { Number n = (Number) l.get(i); average += n.doubleValue(); count += 1; } if (count > 0) { average = average / count; } } if (count == 0) { return null; } return average; } /** * Returns the average value for the specified item. * * @param row the row index. * @param column the column index. * * @return The average value. */ @Override public Number getValue(int row, int column) { List l = (List) this.data.getObject(row, column); double average = 0.0d; int count = 0; if (l != null && l.size() > 0) { for (int i = 0; i < l.size(); i++) { Number n = (Number) l.get(i); average += n.doubleValue(); count += 1; } if (count > 0) { average = average / count; } } if (count == 0) { return null; } return average; } /** * Returns the column index for a given key. * * @param key the column key. * * @return The column index. */ @Override public int getColumnIndex(Comparable key) { return this.data.getColumnIndex(key); } /** * Returns a column key. * * @param column the column index (zero-based). * * @return The column key. */ @Override public Comparable getColumnKey(int column) { return this.data.getColumnKey(column); } /** * Returns the column keys. * * @return The keys. */ @Override public List getColumnKeys() { return this.data.getColumnKeys(); } /** * Returns the row index for a given key. * * @param key the row key. * * @return The row index. */ @Override public int getRowIndex(Comparable key) { return this.data.getRowIndex(key); } /** * Returns a row key. * * @param row the row index (zero-based). * * @return The row key. */ @Override public Comparable getRowKey(int row) { return this.data.getRowKey(row); } /** * Returns the row keys. * * @return The keys. */ @Override public List getRowKeys() { return this.data.getRowKeys(); } /** * Returns the number of rows in the table. * * @return The row count. */ @Override public int getRowCount() { return this.data.getRowCount(); } /** * Returns the number of columns in the table. * * @return The column count. */ @Override public int getColumnCount() { return this.data.getColumnCount(); } /** * Returns the minimum y-value in the dataset. * * @param includeInterval a flag that determines whether or not the * y-interval is taken into account. * * @return The minimum value. */ @Override public double getRangeLowerBound(boolean includeInterval) { double result = Double.NaN; if (this.minimumRangeValue != null) { result = this.minimumRangeValue.doubleValue(); } return result; } /** * Returns the maximum y-value in the dataset. * * @param includeInterval a flag that determines whether or not the * y-interval is taken into account. * * @return The maximum value. */ @Override public double getRangeUpperBound(boolean includeInterval) { double result = Double.NaN; if (this.maximumRangeValue != null) { result = this.maximumRangeValue.doubleValue(); } return result; } /** * Returns the range of the values in this dataset's range. * * @param includeInterval a flag that determines whether or not the * y-interval is taken into account. * @return The range. */ @Override public Range getRangeBounds(boolean includeInterval) { return this.rangeBounds; } /** * Tests this dataset for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof DefaultMultiValueCategoryDataset)) { return false; } DefaultMultiValueCategoryDataset that = (DefaultMultiValueCategoryDataset) obj; return this.data.equals(that.data); } /** * Returns a clone of this instance. * * @return A clone. * * @throws CloneNotSupportedException if the dataset cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { DefaultMultiValueCategoryDataset clone = (DefaultMultiValueCategoryDataset) super.clone(); clone.data = (KeyedObjects2D) this.data.clone(); return clone; } } DefaultStatisticalCategoryDataset.java000066400000000000000000000555031463604235500344510ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/statistics/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------------------- * DefaultStatisticalCategoryDataset.java * -------------------------------------- * (C) Copyright 2002-present, by Pascal Collet and Contributors. * * Original Author: Pascal Collet; * Contributor(s): David Gilbert; * */ package org.jfree.data.statistics; import java.util.List; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.KeyedObjects2D; import org.jfree.data.Range; import org.jfree.data.RangeInfo; import org.jfree.data.general.AbstractDataset; import org.jfree.data.general.DatasetChangeEvent; /** * A convenience class that provides a default implementation of the * {@link StatisticalCategoryDataset} interface. */ public class DefaultStatisticalCategoryDataset extends AbstractDataset implements StatisticalCategoryDataset, RangeInfo, PublicCloneable { /** Storage for the data. */ private KeyedObjects2D data; /** The minimum range value. */ private double minimumRangeValue; /** The row index for the minimum range value. */ private int minimumRangeValueRow; /** The column index for the minimum range value. */ private int minimumRangeValueColumn; /** The minimum range value including the standard deviation. */ private double minimumRangeValueIncStdDev; /** * The row index for the minimum range value (including the standard * deviation). */ private int minimumRangeValueIncStdDevRow; /** * The column index for the minimum range value (including the standard * deviation). */ private int minimumRangeValueIncStdDevColumn; /** The maximum range value. */ private double maximumRangeValue; /** The row index for the maximum range value. */ private int maximumRangeValueRow; /** The column index for the maximum range value. */ private int maximumRangeValueColumn; /** The maximum range value including the standard deviation. */ private double maximumRangeValueIncStdDev; /** * The row index for the maximum range value (including the standard * deviation). */ private int maximumRangeValueIncStdDevRow; /** * The column index for the maximum range value (including the standard * deviation). */ private int maximumRangeValueIncStdDevColumn; /** * Creates a new dataset. */ public DefaultStatisticalCategoryDataset() { this.data = new KeyedObjects2D(); this.minimumRangeValue = Double.NaN; this.minimumRangeValueRow = -1; this.minimumRangeValueColumn = -1; this.maximumRangeValue = Double.NaN; this.maximumRangeValueRow = -1; this.maximumRangeValueColumn = -1; this.minimumRangeValueIncStdDev = Double.NaN; this.minimumRangeValueIncStdDevRow = -1; this.minimumRangeValueIncStdDevColumn = -1; this.maximumRangeValueIncStdDev = Double.NaN; this.maximumRangeValueIncStdDevRow = -1; this.maximumRangeValueIncStdDevColumn = -1; } /** * Returns the mean value for an item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The mean value (possibly {@code null}). */ @Override public Number getMeanValue(int row, int column) { Number result = null; MeanAndStandardDeviation masd = (MeanAndStandardDeviation) this.data.getObject(row, column); if (masd != null) { result = masd.getMean(); } return result; } /** * Returns the value for an item (for this dataset, the mean value is * returned). * * @param row the row index. * @param column the column index. * * @return The value (possibly {@code null}). */ @Override public Number getValue(int row, int column) { return getMeanValue(row, column); } /** * Returns the value for an item (for this dataset, the mean value is * returned). * * @param rowKey the row key. * @param columnKey the columnKey. * * @return The value (possibly {@code null}). */ @Override public Number getValue(Comparable rowKey, Comparable columnKey) { return getMeanValue(rowKey, columnKey); } /** * Returns the mean value for an item. * * @param rowKey the row key. * @param columnKey the columnKey. * * @return The mean value (possibly {@code null}). */ @Override public Number getMeanValue(Comparable rowKey, Comparable columnKey) { Number result = null; MeanAndStandardDeviation masd = (MeanAndStandardDeviation) this.data.getObject(rowKey, columnKey); if (masd != null) { result = masd.getMean(); } return result; } /** * Returns the standard deviation value for an item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The standard deviation (possibly {@code null}). */ @Override public Number getStdDevValue(int row, int column) { Number result = null; MeanAndStandardDeviation masd = (MeanAndStandardDeviation) this.data.getObject(row, column); if (masd != null) { result = masd.getStandardDeviation(); } return result; } /** * Returns the standard deviation value for an item. * * @param rowKey the row key. * @param columnKey the columnKey. * * @return The standard deviation (possibly {@code null}). */ @Override public Number getStdDevValue(Comparable rowKey, Comparable columnKey) { Number result = null; MeanAndStandardDeviation masd = (MeanAndStandardDeviation) this.data.getObject(rowKey, columnKey); if (masd != null) { result = masd.getStandardDeviation(); } return result; } /** * Returns the column index for a given key. * * @param key the column key ({@code null} not permitted). * * @return The column index. */ @Override public int getColumnIndex(Comparable key) { // defer null argument check return this.data.getColumnIndex(key); } /** * Returns a column key. * * @param column the column index (zero-based). * * @return The column key. */ @Override public Comparable getColumnKey(int column) { return this.data.getColumnKey(column); } /** * Returns the column keys. * * @return The keys. */ @Override public List getColumnKeys() { return this.data.getColumnKeys(); } /** * Returns the row index for a given key. * * @param key the row key ({@code null} not permitted). * * @return The row index. */ @Override public int getRowIndex(Comparable key) { // defer null argument check return this.data.getRowIndex(key); } /** * Returns a row key. * * @param row the row index (zero-based). * * @return The row key. */ @Override public Comparable getRowKey(int row) { return this.data.getRowKey(row); } /** * Returns the row keys. * * @return The keys. */ @Override public List getRowKeys() { return this.data.getRowKeys(); } /** * Returns the number of rows in the table. * * @return The row count. * * @see #getColumnCount() */ @Override public int getRowCount() { return this.data.getRowCount(); } /** * Returns the number of columns in the table. * * @return The column count. * * @see #getRowCount() */ @Override public int getColumnCount() { return this.data.getColumnCount(); } /** * Adds a mean and standard deviation to the table. * * @param mean the mean. * @param standardDeviation the standard deviation. * @param rowKey the row key. * @param columnKey the column key. */ public void add(double mean, double standardDeviation, Comparable rowKey, Comparable columnKey) { add(Double.valueOf(mean), Double.valueOf(standardDeviation), rowKey, columnKey); } /** * Adds a mean and standard deviation to the table. * * @param mean the mean. * @param standardDeviation the standard deviation. * @param rowKey the row key. * @param columnKey the column key. */ public void add(Number mean, Number standardDeviation, Comparable rowKey, Comparable columnKey) { MeanAndStandardDeviation item = new MeanAndStandardDeviation( mean, standardDeviation); this.data.addObject(item, rowKey, columnKey); double m = Double.NaN; double sd = Double.NaN; if (mean != null) { m = mean.doubleValue(); } if (standardDeviation != null) { sd = standardDeviation.doubleValue(); } // update cached range values int r = this.data.getColumnIndex(columnKey); int c = this.data.getRowIndex(rowKey); if ((r == this.maximumRangeValueRow && c == this.maximumRangeValueColumn) || (r == this.maximumRangeValueIncStdDevRow && c == this.maximumRangeValueIncStdDevColumn) || (r == this.minimumRangeValueRow && c == this.minimumRangeValueColumn) || (r == this.minimumRangeValueIncStdDevRow && c == this.minimumRangeValueIncStdDevColumn)) { // iterate over all data items and update mins and maxes updateBounds(); } else { if (!Double.isNaN(m)) { if (Double.isNaN(this.maximumRangeValue) || m > this.maximumRangeValue) { this.maximumRangeValue = m; this.maximumRangeValueRow = r; this.maximumRangeValueColumn = c; } } if (!Double.isNaN(m + sd)) { if (Double.isNaN(this.maximumRangeValueIncStdDev) || (m + sd) > this.maximumRangeValueIncStdDev) { this.maximumRangeValueIncStdDev = m + sd; this.maximumRangeValueIncStdDevRow = r; this.maximumRangeValueIncStdDevColumn = c; } } if (!Double.isNaN(m)) { if (Double.isNaN(this.minimumRangeValue) || m < this.minimumRangeValue) { this.minimumRangeValue = m; this.minimumRangeValueRow = r; this.minimumRangeValueColumn = c; } } if (!Double.isNaN(m - sd)) { if (Double.isNaN(this.minimumRangeValueIncStdDev) || (m - sd) < this.minimumRangeValueIncStdDev) { this.minimumRangeValueIncStdDev = m - sd; this.minimumRangeValueIncStdDevRow = r; this.minimumRangeValueIncStdDevColumn = c; } } } fireDatasetChanged(); } /** * Removes an item from the dataset and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param rowKey the row key ({@code null} not permitted). * @param columnKey the column key ({@code null} not permitted). * * @see #add(double, double, Comparable, Comparable) */ public void remove(Comparable rowKey, Comparable columnKey) { // defer null argument checks int r = getRowIndex(rowKey); int c = getColumnIndex(columnKey); this.data.removeObject(rowKey, columnKey); // if this cell held a maximum and/or minimum value, we'll need to // update the cached bounds... if ((r == this.maximumRangeValueRow && c == this.maximumRangeValueColumn) || (r == this.maximumRangeValueIncStdDevRow && c == this.maximumRangeValueIncStdDevColumn) || (r == this.minimumRangeValueRow && c == this.minimumRangeValueColumn) || (r == this.minimumRangeValueIncStdDevRow && c == this.minimumRangeValueIncStdDevColumn)) { // iterate over all data items and update mins and maxes updateBounds(); } fireDatasetChanged(); } /** * Removes a row from the dataset and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param rowIndex the row index. * * @see #removeColumn(int) */ public void removeRow(int rowIndex) { this.data.removeRow(rowIndex); updateBounds(); fireDatasetChanged(); } /** * Removes a row from the dataset and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param rowKey the row key ({@code null} not permitted). * * @see #removeColumn(Comparable) */ public void removeRow(Comparable rowKey) { this.data.removeRow(rowKey); updateBounds(); fireDatasetChanged(); } /** * Removes a column from the dataset and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param columnIndex the column index. * * @see #removeRow(int) */ public void removeColumn(int columnIndex) { this.data.removeColumn(columnIndex); updateBounds(); fireDatasetChanged(); } /** * Removes a column from the dataset and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param columnKey the column key ({@code null} not permitted). * * @see #removeRow(Comparable) */ public void removeColumn(Comparable columnKey) { this.data.removeColumn(columnKey); updateBounds(); fireDatasetChanged(); } /** * Clears all data from the dataset and sends a {@link DatasetChangeEvent} * to all registered listeners. */ public void clear() { this.data.clear(); updateBounds(); fireDatasetChanged(); } /** * Iterate over all the data items and update the cached bound values. */ private void updateBounds() { this.maximumRangeValue = Double.NaN; this.maximumRangeValueRow = -1; this.maximumRangeValueColumn = -1; this.minimumRangeValue = Double.NaN; this.minimumRangeValueRow = -1; this.minimumRangeValueColumn = -1; this.maximumRangeValueIncStdDev = Double.NaN; this.maximumRangeValueIncStdDevRow = -1; this.maximumRangeValueIncStdDevColumn = -1; this.minimumRangeValueIncStdDev = Double.NaN; this.minimumRangeValueIncStdDevRow = -1; this.minimumRangeValueIncStdDevColumn = -1; int rowCount = this.data.getRowCount(); int columnCount = this.data.getColumnCount(); for (int r = 0; r < rowCount; r++) { for (int c = 0; c < columnCount; c++) { MeanAndStandardDeviation masd = (MeanAndStandardDeviation) this.data.getObject(r, c); if (masd == null) { continue; } double m = masd.getMeanValue(); double sd = masd.getStandardDeviationValue(); if (!Double.isNaN(m)) { // update the max value if (Double.isNaN(this.maximumRangeValue)) { this.maximumRangeValue = m; this.maximumRangeValueRow = r; this.maximumRangeValueColumn = c; } else { if (m > this.maximumRangeValue) { this.maximumRangeValue = m; this.maximumRangeValueRow = r; this.maximumRangeValueColumn = c; } } // update the min value if (Double.isNaN(this.minimumRangeValue)) { this.minimumRangeValue = m; this.minimumRangeValueRow = r; this.minimumRangeValueColumn = c; } else { if (m < this.minimumRangeValue) { this.minimumRangeValue = m; this.minimumRangeValueRow = r; this.minimumRangeValueColumn = c; } } if (!Double.isNaN(sd)) { // update the max value if (Double.isNaN(this.maximumRangeValueIncStdDev)) { this.maximumRangeValueIncStdDev = m + sd; this.maximumRangeValueIncStdDevRow = r; this.maximumRangeValueIncStdDevColumn = c; } else { if (m + sd > this.maximumRangeValueIncStdDev) { this.maximumRangeValueIncStdDev = m + sd; this.maximumRangeValueIncStdDevRow = r; this.maximumRangeValueIncStdDevColumn = c; } } // update the min value if (Double.isNaN(this.minimumRangeValueIncStdDev)) { this.minimumRangeValueIncStdDev = m - sd; this.minimumRangeValueIncStdDevRow = r; this.minimumRangeValueIncStdDevColumn = c; } else { if (m - sd < this.minimumRangeValueIncStdDev) { this.minimumRangeValueIncStdDev = m - sd; this.minimumRangeValueIncStdDevRow = r; this.minimumRangeValueIncStdDevColumn = c; } } } } } } } /** * Returns the minimum y-value in the dataset. * * @param includeInterval a flag that determines whether or not the * y-interval is taken into account. * * @return The minimum value. * * @see #getRangeUpperBound(boolean) */ @Override public double getRangeLowerBound(boolean includeInterval) { if (includeInterval && !Double.isNaN(this.minimumRangeValueIncStdDev)) { return this.minimumRangeValueIncStdDev; } else { return this.minimumRangeValue; } } /** * Returns the maximum y-value in the dataset. * * @param includeInterval a flag that determines whether or not the * y-interval is taken into account. * * @return The maximum value. * * @see #getRangeLowerBound(boolean) */ @Override public double getRangeUpperBound(boolean includeInterval) { if (includeInterval && !Double.isNaN(this.maximumRangeValueIncStdDev)) { return this.maximumRangeValueIncStdDev; } else { return this.maximumRangeValue; } } /** * Returns the bounds of the values in this dataset's y-values. * * @param includeInterval a flag that determines whether or not the * y-interval is taken into account. * * @return The range. */ @Override public Range getRangeBounds(boolean includeInterval) { double lower = getRangeLowerBound(includeInterval); double upper = getRangeUpperBound(includeInterval); if (Double.isNaN(lower) && Double.isNaN(upper)) { return null; } return new Range(lower, upper); } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof DefaultStatisticalCategoryDataset)) { return false; } DefaultStatisticalCategoryDataset that = (DefaultStatisticalCategoryDataset) obj; if (!this.data.equals(that.data)) { return false; } return true; } /** * Returns a clone of this dataset. * * @return A clone of this dataset. * * @throws CloneNotSupportedException if cloning cannot be completed. */ @Override public Object clone() throws CloneNotSupportedException { DefaultStatisticalCategoryDataset clone = (DefaultStatisticalCategoryDataset) super.clone(); clone.data = (KeyedObjects2D) this.data.clone(); return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/statistics/HistogramBin.java000066400000000000000000000103701463604235500303120ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * HistogramBin.java * ----------------- * (C) Copyright 2003-present, by Jelai Wang and Contributors. * * Original Author: Jelai Wang (jelaiw AT mindspring.com); * Contributor(s): David Gilbert; * */ package org.jfree.data.statistics; import java.io.Serializable; /** * A bin for the {@link HistogramDataset} class. */ public class HistogramBin implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 7614685080015589931L; /** The number of items in the bin. */ private int count; /** The start boundary. */ private double startBoundary; /** The end boundary. */ private double endBoundary; /** * Creates a new bin. * * @param startBoundary the start boundary. * @param endBoundary the end boundary. */ public HistogramBin(double startBoundary, double endBoundary) { if (startBoundary > endBoundary) { throw new IllegalArgumentException( "HistogramBin(): startBoundary > endBoundary."); } this.count = 0; this.startBoundary = startBoundary; this.endBoundary = endBoundary; } /** * Returns the number of items in the bin. * * @return The item count. */ public int getCount() { return this.count; } /** * Increments the item count. */ public void incrementCount() { this.count++; } /** * Returns the start boundary. * * @return The start boundary. */ public double getStartBoundary() { return this.startBoundary; } /** * Returns the end boundary. * * @return The end boundary. */ public double getEndBoundary() { return this.endBoundary; } /** * Returns the bin width. * * @return The bin width. */ public double getBinWidth() { return this.endBoundary - this.startBoundary; } /** * Tests this object for equality with an arbitrary object. * * @param obj the object to test against. * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (obj == this) { return true; } if (obj instanceof HistogramBin) { HistogramBin bin = (HistogramBin) obj; boolean b0 = bin.startBoundary == this.startBoundary; boolean b1 = bin.endBoundary == this.endBoundary; boolean b2 = bin.count == this.count; return b0 && b1 && b2; } return false; } /** * Returns a clone of the bin. * * @return A clone. * * @throws CloneNotSupportedException not thrown by this class. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/statistics/HistogramDataset.java000066400000000000000000000374621463604235500312020ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * HistogramDataset.java * --------------------- * (C) Copyright 2003-present, by Jelai Wang and Contributors. * * Original Author: Jelai Wang (jelaiw AT mindspring.com); * Contributor(s): David Gilbert; * Cameron Hayne; * Rikard Bj?rklind; * Thomas A Caswell (patch 2902842); * */ package org.jfree.data.statistics; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.general.DatasetChangeEvent; import org.jfree.data.xy.AbstractIntervalXYDataset; import org.jfree.data.xy.IntervalXYDataset; /** * A dataset that can be used for creating histograms. * * @see SimpleHistogramDataset */ public class HistogramDataset extends AbstractIntervalXYDataset implements IntervalXYDataset, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -6341668077370231153L; /** A list of maps. */ private List list; /** The histogram type. */ private HistogramType type; /** * Creates a new (empty) dataset with a default type of * {@link HistogramType}.FREQUENCY. */ public HistogramDataset() { this.list = new ArrayList(); this.type = HistogramType.FREQUENCY; } /** * Returns the histogram type. * * @return The type (never {@code null}). */ public HistogramType getType() { return this.type; } /** * Sets the histogram type and sends a {@link DatasetChangeEvent} to all * registered listeners. * * @param type the type ({@code null} not permitted). */ public void setType(HistogramType type) { Args.nullNotPermitted(type, "type"); this.type = type; fireDatasetChanged(); } /** * Adds a series to the dataset, using the specified number of bins, * and sends a {@link DatasetChangeEvent} to all registered listeners. * * @param key the series key ({@code null} not permitted). * @param values the values ({@code null} not permitted). * @param bins the number of bins (must be at least 1). */ public void addSeries(Comparable key, double[] values, int bins) { // defer argument checking... double minimum = getMinimum(values); double maximum = getMaximum(values); addSeries(key, values, bins, minimum, maximum); } /** * Adds a series to the dataset. Any data value less than minimum will be * assigned to the first bin, and any data value greater than maximum will * be assigned to the last bin. Values falling on the boundary of * adjacent bins will be assigned to the higher indexed bin. * * @param key the series key ({@code null} not permitted). * @param values the raw observations. * @param bins the number of bins (must be at least 1). * @param minimum the lower bound of the bin range. * @param maximum the upper bound of the bin range. */ public void addSeries(Comparable key, double[] values, int bins, double minimum, double maximum) { Args.nullNotPermitted(key, "key"); Args.nullNotPermitted(values, "values"); if (bins < 1) { throw new IllegalArgumentException( "The 'bins' value must be at least 1."); } double binWidth = (maximum - minimum) / bins; double lower = minimum; double upper; List binList = new ArrayList(bins); for (int i = 0; i < bins; i++) { HistogramBin bin; // make sure bins[bins.length]'s upper boundary ends at maximum // to avoid the rounding issue. the bins[0] lower boundary is // guaranteed start from min if (i == bins - 1) { bin = new HistogramBin(lower, maximum); } else { upper = minimum + (i + 1) * binWidth; bin = new HistogramBin(lower, upper); lower = upper; } binList.add(bin); } // fill the bins for (int i = 0; i < values.length; i++) { int binIndex = bins - 1; if (values[i] < maximum) { double fraction = (values[i] - minimum) / (maximum - minimum); if (fraction < 0.0) { fraction = 0.0; } binIndex = (int) (fraction * bins); // rounding could result in binIndex being equal to bins // which will cause an IndexOutOfBoundsException - see bug // report 1553088 if (binIndex >= bins) { binIndex = bins - 1; } } HistogramBin bin = (HistogramBin) binList.get(binIndex); bin.incrementCount(); } // generic map for each series Map map = new HashMap(); map.put("key", key); map.put("bins", binList); map.put("values.length", values.length); map.put("bin width", binWidth); this.list.add(map); fireDatasetChanged(); } /** * Returns the minimum value in an array of values. * * @param values the values ({@code null} not permitted and * zero-length array not permitted). * * @return The minimum value. */ private double getMinimum(double[] values) { if (values == null || values.length < 1) { throw new IllegalArgumentException( "Null or zero length 'values' argument."); } double min = Double.MAX_VALUE; for (int i = 0; i < values.length; i++) { if (values[i] < min) { min = values[i]; } } return min; } /** * Returns the maximum value in an array of values. * * @param values the values ({@code null} not permitted and * zero-length array not permitted). * * @return The maximum value. */ private double getMaximum(double[] values) { if (values == null || values.length < 1) { throw new IllegalArgumentException( "Null or zero length 'values' argument."); } double max = -Double.MAX_VALUE; for (int i = 0; i < values.length; i++) { if (values[i] > max) { max = values[i]; } } return max; } /** * Returns the bins for a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * * @return A list of bins. * * @throws IndexOutOfBoundsException if {@code series} is outside the * specified range. */ List getBins(int series) { Map map = (Map) this.list.get(series); return (List) map.get("bins"); } /** * Returns the total number of observations for a series. * * @param series the series index. * * @return The total. */ private int getTotal(int series) { Map map = (Map) this.list.get(series); return ((Integer) map.get("values.length")); } /** * Returns the bin width for a series. * * @param series the series index (zero based). * * @return The bin width. */ private double getBinWidth(int series) { Map map = (Map) this.list.get(series); return ((Double) map.get("bin width")); } /** * Returns the number of series in the dataset. * * @return The series count. */ @Override public int getSeriesCount() { return this.list.size(); } /** * Returns the key for a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * * @return The series key. * * @throws IndexOutOfBoundsException if {@code series} is outside the * specified range. */ @Override public Comparable getSeriesKey(int series) { Map map = (Map) this.list.get(series); return (Comparable) map.get("key"); } /** * Returns the number of data items for a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * * @return The item count. * * @throws IndexOutOfBoundsException if {@code series} is outside the * specified range. */ @Override public int getItemCount(int series) { return getBins(series).size(); } /** * Returns the X value for a bin. This value won't be used for plotting * histograms, since the renderer will ignore it. But other renderers can * use it (for example, you could use the dataset to create a line * chart). * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item index (zero based). * * @return The start value. * * @throws IndexOutOfBoundsException if {@code series} is outside the * specified range. */ @Override public Number getX(int series, int item) { List bins = getBins(series); HistogramBin bin = (HistogramBin) bins.get(item); double x = (bin.getStartBoundary() + bin.getEndBoundary()) / 2.; return x; } /** * Returns the y-value for a bin (calculated to take into account the * histogram type). * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item index (zero based). * * @return The y-value. * * @throws IndexOutOfBoundsException if {@code series} is outside the * specified range. */ @Override public Number getY(int series, int item) { List bins = getBins(series); HistogramBin bin = (HistogramBin) bins.get(item); double total = getTotal(series); double binWidth = getBinWidth(series); if (this.type == HistogramType.FREQUENCY) { return bin.getCount(); } else if (this.type == HistogramType.RELATIVE_FREQUENCY) { return bin.getCount() / total; } else if (this.type == HistogramType.SCALE_AREA_TO_1) { return bin.getCount() / (binWidth * total); } else { // pretty sure this shouldn't ever happen throw new IllegalStateException(); } } /** * Returns the start value for a bin. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item index (zero based). * * @return The start value. * * @throws IndexOutOfBoundsException if {@code series} is outside the * specified range. */ @Override public Number getStartX(int series, int item) { List bins = getBins(series); HistogramBin bin = (HistogramBin) bins.get(item); return bin.getStartBoundary(); } /** * Returns the end value for a bin. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item index (zero based). * * @return The end value. * * @throws IndexOutOfBoundsException if {@code series} is outside the * specified range. */ @Override public Number getEndX(int series, int item) { List bins = getBins(series); HistogramBin bin = (HistogramBin) bins.get(item); return bin.getEndBoundary(); } /** * Returns the start y-value for a bin (which is the same as the y-value, * this method exists only to support the general form of the * {@link IntervalXYDataset} interface). * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item index (zero based). * * @return The y-value. * * @throws IndexOutOfBoundsException if {@code series} is outside the * specified range. */ @Override public Number getStartY(int series, int item) { return getY(series, item); } /** * Returns the end y-value for a bin (which is the same as the y-value, * this method exists only to support the general form of the * {@link IntervalXYDataset} interface). * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item index (zero based). * * @return The Y value. * * @throws IndexOutOfBoundsException if {@code series} is outside the * specified range. */ @Override public Number getEndY(int series, int item) { return getY(series, item); } /** * Tests this dataset for equality with an arbitrary object. * * @param obj the object to test against ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof HistogramDataset)) { return false; } HistogramDataset that = (HistogramDataset) obj; if (!Objects.equals(this.type, that.type)) { return false; } if (!Objects.equals(this.list, that.list)) { return false; } return true; } /** * Returns a clone of the dataset. * * @return A clone of the dataset. * * @throws CloneNotSupportedException if the object cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { HistogramDataset clone = (HistogramDataset) super.clone(); int seriesCount = getSeriesCount(); clone.list = new java.util.ArrayList(seriesCount); for (int i = 0; i < seriesCount; i++) { clone.list.add(new HashMap((Map) this.list.get(i))); } return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/statistics/HistogramType.java000066400000000000000000000100441463604235500305210ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * HistogramType.java * ------------------ * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.statistics; import java.io.ObjectStreamException; import java.io.Serializable; /** * A class for creating constants to represent the histogram type. See Bloch's * enum tip in 'Effective Java'. */ public class HistogramType implements Serializable { /** For serialization. */ private static final long serialVersionUID = 2618927186251997727L; /** Frequency histogram. */ public static final HistogramType FREQUENCY = new HistogramType("FREQUENCY"); /** Relative frequency. */ public static final HistogramType RELATIVE_FREQUENCY = new HistogramType("RELATIVE_FREQUENCY"); /** Scale area to one. */ public static final HistogramType SCALE_AREA_TO_1 = new HistogramType("SCALE_AREA_TO_1"); /** The type name. */ private String name; /** * Creates a new type. * * @param name the name. */ private HistogramType(String name) { this.name = name; } /** * Returns a string representing the object. * * @return The string. */ @Override public String toString() { return this.name; } /** * Tests this type for equality with an arbitrary object. * * @param obj the object to test against. * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (obj == this) { return true; } if (!(obj instanceof HistogramType)) { return false; } HistogramType t = (HistogramType) obj; if (!this.name.equals(t.name)) { return false; } return true; } /** * Returns a hash code value for the object. * * @return The hashcode */ @Override public int hashCode() { return this.name.hashCode(); } /** * Ensures that serialization returns the unique instances. * * @return The object. * * @throws ObjectStreamException if there is a problem. */ private Object readResolve() throws ObjectStreamException { if (this.equals(HistogramType.FREQUENCY)) { return HistogramType.FREQUENCY; } else if (this.equals(HistogramType.RELATIVE_FREQUENCY)) { return HistogramType.RELATIVE_FREQUENCY; } else if (this.equals(HistogramType.SCALE_AREA_TO_1)) { return HistogramType.SCALE_AREA_TO_1; } return null; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/statistics/MeanAndStandardDeviation.java000066400000000000000000000116321463604235500325550ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------- * MeanAndStandardDeviation.java * ----------------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.statistics; import java.io.Serializable; import java.util.Objects; /** * A simple data structure that holds a mean value and a standard deviation * value. This is used in the * {@link org.jfree.data.statistics.DefaultStatisticalCategoryDataset} class. */ public class MeanAndStandardDeviation implements Serializable { /** For serialization. */ private static final long serialVersionUID = 7413468697315721515L; /** The mean. */ private Number mean; /** The standard deviation. */ private Number standardDeviation; /** * Creates a new mean and standard deviation record. * * @param mean the mean. * @param standardDeviation the standard deviation. */ public MeanAndStandardDeviation(double mean, double standardDeviation) { this(Double.valueOf(mean), Double.valueOf(standardDeviation)); } /** * Creates a new mean and standard deviation record. * * @param mean the mean ({@code null} permitted). * @param standardDeviation the standard deviation ({@code null} * permitted. */ public MeanAndStandardDeviation(Number mean, Number standardDeviation) { this.mean = mean; this.standardDeviation = standardDeviation; } /** * Returns the mean. * * @return The mean. */ public Number getMean() { return this.mean; } /** * Returns the mean as a double primitive. If the underlying mean is * {@code null}, this method will return {@code Double.NaN}. * * @return The mean. * * @see #getMean() */ public double getMeanValue() { double result = Double.NaN; if (this.mean != null) { result = this.mean.doubleValue(); } return result; } /** * Returns the standard deviation. * * @return The standard deviation. */ public Number getStandardDeviation() { return this.standardDeviation; } /** * Returns the standard deviation as a double primitive. If the underlying * standard deviation is {@code null}, this method will return * {@code Double.NaN}. * * @return The standard deviation. */ public double getStandardDeviationValue() { double result = Double.NaN; if (this.standardDeviation != null) { result = this.standardDeviation.doubleValue(); } return result; } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof MeanAndStandardDeviation)) { return false; } MeanAndStandardDeviation that = (MeanAndStandardDeviation) obj; if (!Objects.equals(this.mean, that.mean)) { return false; } if (!Objects.equals(this.standardDeviation, that.standardDeviation)) { return false; } return true; } /** * Returns a string representing this instance. * * @return A string. */ @Override public String toString() { return "[" + this.mean + ", " + this.standardDeviation + "]"; } }jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/statistics/MultiValueCategoryDataset.java000066400000000000000000000046151463604235500330240ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------ * MultiValueCategoryDataset.java * ------------------------------ * (C) Copyright 2007-present, by David Forslund and Contributors. * * Original Author: David Forslund; * Contributor(s): David Gilbert; * */ package org.jfree.data.statistics; import java.util.List; import org.jfree.data.category.CategoryDataset; /** * A category dataset that defines multiple values for each item. */ public interface MultiValueCategoryDataset extends CategoryDataset { /** * Returns a list (possibly empty) of the values for the specified item. * The returned list should be unmodifiable. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The list of values. */ List getValues(int row, int column); /** * Returns a list (possibly empty) of the values for the specified item. * The returned list should be unmodifiable. * * @param rowKey the row key ({@code null} not permitted). * @param columnKey the column key ({@code null} not permitted). * * @return The list of values. */ List getValues(Comparable rowKey, Comparable columnKey); }jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/statistics/Regression.java000066400000000000000000000306561463604235500300550ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * Regression.java * --------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Peter Kolb (patch 2795746); * */ package org.jfree.data.statistics; import org.jfree.chart.util.Args; import org.jfree.data.xy.XYDataset; /** * A utility class for fitting regression curves to data. */ public abstract class Regression { /** * Returns the parameters 'a' and 'b' for an equation y = a + bx, fitted to * the data using ordinary least squares regression. The result is * returned as a double[], where result[0] --> a, and result[1] --> b. * * @param data the data. * * @return The parameters. */ public static double[] getOLSRegression(double[][] data) { int n = data.length; if (n < 2) { throw new IllegalArgumentException("Not enough data."); } double sumX = 0; double sumY = 0; double sumXX = 0; double sumXY = 0; for (int i = 0; i < n; i++) { double x = data[i][0]; double y = data[i][1]; sumX += x; sumY += y; double xx = x * x; sumXX += xx; double xy = x * y; sumXY += xy; } double sxx = sumXX - (sumX * sumX) / n; double sxy = sumXY - (sumX * sumY) / n; double xbar = sumX / n; double ybar = sumY / n; double[] result = new double[2]; result[1] = sxy / sxx; result[0] = ybar - result[1] * xbar; return result; } /** * Returns the parameters 'a' and 'b' for an equation y = a + bx, fitted to * the data using ordinary least squares regression. The result is returned * as a double[], where result[0] --> a, and result[1] --> b. * * @param data the data. * @param series the series (zero-based index). * * @return The parameters. */ public static double[] getOLSRegression(XYDataset data, int series) { int n = data.getItemCount(series); if (n < 2) { throw new IllegalArgumentException("Not enough data."); } double sumX = 0; double sumY = 0; double sumXX = 0; double sumXY = 0; for (int i = 0; i < n; i++) { double x = data.getXValue(series, i); double y = data.getYValue(series, i); sumX += x; sumY += y; double xx = x * x; sumXX += xx; double xy = x * y; sumXY += xy; } double sxx = sumXX - (sumX * sumX) / n; double sxy = sumXY - (sumX * sumY) / n; double xbar = sumX / n; double ybar = sumY / n; double[] result = new double[2]; result[1] = sxy / sxx; result[0] = ybar - result[1] * xbar; return result; } /** * Returns the parameters 'a' and 'b' for an equation y = ax^b, fitted to * the data using a power regression equation. The result is returned as * an array, where double[0] --> a, and double[1] --> b. * * @param data the data. * * @return The parameters. */ public static double[] getPowerRegression(double[][] data) { int n = data.length; if (n < 2) { throw new IllegalArgumentException("Not enough data."); } double sumX = 0; double sumY = 0; double sumXX = 0; double sumXY = 0; for (int i = 0; i < n; i++) { double x = Math.log(data[i][0]); double y = Math.log(data[i][1]); sumX += x; sumY += y; double xx = x * x; sumXX += xx; double xy = x * y; sumXY += xy; } double sxx = sumXX - (sumX * sumX) / n; double sxy = sumXY - (sumX * sumY) / n; double xbar = sumX / n; double ybar = sumY / n; double[] result = new double[2]; result[1] = sxy / sxx; result[0] = Math.pow(Math.exp(1.0), ybar - result[1] * xbar); return result; } /** * Returns the parameters 'a' and 'b' for an equation y = ax^b, fitted to * the data using a power regression equation. The result is returned as * an array, where double[0] --> a, and double[1] --> b. * * @param data the data. * @param series the series to fit the regression line against. * * @return The parameters. */ public static double[] getPowerRegression(XYDataset data, int series) { int n = data.getItemCount(series); if (n < 2) { throw new IllegalArgumentException("Not enough data."); } double sumX = 0; double sumY = 0; double sumXX = 0; double sumXY = 0; for (int i = 0; i < n; i++) { double x = Math.log(data.getXValue(series, i)); double y = Math.log(data.getYValue(series, i)); sumX += x; sumY += y; double xx = x * x; sumXX += xx; double xy = x * y; sumXY += xy; } double sxx = sumXX - (sumX * sumX) / n; double sxy = sumXY - (sumX * sumY) / n; double xbar = sumX / n; double ybar = sumY / n; double[] result = new double[2]; result[1] = sxy / sxx; result[0] = Math.pow(Math.exp(1.0), ybar - result[1] * xbar); return result; } /** * Returns the parameters 'a0', 'a1', 'a2', ..., 'an' for a polynomial * function of order n, y = a0 + a1 * x + a2 * x^2 + ... + an * x^n, * fitted to the data using a polynomial regression equation. * The result is returned as an array with a length of n + 2, * where double[0] --> a0, double[1] --> a1, .., double[n] --> an. * and double[n + 1] is the correlation coefficient R2 * Reference: J. D. Faires, R. L. Burden, Numerische Methoden (german * edition), pp. 243ff and 327ff. * * @param dataset the dataset ({@code null} not permitted). * @param series the series to fit the regression line against (the series * must have at least order + 1 non-NaN items). * @param order the order of the function (> 0). * * @return The parameters. */ public static double[] getPolynomialRegression(XYDataset dataset, int series, int order) { Args.nullNotPermitted(dataset, "dataset"); int itemCount = dataset.getItemCount(series); if (itemCount < order + 1) { throw new IllegalArgumentException("Not enough data."); } int validItems = 0; double[][] data = new double[2][itemCount]; for(int item = 0; item < itemCount; item++){ double x = dataset.getXValue(series, item); double y = dataset.getYValue(series, item); if (!Double.isNaN(x) && !Double.isNaN(y)){ data[0][validItems] = x; data[1][validItems] = y; validItems++; } } if (validItems < order + 1) { throw new IllegalArgumentException("Not enough data."); } int equations = order + 1; int coefficients = order + 2; double[] result = new double[equations + 1]; double[][] matrix = new double[equations][coefficients]; double sumX = 0.0; double sumY = 0.0; for(int item = 0; item < validItems; item++){ sumX += data[0][item]; sumY += data[1][item]; for(int eq = 0; eq < equations; eq++){ for(int coe = 0; coe < coefficients - 1; coe++){ matrix[eq][coe] += Math.pow(data[0][item],eq + coe); } matrix[eq][coefficients - 1] += data[1][item] * Math.pow(data[0][item],eq); } } double[][] subMatrix = calculateSubMatrix(matrix); for (int eq = 1; eq < equations; eq++) { matrix[eq][0] = 0; if (coefficients - 1 >= 0) System.arraycopy(subMatrix[eq - 1], 0, matrix[eq], 1, coefficients - 1); } for (int eq = equations - 1; eq > -1; eq--) { double value = matrix[eq][coefficients - 1]; for (int coe = eq; coe < coefficients -1; coe++) { value -= matrix[eq][coe] * result[coe]; } result[eq] = value / matrix[eq][eq]; } double meanY = sumY / validItems; double yObsSquare = 0.0; double yRegSquare = 0.0; for (int item = 0; item < validItems; item++) { double yCalc = 0; for (int eq = 0; eq < equations; eq++) { yCalc += result[eq] * Math.pow(data[0][item],eq); } yRegSquare += Math.pow(yCalc - meanY, 2); yObsSquare += Math.pow(data[1][item] - meanY, 2); } double rSquare = yRegSquare / yObsSquare; result[equations] = rSquare; return result; } /** * Returns a matrix with the following features: (1) the number of rows * and columns is 1 less than that of the original matrix; (2)the matrix * is triangular, i.e. all elements a (row, column) with column > row are * zero. This method is used for calculating a polynomial regression. * * @param matrix the start matrix. * * @return The new matrix. */ private static double[][] calculateSubMatrix(double[][] matrix){ int equations = matrix.length; int coefficients = matrix[0].length; double[][] result = new double[equations - 1][coefficients - 1]; for (int eq = 1; eq < equations; eq++) { double factor = matrix[0][0] / matrix[eq][0]; for (int coe = 1; coe < coefficients; coe++) { result[eq - 1][coe -1] = matrix[0][coe] - matrix[eq][coe] * factor; } } if (equations == 1) { return result; } // check for zero pivot element if (result[0][0] == 0) { boolean found = false; for (int i = 0; i < result.length; i ++) { if (result[i][0] != 0) { found = true; double[] temp = result[0]; System.arraycopy(result[i], 0, result[0], 0, result[i].length); System.arraycopy(temp, 0, result[i], 0, temp.length); break; } } if (!found) { //System.out.println("Equation has no solution!"); return new double[equations - 1][coefficients - 1]; } } double[][] subMatrix = calculateSubMatrix(result); for (int eq = 1; eq < equations - 1; eq++) { result[eq][0] = 0; if (coefficients - 1 - 1 >= 0) System.arraycopy(subMatrix[eq - 1], 0, result[eq], 1, coefficients - 1 - 1); } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/statistics/SimpleHistogramBin.java000066400000000000000000000167361463604235500315000ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * SimpleHistogramBin.java * ----------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.statistics; import java.io.Serializable; import org.jfree.chart.util.PublicCloneable; /** * A bin for the {@link SimpleHistogramDataset}. */ public class SimpleHistogramBin implements Comparable, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 3480862537505941742L; /** The lower bound for the bin. */ private double lowerBound; /** The upper bound for the bin. */ private double upperBound; /** * A flag that controls whether the lower bound is included in the bin * range. */ private boolean includeLowerBound; /** * A flag that controls whether the upper bound is included in the bin * range. */ private boolean includeUpperBound; /** The item count. */ private int itemCount; /** * Creates a new bin. * * @param lowerBound the lower bound (inclusive). * @param upperBound the upper bound (inclusive); */ public SimpleHistogramBin(double lowerBound, double upperBound) { this(lowerBound, upperBound, true, true); } /** * Creates a new bin. * * @param lowerBound the lower bound. * @param upperBound the upper bound. * @param includeLowerBound include the lower bound? * @param includeUpperBound include the upper bound? */ public SimpleHistogramBin(double lowerBound, double upperBound, boolean includeLowerBound, boolean includeUpperBound) { if (lowerBound >= upperBound) { throw new IllegalArgumentException("Invalid bounds"); } this.lowerBound = lowerBound; this.upperBound = upperBound; this.includeLowerBound = includeLowerBound; this.includeUpperBound = includeUpperBound; this.itemCount = 0; } /** * Returns the lower bound. * * @return The lower bound. */ public double getLowerBound() { return this.lowerBound; } /** * Return the upper bound. * * @return The upper bound. */ public double getUpperBound() { return this.upperBound; } /** * Returns the item count. * * @return The item count. */ public int getItemCount() { return this.itemCount; } /** * Sets the item count. * * @param count the item count. */ public void setItemCount(int count) { this.itemCount = count; } /** * Returns {@code true} if the specified value belongs in the bin, * and {@code false} otherwise. * * @param value the value. * * @return A boolean. */ public boolean accepts(double value) { if (Double.isNaN(value)) { return false; } if (value < this.lowerBound) { return false; } if (value > this.upperBound) { return false; } if (value == this.lowerBound) { return this.includeLowerBound; } if (value == this.upperBound) { return this.includeUpperBound; } return true; } /** * Returns {@code true} if this bin overlaps with the specified bin, * and {@code false} otherwise. * * @param bin the other bin ({@code null} not permitted). * * @return A boolean. */ public boolean overlapsWith(SimpleHistogramBin bin) { if (this.upperBound < bin.lowerBound) { return false; } if (this.lowerBound > bin.upperBound) { return false; } if (this.upperBound == bin.lowerBound) { return this.includeUpperBound && bin.includeLowerBound; } if (this.lowerBound == bin.upperBound) { return this.includeLowerBound && bin.includeUpperBound; } return true; } /** * Compares the bin to an arbitrary object and returns the relative * ordering. * * @param obj the object. * * @return An integer indicating the relative ordering of the this bin and * the given object. */ @Override public int compareTo(Object obj) { if (!(obj instanceof SimpleHistogramBin)) { return 0; } SimpleHistogramBin bin = (SimpleHistogramBin) obj; if (this.lowerBound < bin.lowerBound) { return -1; } if (this.lowerBound > bin.lowerBound) { return 1; } // lower bounds are the same if (this.upperBound < bin.upperBound) { return -1; } if (this.upperBound > bin.upperBound) { return 1; } return 0; } /** * Tests this bin for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (!(obj instanceof SimpleHistogramBin)) { return false; } SimpleHistogramBin that = (SimpleHistogramBin) obj; if (this.lowerBound != that.lowerBound) { return false; } if (this.upperBound != that.upperBound) { return false; } if (this.includeLowerBound != that.includeLowerBound) { return false; } if (this.includeUpperBound != that.includeUpperBound) { return false; } if (this.itemCount != that.itemCount) { return false; } return true; } /** * Returns a clone of the bin. * * @return A clone. * * @throws CloneNotSupportedException not thrown by this class. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/statistics/SimpleHistogramDataset.java000066400000000000000000000350261463604235500323460ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * SimpleHistogramDataset.java * --------------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Sergei Ivanov; * */ package org.jfree.data.statistics; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.DomainOrder; import org.jfree.data.general.DatasetChangeEvent; import org.jfree.data.xy.AbstractIntervalXYDataset; import org.jfree.data.xy.IntervalXYDataset; /** * A dataset used for creating simple histograms with custom defined bins. * * @see HistogramDataset */ public class SimpleHistogramDataset extends AbstractIntervalXYDataset implements IntervalXYDataset, Cloneable, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 7997996479768018443L; /** The series key. */ private Comparable key; /** The bins. */ private List bins; /** * A flag that controls whether or not the bin count is divided by the * bin size. */ private boolean adjustForBinSize; /** * Creates a new histogram dataset. Note that the * {@code adjustForBinSize} flag defaults to {@code true}. * * @param key the series key ({@code null} not permitted). */ public SimpleHistogramDataset(Comparable key) { Args.nullNotPermitted(key, "key"); this.key = key; this.bins = new ArrayList(); this.adjustForBinSize = true; } /** * Returns a flag that controls whether or not the bin count is divided by * the bin size in the {@link #getXValue(int, int)} method. * * @return A boolean. * * @see #setAdjustForBinSize(boolean) */ public boolean getAdjustForBinSize() { return this.adjustForBinSize; } /** * Sets the flag that controls whether or not the bin count is divided by * the bin size in the {@link #getYValue(int, int)} method, and sends a * {@link DatasetChangeEvent} to all registered listeners. * * @param adjust the flag. * * @see #getAdjustForBinSize() */ public void setAdjustForBinSize(boolean adjust) { this.adjustForBinSize = adjust; notifyListeners(new DatasetChangeEvent(this, this)); } /** * Returns the number of series in the dataset (always 1 for this dataset). * * @return The series count. */ @Override public int getSeriesCount() { return 1; } /** * Returns the key for a series. Since this dataset only stores a single * series, the {@code series} argument is ignored. * * @param series the series (zero-based index, ignored in this dataset). * * @return The key for the series. */ @Override public Comparable getSeriesKey(int series) { return this.key; } /** * Returns the order of the domain (or X) values returned by the dataset. * * @return The order (never {@code null}). */ @Override public DomainOrder getDomainOrder() { return DomainOrder.ASCENDING; } /** * Returns the number of items in a series. Since this dataset only stores * a single series, the {@code series} argument is ignored. * * @param series the series index (zero-based, ignored in this dataset). * * @return The item count. */ @Override public int getItemCount(int series) { return this.bins.size(); } /** * Adds a bin to the dataset. An exception is thrown if the bin overlaps * with any existing bin in the dataset. * * @param bin the bin ({@code null} not permitted). * * @see #removeAllBins() */ public void addBin(SimpleHistogramBin bin) { // check that the new bin doesn't overlap with any existing bin Iterator iterator = this.bins.iterator(); while (iterator.hasNext()) { SimpleHistogramBin existingBin = (SimpleHistogramBin) iterator.next(); if (bin.overlapsWith(existingBin)) { throw new RuntimeException("Overlapping bin"); } } this.bins.add(bin); Collections.sort(this.bins); } /** * Adds an observation to the dataset (by incrementing the item count for * the appropriate bin). A runtime exception is thrown if the value does * not fit into any bin. * * @param value the value. */ public void addObservation(double value) { addObservation(value, true); } /** * Adds an observation to the dataset (by incrementing the item count for * the appropriate bin). A runtime exception is thrown if the value does * not fit into any bin. * * @param value the value. * @param notify send {@link DatasetChangeEvent} to listeners? */ public void addObservation(double value, boolean notify) { boolean placed = false; Iterator iterator = this.bins.iterator(); while (iterator.hasNext() && !placed) { SimpleHistogramBin bin = (SimpleHistogramBin) iterator.next(); if (bin.accepts(value)) { bin.setItemCount(bin.getItemCount() + 1); placed = true; } } if (!placed) { throw new RuntimeException("No bin."); } if (notify) { notifyListeners(new DatasetChangeEvent(this, this)); } } /** * Adds a set of values to the dataset and sends a * {@link DatasetChangeEvent} to all registered listeners. * * @param values the values ({@code null} not permitted). * * @see #clearObservations() */ public void addObservations(double[] values) { for (int i = 0; i < values.length; i++) { addObservation(values[i], false); } notifyListeners(new DatasetChangeEvent(this, this)); } /** * Removes all current observation data and sends a * {@link DatasetChangeEvent} to all registered listeners. * * @see #addObservations(double[]) * @see #removeAllBins() */ public void clearObservations() { Iterator iterator = this.bins.iterator(); while (iterator.hasNext()) { SimpleHistogramBin bin = (SimpleHistogramBin) iterator.next(); bin.setItemCount(0); } notifyListeners(new DatasetChangeEvent(this, this)); } /** * Removes all bins and sends a {@link DatasetChangeEvent} to all * registered listeners. * * @see #addBin(SimpleHistogramBin) */ public void removeAllBins() { this.bins = new ArrayList(); notifyListeners(new DatasetChangeEvent(this, this)); } /** * Returns the x-value for an item within a series. The x-values may or * may not be returned in ascending order, that is up to the class * implementing the interface. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The x-value (never {@code null}). */ @Override public Number getX(int series, int item) { return getXValue(series, item); } /** * Returns the x-value (as a double primitive) for an item within a series. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The x-value. */ @Override public double getXValue(int series, int item) { SimpleHistogramBin bin = (SimpleHistogramBin) this.bins.get(item); return (bin.getLowerBound() + bin.getUpperBound()) / 2.0; } /** * Returns the y-value for an item within a series. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The y-value (possibly {@code null}). */ @Override public Number getY(int series, int item) { return getYValue(series, item); } /** * Returns the y-value (as a double primitive) for an item within a series. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The y-value. * * @see #getAdjustForBinSize() */ @Override public double getYValue(int series, int item) { SimpleHistogramBin bin = (SimpleHistogramBin) this.bins.get(item); if (this.adjustForBinSize) { return bin.getItemCount() / (bin.getUpperBound() - bin.getLowerBound()); } else { return bin.getItemCount(); } } /** * Returns the starting X value for the specified series and item. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public Number getStartX(int series, int item) { return getStartXValue(series, item); } /** * Returns the start x-value (as a double primitive) for an item within a * series. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The start x-value. */ @Override public double getStartXValue(int series, int item) { SimpleHistogramBin bin = (SimpleHistogramBin) this.bins.get(item); return bin.getLowerBound(); } /** * Returns the ending X value for the specified series and item. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public Number getEndX(int series, int item) { return getEndXValue(series, item); } /** * Returns the end x-value (as a double primitive) for an item within a * series. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The end x-value. */ @Override public double getEndXValue(int series, int item) { SimpleHistogramBin bin = (SimpleHistogramBin) this.bins.get(item); return bin.getUpperBound(); } /** * Returns the starting Y value for the specified series and item. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public Number getStartY(int series, int item) { return getY(series, item); } /** * Returns the start y-value (as a double primitive) for an item within a * series. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The start y-value. */ @Override public double getStartYValue(int series, int item) { return getYValue(series, item); } /** * Returns the ending Y value for the specified series and item. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public Number getEndY(int series, int item) { return getY(series, item); } /** * Returns the end y-value (as a double primitive) for an item within a * series. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The end y-value. */ @Override public double getEndYValue(int series, int item) { return getYValue(series, item); } /** * Compares the dataset for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof SimpleHistogramDataset)) { return false; } SimpleHistogramDataset that = (SimpleHistogramDataset) obj; if (!this.key.equals(that.key)) { return false; } if (this.adjustForBinSize != that.adjustForBinSize) { return false; } if (!this.bins.equals(that.bins)) { return false; } return true; } /** * Returns a clone of the dataset. * * @return A clone. * * @throws CloneNotSupportedException not thrown by this class, but maybe * by subclasses (if any). */ @Override public Object clone() throws CloneNotSupportedException { SimpleHistogramDataset clone = (SimpleHistogramDataset) super.clone(); clone.bins = (List) ObjectUtils.deepClone(this.bins); return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/statistics/StatisticalCategoryDataset.java000066400000000000000000000056111463604235500332160ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------- * StatisticalCategoryDataset.java * ------------------------------- * (C) Copyright 2002-present, by Pascal Collet and Contributors. * * Original Author: Pascal Collet; * Contributor(s): David Gilbert; * */ package org.jfree.data.statistics; import org.jfree.data.category.CategoryDataset; /** * A category dataset that defines a mean and standard deviation value for * each item. */ public interface StatisticalCategoryDataset extends CategoryDataset { /** * Returns the mean value for an item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The mean value (possibly {@code null}). */ Number getMeanValue(int row, int column); /** * Returns the mean value for an item. * * @param rowKey the row key. * @param columnKey the columnKey. * * @return The mean value (possibly {@code null}). */ Number getMeanValue(Comparable rowKey, Comparable columnKey); /** * Returns the standard deviation value for an item. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The standard deviation (possibly {@code null}). */ Number getStdDevValue(int row, int column); /** * Returns the standard deviation value for an item. * * @param rowKey the row key. * @param columnKey the columnKey. * * @return The standard deviation (possibly {@code null}). */ Number getStdDevValue(Comparable rowKey, Comparable columnKey); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/statistics/Statistics.java000066400000000000000000000372621463604235500300670ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * Statistics.java * --------------- * (C) Copyright 2000-present, by Matthew Wright and Contributors. * * Original Author: Matthew Wright; * Contributor(s): David Gilbert; * */ package org.jfree.data.statistics; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.jfree.chart.util.Args; /** * A utility class that provides some common statistical functions. */ public abstract class Statistics { /** * Returns the mean of an array of numbers. This is equivalent to calling * {@code calculateMean(values, true)}. * * @param values the values ({@code null} not permitted). * * @return The mean. */ public static double calculateMean(Number[] values) { return calculateMean(values, true); } /** * Returns the mean of an array of numbers. * * @param values the values ({@code null} not permitted). * @param includeNullAndNaN a flag that controls whether or not * {@code null} and {@code Double.NaN} values are included * in the calculation (if either is present in the array, the result is * {@link Double#NaN}). * * @return The mean. */ public static double calculateMean(Number[] values, boolean includeNullAndNaN) { Args.nullNotPermitted(values, "values"); double sum = 0.0; double current; int counter = 0; for (int i = 0; i < values.length; i++) { // treat nulls the same as NaNs if (values[i] != null) { current = values[i].doubleValue(); } else { current = Double.NaN; } // calculate the sum and count if (includeNullAndNaN || !Double.isNaN(current)) { sum = sum + current; counter++; } } double result = (sum / counter); return result; } /** * Returns the mean of a collection of {@code Number} objects. * * @param values the values ({@code null} not permitted). * * @return The mean. */ public static double calculateMean(Collection values) { return calculateMean(values, true); } /** * Returns the mean of a collection of {@code Number} objects. * * @param values the values ({@code null} not permitted). * @param includeNullAndNaN a flag that controls whether or not * {@code null} and {@code Double.NaN} values are included * in the calculation (if either is present in the array, the result is * {@link Double#NaN}). * * @return The mean. */ public static double calculateMean(Collection values, boolean includeNullAndNaN) { Args.nullNotPermitted(values, "values"); int count = 0; double total = 0.0; Iterator iterator = values.iterator(); while (iterator.hasNext()) { Object object = iterator.next(); if (object == null) { if (includeNullAndNaN) { return Double.NaN; } } else { if (object instanceof Number) { Number number = (Number) object; double value = number.doubleValue(); if (Double.isNaN(value)) { if (includeNullAndNaN) { return Double.NaN; } } else { total = total + number.doubleValue(); count = count + 1; } } } } return total / count; } /** * Calculates the median for a list of values ({@code Number} objects). * The list of values will be copied, and the copy sorted, before * calculating the median. To avoid this step (if your list of values * is already sorted), use the {@link #calculateMedian(List, boolean)} * method. * * @param values the values ({@code null} permitted). * * @return The median. */ public static double calculateMedian(List values) { return calculateMedian(values, true); } /** * Calculates the median for a list of values ({@code Number} objects). * If {@code copyAndSort} is {@code false}, the list is assumed * to be presorted in ascending order by value. * * @param values the values ({@code null} permitted). * @param copyAndSort a flag that controls whether the list of values is * copied and sorted. * * @return The median. */ public static double calculateMedian(List values, boolean copyAndSort) { double result = Double.NaN; if (values != null) { if (copyAndSort) { int itemCount = values.size(); List copy = new ArrayList(itemCount); for (int i = 0; i < itemCount; i++) { copy.add(i, values.get(i)); } Collections.sort(copy); values = copy; } int count = values.size(); if (count > 0) { if (count % 2 == 1) { if (count > 1) { Number value = (Number) values.get((count - 1) / 2); result = value.doubleValue(); } else { Number value = (Number) values.get(0); result = value.doubleValue(); } } else { Number value1 = (Number) values.get(count / 2 - 1); Number value2 = (Number) values.get(count / 2); result = (value1.doubleValue() + value2.doubleValue()) / 2.0; } } } return result; } /** * Calculates the median for a sublist within a list of values * ({@code Number} objects). * * @param values the values, in any order ({@code null} not permitted). * @param start the start index. * @param end the end index. * * @return The median. */ public static double calculateMedian(List values, int start, int end) { return calculateMedian(values, start, end, true); } /** * Calculates the median for a sublist within a list of values * ({@code Number} objects). The entire list will be sorted if the * {@code ascending} argument is {@code false}. * * @param values the values ({@code null} not permitted). * @param start the start index. * @param end the end index. * @param copyAndSort a flag that that controls whether the list of values * is copied and sorted. * * @return The median. */ public static double calculateMedian(List values, int start, int end, boolean copyAndSort) { double result = Double.NaN; if (copyAndSort) { List working = new ArrayList(end - start + 1); for (int i = start; i <= end; i++) { working.add(values.get(i)); } Collections.sort(working); result = calculateMedian(working, false); } else { int count = end - start + 1; if (count > 0) { if (count % 2 == 1) { if (count > 1) { Number value = (Number) values.get(start + (count - 1) / 2); result = value.doubleValue(); } else { Number value = (Number) values.get(start); result = value.doubleValue(); } } else { Number value1 = (Number) values.get(start + count / 2 - 1); Number value2 = (Number) values.get(start + count / 2); result = (value1.doubleValue() + value2.doubleValue()) / 2.0; } } } return result; } /** * Returns the standard deviation of a set of numbers. * * @param data the data ({@code null} or zero length array not * permitted). * * @return The standard deviation of a set of numbers. */ public static double getStdDev(Number[] data) { Args.nullNotPermitted(data, "data"); if (data.length == 0) { throw new IllegalArgumentException("Zero length 'data' array."); } double avg = calculateMean(data); double sum = 0.0; for (int counter = 0; counter < data.length; counter++) { double diff = data[counter].doubleValue() - avg; sum = sum + diff * diff; } return Math.sqrt(sum / (data.length - 1)); } /** * Fits a straight line to a set of (x, y) data, returning the slope and * intercept. * * @param xData the x-data ({@code null} not permitted). * @param yData the y-data ({@code null} not permitted). * * @return A double array with the intercept in [0] and the slope in [1]. */ public static double[] getLinearFit(Number[] xData, Number[] yData) { Args.nullNotPermitted(xData, "xData"); Args.nullNotPermitted(yData, "yData"); if (xData.length != yData.length) { throw new IllegalArgumentException( "Statistics.getLinearFit(): array lengths must be equal."); } double[] result = new double[2]; // slope result[1] = getSlope(xData, yData); // intercept result[0] = calculateMean(yData) - result[1] * calculateMean(xData); return result; } /** * Finds the slope of a regression line using least squares. * * @param xData the x-values ({@code null} not permitted). * @param yData the y-values ({@code null} not permitted). * * @return The slope. */ public static double getSlope(Number[] xData, Number[] yData) { Args.nullNotPermitted(xData, "xData"); Args.nullNotPermitted(yData, "yData"); if (xData.length != yData.length) { throw new IllegalArgumentException("Array lengths must be equal."); } // ********* stat function for linear slope ******** // y = a + bx // a = ybar - b * xbar // sum(x * y) - (sum (x) * sum(y)) / n // b = ------------------------------------ // sum (x^2) - (sum(x)^2 / n // ************************************************* // sum of x, x^2, x * y, y double sx = 0.0, sxx = 0.0, sxy = 0.0, sy = 0.0; int counter; for (counter = 0; counter < xData.length; counter++) { sx = sx + xData[counter].doubleValue(); sxx = sxx + Math.pow(xData[counter].doubleValue(), 2); sxy = sxy + yData[counter].doubleValue() * xData[counter].doubleValue(); sy = sy + yData[counter].doubleValue(); } return (sxy - (sx * sy) / counter) / (sxx - (sx * sx) / counter); } /** * Calculates the correlation between two datasets. Both arrays should * contain the same number of items. Null values are treated as zero. *

* Information about the correlation calculation was obtained from: * * http://trochim.human.cornell.edu/kb/statcorr.htm * * @param data1 the first dataset. * @param data2 the second dataset. * * @return The correlation. */ public static double getCorrelation(Number[] data1, Number[] data2) { Args.nullNotPermitted(data1, "data1"); Args.nullNotPermitted(data2, "data2"); if (data1.length != data2.length) { throw new IllegalArgumentException( "'data1' and 'data2' arrays must have same length." ); } int n = data1.length; double sumX = 0.0; double sumY = 0.0; double sumX2 = 0.0; double sumY2 = 0.0; double sumXY = 0.0; for (int i = 0; i < n; i++) { double x = 0.0; if (data1[i] != null) { x = data1[i].doubleValue(); } double y = 0.0; if (data2[i] != null) { y = data2[i].doubleValue(); } sumX = sumX + x; sumY = sumY + y; sumXY = sumXY + (x * y); sumX2 = sumX2 + (x * x); sumY2 = sumY2 + (y * y); } return (n * sumXY - sumX * sumY) / Math.pow((n * sumX2 - sumX * sumX) * (n * sumY2 - sumY * sumY), 0.5); } /** * Returns a data set for a moving average on the data set passed in. * * @param xData an array of the x data. * @param yData an array of the y data. * @param period the number of data points to average * * @return A double[][] the length of the data set in the first dimension, * with two doubles for x and y in the second dimension */ public static double[][] getMovingAverage(Number[] xData, Number[] yData, int period) { // check arguments... if (xData.length != yData.length) { throw new IllegalArgumentException("Array lengths must be equal."); } if (period > xData.length) { throw new IllegalArgumentException( "Period can't be longer than dataset."); } double[][] result = new double[xData.length - period][2]; for (int i = 0; i < result.length; i++) { result[i][0] = xData[i + period].doubleValue(); // holds the moving average sum double sum = 0.0; for (int j = 0; j < period; j++) { sum += yData[i + j].doubleValue(); } sum = sum / period; result[i][1] = sum; } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/statistics/package.html000066400000000000000000000002271463604235500273420ustar00rootroot00000000000000 Classes for representing statistical data. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/000077500000000000000000000000001463604235500236245ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/DateRange.java000066400000000000000000000113141463604235500263210ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * DateRange.java * -------------- * (C) Copyright 2002-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Bill Kelemen; * */ package org.jfree.data.time; import java.io.Serializable; import java.text.DateFormat; import java.util.Date; import org.jfree.data.Range; /** * A range specified in terms of two {@code java.util.Date} objects. * Instances of this class are immutable. */ public class DateRange extends Range implements Serializable { /** For serialization. */ private static final long serialVersionUID = -4705682568375418157L; /** The lower bound for the range. */ private final long lowerDate; /** The upper bound for the range. */ private final long upperDate; /** * Default constructor. */ public DateRange() { this(new Date(0), new Date(1)); } /** * Constructs a new range. * * @param lower the lower bound ({@code null} not permitted). * @param upper the upper bound ({@code null} not permitted). */ public DateRange(Date lower, Date upper) { super(lower.getTime(), upper.getTime()); this.lowerDate = lower.getTime(); this.upperDate = upper.getTime(); } /** * Constructs a new range using two values that will be interpreted as * "milliseconds since midnight GMT, 1-Jan-1970". * * @param lower the lower (oldest) date. * @param upper the upper (most recent) date. */ public DateRange(double lower, double upper) { super(lower, upper); this.lowerDate = (long) lower; this.upperDate = (long) upper; } /** * Constructs a new range that is based on another {@link Range}. The * other range does not have to be a {@link DateRange}. If it is not, the * upper and lower bounds are evaluated as milliseconds since midnight * GMT, 1-Jan-1970. * * @param other the other range ({@code null} not permitted). */ public DateRange(Range other) { this(other.getLowerBound(), other.getUpperBound()); } /** * Returns the lower (earlier) date for the range. * * @return The lower date for the range. * * @see #getLowerMillis() */ public Date getLowerDate() { return new Date(this.lowerDate); } /** * Returns the lower bound of the range in milliseconds. * * @return The lower bound. * * @see #getLowerDate() */ public long getLowerMillis() { return this.lowerDate; } /** * Returns the upper (later) date for the range. * * @return The upper date for the range. * * @see #getUpperMillis() */ public Date getUpperDate() { return new Date(this.upperDate); } /** * Returns the upper bound of the range in milliseconds. * * @return The upper bound. * * @see #getUpperDate() */ public long getUpperMillis() { return this.upperDate; } /** * Returns a string representing the date range (useful for debugging). * * @return A string representing the date range. */ @Override public String toString() { DateFormat df = DateFormat.getDateTimeInstance(); return "[" + df.format(getLowerDate()) + " --> " + df.format(getUpperDate()) + "]"; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/Day.java000066400000000000000000000357151463604235500252170ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------- * Day.java * -------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import java.io.Serializable; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; import org.jfree.chart.date.SerialDate; import org.jfree.chart.util.Args; /** * Represents a single day in the range 1-Jan-1900 to 31-Dec-9999. This class * is immutable, which is a requirement for all {@link RegularTimePeriod} * subclasses. */ public class Day extends RegularTimePeriod implements Serializable { /** For serialization. */ private static final long serialVersionUID = -7082667380758962755L; /** A standard date formatter. */ protected static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd"); /** A date formatter for the default locale. */ protected static final DateFormat DATE_FORMAT_SHORT = DateFormat.getDateInstance(DateFormat.SHORT); /** A date formatter for the default locale. */ protected static final DateFormat DATE_FORMAT_MEDIUM = DateFormat.getDateInstance(DateFormat.MEDIUM); /** A date formatter for the default locale. */ protected static final DateFormat DATE_FORMAT_LONG = DateFormat.getDateInstance(DateFormat.LONG); /** The day (uses SerialDate for convenience). */ private SerialDate serialDate; /** The first millisecond. */ private long firstMillisecond; /** The last millisecond. */ private long lastMillisecond; /** * Creates a new instance, derived from the system date/time. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. */ public Day() { this(new Date()); } /** * Constructs a new one day time period. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. * * @param day the day-of-the-month. * @param month the month (1 to 12). * @param year the year (1900 <= year <= 9999). */ public Day(int day, int month, int year) { this.serialDate = SerialDate.createInstance(day, month, year); peg(getCalendarInstance()); } /** * Constructs a new one day time period. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. * * @param serialDate the day ({@code null} not permitted). */ public Day(SerialDate serialDate) { Args.nullNotPermitted(serialDate, "serialDate"); this.serialDate = serialDate; peg(getCalendarInstance()); } /** * Constructs a new instance, based on a particular date/time. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. * * @param time the time ({@code null} not permitted). * * @see #Day(Date, TimeZone, Locale) */ public Day(Date time) { // defer argument checking... this(time, getCalendarInstance()); } /** * Constructs a new instance, based on a particular date/time and time zone. * * @param time the date/time ({@code null} not permitted). * @param zone the time zone ({@code null} not permitted). * @param locale the locale ({@code null} not permitted). */ public Day(Date time, TimeZone zone, Locale locale) { Args.nullNotPermitted(time, "time"); Args.nullNotPermitted(zone, "zone"); Args.nullNotPermitted(locale, "locale"); Calendar calendar = Calendar.getInstance(zone, locale); calendar.setTime(time); initUsing(calendar); peg(calendar); } /** * Constructs a new instance, based on a particular date/time. * The time zone and locale are determined by the {@code calendar} * parameter. * * @param time the date/time ({@code null} not permitted). * @param calendar the calendar to use for calculations ({@code null} not permitted). */ public Day(Date time, Calendar calendar) { Args.nullNotPermitted(time, "time"); Args.nullNotPermitted(calendar, "calendar"); calendar.setTime(time); initUsing(calendar); peg(calendar); } private void initUsing(Calendar calendar) { int d = calendar.get(Calendar.DAY_OF_MONTH); int m = calendar.get(Calendar.MONTH) + 1; int y = calendar.get(Calendar.YEAR); this.serialDate = SerialDate.createInstance(d, m, y); } /** * Returns the day as a {@link SerialDate}. Note: the reference that is * returned should be an instance of an immutable {@link SerialDate} * (otherwise the caller could use the reference to alter the state of * this {@code Day} instance, and {@code Day} is supposed * to be immutable). * * @return The day as a {@link SerialDate}. */ public SerialDate getSerialDate() { return this.serialDate; } /** * Returns the year. * * @return The year. */ public int getYear() { return this.serialDate.getYYYY(); } /** * Returns the month. * * @return The month. */ public int getMonth() { return this.serialDate.getMonth(); } /** * Returns the day of the month. * * @return The day of the month. */ public int getDayOfMonth() { return this.serialDate.getDayOfMonth(); } /** * Returns the first millisecond of the day. This will be determined * relative to the time zone specified in the constructor, or in the * calendar instance passed in the most recent call to the * {@link #peg(Calendar)} method. * * @return The first millisecond of the day. * * @see #getLastMillisecond() */ @Override public long getFirstMillisecond() { return this.firstMillisecond; } /** * Returns the last millisecond of the day. This will be * determined relative to the time zone specified in the constructor, or * in the calendar instance passed in the most recent call to the * {@link #peg(Calendar)} method. * * @return The last millisecond of the day. * * @see #getFirstMillisecond() */ @Override public long getLastMillisecond() { return this.lastMillisecond; } /** * Recalculates the start date/time and end date/time for this time period * relative to the supplied calendar (which incorporates a time zone). * * @param calendar the calendar ({@code null} not permitted). */ @Override public void peg(Calendar calendar) { this.firstMillisecond = getFirstMillisecond(calendar); this.lastMillisecond = getLastMillisecond(calendar); } /** * Returns the day preceding this one. * No matter what time zone and locale this instance was created with, * the returned instance will use the default calendar for time * calculations, obtained with {@link RegularTimePeriod#getCalendarInstance()}. * * @return The day preceding this one. */ @Override public RegularTimePeriod previous() { Day result; int serial = this.serialDate.toSerial(); if (serial > SerialDate.SERIAL_LOWER_BOUND) { SerialDate yesterday = SerialDate.createInstance(serial - 1); return new Day(yesterday); } else { result = null; } return result; } /** * Returns the day following this one, or {@code null} if some limit * has been reached. * No matter what time zone and locale this instance was created with, * the returned instance will use the default calendar for time * calculations, obtained with {@link RegularTimePeriod#getCalendarInstance()}. * * @return The day following this one, or {@code null} if some limit * has been reached. */ @Override public RegularTimePeriod next() { Day result; int serial = this.serialDate.toSerial(); if (serial < SerialDate.SERIAL_UPPER_BOUND) { SerialDate tomorrow = SerialDate.createInstance(serial + 1); return new Day(tomorrow); } else { result = null; } return result; } /** * Returns a serial index number for the day. * * @return The serial index number. */ @Override public long getSerialIndex() { return this.serialDate.toSerial(); } /** * Returns the first millisecond of the day, evaluated using the supplied * calendar (which determines the time zone). * * @param calendar calendar to use ({@code null} not permitted). * * @return The start of the day as milliseconds since 01-01-1970. * * @throws NullPointerException if {@code calendar} is * {@code null}. */ @Override public long getFirstMillisecond(Calendar calendar) { int year = this.serialDate.getYYYY(); int month = this.serialDate.getMonth(); int day = this.serialDate.getDayOfMonth(); calendar.clear(); calendar.set(year, month - 1, day, 0, 0, 0); calendar.set(Calendar.MILLISECOND, 0); return calendar.getTimeInMillis(); } /** * Returns the last millisecond of the day, evaluated using the supplied * calendar (which determines the time zone). * * @param calendar calendar to use ({@code null} not permitted). * * @return The end of the day as milliseconds since 01-01-1970. * * @throws NullPointerException if {@code calendar} is * {@code null}. */ @Override public long getLastMillisecond(Calendar calendar) { int year = this.serialDate.getYYYY(); int month = this.serialDate.getMonth(); int day = this.serialDate.getDayOfMonth(); calendar.clear(); calendar.set(year, month - 1, day, 23, 59, 59); calendar.set(Calendar.MILLISECOND, 999); return calendar.getTimeInMillis(); } /** * Tests the equality of this Day object to an arbitrary object. Returns * true if the target is a Day instance or a SerialDate instance * representing the same day as this object. In all other cases, * returns false. * * @param obj the object ({@code null} permitted). * * @return A flag indicating whether or not an object is equal to this day. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof Day)) { return false; } Day that = (Day) obj; if (!this.serialDate.equals(that.getSerialDate())) { return false; } return true; } /** * Returns a hash code for this object instance. The approach described by * Joshua Bloch in "Effective Java" has been used here: *

* {@code http://developer.java.sun.com/developer/Books/effectivejava * /Chapter3.pdf} * * @return A hash code. */ @Override public int hashCode() { return this.serialDate.hashCode(); } /** * Returns an integer indicating the order of this Day object relative to * the specified object: * * negative == before, zero == same, positive == after. * * @param o1 the object to compare. * * @return negative == before, zero == same, positive == after. */ @Override public int compareTo(Object o1) { int result; // CASE 1 : Comparing to another Day object // ---------------------------------------- if (o1 instanceof Day) { Day d = (Day) o1; result = -d.getSerialDate().compare(this.serialDate); } // CASE 2 : Comparing to another TimePeriod object // ----------------------------------------------- else if (o1 instanceof RegularTimePeriod) { // more difficult case - evaluate later... result = 0; } // CASE 3 : Comparing to a non-TimePeriod object // --------------------------------------------- else { // consider time periods to be ordered after general objects result = 1; } return result; } /** * Returns a string representing the day. * * @return A string representing the day. */ @Override public String toString() { return this.serialDate.toString(); } /** * Parses the string argument as a day. *

* This method is required to recognise YYYY-MM-DD as a valid format. * Anything else, for now, is a bonus. * * @param s the date string to parse. * * @return {@code null} if the string does not contain any parseable * string, the day otherwise. */ public static Day parseDay(String s) { try { return new Day (Day.DATE_FORMAT.parse(s)); } catch (ParseException e1) { try { return new Day (Day.DATE_FORMAT_SHORT.parse(s)); } catch (ParseException e2) { // ignore } } return null; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/DynamicTimeSeriesCollection.java000066400000000000000000000734221463604235500320710ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------------- * DynamicTimeSeriesCollection.java * -------------------------------- * (C) Copyright 2002-present, by I. H. Thomae and Contributors. * * Original Author: I. H. Thomae (ithomae@ists.dartmouth.edu); * Contributor(s): David Gilbert; * Ricardo JL Rufino (patch #310); * */ package org.jfree.data.time; import java.util.Calendar; import java.util.TimeZone; import org.jfree.data.DomainInfo; import org.jfree.data.Range; import org.jfree.data.RangeInfo; import org.jfree.data.general.SeriesChangeEvent; import org.jfree.data.xy.AbstractIntervalXYDataset; import org.jfree.data.xy.IntervalXYDataset; /** * A dynamic dataset. *

* Like FastTimeSeriesCollection, this class is a functional replacement * for JFreeChart's TimeSeriesCollection _and_ TimeSeries classes. * FastTimeSeriesCollection is appropriate for a fixed time range; for * real-time applications this subclass adds the ability to append new * data and discard the oldest. * In this class, the arrays used in FastTimeSeriesCollection become FIFO's. * NOTE:As presented here, all data is assumed >= 0, an assumption which is * embodied only in methods associated with interface RangeInfo. */ public class DynamicTimeSeriesCollection extends AbstractIntervalXYDataset implements IntervalXYDataset, DomainInfo, RangeInfo { /** * Useful constant for controlling the x-value returned for a time * period. */ public static final int START = 0; /** * Useful constant for controlling the x-value returned for a time period. */ public static final int MIDDLE = 1; /** * Useful constant for controlling the x-value returned for a time period. */ public static final int END = 2; /** The maximum number of items for each series (can be overridden). */ private int maximumItemCount = 2000; // an arbitrary safe default value /** The history count. */ protected int historyCount; /** Storage for the series keys. */ private Comparable[] seriesKeys; /** The time period class - barely used, and could be removed (DG). */ private Class timePeriodClass = Minute.class; // default value; /** Storage for the x-values. */ protected RegularTimePeriod[] pointsInTime; /** The number of series. */ private int seriesCount; /** * A wrapper for a fixed array of float values. */ protected class ValueSequence { /** Storage for the float values. */ float[] dataPoints; /** * Default constructor: */ public ValueSequence() { this(DynamicTimeSeriesCollection.this.maximumItemCount); } /** * Creates a sequence with the specified length. * * @param length the length. */ public ValueSequence(int length) { this.dataPoints = new float[length]; for (int i = 0; i < length; i++) { this.dataPoints[i] = 0.0f; } } /** * Enters data into the storage array. * * @param index the index. * @param value the value. */ public void enterData(int index, float value) { this.dataPoints[index] = value; } /** * Returns a value from the storage array. * * @param index the index. * * @return The value. */ public float getData(int index) { return this.dataPoints[index]; } } /** An array for storing the objects that represent each series. */ protected ValueSequence[] valueHistory; /** A working calendar (to recycle) */ protected Calendar workingCalendar; /** * The position within a time period to return as the x-value (START, * MIDDLE or END). */ private int position; /** * A flag that indicates that the domain is 'points in time'. If this flag * is true, only the x-value is used to determine the range of values in * the domain, the start and end x-values are ignored. */ private boolean domainIsPointsInTime; /** index for mapping: points to the oldest valid time and data. */ private int oldestAt; // as a class variable, initializes == 0 /** Index of the newest data item. */ private int newestAt; // cached values used for interface DomainInfo: /** the # of msec by which time advances. */ private long deltaTime; /** Cached domain start (for use by DomainInfo). */ private Long domainStart; /** Cached domain end (for use by DomainInfo). */ private Long domainEnd; /** Cached domain range (for use by DomainInfo). */ private Range domainRange; // Cached values used for interface RangeInfo: (note minValue pinned at 0) // A single set of extrema covers the entire SeriesCollection /** The minimum value. */ private Float minValue = 0.0f; /** The maximum value. */ private Float maxValue = null; /** The value range. */ private Range valueRange; // autoinit's to null. /** * Constructs a dataset with capacity for N series, tied to default * timezone. * * @param nSeries the number of series to be accommodated. * @param nMoments the number of TimePeriods to be spanned. */ public DynamicTimeSeriesCollection(int nSeries, int nMoments) { this(nSeries, nMoments, new Millisecond(), TimeZone.getDefault()); this.newestAt = nMoments - 1; } /** * Constructs an empty dataset, tied to a specific timezone. * * @param nSeries the number of series to be accommodated * @param nMoments the number of TimePeriods to be spanned * @param zone the timezone. */ public DynamicTimeSeriesCollection(int nSeries, int nMoments, TimeZone zone) { this(nSeries, nMoments, new Millisecond(), zone); this.newestAt = nMoments - 1; } /** * Creates a new dataset. * * @param nSeries the number of series. * @param nMoments the number of items per series. * @param timeSample a time period sample. */ public DynamicTimeSeriesCollection(int nSeries, int nMoments, RegularTimePeriod timeSample) { this(nSeries, nMoments, timeSample, TimeZone.getDefault()); } /** * Creates a new dataset. * * @param nSeries the number of series. * @param nMoments the number of items per series. * @param timeSample a time period sample. * @param zone the time zone. */ public DynamicTimeSeriesCollection(int nSeries, int nMoments, RegularTimePeriod timeSample, TimeZone zone) { // the first initialization must precede creation of the ValueSet array: this.maximumItemCount = nMoments; // establishes length of each array this.historyCount = nMoments; this.seriesKeys = new Comparable[nSeries]; // initialize the members of "seriesNames" array so they won't be null: for (int i = 0; i < nSeries; i++) { this.seriesKeys[i] = ""; } this.newestAt = nMoments - 1; this.valueHistory = new ValueSequence[nSeries]; this.timePeriodClass = timeSample.getClass(); /// Expand the following for all defined TimePeriods: if (this.timePeriodClass == Millisecond.class) { this.pointsInTime = new Millisecond[nMoments]; } else if (this.timePeriodClass == Second.class) { this.pointsInTime = new Second[nMoments]; } else if (this.timePeriodClass == Minute.class) { this.pointsInTime = new Minute[nMoments]; } else if (this.timePeriodClass == Hour.class) { this.pointsInTime = new Hour[nMoments]; } /// .. etc.... this.workingCalendar = Calendar.getInstance(zone); this.position = START; this.domainIsPointsInTime = true; } /** * Fill the pointsInTime with times using TimePeriod.next(): * Will silently return if the time array was already populated. * * Also computes the data cached for later use by * methods implementing the DomainInfo interface: * * @param start the start. * * @return ??. */ public synchronized long setTimeBase(RegularTimePeriod start) { if (this.pointsInTime[0] == null) { this.pointsInTime[0] = start; for (int i = 1; i < this.historyCount; i++) { this.pointsInTime[i] = this.pointsInTime[i - 1].next(); } } long oldestL = this.pointsInTime[0].getFirstMillisecond( this.workingCalendar); long nextL = this.pointsInTime[1].getFirstMillisecond( this.workingCalendar); this.deltaTime = nextL - oldestL; this.oldestAt = 0; this.newestAt = this.historyCount - 1; findDomainLimits(); return this.deltaTime; } /** * Finds the domain limits. Note: this doesn't need to be synchronized * because it's called from within another method that already is. */ protected void findDomainLimits() { long startL = getOldestTime().getFirstMillisecond(this.workingCalendar); long endL; if (this.domainIsPointsInTime) { endL = getNewestTime().getFirstMillisecond(this.workingCalendar); } else { endL = getNewestTime().getLastMillisecond(this.workingCalendar); } this.domainStart = startL; this.domainEnd = endL; this.domainRange = new Range(startL, endL); } /** * Returns the x position type (START, MIDDLE or END). * * @return The x position type. */ public int getPosition() { return this.position; } /** * Sets the x position type (START, MIDDLE or END). * * @param position The x position type. */ public void setPosition(int position) { this.position = position; } /** * Adds a series to the dataset. Only the y-values are supplied, the * x-values are specified elsewhere. * * @param values the y-values. * @param seriesNumber the series index (zero-based). * @param seriesKey the series key. * * Use this as-is during setup only, or add the synchronized keyword around * the copy loop. */ public void addSeries(float[] values, int seriesNumber, Comparable seriesKey) { invalidateRangeInfo(); int i; if (values == null) { throw new IllegalArgumentException("TimeSeriesDataset.addSeries(): " + "cannot add null array of values."); } if (seriesNumber >= this.valueHistory.length) { throw new IllegalArgumentException("TimeSeriesDataset.addSeries(): " + "cannot add more series than specified in c'tor"); } if (this.valueHistory[seriesNumber] == null) { this.valueHistory[seriesNumber] = new ValueSequence(this.historyCount); this.seriesCount++; } // But if that series array already exists, just overwrite its contents // Avoid IndexOutOfBoundsException: int srcLength = values.length; int copyLength = this.historyCount; boolean fillNeeded = false; if (srcLength < this.historyCount) { fillNeeded = true; copyLength = srcLength; } //{ for (i = 0; i < copyLength; i++) { // deep copy from values[], caller // can safely discard that array this.valueHistory[seriesNumber].enterData(i, values[i]); } if (fillNeeded) { for (i = copyLength; i < this.historyCount; i++) { this.valueHistory[seriesNumber].enterData(i, 0.0f); } } //} if (seriesKey != null) { this.seriesKeys[seriesNumber] = seriesKey; } fireSeriesChanged(); } /** * Sets the name of a series. If planning to add values individually. * * @param seriesNumber the series. * @param key the new key. */ public void setSeriesKey(int seriesNumber, Comparable key) { this.seriesKeys[seriesNumber] = key; } /** * Adds a value to a series. * * @param seriesNumber the series index. * @param index ??. * @param value the value. */ public void addValue(int seriesNumber, int index, float value) { invalidateRangeInfo(); if (seriesNumber >= this.valueHistory.length) { throw new IllegalArgumentException( "TimeSeriesDataset.addValue(): series #" + seriesNumber + "unspecified in c'tor" ); } if (this.valueHistory[seriesNumber] == null) { this.valueHistory[seriesNumber] = new ValueSequence(this.historyCount); this.seriesCount++; } // But if that series array already exists, just overwrite its contents //synchronized(this) //{ this.valueHistory[seriesNumber].enterData(index, value); //} fireSeriesChanged(); } /** * Returns the number of series in the collection. * * @return The series count. */ @Override public int getSeriesCount() { return this.seriesCount; } /** * Returns the number of items in a series. *

* For this implementation, all series have the same number of items. * * @param series the series index (zero-based). * * @return The item count. */ @Override public int getItemCount(int series) { // all arrays equal length, // so ignore argument: return this.historyCount; } // Methods for managing the FIFO's: /** * Re-map an index, for use in retrieving data. * * @param toFetch the index. * * @return The translated index. */ protected int translateGet(int toFetch) { if (this.oldestAt == 0) { return toFetch; // no translation needed } // else [implicit here] int newIndex = toFetch + this.oldestAt; if (newIndex >= this.historyCount) { newIndex -= this.historyCount; } return newIndex; } /** * Returns the actual index to a time offset by "delta" from newestAt. * * @param delta the delta. * * @return The offset. */ public int offsetFromNewest(int delta) { return wrapOffset(this.newestAt + delta); } /** * ?? * * @param delta ?? * * @return The offset. */ public int offsetFromOldest(int delta) { return wrapOffset(this.oldestAt + delta); } /** * ?? * * @param protoIndex the index. * * @return The offset. */ protected int wrapOffset(int protoIndex) { int tmp = protoIndex; if (tmp >= this.historyCount) { tmp -= this.historyCount; } else if (tmp < 0) { tmp += this.historyCount; } return tmp; } /** * Adjust the array offset as needed when a new time-period is added: * Increments the indices "oldestAt" and "newestAt", mod(array length), * zeroes the series values at newestAt, returns the new TimePeriod. * * @return The new time period. */ public synchronized RegularTimePeriod advanceTime() { RegularTimePeriod nextInstant = this.pointsInTime[this.newestAt].next(); this.newestAt = this.oldestAt; // newestAt takes value previously held // by oldestAT // The next 10 lines or so should be expanded if data can be negative // if the oldest data contained a maximum Y-value, invalidate the stored // Y-max and Y-range data: boolean extremaChanged = false; float oldMax = 0.0f; if (this.maxValue != null) { oldMax = this.maxValue; } for (int s = 0; s < getSeriesCount(); s++) { if (this.valueHistory[s].getData(this.oldestAt) == oldMax) { extremaChanged = true; } if (extremaChanged) { break; } } // If data can be < 0, add code here to check the minimum if (extremaChanged) { invalidateRangeInfo(); } // wipe the next (about to be used) set of data slots float wiper = (float) 0.0; for (int s = 0; s < getSeriesCount(); s++) { this.valueHistory[s].enterData(this.newestAt, wiper); } // Update the array of TimePeriods: this.pointsInTime[this.newestAt] = nextInstant; // Now advance "oldestAt", wrapping at end of the array this.oldestAt++; if (this.oldestAt >= this.historyCount) { this.oldestAt = 0; } // Update the domain limits: long startL = this.domainStart; //(time is kept in msec) this.domainStart = startL + this.deltaTime; long endL = this.domainEnd; this.domainEnd = endL + this.deltaTime; this.domainRange = new Range(startL, endL); fireSeriesChanged(); return nextInstant; } // If data can be < 0, the next 2 methods should be modified /** * Invalidates the range info. */ public void invalidateRangeInfo() { this.maxValue = null; this.valueRange = null; } /** * Returns the maximum value. * * @return The maximum value. */ protected double findMaxValue() { double max = 0.0f; for (int s = 0; s < getSeriesCount(); s++) { for (int i = 0; i < this.historyCount; i++) { double tmp = getYValue(s, i); if (tmp > max) { max = tmp; } } } return max; } // End, positive-data-only code /** * Returns the index of the oldest data item. * * @return The index. */ public int getOldestIndex() { return this.oldestAt; } /** * Returns the index of the newest data item. * * @return The index. */ public int getNewestIndex() { return this.newestAt; } // appendData() writes new data at the index position given by newestAt/ // When adding new data dynamically, use advanceTime(), followed by this: /** * Appends new data. * * @param newData the data. */ public void appendData(float[] newData) { int nDataPoints = newData.length; if (nDataPoints > this.valueHistory.length) { throw new IllegalArgumentException( "More data than series to put them in"); } int s; // index to select the "series" for (s = 0; s < nDataPoints; s++) { // check whether the "valueHistory" array member exists; if not, // create them: if (this.valueHistory[s] == null) { this.valueHistory[s] = new ValueSequence(this.historyCount); } this.valueHistory[s].enterData(this.newestAt, newData[s]); } fireSeriesChanged(); } /** * Appends data at specified index, for loading up with data from file(s). * * @param newData the data * @param insertionIndex the index value at which to put it * @param refresh value of n in "refresh the display on every nth call" * (ignored if <= 0 ) */ public void appendData(float[] newData, int insertionIndex, int refresh) { int nDataPoints = newData.length; if (nDataPoints > this.valueHistory.length) { throw new IllegalArgumentException( "More data than series to put them in"); } for (int s = 0; s < nDataPoints; s++) { if (this.valueHistory[s] == null) { this.valueHistory[s] = new ValueSequence(this.historyCount); } this.valueHistory[s].enterData(insertionIndex, newData[s]); } if (refresh > 0) { insertionIndex++; if (insertionIndex % refresh == 0) { fireSeriesChanged(); } } } /** * Returns the newest time. * * @return The newest time. */ public RegularTimePeriod getNewestTime() { return this.pointsInTime[this.newestAt]; } /** * Returns the oldest time. * * @return The oldest time. */ public RegularTimePeriod getOldestTime() { return this.pointsInTime[this.oldestAt]; } /** * Returns the x-value. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ // getXxx() ftns can ignore the "series" argument: // Don't synchronize this!! Instead, synchronize the loop that calls it. @Override public Number getX(int series, int item) { RegularTimePeriod tp = this.pointsInTime[translateGet(item)]; return getX(tp); } /** * Returns the y-value. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public double getYValue(int series, int item) { // Don't synchronize this!! // Instead, synchronize the loop that calls it. ValueSequence values = this.valueHistory[series]; return values.getData(translateGet(item)); } /** * Returns the y-value. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public Number getY(int series, int item) { return getYValue(series, item); } /** * Returns the start x-value. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public Number getStartX(int series, int item) { RegularTimePeriod tp = this.pointsInTime[translateGet(item)]; return tp.getFirstMillisecond(this.workingCalendar); } /** * Returns the end x-value. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public Number getEndX(int series, int item) { RegularTimePeriod tp = this.pointsInTime[translateGet(item)]; return tp.getLastMillisecond(this.workingCalendar); } /** * Returns the start y-value. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public Number getStartY(int series, int item) { return getY(series, item); } /** * Returns the end y-value. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public Number getEndY(int series, int item) { return getY(series, item); } /* // "Extras" found useful when analyzing/verifying class behavior: public Number getUntranslatedXValue(int series, int item) { return super.getXValue(series, item); } public float getUntranslatedY(int series, int item) { return super.getY(series, item); } */ /** * Returns the key for a series. * * @param series the series index (zero-based). * * @return The key. */ @Override public Comparable getSeriesKey(int series) { return this.seriesKeys[series]; } /** * Sends a {@link SeriesChangeEvent} to all registered listeners. */ protected void fireSeriesChanged() { seriesChanged(new SeriesChangeEvent(this)); } // The next 3 functions override the base-class implementation of // the DomainInfo interface. Using saved limits (updated by // each updateTime() call), improves performance. // /** * Returns the minimum x-value in the dataset. * * @param includeInterval a flag that determines whether or not the * x-interval is taken into account. * * @return The minimum value. */ @Override public double getDomainLowerBound(boolean includeInterval) { return this.domainStart.doubleValue(); // a Long kept updated by advanceTime() } /** * Returns the maximum x-value in the dataset. * * @param includeInterval a flag that determines whether or not the * x-interval is taken into account. * * @return The maximum value. */ @Override public double getDomainUpperBound(boolean includeInterval) { return this.domainEnd.doubleValue(); // a Long kept updated by advanceTime() } /** * Returns the range of the values in this dataset's domain. * * @param includeInterval a flag that determines whether or not the * x-interval is taken into account. * * @return The range. */ @Override public Range getDomainBounds(boolean includeInterval) { if (this.domainRange == null) { findDomainLimits(); } return this.domainRange; } /** * Returns the x-value for a time period. * * @param period the period. * * @return The x-value. */ private long getX(RegularTimePeriod period) { switch (this.position) { case (START) : return period.getFirstMillisecond(this.workingCalendar); case (MIDDLE) : return period.getMiddleMillisecond(this.workingCalendar); case (END) : return period.getLastMillisecond(this.workingCalendar); default: return period.getMiddleMillisecond(this.workingCalendar); } } // The next 3 functions implement the RangeInfo interface. // Using saved limits (updated by each updateTime() call) significantly // improves performance. WARNING: this code makes the simplifying // assumption that data is never negative. Expand as needed for the // general case. /** * Returns the minimum range value. * * @param includeInterval a flag that determines whether or not the * y-interval is taken into account. * * @return The minimum range value. */ @Override public double getRangeLowerBound(boolean includeInterval) { double result = Double.NaN; if (this.minValue != null) { result = this.minValue.doubleValue(); } return result; } /** * Returns the maximum range value. * * @param includeInterval a flag that determines whether or not the * y-interval is taken into account. * * @return The maximum range value. */ @Override public double getRangeUpperBound(boolean includeInterval) { double result = Double.NaN; if (this.maxValue != null) { result = this.maxValue.doubleValue(); } return result; } /** * Returns the value range. * * @param includeInterval a flag that determines whether or not the * y-interval is taken into account. * * @return The range. */ @Override public Range getRangeBounds(boolean includeInterval) { if (this.valueRange == null) { double max = getRangeUpperBound(includeInterval); this.valueRange = new Range(0.0, max); } return this.valueRange; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/FixedMillisecond.java000066400000000000000000000174301463604235500277160ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * FixedMillisecond.java * --------------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Ulrich Voigt; * */ package org.jfree.data.time; import java.io.Serializable; import java.util.Calendar; import java.util.Date; /** * Wrapper for a {@code java.util.Date} object that allows it to be used * as a {@link RegularTimePeriod}. This class is immutable, which is a * requirement for all {@link RegularTimePeriod} subclasses. */ public class FixedMillisecond extends RegularTimePeriod implements Serializable { /** For serialization. */ private static final long serialVersionUID = 7867521484545646931L; /** The millisecond. */ private final long time; /** * Constructs a millisecond based on the current system time. */ public FixedMillisecond() { this(System.currentTimeMillis()); } /** * Constructs a millisecond. * * @param millisecond the millisecond (same encoding as java.util.Date). */ public FixedMillisecond(long millisecond) { this.time = millisecond; } /** * Constructs a millisecond. * * @param time the time ({@code null} not permitted). */ public FixedMillisecond(Date time) { this(time.getTime()); } /** * Returns the date/time (creates a new {@code Date} instance each time * this method is called). * * @return The date/time. */ public Date getTime() { return new Date(this.time); } /** * This method is overridden to do nothing. * * @param calendar ignored */ @Override public void peg(Calendar calendar) { // nothing to do } /** * Returns the millisecond preceding this one. * * @return The millisecond preceding this one. */ @Override public RegularTimePeriod previous() { RegularTimePeriod result = null; long t = this.time; if (t != Long.MIN_VALUE) { result = new FixedMillisecond(t - 1); } return result; } /** * Returns the millisecond following this one. * * @return The millisecond following this one. */ @Override public RegularTimePeriod next() { RegularTimePeriod result = null; long t = this.time; if (t != Long.MAX_VALUE) { result = new FixedMillisecond(t + 1); } return result; } /** * Tests the equality of this object against an arbitrary Object. * * @param object the object to compare * * @return A boolean. */ @Override public boolean equals(Object object) { if (object instanceof FixedMillisecond) { FixedMillisecond m = (FixedMillisecond) object; return this.time == m.getFirstMillisecond(); } else { return false; } } /** * Returns a hash code for this object instance. * * @return A hash code. */ @Override public int hashCode() { return (int) this.time; } /** * Returns an integer indicating the order of this Millisecond object * relative to the specified * object: negative == before, zero == same, positive == after. * * @param o1 the object to compare. * * @return negative == before, zero == same, positive == after. */ @Override public int compareTo(Object o1) { int result; long difference; // CASE 1 : Comparing to another Second object // ------------------------------------------- if (o1 instanceof FixedMillisecond) { FixedMillisecond t1 = (FixedMillisecond) o1; difference = this.time - t1.time; if (difference > 0) { result = 1; } else { if (difference < 0) { result = -1; } else { result = 0; } } } // CASE 2 : Comparing to another TimePeriod object // ----------------------------------------------- else if (o1 instanceof RegularTimePeriod) { // more difficult case - evaluate later... result = 0; } // CASE 3 : Comparing to a non-TimePeriod object // --------------------------------------------- else { // consider time periods to be ordered after general objects result = 1; } return result; } /** * Returns the first millisecond of the time period. * * @return The first millisecond of the time period. */ @Override public long getFirstMillisecond() { return this.time; } /** * Returns the first millisecond of the time period. * * @param calendar the calendar. * * @return The first millisecond of the time period. */ @Override public long getFirstMillisecond(Calendar calendar) { return this.time; } /** * Returns the last millisecond of the time period. * * @return The last millisecond of the time period. */ @Override public long getLastMillisecond() { return this.time; } /** * Returns the last millisecond of the time period. * * @param calendar the calendar. * * @return The last millisecond of the time period. */ @Override public long getLastMillisecond(Calendar calendar) { return this.time; } /** * Returns the millisecond closest to the middle of the time period. * * @return The millisecond closest to the middle of the time period. */ @Override public long getMiddleMillisecond() { return this.time; } /** * Returns the millisecond closest to the middle of the time period. * * @param calendar the calendar. * * @return The millisecond closest to the middle of the time period. */ @Override public long getMiddleMillisecond(Calendar calendar) { return this.time; } /** * Returns a serial index number for the millisecond. * * @return The serial index number. */ @Override public long getSerialIndex() { return this.time; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/Hour.java000066400000000000000000000354111463604235500254100ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------- * Hour.java * --------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import java.io.Serializable; import java.util.Calendar; import java.util.Date; import java.util.Locale; import java.util.TimeZone; import org.jfree.chart.util.Args; /** * Represents an hour in a specific day. This class is immutable, which is a * requirement for all {@link RegularTimePeriod} subclasses. */ public class Hour extends RegularTimePeriod implements Serializable { /** For serialization. */ private static final long serialVersionUID = -835471579831937652L; /** Useful constant for the first hour in the day. */ public static final int FIRST_HOUR_IN_DAY = 0; /** Useful constant for the last hour in the day. */ public static final int LAST_HOUR_IN_DAY = 23; /** The day. */ private Day day; /** The hour. */ private byte hour; /** The first millisecond. */ private long firstMillisecond; /** The last millisecond. */ private long lastMillisecond; /** * Constructs a new Hour, based on the system date/time. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. */ public Hour() { this(new Date()); } /** * Constructs a new Hour. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. * * @param hour the hour (in the range 0 to 23). * @param day the day ({@code null} not permitted). */ public Hour(int hour, Day day) { Args.nullNotPermitted(day, "day"); this.hour = (byte) hour; this.day = day; peg(getCalendarInstance()); } /** * Creates a new hour. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. * * @param hour the hour (0-23). * @param day the day (1-31). * @param month the month (1-12). * @param year the year (1900-9999). */ public Hour(int hour, int day, int month, int year) { this(hour, new Day(day, month, year)); } /** * Constructs a new instance, based on the supplied date/time. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. * * @param time the date-time ({@code null} not permitted). * * @see #Hour(Date, TimeZone, Locale) */ public Hour(Date time) { // defer argument checking... this(time, getCalendarInstance()); } /** * Constructs a new instance, based on the supplied date/time evaluated * in the specified time zone. * * @param time the date-time ({@code null} not permitted). * @param zone the time zone ({@code null} not permitted). * @param locale the locale ({@code null} not permitted). */ public Hour(Date time, TimeZone zone, Locale locale) { Args.nullNotPermitted(time, "time"); Args.nullNotPermitted(zone, "zone"); Args.nullNotPermitted(locale, "locale"); Calendar calendar = Calendar.getInstance(zone, locale); calendar.setTime(time); this.hour = (byte) calendar.get(Calendar.HOUR_OF_DAY); this.day = new Day(time, zone, locale); peg(calendar); } /** * Constructs a new instance, based on a particular date/time. * The time zone and locale are determined by the {@code calendar} * parameter. * * @param time the date/time ({@code null} not permitted). * @param calendar the calendar to use for calculations ({@code null} not permitted). */ public Hour(Date time, Calendar calendar) { Args.nullNotPermitted(time, "time"); Args.nullNotPermitted(calendar, "calendar"); calendar.setTime(time); this.hour = (byte) calendar.get(Calendar.HOUR_OF_DAY); this.day = new Day(time, calendar); peg(calendar); } /** * Returns the hour. * * @return The hour (0 <= hour <= 23). */ public int getHour() { return this.hour; } /** * Returns the day in which this hour falls. * * @return The day. */ public Day getDay() { return this.day; } /** * Returns the year in which this hour falls. * * @return The year. */ public int getYear() { return this.day.getYear(); } /** * Returns the month in which this hour falls. * * @return The month. */ public int getMonth() { return this.day.getMonth(); } /** * Returns the day-of-the-month in which this hour falls. * * @return The day-of-the-month. */ public int getDayOfMonth() { return this.day.getDayOfMonth(); } /** * Returns the first millisecond of the hour. This will be determined * relative to the time zone specified in the constructor, or in the * calendar instance passed in the most recent call to the * {@link #peg(Calendar)} method. * * @return The first millisecond of the hour. * * @see #getLastMillisecond() */ @Override public long getFirstMillisecond() { return this.firstMillisecond; } /** * Returns the last millisecond of the hour. This will be * determined relative to the time zone specified in the constructor, or * in the calendar instance passed in the most recent call to the * {@link #peg(Calendar)} method. * * @return The last millisecond of the hour. * * @see #getFirstMillisecond() */ @Override public long getLastMillisecond() { return this.lastMillisecond; } /** * Recalculates the start date/time and end date/time for this time period * relative to the supplied calendar (which incorporates a time zone). * * @param calendar the calendar ({@code null} not permitted). */ @Override public void peg(Calendar calendar) { this.firstMillisecond = getFirstMillisecond(calendar); this.lastMillisecond = getLastMillisecond(calendar); } /** * Returns the hour preceding this one. * No matter what time zone and locale this instance was created with, * the returned instance will use the default calendar for time * calculations, obtained with {@link RegularTimePeriod#getCalendarInstance()}. * * @return The hour preceding this one. */ @Override public RegularTimePeriod previous() { Hour result; if (this.hour != FIRST_HOUR_IN_DAY) { result = new Hour(this.hour - 1, this.day); } else { // we are at the first hour in the day... Day prevDay = (Day) this.day.previous(); if (prevDay != null) { result = new Hour(LAST_HOUR_IN_DAY, prevDay); } else { result = null; } } return result; } /** * Returns the hour following this one. * No matter what time zone and locale this instance was created with, * the returned instance will use the default calendar for time * calculations, obtained with {@link RegularTimePeriod#getCalendarInstance()}. * * @return The hour following this one. */ @Override public RegularTimePeriod next() { Hour result; if (this.hour != LAST_HOUR_IN_DAY) { result = new Hour(this.hour + 1, this.day); } else { // we are at the last hour in the day... Day nextDay = (Day) this.day.next(); if (nextDay != null) { result = new Hour(FIRST_HOUR_IN_DAY, nextDay); } else { result = null; } } return result; } /** * Returns a serial index number for the hour. * * @return The serial index number. */ @Override public long getSerialIndex() { return this.day.getSerialIndex() * 24L + this.hour; } /** * Returns the first millisecond of the hour. * * @param calendar the calendar/timezone ({@code null} not permitted). * * @return The first millisecond. * * @throws NullPointerException if {@code calendar} is * {@code null}. */ @Override public long getFirstMillisecond(Calendar calendar) { int year = this.day.getYear(); int month = this.day.getMonth() - 1; int dom = this.day.getDayOfMonth(); calendar.set(year, month, dom, this.hour, 0, 0); calendar.set(Calendar.MILLISECOND, 0); return calendar.getTimeInMillis(); } /** * Returns the last millisecond of the hour. * * @param calendar the calendar/timezone ({@code null} not permitted). * * @return The last millisecond. * * @throws NullPointerException if {@code calendar} is * {@code null}. */ @Override public long getLastMillisecond(Calendar calendar) { int year = this.day.getYear(); int month = this.day.getMonth() - 1; int dom = this.day.getDayOfMonth(); calendar.set(year, month, dom, this.hour, 59, 59); calendar.set(Calendar.MILLISECOND, 999); return calendar.getTimeInMillis(); } /** * Tests the equality of this object against an arbitrary Object. *

* This method will return true ONLY if the object is an Hour object * representing the same hour as this instance. * * @param obj the object to compare ({@code null} permitted). * * @return {@code true} if the hour and day value of the object * is the same as this. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof Hour)) { return false; } Hour that = (Hour) obj; if (this.hour != that.hour) { return false; } if (!this.day.equals(that.day)) { return false; } return true; } /** * Returns a string representation of this instance, for debugging * purposes. * * @return A string. */ @Override public String toString() { return "[" + this.hour + "," + getDayOfMonth() + "/" + getMonth() + "/" + getYear() + "]"; } /** * Returns a hash code for this object instance. The approach described by * Joshua Bloch in "Effective Java" has been used here: *

* {@code http://developer.java.sun.com/developer/Books/effectivejava * /Chapter3.pdf} * * @return A hash code. */ @Override public int hashCode() { int result = 17; result = 37 * result + this.hour; result = 37 * result + this.day.hashCode(); return result; } /** * Returns an integer indicating the order of this Hour object relative to * the specified object: * * negative == before, zero == same, positive == after. * * @param o1 the object to compare. * * @return negative == before, zero == same, positive == after. */ @Override public int compareTo(Object o1) { int result; // CASE 1 : Comparing to another Hour object // ----------------------------------------- if (o1 instanceof Hour) { Hour h = (Hour) o1; result = getDay().compareTo(h.getDay()); if (result == 0) { result = this.hour - h.getHour(); } } // CASE 2 : Comparing to another TimePeriod object // ----------------------------------------------- else if (o1 instanceof RegularTimePeriod) { // more difficult case - evaluate later... result = 0; } // CASE 3 : Comparing to a non-TimePeriod object // --------------------------------------------- else { // consider time periods to be ordered after general objects result = 1; } return result; } /** * Creates an Hour instance by parsing a string. The string is assumed to * be in the format "YYYY-MM-DD HH", perhaps with leading or trailing * whitespace. * * @param s the hour string to parse. * * @return {@code null} if the string is not parseable, the hour * otherwise. */ public static Hour parseHour(String s) { Hour result = null; s = s.trim(); String daystr = s.substring(0, Math.min(10, s.length())); Day day = Day.parseDay(daystr); if (day != null) { String hourstr = s.substring( Math.min(daystr.length() + 1, s.length()) ); hourstr = hourstr.trim(); int hour = Integer.parseInt(hourstr); // if the hour is 0 - 23 then create an hour if ((hour >= FIRST_HOUR_IN_DAY) && (hour <= LAST_HOUR_IN_DAY)) { result = new Hour(hour, day); } } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/Millisecond.java000066400000000000000000000350771463604235500267450ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * Millisecond.java * ---------------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import org.jfree.chart.util.Args; import java.io.Serializable; import java.util.Calendar; import java.util.Date; import java.util.Locale; import java.util.TimeZone; /** * Represents a millisecond. This class is immutable, which is a requirement * for all {@link RegularTimePeriod} subclasses. */ public class Millisecond extends RegularTimePeriod implements Serializable { /** For serialization. */ static final long serialVersionUID = -5316836467277638485L; /** A constant for the first millisecond in a second. */ public static final int FIRST_MILLISECOND_IN_SECOND = 0; /** A constant for the last millisecond in a second. */ public static final int LAST_MILLISECOND_IN_SECOND = 999; /** The day. */ private Day day; /** The hour in the day. */ private byte hour; /** The minute. */ private byte minute; /** The second. */ private byte second; /** The millisecond. */ private int millisecond; /** * The pegged millisecond. */ private long firstMillisecond; /** * Constructs a millisecond based on the current system time. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. */ public Millisecond() { this(new Date()); } /** * Constructs a millisecond. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. * * @param millisecond the millisecond (0-999). * @param second the second ({@code null} not permitted). */ public Millisecond(int millisecond, Second second) { Args.nullNotPermitted(second, "second"); this.millisecond = millisecond; this.second = (byte) second.getSecond(); this.minute = (byte) second.getMinute().getMinute(); this.hour = (byte) second.getMinute().getHourValue(); this.day = second.getMinute().getDay(); peg(getCalendarInstance()); } /** * Creates a new millisecond. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. * * @param millisecond the millisecond (0-999). * @param second the second (0-59). * @param minute the minute (0-59). * @param hour the hour (0-23). * @param day the day (1-31). * @param month the month (1-12). * @param year the year (1900-9999). */ public Millisecond(int millisecond, int second, int minute, int hour, int day, int month, int year) { this(millisecond, new Second(second, minute, hour, day, month, year)); } /** * Constructs a new millisecond. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. * * @param time the time. * * @see #Millisecond(Date, TimeZone, Locale) */ public Millisecond(Date time) { this(time, getCalendarInstance()); } /** * Creates a millisecond. * * @param time the date-time ({@code null} not permitted). * @param zone the time zone ({@code null} not permitted). * @param locale the locale ({@code null} not permitted). */ public Millisecond(Date time, TimeZone zone, Locale locale) { Args.nullNotPermitted(time, "time"); Args.nullNotPermitted(zone, "zone"); Args.nullNotPermitted(locale, "locale"); Calendar calendar = Calendar.getInstance(zone, locale); calendar.setTime(time); this.millisecond = calendar.get(Calendar.MILLISECOND); this.second = (byte) calendar.get(Calendar.SECOND); this.minute = (byte) calendar.get(Calendar.MINUTE); this.hour = (byte) calendar.get(Calendar.HOUR_OF_DAY); this.day = new Day(time, zone, locale); peg(calendar); } /** * Constructs a new instance, based on a particular date/time. * The time zone and locale are determined by the {@code calendar} * parameter. * * @param time the date/time ({@code null} not permitted). * @param calendar the calendar to use for calculations ({@code null} not permitted). */ public Millisecond(Date time, Calendar calendar) { Args.nullNotPermitted(time, "time"); Args.nullNotPermitted(calendar, "calendar"); calendar.setTime(time); this.millisecond = calendar.get(Calendar.MILLISECOND); this.second = (byte) calendar.get(Calendar.SECOND); this.minute = (byte) calendar.get(Calendar.MINUTE); this.hour = (byte) calendar.get(Calendar.HOUR_OF_DAY); this.day = new Day(time, calendar); peg(calendar); } /** * Returns the second. * * @return The second. */ public Second getSecond() { return new Second(this.second, this.minute, this.hour, this.day.getDayOfMonth(), this.day.getMonth(), this.day.getYear()); } /** * Returns the millisecond. * * @return The millisecond. */ public long getMillisecond() { return this.millisecond; } /** * Returns the first millisecond of the second. This will be determined * relative to the time zone specified in the constructor, or in the * calendar instance passed in the most recent call to the * {@link #peg(Calendar)} method. * * @return The first millisecond of the second. * * @see #getLastMillisecond() */ @Override public long getFirstMillisecond() { return this.firstMillisecond; } /** * Returns the last millisecond of the second. This will be * determined relative to the time zone specified in the constructor, or * in the calendar instance passed in the most recent call to the * {@link #peg(Calendar)} method. * * @return The last millisecond of the second. * * @see #getFirstMillisecond() */ @Override public long getLastMillisecond() { return this.firstMillisecond; } /** * Recalculates the start date/time and end date/time for this time period * relative to the supplied calendar (which incorporates a time zone). * * @param calendar the calendar ({@code null} not permitted). */ @Override public void peg(Calendar calendar) { this.firstMillisecond = getFirstMillisecond(calendar); } /** * Returns the millisecond preceding this one. * No matter what time zone and locale this instance was created with, * the returned instance will use the default calendar for time * calculations, obtained with {@link RegularTimePeriod#getCalendarInstance()}. * * @return The millisecond preceding this one. */ @Override public RegularTimePeriod previous() { RegularTimePeriod result = null; if (this.millisecond != FIRST_MILLISECOND_IN_SECOND) { result = new Millisecond(this.millisecond - 1, getSecond()); } else { Second previous = (Second) getSecond().previous(); if (previous != null) { result = new Millisecond(LAST_MILLISECOND_IN_SECOND, previous); } } return result; } /** * Returns the millisecond following this one. * No matter what time zone and locale this instance was created with, * the returned instance will use the default calendar for time * calculations, obtained with {@link RegularTimePeriod#getCalendarInstance()}. * * @return The millisecond following this one. */ @Override public RegularTimePeriod next() { RegularTimePeriod result = null; if (this.millisecond != LAST_MILLISECOND_IN_SECOND) { result = new Millisecond(this.millisecond + 1, getSecond()); } else { Second next = (Second) getSecond().next(); if (next != null) { result = new Millisecond(FIRST_MILLISECOND_IN_SECOND, next); } } return result; } /** * Returns a serial index number for the millisecond. * * @return The serial index number. */ @Override public long getSerialIndex() { long hourIndex = this.day.getSerialIndex() * 24L + this.hour; long minuteIndex = hourIndex * 60L + this.minute; long secondIndex = minuteIndex * 60L + this.second; return secondIndex * 1000L + this.millisecond; } /** * Tests the equality of this object against an arbitrary Object. *

* This method will return true ONLY if the object is a Millisecond object * representing the same millisecond as this instance. * * @param obj the object to compare * * @return {@code true} if milliseconds and seconds of this and object * are the same. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof Millisecond)) { return false; } Millisecond that = (Millisecond) obj; if (this.millisecond != that.millisecond) { return false; } if (this.second != that.second) { return false; } if (this.minute != that.minute) { return false; } if (this.hour != that.hour) { return false; } if (!this.day.equals(that.day)) { return false; } return true; } /** * Returns a hash code for this object instance. The approach described by * Joshua Bloch in "Effective Java" has been used here: *

* {@code http://developer.java.sun.com/developer/Books/effectivejava * /Chapter3.pdf} * * @return A hashcode. */ @Override public int hashCode() { int result = 17; result = 37 * result + this.millisecond; result = 37 * result + getSecond().hashCode(); return result; } /** * Returns an integer indicating the order of this Millisecond object * relative to the specified object: * * negative == before, zero == same, positive == after. * * @param obj the object to compare * * @return negative == before, zero == same, positive == after. */ @Override public int compareTo(Object obj) { int result; long difference; // CASE 1 : Comparing to another Second object // ------------------------------------------- if (obj instanceof Millisecond) { Millisecond ms = (Millisecond) obj; difference = getFirstMillisecond() - ms.getFirstMillisecond(); if (difference > 0) { result = 1; } else { if (difference < 0) { result = -1; } else { result = 0; } } } // CASE 2 : Comparing to another TimePeriod object // ----------------------------------------------- else if (obj instanceof RegularTimePeriod) { RegularTimePeriod rtp = (RegularTimePeriod) obj; final long thisVal = this.getFirstMillisecond(); final long anotherVal = rtp.getFirstMillisecond(); result = (thisVal < anotherVal ? -1 : (thisVal == anotherVal ? 0 : 1)); } // CASE 3 : Comparing to a non-TimePeriod object // --------------------------------------------- else { // consider time periods to be ordered after general objects result = 1; } return result; } /** * Returns the first millisecond of the time period. * * @param calendar the calendar ({@code null} not permitted). * * @return The first millisecond of the time period. * * @throws NullPointerException if {@code calendar} is * {@code null}. */ @Override public long getFirstMillisecond(Calendar calendar) { int year = this.day.getYear(); int month = this.day.getMonth() - 1; int d = this.day.getDayOfMonth(); calendar.clear(); calendar.set(year, month, d, this.hour, this.minute, this.second); calendar.set(Calendar.MILLISECOND, this.millisecond); return calendar.getTimeInMillis(); } /** * Returns the last millisecond of the time period. * * @param calendar the calendar ({@code null} not permitted). * * @return The last millisecond of the time period. * * @throws NullPointerException if {@code calendar} is * {@code null}. */ @Override public long getLastMillisecond(Calendar calendar) { return getFirstMillisecond(calendar); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/Minute.java000066400000000000000000000362141463604235500257360ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------- * Minute.java * ----------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import java.io.Serializable; import java.util.Calendar; import java.util.Date; import java.util.Locale; import java.util.TimeZone; import org.jfree.chart.util.Args; /** * Represents a minute. This class is immutable, which is a requirement for * all {@link RegularTimePeriod} subclasses. */ public class Minute extends RegularTimePeriod implements Serializable { /** For serialization. */ private static final long serialVersionUID = 2144572840034842871L; /** Useful constant for the first minute in a day. */ public static final int FIRST_MINUTE_IN_HOUR = 0; /** Useful constant for the last minute in a day. */ public static final int LAST_MINUTE_IN_HOUR = 59; /** The day. */ private final Day day; /** The hour in which the minute falls. */ private final byte hour; /** The minute. */ private final byte minute; /** The first millisecond. */ private long firstMillisecond; /** The last millisecond. */ private long lastMillisecond; /** * Constructs a new Minute, based on the system date/time. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. */ public Minute() { this(new Date()); } /** * Constructs a new Minute. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. * * @param minute the minute (0 to 59). * @param hour the hour ({@code null} not permitted). */ public Minute(int minute, Hour hour) { Args.nullNotPermitted(hour, "hour"); this.minute = (byte) minute; this.hour = (byte) hour.getHour(); this.day = hour.getDay(); peg(getCalendarInstance()); } /** * Constructs a new instance, based on the supplied date/time. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. * * @param time the time ({@code null} not permitted). * * @see #Minute(Date, TimeZone, Locale) */ public Minute(Date time) { // defer argument checking this(time, getCalendarInstance()); } /** * Constructs a new Minute, based on the supplied date/time and timezone. * * @param time the time ({@code null} not permitted). * @param zone the time zone ({@code null} not permitted). * @param locale the locale ({@code null} not permitted). */ public Minute(Date time, TimeZone zone, Locale locale) { Args.nullNotPermitted(time, "time"); Args.nullNotPermitted(zone, "zone"); Args.nullNotPermitted(locale, "locale"); Calendar calendar = Calendar.getInstance(zone, locale); calendar.setTime(time); int min = calendar.get(Calendar.MINUTE); this.minute = (byte) min; this.hour = (byte) calendar.get(Calendar.HOUR_OF_DAY); this.day = new Day(time, zone, locale); peg(calendar); } /** * Constructs a new instance, based on a particular date/time. * The time zone and locale are determined by the {@code calendar} * parameter. * * @param time the date/time ({@code null} not permitted). * @param calendar the calendar to use for calculations ({@code null} not permitted). */ public Minute(Date time, Calendar calendar) { Args.nullNotPermitted(time, "time"); Args.nullNotPermitted(calendar, "calendar"); calendar.setTime(time); int min = calendar.get(Calendar.MINUTE); this.minute = (byte) min; this.hour = (byte) calendar.get(Calendar.HOUR_OF_DAY); this.day = new Day(time, calendar); peg(calendar); } /** * Creates a new minute. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. * * @param minute the minute (0-59). * @param hour the hour (0-23). * @param day the day (1-31). * @param month the month (1-12). * @param year the year (1900-9999). */ public Minute(int minute, int hour, int day, int month, int year) { this(minute, new Hour(hour, new Day(day, month, year))); } /** * Returns the day. * * @return The day. */ public Day getDay() { return this.day; } /** * Returns the hour. * * @return The hour (never {@code null}). */ public Hour getHour() { return new Hour(this.hour, this.day); } /** * Returns the hour. * * @return The hour. */ public int getHourValue() { return this.hour; } /** * Returns the minute. * * @return The minute. */ public int getMinute() { return this.minute; } /** * Returns the first millisecond of the minute. This will be determined * relative to the time zone specified in the constructor, or in the * calendar instance passed in the most recent call to the * {@link #peg(Calendar)} method. * * @return The first millisecond of the minute. * * @see #getLastMillisecond() */ @Override public long getFirstMillisecond() { return this.firstMillisecond; } /** * Returns the last millisecond of the minute. This will be * determined relative to the time zone specified in the constructor, or * in the calendar instance passed in the most recent call to the * {@link #peg(Calendar)} method. * * @return The last millisecond of the minute. * * @see #getFirstMillisecond() */ @Override public long getLastMillisecond() { return this.lastMillisecond; } /** * Recalculates the start date/time and end date/time for this time period * relative to the supplied calendar (which incorporates a time zone). * * @param calendar the calendar ({@code null} not permitted). */ @Override public void peg(Calendar calendar) { this.firstMillisecond = getFirstMillisecond(calendar); this.lastMillisecond = getLastMillisecond(calendar); } /** * Returns the minute preceding this one. * No matter what time zone and locale this instance was created with, * the returned instance will use the default calendar for time * calculations, obtained with {@link RegularTimePeriod#getCalendarInstance()}. * * @return The minute preceding this one. */ @Override public RegularTimePeriod previous() { Minute result; if (this.minute != FIRST_MINUTE_IN_HOUR) { result = new Minute(this.minute - 1, getHour()); } else { Hour h = (Hour) getHour().previous(); if (h != null) { result = new Minute(LAST_MINUTE_IN_HOUR, h); } else { result = null; } } return result; } /** * Returns the minute following this one. * No matter what time zone and locale this instance was created with, * the returned instance will use the default calendar for time * calculations, obtained with {@link RegularTimePeriod#getCalendarInstance()}. * * @return The minute following this one. */ @Override public RegularTimePeriod next() { Minute result; if (this.minute != LAST_MINUTE_IN_HOUR) { result = new Minute(this.minute + 1, getHour()); } else { // we are at the last minute in the hour... Hour nextHour = (Hour) getHour().next(); if (nextHour != null) { result = new Minute(FIRST_MINUTE_IN_HOUR, nextHour); } else { result = null; } } return result; } /** * Returns a serial index number for the minute. * * @return The serial index number. */ @Override public long getSerialIndex() { long hourIndex = this.day.getSerialIndex() * 24L + this.hour; return hourIndex * 60L + this.minute; } /** * Returns the first millisecond of the minute. * * @param calendar the calendar which defines the timezone * ({@code null} not permitted). * * @return The first millisecond. * * @throws NullPointerException if {@code calendar} is * {@code null}. */ @Override public long getFirstMillisecond(Calendar calendar) { int year = this.day.getYear(); int month = this.day.getMonth() - 1; int d = this.day.getDayOfMonth(); calendar.clear(); calendar.set(year, month, d, this.hour, this.minute, 0); calendar.set(Calendar.MILLISECOND, 0); return calendar.getTimeInMillis(); } /** * Returns the last millisecond of the minute. * * @param calendar the calendar / timezone ({@code null} not * permitted). * * @return The last millisecond. * * @throws NullPointerException if {@code calendar} is * {@code null}. */ @Override public long getLastMillisecond(Calendar calendar) { int year = this.day.getYear(); int month = this.day.getMonth() - 1; int d = this.day.getDayOfMonth(); calendar.clear(); calendar.set(year, month, d, this.hour, this.minute, 59); calendar.set(Calendar.MILLISECOND, 999); return calendar.getTimeInMillis(); } /** * Tests the equality of this object against an arbitrary Object. *

* This method will return true ONLY if the object is a Minute object * representing the same minute as this instance. * * @param obj the object to compare ({@code null} permitted). * * @return {@code true} if the minute and hour value of this and the * object are the same. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof Minute)) { return false; } Minute that = (Minute) obj; if (this.minute != that.minute) { return false; } if (this.hour != that.hour) { return false; } return this.day.equals(that.day); } /** * Returns a hash code for this object instance. The approach described * by Joshua Bloch in "Effective Java" has been used here: *

* {@code http://developer.java.sun.com/developer/Books/effectivejava * /Chapter3.pdf} * * @return A hash code. */ @Override public int hashCode() { int result = 17; result = 37 * result + this.minute; result = 37 * result + this.hour; result = 37 * result + this.day.hashCode(); return result; } /** * Returns an integer indicating the order of this Minute object relative * to the specified object: * * negative == before, zero == same, positive == after. * * @param o1 object to compare. * * @return negative == before, zero == same, positive == after. */ @Override public int compareTo(Object o1) { int result; // CASE 1 : Comparing to another Minute object // ------------------------------------------- if (o1 instanceof Minute) { Minute m = (Minute) o1; result = this.day.compareTo(m.day); if (result == 0) { result = this.hour - m.hour; if (result == 0) { result = this.minute - m.getMinute(); } } } // CASE 2 : Comparing to another TimePeriod object // ----------------------------------------------- else if (o1 instanceof RegularTimePeriod) { // more difficult case - evaluate later... result = 0; } // CASE 3 : Comparing to a non-TimePeriod object // --------------------------------------------- else { // consider time periods to be ordered after general objects result = 1; } return result; } /** * Creates a Minute instance by parsing a string. The string is assumed to * be in the format "YYYY-MM-DD HH:MM", perhaps with leading or trailing * whitespace. * * @param s the minute string to parse. * * @return {@code null}, if the string is not parseable, the minute * otherwise. */ public static Minute parseMinute(String s) { Minute result = null; s = s.trim(); String daystr = s.substring(0, Math.min(10, s.length())); Day day = Day.parseDay(daystr); if (day != null) { String hmstr = s.substring(Math.min(daystr.length() + 1, s.length())); hmstr = hmstr.trim(); String hourstr = hmstr.substring(0, Math.min(2, hmstr.length())); int hour = Integer.parseInt(hourstr); if ((hour >= 0) && (hour <= 23)) { String minstr = hmstr.substring( Math.min(hourstr.length() + 1, hmstr.length())); int minute = Integer.parseInt(minstr); if ((minute >= 0) && (minute <= 59)) { result = new Minute(minute, new Hour(hour, day)); } } } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/Month.java000066400000000000000000000410071463604235500255560ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------- * Month.java * ---------- * (C) Copyright 2001-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Chris Boek; * */ package org.jfree.data.time; import java.io.Serializable; import java.util.Calendar; import java.util.Date; import java.util.Locale; import java.util.TimeZone; import org.jfree.chart.date.MonthConstants; import org.jfree.chart.date.SerialDate; /** * Represents a single month. This class is immutable, which is a requirement * for all {@link RegularTimePeriod} subclasses. */ public class Month extends RegularTimePeriod implements Serializable { /** For serialization. */ private static final long serialVersionUID = -5090216912548722570L; /** The month (1-12). */ private int month; /** The year in which the month falls. */ private int year; /** The first millisecond. */ private long firstMillisecond; /** The last millisecond. */ private long lastMillisecond; /** * Constructs a new Month, based on the current system time. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. */ public Month() { this(new Date()); } /** * Constructs a new month instance. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. * * @param month the month (in the range 1 to 12). * @param year the year. */ public Month(int month, int year) { if ((month < 1) || (month > 12)) { throw new IllegalArgumentException("Month outside valid range."); } this.month = month; this.year = year; peg(getCalendarInstance()); } /** * Constructs a new month instance. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. * * @param month the month (in the range 1 to 12). * @param year the year. */ public Month(int month, Year year) { if ((month < 1) || (month > 12)) { throw new IllegalArgumentException("Month outside valid range."); } this.month = month; this.year = year.getYear(); peg(getCalendarInstance()); } /** * Constructs a new {@code Month} instance, based on a date/time. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. * * @param time the date/time ({@code null} not permitted). * * @see #Month(Date, TimeZone, Locale) */ public Month(Date time) { this(time, getCalendarInstance()); } /** * Creates a new {@code Month} instance, based on the specified time, * zone and locale. * * @param time the current time. * @param zone the time zone. * @param locale the locale. */ public Month(Date time, TimeZone zone, Locale locale) { Calendar calendar = Calendar.getInstance(zone, locale); calendar.setTime(time); this.month = calendar.get(Calendar.MONTH) + 1; this.year = calendar.get(Calendar.YEAR); peg(calendar); } /** * Constructs a new instance, based on a particular date/time. * The time zone and locale are determined by the {@code calendar} * parameter. * * @param time the date/time ({@code null} not permitted). * @param calendar the calendar to use for calculations ({@code null} not permitted). */ public Month(Date time, Calendar calendar) { calendar.setTime(time); this.month = calendar.get(Calendar.MONTH) + 1; this.year = calendar.get(Calendar.YEAR); peg(calendar); } /** * Returns the year in which the month falls. * * @return The year in which the month falls (as a Year object). */ public Year getYear() { return new Year(this.year); } /** * Returns the year in which the month falls. * * @return The year in which the month falls (as an int). */ public int getYearValue() { return this.year; } /** * Returns the month. Note that 1=JAN, 2=FEB, ... * * @return The month. */ public int getMonth() { return this.month; } /** * Returns the first millisecond of the month. This will be determined * relative to the time zone specified in the constructor, or in the * calendar instance passed in the most recent call to the * {@link #peg(Calendar)} method. * * @return The first millisecond of the month. * * @see #getLastMillisecond() */ @Override public long getFirstMillisecond() { return this.firstMillisecond; } /** * Returns the last millisecond of the month. This will be * determined relative to the time zone specified in the constructor, or * in the calendar instance passed in the most recent call to the * {@link #peg(Calendar)} method. * * @return The last millisecond of the month. * * @see #getFirstMillisecond() */ @Override public long getLastMillisecond() { return this.lastMillisecond; } /** * Recalculates the start date/time and end date/time for this time period * relative to the supplied calendar (which incorporates a time zone). * * @param calendar the calendar ({@code null} not permitted). */ @Override public void peg(Calendar calendar) { this.firstMillisecond = getFirstMillisecond(calendar); this.lastMillisecond = getLastMillisecond(calendar); } /** * Returns the month preceding this one. Note that the returned * {@link Month} is "pegged" using the default calendar, obtained * with {@link RegularTimePeriod#getCalendarInstance()}, irrespective of * the time-zone used to peg of the current month (which is not recorded * anywhere). See the {@link #peg(Calendar)} method. * * @return The month preceding this one. */ @Override public RegularTimePeriod previous() { Month result; if (this.month != MonthConstants.JANUARY) { result = new Month(this.month - 1, this.year); } else { if (this.year > 1900) { result = new Month(MonthConstants.DECEMBER, this.year - 1); } else { result = null; } } return result; } /** * Returns the month following this one. Note that the returned * {@link Month} is "pegged" using the default calendar, obtained * with {@link RegularTimePeriod#getCalendarInstance()}, irrespective of * the time-zone used to peg of the current month (which is not recorded * anywhere). See the {@link #peg(Calendar)} method. * * @return The month following this one. */ @Override public RegularTimePeriod next() { Month result; if (this.month != MonthConstants.DECEMBER) { result = new Month(this.month + 1, this.year); } else { if (this.year < 9999) { result = new Month(MonthConstants.JANUARY, this.year + 1); } else { result = null; } } return result; } /** * Returns a serial index number for the month. * * @return The serial index number. */ @Override public long getSerialIndex() { return this.year * 12L + this.month; } /** * Returns a string representing the month (e.g. "January 2002"). *

* To do: look at internationalisation. * * @return A string representing the month. */ @Override public String toString() { return SerialDate.monthCodeToString(this.month) + " " + this.year; } /** * Tests the equality of this Month object to an arbitrary object. * Returns true if the target is a Month instance representing the same * month as this object. In all other cases, returns false. * * @param obj the object ({@code null} permitted). * * @return {@code true} if month and year of this and object are the * same. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof Month)) { return false; } Month that = (Month) obj; if (this.month != that.month) { return false; } if (this.year != that.year) { return false; } return true; } /** * Returns a hash code for this object instance. The approach described by * Joshua Bloch in "Effective Java" has been used here: *

* {@code http://developer.java.sun.com/developer/Books/effectivejava * /Chapter3.pdf} * * @return A hash code. */ @Override public int hashCode() { int result = 17; result = 37 * result + this.month; result = 37 * result + this.year; return result; } /** * Returns an integer indicating the order of this Month object relative to * the specified * object: negative == before, zero == same, positive == after. * * @param o1 the object to compare. * * @return negative == before, zero == same, positive == after. */ @Override public int compareTo(Object o1) { int result; // CASE 1 : Comparing to another Month object // -------------------------------------------- if (o1 instanceof Month) { Month m = (Month) o1; result = this.year - m.getYearValue(); if (result == 0) { result = this.month - m.getMonth(); } } // CASE 2 : Comparing to another TimePeriod object // ----------------------------------------------- else if (o1 instanceof RegularTimePeriod) { // more difficult case - evaluate later... result = 0; } // CASE 3 : Comparing to a non-TimePeriod object // --------------------------------------------- else { // consider time periods to be ordered after general objects result = 1; } return result; } /** * Returns the first millisecond of the month, evaluated using the supplied * calendar (which determines the time zone). * * @param calendar the calendar ({@code null} not permitted). * * @return The first millisecond of the month. * * @throws NullPointerException if {@code calendar} is * {@code null}. */ @Override public long getFirstMillisecond(Calendar calendar) { calendar.set(this.year, this.month - 1, 1, 0, 0, 0); calendar.set(Calendar.MILLISECOND, 0); return calendar.getTimeInMillis(); } /** * Returns the last millisecond of the month, evaluated using the supplied * calendar (which determines the time zone). * * @param calendar the calendar ({@code null} not permitted). * * @return The last millisecond of the month. * * @throws NullPointerException if {@code calendar} is * {@code null}. */ @Override public long getLastMillisecond(Calendar calendar) { int eom = SerialDate.lastDayOfMonth(this.month, this.year); calendar.set(this.year, this.month - 1, eom, 23, 59, 59); calendar.set(Calendar.MILLISECOND, 999); return calendar.getTimeInMillis(); } /** * Parses the string argument as a month. This method is required to * accept the format "YYYY-MM". It will also accept "MM-YYYY". Anything * else, at the moment, is a bonus. * * @param s the string to parse ({@code null} permitted). * * @return {@code null} if the string is not parseable, the month * otherwise. */ public static Month parseMonth(String s) { Month result = null; if (s == null) { return result; } // trim whitespace from either end of the string s = s.trim(); int i = Month.findSeparator(s); String s1, s2; boolean yearIsFirst; // if there is no separator, we assume the first four characters // are YYYY if (i == -1) { yearIsFirst = true; s1 = s.substring(0, 5); s2 = s.substring(5); } else { s1 = s.substring(0, i).trim(); s2 = s.substring(i + 1).trim(); // now it is trickier to determine if the month or year is first Year y1 = Month.evaluateAsYear(s1); if (y1 == null) { yearIsFirst = false; } else { Year y2 = Month.evaluateAsYear(s2); if (y2 == null) { yearIsFirst = true; } else { yearIsFirst = (s1.length() > s2.length()); } } } Year year; int month; if (yearIsFirst) { year = Month.evaluateAsYear(s1); month = SerialDate.stringToMonthCode(s2); } else { year = Month.evaluateAsYear(s2); month = SerialDate.stringToMonthCode(s1); } if (month == -1) { throw new TimePeriodFormatException("Can't evaluate the month."); } if (year == null) { throw new TimePeriodFormatException("Can't evaluate the year."); } result = new Month(month, year); return result; } /** * Finds the first occurrence of '-', or if that character is not found, * the first occurrence of ',', or the first occurrence of ' ' or '.' * * @param s the string to parse. * * @return The position of the separator character, or {@code -1} if * none of the characters were found. */ private static int findSeparator(String s) { int result = s.indexOf('-'); if (result == -1) { result = s.indexOf(','); } if (result == -1) { result = s.indexOf(' '); } if (result == -1) { result = s.indexOf('.'); } return result; } /** * Creates a year from a string, or returns {@code null} (format * exceptions suppressed). * * @param s the string to parse. * * @return {@code null} if the string is not parseable, the year * otherwise. */ private static Year evaluateAsYear(String s) { Year result = null; try { result = Year.parseYear(s); } catch (TimePeriodFormatException e) { // suppress } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/MovingAverage.java000066400000000000000000000307021463604235500272230ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * MovingAverage.java * ------------------ * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Benoit Xhenseval; * */ package org.jfree.data.time; import org.jfree.chart.util.Args; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; /** * A utility class for calculating moving averages of time series data. */ public class MovingAverage { /** * Creates a new {@link TimeSeriesCollection} containing a moving average * series for each series in the source collection. * * @param source the source collection. * @param suffix the suffix added to each source series name to create the * corresponding moving average series name. * @param periodCount the number of periods in the moving average * calculation. * @param skip the number of initial periods to skip. * * @return A collection of moving average time series. */ public static TimeSeriesCollection createMovingAverage( TimeSeriesCollection source, String suffix, int periodCount, int skip) { Args.nullNotPermitted(source, "source"); if (periodCount < 1) { throw new IllegalArgumentException("periodCount must be greater " + "than or equal to 1."); } TimeSeriesCollection result = new TimeSeriesCollection(); for (int i = 0; i < source.getSeriesCount(); i++) { TimeSeries sourceSeries = source.getSeries(i); TimeSeries maSeries = createMovingAverage(sourceSeries, sourceSeries.getKey() + suffix, periodCount, skip); result.addSeries(maSeries); } return result; } /** * Creates a new {@link TimeSeries} containing moving average values for * the given series. If the series is empty (contains zero items), the * result is an empty series. * * @param source the source series. * @param name the name of the new series. * @param periodCount the number of periods used in the average * calculation. * @param skip the number of initial periods to skip. * * @return The moving average series. */ public static TimeSeries createMovingAverage(TimeSeries source, String name, int periodCount, int skip) { Args.nullNotPermitted(source, "source"); if (periodCount < 1) { throw new IllegalArgumentException("periodCount must be greater " + "than or equal to 1."); } TimeSeries result = new TimeSeries(name); if (source.getItemCount() > 0) { // if the initial averaging period is to be excluded, then // calculate the index of the // first data item to have an average calculated... long firstSerial = source.getTimePeriod(0).getSerialIndex() + skip; for (int i = source.getItemCount() - 1; i >= 0; i--) { // get the current data item... RegularTimePeriod period = source.getTimePeriod(i); long serial = period.getSerialIndex(); if (serial >= firstSerial) { // work out the average for the earlier values... int n = 0; double sum = 0.0; long serialLimit = period.getSerialIndex() - periodCount; int offset = 0; boolean finished = false; while ((offset < periodCount) && (!finished)) { if ((i - offset) >= 0) { TimeSeriesDataItem item = source.getRawDataItem( i - offset); RegularTimePeriod p = item.getPeriod(); Number v = item.getValue(); long currentIndex = p.getSerialIndex(); if (currentIndex > serialLimit) { if (v != null) { sum = sum + v.doubleValue(); n = n + 1; } } else { finished = true; } } offset = offset + 1; } if (n > 0) { result.add(period, sum / n); } else { result.add(period, null); } } } } return result; } /** * Creates a new {@link TimeSeries} containing moving average values for * the given series, calculated by number of points (irrespective of the * 'age' of those points). If the series is empty (contains zero items), * the result is an empty series. *

* Developed by Benoit Xhenseval (www.ObjectLab.co.uk). * * @param source the source series. * @param name the name of the new series. * @param pointCount the number of POINTS used in the average calculation * (not periods!) * * @return The moving average series. */ public static TimeSeries createPointMovingAverage(TimeSeries source, String name, int pointCount) { Args.nullNotPermitted(source, "source"); if (pointCount < 2) { throw new IllegalArgumentException("periodCount must be greater " + "than or equal to 2."); } TimeSeries result = new TimeSeries(name); double rollingSumForPeriod = 0.0; for (int i = 0; i < source.getItemCount(); i++) { // get the current data item... TimeSeriesDataItem current = source.getRawDataItem(i); RegularTimePeriod period = current.getPeriod(); // FIXME: what if value is null on next line? rollingSumForPeriod += current.getValue().doubleValue(); if (i > pointCount - 1) { // remove the point i-periodCount out of the rolling sum. TimeSeriesDataItem startOfMovingAvg = source.getRawDataItem( i - pointCount); rollingSumForPeriod -= startOfMovingAvg.getValue() .doubleValue(); result.add(period, rollingSumForPeriod / pointCount); } else if (i == pointCount - 1) { result.add(period, rollingSumForPeriod / pointCount); } } return result; } /** * Creates a new {@link XYDataset} containing the moving averages of each * series in the {@code source} dataset. * * @param source the source dataset. * @param suffix the string to append to source series names to create * target series names. * @param period the averaging period. * @param skip the length of the initial skip period. * * @return The dataset. */ public static XYDataset createMovingAverage(XYDataset source, String suffix, long period, long skip) { return createMovingAverage(source, suffix, (double) period, (double) skip); } /** * Creates a new {@link XYDataset} containing the moving averages of each * series in the {@code source} dataset. * * @param source the source dataset. * @param suffix the string to append to source series names to create * target series names. * @param period the averaging period. * @param skip the length of the initial skip period. * * @return The dataset. */ public static XYDataset createMovingAverage(XYDataset source, String suffix, double period, double skip) { Args.nullNotPermitted(source, "source"); XYSeriesCollection result = new XYSeriesCollection(); for (int i = 0; i < source.getSeriesCount(); i++) { XYSeries s = createMovingAverage(source, i, source.getSeriesKey(i) + suffix, period, skip); result.addSeries(s); } return result; } /** * Creates a new {@link XYSeries} containing the moving averages of one * series in the {@code source} dataset. * * @param source the source dataset. * @param series the series index (zero based). * @param name the name for the new series. * @param period the averaging period. * @param skip the length of the initial skip period. * * @return The dataset. */ public static XYSeries createMovingAverage(XYDataset source, int series, String name, double period, double skip) { Args.nullNotPermitted(source, "source"); if (period < Double.MIN_VALUE) { throw new IllegalArgumentException("period must be positive."); } if (skip < 0.0) { throw new IllegalArgumentException("skip must be >= 0.0."); } XYSeries result = new XYSeries(name); if (source.getItemCount(series) > 0) { // if the initial averaging period is to be excluded, then // calculate the lowest x-value to have an average calculated... double first = source.getXValue(series, 0) + skip; for (int i = source.getItemCount(series) - 1; i >= 0; i--) { // get the current data item... double x = source.getXValue(series, i); if (x >= first) { // work out the average for the earlier values... int n = 0; double sum = 0.0; double limit = x - period; int offset = 0; boolean finished = false; while (!finished) { if ((i - offset) >= 0) { double xx = source.getXValue(series, i - offset); Number yy = source.getY(series, i - offset); if (xx > limit) { if (yy != null) { sum = sum + yy.doubleValue(); n = n + 1; } } else { finished = true; } } else { finished = true; } offset = offset + 1; } if (n > 0) { result.add(x, sum / n); } else { result.add(x, null); } } } } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/Quarter.java000066400000000000000000000363511463604235500261220ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------ * Quarter.java * ------------ * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import java.io.Serializable; import java.util.Calendar; import java.util.Date; import java.util.Locale; import java.util.TimeZone; import org.jfree.chart.date.MonthConstants; import org.jfree.chart.date.SerialDate; /** * Defines a quarter (in a given year). The range supported is Q1 1900 to * Q4 9999. This class is immutable, which is a requirement for all * {@link RegularTimePeriod} subclasses. */ public class Quarter extends RegularTimePeriod implements Serializable { /** For serialization. */ private static final long serialVersionUID = 3810061714380888671L; /** Constant for quarter 1. */ public static final int FIRST_QUARTER = 1; /** Constant for quarter 4. */ public static final int LAST_QUARTER = 4; /** The first month in each quarter. */ public static final int[] FIRST_MONTH_IN_QUARTER = { 0, MonthConstants.JANUARY, MonthConstants.APRIL, MonthConstants.JULY, MonthConstants.OCTOBER }; /** The last month in each quarter. */ public static final int[] LAST_MONTH_IN_QUARTER = { 0, MonthConstants.MARCH, MonthConstants.JUNE, MonthConstants.SEPTEMBER, MonthConstants.DECEMBER }; /** The year in which the quarter falls. */ private short year; /** The quarter (1-4). */ private byte quarter; /** The first millisecond. */ private long firstMillisecond; /** The last millisecond. */ private long lastMillisecond; /** * Constructs a new Quarter, based on the current system date/time. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. */ public Quarter() { this(new Date()); } /** * Constructs a new quarter. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. * * @param year the year (1900 to 9999). * @param quarter the quarter (1 to 4). */ public Quarter(int quarter, int year) { if ((quarter < FIRST_QUARTER) || (quarter > LAST_QUARTER)) { throw new IllegalArgumentException("Quarter outside valid range."); } this.year = (short) year; this.quarter = (byte) quarter; peg(getCalendarInstance()); } /** * Constructs a new quarter. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. * * @param quarter the quarter (1 to 4). * @param year the year (1900 to 9999). */ public Quarter(int quarter, Year year) { if ((quarter < FIRST_QUARTER) || (quarter > LAST_QUARTER)) { throw new IllegalArgumentException("Quarter outside valid range."); } this.year = (short) year.getYear(); this.quarter = (byte) quarter; peg(getCalendarInstance()); } /** * Constructs a new instance, based on a date/time. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. * * @param time the date/time ({@code null} not permitted). * * @see #Quarter(Date, TimeZone, Locale) */ public Quarter(Date time) { this(time, getCalendarInstance()); } /** * Creates a new {@code Quarter} instance, using the specified * zone and locale. * * @param time the current time. * @param zone the time zone. * @param locale the locale. */ public Quarter(Date time, TimeZone zone, Locale locale) { Calendar calendar = Calendar.getInstance(zone, locale); calendar.setTime(time); int month = calendar.get(Calendar.MONTH) + 1; this.quarter = (byte) SerialDate.monthCodeToQuarter(month); this.year = (short) calendar.get(Calendar.YEAR); peg(calendar); } /** * Constructs a new instance, based on a particular date/time. * The time zone and locale are determined by the {@code calendar} * parameter. * * @param time the date/time ({@code null} not permitted). * @param calendar the calendar to use for calculations ({@code null} not permitted). */ public Quarter(Date time, Calendar calendar) { calendar.setTime(time); int month = calendar.get(Calendar.MONTH) + 1; this.quarter = (byte) SerialDate.monthCodeToQuarter(month); this.year = (short) calendar.get(Calendar.YEAR); peg(calendar); } /** * Returns the quarter. * * @return The quarter. */ public int getQuarter() { return this.quarter; } /** * Returns the year. * * @return The year. */ public Year getYear() { return new Year(this.year); } /** * Returns the year. * * @return The year. */ public int getYearValue() { return this.year; } /** * Returns the first millisecond of the quarter. This will be determined * relative to the time zone specified in the constructor, or in the * calendar instance passed in the most recent call to the * {@link #peg(Calendar)} method. * * @return The first millisecond of the quarter. * * @see #getLastMillisecond() */ @Override public long getFirstMillisecond() { return this.firstMillisecond; } /** * Returns the last millisecond of the quarter. This will be * determined relative to the time zone specified in the constructor, or * in the calendar instance passed in the most recent call to the * {@link #peg(Calendar)} method. * * @return The last millisecond of the quarter. * * @see #getFirstMillisecond() */ @Override public long getLastMillisecond() { return this.lastMillisecond; } /** * Recalculates the start date/time and end date/time for this time period * relative to the supplied calendar (which incorporates a time zone). * * @param calendar the calendar ({@code null} not permitted). */ @Override public void peg(Calendar calendar) { this.firstMillisecond = getFirstMillisecond(calendar); this.lastMillisecond = getLastMillisecond(calendar); } /** * Returns the quarter preceding this one. * No matter what time zone and locale this instance was created with, * the returned instance will use the default calendar for time * calculations, obtained with {@link RegularTimePeriod#getCalendarInstance()}. * * @return The quarter preceding this one (or {@code null} if this is * Q1 1900). */ @Override public RegularTimePeriod previous() { Quarter result; if (this.quarter > FIRST_QUARTER) { result = new Quarter(this.quarter - 1, this.year); } else { if (this.year > 1900) { result = new Quarter(LAST_QUARTER, this.year - 1); } else { result = null; } } return result; } /** * Returns the quarter following this one. * No matter what time zone and locale this instance was created with, * the returned instance will use the default calendar for time * calculations, obtained with {@link RegularTimePeriod#getCalendarInstance()}. * * @return The quarter following this one (or null if this is Q4 9999). */ @Override public RegularTimePeriod next() { Quarter result; if (this.quarter < LAST_QUARTER) { result = new Quarter(this.quarter + 1, this.year); } else { if (this.year < 9999) { result = new Quarter(FIRST_QUARTER, this.year + 1); } else { result = null; } } return result; } /** * Returns a serial index number for the quarter. * * @return The serial index number. */ @Override public long getSerialIndex() { return this.year * 4L + this.quarter; } /** * Tests the equality of this Quarter object to an arbitrary object. * Returns {@code true} if the target is a Quarter instance * representing the same quarter as this object. In all other cases, * returns {@code false}. * * @param obj the object ({@code null} permitted). * * @return {@code true} if quarter and year of this and the object are * the same. */ @Override public boolean equals(Object obj) { if (obj != null) { if (obj instanceof Quarter) { Quarter target = (Quarter) obj; return (this.quarter == target.getQuarter() && (this.year == target.getYearValue())); } else { return false; } } else { return false; } } /** * Returns a hash code for this object instance. The approach described by * Joshua Bloch in "Effective Java" has been used here: *

* {@code http://developer.java.sun.com/developer/Books/effectivejava * /Chapter3.pdf} * * @return A hash code. */ @Override public int hashCode() { int result = 17; result = 37 * result + this.quarter; result = 37 * result + this.year; return result; } /** * Returns an integer indicating the order of this Quarter object relative * to the specified object: * * negative == before, zero == same, positive == after. * * @param o1 the object to compare * * @return negative == before, zero == same, positive == after. */ @Override public int compareTo(Object o1) { int result; // CASE 1 : Comparing to another Quarter object // -------------------------------------------- if (o1 instanceof Quarter) { Quarter q = (Quarter) o1; result = this.year - q.getYearValue(); if (result == 0) { result = this.quarter - q.getQuarter(); } } // CASE 2 : Comparing to another TimePeriod object // ----------------------------------------------- else if (o1 instanceof RegularTimePeriod) { // more difficult case - evaluate later... result = 0; } // CASE 3 : Comparing to a non-TimePeriod object // --------------------------------------------- else { // consider time periods to be ordered after general objects result = 1; } return result; } /** * Returns a string representing the quarter (e.g. "Q1/2002"). * * @return A string representing the quarter. */ @Override public String toString() { return "Q" + this.quarter + "/" + this.year; } /** * Returns the first millisecond in the Quarter, evaluated using the * supplied calendar (which determines the time zone). * * @param calendar the calendar ({@code null} not permitted). * * @return The first millisecond in the Quarter. * * @throws NullPointerException if {@code calendar} is * {@code null}. */ @Override public long getFirstMillisecond(Calendar calendar) { int month = Quarter.FIRST_MONTH_IN_QUARTER[this.quarter]; calendar.set(this.year, month - 1, 1, 0, 0, 0); calendar.set(Calendar.MILLISECOND, 0); return calendar.getTimeInMillis(); } /** * Returns the last millisecond of the Quarter, evaluated using the * supplied calendar (which determines the time zone). * * @param calendar the calendar ({@code null} not permitted). * * @return The last millisecond of the Quarter. * * @throws NullPointerException if {@code calendar} is * {@code null}. */ @Override public long getLastMillisecond(Calendar calendar) { int month = Quarter.LAST_MONTH_IN_QUARTER[this.quarter]; int eom = SerialDate.lastDayOfMonth(month, this.year); calendar.set(this.year, month - 1, eom, 23, 59, 59); calendar.set(Calendar.MILLISECOND, 999); return calendar.getTimeInMillis(); } /** * Parses the string argument as a quarter. *

* This method should accept the following formats: "YYYY-QN" and "QN-YYYY", * where the "-" can be a space, a forward-slash (/), comma or a dash (-). * @param s A string representing the quarter. * * @return The quarter. */ public static Quarter parseQuarter(String s) { // find the Q and the integer following it (remove both from the // string)... int i = s.indexOf("Q"); if (i == -1) { throw new TimePeriodFormatException("Missing Q."); } if (i == s.length() - 1) { throw new TimePeriodFormatException("Q found at end of string."); } String qstr = s.substring(i + 1, i + 2); int quarter = Integer.parseInt(qstr); String remaining = s.substring(0, i) + s.substring(i + 2); // replace any / , or - with a space remaining = remaining.replace('/', ' '); remaining = remaining.replace(',', ' '); remaining = remaining.replace('-', ' '); // parse the string... Year year = Year.parseYear(remaining.trim()); Quarter result = new Quarter(quarter, year); return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/RegularTimePeriod.java000066400000000000000000000350261463604235500300600ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * RegularTimePeriod.java * ---------------------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import java.lang.reflect.Constructor; import java.util.Calendar; import java.util.Date; import java.util.Locale; import java.util.TimeZone; import java.util.concurrent.atomic.AtomicReference; import org.jfree.chart.date.MonthConstants; /** * An abstract class representing a unit of time. Convenient methods are * provided for calculating the next and previous time periods. Conversion * methods are defined that return the first and last milliseconds of the time * period. The results from these methods are timezone dependent. *

* This class is immutable, and all subclasses should be immutable also. */ public abstract class RegularTimePeriod implements TimePeriod, Comparable, MonthConstants { private static final AtomicReference calendarPrototype = new AtomicReference<>(); private static final ThreadLocal threadLocalCalendar = new ThreadLocal<>(); /** * Creates a time period that includes the specified millisecond, assuming * the given time zone. * * @param c the time period class. * @param millisecond the time. * @param zone the time zone. * @param locale the locale. * * @return The time period. */ public static RegularTimePeriod createInstance(Class c, Date millisecond, TimeZone zone, Locale locale) { RegularTimePeriod result = null; try { Constructor constructor = c.getDeclaredConstructor( new Class[] {Date.class, TimeZone.class, Locale.class}); result = (RegularTimePeriod) constructor.newInstance( new Object[] {millisecond, zone, locale}); } catch (Exception e) { // do nothing, so null is returned } return result; } /** * Returns a subclass of {@link RegularTimePeriod} that is smaller than * the specified class. * * @param c a subclass of {@link RegularTimePeriod}. * * @return A class. */ public static Class downsize(Class c) { if (c.equals(Year.class)) { return Quarter.class; } else if (c.equals(Quarter.class)) { return Month.class; } else if (c.equals(Month.class)) { return Day.class; } else if (c.equals(Day.class)) { return Hour.class; } else if (c.equals(Hour.class)) { return Minute.class; } else if (c.equals(Minute.class)) { return Second.class; } else if (c.equals(Second.class)) { return Millisecond.class; } else { return Millisecond.class; } } /** * Creates or returns a thread-local Calendar instance. * This function is used by the various subclasses to obtain a calendar for * date-time to/from ms-since-epoch conversions (and to determine * the first day of the week, in case of {@link Week}). *

* If a thread-local calendar was set with {@link #setThreadLocalCalendarInstance(Calendar)}, * then it is simply returned. *

* Otherwise, If a global calendar prototype was set with {@link #setCalendarInstancePrototype(Calendar)}, * then it is cloned and set as the thread-local calendar instance for future use, * as if it was set with {@link #setThreadLocalCalendarInstance(Calendar)}. *

* Otherwise, if neither is set, a new instance will be created every * time with {@link Calendar#getInstance()}, resorting to JFreeChart 1.5.0 * behavior (leading to huge load on GC and high memory consumption * if many instances are created). * * @return a thread-local Calendar instance */ protected static Calendar getCalendarInstance() { Calendar calendar = threadLocalCalendar.get(); if (calendar == null) { Calendar prototype = calendarPrototype.get(); if (prototype != null) { calendar = (Calendar) prototype.clone(); threadLocalCalendar.set(calendar); } } return calendar != null ? calendar : Calendar.getInstance(); } /** * Sets the thread-local calendar instance for time calculations. *

* {@code RegularTimePeriod} instances sometimes need a {@link Calendar} * to perform time calculations (date-time from/to milliseconds-since-epoch). * In JFreeChart 1.5.0, they created a new {@code Calendar} instance * every time they needed one. This created a huge load on GC and lead * to high memory consumption. To avoid this, a thread-local {@code Calendar} * instance can be set, which will then be used for time calculations * every time, unless the caller passes a specific {@code Calendar} * instance in places where the API allows it. *

* If the specified calendar is {@code null}, or if this method was never called, * then the next time a calendar instance is needed, a new one will be created by cloning * the global prototype set with {@link #setCalendarInstancePrototype(Calendar)}. * If none was set either, then a new instance will be created every time * with {@link Calendar#getInstance()}, resorting to JFreeChart 1.5.0 behavior. * * @param calendar the new thread-local calendar instance */ public static void setThreadLocalCalendarInstance(Calendar calendar) { threadLocalCalendar.set(calendar); } /** * Sets a global calendar prototype for time calculations. *

* {@code RegularTimePeriod} instances sometimes need a {@link Calendar} * to perform time calculations (date-time from/to milliseconds-since-epoch). * In JFreeChart 1.5.0, they created a new {@code Calendar} instance * every time they needed one. This created a huge load on GC and lead * to high memory consumption. To avoid this, a prototype {@code Calendar} * can be set, which will be then cloned by every thread that needs * a {@code Calendar} instance. The prototype is not cloned right away, * and stored instead for later cloning, therefore the caller must not * alter the prototype after it has been passed to this method. *

* If the prototype is {@code null}, then thread-local calendars * set with {@link #setThreadLocalCalendarInstance(Calendar)} will be * used instead. If none was set for some thread, then a new instance will be * created with {@link Calendar#getInstance()} every time one is needed. * However, if the prototype was already cloned by some thread, * then setting it to {@code null} has no effect, and that thread must * explicitly set its own instance to {@code null} or something else to get * rid of the cloned calendar. *

* Calling {@code setCalendarInstancePrototype(Calendar.getInstance())} * somewhere early in an application will effectively mimic JFreeChart * 1.5.0 behavior (using the default calendar everywhere unless explicitly * specified), while preventing the many-allocations problem. There is one * important caveat, however: once a prototype is cloned by some * thread, calling {@link TimeZone#setDefault(TimeZone)} * or {@link Locale#setDefault(Locale)}} will have no * effect on future calculations. To avoid this problem, simply set * the default time zone and locale before setting the prototype. * * @param calendar the new thread-local calendar instance */ public static void setCalendarInstancePrototype(Calendar calendar) { calendarPrototype.set(calendar); } /** * Returns the time period preceding this one, or {@code null} if some * lower limit has been reached. * * @return The previous time period (possibly {@code null}). */ public abstract RegularTimePeriod previous(); /** * Returns the time period following this one, or {@code null} if some * limit has been reached. * * @return The next time period (possibly {@code null}). */ public abstract RegularTimePeriod next(); /** * Returns a serial index number for the time unit. * * @return The serial index number. */ public abstract long getSerialIndex(); ////////////////////////////////////////////////////////////////////////// /** * Recalculates the start date/time and end date/time for this time period * relative to the supplied calendar (which incorporates a time zone). * * @param calendar the calendar ({@code null} not permitted). */ public abstract void peg(Calendar calendar); /** * Returns the date/time that marks the start of the time period. This * method returns a new {@code Date} instance every time it is called. * * @return The start date/time. * * @see #getFirstMillisecond() */ @Override public Date getStart() { return new Date(getFirstMillisecond()); } /** * Returns the date/time that marks the end of the time period. This * method returns a new {@code Date} instance every time it is called. * * @return The end date/time. * * @see #getLastMillisecond() */ @Override public Date getEnd() { return new Date(getLastMillisecond()); } /** * Returns the first millisecond of the time period. This will be * determined relative to the time zone specified in the constructor, or * in the calendar instance passed in the most recent call to the * {@link #peg(Calendar)} method. * * @return The first millisecond of the time period. * * @see #getLastMillisecond() */ public abstract long getFirstMillisecond(); /** * Returns the first millisecond of the time period, evaluated using the * supplied calendar (which incorporates a timezone). * * @param calendar the calendar ({@code null} not permitted). * * @return The first millisecond of the time period. * * @throws NullPointerException if {@code calendar} is {@code null}. * * @see #getLastMillisecond(Calendar) */ public abstract long getFirstMillisecond(Calendar calendar); /** * Returns the last millisecond of the time period. This will be * determined relative to the time zone specified in the constructor, or * in the calendar instance passed in the most recent call to the * {@link #peg(Calendar)} method. * * @return The last millisecond of the time period. * * @see #getFirstMillisecond() */ public abstract long getLastMillisecond(); /** * Returns the last millisecond of the time period, evaluated using the * supplied calendar (which incorporates a timezone). * * @param calendar the calendar ({@code null} not permitted). * * @return The last millisecond of the time period. * * @see #getFirstMillisecond(Calendar) */ public abstract long getLastMillisecond(Calendar calendar); /** * Returns the millisecond closest to the middle of the time period. * * @return The middle millisecond. */ public long getMiddleMillisecond() { long m1 = getFirstMillisecond(); long m2 = getLastMillisecond(); return m1 + (m2 - m1) / 2; } /** * Returns the millisecond closest to the middle of the time period, * evaluated using the supplied calendar (which incorporates a timezone). * * @param calendar the calendar. * * @return The middle millisecond. */ public long getMiddleMillisecond(Calendar calendar) { long m1 = getFirstMillisecond(calendar); long m2 = getLastMillisecond(calendar); return m1 + (m2 - m1) / 2; } /** * Returns the millisecond (relative to the epoch) corresponding to the * specified {@code anchor} using the supplied {@code calendar} * (which incorporates a time zone). * * @param anchor the anchor ({@code null} not permitted). * @param calendar the calendar ({@code null} not permitted). * * @return Milliseconds since the epoch. */ public long getMillisecond(TimePeriodAnchor anchor, Calendar calendar) { if (anchor.equals(TimePeriodAnchor.START)) { return getFirstMillisecond(calendar); } else if (anchor.equals(TimePeriodAnchor.MIDDLE)) { return getMiddleMillisecond(calendar); } else if (anchor.equals(TimePeriodAnchor.END)) { return getLastMillisecond(calendar); } else { throw new IllegalStateException("Unrecognised anchor: " + anchor); } } /** * Returns a string representation of the time period. * * @return The string. */ @Override public String toString() { return String.valueOf(getStart()); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/Second.java000066400000000000000000000354011463604235500257050ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------- * Second.java * ----------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import java.io.Serializable; import java.util.Calendar; import java.util.Date; import java.util.Locale; import java.util.TimeZone; import org.jfree.chart.util.Args; /** * Represents a second in a particular day. This class is immutable, which is * a requirement for all {@link RegularTimePeriod} subclasses. */ public class Second extends RegularTimePeriod implements Serializable { /** For serialization. */ private static final long serialVersionUID = -6536564190712383466L; /** Useful constant for the first second in a minute. */ public static final int FIRST_SECOND_IN_MINUTE = 0; /** Useful constant for the last second in a minute. */ public static final int LAST_SECOND_IN_MINUTE = 59; /** The day. */ private Day day; /** The hour of the day. */ private byte hour; /** The minute. */ private byte minute; /** The second. */ private byte second; /** * The first millisecond. We don't store the last millisecond, because it * is always firstMillisecond + 999L. */ private long firstMillisecond; /** * Constructs a new Second, based on the system date/time. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. */ public Second() { this(new Date()); } /** * Constructs a new Second. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. * * @param second the second (0 to 59). * @param minute the minute ({@code null} not permitted). */ public Second(int second, Minute minute) { Args.requireInRange(second, "second", Second.FIRST_SECOND_IN_MINUTE, Second.LAST_SECOND_IN_MINUTE); Args.nullNotPermitted(minute, "minute"); this.day = minute.getDay(); this.hour = (byte) minute.getHourValue(); this.minute = (byte) minute.getMinute(); this.second = (byte) second; peg(getCalendarInstance()); } /** * Creates a new second. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. * * @param second the second (0-59). * @param minute the minute (0-59). * @param hour the hour (0-23). * @param day the day (1-31). * @param month the month (1-12). * @param year the year (1900-9999). */ public Second(int second, int minute, int hour, int day, int month, int year) { this(second, new Minute(minute, hour, day, month, year)); } /** * Constructs a new instance from the specified date/time. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. * * @param time the time ({@code null} not permitted). * * @see #Second(Date, TimeZone, Locale) */ public Second(Date time) { this(time, getCalendarInstance()); } /** * Creates a new second based on the supplied time and time zone. * * @param time the time ({@code null} not permitted). * @param zone the time zone ({@code null} not permitted). * @param locale the locale ({@code null} not permitted). */ public Second(Date time, TimeZone zone, Locale locale) { this(time, Calendar.getInstance(zone, locale)); } /** * Constructs a new instance, based on a particular date/time. * The time zone and locale are determined by the {@code calendar} * parameter. * * @param time the date/time ({@code null} not permitted). * @param calendar the calendar to use for calculations ({@code null} not permitted). */ public Second(Date time, Calendar calendar) { calendar.setTime(time); this.second = (byte) calendar.get(Calendar.SECOND); this.minute = (byte) calendar.get(Calendar.MINUTE); this.hour = (byte) calendar.get(Calendar.HOUR_OF_DAY); this.day = new Day(time, calendar); peg(calendar); } /** * Returns the second within the minute. * * @return The second (0 - 59). */ public int getSecond() { return this.second; } /** * Returns the minute. * * @return The minute (never {@code null}). */ public Minute getMinute() { return new Minute(this.minute, new Hour(this.hour, this.day)); } /** * Returns the first millisecond of the second. This will be determined * relative to the time zone specified in the constructor, or in the * calendar instance passed in the most recent call to the * {@link #peg(Calendar)} method. * * @return The first millisecond of the second. * * @see #getLastMillisecond() */ @Override public long getFirstMillisecond() { return this.firstMillisecond; } /** * Returns the last millisecond of the second. This will be * determined relative to the time zone specified in the constructor, or * in the calendar instance passed in the most recent call to the * {@link #peg(Calendar)} method. * * @return The last millisecond of the second. * * @see #getFirstMillisecond() */ @Override public long getLastMillisecond() { return this.firstMillisecond + 999L; } /** * Recalculates the start date/time and end date/time for this time period * relative to the supplied calendar (which incorporates a time zone). * * @param calendar the calendar ({@code null} not permitted). */ @Override public void peg(Calendar calendar) { this.firstMillisecond = getFirstMillisecond(calendar); } /** * Returns the second preceding this one. * No matter what time zone and locale this instance was created with, * the returned instance will use the default calendar for time * calculations, obtained with {@link RegularTimePeriod#getCalendarInstance()}. * * @return The second preceding this one. */ @Override public RegularTimePeriod previous() { Second result = null; if (this.second != FIRST_SECOND_IN_MINUTE) { result = new Second(this.second - 1, getMinute()); } else { Minute previous = (Minute) getMinute().previous(); if (previous != null) { result = new Second(LAST_SECOND_IN_MINUTE, previous); } } return result; } /** * Returns the second following this one. * No matter what time zone and locale this instance was created with, * the returned instance will use the default calendar for time * calculations, obtained with {@link RegularTimePeriod#getCalendarInstance()}. * * @return The second following this one. */ @Override public RegularTimePeriod next() { Second result = null; if (this.second != LAST_SECOND_IN_MINUTE) { result = new Second(this.second + 1, getMinute()); } else { Minute next = (Minute) getMinute().next(); if (next != null) { result = new Second(FIRST_SECOND_IN_MINUTE, next); } } return result; } /** * Returns a serial index number for the minute. * * @return The serial index number. */ @Override public long getSerialIndex() { long hourIndex = this.day.getSerialIndex() * 24L + this.hour; long minuteIndex = hourIndex * 60L + this.minute; return minuteIndex * 60L + this.second; } /** * Returns the first millisecond of the minute. * * @param calendar the calendar/timezone ({@code null} not permitted). * * @return The first millisecond. * * @throws NullPointerException if {@code calendar} is {@code null}. */ @Override public long getFirstMillisecond(Calendar calendar) { int year = this.day.getYear(); int month = this.day.getMonth() - 1; int d = this.day.getDayOfMonth(); calendar.clear(); calendar.set(year, month, d, this.hour, this.minute, this.second); calendar.set(Calendar.MILLISECOND, 0); return calendar.getTimeInMillis(); } /** * Returns the last millisecond of the second. * * @param calendar the calendar/timezone ({@code null} not permitted). * * @return The last millisecond. * * @throws NullPointerException if {@code calendar} is {@code null}. */ @Override public long getLastMillisecond(Calendar calendar) { return getFirstMillisecond(calendar) + 999L; } /** * Tests the equality of this object against an arbitrary Object. *

* This method will return true ONLY if the object is a Second object * representing the same second as this instance. * * @param obj the object to compare ({@code null} permitted). * * @return {@code true} if second and minute of this and the object * are the same. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof Second)) { return false; } Second that = (Second) obj; if (this.second != that.second) { return false; } if (this.minute != that.minute) { return false; } if (this.hour != that.hour) { return false; } if (!this.day.equals(that.day)) { return false; } return true; } /** * Returns a hash code for this object instance. The approach described by * Joshua Bloch in "Effective Java" has been used here: *

* {@code http://developer.java.sun.com/developer/Books/effectivejava * /Chapter3.pdf} * * @return A hash code. */ @Override public int hashCode() { int result = 17; result = 37 * result + this.second; result = 37 * result + this.minute; result = 37 * result + this.hour; result = 37 * result + this.day.hashCode(); return result; } /** * Returns an integer indicating the order of this Second object relative * to the specified * object: negative == before, zero == same, positive == after. * * @param o1 the object to compare. * * @return negative == before, zero == same, positive == after. */ @Override public int compareTo(Object o1) { int result; // CASE 1 : Comparing to another Second object // ------------------------------------------- if (o1 instanceof Second) { Second s = (Second) o1; if (this.firstMillisecond < s.firstMillisecond) { return -1; } else if (this.firstMillisecond > s.firstMillisecond) { return 1; } else { return 0; } } // CASE 2 : Comparing to another TimePeriod object // ----------------------------------------------- else if (o1 instanceof RegularTimePeriod) { // more difficult case - evaluate later... result = 0; } // CASE 3 : Comparing to a non-TimePeriod object // --------------------------------------------- else { // consider time periods to be ordered after general objects result = 1; } return result; } /** * Creates a new instance by parsing a string. The string is assumed to * be in the format "YYYY-MM-DD HH:MM:SS", perhaps with leading or trailing * whitespace. * * @param s the string to parse. * * @return The second, or {@code null} if the string is not parseable. */ public static Second parseSecond(String s) { Second result = null; s = s.trim(); String daystr = s.substring(0, Math.min(10, s.length())); Day day = Day.parseDay(daystr); if (day != null) { String hmsstr = s.substring(Math.min(daystr.length() + 1, s.length())); hmsstr = hmsstr.trim(); int l = hmsstr.length(); String hourstr = hmsstr.substring(0, Math.min(2, l)); String minstr = hmsstr.substring(Math.min(3, l), Math.min(5, l)); String secstr = hmsstr.substring(Math.min(6, l), Math.min(8, l)); int hour = Integer.parseInt(hourstr); if ((hour >= 0) && (hour <= 23)) { int minute = Integer.parseInt(minstr); if ((minute >= 0) && (minute <= 59)) { Minute m = new Minute(minute, new Hour(hour, day)); int second = Integer.parseInt(secstr); if ((second >= 0) && (second <= 59)) { result = new Second(second, m); } } } } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/SimpleTimePeriod.java000066400000000000000000000140561463604235500277100ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * SimpleTimePeriod.java * --------------------- * (C) Copyright 2002-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import java.io.Serializable; import java.util.Date; /** * An arbitrary period of time, measured to millisecond precision using * {@code java.util.Date}. *

* This class is intentionally immutable (that is, once constructed, you cannot * alter the start and end attributes). */ public class SimpleTimePeriod implements TimePeriod, Comparable, Serializable { /** For serialization. */ private static final long serialVersionUID = 8684672361131829554L; /** The start date/time. */ private long start; /** The end date/time. */ private long end; /** * Creates a new time allocation. * * @param start the start date/time in milliseconds. * @param end the end date/time in milliseconds. */ public SimpleTimePeriod(long start, long end) { if (start > end) { throw new IllegalArgumentException("Requires start <= end."); } this.start = start; this.end = end; } /** * Creates a new time allocation. * * @param start the start date/time ({@code null} not permitted). * @param end the end date/time ({@code null} not permitted). */ public SimpleTimePeriod(Date start, Date end) { this(start.getTime(), end.getTime()); } /** * Returns the start date/time. * * @return The start date/time (never {@code null}). */ @Override public Date getStart() { return new Date(this.start); } /** * Returns the start date/time in milliseconds. * * @return The start. */ public long getStartMillis() { return this.start; } /** * Returns the end date/time. * * @return The end date/time (never {@code null}). */ @Override public Date getEnd() { return new Date(this.end); } /** * Returns the end date/time in milliseconds. * * @return The end. */ public long getEndMillis() { return this.end; } /** * Tests this time period instance for equality with an arbitrary object. * The object is considered equal if it is an instance of {@link TimePeriod} * and it has the same start and end dates. * * @param obj the other object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof TimePeriod)) { return false; } TimePeriod that = (TimePeriod) obj; if (!this.getStart().equals(that.getStart())) { return false; } if (!this.getEnd().equals(that.getEnd())) { return false; } return true; } /** * Returns an integer that indicates the relative ordering of two * time periods. * * @param obj the object ({@code null} not permitted). * * @return An integer. * * @throws ClassCastException if {@code obj} is not an instance of * {@link TimePeriod}. */ @Override public int compareTo(Object obj) { TimePeriod that = (TimePeriod) obj; long t0 = getStart().getTime(); long t1 = getEnd().getTime(); long m0 = t0 + (t1 - t0) / 2L; long t2 = that.getStart().getTime(); long t3 = that.getEnd().getTime(); long m1 = t2 + (t3 - t2) / 2L; if (m0 < m1) { return -1; } else if (m0 > m1) { return 1; } else { if (t0 < t2) { return -1; } else if (t0 > t2) { return 1; } else { if (t1 < t3) { return -1; } else if (t1 > t3) { return 1; } else { return 0; } } } } /** * Returns a hash code for this object instance. The approach described by * Joshua Bloch in "Effective Java" has been used here - see: *

* {@code http://developer.java.sun.com/ * developer/Books/effectivejava/Chapter3.pdf} * * @return A hash code. */ @Override public int hashCode() { int result = 17; result = 37 * result + (int) this.start; result = 37 * result + (int) this.end; return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/TimePeriod.java000066400000000000000000000040311463604235500265260ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * TimePeriod.java * --------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import java.util.Date; /** * A period of time measured to millisecond precision using two instances of * {@code java.util.Date}. */ public interface TimePeriod extends Comparable { /** * Returns the start date/time. This will always be on or before the * end date. * * @return The start date/time (never {@code null}). */ Date getStart(); /** * Returns the end date/time. This will always be on or after the * start date. * * @return The end date/time (never {@code null}). */ Date getEnd(); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/TimePeriodAnchor.java000066400000000000000000000077601463604235500276750ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * TimePeriodAnchor.java * --------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import java.io.ObjectStreamException; import java.io.Serializable; /** * Used to indicate one of three positions in a time period: * {@code START}, {@code MIDDLE} and {@code END}. */ public final class TimePeriodAnchor implements Serializable { /** For serialization. */ private static final long serialVersionUID = 2011955697457548862L; /** Start of period. */ public static final TimePeriodAnchor START = new TimePeriodAnchor("TimePeriodAnchor.START"); /** Middle of period. */ public static final TimePeriodAnchor MIDDLE = new TimePeriodAnchor("TimePeriodAnchor.MIDDLE"); /** End of period. */ public static final TimePeriodAnchor END = new TimePeriodAnchor("TimePeriodAnchor.END"); /** The name. */ private String name; /** * Private constructor. * * @param name the name. */ private TimePeriodAnchor(String name) { this.name = name; } /** * Returns a string representing the object. * * @return The string. */ @Override public String toString() { return this.name; } /** * Returns {@code true} if this object is equal to the specified * object, and {@code false} otherwise. * * @param obj the other object. * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof TimePeriodAnchor)) { return false; } TimePeriodAnchor position = (TimePeriodAnchor) obj; if (!this.name.equals(position.name)) { return false; } return true; } /** * Returns a hash code value for the object. * * @return The hashcode */ @Override public int hashCode() { return this.name.hashCode(); } /** * Ensures that serialization returns the unique instances. * * @return The object. * * @throws ObjectStreamException if there is a problem. */ private Object readResolve() throws ObjectStreamException { if (this.equals(TimePeriodAnchor.START)) { return TimePeriodAnchor.START; } else if (this.equals(TimePeriodAnchor.MIDDLE)) { return TimePeriodAnchor.MIDDLE; } else if (this.equals(TimePeriodAnchor.END)) { return TimePeriodAnchor.END; } return null; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/TimePeriodFormatException.java000066400000000000000000000035501463604235500315630ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------ * TimePeriodFormatException.java * ------------------------------ * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; */ package org.jfree.data.time; /** * An exception that indicates an invalid format in a string representing a * time period. */ public class TimePeriodFormatException extends IllegalArgumentException { /** * Creates a new exception. * * @param message a message describing the exception. */ public TimePeriodFormatException(String message) { super(message); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/TimePeriodValue.java000066400000000000000000000122171463604235500275300ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * TimePeriodValue.java * -------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.util.Args; /** * Represents a time period and an associated value. */ public class TimePeriodValue implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = 3390443360845711275L; /** The time period. */ private TimePeriod period; /** The value associated with the time period. */ private Number value; /** * Constructs a new data item. * * @param period the time period ({@code null} not permitted). * @param value the value associated with the time period. * * @throws IllegalArgumentException if {@code period} is {@code null}. */ public TimePeriodValue(TimePeriod period, Number value) { Args.nullNotPermitted(period, "period"); this.period = period; this.value = value; } /** * Constructs a new data item. * * @param period the time period ({@code null} not permitted). * @param value the value associated with the time period. * * @throws IllegalArgumentException if {@code period} is {@code null}. */ public TimePeriodValue(TimePeriod period, double value) { this(period, Double.valueOf(value)); } /** * Returns the time period. * * @return The time period (never {@code null}). */ public TimePeriod getPeriod() { return this.period; } /** * Returns the value. * * @return The value (possibly {@code null}). * * @see #setValue(Number) */ public Number getValue() { return this.value; } /** * Sets the value for this data item. * * @param value the new value ({@code null} permitted). * * @see #getValue() */ public void setValue(Number value) { this.value = value; } /** * Tests this object for equality with the target object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof TimePeriodValue)) { return false; } TimePeriodValue timePeriodValue = (TimePeriodValue) obj; if (!Objects.equals(this.period, timePeriodValue.period)) { return false; } if (!Objects.equals(this.value, timePeriodValue.value)) { return false; } return true; } /** * Returns a hash code value for the object. * * @return The hashcode */ @Override public int hashCode() { int result; result = (this.period != null ? this.period.hashCode() : 0); result = 29 * result + (this.value != null ? this.value.hashCode() : 0); return result; } /** * Clones the object. *

* Note: no need to clone the period or value since they are immutable * classes. * * @return A clone. */ @Override public Object clone() { Object clone; try { clone = super.clone(); } catch (CloneNotSupportedException e) { // won't get here... throw new RuntimeException(e); } return clone; } /** * Returns a string representing this instance, primarily for use in * debugging. * * @return A string. */ @Override public String toString() { return "TimePeriodValue[" + getPeriod() + "," + getValue() + "]"; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/TimePeriodValues.java000066400000000000000000000425621463604235500277210ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * TimePeriodValues.java * --------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Objects; import org.jfree.chart.util.Args; import org.jfree.data.general.Series; import org.jfree.data.general.SeriesChangeEvent; import org.jfree.data.general.SeriesException; /** * A structure containing zero, one or many {@link TimePeriodValue} instances. * The time periods can overlap, and are maintained in the order that they are * added to the collection. *

* This is similar to the {@link TimeSeries} class, except that the time * periods can have irregular lengths. */ public class TimePeriodValues extends Series implements Serializable { /** For serialization. */ static final long serialVersionUID = -2210593619794989709L; /** Default value for the domain description. */ protected static final String DEFAULT_DOMAIN_DESCRIPTION = "Time"; /** Default value for the range description. */ protected static final String DEFAULT_RANGE_DESCRIPTION = "Value"; /** A description of the domain. */ private String domain; /** A description of the range. */ private String range; /** The list of data pairs in the series. */ private List data; /** Index of the time period with the minimum start milliseconds. */ private int minStartIndex = -1; /** Index of the time period with the maximum start milliseconds. */ private int maxStartIndex = -1; /** Index of the time period with the minimum middle milliseconds. */ private int minMiddleIndex = -1; /** Index of the time period with the maximum middle milliseconds. */ private int maxMiddleIndex = -1; /** Index of the time period with the minimum end milliseconds. */ private int minEndIndex = -1; /** Index of the time period with the maximum end milliseconds. */ private int maxEndIndex = -1; /** * Creates a new (empty) collection of time period values. * * @param name the name of the series ({@code null} not permitted). */ public TimePeriodValues(String name) { this(name, DEFAULT_DOMAIN_DESCRIPTION, DEFAULT_RANGE_DESCRIPTION); } /** * Creates a new time series that contains no data. *

* Descriptions can be specified for the domain and range. One situation * where this is helpful is when generating a chart for the time series - * axis labels can be taken from the domain and range description. * * @param name the name of the series ({@code null} not permitted). * @param domain the domain description. * @param range the range description. */ public TimePeriodValues(String name, String domain, String range) { super(name); this.domain = domain; this.range = range; this.data = new ArrayList(); } /** * Returns the domain description. * * @return The domain description (possibly {@code null}). * * @see #getRangeDescription() * @see #setDomainDescription(String) */ public String getDomainDescription() { return this.domain; } /** * Sets the domain description and fires a property change event (with the * property name {@code Domain} if the description changes). * * @param description the new description ({@code null} permitted). * * @see #getDomainDescription() */ public void setDomainDescription(String description) { String old = this.domain; this.domain = description; firePropertyChange("Domain", old, description); } /** * Returns the range description. * * @return The range description (possibly {@code null}). * * @see #getDomainDescription() * @see #setRangeDescription(String) */ public String getRangeDescription() { return this.range; } /** * Sets the range description and fires a property change event with the * name {@code Range}. * * @param description the new description ({@code null} permitted). * * @see #getRangeDescription() */ public void setRangeDescription(String description) { String old = this.range; this.range = description; firePropertyChange("Range", old, description); } /** * Returns the number of items in the series. * * @return The item count. */ @Override public int getItemCount() { return this.data.size(); } /** * Returns one data item for the series. * * @param index the item index (in the range {@code 0} to * {@code getItemCount() -1}). * * @return One data item for the series. */ public TimePeriodValue getDataItem(int index) { return (TimePeriodValue) this.data.get(index); } /** * Returns the time period at the specified index. * * @param index the item index (in the range {@code 0} to * {@code getItemCount() -1}). * * @return The time period at the specified index. * * @see #getDataItem(int) */ public TimePeriod getTimePeriod(int index) { return getDataItem(index).getPeriod(); } /** * Returns the value at the specified index. * * @param index the item index (in the range {@code 0} to * {@code getItemCount() -1}). * * @return The value at the specified index (possibly {@code null}). * * @see #getDataItem(int) */ public Number getValue(int index) { return getDataItem(index).getValue(); } /** * Adds a data item to the series and sends a {@link SeriesChangeEvent} to * all registered listeners. * * @param item the item ({@code null} not permitted). */ public void add(TimePeriodValue item) { Args.nullNotPermitted(item, "item"); this.data.add(item); updateBounds(item.getPeriod(), this.data.size() - 1); fireSeriesChanged(); } /** * Update the index values for the maximum and minimum bounds. * * @param period the time period. * @param index the index of the time period. */ private void updateBounds(TimePeriod period, int index) { long start = period.getStart().getTime(); long end = period.getEnd().getTime(); long middle = start + ((end - start) / 2); if (this.minStartIndex >= 0) { long minStart = getDataItem(this.minStartIndex).getPeriod() .getStart().getTime(); if (start < minStart) { this.minStartIndex = index; } } else { this.minStartIndex = index; } if (this.maxStartIndex >= 0) { long maxStart = getDataItem(this.maxStartIndex).getPeriod() .getStart().getTime(); if (start > maxStart) { this.maxStartIndex = index; } } else { this.maxStartIndex = index; } if (this.minMiddleIndex >= 0) { long s = getDataItem(this.minMiddleIndex).getPeriod().getStart() .getTime(); long e = getDataItem(this.minMiddleIndex).getPeriod().getEnd() .getTime(); long minMiddle = s + (e - s) / 2; if (middle < minMiddle) { this.minMiddleIndex = index; } } else { this.minMiddleIndex = index; } if (this.maxMiddleIndex >= 0) { long s = getDataItem(this.maxMiddleIndex).getPeriod().getStart() .getTime(); long e = getDataItem(this.maxMiddleIndex).getPeriod().getEnd() .getTime(); long maxMiddle = s + (e - s) / 2; if (middle > maxMiddle) { this.maxMiddleIndex = index; } } else { this.maxMiddleIndex = index; } if (this.minEndIndex >= 0) { long minEnd = getDataItem(this.minEndIndex).getPeriod().getEnd() .getTime(); if (end < minEnd) { this.minEndIndex = index; } } else { this.minEndIndex = index; } if (this.maxEndIndex >= 0) { long maxEnd = getDataItem(this.maxEndIndex).getPeriod().getEnd() .getTime(); if (end > maxEnd) { this.maxEndIndex = index; } } else { this.maxEndIndex = index; } } /** * Recalculates the bounds for the collection of items. */ private void recalculateBounds() { this.minStartIndex = -1; this.minMiddleIndex = -1; this.minEndIndex = -1; this.maxStartIndex = -1; this.maxMiddleIndex = -1; this.maxEndIndex = -1; for (int i = 0; i < this.data.size(); i++) { TimePeriodValue tpv = (TimePeriodValue) this.data.get(i); updateBounds(tpv.getPeriod(), i); } } /** * Adds a new data item to the series and sends a {@link SeriesChangeEvent} * to all registered listeners. * * @param period the time period ({@code null} not permitted). * @param value the value. * * @see #add(TimePeriod, Number) */ public void add(TimePeriod period, double value) { TimePeriodValue item = new TimePeriodValue(period, value); add(item); } /** * Adds a new data item to the series and sends a {@link SeriesChangeEvent} * to all registered listeners. * * @param period the time period ({@code null} not permitted). * @param value the value ({@code null} permitted). */ public void add(TimePeriod period, Number value) { TimePeriodValue item = new TimePeriodValue(period, value); add(item); } /** * Updates (changes) the value of a data item and sends a * {@link SeriesChangeEvent} to all registered listeners. * * @param index the index of the data item to update. * @param value the new value ({@code null} not permitted). */ public void update(int index, Number value) { TimePeriodValue item = getDataItem(index); item.setValue(value); fireSeriesChanged(); } /** * Deletes data from start until end index (end inclusive) and sends a * {@link SeriesChangeEvent} to all registered listeners. * * @param start the index of the first period to delete. * @param end the index of the last period to delete. */ public void delete(int start, int end) { for (int i = 0; i <= (end - start); i++) { this.data.remove(start); } recalculateBounds(); fireSeriesChanged(); } /** * Tests the series for equality with another object. * * @param obj the object ({@code null} permitted). * * @return {@code true} or {@code false}. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof TimePeriodValues)) { return false; } if (!super.equals(obj)) { return false; } TimePeriodValues that = (TimePeriodValues) obj; if (!Objects.equals(this.getDomainDescription(), that.getDomainDescription())) { return false; } if (!Objects.equals(this.getRangeDescription(), that.getRangeDescription())) { return false; } int count = getItemCount(); if (count != that.getItemCount()) { return false; } for (int i = 0; i < count; i++) { if (!getDataItem(i).equals(that.getDataItem(i))) { return false; } } return true; } /** * Returns a hash code value for the object. * * @return The hashcode */ @Override public int hashCode() { int result; result = (this.domain != null ? this.domain.hashCode() : 0); result = 29 * result + (this.range != null ? this.range.hashCode() : 0); result = 29 * result + this.data.hashCode(); result = 29 * result + this.minStartIndex; result = 29 * result + this.maxStartIndex; result = 29 * result + this.minMiddleIndex; result = 29 * result + this.maxMiddleIndex; result = 29 * result + this.minEndIndex; result = 29 * result + this.maxEndIndex; return result; } /** * Returns a clone of the collection. *

* Notes: *

    *
  • no need to clone the domain and range descriptions, since String * object is immutable;
  • *
  • we pass over to the more general method createCopy(start, end). *
  • *
* * @return A clone of the time series. * * @throws CloneNotSupportedException if there is a cloning problem. */ @Override public Object clone() throws CloneNotSupportedException { Object clone = createCopy(0, getItemCount() - 1); return clone; } /** * Creates a new instance by copying a subset of the data in this * collection. * * @param start the index of the first item to copy. * @param end the index of the last item to copy. * * @return A copy of a subset of the items. * * @throws CloneNotSupportedException if there is a cloning problem. */ public TimePeriodValues createCopy(int start, int end) throws CloneNotSupportedException { TimePeriodValues copy = (TimePeriodValues) super.clone(); copy.data = new ArrayList(); if (this.data.size() > 0) { for (int index = start; index <= end; index++) { TimePeriodValue item = (TimePeriodValue) this.data.get(index); TimePeriodValue clone = (TimePeriodValue) item.clone(); try { copy.add(clone); } catch (SeriesException e) { System.err.println("Failed to add cloned item."); } } } return copy; } /** * Returns the index of the time period with the minimum start milliseconds. * * @return The index. */ public int getMinStartIndex() { return this.minStartIndex; } /** * Returns the index of the time period with the maximum start milliseconds. * * @return The index. */ public int getMaxStartIndex() { return this.maxStartIndex; } /** * Returns the index of the time period with the minimum middle * milliseconds. * * @return The index. */ public int getMinMiddleIndex() { return this.minMiddleIndex; } /** * Returns the index of the time period with the maximum middle * milliseconds. * * @return The index. */ public int getMaxMiddleIndex() { return this.maxMiddleIndex; } /** * Returns the index of the time period with the minimum end milliseconds. * * @return The index. */ public int getMinEndIndex() { return this.minEndIndex; } /** * Returns the index of the time period with the maximum end milliseconds. * * @return The index. */ public int getMaxEndIndex() { return this.maxEndIndex; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/TimePeriodValuesCollection.java000066400000000000000000000341071463604235500317310ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------- * TimePeriodValuesCollection.java * ------------------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import java.io.Serializable; import java.util.Iterator; import java.util.List; import java.util.Objects; import org.jfree.chart.util.Args; import org.jfree.data.DomainInfo; import org.jfree.data.Range; import org.jfree.data.xy.AbstractIntervalXYDataset; import org.jfree.data.xy.IntervalXYDataset; /** * A collection of {@link TimePeriodValues} objects. *

* This class implements the {@link org.jfree.data.xy.XYDataset} interface, as * well as the extended {@link IntervalXYDataset} interface. This makes it a * convenient dataset for use with the {@link org.jfree.chart.plot.XYPlot} * class. */ public class TimePeriodValuesCollection extends AbstractIntervalXYDataset implements IntervalXYDataset, DomainInfo, Serializable { /** For serialization. */ private static final long serialVersionUID = -3077934065236454199L; /** Storage for the time series. */ private List data; /** * The position within a time period to return as the x-value (START, * MIDDLE or END). */ private TimePeriodAnchor xPosition; /** * Constructs an empty dataset. */ public TimePeriodValuesCollection() { this((TimePeriodValues) null); } /** * Constructs a dataset containing a single series. Additional series can * be added. * * @param series the series ({@code null} ignored). */ public TimePeriodValuesCollection(TimePeriodValues series) { this.data = new java.util.ArrayList(); this.xPosition = TimePeriodAnchor.MIDDLE; if (series != null) { this.data.add(series); series.addChangeListener(this); } } /** * Returns the position of the X value within each time period. * * @return The position (never {@code null}). * * @see #setXPosition(TimePeriodAnchor) */ public TimePeriodAnchor getXPosition() { return this.xPosition; } /** * Sets the position of the x axis within each time period. * * @param position the position ({@code null} not permitted). * * @see #getXPosition() */ public void setXPosition(TimePeriodAnchor position) { Args.nullNotPermitted(position, "position"); this.xPosition = position; } /** * Returns the number of series in the collection. * * @return The series count. */ @Override public int getSeriesCount() { return this.data.size(); } /** * Returns a series. * * @param series the index of the series (zero-based). * * @return The series. */ public TimePeriodValues getSeries(int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Index 'series' out of range."); } return (TimePeriodValues) this.data.get(series); } /** * Returns the key for a series. * * @param series the index of the series (zero-based). * * @return The key for a series. */ @Override public Comparable getSeriesKey(int series) { // defer argument checking return getSeries(series).getKey(); } /** * Adds a series to the collection. A * {@link org.jfree.data.general.DatasetChangeEvent} is sent to all * registered listeners. * * @param series the time series. */ public void addSeries(TimePeriodValues series) { Args.nullNotPermitted(series, "series"); this.data.add(series); series.addChangeListener(this); fireDatasetChanged(); } /** * Removes the specified series from the collection. * * @param series the series to remove ({@code null} not permitted). */ public void removeSeries(TimePeriodValues series) { Args.nullNotPermitted(series, "series"); this.data.remove(series); series.removeChangeListener(this); fireDatasetChanged(); } /** * Removes a series from the collection. * * @param index the series index (zero-based). */ public void removeSeries(int index) { TimePeriodValues series = getSeries(index); if (series != null) { removeSeries(series); } } /** * Returns the number of items in the specified series. *

* This method is provided for convenience. * * @param series the index of the series of interest (zero-based). * * @return The number of items in the specified series. */ @Override public int getItemCount(int series) { return getSeries(series).getItemCount(); } /** * Returns the x-value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The x-value for the specified series and item. */ @Override public Number getX(int series, int item) { TimePeriodValues ts = (TimePeriodValues) this.data.get(series); TimePeriodValue dp = ts.getDataItem(item); TimePeriod period = dp.getPeriod(); return getX(period); } /** * Returns the x-value for a time period. * * @param period the time period. * * @return The x-value. */ private long getX(TimePeriod period) { if (this.xPosition == TimePeriodAnchor.START) { return period.getStart().getTime(); } else if (this.xPosition == TimePeriodAnchor.MIDDLE) { return period.getStart().getTime() / 2 + period.getEnd().getTime() / 2; } else if (this.xPosition == TimePeriodAnchor.END) { return period.getEnd().getTime(); } else { throw new IllegalStateException("TimePeriodAnchor unknown."); } } /** * Returns the starting X value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The starting X value for the specified series and item. */ @Override public Number getStartX(int series, int item) { TimePeriodValues ts = (TimePeriodValues) this.data.get(series); TimePeriodValue dp = ts.getDataItem(item); return dp.getPeriod().getStart().getTime(); } /** * Returns the ending X value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The ending X value for the specified series and item. */ @Override public Number getEndX(int series, int item) { TimePeriodValues ts = (TimePeriodValues) this.data.get(series); TimePeriodValue dp = ts.getDataItem(item); return dp.getPeriod().getEnd().getTime(); } /** * Returns the y-value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The y-value for the specified series and item. */ @Override public Number getY(int series, int item) { TimePeriodValues ts = (TimePeriodValues) this.data.get(series); TimePeriodValue dp = ts.getDataItem(item); return dp.getValue(); } /** * Returns the starting Y value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The starting Y value for the specified series and item. */ @Override public Number getStartY(int series, int item) { return getY(series, item); } /** * Returns the ending Y value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The ending Y value for the specified series and item. */ @Override public Number getEndY(int series, int item) { return getY(series, item); } /** * Returns the minimum x-value in the dataset. * * @param includeInterval a flag that determines whether or not the * x-interval is taken into account. * * @return The minimum value. */ @Override public double getDomainLowerBound(boolean includeInterval) { double result = Double.NaN; Range r = getDomainBounds(includeInterval); if (r != null) { result = r.getLowerBound(); } return result; } /** * Returns the maximum x-value in the dataset. * * @param includeInterval a flag that determines whether or not the * x-interval is taken into account. * * @return The maximum value. */ @Override public double getDomainUpperBound(boolean includeInterval) { double result = Double.NaN; Range r = getDomainBounds(includeInterval); if (r != null) { result = r.getUpperBound(); } return result; } /** * Returns the range of the values in this dataset's domain. * * @param includeInterval a flag that determines whether or not the * x-interval is taken into account. * * @return The range. */ @Override public Range getDomainBounds(boolean includeInterval) { boolean interval = includeInterval; Range result = null; Range temp = null; Iterator iterator = this.data.iterator(); while (iterator.hasNext()) { TimePeriodValues series = (TimePeriodValues) iterator.next(); int count = series.getItemCount(); if (count > 0) { TimePeriod start = series.getTimePeriod( series.getMinStartIndex()); TimePeriod end = series.getTimePeriod(series.getMaxEndIndex()); if (!interval) { if (this.xPosition == TimePeriodAnchor.START) { TimePeriod maxStart = series.getTimePeriod( series.getMaxStartIndex()); temp = new Range(start.getStart().getTime(), maxStart.getStart().getTime()); } else if (this.xPosition == TimePeriodAnchor.MIDDLE) { TimePeriod minMiddle = series.getTimePeriod( series.getMinMiddleIndex()); long s1 = minMiddle.getStart().getTime(); long e1 = minMiddle.getEnd().getTime(); TimePeriod maxMiddle = series.getTimePeriod( series.getMaxMiddleIndex()); long s2 = maxMiddle.getStart().getTime(); long e2 = maxMiddle.getEnd().getTime(); temp = new Range(s1 + (e1 - s1) / 2.0, s2 + (e2 - s2) / 2.0); } else if (this.xPosition == TimePeriodAnchor.END) { TimePeriod minEnd = series.getTimePeriod( series.getMinEndIndex()); temp = new Range(minEnd.getEnd().getTime(), end.getEnd().getTime()); } } else { temp = new Range(start.getStart().getTime(), end.getEnd().getTime()); } result = Range.combine(result, temp); } } return result; } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof TimePeriodValuesCollection)) { return false; } TimePeriodValuesCollection that = (TimePeriodValuesCollection) obj; if (this.xPosition != that.xPosition) { return false; } if (!Objects.equals(this.data, that.data)) { return false; } return true; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/TimeSeries.java000066400000000000000000001305231463604235500265440ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * TimeSeries.java * --------------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Bryan Scott; * Nick Guenther; * */ package org.jfree.data.time; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Objects; import java.util.TimeZone; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.Args; import org.jfree.data.Range; import org.jfree.data.general.Series; import org.jfree.data.general.SeriesChangeEvent; import org.jfree.data.general.SeriesException; /** * Represents a sequence of zero or more data items in the form (period, value) * where 'period' is some instance of a subclass of {@link RegularTimePeriod}. * The time series will ensure that (a) all data items have the same type of * period (for example, {@link Day}) and (b) that each period appears at * most one time in the series. */ public class TimeSeries extends Series implements Cloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -5032960206869675528L; /** Default value for the domain description. */ protected static final String DEFAULT_DOMAIN_DESCRIPTION = "Time"; /** Default value for the range description. */ protected static final String DEFAULT_RANGE_DESCRIPTION = "Value"; /** A description of the domain. */ private String domain; /** A description of the range. */ private String range; /** The type of period for the data. */ protected Class timePeriodClass; /** The list of data items in the series. */ protected List data; /** The maximum number of items for the series. */ private int maximumItemCount; /** * The maximum age of items for the series, specified as a number of * time periods. */ private long maximumItemAge; /** * The minimum y-value in the series. */ private double minY; /** * The maximum y-value in the series. */ private double maxY; /** * Creates a new (empty) time series. By default, a daily time series is * created. Use one of the other constructors if you require a different * time period. * * @param name the series name ({@code null} not permitted). */ public TimeSeries(Comparable name) { this(name, DEFAULT_DOMAIN_DESCRIPTION, DEFAULT_RANGE_DESCRIPTION); } /** * Creates a new time series that contains no data. *

* Descriptions can be specified for the domain and range. One situation * where this is helpful is when generating a chart for the time series - * axis labels can be taken from the domain and range description. * * @param name the name of the series ({@code null} not permitted). * @param domain the domain description ({@code null} permitted). * @param range the range description ({@code null} permitted). */ public TimeSeries(Comparable name, String domain, String range) { super(name); this.domain = domain; this.range = range; this.timePeriodClass = null; this.data = new java.util.ArrayList(); this.maximumItemCount = Integer.MAX_VALUE; this.maximumItemAge = Long.MAX_VALUE; this.minY = Double.NaN; this.maxY = Double.NaN; } /** * Returns the domain description. * * @return The domain description (possibly {@code null}). * * @see #setDomainDescription(String) */ public String getDomainDescription() { return this.domain; } /** * Sets the domain description and sends a {@code PropertyChangeEvent} * (with the property name {@code Domain}) to all registered * property change listeners. * * @param description the description ({@code null} permitted). * * @see #getDomainDescription() */ public void setDomainDescription(String description) { String old = this.domain; this.domain = description; firePropertyChange("Domain", old, description); } /** * Returns the range description. * * @return The range description (possibly {@code null}). * * @see #setRangeDescription(String) */ public String getRangeDescription() { return this.range; } /** * Sets the range description and sends a {@code PropertyChangeEvent} * (with the property name {@code Range}) to all registered listeners. * * @param description the description ({@code null} permitted). * * @see #getRangeDescription() */ public void setRangeDescription(String description) { String old = this.range; this.range = description; firePropertyChange("Range", old, description); } /** * Returns the number of items in the series. * * @return The item count. */ @Override public int getItemCount() { return this.data.size(); } /** * Returns the list of data items for the series (the list contains * {@link TimeSeriesDataItem} objects and is unmodifiable). * * @return The list of data items. */ public List getItems() { // FIXME: perhaps we should clone the data list return Collections.unmodifiableList(this.data); } /** * Returns the maximum number of items that will be retained in the series. * The default value is {@code Integer.MAX_VALUE}. * * @return The maximum item count. * * @see #setMaximumItemCount(int) */ public int getMaximumItemCount() { return this.maximumItemCount; } /** * Sets the maximum number of items that will be retained in the series. * If you add a new item to the series such that the number of items will * exceed the maximum item count, then the FIRST element in the series is * automatically removed, ensuring that the maximum item count is not * exceeded. * * @param maximum the maximum (requires >= 0). * * @see #getMaximumItemCount() */ public void setMaximumItemCount(int maximum) { if (maximum < 0) { throw new IllegalArgumentException("Negative 'maximum' argument."); } this.maximumItemCount = maximum; int count = this.data.size(); if (count > maximum) { delete(0, count - maximum - 1); } } /** * Returns the maximum item age (in time periods) for the series. * * @return The maximum item age. * * @see #setMaximumItemAge(long) */ public long getMaximumItemAge() { return this.maximumItemAge; } /** * Sets the number of time units in the 'history' for the series. This * provides one mechanism for automatically dropping old data from the * time series. For example, if a series contains daily data, you might set * the history count to 30. Then, when you add a new data item, all data * items more than 30 days older than the latest value are automatically * dropped from the series. * * @param periods the number of time periods. * * @see #getMaximumItemAge() */ public void setMaximumItemAge(long periods) { if (periods < 0) { throw new IllegalArgumentException("Negative 'periods' argument."); } this.maximumItemAge = periods; removeAgedItems(true); // remove old items and notify if necessary } /** * Returns the range of y-values in the time series. Any {@code null} or * {@code Double.NaN} data values in the series will be ignored (except for * the special case where all data values are {@code null}, in which case * the return value is {@code Range(Double.NaN, Double.NaN)}). If the time * series contains no items, this method will return {@code null}. * * @return The range of y-values in the time series (possibly {@code null}). */ public Range findValueRange() { if (this.data.isEmpty()) { return null; } return new Range(this.minY, this.maxY); } /** * Returns the range of y-values in the time series that fall within * the specified range of x-values. This is equivalent to * {@code findValueRange(xRange, TimePeriodAnchor.MIDDLE, timeZone)}. * * @param xRange the subrange of x-values ({@code null} not permitted). * @param timeZone the time zone used to convert x-values to time periods * ({@code null} not permitted). * * @return The range. */ public Range findValueRange(Range xRange, TimeZone timeZone) { return findValueRange(xRange, TimePeriodAnchor.MIDDLE, timeZone); } /** * Finds the range of y-values that fall within the specified range of * x-values (where the x-values are interpreted as milliseconds since the * epoch and converted to time periods using the specified timezone). * * @param xRange the subset of x-values to use ({@code null} not * permitted). * @param xAnchor the anchor point for the x-values ({@code null} * not permitted). * @param zone the time zone ({@code null} not permitted). * * @return The range of y-values. */ public Range findValueRange(Range xRange, TimePeriodAnchor xAnchor, TimeZone zone) { Args.nullNotPermitted(xRange, "xRange"); Args.nullNotPermitted(xAnchor, "xAnchor"); Args.nullNotPermitted(zone, "zone"); if (this.data.isEmpty()) { return null; } Calendar calendar = Calendar.getInstance(zone); return findValueRange(xRange, xAnchor, calendar); } /** * Finds the range of y-values that fall within the specified range of * x-values (where the x-values are interpreted as milliseconds since the * epoch and converted to time periods using the specified calendar). * * @param xRange the subset of x-values to use ({@code null} not * permitted). * @param xAnchor the anchor point for the x-values ({@code null} * not permitted). * @param calendar the calendar ({@code null} not permitted). * * @return The range of y-values. */ public Range findValueRange(Range xRange, TimePeriodAnchor xAnchor, Calendar calendar) { // since the items are ordered, we could be more clever here and avoid // iterating over all the data double lowY = Double.POSITIVE_INFINITY; double highY = Double.NEGATIVE_INFINITY; for (int i = 0; i < this.data.size(); i++) { TimeSeriesDataItem item = (TimeSeriesDataItem) this.data.get(i); long millis = item.getPeriod().getMillisecond(xAnchor, calendar); if (xRange.contains(millis)) { Number n = item.getValue(); if (n != null) { double v = n.doubleValue(); lowY = minIgnoreNaN(lowY, v); highY = maxIgnoreNaN(highY, v); } } } if (Double.isInfinite(lowY) && Double.isInfinite(highY)) { if (lowY < highY) { return new Range(lowY, highY); } else { return new Range(Double.NaN, Double.NaN); } } return new Range(lowY, highY); } /** * Returns the smallest y-value in the series, ignoring any * {@code null} and {@code Double.NaN} values. This method * returns {@code Double.NaN} if there is no smallest y-value (for * example, when the series is empty). * * @return The smallest y-value. * * @see #getMaxY() */ public double getMinY() { return this.minY; } /** * Returns the largest y-value in the series, ignoring any * {@code null} and {@code Double.NaN} values. This method * returns {@code Double.NaN} if there is no largest y-value * (for example, when the series is empty). * * @return The largest y-value. * * @see #getMinY() */ public double getMaxY() { return this.maxY; } /** * Returns the time period class for this series. *

* Only one time period class can be used within a single series (enforced). * If you add a data item with a {@link Year} for the time period, then all * subsequent data items must also have a {@link Year} for the time period. * * @return The time period class (may be {@code null} but only for * an empty series). */ public Class getTimePeriodClass() { return this.timePeriodClass; } /** * Returns a data item from the dataset. Note that the returned object * is a clone of the item in the series, so modifying it will have no * effect on the data series. * * @param index the item index. * * @return The data item. */ public TimeSeriesDataItem getDataItem(int index) { TimeSeriesDataItem item = (TimeSeriesDataItem) this.data.get(index); return (TimeSeriesDataItem) item.clone(); } /** * Returns the data item for a specific period. Note that the returned * object is a clone of the item in the series, so modifying it will have * no effect on the data series. * * @param period the period of interest ({@code null} not allowed). * * @return The data item matching the specified period (or * {@code null} if there is no match). * * @see #getDataItem(int) */ public TimeSeriesDataItem getDataItem(RegularTimePeriod period) { int index = getIndex(period); if (index >= 0) { return getDataItem(index); } return null; } /** * Returns a data item for the series. This method returns the object * that is used for the underlying storage - you should not modify the * contents of the returned value unless you know what you are doing. * * @param index the item index (zero-based). * * @return The data item. * * @see #getDataItem(int) */ TimeSeriesDataItem getRawDataItem(int index) { return (TimeSeriesDataItem) this.data.get(index); } /** * Returns a data item for the series. This method returns the object * that is used for the underlying storage - you should not modify the * contents of the returned value unless you know what you are doing. * * @param period the item index (zero-based). * * @return The data item. * * @see #getDataItem(RegularTimePeriod) */ TimeSeriesDataItem getRawDataItem(RegularTimePeriod period) { int index = getIndex(period); if (index >= 0) { return (TimeSeriesDataItem) this.data.get(index); } return null; } /** * Returns the time period at the specified index. * * @param index the index of the data item. * * @return The time period. */ public RegularTimePeriod getTimePeriod(int index) { return getRawDataItem(index).getPeriod(); } /** * Returns a time period that would be the next in sequence on the end of * the time series. * * @return The next time period. */ public RegularTimePeriod getNextTimePeriod() { RegularTimePeriod last = getTimePeriod(getItemCount() - 1); return last.next(); } /** * Returns a collection of all the time periods in the time series. * * @return A collection of all the time periods. */ public Collection getTimePeriods() { Collection result = new java.util.ArrayList(); for (int i = 0; i < getItemCount(); i++) { result.add(getTimePeriod(i)); } return result; } /** * Returns a collection of time periods in the specified series, but not in * this series, and therefore unique to the specified series. * * @param series the series to check against this one. * * @return The unique time periods. */ public Collection getTimePeriodsUniqueToOtherSeries(TimeSeries series) { Collection result = new java.util.ArrayList(); for (int i = 0; i < series.getItemCount(); i++) { RegularTimePeriod period = series.getTimePeriod(i); int index = getIndex(period); if (index < 0) { result.add(period); } } return result; } /** * Returns the index for the item (if any) that corresponds to a time * period. * * @param period the time period ({@code null} not permitted). * * @return The index. */ public int getIndex(RegularTimePeriod period) { Args.nullNotPermitted(period, "period"); TimeSeriesDataItem dummy = new TimeSeriesDataItem( period, Integer.MIN_VALUE); return Collections.binarySearch(this.data, dummy); } /** * Returns the value at the specified index. * * @param index index of a value. * * @return The value (possibly {@code null}). */ public Number getValue(int index) { return getRawDataItem(index).getValue(); } /** * Returns the value for a time period. If there is no data item with the * specified period, this method will return {@code null}. * * @param period time period ({@code null} not permitted). * * @return The value (possibly {@code null}). */ public Number getValue(RegularTimePeriod period) { int index = getIndex(period); if (index >= 0) { return getValue(index); } return null; } /** * Adds a data item to the series and sends a {@link SeriesChangeEvent} to * all registered listeners. * * @param item the (timeperiod, value) pair ({@code null} not permitted). */ public void add(TimeSeriesDataItem item) { add(item, true); } /** * Adds a data item to the series and sends a {@link SeriesChangeEvent} to * all registered listeners. * * @param item the (timeperiod, value) pair ({@code null} not permitted). * @param notify notify listeners? */ public void add(TimeSeriesDataItem item, boolean notify) { Args.nullNotPermitted(item, "item"); item = (TimeSeriesDataItem) item.clone(); Class c = item.getPeriod().getClass(); if (this.timePeriodClass == null) { this.timePeriodClass = c; } else if (!this.timePeriodClass.equals(c)) { StringBuilder b = new StringBuilder(); b.append("You are trying to add data where the time period class "); b.append("is "); b.append(item.getPeriod().getClass().getName()); b.append(", but the TimeSeries is expecting an instance of "); b.append(this.timePeriodClass.getName()); b.append("."); throw new SeriesException(b.toString()); } // make the change (if it's not a duplicate time period)... boolean added = false; int count = getItemCount(); if (count == 0) { this.data.add(item); added = true; } else { RegularTimePeriod last = getTimePeriod(getItemCount() - 1); if (item.getPeriod().compareTo(last) > 0) { this.data.add(item); added = true; } else { int index = Collections.binarySearch(this.data, item); if (index < 0) { this.data.add(-index - 1, item); added = true; } else { StringBuilder b = new StringBuilder(); b.append("You are attempting to add an observation for "); b.append("the time period "); b.append(item.getPeriod().toString()); b.append(" but the series already contains an observation"); b.append(" for that time period. Duplicates are not "); b.append("permitted. Try using the addOrUpdate() method."); throw new SeriesException(b.toString()); } } } if (added) { updateBoundsForAddedItem(item); // check if this addition will exceed the maximum item count... if (getItemCount() > this.maximumItemCount) { TimeSeriesDataItem d = (TimeSeriesDataItem) this.data.remove(0); updateBoundsForRemovedItem(d); } removeAgedItems(false); // remove old items if necessary, but // don't notify anyone, because that // happens next anyway... if (notify) { fireSeriesChanged(); } } } /** * Adds a new data item to the series and sends a {@link SeriesChangeEvent} * to all registered listeners. * * @param period the time period ({@code null} not permitted). * @param value the value. */ public void add(RegularTimePeriod period, double value) { // defer argument checking... add(period, value, true); } /** * Adds a new data item to the series and sends a {@link SeriesChangeEvent} * to all registered listeners. * * @param period the time period ({@code null} not permitted). * @param value the value. * @param notify notify listeners? */ public void add(RegularTimePeriod period, double value, boolean notify) { // defer argument checking... TimeSeriesDataItem item = new TimeSeriesDataItem(period, value); add(item, notify); } /** * Adds a new data item to the series and sends * a {@link org.jfree.data.general.SeriesChangeEvent} to all registered * listeners. * * @param period the time period ({@code null} not permitted). * @param value the value ({@code null} permitted). */ public void add(RegularTimePeriod period, Number value) { // defer argument checking... add(period, value, true); } /** * Adds a new data item to the series and sends a {@link SeriesChangeEvent} * to all registered listeners. * * @param period the time period ({@code null} not permitted). * @param value the value ({@code null} permitted). * @param notify notify listeners? */ public void add(RegularTimePeriod period, Number value, boolean notify) { // defer argument checking... TimeSeriesDataItem item = new TimeSeriesDataItem(period, value); add(item, notify); } /** * Updates (changes) the value for a time period. Throws a * {@link SeriesException} if the period does not exist. * * @param period the period ({@code null} not permitted). * @param value the value. */ public void update(RegularTimePeriod period, double value) { update(period, Double.valueOf(value)); } /** * Updates (changes) the value for a time period. Throws a * {@link SeriesException} if the period does not exist. * * @param period the period ({@code null} not permitted). * @param value the value ({@code null} permitted). */ public void update(RegularTimePeriod period, Number value) { TimeSeriesDataItem temp = new TimeSeriesDataItem(period, value); int index = Collections.binarySearch(this.data, temp); if (index < 0) { throw new SeriesException("There is no existing value for the " + "specified 'period'."); } update(index, value); } /** * Updates (changes) the value of a data item. * * @param index the index of the data item. * @param value the new value ({@code null} permitted). */ public void update(int index, Number value) { TimeSeriesDataItem item = (TimeSeriesDataItem) this.data.get(index); boolean iterate = false; Number oldYN = item.getValue(); if (oldYN != null) { double oldY = oldYN.doubleValue(); if (!Double.isNaN(oldY)) { iterate = oldY <= this.minY || oldY >= this.maxY; } } item.setValue(value); if (iterate) { updateMinMaxYByIteration(); } else if (value != null) { double yy = value.doubleValue(); this.minY = minIgnoreNaN(this.minY, yy); this.maxY = maxIgnoreNaN(this.maxY, yy); } fireSeriesChanged(); } /** * Adds or updates data from one series to another. Returns another series * containing the values that were overwritten. * * @param series the series to merge with this. * * @return A series containing the values that were overwritten. */ public TimeSeries addAndOrUpdate(TimeSeries series) { TimeSeries overwritten = new TimeSeries("Overwritten values from: " + getKey()); for (int i = 0; i < series.getItemCount(); i++) { TimeSeriesDataItem item = series.getRawDataItem(i); TimeSeriesDataItem oldItem = addOrUpdate(item.getPeriod(), item.getValue()); if (oldItem != null) { overwritten.add(oldItem); } } return overwritten; } /** * Adds or updates an item in the times series and sends a * {@link SeriesChangeEvent} to all registered listeners. * * @param period the time period to add/update ({@code null} not * permitted). * @param value the new value. * * @return A copy of the overwritten data item, or {@code null} if no * item was overwritten. */ public TimeSeriesDataItem addOrUpdate(RegularTimePeriod period, double value) { return addOrUpdate(period, Double.valueOf(value)); } /** * Adds or updates an item in the times series and sends a * {@link SeriesChangeEvent} to all registered listeners. * * @param period the time period to add/update ({@code null} not * permitted). * @param value the new value ({@code null} permitted). * * @return A copy of the overwritten data item, or {@code null} if no * item was overwritten. */ public TimeSeriesDataItem addOrUpdate(RegularTimePeriod period, Number value) { return addOrUpdate(new TimeSeriesDataItem(period, value)); } /** * Adds or updates an item in the times series and sends a * {@link SeriesChangeEvent} to all registered listeners. * * @param item the data item ({@code null} not permitted). * * @return A copy of the overwritten data item, or {@code null} if no * item was overwritten. */ public TimeSeriesDataItem addOrUpdate(TimeSeriesDataItem item) { Args.nullNotPermitted(item, "item"); Class periodClass = item.getPeriod().getClass(); if (this.timePeriodClass == null) { this.timePeriodClass = periodClass; } else if (!this.timePeriodClass.equals(periodClass)) { String msg = "You are trying to add data where the time " + "period class is " + periodClass.getName() + ", but the TimeSeries is expecting an instance of " + this.timePeriodClass.getName() + "."; throw new SeriesException(msg); } TimeSeriesDataItem overwritten = null; int index = Collections.binarySearch(this.data, item); if (index >= 0) { TimeSeriesDataItem existing = (TimeSeriesDataItem) this.data.get(index); overwritten = (TimeSeriesDataItem) existing.clone(); // figure out if we need to iterate through all the y-values // to find the revised minY / maxY boolean iterate = false; Number oldYN = existing.getValue(); double oldY = oldYN != null ? oldYN.doubleValue() : Double.NaN; if (!Double.isNaN(oldY)) { iterate = oldY <= this.minY || oldY >= this.maxY; } existing.setValue(item.getValue()); if (iterate) { updateMinMaxYByIteration(); } else if (item.getValue() != null) { double yy = item.getValue().doubleValue(); this.minY = minIgnoreNaN(this.minY, yy); this.maxY = maxIgnoreNaN(this.maxY, yy); } } else { item = (TimeSeriesDataItem) item.clone(); this.data.add(-index - 1, item); updateBoundsForAddedItem(item); // check if this addition will exceed the maximum item count... if (getItemCount() > this.maximumItemCount) { TimeSeriesDataItem d = (TimeSeriesDataItem) this.data.remove(0); updateBoundsForRemovedItem(d); } } removeAgedItems(false); // remove old items if necessary, but // don't notify anyone, because that // happens next anyway... fireSeriesChanged(); return overwritten; } /** * Age items in the series. Ensure that the timespan from the youngest to * the oldest record in the series does not exceed maximumItemAge time * periods. Oldest items will be removed if required. * * @param notify controls whether or not a {@link SeriesChangeEvent} is * sent to registered listeners IF any items are removed. */ public void removeAgedItems(boolean notify) { // check if there are any values earlier than specified by the history // count... if (getItemCount() > 1) { long latest = getTimePeriod(getItemCount() - 1).getSerialIndex(); boolean removed = false; while ((latest - getTimePeriod(0).getSerialIndex()) > this.maximumItemAge) { this.data.remove(0); removed = true; } if (removed) { updateMinMaxYByIteration(); if (notify) { fireSeriesChanged(); } } } } /** * Age items in the series. Ensure that the timespan from the supplied * time to the oldest record in the series does not exceed history count. * oldest items will be removed if required. * * @param latest the time to be compared against when aging data * (specified in milliseconds). * @param notify controls whether or not a {@link SeriesChangeEvent} is * sent to registered listeners IF any items are removed. */ public void removeAgedItems(long latest, boolean notify) { if (this.data.isEmpty()) { return; // nothing to do } // find the serial index of the period specified by 'latest' long index = Long.MAX_VALUE; try { Method m = RegularTimePeriod.class.getDeclaredMethod( "createInstance", new Class[] {Class.class, Date.class, TimeZone.class, Locale.class}); RegularTimePeriod newest = (RegularTimePeriod) m.invoke( this.timePeriodClass, new Object[] {this.timePeriodClass, new Date(latest), TimeZone.getDefault(), Locale.getDefault()}); index = newest.getSerialIndex(); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { throw new RuntimeException(e); } // check if there are any values earlier than specified by the history // count... boolean removed = false; while (getItemCount() > 0 && (index - getTimePeriod(0).getSerialIndex()) > this.maximumItemAge) { this.data.remove(0); removed = true; } if (removed) { updateMinMaxYByIteration(); if (notify) { fireSeriesChanged(); } } } /** * Removes all data items from the series and sends a * {@link SeriesChangeEvent} to all registered listeners. */ public void clear() { if (this.data.size() > 0) { this.data.clear(); this.timePeriodClass = null; this.minY = Double.NaN; this.maxY = Double.NaN; fireSeriesChanged(); } } /** * Deletes the data item for the given time period and sends a * {@link SeriesChangeEvent} to all registered listeners. If there is no * item with the specified time period, this method does nothing. * * @param period the period of the item to delete ({@code null} not * permitted). */ public void delete(RegularTimePeriod period) { int index = getIndex(period); if (index >= 0) { TimeSeriesDataItem item = (TimeSeriesDataItem) this.data.remove( index); updateBoundsForRemovedItem(item); if (this.data.isEmpty()) { this.timePeriodClass = null; } fireSeriesChanged(); } } /** * Deletes data from start until end index (end inclusive). * * @param start the index of the first period to delete. * @param end the index of the last period to delete. */ public void delete(int start, int end) { delete(start, end, true); } /** * Deletes data from start until end index (end inclusive). * * @param start the index of the first period to delete. * @param end the index of the last period to delete. * @param notify notify listeners? */ public void delete(int start, int end, boolean notify) { if (end < start) { throw new IllegalArgumentException("Requires start <= end."); } for (int i = 0; i <= (end - start); i++) { this.data.remove(start); } updateMinMaxYByIteration(); if (this.data.isEmpty()) { this.timePeriodClass = null; } if (notify) { fireSeriesChanged(); } } /** * Returns a clone of the time series. *

* Notes: *

    *
  • no need to clone the domain and range descriptions, since String * object is immutable;
  • *
  • we pass over to the more general method clone(start, end).
  • *
* * @return A clone of the time series. * * @throws CloneNotSupportedException not thrown by this class, but * subclasses may differ. */ @Override public Object clone() throws CloneNotSupportedException { TimeSeries clone = (TimeSeries) super.clone(); clone.data = (List) ObjectUtils.deepClone(this.data); return clone; } /** * Creates a new timeseries by copying a subset of the data in this time * series. * * @param start the index of the first time period to copy. * @param end the index of the last time period to copy. * * @return A series containing a copy of this times series from start until * end. * * @throws CloneNotSupportedException if there is a cloning problem. */ public TimeSeries createCopy(int start, int end) throws CloneNotSupportedException { if (start < 0) { throw new IllegalArgumentException("Requires start >= 0."); } if (end < start) { throw new IllegalArgumentException("Requires start <= end."); } TimeSeries copy = (TimeSeries) super.clone(); copy.minY = Double.NaN; copy.maxY = Double.NaN; copy.data = new java.util.ArrayList(); if (this.data.size() > 0) { for (int index = start; index <= end; index++) { TimeSeriesDataItem item = (TimeSeriesDataItem) this.data.get(index); TimeSeriesDataItem clone = (TimeSeriesDataItem) item.clone(); try { copy.add(clone); } catch (SeriesException e) { throw new RuntimeException(e); } } } return copy; } /** * Creates a new timeseries by copying a subset of the data in this time * series. * * @param start the first time period to copy ({@code null} not * permitted). * @param end the last time period to copy ({@code null} not permitted). * * @return A time series containing a copy of this time series from start * until end. * * @throws CloneNotSupportedException if there is a cloning problem. */ public TimeSeries createCopy(RegularTimePeriod start, RegularTimePeriod end) throws CloneNotSupportedException { Args.nullNotPermitted(start, "start"); Args.nullNotPermitted(end, "end"); if (start.compareTo(end) > 0) { throw new IllegalArgumentException( "Requires start on or before end."); } boolean emptyRange = false; int startIndex = getIndex(start); if (startIndex < 0) { startIndex = -(startIndex + 1); if (startIndex == this.data.size()) { emptyRange = true; // start is after last data item } } int endIndex = getIndex(end); if (endIndex < 0) { // end period is not in original series endIndex = -(endIndex + 1); // this is first item AFTER end period endIndex = endIndex - 1; // so this is last item BEFORE end } if ((endIndex < 0) || (endIndex < startIndex)) { emptyRange = true; } if (emptyRange) { TimeSeries copy = (TimeSeries) super.clone(); copy.data = new java.util.ArrayList(); return copy; } return createCopy(startIndex, endIndex); } /** * Tests the series for equality with an arbitrary object. * * @param obj the object to test against ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof TimeSeries)) { return false; } TimeSeries that = (TimeSeries) obj; if (!Objects.equals(getDomainDescription(), that.getDomainDescription())) { return false; } if (!Objects.equals(getRangeDescription(), that.getRangeDescription())) { return false; } if (!Objects.equals(this.timePeriodClass, that.timePeriodClass)) { return false; } if (getMaximumItemAge() != that.getMaximumItemAge()) { return false; } if (getMaximumItemCount() != that.getMaximumItemCount()) { return false; } int count = getItemCount(); if (count != that.getItemCount()) { return false; } if (!Objects.equals(this.data, that.data)) { return false; } return super.equals(obj); } /** * Returns a hash code value for the object. * * @return The hashcode */ @Override public int hashCode() { int result = super.hashCode(); result = 29 * result + (this.domain != null ? this.domain.hashCode() : 0); result = 29 * result + (this.range != null ? this.range.hashCode() : 0); result = 29 * result + (this.timePeriodClass != null ? this.timePeriodClass.hashCode() : 0); // it is too slow to look at every data item, so let's just look at // the first, middle and last items... int count = getItemCount(); if (count > 0) { TimeSeriesDataItem item = getRawDataItem(0); result = 29 * result + item.hashCode(); } if (count > 1) { TimeSeriesDataItem item = getRawDataItem(count - 1); result = 29 * result + item.hashCode(); } if (count > 2) { TimeSeriesDataItem item = getRawDataItem(count / 2); result = 29 * result + item.hashCode(); } result = 29 * result + this.maximumItemCount; result = 29 * result + (int) this.maximumItemAge; return result; } /** * Updates the cached values for the minimum and maximum data values. * * @param item the item added ({@code null} not permitted). */ private void updateBoundsForAddedItem(TimeSeriesDataItem item) { Number yN = item.getValue(); if (item.getValue() != null) { double y = yN.doubleValue(); this.minY = minIgnoreNaN(this.minY, y); this.maxY = maxIgnoreNaN(this.maxY, y); } } /** * Updates the cached values for the minimum and maximum data values on * the basis that the specified item has just been removed. * * @param item the item added ({@code null} not permitted). */ private void updateBoundsForRemovedItem(TimeSeriesDataItem item) { Number yN = item.getValue(); if (yN != null) { double y = yN.doubleValue(); if (!Double.isNaN(y)) { if (y <= this.minY || y >= this.maxY) { updateMinMaxYByIteration(); } } } } /** * Finds the bounds of the x and y values for the series, by iterating * through all the data items. */ private void updateMinMaxYByIteration() { this.minY = Double.NaN; this.maxY = Double.NaN; Iterator iterator = this.data.iterator(); while (iterator.hasNext()) { TimeSeriesDataItem item = (TimeSeriesDataItem) iterator.next(); updateBoundsForAddedItem(item); } } /** * A function to find the minimum of two values, but ignoring any * Double.NaN values. * * @param a the first value. * @param b the second value. * * @return The minimum of the two values. */ private double minIgnoreNaN(double a, double b) { if (Double.isNaN(a)) { return b; } if (Double.isNaN(b)) { return a; } return Math.min(a, b); } /** * A function to find the maximum of two values, but ignoring any * Double.NaN values. * * @param a the first value. * @param b the second value. * * @return The maximum of the two values. */ private double maxIgnoreNaN(double a, double b) { if (Double.isNaN(a)) { return b; } if (Double.isNaN(b)) { return a; } else { return Math.max(a, b); } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/TimeSeriesCollection.java000066400000000000000000000577771463604235500306030ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * TimeSeriesCollection.java * ------------------------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import org.jfree.chart.util.Args; import org.jfree.chart.util.ObjectUtils; import org.jfree.data.DomainInfo; import org.jfree.data.DomainOrder; import org.jfree.data.Range; import org.jfree.data.general.DatasetChangeEvent; import org.jfree.data.general.Series; import org.jfree.data.xy.*; import java.beans.PropertyChangeEvent; import java.beans.PropertyVetoException; import java.beans.VetoableChangeListener; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.*; /** * A collection of time series objects. This class implements the * {@link XYDataset} interface, as well as the extended * {@link IntervalXYDataset} interface. This makes it a convenient dataset for * use with the {@link org.jfree.chart.plot.XYPlot} class. */ public class TimeSeriesCollection extends AbstractIntervalXYDataset implements XYDataset, IntervalXYDataset, DomainInfo, XYDomainInfo, XYRangeInfo, VetoableChangeListener, Serializable { /** For serialization. */ private static final long serialVersionUID = 834149929022371137L; /** Storage for the time series. */ private List data; /** A working calendar (to recycle) */ private Calendar workingCalendar; /** * The point within each time period that is used for the X value when this * collection is used as an {@link org.jfree.data.xy.XYDataset}. This can * be the start, middle or end of the time period. */ private TimePeriodAnchor xPosition; /** * Constructs an empty dataset, tied to the default timezone. */ public TimeSeriesCollection() { this(null, TimeZone.getDefault()); } /** * Constructs an empty dataset, tied to a specific timezone. * * @param zone the timezone ({@code null} permitted, will use * {@code TimeZone.getDefault()} in that case). */ public TimeSeriesCollection(TimeZone zone) { // FIXME: need a locale as well as a timezone this(null, zone); } /** * Constructs a dataset containing a single series (more can be added), * tied to the default timezone. * * @param series the series ({@code null} permitted). */ public TimeSeriesCollection(TimeSeries series) { this(series, TimeZone.getDefault()); } /** * Constructs a dataset containing a single series (more can be added), * tied to a specific timezone. * * @param series a series to add to the collection ({@code null} * permitted). * @param zone the timezone ({@code null} permitted, will use * {@code TimeZone.getDefault()} in that case). */ public TimeSeriesCollection(TimeSeries series, TimeZone zone) { // FIXME: need a locale as well as a timezone if (zone == null) { zone = TimeZone.getDefault(); } this.workingCalendar = Calendar.getInstance(zone); this.data = new ArrayList(); if (series != null) { this.data.add(series); series.addChangeListener(this); } this.xPosition = TimePeriodAnchor.START; } /** * Returns the order of the domain values in this dataset. * * @return {@link DomainOrder#ASCENDING} */ @Override public DomainOrder getDomainOrder() { return DomainOrder.ASCENDING; } /** * Returns the position within each time period that is used for the X * value when the collection is used as an * {@link org.jfree.data.xy.XYDataset}. * * @return The anchor position (never {@code null}). */ public TimePeriodAnchor getXPosition() { return this.xPosition; } /** * Sets the position within each time period that is used for the X values * when the collection is used as an {@link XYDataset}, then sends a * {@link DatasetChangeEvent} is sent to all registered listeners. * * @param anchor the anchor position ({@code null} not permitted). */ public void setXPosition(TimePeriodAnchor anchor) { Args.nullNotPermitted(anchor, "anchor"); this.xPosition = anchor; notifyListeners(new DatasetChangeEvent(this, this)); } /** * Returns a list of all the series in the collection. * * @return The list (which is unmodifiable). */ public List getSeries() { return Collections.unmodifiableList(this.data); } /** * Returns the number of series in the collection. * * @return The series count. */ @Override public int getSeriesCount() { return this.data.size(); } /** * Returns the index of the specified series, or -1 if that series is not * present in the dataset. * * @param series the series ({@code null} not permitted). * * @return The series index. */ public int indexOf(TimeSeries series) { Args.nullNotPermitted(series, "series"); return this.data.indexOf(series); } /** * Returns a series. * * @param series the index of the series (zero-based). * * @return The series. */ public TimeSeries getSeries(int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException( "The 'series' argument is out of bounds (" + series + ")."); } return (TimeSeries) this.data.get(series); } /** * Returns the series with the specified key, or {@code null} if * there is no such series. * * @param key the series key ({@code null} permitted). * * @return The series with the given key. */ public TimeSeries getSeries(Comparable key) { TimeSeries result = null; Iterator iterator = this.data.iterator(); while (iterator.hasNext()) { TimeSeries series = (TimeSeries) iterator.next(); Comparable k = series.getKey(); if (k != null && k.equals(key)) { result = series; } } return result; } /** * Returns the key for a series. * * @param series the index of the series (zero-based). * * @return The key for a series. */ @Override public Comparable getSeriesKey(int series) { // check arguments...delegated // fetch the series name... return getSeries(series).getKey(); } /** * Returns the index of the series with the specified key, or -1 if no * series has that key. * * @param key the key ({@code null} not permitted). * * @return The index. */ public int getSeriesIndex(Comparable key) { Args.nullNotPermitted(key, "key"); int seriesCount = getSeriesCount(); for (int i = 0; i < seriesCount; i++) { TimeSeries series = (TimeSeries) this.data.get(i); if (key.equals(series.getKey())) { return i; } } return -1; } /** * Adds a series to the collection and sends a {@link DatasetChangeEvent} to * all registered listeners. * * @param series the series ({@code null} not permitted). */ public void addSeries(TimeSeries series) { Args.nullNotPermitted(series, "series"); this.data.add(series); series.addChangeListener(this); series.addVetoableChangeListener(this); fireDatasetChanged(); } /** * Removes the specified series from the collection and sends a * {@link DatasetChangeEvent} to all registered listeners. * * @param series the series ({@code null} not permitted). */ public void removeSeries(TimeSeries series) { Args.nullNotPermitted(series, "series"); this.data.remove(series); series.removeChangeListener(this); series.removeVetoableChangeListener(this); fireDatasetChanged(); } /** * Removes a series from the collection. * * @param index the series index (zero-based). */ public void removeSeries(int index) { TimeSeries series = getSeries(index); if (series != null) { removeSeries(series); } } /** * Removes all the series from the collection and sends a * {@link DatasetChangeEvent} to all registered listeners. */ public void removeAllSeries() { // deregister the collection as a change listener to each series in the // collection for (int i = 0; i < this.data.size(); i++) { TimeSeries series = (TimeSeries) this.data.get(i); series.removeChangeListener(this); series.removeVetoableChangeListener(this); } // remove all the series from the collection and notify listeners. this.data.clear(); fireDatasetChanged(); } /** * Returns the number of items in the specified series. This method is * provided for convenience. * * @param series the series index (zero-based). * * @return The item count. */ @Override public int getItemCount(int series) { return getSeries(series).getItemCount(); } /** * Returns the x-value (as a double primitive) for an item within a series. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The x-value. */ @Override public double getXValue(int series, int item) { TimeSeries s = (TimeSeries) this.data.get(series); RegularTimePeriod period = s.getTimePeriod(item); return getX(period); } /** * Returns the x-value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The value. */ @Override public Number getX(int series, int item) { TimeSeries ts = (TimeSeries) this.data.get(series); RegularTimePeriod period = ts.getTimePeriod(item); return getX(period); } /** * Returns the x-value for a time period. * * @param period the time period ({@code null} not permitted). * * @return The x-value. */ protected synchronized long getX(RegularTimePeriod period) { long result = 0L; if (this.xPosition == TimePeriodAnchor.START) { result = period.getFirstMillisecond(this.workingCalendar); } else if (this.xPosition == TimePeriodAnchor.MIDDLE) { result = period.getMiddleMillisecond(this.workingCalendar); } else if (this.xPosition == TimePeriodAnchor.END) { result = period.getLastMillisecond(this.workingCalendar); } return result; } /** * Returns the starting X value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The value. */ @Override public synchronized Number getStartX(int series, int item) { TimeSeries ts = (TimeSeries) this.data.get(series); return ts.getTimePeriod(item).getFirstMillisecond(this.workingCalendar); } /** * Returns the ending X value for the specified series and item. * * @param series The series (zero-based index). * @param item The item (zero-based index). * * @return The value. */ @Override public synchronized Number getEndX(int series, int item) { TimeSeries ts = (TimeSeries) this.data.get(series); return ts.getTimePeriod(item).getLastMillisecond(this.workingCalendar); } /** * Returns the y-value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The value (possibly {@code null}). */ @Override public Number getY(int series, int item) { TimeSeries ts = (TimeSeries) this.data.get(series); return ts.getValue(item); } /** * Returns the starting Y value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The value (possibly {@code null}). */ @Override public Number getStartY(int series, int item) { return getY(series, item); } /** * Returns the ending Y value for the specified series and item. * * @param series te series (zero-based index). * @param item the item (zero-based index). * * @return The value (possibly {@code null}). */ @Override public Number getEndY(int series, int item) { return getY(series, item); } /** * Returns the indices of the two data items surrounding a particular * millisecond value. * * @param series the series index. * @param milliseconds the time. * * @return An array containing the (two) indices of the items surrounding * the time. */ public int[] getSurroundingItems(int series, long milliseconds) { int[] result = new int[] {-1, -1}; TimeSeries timeSeries = getSeries(series); for (int i = 0; i < timeSeries.getItemCount(); i++) { Number x = getX(series, i); long m = x.longValue(); if (m <= milliseconds) { result[0] = i; } if (m >= milliseconds) { result[1] = i; break; } } return result; } /** * Returns the minimum x-value in the dataset. * * @param includeInterval a flag that determines whether or not the * x-interval is taken into account. * * @return The minimum value. */ @Override public double getDomainLowerBound(boolean includeInterval) { double result = Double.NaN; Range r = getDomainBounds(includeInterval); if (r != null) { result = r.getLowerBound(); } return result; } /** * Returns the maximum x-value in the dataset. * * @param includeInterval a flag that determines whether or not the * x-interval is taken into account. * * @return The maximum value. */ @Override public double getDomainUpperBound(boolean includeInterval) { double result = Double.NaN; Range r = getDomainBounds(includeInterval); if (r != null) { result = r.getUpperBound(); } return result; } /** * Returns the range of the values in this dataset's domain. * * @param includeInterval a flag that determines whether or not the * x-interval is taken into account. * * @return The range. */ @Override public Range getDomainBounds(boolean includeInterval) { Range result = null; Iterator iterator = this.data.iterator(); while (iterator.hasNext()) { TimeSeries series = (TimeSeries) iterator.next(); int count = series.getItemCount(); if (count > 0) { RegularTimePeriod start = series.getTimePeriod(0); RegularTimePeriod end = series.getTimePeriod(count - 1); Range temp; if (!includeInterval) { temp = new Range(getX(start), getX(end)); } else { temp = new Range( start.getFirstMillisecond(this.workingCalendar), end.getLastMillisecond(this.workingCalendar)); } result = Range.combine(result, temp); } } return result; } /** * Returns the bounds of the domain values for the specified series. * * @param visibleSeriesKeys a list of keys for the visible series. * @param includeInterval include the x-interval? * * @return A range. */ @Override public Range getDomainBounds(List visibleSeriesKeys, boolean includeInterval) { Range result = null; Iterator iterator = visibleSeriesKeys.iterator(); while (iterator.hasNext()) { Comparable seriesKey = (Comparable) iterator.next(); TimeSeries series = getSeries(seriesKey); int count = series.getItemCount(); if (count > 0) { RegularTimePeriod start = series.getTimePeriod(0); RegularTimePeriod end = series.getTimePeriod(count - 1); Range temp; if (!includeInterval) { temp = new Range(getX(start), getX(end)); } else { temp = new Range( start.getFirstMillisecond(this.workingCalendar), end.getLastMillisecond(this.workingCalendar)); } result = Range.combine(result, temp); } } return result; } /** * Returns the bounds for the y-values in the dataset. * * @param includeInterval ignored for this dataset. * * @return The range of value in the dataset (possibly {@code null}). */ public Range getRangeBounds(boolean includeInterval) { Range result = null; Iterator iterator = this.data.iterator(); while (iterator.hasNext()) { TimeSeries series = (TimeSeries) iterator.next(); Range r = new Range(series.getMinY(), series.getMaxY()); result = Range.combineIgnoringNaN(result, r); } return result; } /** * Returns the bounds for the y-values in the dataset. * * @param visibleSeriesKeys the visible series keys. * @param xRange the x-range ({@code null} not permitted). * @param includeInterval ignored. * * @return The bounds. */ @Override public Range getRangeBounds(List visibleSeriesKeys, Range xRange, boolean includeInterval) { Range result = null; for (Object visibleSeriesKey : visibleSeriesKeys) { Comparable seriesKey = (Comparable) visibleSeriesKey; TimeSeries series = getSeries(seriesKey); Range r = series.findValueRange(xRange, this.xPosition, this.workingCalendar); result = Range.combineIgnoringNaN(result, r); } return result; } /** * Receives notification that the key for one of the series in the * collection has changed, and vetos it if the key is already present in * the collection. * * @param e the event. */ @Override public void vetoableChange(PropertyChangeEvent e) throws PropertyVetoException { // if it is not the series name, then we have no interest if (!"Key".equals(e.getPropertyName())) { return; } // to be defensive, let's check that the source series does in fact // belong to this collection Series s = (Series) e.getSource(); if (getSeriesIndex(s.getKey()) == -1) { throw new IllegalStateException("Receiving events from a series " + "that does not belong to this collection."); } // check if the new series name already exists for another series Comparable key = (Comparable) e.getNewValue(); if (getSeriesIndex(key) >= 0) { throw new PropertyVetoException("Duplicate key2", e); } } /** * Tests this time series collection for equality with another object. * * @param obj the other object. * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof TimeSeriesCollection)) { return false; } TimeSeriesCollection that = (TimeSeriesCollection) obj; if (this.xPosition != that.xPosition) { return false; } if (!Objects.equals(this.data, that.data)) { return false; } return true; } /** * Returns a hash code value for the object. * * @return The hashcode */ @Override public int hashCode() { int result; result = this.data.hashCode(); result = 29 * result + (this.workingCalendar != null ? this.workingCalendar.hashCode() : 0); result = 29 * result + (this.xPosition != null ? this.xPosition.hashCode() : 0); return result; } /** * Returns a clone of this time series collection. * * @return A clone. * * @throws java.lang.CloneNotSupportedException if there is a problem * cloning. */ @Override public Object clone() throws CloneNotSupportedException { TimeSeriesCollection clone = (TimeSeriesCollection) super.clone(); clone.data = (List) ObjectUtils.deepClone(this.data); clone.workingCalendar = (Calendar) this.workingCalendar.clone(); return clone; } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); for (Object item : this.data) { TimeSeries series = (TimeSeries) item; series.addChangeListener(this); } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/TimeSeriesDataItem.java000066400000000000000000000151221463604235500301520ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * TimeSeriesDataItem.java * ----------------------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.util.Args; /** * Represents one data item in a time series. *

* The time period can be any of the following: *

    *
  • {@link Year}
  • *
  • {@link Quarter}
  • *
  • {@link Month}
  • *
  • {@link Week}
  • *
  • {@link Day}
  • *
  • {@link Hour}
  • *
  • {@link Minute}
  • *
  • {@link Second}
  • *
  • {@link Millisecond}
  • *
  • {@link FixedMillisecond}
  • *
* * The time period is an immutable property of the data item. Data items will * often be sorted within a list, and allowing the time period to be changed * could destroy the sort order. *

* Implements the {@code Comparable} interface so that standard Java * sorting can be used to keep the data items in order. * */ public class TimeSeriesDataItem implements Cloneable, Comparable, Serializable { /** For serialization. */ private static final long serialVersionUID = -2235346966016401302L; /** The time period. */ private RegularTimePeriod period; /** The value associated with the time period. */ private Number value; /** * Constructs a new data item that associates a value with a time period. * * @param period the time period ({@code null} not permitted). * @param value the value ({@code null} permitted). */ public TimeSeriesDataItem(RegularTimePeriod period, Number value) { Args.nullNotPermitted(period, "period"); this.period = period; this.value = value; } /** * Constructs a new data item that associates a value with a time period. * * @param period the time period ({@code null} not permitted). * @param value the value associated with the time period. */ public TimeSeriesDataItem(RegularTimePeriod period, double value) { this(period, Double.valueOf(value)); } /** * Returns the time period. * * @return The time period (never {@code null}). */ public RegularTimePeriod getPeriod() { return this.period; } /** * Returns the value. * * @return The value ({@code null} possible). * * @see #setValue(java.lang.Number) */ public Number getValue() { return this.value; } /** * Sets the value for this data item. * * @param value the value ({@code null} permitted). * * @see #getValue() */ public void setValue(Number value) { this.value = value; } /** * Tests this object for equality with an arbitrary object. * * @param obj the other object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof TimeSeriesDataItem)) { return false; } TimeSeriesDataItem that = (TimeSeriesDataItem) obj; if (!Objects.equals(this.period, that.period)) { return false; } if (!Objects.equals(this.value, that.value)) { return false; } return true; } /** * Returns a hash code. * * @return A hash code. */ @Override public int hashCode() { int result; result = (this.period != null ? this.period.hashCode() : 0); result = 29 * result + (this.value != null ? this.value.hashCode() : 0); return result; } /** * Returns an integer indicating the order of this data pair object * relative to another object. *

* For the order we consider only the timing: * negative == before, zero == same, positive == after. * * @param o1 The object being compared to. * * @return An integer indicating the order of the data item object * relative to another object. */ @Override public int compareTo(Object o1) { int result; // CASE 1 : Comparing to another TimeSeriesDataItem object // ------------------------------------------------------- if (o1 instanceof TimeSeriesDataItem) { TimeSeriesDataItem datapair = (TimeSeriesDataItem) o1; result = getPeriod().compareTo(datapair.getPeriod()); } // CASE 2 : Comparing to a general object // --------------------------------------------- else { // consider time periods to be ordered after general objects result = 1; } return result; } /** * Clones the data item. Note: there is no need to clone the period or * value since they are immutable classes. * * @return A clone of the data item. */ @Override public Object clone() { Object clone = null; try { clone = super.clone(); } catch (CloneNotSupportedException e) { // won't get here... e.printStackTrace(); } return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/TimeSeriesTableModel.java000066400000000000000000000161501463604235500304740ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * TimeSeriesTableModel.java * ------------------------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import javax.swing.table.AbstractTableModel; import org.jfree.data.general.SeriesChangeEvent; import org.jfree.data.general.SeriesChangeListener; /** * Wrapper around a time series to convert it to a table model for use in * a {@code JTable}. */ public class TimeSeriesTableModel extends AbstractTableModel implements SeriesChangeListener { /** The series. */ private TimeSeries series; /** A flag that controls whether the series is editable. */ private boolean editable; /** The new time period. */ private RegularTimePeriod newTimePeriod; /** The new value. */ private Number newValue; /** * Default constructor. */ public TimeSeriesTableModel() { this(new TimeSeries("Untitled")); } /** * Constructs a table model for a time series. * * @param series the time series. */ public TimeSeriesTableModel(TimeSeries series) { this(series, false); } /** * Creates a table model based on a time series. * * @param series the time series. * @param editable if {@code true}, the table is editable. */ public TimeSeriesTableModel(TimeSeries series, boolean editable) { this.series = series; this.series.addChangeListener(this); this.editable = editable; } /** * Returns the number of columns in the table model. For this particular * model, the column count is fixed at 2. * * @return The column count. */ @Override public int getColumnCount() { return 2; } /** * Returns the column class in the table model. * * @param column the column index. * * @return The column class in the table model. */ @Override public Class getColumnClass(int column) { if (column == 0) { return String.class; } else { if (column == 1) { return Double.class; } else { return null; } } } /** * Returns the name of a column * * @param column the column index. * * @return The name of a column. */ @Override public String getColumnName(int column) { if (column == 0) { return "Period:"; } else { if (column == 1) { return "Value:"; } else { return null; } } } /** * Returns the number of rows in the table model. * * @return The row count. */ @Override public int getRowCount() { return this.series.getItemCount(); } /** * Returns the data value for a cell in the table model. * * @param row the row number. * @param column the column number. * * @return The data value for a cell in the table model. */ @Override public Object getValueAt(int row, int column) { if (row < this.series.getItemCount()) { if (column == 0) { return this.series.getTimePeriod(row); } else { if (column == 1) { return this.series.getValue(row); } else { return null; } } } else { if (column == 0) { return this.newTimePeriod; } else { if (column == 1) { return this.newValue; } else { return null; } } } } /** * Returns a flag indicating whether or not the specified cell is editable. * * @param row the row number. * @param column the column number. * * @return {@code true} if the specified cell is editable. */ @Override public boolean isCellEditable(int row, int column) { if (this.editable) { if ((column == 0) || (column == 1)) { return true; } else { return false; } } else { return false; } } /** * Updates the time series. * * @param value the new value. * @param row the row. * @param column the column. */ @Override public void setValueAt(Object value, int row, int column) { if (row < this.series.getItemCount()) { // update the time series appropriately if (column == 1) { try { Double v = Double.valueOf(value.toString()); this.series.update(row, v); } catch (NumberFormatException nfe) { System.err.println("Number format exception"); } } } else { if (column == 0) { // this.series.getClass().valueOf(value.toString()); this.newTimePeriod = null; } else if (column == 1) { this.newValue = Double.valueOf(value.toString()); } } } /** * Receives notification that the time series has been changed. Responds * by firing a table data change event. * * @param event the event. */ @Override public void seriesChanged(SeriesChangeEvent event) { fireTableDataChanged(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/TimeTableXYDataset.java000066400000000000000000000461751463604235500301410ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * TimeTableXYDataset.java * ----------------------- * (C) Copyright 2004-present, by Andreas Schroeder and Contributors. * * Original Author: Andreas Schroeder; * Contributor(s): David Gilbert; * Rob Eden; * */ package org.jfree.data.time; import java.util.Calendar; import java.util.List; import java.util.Locale; import java.util.TimeZone; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.DefaultKeyedValues2D; import org.jfree.data.DomainInfo; import org.jfree.data.Range; import org.jfree.data.general.DatasetChangeEvent; import org.jfree.data.xy.AbstractIntervalXYDataset; import org.jfree.data.xy.IntervalXYDataset; import org.jfree.data.xy.TableXYDataset; /** * A dataset for regular time periods that implements the * {@link TableXYDataset} interface. Note that the {@link TableXYDataset} * interface requires all series to share the same set of x-values. When * adding a new item {@code (x, y)} to one series, all other series * automatically get a new item {@code (x, null)} unless a non-null item * has already been specified. * * @see org.jfree.data.xy.TableXYDataset */ public class TimeTableXYDataset extends AbstractIntervalXYDataset implements Cloneable, PublicCloneable, IntervalXYDataset, DomainInfo, TableXYDataset { /** * The data structure to store the values. Each column represents * a series (elsewhere in JFreeChart rows are typically used for series, * but it doesn't matter that much since this data structure is private * and symmetrical anyway), each row contains values for the same * {@link RegularTimePeriod} (the rows are sorted into ascending order). */ private DefaultKeyedValues2D values; /** * A flag that indicates that the domain is 'points in time'. If this flag * is true, only the x-value (and not the x-interval) is used to determine * the range of values in the domain. */ private boolean domainIsPointsInTime; /** * The point within each time period that is used for the X value when this * collection is used as an {@link org.jfree.data.xy.XYDataset}. This can * be the start, middle or end of the time period. */ private TimePeriodAnchor xPosition; /** A working calendar (to recycle) */ private Calendar workingCalendar; /** * Creates a new dataset. */ public TimeTableXYDataset() { // defer argument checking this(TimeZone.getDefault(), Locale.getDefault()); } /** * Creates a new dataset with the given time zone. * * @param zone the time zone to use ({@code null} not permitted). */ public TimeTableXYDataset(TimeZone zone) { // defer argument checking this(zone, Locale.getDefault()); } /** * Creates a new dataset with the given time zone and locale. * * @param zone the time zone to use ({@code null} not permitted). * @param locale the locale to use ({@code null} not permitted). */ public TimeTableXYDataset(TimeZone zone, Locale locale) { Args.nullNotPermitted(zone, "zone"); Args.nullNotPermitted(locale, "locale"); this.values = new DefaultKeyedValues2D(true); this.workingCalendar = Calendar.getInstance(zone, locale); this.xPosition = TimePeriodAnchor.START; } /** * Returns a flag that controls whether the domain is treated as 'points in * time'. *

* This flag is used when determining the max and min values for the domain. * If true, then only the x-values are considered for the max and min * values. If false, then the start and end x-values will also be taken * into consideration. * * @return The flag. * * @see #setDomainIsPointsInTime(boolean) */ public boolean getDomainIsPointsInTime() { return this.domainIsPointsInTime; } /** * Sets a flag that controls whether the domain is treated as 'points in * time', or time periods. A {@link DatasetChangeEvent} is sent to all * registered listeners. * * @param flag the new value of the flag. * * @see #getDomainIsPointsInTime() */ public void setDomainIsPointsInTime(boolean flag) { this.domainIsPointsInTime = flag; notifyListeners(new DatasetChangeEvent(this, this)); } /** * Returns the position within each time period that is used for the X * value. * * @return The anchor position (never {@code null}). * * @see #setXPosition(TimePeriodAnchor) */ public TimePeriodAnchor getXPosition() { return this.xPosition; } /** * Sets the position within each time period that is used for the X values, * then sends a {@link DatasetChangeEvent} to all registered listeners. * * @param anchor the anchor position ({@code null} not permitted). * * @see #getXPosition() */ public void setXPosition(TimePeriodAnchor anchor) { Args.nullNotPermitted(anchor, "anchor"); this.xPosition = anchor; notifyListeners(new DatasetChangeEvent(this, this)); } /** * Adds a new data item to the dataset and sends a * {@link DatasetChangeEvent} to all registered listeners. * * @param period the time period. * @param y the value for this period. * @param seriesName the name of the series to add the value. * * @see #remove(TimePeriod, Comparable) */ public void add(TimePeriod period, double y, Comparable seriesName) { add(period, y, seriesName, true); } /** * Adds a new data item to the dataset and, if requested, sends a * {@link DatasetChangeEvent} to all registered listeners. * * @param period the time period ({@code null} not permitted). * @param y the value for this period ({@code null} permitted). * @param seriesName the name of the series to add the value * ({@code null} not permitted). * @param notify whether dataset listener are notified or not. * * @see #remove(TimePeriod, Comparable, boolean) */ public void add(TimePeriod period, Number y, Comparable seriesName, boolean notify) { // here's a quirk - the API has been defined in terms of a plain // TimePeriod, which cannot make use of the timezone and locale // specified in the constructor...so we only do the time zone // pegging if the period is an instanceof RegularTimePeriod if (period instanceof RegularTimePeriod) { RegularTimePeriod p = (RegularTimePeriod) period; p.peg(this.workingCalendar); } this.values.addValue(y, period, seriesName); if (notify) { fireDatasetChanged(); } } /** * Removes an existing data item from the dataset. * * @param period the (existing!) time period of the value to remove * ({@code null} not permitted). * @param seriesName the (existing!) series name to remove the value * ({@code null} not permitted). * * @see #add(TimePeriod, double, Comparable) */ public void remove(TimePeriod period, Comparable seriesName) { remove(period, seriesName, true); } /** * Removes an existing data item from the dataset and, if requested, * sends a {@link DatasetChangeEvent} to all registered listeners. * * @param period the (existing!) time period of the value to remove * ({@code null} not permitted). * @param seriesName the (existing!) series name to remove the value * ({@code null} not permitted). * @param notify whether dataset listener are notified or not. * * @see #add(TimePeriod, double, Comparable) */ public void remove(TimePeriod period, Comparable seriesName, boolean notify) { this.values.removeValue(period, seriesName); if (notify) { fireDatasetChanged(); } } /** * Removes all data items from the dataset and sends a * {@link DatasetChangeEvent} to all registered listeners. */ public void clear() { if (this.values.getRowCount() > 0) { this.values.clear(); fireDatasetChanged(); } } /** * Returns the time period for the specified item. Bear in mind that all * series share the same set of time periods. * * @param item the item index (0 <= i <= {@link #getItemCount()}). * * @return The time period. */ public TimePeriod getTimePeriod(int item) { return (TimePeriod) this.values.getRowKey(item); } /** * Returns the number of items in ALL series. * * @return The item count. */ @Override public int getItemCount() { return this.values.getRowCount(); } /** * Returns the number of items in a series. This is the same value * that is returned by {@link #getItemCount()} since all series * share the same x-values (time periods). * * @param series the series (zero-based index, ignored). * * @return The number of items within the series. */ @Override public int getItemCount(int series) { return getItemCount(); } /** * Returns the number of series in the dataset. * * @return The series count. */ @Override public int getSeriesCount() { return this.values.getColumnCount(); } /** * Returns the key for a series. * * @param series the series (zero-based index). * * @return The key for the series. */ @Override public Comparable getSeriesKey(int series) { return this.values.getColumnKey(series); } /** * Returns the x-value for an item within a series. The x-values may or * may not be returned in ascending order, that is up to the class * implementing the interface. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The x-value. */ @Override public Number getX(int series, int item) { return getXValue(series, item); } /** * Returns the x-value (as a double primitive) for an item within a series. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public double getXValue(int series, int item) { TimePeriod period = (TimePeriod) this.values.getRowKey(item); return getXValue(period); } /** * Returns the starting X value for the specified series and item. * * @param series the series (zero-based index). * @param item the item within a series (zero-based index). * * @return The starting X value for the specified series and item. * * @see #getStartXValue(int, int) */ @Override public Number getStartX(int series, int item) { return getStartXValue(series, item); } /** * Returns the start x-value (as a double primitive) for an item within * a series. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public double getStartXValue(int series, int item) { TimePeriod period = (TimePeriod) this.values.getRowKey(item); return period.getStart().getTime(); } /** * Returns the ending X value for the specified series and item. * * @param series the series (zero-based index). * @param item the item within a series (zero-based index). * * @return The ending X value for the specified series and item. * * @see #getEndXValue(int, int) */ @Override public Number getEndX(int series, int item) { return getEndXValue(series, item); } /** * Returns the end x-value (as a double primitive) for an item within * a series. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public double getEndXValue(int series, int item) { TimePeriod period = (TimePeriod) this.values.getRowKey(item); return period.getEnd().getTime(); } /** * Returns the y-value for an item within a series. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The y-value (possibly {@code null}). */ @Override public Number getY(int series, int item) { return this.values.getValue(item, series); } /** * Returns the starting Y value for the specified series and item. * * @param series the series (zero-based index). * @param item the item within a series (zero-based index). * * @return The starting Y value for the specified series and item. */ @Override public Number getStartY(int series, int item) { return getY(series, item); } /** * Returns the ending Y value for the specified series and item. * * @param series the series (zero-based index). * @param item the item within a series (zero-based index). * * @return The ending Y value for the specified series and item. */ @Override public Number getEndY(int series, int item) { return getY(series, item); } /** * Returns the x-value for a time period. * * @param period the time period. * * @return The x-value. */ private long getXValue(TimePeriod period) { long result = 0L; if (this.xPosition == TimePeriodAnchor.START) { result = period.getStart().getTime(); } else if (this.xPosition == TimePeriodAnchor.MIDDLE) { long t0 = period.getStart().getTime(); long t1 = period.getEnd().getTime(); result = t0 + (t1 - t0) / 2L; } else if (this.xPosition == TimePeriodAnchor.END) { result = period.getEnd().getTime(); } return result; } /** * Returns the minimum x-value in the dataset. * * @param includeInterval a flag that determines whether or not the * x-interval is taken into account. * * @return The minimum value. */ @Override public double getDomainLowerBound(boolean includeInterval) { double result = Double.NaN; Range r = getDomainBounds(includeInterval); if (r != null) { result = r.getLowerBound(); } return result; } /** * Returns the maximum x-value in the dataset. * * @param includeInterval a flag that determines whether or not the * x-interval is taken into account. * * @return The maximum value. */ @Override public double getDomainUpperBound(boolean includeInterval) { double result = Double.NaN; Range r = getDomainBounds(includeInterval); if (r != null) { result = r.getUpperBound(); } return result; } /** * Returns the range of the values in this dataset's domain. * * @param includeInterval a flag that controls whether or not the * x-intervals are taken into account. * * @return The range. */ @Override public Range getDomainBounds(boolean includeInterval) { List keys = this.values.getRowKeys(); if (keys.isEmpty()) { return null; } TimePeriod first = (TimePeriod) keys.get(0); TimePeriod last = (TimePeriod) keys.get(keys.size() - 1); if (!includeInterval || this.domainIsPointsInTime) { return new Range(getXValue(first), getXValue(last)); } else { return new Range(first.getStart().getTime(), last.getEnd().getTime()); } } /** * Tests this dataset for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof TimeTableXYDataset)) { return false; } TimeTableXYDataset that = (TimeTableXYDataset) obj; if (this.domainIsPointsInTime != that.domainIsPointsInTime) { return false; } if (this.xPosition != that.xPosition) { return false; } if (!this.workingCalendar.getTimeZone().equals( that.workingCalendar.getTimeZone()) ) { return false; } if (!this.values.equals(that.values)) { return false; } return true; } /** * Returns a clone of this dataset. * * @return A clone. * * @throws CloneNotSupportedException if the dataset cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { TimeTableXYDataset clone = (TimeTableXYDataset) super.clone(); clone.values = (DefaultKeyedValues2D) this.values.clone(); clone.workingCalendar = (Calendar) this.workingCalendar.clone(); return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/Week.java000066400000000000000000000514211463604235500253650ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------- * Week.java * --------- * (C) Copyright 2001-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Aimin Han; * */ package org.jfree.data.time; import java.io.Serializable; import java.util.Calendar; import java.util.Date; import java.util.Locale; import java.util.TimeZone; import org.jfree.chart.util.Args; /** * A calendar week. All years are considered to have 53 weeks, numbered from 1 * to 53, although in many cases the 53rd week is empty. Most of the time, the * 1st week of the year *begins* in the previous calendar year, but it always * finishes in the current year (this behaviour matches the workings of the * {@code GregorianCalendar} class). *

* This class is immutable, which is a requirement for all * {@link RegularTimePeriod} subclasses. */ public class Week extends RegularTimePeriod implements Serializable { /** For serialization. */ private static final long serialVersionUID = 1856387786939865061L; /** Constant for the first week in the year. */ public static final int FIRST_WEEK_IN_YEAR = 1; /** Constant for the last week in the year. */ public static final int LAST_WEEK_IN_YEAR = 53; /** The year in which the week falls. */ private short year; /** The week (1-53). */ private byte week; /** The first millisecond. */ private long firstMillisecond; /** The last millisecond. */ private long lastMillisecond; /** * Creates a new time period for the week in which the current system * date/time falls. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. */ public Week() { this(new Date()); } /** * Creates a time period representing the week in the specified year. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. * * @param week the week (1 to 53). * @param year the year (1900 to 9999). */ public Week(int week, int year) { Args.requireInRange(week, "week", FIRST_WEEK_IN_YEAR, LAST_WEEK_IN_YEAR); this.week = (byte) week; this.year = (short) year; peg(getCalendarInstance()); } /** * Creates a time period representing the week in the specified year. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. * * @param week the week (1 to 53). * @param year the year (1900 to 9999). */ public Week(int week, Year year) { this(week, year.getYear()); } /** * Creates a time period for the week in which the specified date/time * falls. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. * The locale can affect the day-of-the-week that marks the beginning * of the week, as well as the minimal number of days in the first week * of the year. * * @param time the time ({@code null} not permitted). * * @see #Week(Date, TimeZone, Locale) */ public Week(Date time) { // defer argument checking... this(time, getCalendarInstance()); } /** * Creates a time period for the week in which the specified date/time * falls, calculated relative to the specified time zone. * * @param time the date/time ({@code null} not permitted). * @param zone the time zone ({@code null} not permitted). * @param locale the locale ({@code null} not permitted). */ public Week(Date time, TimeZone zone, Locale locale) { Args.nullNotPermitted(time, "time"); Args.nullNotPermitted(zone, "zone"); Args.nullNotPermitted(locale, "locale"); Calendar calendar = Calendar.getInstance(zone, locale); calendar.setTime(time); // sometimes the last few days of the year are considered to fall in // the *first* week of the following year. Refer to the Javadocs for // GregorianCalendar. int tempWeek = calendar.get(Calendar.WEEK_OF_YEAR); if (tempWeek == 1 && calendar.get(Calendar.MONTH) == Calendar.DECEMBER) { this.week = 1; this.year = (short) (calendar.get(Calendar.YEAR) + 1); } else { this.week = (byte) Math.min(tempWeek, LAST_WEEK_IN_YEAR); int yyyy = calendar.get(Calendar.YEAR); // alternatively, sometimes the first few days of the year are // considered to fall in the *last* week of the previous year... if (calendar.get(Calendar.MONTH) == Calendar.JANUARY && this.week >= 52) { yyyy--; } this.year = (short) yyyy; } peg(calendar); } /** * Constructs a new instance, based on a particular date/time. * The time zone and locale are determined by the {@code calendar} * parameter. * * @param time the date/time ({@code null} not permitted). * @param calendar the calendar to use for calculations ({@code null} not permitted). */ public Week(Date time, Calendar calendar) { calendar.setTime(time); // sometimes the last few days of the year are considered to fall in // the *first* week of the following year. Refer to the Javadocs for // GregorianCalendar. int tempWeek = calendar.get(Calendar.WEEK_OF_YEAR); if (tempWeek == 1 && calendar.get(Calendar.MONTH) == Calendar.DECEMBER) { this.week = 1; this.year = (short) (calendar.get(Calendar.YEAR) + 1); } else { this.week = (byte) Math.min(tempWeek, LAST_WEEK_IN_YEAR); int yyyy = calendar.get(Calendar.YEAR); // alternatively, sometimes the first few days of the year are // considered to fall in the *last* week of the previous year... if (calendar.get(Calendar.MONTH) == Calendar.JANUARY && this.week >= 52) { yyyy--; } this.year = (short) yyyy; } peg(calendar); } /** * Returns the year in which the week falls. * * @return The year (never {@code null}). */ public Year getYear() { return new Year(this.year); } /** * Returns the year in which the week falls, as an integer value. * * @return The year. */ public int getYearValue() { return this.year; } /** * Returns the week. * * @return The week. */ public int getWeek() { return this.week; } /** * Returns the first millisecond of the week. This will be determined * relative to the time zone specified in the constructor, or in the * calendar instance passed in the most recent call to the * {@link #peg(Calendar)} method. * * @return The first millisecond of the week. * * @see #getLastMillisecond() */ @Override public long getFirstMillisecond() { return this.firstMillisecond; } /** * Returns the last millisecond of the week. This will be * determined relative to the time zone specified in the constructor, or * in the calendar instance passed in the most recent call to the * {@link #peg(Calendar)} method. * * @return The last millisecond of the week. * * @see #getFirstMillisecond() */ @Override public long getLastMillisecond() { return this.lastMillisecond; } /** * Recalculates the start date/time and end date/time for this time period * relative to the supplied calendar (which incorporates a time zone * and information about what day is the first day of the week). * * @param calendar the calendar ({@code null} not permitted). */ @Override public void peg(Calendar calendar) { this.firstMillisecond = getFirstMillisecond(calendar); this.lastMillisecond = getLastMillisecond(calendar); } /** * Returns the week preceding this one. This method will return * {@code null} for some lower limit on the range of weeks (currently * week 1, 1900). For week 1 of any year, the previous week is always week * 53, but week 53 may not contain any days (you should check for this). * No matter what time zone and locale this instance was created with, * the returned instance will use the default calendar for time * calculations, obtained with {@link RegularTimePeriod#getCalendarInstance()}. * * @return The preceding week (possibly {@code null}). */ @Override public RegularTimePeriod previous() { Week result; if (this.week != FIRST_WEEK_IN_YEAR) { result = new Week(this.week - 1, this.year); } else { // we need to work out if the previous year has 52 or 53 weeks... if (this.year > 1900) { int yy = this.year - 1; Calendar prevYearCalendar = getCalendarInstance(); prevYearCalendar.set(yy, Calendar.DECEMBER, 31); result = new Week(prevYearCalendar.getActualMaximum( Calendar.WEEK_OF_YEAR), yy); } else { result = null; } } return result; } /** * Returns the week following this one. This method will return * {@code null} for some upper limit on the range of weeks (currently * week 53, 9999). For week 52 of any year, the following week is always * week 53, but week 53 may not contain any days (you should check for * this). * No matter what time zone and locale this instance was created with, * the returned instance will use the default calendar for time * calculations, obtained with {@link RegularTimePeriod#getCalendarInstance()}. * * @return The following week (possibly {@code null}). */ @Override public RegularTimePeriod next() { Week result; if (this.week < 52) { result = new Week(this.week + 1, this.year); } else { Calendar calendar = getCalendarInstance(); calendar.set(this.year, Calendar.DECEMBER, 31); int actualMaxWeek = calendar.getActualMaximum(Calendar.WEEK_OF_YEAR); if (this.week < actualMaxWeek) { result = new Week(this.week + 1, this.year); } else { if (this.year < 9999) { result = new Week(FIRST_WEEK_IN_YEAR, this.year + 1); } else { result = null; } } } return result; } /** * Returns a serial index number for the week. * * @return The serial index number. */ @Override public long getSerialIndex() { return this.year * 53L + this.week; } /** * Returns the first millisecond of the week, evaluated using the supplied * calendar (which determines the time zone). * * @param calendar the calendar ({@code null} not permitted). * * @return The first millisecond of the week. * * @throws NullPointerException if {@code calendar} is * {@code null}. */ @Override public long getFirstMillisecond(Calendar calendar) { Calendar c = (Calendar) calendar.clone(); c.clear(); c.set(Calendar.YEAR, this.year); c.set(Calendar.WEEK_OF_YEAR, this.week); c.set(Calendar.DAY_OF_WEEK, c.getFirstDayOfWeek()); c.set(Calendar.HOUR, 0); c.set(Calendar.MINUTE, 0); c.set(Calendar.SECOND, 0); c.set(Calendar.MILLISECOND, 0); return c.getTimeInMillis(); } /** * Returns the last millisecond of the week, evaluated using the supplied * calendar (which determines the time zone). * * @param calendar the calendar ({@code null} not permitted). * * @return The last millisecond of the week. * * @throws NullPointerException if {@code calendar} is * {@code null}. */ @Override public long getLastMillisecond(Calendar calendar) { Calendar c = (Calendar) calendar.clone(); c.clear(); c.set(Calendar.YEAR, this.year); c.set(Calendar.WEEK_OF_YEAR, this.week + 1); c.set(Calendar.DAY_OF_WEEK, c.getFirstDayOfWeek()); c.set(Calendar.HOUR, 0); c.set(Calendar.MINUTE, 0); c.set(Calendar.SECOND, 0); c.set(Calendar.MILLISECOND, 0); return c.getTimeInMillis() - 1; } /** * Returns a string representing the week (e.g. "Week 9, 2002"). * * TODO: look at internationalisation. * * @return A string representing the week. */ @Override public String toString() { return "Week " + this.week + ", " + this.year; } /** * Tests the equality of this Week object to an arbitrary object. Returns * true if the target is a Week instance representing the same week as this * object. In all other cases, returns false. * * @param obj the object ({@code null} permitted). * * @return {@code true} if week and year of this and object are the * same. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof Week)) { return false; } Week that = (Week) obj; if (this.week != that.week) { return false; } if (this.year != that.year) { return false; } return true; } /** * Returns a hash code for this object instance. The approach described by * Joshua Bloch in "Effective Java" has been used here: *

* {@code http://developer.java.sun.com/developer/Books/effectivejava * /Chapter3.pdf} * * @return A hash code. */ @Override public int hashCode() { int result = 17; result = 37 * result + this.week; result = 37 * result + this.year; return result; } /** * Returns an integer indicating the order of this Week object relative to * the specified object: * * negative == before, zero == same, positive == after. * * @param o1 the object to compare. * * @return negative == before, zero == same, positive == after. */ @Override public int compareTo(Object o1) { int result; // CASE 1 : Comparing to another Week object // -------------------------------------------- if (o1 instanceof Week) { Week w = (Week) o1; result = this.year - w.getYear().getYear(); if (result == 0) { result = this.week - w.getWeek(); } } // CASE 2 : Comparing to another TimePeriod object // ----------------------------------------------- else if (o1 instanceof RegularTimePeriod) { // more difficult case - evaluate later... result = 0; } // CASE 3 : Comparing to a non-TimePeriod object // --------------------------------------------- else { // consider time periods to be ordered after general objects result = 1; } return result; } /** * Parses the string argument as a week. *

* This method is required to accept the format "YYYY-Wnn". It will also * accept "Wnn-YYYY". Anything else, at the moment, is a bonus. * * @param s string to parse. * * @return {@code null} if the string is not parseable, the week * otherwise. */ public static Week parseWeek(String s) { Week result = null; if (s != null) { // trim whitespace from either end of the string s = s.trim(); int i = Week.findSeparator(s); if (i != -1) { String s1 = s.substring(0, i).trim(); String s2 = s.substring(i + 1).trim(); Year y = Week.evaluateAsYear(s1); int w; if (y != null) { w = Week.stringToWeek(s2); if (w == -1) { throw new TimePeriodFormatException( "Can't evaluate the week."); } result = new Week(w, y); } else { y = Week.evaluateAsYear(s2); if (y != null) { w = Week.stringToWeek(s1); if (w == -1) { throw new TimePeriodFormatException( "Can't evaluate the week."); } result = new Week(w, y); } else { throw new TimePeriodFormatException( "Can't evaluate the year."); } } } else { throw new TimePeriodFormatException( "Could not find separator."); } } return result; } /** * Finds the first occurrence of ' ', '-', ',' or '.' * * @param s the string to parse. * * @return {@code -1} if none of the characters was found, the * index of the first occurrence otherwise. */ private static int findSeparator(String s) { int result = s.indexOf('-'); if (result == -1) { result = s.indexOf(','); } if (result == -1) { result = s.indexOf(' '); } if (result == -1) { result = s.indexOf('.'); } return result; } /** * Creates a year from a string, or returns null (format exceptions * suppressed). * * @param s string to parse. * * @return {@code null} if the string is not parseable, the year * otherwise. */ private static Year evaluateAsYear(String s) { Year result = null; try { result = Year.parseYear(s); } catch (TimePeriodFormatException e) { // suppress } return result; } /** * Converts a string to a week. * * @param s the string to parse. * @return {@code -1} if the string does not contain a week number, * the number of the week otherwise. */ private static int stringToWeek(String s) { int result = -1; s = s.replace('W', ' '); s = s.trim(); try { result = Integer.parseInt(s); if ((result < 1) || (result > LAST_WEEK_IN_YEAR)) { result = -1; } } catch (NumberFormatException e) { // suppress } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/Year.java000066400000000000000000000301101463604235500253620ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------- * Year.java * --------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import java.io.Serializable; import java.util.Calendar; import java.util.Date; import java.util.Locale; import java.util.TimeZone; /** * Represents a year in the range -9999 to 9999. This class is immutable, * which is a requirement for all {@link RegularTimePeriod} subclasses. */ public class Year extends RegularTimePeriod implements Serializable { /** * The minimum year value. */ public static final int MINIMUM_YEAR = -9999; /** * The maximum year value. */ public static final int MAXIMUM_YEAR = 9999; /** For serialization. */ private static final long serialVersionUID = -7659990929736074836L; /** The year. */ private short year; /** The first millisecond. */ private long firstMillisecond; /** The last millisecond. */ private long lastMillisecond; /** * Creates a new {@code Year}, based on the current system date/time. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. */ public Year() { this(new Date()); } /** * Creates a time period representing a single year. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. * * @param year the year. */ public Year(int year) { if ((year < Year.MINIMUM_YEAR) || (year > Year.MAXIMUM_YEAR)) { throw new IllegalArgumentException( "Year constructor: year (" + year + ") outside valid range."); } this.year = (short) year; peg(getCalendarInstance()); } /** * Creates a new {@code Year}, based on a particular instant in time. * The time zone and locale are determined by the calendar * returned by {@link RegularTimePeriod#getCalendarInstance()}. * * @param time the time ({@code null} not permitted). * * @see #Year(Date, TimeZone, Locale) */ public Year(Date time) { this(time, getCalendarInstance()); } /** * Creates a new {@code Year} instance, for the specified time zone * and locale. * * @param time the current time ({@code null} not permitted). * @param zone the time zone. * @param locale the locale. */ public Year(Date time, TimeZone zone, Locale locale) { Calendar calendar = Calendar.getInstance(zone, locale); calendar.setTime(time); this.year = (short) calendar.get(Calendar.YEAR); peg(calendar); } /** * Constructs a new instance, based on a particular date/time. * The time zone and locale are determined by the {@code calendar} * parameter. * * @param time the date/time ({@code null} not permitted). * @param calendar the calendar to use for calculations ({@code null} not permitted). */ public Year(Date time, Calendar calendar) { calendar.setTime(time); this.year = (short) calendar.get(Calendar.YEAR); peg(calendar); } /** * Returns the year. * * @return The year. */ public int getYear() { return this.year; } /** * Returns the first millisecond of the year. This will be determined * relative to the time zone specified in the constructor, or in the * calendar instance passed in the most recent call to the * {@link #peg(Calendar)} method. * * @return The first millisecond of the year. * * @see #getLastMillisecond() */ @Override public long getFirstMillisecond() { return this.firstMillisecond; } /** * Returns the last millisecond of the year. This will be * determined relative to the time zone specified in the constructor, or * in the calendar instance passed in the most recent call to the * {@link #peg(Calendar)} method. * * @return The last millisecond of the year. * * @see #getFirstMillisecond() */ @Override public long getLastMillisecond() { return this.lastMillisecond; } /** * Recalculates the start date/time and end date/time for this time period * relative to the supplied calendar (which incorporates a time zone). * * @param calendar the calendar ({@code null} not permitted). */ @Override public void peg(Calendar calendar) { this.firstMillisecond = getFirstMillisecond(calendar); this.lastMillisecond = getLastMillisecond(calendar); } /** * Returns the year preceding this one. * No matter what time zone and locale this instance was created with, * the returned instance will use the default calendar for time * calculations, obtained with {@link RegularTimePeriod#getCalendarInstance()}. * * @return The year preceding this one (or {@code null} if the * current year is -9999). */ @Override public RegularTimePeriod previous() { if (this.year > Year.MINIMUM_YEAR) { return new Year(this.year - 1); } else { return null; } } /** * Returns the year following this one. * No matter what time zone and locale this instance was created with, * the returned instance will use the default calendar for time * calculations, obtained with {@link RegularTimePeriod#getCalendarInstance()}. * * @return The year following this one (or {@code null} if the current * year is 9999). */ @Override public RegularTimePeriod next() { if (this.year < Year.MAXIMUM_YEAR) { return new Year(this.year + 1); } else { return null; } } /** * Returns a serial index number for the year. *

* The implementation simply returns the year number (e.g. 2002). * * @return The serial index number. */ @Override public long getSerialIndex() { return this.year; } /** * Returns the first millisecond of the year, evaluated using the supplied * calendar (which determines the time zone). * * @param calendar the calendar ({@code null} not permitted). * * @return The first millisecond of the year. * * @throws NullPointerException if {@code calendar} is * {@code null}. */ @Override public long getFirstMillisecond(Calendar calendar) { calendar.set(this.year, Calendar.JANUARY, 1, 0, 0, 0); calendar.set(Calendar.MILLISECOND, 0); return calendar.getTimeInMillis(); } /** * Returns the last millisecond of the year, evaluated using the supplied * calendar (which determines the time zone). * * @param calendar the calendar ({@code null} not permitted). * * @return The last millisecond of the year. * * @throws NullPointerException if {@code calendar} is * {@code null}. */ @Override public long getLastMillisecond(Calendar calendar) { calendar.set(this.year, Calendar.DECEMBER, 31, 23, 59, 59); calendar.set(Calendar.MILLISECOND, 999); return calendar.getTimeInMillis(); } /** * Tests the equality of this {@code Year} object to an arbitrary * object. Returns {@code true} if the target is a {@code Year} * instance representing the same year as this object. In all other cases, * returns {@code false}. * * @param obj the object ({@code null} permitted). * * @return {@code true} if the year of this and the object are the * same. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof Year)) { return false; } Year that = (Year) obj; return (this.year == that.year); } /** * Returns a hash code for this object instance. The approach described by * Joshua Bloch in "Effective Java" has been used here: *

* {@code http://developer.java.sun.com/developer/Books/effectivejava * /Chapter3.pdf} * * @return A hash code. */ @Override public int hashCode() { int result = 17; int c = this.year; result = 37 * result + c; return result; } /** * Returns an integer indicating the order of this {@code Year} object * relative to the specified object: * * negative == before, zero == same, positive == after. * * @param o1 the object to compare. * * @return negative == before, zero == same, positive == after. */ @Override public int compareTo(Object o1) { int result; // CASE 1 : Comparing to another Year object // ----------------------------------------- if (o1 instanceof Year) { Year y = (Year) o1; result = this.year - y.getYear(); } // CASE 2 : Comparing to another TimePeriod object // ----------------------------------------------- else if (o1 instanceof RegularTimePeriod) { // more difficult case - evaluate later... result = 0; } // CASE 3 : Comparing to a non-TimePeriod object // --------------------------------------------- else { // consider time periods to be ordered after general objects result = 1; } return result; } /** * Returns a string representing the year.. * * @return A string representing the year. */ @Override public String toString() { return Integer.toString(this.year); } /** * Parses the string argument as a year. *

* The string format is YYYY. * * @param s a string representing the year. * * @return {@code null} if the string is not parseable, the year * otherwise. */ public static Year parseYear(String s) { // parse the string... int y; try { y = Integer.parseInt(s.trim()); } catch (NumberFormatException e) { throw new TimePeriodFormatException("Cannot parse string."); } // create the year... try { return new Year(y); } catch (IllegalArgumentException e) { throw new TimePeriodFormatException("Year outside valid range."); } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/ohlc/000077500000000000000000000000001463604235500245515ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/ohlc/OHLC.java000066400000000000000000000101011463604235500261320ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------- * OHLC.java * --------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time.ohlc; import java.io.Serializable; import org.jfree.chart.HashUtils; /** * A data record containing open-high-low-close data (immutable). This class * is used internally by the {@link OHLCItem} class. */ public class OHLC implements Serializable { /** The open value. */ private double open; /** The close value. */ private double close; /** The high value. */ private double high; /** The low value. */ private double low; /** * Creates a new instance of {@code OHLC}. * * @param open the open value. * @param close the close value. * @param high the high value. * @param low the low value. */ public OHLC(double open, double high, double low, double close) { this.open = open; this.close = close; this.high = high; this.low = low; } /** * Returns the open value. * * @return The open value. */ public double getOpen() { return this.open; } /** * Returns the close value. * * @return The close value. */ public double getClose() { return this.close; } /** * Returns the high value. * * @return The high value. */ public double getHigh() { return this.high; } /** * Returns the low value. * * @return The low value. */ public double getLow() { return this.low; } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof OHLC)) { return false; } OHLC that = (OHLC) obj; if (this.open != that.open) { return false; } if (this.close != that.close) { return false; } if (this.high != that.high) { return false; } if (this.low != that.low) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = 193; result = HashUtils.hashCode(result, this.open); result = HashUtils.hashCode(result, this.high); result = HashUtils.hashCode(result, this.low); result = HashUtils.hashCode(result, this.close); return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/ohlc/OHLCItem.java000066400000000000000000000073011463604235500267610ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------- * OHLCItem.java * ------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time.ohlc; import org.jfree.data.ComparableObjectItem; import org.jfree.data.time.RegularTimePeriod; /** * An item representing data in the form {@code (time-period, open, high, low, * close)}. */ public class OHLCItem extends ComparableObjectItem { /** * Creates a new instance of {@code OHLCItem}. * * @param period the time period. * @param open the open-value. * @param high the high-value. * @param low the low-value. * @param close the close-value. */ public OHLCItem(RegularTimePeriod period, double open, double high, double low, double close) { super(period, new OHLC(open, high, low, close)); } /** * Returns the period. * * @return The period (never {@code null}). */ public RegularTimePeriod getPeriod() { return (RegularTimePeriod) getComparable(); } /** * Returns the y-value. * * @return The y-value. */ public double getYValue() { return getCloseValue(); } /** * Returns the open value. * * @return The open value. */ public double getOpenValue() { OHLC ohlc = (OHLC) getObject(); if (ohlc != null) { return ohlc.getOpen(); } else { return Double.NaN; } } /** * Returns the high value. * * @return The high value. */ public double getHighValue() { OHLC ohlc = (OHLC) getObject(); if (ohlc != null) { return ohlc.getHigh(); } else { return Double.NaN; } } /** * Returns the low value. * * @return The low value. */ public double getLowValue() { OHLC ohlc = (OHLC) getObject(); if (ohlc != null) { return ohlc.getLow(); } else { return Double.NaN; } } /** * Returns the close value. * * @return The close value. */ public double getCloseValue() { OHLC ohlc = (OHLC) getObject(); if (ohlc != null) { return ohlc.getClose(); } else { return Double.NaN; } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/ohlc/OHLCSeries.java000066400000000000000000000102471463604235500273200ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * OHLCSeries.java * --------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time.ohlc; import org.jfree.chart.util.Args; import org.jfree.data.ComparableObjectItem; import org.jfree.data.ComparableObjectSeries; import org.jfree.data.time.RegularTimePeriod; /** * A list of ({@link RegularTimePeriod}, open, high, low, close) data items. * * @see OHLCSeriesCollection */ public class OHLCSeries extends ComparableObjectSeries { /** * Creates a new empty series. By default, items added to the series will * be sorted into ascending order by period, and duplicate periods will * not be allowed. * * @param key the series key ({@code null} not permitted). */ public OHLCSeries(Comparable key) { super(key, true, false); } /** * Returns the time period for the specified item. * * @param index the item index. * * @return The time period. */ public RegularTimePeriod getPeriod(int index) { OHLCItem item = (OHLCItem) getDataItem(index); return item.getPeriod(); } /** * Returns the data item at the specified index. * * @param index the item index. * * @return The data item. */ @Override public ComparableObjectItem getDataItem(int index) { return super.getDataItem(index); } /** * Adds a data item to the series. * * @param period the period. * @param open the open-value. * @param high the high-value. * @param low the low-value. * @param close the close-value. */ public void add(RegularTimePeriod period, double open, double high, double low, double close) { if (getItemCount() > 0) { OHLCItem item0 = (OHLCItem) this.getDataItem(0); if (!period.getClass().equals(item0.getPeriod().getClass())) { throw new IllegalArgumentException( "Can't mix RegularTimePeriod class types."); } } super.add(new OHLCItem(period, open, high, low, close), true); } /** * Adds a data item to the series. The values from the item passed to * this method will be copied into a new object. * * @param item the item ({@code null} not permitted). */ public void add(OHLCItem item) { Args.nullNotPermitted(item, "item"); add(item.getPeriod(), item.getOpenValue(), item.getHighValue(), item.getLowValue(), item.getCloseValue()); } /** * Removes the item with the specified index. * * @param index the item index. * * @return The item removed. */ @Override public ComparableObjectItem remove(int index) { return super.remove(index); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/ohlc/OHLCSeriesCollection.java000066400000000000000000000337251463604235500313420ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * OHLCSeriesCollection.java * ------------------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time.ohlc; import java.io.Serializable; import java.util.List; import java.util.Objects; import org.jfree.chart.HashUtils; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.Args; import org.jfree.data.general.DatasetChangeEvent; import org.jfree.data.time.RegularTimePeriod; import org.jfree.data.time.TimePeriodAnchor; import org.jfree.data.xy.AbstractXYDataset; import org.jfree.data.xy.OHLCDataset; import org.jfree.data.xy.XYDataset; /** * A collection of {@link OHLCSeries} objects. * * @see OHLCSeries */ public class OHLCSeriesCollection extends AbstractXYDataset implements OHLCDataset, Serializable { /** Storage for the data series. */ private List data; private TimePeriodAnchor xPosition = TimePeriodAnchor.MIDDLE; /** * Creates a new instance of {@code OHLCSeriesCollection}. */ public OHLCSeriesCollection() { this.data = new java.util.ArrayList(); } /** * Returns the position within each time period that is used for the X * value when the collection is used as an {@link XYDataset}. * * @return The anchor position (never {@code null}). */ public TimePeriodAnchor getXPosition() { return this.xPosition; } /** * Sets the position within each time period that is used for the X values * when the collection is used as an {@link XYDataset}, then sends a * {@link DatasetChangeEvent} is sent to all registered listeners. * * @param anchor the anchor position ({@code null} not permitted). */ public void setXPosition(TimePeriodAnchor anchor) { Args.nullNotPermitted(anchor, "anchor"); this.xPosition = anchor; notifyListeners(new DatasetChangeEvent(this, this)); } /** * Adds a series to the collection and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param series the series ({@code null} not permitted). */ public void addSeries(OHLCSeries series) { Args.nullNotPermitted(series, "series"); this.data.add(series); series.addChangeListener(this); fireDatasetChanged(); } /** * Returns the number of series in the collection. * * @return The series count. */ @Override public int getSeriesCount() { return this.data.size(); } /** * Returns a series from the collection. * * @param series the series index (zero-based). * * @return The series. * * @throws IllegalArgumentException if {@code series} is not in the * range {@code 0} to {@code getSeriesCount() - 1}. */ public OHLCSeries getSeries(int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds"); } return (OHLCSeries) this.data.get(series); } /** * Returns the key for a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * * @return The key for a series. * * @throws IllegalArgumentException if {@code series} is not in the * specified range. */ @Override public Comparable getSeriesKey(int series) { // defer argument checking return getSeries(series).getKey(); } /** * Returns the number of items in the specified series. * * @param series the series (zero-based index). * * @return The item count. * * @throws IllegalArgumentException if {@code series} is not in the * range {@code 0} to {@code getSeriesCount() - 1}. */ @Override public int getItemCount(int series) { // defer argument checking return getSeries(series).getItemCount(); } /** * Returns the x-value for a time period. * * @param period the time period ({@code null} not permitted). * * @return The x-value. */ protected synchronized long getX(RegularTimePeriod period) { long result = 0L; if (this.xPosition == TimePeriodAnchor.START) { result = period.getFirstMillisecond(); } else if (this.xPosition == TimePeriodAnchor.MIDDLE) { result = period.getMiddleMillisecond(); } else if (this.xPosition == TimePeriodAnchor.END) { result = period.getLastMillisecond(); } return result; } /** * Returns the x-value for an item within a series. * * @param series the series index. * @param item the item index. * * @return The x-value. */ @Override public double getXValue(int series, int item) { OHLCSeries s = (OHLCSeries) this.data.get(series); OHLCItem di = (OHLCItem) s.getDataItem(item); RegularTimePeriod period = di.getPeriod(); return getX(period); } /** * Returns the x-value for an item within a series. * * @param series the series index. * @param item the item index. * * @return The x-value. */ @Override public Number getX(int series, int item) { return getXValue(series, item); } /** * Returns the y-value for an item within a series. * * @param series the series index. * @param item the item index. * * @return The y-value. */ @Override public Number getY(int series, int item) { OHLCSeries s = (OHLCSeries) this.data.get(series); OHLCItem di = (OHLCItem) s.getDataItem(item); return di.getYValue(); } /** * Returns the open-value for an item within a series. * * @param series the series index. * @param item the item index. * * @return The open-value. */ @Override public double getOpenValue(int series, int item) { OHLCSeries s = (OHLCSeries) this.data.get(series); OHLCItem di = (OHLCItem) s.getDataItem(item); return di.getOpenValue(); } /** * Returns the open-value for an item within a series. * * @param series the series index. * @param item the item index. * * @return The open-value. */ @Override public Number getOpen(int series, int item) { return getOpenValue(series, item); } /** * Returns the close-value for an item within a series. * * @param series the series index. * @param item the item index. * * @return The close-value. */ @Override public double getCloseValue(int series, int item) { OHLCSeries s = (OHLCSeries) this.data.get(series); OHLCItem di = (OHLCItem) s.getDataItem(item); return di.getCloseValue(); } /** * Returns the close-value for an item within a series. * * @param series the series index. * @param item the item index. * * @return The close-value. */ @Override public Number getClose(int series, int item) { return getCloseValue(series, item); } /** * Returns the high-value for an item within a series. * * @param series the series index. * @param item the item index. * * @return The high-value. */ @Override public double getHighValue(int series, int item) { OHLCSeries s = (OHLCSeries) this.data.get(series); OHLCItem di = (OHLCItem) s.getDataItem(item); return di.getHighValue(); } /** * Returns the high-value for an item within a series. * * @param series the series index. * @param item the item index. * * @return The high-value. */ @Override public Number getHigh(int series, int item) { return getHighValue(series, item); } /** * Returns the low-value for an item within a series. * * @param series the series index. * @param item the item index. * * @return The low-value. */ @Override public double getLowValue(int series, int item) { OHLCSeries s = (OHLCSeries) this.data.get(series); OHLCItem di = (OHLCItem) s.getDataItem(item); return di.getLowValue(); } /** * Returns the low-value for an item within a series. * * @param series the series index. * @param item the item index. * * @return The low-value. */ @Override public Number getLow(int series, int item) { return getLowValue(series, item); } /** * Returns {@code null} always, because this dataset doesn't record * any volume data. * * @param series the series index (ignored). * @param item the item index (ignored). * * @return {@code null}. */ @Override public Number getVolume(int series, int item) { return null; } /** * Returns {@code Double.NaN} always, because this dataset doesn't * record any volume data. * * @param series the series index (ignored). * @param item the item index (ignored). * * @return {@code Double.NaN}. */ @Override public double getVolumeValue(int series, int item) { return Double.NaN; } /** * Removes the series with the specified index and sends a * {@link DatasetChangeEvent} to all registered listeners. * * @param index the series index. */ public void removeSeries(int index) { OHLCSeries series = getSeries(index); if (series != null) { removeSeries(series); } } /** * Removes the specified series from the dataset and sends a * {@link DatasetChangeEvent} to all registered listeners. * * @param series the series ({@code null} not permitted). * * @return {@code true} if the series was removed, and * {@code false} otherwise. */ public boolean removeSeries(OHLCSeries series) { Args.nullNotPermitted(series, "series"); boolean removed = this.data.remove(series); if (removed) { series.removeChangeListener(this); fireDatasetChanged(); } return removed; } /** * Removes all the series from the collection and sends a * {@link DatasetChangeEvent} to all registered listeners. */ public void removeAllSeries() { if (this.data.isEmpty()) { return; // nothing to do } // deregister the collection as a change listener to each series in the // collection for (int i = 0; i < this.data.size(); i++) { OHLCSeries series = (OHLCSeries) this.data.get(i); series.removeChangeListener(this); } // remove all the series from the collection and notify listeners. this.data.clear(); fireDatasetChanged(); } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof OHLCSeriesCollection)) { return false; } OHLCSeriesCollection that = (OHLCSeriesCollection) obj; if (!this.xPosition.equals(that.xPosition)) { return false; } return Objects.equals(this.data, that.data); } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = 137; result = HashUtils.hashCode(result, this.xPosition); for (int i = 0; i < this.data.size(); i++) { result = HashUtils.hashCode(result, this.data.get(i)); } return result; } /** * Returns a clone of this instance. * * @return A clone. * * @throws CloneNotSupportedException if there is a problem. */ @Override public Object clone() throws CloneNotSupportedException { OHLCSeriesCollection clone = (OHLCSeriesCollection) super.clone(); clone.data = (List) ObjectUtils.deepClone(this.data); return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/ohlc/package.html000066400000000000000000000002611463604235500270310ustar00rootroot00000000000000 Classes for representing financial data in open-high-low-close form. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/time/package.html000066400000000000000000000002321463604235500261020ustar00rootroot00000000000000 Interfaces and classes for time-related data. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xml/000077500000000000000000000000001463604235500234665ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xml/CategoryDatasetHandler.java000066400000000000000000000104231463604235500307120ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * CategoryDatasetHandler.java * --------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xml; import org.jfree.data.category.CategoryDataset; import org.jfree.data.category.DefaultCategoryDataset; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; /** * A SAX handler for reading a {@link CategoryDataset} from an XML file. */ public class CategoryDatasetHandler extends RootHandler implements DatasetTags { /** The dataset under construction. */ private DefaultCategoryDataset dataset; /** * Creates a new handler. */ public CategoryDatasetHandler() { this.dataset = null; } /** * Returns the dataset. * * @return The dataset. */ public CategoryDataset getDataset() { return this.dataset; } /** * Adds an item to the dataset. * * @param rowKey the row key. * @param columnKey the column key. * @param value the value. */ public void addItem(Comparable rowKey, Comparable columnKey, Number value) { this.dataset.addValue(value, rowKey, columnKey); } /** * The start of an element. * * @param namespaceURI the namespace. * @param localName the element name. * @param qName the element name. * @param atts the element attributes. * * @throws SAXException for errors. */ @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { DefaultHandler current = getCurrentHandler(); if (current != this) { current.startElement(namespaceURI, localName, qName, atts); } else if (qName.equals(CATEGORYDATASET_TAG)) { this.dataset = new DefaultCategoryDataset(); } else if (qName.equals(SERIES_TAG)) { CategorySeriesHandler subhandler = new CategorySeriesHandler(this); getSubHandlers().push(subhandler); subhandler.startElement(namespaceURI, localName, qName, atts); } else { throw new SAXException("Element not recognised: " + qName); } } /** * The end of an element. * * @param namespaceURI the namespace. * @param localName the element name. * @param qName the element name. * * @throws SAXException for errors. */ @Override public void endElement(String namespaceURI, String localName, String qName) throws SAXException { DefaultHandler current = getCurrentHandler(); if (current != this) { current.endElement(namespaceURI, localName, qName); } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xml/CategorySeriesHandler.java000066400000000000000000000112041463604235500305550ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * CategorySeriesHandler.java * -------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -;; * */ package org.jfree.data.xml; import java.util.Iterator; import org.jfree.data.DefaultKeyedValues; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; /** * A handler for reading a series for a category dataset. */ public class CategorySeriesHandler extends DefaultHandler implements DatasetTags { /** The root handler. */ private final RootHandler root; /** The series key. */ private Comparable seriesKey; /** The values. */ private final DefaultKeyedValues values; /** * Creates a new item handler. * * @param root the root handler. */ public CategorySeriesHandler(RootHandler root) { this.root = root; this.values = new DefaultKeyedValues(); } /** * Sets the series key. * * @param key the key. */ public void setSeriesKey(Comparable key) { this.seriesKey = key; } /** * Adds an item to the temporary storage for the series. * * @param key the key. * @param value the value. */ public void addItem(Comparable key, Number value) { this.values.addValue(key, value); } /** * The start of an element. * * @param namespaceURI the namespace. * @param localName the element name. * @param qName the element name. * @param atts the attributes. * * @throws SAXException for errors. */ @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { if (qName.equals(SERIES_TAG)) { setSeriesKey(atts.getValue("name")); ItemHandler subhandler = new ItemHandler(this.root, this); this.root.pushSubHandler(subhandler); } else if (qName.equals(ITEM_TAG)) { ItemHandler subhandler = new ItemHandler(this.root, this); this.root.pushSubHandler(subhandler); subhandler.startElement(namespaceURI, localName, qName, atts); } else { throw new SAXException( "Expecting or tag...found " + qName ); } } /** * The end of an element. * * @param namespaceURI the namespace. * @param localName the element name. * @param qName the element name. */ @Override public void endElement(String namespaceURI, String localName, String qName) { if (this.root instanceof CategoryDatasetHandler) { CategoryDatasetHandler handler = (CategoryDatasetHandler) this.root; Iterator iterator = this.values.getKeys().iterator(); while (iterator.hasNext()) { Comparable key = (Comparable) iterator.next(); Number value = this.values.getValue(key); handler.addItem(this.seriesKey, key, value); } this.root.popSubHandler(); } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xml/DatasetReader.java000066400000000000000000000134621463604235500270470ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * DatasetReader.java * ------------------ * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xml; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import javax.xml.XMLConstants; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.jfree.chart.util.Args; import org.jfree.data.category.CategoryDataset; import org.jfree.data.general.PieDataset; import org.xml.sax.SAXException; import org.xml.sax.SAXNotRecognizedException; import org.xml.sax.SAXNotSupportedException; /** * A utility class for reading datasets from XML. */ public class DatasetReader { /** A factory for creating new parser instances. */ static SAXParserFactory factory; /** * Returns the {@link SAXParserFactory} used to create {@link SAXParser} instances. * * @return The {@link SAXParserFactory} (never {@code null}). */ public static SAXParserFactory getSAXParserFactory() { if (factory == null) { SAXParserFactory f = SAXParserFactory.newInstance(); try { f.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); f.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); f.setFeature("http://xml.org/sax/features/external-general-entities", false); factory = f; } catch (SAXNotRecognizedException | SAXNotSupportedException | ParserConfigurationException e) { throw new RuntimeException(e); } } return factory; } /** * Sets the SAXParserFactory that will be used to create SAXParser instances. * You would only call this method if you wish to configure a new factory because * the default does not meet requirements. * * @param f the new factory ({@code null} not permitted). */ public static void setSAXParserFactory(SAXParserFactory f) { Args.nullNotPermitted(f, "f"); factory = f; } /** * Reads a {@link PieDataset} from an XML file. * * @param file the file ({@code null} not permitted). * * @return A dataset. * * @throws IOException if there is a problem reading the file. */ public static PieDataset readPieDatasetFromXML(File file) throws IOException { InputStream in = new FileInputStream(file); return readPieDatasetFromXML(in); } /** * Reads a {@link PieDataset} from a stream. * * @param in the input stream. * * @return A dataset. * * @throws IOException if there is an I/O error. */ public static PieDataset readPieDatasetFromXML(InputStream in) throws IOException { PieDataset result = null; try { SAXParser parser = getSAXParserFactory().newSAXParser(); PieDatasetHandler handler = new PieDatasetHandler(); parser.parse(in, handler); result = handler.getDataset(); } catch (SAXException | ParserConfigurationException e) { throw new RuntimeException(e); } return result; } /** * Reads a {@link CategoryDataset} from a file. * * @param file the file. * * @return A dataset. * * @throws IOException if there is a problem reading the file. */ public static CategoryDataset readCategoryDatasetFromXML(File file) throws IOException { InputStream in = new FileInputStream(file); return readCategoryDatasetFromXML(in); } /** * Reads a {@link CategoryDataset} from a stream. * * @param in the stream. * * @return A dataset. * * @throws IOException if there is a problem reading the file. */ public static CategoryDataset readCategoryDatasetFromXML(InputStream in) throws IOException { CategoryDataset result = null; try { SAXParser parser = getSAXParserFactory().newSAXParser(); CategoryDatasetHandler handler = new CategoryDatasetHandler(); parser.parse(in, handler); result = handler.getDataset(); } catch (SAXException | ParserConfigurationException e) { throw new RuntimeException(e); } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xml/DatasetTags.java000066400000000000000000000040351463604235500265370ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * DatasetTags.java * ---------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xml; /** * Constants for the tags that identify the elements in the XML files. */ public interface DatasetTags { /** The 'PieDataset' element name. */ String PIEDATASET_TAG = "PieDataset"; /** The 'CategoryDataset' element name. */ String CATEGORYDATASET_TAG = "CategoryDataset"; /** The 'Series' element name. */ String SERIES_TAG = "Series"; /** The 'Item' element name. */ String ITEM_TAG = "Item"; /** The 'Key' element name. */ String KEY_TAG = "Key"; /** The 'Value' element name. */ String VALUE_TAG = "Value"; } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xml/ItemHandler.java000066400000000000000000000114331463604235500265270ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * ItemHandler.java * ---------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xml; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; /** * A handler for reading key-value items. */ public class ItemHandler extends DefaultHandler implements DatasetTags { /** The root handler. */ private RootHandler root; /** The parent handler (can be the same as root, but not always). */ private DefaultHandler parent; /** The key. */ private Comparable key; /** The value. */ private Number value; /** * Creates a new item handler. * * @param root the root handler. * @param parent the parent handler. */ public ItemHandler(RootHandler root, DefaultHandler parent) { this.root = root; this.parent = parent; this.key = null; this.value = null; } /** * Returns the key that has been read by the handler, or {@code null}. * * @return The key. */ public Comparable getKey() { return this.key; } /** * Sets the key. * * @param key the key. */ public void setKey(Comparable key) { this.key = key; } /** * Returns the key that has been read by the handler, or {@code null}. * * @return The value. */ public Number getValue() { return this.value; } /** * Sets the value. * * @param value the value. */ public void setValue(Number value) { this.value = value; } /** * The start of an element. * * @param namespaceURI the namespace. * @param localName the element name. * @param qName the element name. * @param atts the attributes. * * @throws SAXException for errors. */ @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { if (qName.equals(ITEM_TAG)) { KeyHandler subhandler = new KeyHandler(this.root, this); this.root.pushSubHandler(subhandler); } else if (qName.equals(VALUE_TAG)) { ValueHandler subhandler = new ValueHandler(this.root, this); this.root.pushSubHandler(subhandler); } else { throw new SAXException( "Expected or ...found " + qName ); } } /** * The end of an element. * * @param namespaceURI the namespace. * @param localName the element name. * @param qName the element name. */ @Override public void endElement(String namespaceURI, String localName, String qName) { if (this.parent instanceof PieDatasetHandler) { PieDatasetHandler handler = (PieDatasetHandler) this.parent; handler.addItem(this.key, this.value); this.root.popSubHandler(); } else if (this.parent instanceof CategorySeriesHandler) { CategorySeriesHandler handler = (CategorySeriesHandler) this.parent; handler.addItem(this.key, this.value); this.root.popSubHandler(); } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xml/KeyHandler.java000066400000000000000000000112201463604235500263530ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * KeyHandler.java * --------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xml; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; /** * A SAX handler for reading a key. */ public class KeyHandler extends DefaultHandler implements DatasetTags { /** The root handler. */ private RootHandler rootHandler; /** The item handler. */ private ItemHandler itemHandler; /** Storage for the current CDATA */ private StringBuffer currentText; /** The key. */ //private Comparable key; /** * Creates a new handler. * * @param rootHandler the root handler. * @param itemHandler the item handler. */ public KeyHandler(RootHandler rootHandler, ItemHandler itemHandler) { this.rootHandler = rootHandler; this.itemHandler = itemHandler; this.currentText = new StringBuffer(); //this.key = null; } /** * The start of an element. * * @param namespaceURI the namespace. * @param localName the element name. * @param qName the element name. * @param atts the attributes. * * @throws SAXException for errors. */ @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { if (qName.equals(KEY_TAG)) { clearCurrentText(); } else { throw new SAXException("Expecting but found " + qName); } } /** * The end of an element. * * @param namespaceURI the namespace. * @param localName the element name. * @param qName the element name. * * @throws SAXException for errors. */ @Override public void endElement(String namespaceURI, String localName, String qName) throws SAXException { if (qName.equals(KEY_TAG)) { this.itemHandler.setKey(getCurrentText()); this.rootHandler.popSubHandler(); this.rootHandler.pushSubHandler( new ValueHandler(this.rootHandler, this.itemHandler) ); } else { throw new SAXException("Expecting but found " + qName); } } /** * Receives some (or all) of the text in the current element. * * @param ch character buffer. * @param start the start index. * @param length the length of the valid character data. */ @Override public void characters(char[] ch, int start, int length) { if (this.currentText != null) { this.currentText.append(String.copyValueOf(ch, start, length)); } } /** * Returns the current text of the textbuffer. * * @return The current text. */ protected String getCurrentText() { return this.currentText.toString(); } /** * Removes all text from the textbuffer at the end of a CDATA section. */ protected void clearCurrentText() { this.currentText.delete(0, this.currentText.length()); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xml/PieDatasetHandler.java000066400000000000000000000100301463604235500276440ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * PieDatasetHandler.java * ---------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xml; import org.jfree.data.general.DefaultPieDataset; import org.jfree.data.general.PieDataset; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; /** * A SAX handler for reading a {@link PieDataset} from an XML file. */ public class PieDatasetHandler extends RootHandler implements DatasetTags { /** The pie dataset under construction. */ private DefaultPieDataset dataset; /** * Default constructor. */ public PieDatasetHandler() { this.dataset = null; } /** * Returns the dataset. * * @return The dataset. */ public PieDataset getDataset() { return this.dataset; } /** * Adds an item to the dataset under construction. * * @param key the key. * @param value the value. */ public void addItem(Comparable key, Number value) { this.dataset.setValue(key, value); } /** * Starts an element. * * @param namespaceURI the namespace. * @param localName the element name. * @param qName the element name. * @param atts the element attributes. * * @throws SAXException for errors. */ @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { DefaultHandler current = getCurrentHandler(); if (current != this) { current.startElement(namespaceURI, localName, qName, atts); } else if (qName.equals(PIEDATASET_TAG)) { this.dataset = new DefaultPieDataset(); } else if (qName.equals(ITEM_TAG)) { ItemHandler subhandler = new ItemHandler(this, this); getSubHandlers().push(subhandler); subhandler.startElement(namespaceURI, localName, qName, atts); } } /** * The end of an element. * * @param namespaceURI the namespace. * @param localName the element name. * @param qName the element name. * * @throws SAXException for errors. */ @Override public void endElement(String namespaceURI, String localName, String qName) throws SAXException { DefaultHandler current = getCurrentHandler(); if (current != this) { current.endElement(namespaceURI, localName, qName); } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xml/RootHandler.java000066400000000000000000000070271463604235500265600ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * RootHandler.java * ---------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xml; import java.util.Stack; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; /** * A SAX handler that delegates work to sub-handlers. */ public class RootHandler extends DefaultHandler implements DatasetTags { /** The sub-handlers. */ private Stack subHandlers; /** * Creates a new handler. */ public RootHandler() { this.subHandlers = new Stack(); } /** * Returns the stack of sub handlers. * * @return The sub-handler stack. */ public Stack getSubHandlers() { return this.subHandlers; } /** * Receives some (or all) of the text in the current element. * * @param ch character buffer. * @param start the start index. * @param length the length of the valid character data. * * @throws SAXException for errors. */ @Override public void characters(char[] ch, int start, int length) throws SAXException { DefaultHandler handler = getCurrentHandler(); if (handler != this) { handler.characters(ch, start, length); } } /** * Returns the handler at the top of the stack. * * @return The handler. */ public DefaultHandler getCurrentHandler() { DefaultHandler result = this; if (this.subHandlers != null) { if (this.subHandlers.size() > 0) { Object top = this.subHandlers.peek(); if (top != null) { result = (DefaultHandler) top; } } } return result; } /** * Pushes a sub-handler onto the stack. * * @param subhandler the sub-handler. */ public void pushSubHandler(DefaultHandler subhandler) { this.subHandlers.push(subhandler); } /** * Pops a sub-handler from the stack. * * @return The sub-handler. */ public DefaultHandler popSubHandler() { return (DefaultHandler) this.subHandlers.pop(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xml/ValueHandler.java000066400000000000000000000114771463604235500267150ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * ValueHandler.java * ----------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Luke Quinane; * */ package org.jfree.data.xml; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; /** * A handler for reading a 'Value' element. */ public class ValueHandler extends DefaultHandler implements DatasetTags { /** The root handler. */ private RootHandler rootHandler; /** The item handler. */ private ItemHandler itemHandler; /** Storage for the current CDATA */ private StringBuffer currentText; /** * Creates a new value handler. * * @param rootHandler the root handler. * @param itemHandler the item handler. */ public ValueHandler(RootHandler rootHandler, ItemHandler itemHandler) { this.rootHandler = rootHandler; this.itemHandler = itemHandler; this.currentText = new StringBuffer(); } /** * The start of an element. * * @param namespaceURI the namespace. * @param localName the element name. * @param qName the element name. * @param atts the attributes. * * @throws SAXException for errors. */ @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { if (qName.equals(VALUE_TAG)) { // no attributes to read clearCurrentText(); } else { throw new SAXException("Expecting but found " + qName); } } /** * The end of an element. * * @param namespaceURI the namespace. * @param localName the element name. * @param qName the element name. * * @throws SAXException for errors. */ @Override public void endElement(String namespaceURI, String localName, String qName) throws SAXException { if (qName.equals(VALUE_TAG)) { Double value; try { value = Double.valueOf(this.currentText.toString()); if (value.isNaN()) { value = null; } } catch (NumberFormatException e1) { value = null; } this.itemHandler.setValue(value); this.rootHandler.popSubHandler(); } else { throw new SAXException("Expecting but found " + qName); } } /** * Receives some (or all) of the text in the current element. * * @param ch character buffer. * @param start the start index. * @param length the length of the valid character data. */ @Override public void characters(char[] ch, int start, int length) { if (this.currentText != null) { this.currentText.append(String.copyValueOf(ch, start, length)); } } /** * Returns the current text of the textbuffer. * * @return The current text. */ protected String getCurrentText() { return this.currentText.toString(); } /** * Removes all text from the textbuffer at the end of a CDATA section. */ protected void clearCurrentText() { this.currentText.delete(0, this.currentText.length()); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xml/package.html000066400000000000000000000002311463604235500257430ustar00rootroot00000000000000 Support for reading datasets from XML files. jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/000077500000000000000000000000001463604235500233265ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/AbstractIntervalXYDataset.java000066400000000000000000000073721463604235500312410ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------ * AbstractIntervalXYDataset.java * ------------------------------ * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert. * Contributor(s): -; * */ package org.jfree.data.xy; /** * An base class that you can use to create new implementations of the * {@link IntervalXYDataset} interface. */ public abstract class AbstractIntervalXYDataset extends AbstractXYDataset implements IntervalXYDataset { /** * Returns the start x-value (as a double primitive) for an item within a * series. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public double getStartXValue(int series, int item) { double result = Double.NaN; Number x = getStartX(series, item); if (x != null) { result = x.doubleValue(); } return result; } /** * Returns the end x-value (as a double primitive) for an item within a * series. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public double getEndXValue(int series, int item) { double result = Double.NaN; Number x = getEndX(series, item); if (x != null) { result = x.doubleValue(); } return result; } /** * Returns the start y-value (as a double primitive) for an item within a * series. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public double getStartYValue(int series, int item) { double result = Double.NaN; Number y = getStartY(series, item); if (y != null) { result = y.doubleValue(); } return result; } /** * Returns the end y-value (as a double primitive) for an item within a * series. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The value. */ @Override public double getEndYValue(int series, int item) { double result = Double.NaN; Number y = getEndY(series, item); if (y != null) { result = y.doubleValue(); } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/AbstractXYDataset.java000066400000000000000000000057011463604235500275260ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * AbstractXYDataset.java * ---------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert. * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.data.DomainOrder; import org.jfree.data.general.AbstractSeriesDataset; /** * An base class that you can use to create new implementations of the * {@link XYDataset} interface. */ public abstract class AbstractXYDataset extends AbstractSeriesDataset implements XYDataset { /** * Returns the order of the domain (X) values. * * @return The domain order. */ @Override public DomainOrder getDomainOrder() { return DomainOrder.NONE; } /** * Returns the x-value (as a double primitive) for an item within a series. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public double getXValue(int series, int item) { double result = Double.NaN; Number x = getX(series, item); if (x != null) { result = x.doubleValue(); } return result; } /** * Returns the y-value (as a double primitive) for an item within a series. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public double getYValue(int series, int item) { double result = Double.NaN; Number y = getY(series, item); if (y != null) { result = y.doubleValue(); } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/AbstractXYZDataset.java000066400000000000000000000042401463604235500276550ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * AbstractXYZDataset.java * ----------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert. * Contributor(s): -; * */ package org.jfree.data.xy; /** * An base class that you can use to create new implementations of the * {@link XYZDataset} interface. */ public abstract class AbstractXYZDataset extends AbstractXYDataset implements XYZDataset { /** * Returns the z-value (as a double primitive) for an item within a series. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The z-value. */ @Override public double getZValue(int series, int item) { double result = Double.NaN; Number z = getZ(series, item); if (z != null) { result = z.doubleValue(); } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/CategoryTableXYDataset.java000066400000000000000000000306401463604235500305100ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * CategoryTableXYDataset.java * --------------------------- * (C) Copyright 2004-present, by Andreas Schroeder and Contributors. * * Original Author: Andreas Schroeder; * Contributor(s): David Gilbert; * */ package org.jfree.data.xy; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.DefaultKeyedValues2D; import org.jfree.data.DomainInfo; import org.jfree.data.Range; import org.jfree.data.general.DatasetChangeEvent; import org.jfree.data.general.DatasetUtils; /** * An implementation variant of the {@link TableXYDataset} where every series * shares the same x-values (required for generating stacked area charts). * This implementation uses a {@link DefaultKeyedValues2D} Object as backend * implementation and is hence more "category oriented" than the {@link * DefaultTableXYDataset} implementation. *

* This implementation provides no means to remove data items yet. * This is due to the lack of such facility in the DefaultKeyedValues2D class. *

* This class also implements the {@link IntervalXYDataset} interface, but this * implementation is provisional. */ public class CategoryTableXYDataset extends AbstractIntervalXYDataset implements TableXYDataset, IntervalXYDataset, DomainInfo, PublicCloneable { /** * The backing data structure. */ private DefaultKeyedValues2D values; /** A delegate for controlling the interval width. */ private IntervalXYDelegate intervalDelegate; /** * Creates a new empty CategoryTableXYDataset. */ public CategoryTableXYDataset() { this.values = new DefaultKeyedValues2D(true); this.intervalDelegate = new IntervalXYDelegate(this); addChangeListener(this.intervalDelegate); } /** * Adds a data item to this dataset and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param x the x value. * @param y the y value. * @param seriesName the name of the series to add the data item. */ public void add(double x, double y, String seriesName) { add(x, y, seriesName, true); } /** * Adds a data item to this dataset and, if requested, sends a * {@link DatasetChangeEvent} to all registered listeners. * * @param x the x value. * @param y the y value. * @param seriesName the name of the series to add the data item. * @param notify notify listeners? */ public void add(Number x, Number y, String seriesName, boolean notify) { this.values.addValue(y, (Comparable) x, seriesName); if (notify) { fireDatasetChanged(); } } /** * Removes a value from the dataset. * * @param x the x-value. * @param seriesName the series name. */ public void remove(double x, String seriesName) { remove(x, seriesName, true); } /** * Removes an item from the dataset. * * @param x the x-value. * @param seriesName the series name. * @param notify notify listeners? */ public void remove(Number x, String seriesName, boolean notify) { this.values.removeValue((Comparable) x, seriesName); if (notify) { fireDatasetChanged(); } } /** * Clears all data from the dataset and sends a {@link DatasetChangeEvent} * to all registered listeners. */ public void clear() { this.values.clear(); fireDatasetChanged(); } /** * Returns the number of series in the collection. * * @return The series count. */ @Override public int getSeriesCount() { return this.values.getColumnCount(); } /** * Returns the key for a series. * * @param series the series index (zero-based). * * @return The key for a series. */ @Override public Comparable getSeriesKey(int series) { return this.values.getColumnKey(series); } /** * Returns the number of x values in the dataset. * * @return The item count. */ @Override public int getItemCount() { return this.values.getRowCount(); } /** * Returns the number of items in the specified series. * Returns the same as {@link CategoryTableXYDataset#getItemCount()}. * * @param series the series index (zero-based). * * @return The item count. */ @Override public int getItemCount(int series) { return getItemCount(); // all series have the same number of items in // this dataset } /** * Returns the x-value for the specified series and item. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public Number getX(int series, int item) { return (Number) this.values.getRowKey(item); } /** * Returns the starting X value for the specified series and item. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The starting X value. */ @Override public Number getStartX(int series, int item) { return this.intervalDelegate.getStartX(series, item); } /** * Returns the ending X value for the specified series and item. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The ending X value. */ @Override public Number getEndX(int series, int item) { return this.intervalDelegate.getEndX(series, item); } /** * Returns the y-value for the specified series and item. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The y value (possibly {@code null}). */ @Override public Number getY(int series, int item) { return this.values.getValue(item, series); } /** * Returns the starting Y value for the specified series and item. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The starting Y value. */ @Override public Number getStartY(int series, int item) { return getY(series, item); } /** * Returns the ending Y value for the specified series and item. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The ending Y value. */ @Override public Number getEndY(int series, int item) { return getY(series, item); } /** * Returns the minimum x-value in the dataset. * * @param includeInterval a flag that determines whether or not the * x-interval is taken into account. * * @return The minimum value. */ @Override public double getDomainLowerBound(boolean includeInterval) { return this.intervalDelegate.getDomainLowerBound(includeInterval); } /** * Returns the maximum x-value in the dataset. * * @param includeInterval a flag that determines whether or not the * x-interval is taken into account. * * @return The maximum value. */ @Override public double getDomainUpperBound(boolean includeInterval) { return this.intervalDelegate.getDomainUpperBound(includeInterval); } /** * Returns the range of the values in this dataset's domain. * * @param includeInterval a flag that determines whether or not the * x-interval is taken into account. * * @return The range. */ @Override public Range getDomainBounds(boolean includeInterval) { if (includeInterval) { return this.intervalDelegate.getDomainBounds(includeInterval); } else { return DatasetUtils.iterateDomainBounds(this, includeInterval); } } /** * Returns the interval position factor. * * @return The interval position factor. */ public double getIntervalPositionFactor() { return this.intervalDelegate.getIntervalPositionFactor(); } /** * Sets the interval position factor. Must be between 0.0 and 1.0 inclusive. * If the factor is 0.5, the gap is in the middle of the x values. If it * is lesser than 0.5, the gap is farther to the left and if greater than * 0.5 it gets farther to the right. * * @param d the new interval position factor. */ public void setIntervalPositionFactor(double d) { this.intervalDelegate.setIntervalPositionFactor(d); fireDatasetChanged(); } /** * Returns the full interval width. * * @return The interval width to use. */ public double getIntervalWidth() { return this.intervalDelegate.getIntervalWidth(); } /** * Sets the interval width to a fixed value, and sends a * {@link DatasetChangeEvent} to all registered listeners. * * @param d the new interval width (must be > 0). */ public void setIntervalWidth(double d) { this.intervalDelegate.setFixedIntervalWidth(d); fireDatasetChanged(); } /** * Returns whether the interval width is automatically calculated or not. * * @return whether the width is automatically calculated or not. */ public boolean isAutoWidth() { return this.intervalDelegate.isAutoWidth(); } /** * Sets the flag that indicates whether the interval width is automatically * calculated or not. * * @param b the flag. */ public void setAutoWidth(boolean b) { this.intervalDelegate.setAutoWidth(b); fireDatasetChanged(); } /** * Tests this dataset for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (!(obj instanceof CategoryTableXYDataset)) { return false; } CategoryTableXYDataset that = (CategoryTableXYDataset) obj; if (!this.intervalDelegate.equals(that.intervalDelegate)) { return false; } if (!this.values.equals(that.values)) { return false; } return true; } /** * Returns an independent copy of this dataset. * * @return A clone. * * @throws CloneNotSupportedException if there is some reason that cloning * cannot be performed. */ @Override public Object clone() throws CloneNotSupportedException { CategoryTableXYDataset clone = (CategoryTableXYDataset) super.clone(); clone.values = (DefaultKeyedValues2D) this.values.clone(); clone.intervalDelegate = new IntervalXYDelegate(clone); // need to configure the intervalDelegate to match the original clone.intervalDelegate.setFixedIntervalWidth(getIntervalWidth()); clone.intervalDelegate.setAutoWidth(isAutoWidth()); clone.intervalDelegate.setIntervalPositionFactor( getIntervalPositionFactor()); return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/DefaultHighLowDataset.java000066400000000000000000000310741463604235500303520ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * DefaultHighLowDataset.java * -------------------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import java.util.Arrays; import java.util.Date; import java.util.Objects; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; /** * A simple implementation of the {@link OHLCDataset} interface. See also * the {@link DefaultOHLCDataset} class, which provides another implementation * that is very similar. */ public class DefaultHighLowDataset extends AbstractXYDataset implements OHLCDataset, PublicCloneable { /** The series key. */ private final Comparable seriesKey; /** Storage for the dates. */ private Date[] date; /** Storage for the high values. */ private Number[] high; /** Storage for the low values. */ private Number[] low; /** Storage for the open values. */ private Number[] open; /** Storage for the close values. */ private Number[] close; /** Storage for the volume values. */ private Number[] volume; /** * Constructs a new high/low/open/close dataset. *

* The current implementation allows only one series in the dataset. * This may be extended in a future version. * * @param seriesKey the key for the series ({@code null} not * permitted). * @param date the dates ({@code null} not permitted). * @param high the high values ({@code null} not permitted). * @param low the low values ({@code null} not permitted). * @param open the open values ({@code null} not permitted). * @param close the close values ({@code null} not permitted). * @param volume the volume values ({@code null} not permitted). */ public DefaultHighLowDataset(Comparable seriesKey, Date[] date, double[] high, double[] low, double[] open, double[] close, double[] volume) { Args.nullNotPermitted(seriesKey, "seriesKey"); Args.nullNotPermitted(date, "date"); this.seriesKey = seriesKey; this.date = date; this.high = createNumberArray(high); this.low = createNumberArray(low); this.open = createNumberArray(open); this.close = createNumberArray(close); this.volume = createNumberArray(volume); } /** * Returns the key for the series stored in this dataset. * * @param series the index of the series (ignored, this dataset supports * only one series and this method always returns the key for series 0). * * @return The series key (never {@code null}). */ @Override public Comparable getSeriesKey(int series) { return this.seriesKey; } /** * Returns the x-value for one item in a series. The value returned is a * {@code Long} instance generated from the underlying * {@code Date} object. To avoid generating a new object instance, * you might prefer to call {@link #getXValue(int, int)}. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The x-value. * * @see #getXValue(int, int) * @see #getXDate(int, int) */ @Override public Number getX(int series, int item) { return this.date[item].getTime(); } /** * Returns the x-value for one item in a series, as a Date. *

* This method is provided for convenience only. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The x-value as a Date. * * @see #getX(int, int) */ public Date getXDate(int series, int item) { return this.date[item]; } /** * Returns the y-value for one item in a series. *

* This method (from the {@link XYDataset} interface) is mapped to the * {@link #getCloseValue(int, int)} method. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The y-value. * * @see #getYValue(int, int) */ @Override public Number getY(int series, int item) { return getClose(series, item); } /** * Returns the high-value for one item in a series. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The high-value. * * @see #getHighValue(int, int) */ @Override public Number getHigh(int series, int item) { return this.high[item]; } /** * Returns the high-value (as a double primitive) for an item within a * series. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The high-value. * * @see #getHigh(int, int) */ @Override public double getHighValue(int series, int item) { double result = Double.NaN; Number h = getHigh(series, item); if (h != null) { result = h.doubleValue(); } return result; } /** * Returns the low-value for one item in a series. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The low-value. * * @see #getLowValue(int, int) */ @Override public Number getLow(int series, int item) { return this.low[item]; } /** * Returns the low-value (as a double primitive) for an item within a * series. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The low-value. * * @see #getLow(int, int) */ @Override public double getLowValue(int series, int item) { double result = Double.NaN; Number l = getLow(series, item); if (l != null) { result = l.doubleValue(); } return result; } /** * Returns the open-value for one item in a series. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The open-value. * * @see #getOpenValue(int, int) */ @Override public Number getOpen(int series, int item) { return this.open[item]; } /** * Returns the open-value (as a double primitive) for an item within a * series. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The open-value. * * @see #getOpen(int, int) */ @Override public double getOpenValue(int series, int item) { double result = Double.NaN; Number open = getOpen(series, item); if (open != null) { result = open.doubleValue(); } return result; } /** * Returns the close-value for one item in a series. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The close-value. * * @see #getCloseValue(int, int) */ @Override public Number getClose(int series, int item) { return this.close[item]; } /** * Returns the close-value (as a double primitive) for an item within a * series. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The close-value. * * @see #getClose(int, int) */ @Override public double getCloseValue(int series, int item) { double result = Double.NaN; Number c = getClose(series, item); if (c != null) { result = c.doubleValue(); } return result; } /** * Returns the volume-value for one item in a series. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The volume-value. * * @see #getVolumeValue(int, int) */ @Override public Number getVolume(int series, int item) { return this.volume[item]; } /** * Returns the volume-value (as a double primitive) for an item within a * series. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The volume-value. * * @see #getVolume(int, int) */ @Override public double getVolumeValue(int series, int item) { double result = Double.NaN; Number v = getVolume(series, item); if (v != null) { result = v.doubleValue(); } return result; } /** * Returns the number of series in the dataset. *

* This implementation only allows one series. * * @return The number of series. */ @Override public int getSeriesCount() { return 1; } /** * Returns the number of items in the specified series. * * @param series the index (zero-based) of the series. * * @return The number of items in the specified series. */ @Override public int getItemCount(int series) { return this.date.length; } /** * Tests this dataset for equality with an arbitrary instance. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof DefaultHighLowDataset)) { return false; } DefaultHighLowDataset that = (DefaultHighLowDataset) obj; if (!this.seriesKey.equals(that.seriesKey)) { return false; } if (!Arrays.equals(this.date, that.date)) { return false; } if (!Arrays.equals(this.open, that.open)) { return false; } if (!Arrays.equals(this.high, that.high)) { return false; } if (!Arrays.equals(this.low, that.low)) { return false; } if (!Arrays.equals(this.close, that.close)) { return false; } if (!Arrays.equals(this.volume, that.volume)) { return false; } return true; } @Override public int hashCode() { int hash = 5; hash = 67 * hash + Objects.hashCode(this.seriesKey); return hash; } /** * Constructs an array of Number objects from an array of doubles. * * @param data the double values to convert ({@code null} not * permitted). * * @return The data as an array of Number objects. */ public static Number[] createNumberArray(double[] data) { Number[] result = new Number[data.length]; for (int i = 0; i < data.length; i++) { result[i] = data[i]; } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/DefaultIntervalXYDataset.java000066400000000000000000000463441463604235500310640ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------- * DefaultIntervalXYDataset.java * ----------------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.general.DatasetChangeEvent; /** * A dataset that defines a range (interval) for both the x-values and the * y-values. This implementation uses six arrays to store the x, start-x, * end-x, y, start-y and end-y values. *

* An alternative implementation of the {@link IntervalXYDataset} interface * is provided by the {@link XYIntervalSeriesCollection} class. */ public class DefaultIntervalXYDataset extends AbstractIntervalXYDataset implements PublicCloneable { /** * Storage for the series keys. This list must be kept in sync with the * seriesList. */ private List seriesKeys; /** * Storage for the series in the dataset. We use a list because the * order of the series is significant. This list must be kept in sync * with the seriesKeys list. */ private List seriesList; /** * Creates a new {@code DefaultIntervalXYDataset} instance, initially * containing no data. */ public DefaultIntervalXYDataset() { this.seriesKeys = new java.util.ArrayList(); this.seriesList = new java.util.ArrayList(); } /** * Returns the number of series in the dataset. * * @return The series count. */ @Override public int getSeriesCount() { return this.seriesList.size(); } /** * Returns the key for a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * * @return The key for the series. * * @throws IllegalArgumentException if {@code series} is not in the * specified range. */ @Override public Comparable getSeriesKey(int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds"); } return (Comparable) this.seriesKeys.get(series); } /** * Returns the number of items in the specified series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * * @return The item count. * * @throws IllegalArgumentException if {@code series} is not in the * specified range. */ @Override public int getItemCount(int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds"); } double[][] seriesArray = (double[][]) this.seriesList.get(series); return seriesArray[0].length; } /** * Returns the x-value for an item within a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item index (in the range {@code 0} to * {@code getItemCount(series)}). * * @return The x-value. * * @throws ArrayIndexOutOfBoundsException if {@code series} is not * within the specified range. * @throws ArrayIndexOutOfBoundsException if {@code item} is not * within the specified range. * * @see #getX(int, int) */ @Override public double getXValue(int series, int item) { double[][] seriesData = (double[][]) this.seriesList.get(series); return seriesData[0][item]; } /** * Returns the y-value for an item within a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item index (in the range {@code 0} to * {@code getItemCount(series)}). * * @return The y-value. * * @throws ArrayIndexOutOfBoundsException if {@code series} is not * within the specified range. * @throws ArrayIndexOutOfBoundsException if {@code item} is not * within the specified range. * * @see #getY(int, int) */ @Override public double getYValue(int series, int item) { double[][] seriesData = (double[][]) this.seriesList.get(series); return seriesData[3][item]; } /** * Returns the starting x-value for an item within a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item index (in the range {@code 0} to * {@code getItemCount(series)}). * * @return The starting x-value. * * @throws ArrayIndexOutOfBoundsException if {@code series} is not * within the specified range. * @throws ArrayIndexOutOfBoundsException if {@code item} is not * within the specified range. * * @see #getStartX(int, int) */ @Override public double getStartXValue(int series, int item) { double[][] seriesData = (double[][]) this.seriesList.get(series); return seriesData[1][item]; } /** * Returns the ending x-value for an item within a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item index (in the range {@code 0} to * {@code getItemCount(series)}). * * @return The ending x-value. * * @throws ArrayIndexOutOfBoundsException if {@code series} is not * within the specified range. * @throws ArrayIndexOutOfBoundsException if {@code item} is not * within the specified range. * * @see #getEndX(int, int) */ @Override public double getEndXValue(int series, int item) { double[][] seriesData = (double[][]) this.seriesList.get(series); return seriesData[2][item]; } /** * Returns the starting y-value for an item within a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item index (in the range {@code 0} to * {@code getItemCount(series)}). * * @return The starting y-value. * * @throws ArrayIndexOutOfBoundsException if {@code series} is not * within the specified range. * @throws ArrayIndexOutOfBoundsException if {@code item} is not * within the specified range. * * @see #getStartY(int, int) */ @Override public double getStartYValue(int series, int item) { double[][] seriesData = (double[][]) this.seriesList.get(series); return seriesData[4][item]; } /** * Returns the ending y-value for an item within a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item index (in the range {@code 0} to * {@code getItemCount(series)}). * * @return The ending y-value. * * @throws ArrayIndexOutOfBoundsException if {@code series} is not * within the specified range. * @throws ArrayIndexOutOfBoundsException if {@code item} is not * within the specified range. * * @see #getEndY(int, int) */ @Override public double getEndYValue(int series, int item) { double[][] seriesData = (double[][]) this.seriesList.get(series); return seriesData[5][item]; } /** * Returns the ending x-value for an item within a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item index (in the range {@code 0} to * {@code getItemCount(series)}). * * @return The ending x-value. * * @throws ArrayIndexOutOfBoundsException if {@code series} is not * within the specified range. * @throws ArrayIndexOutOfBoundsException if {@code item} is not * within the specified range. * * @see #getEndXValue(int, int) */ @Override public Number getEndX(int series, int item) { return getEndXValue(series, item); } /** * Returns the ending y-value for an item within a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item index (in the range {@code 0} to * {@code getItemCount(series)}). * * @return The ending y-value. * * @throws ArrayIndexOutOfBoundsException if {@code series} is not * within the specified range. * @throws ArrayIndexOutOfBoundsException if {@code item} is not * within the specified range. * * @see #getEndYValue(int, int) */ @Override public Number getEndY(int series, int item) { return getEndYValue(series, item); } /** * Returns the starting x-value for an item within a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item index (in the range {@code 0} to * {@code getItemCount(series)}). * * @return The starting x-value. * * @throws ArrayIndexOutOfBoundsException if {@code series} is not * within the specified range. * @throws ArrayIndexOutOfBoundsException if {@code item} is not * within the specified range. * * @see #getStartXValue(int, int) */ @Override public Number getStartX(int series, int item) { return getStartXValue(series, item); } /** * Returns the starting y-value for an item within a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item index (in the range {@code 0} to * {@code getItemCount(series)}). * * @return The starting y-value. * * @throws ArrayIndexOutOfBoundsException if {@code series} is not * within the specified range. * @throws ArrayIndexOutOfBoundsException if {@code item} is not * within the specified range. * * @see #getStartYValue(int, int) */ @Override public Number getStartY(int series, int item) { return getStartYValue(series, item); } /** * Returns the x-value for an item within a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item index (in the range {@code 0} to * {@code getItemCount(series)}). * * @return The x-value. * * @throws ArrayIndexOutOfBoundsException if {@code series} is not * within the specified range. * @throws ArrayIndexOutOfBoundsException if {@code item} is not * within the specified range. * * @see #getXValue(int, int) */ @Override public Number getX(int series, int item) { return getXValue(series, item); } /** * Returns the y-value for an item within a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item index (in the range {@code 0} to * {@code getItemCount(series)}). * * @return The y-value. * * @throws ArrayIndexOutOfBoundsException if {@code series} is not * within the specified range. * @throws ArrayIndexOutOfBoundsException if {@code item} is not * within the specified range. * * @see #getYValue(int, int) */ @Override public Number getY(int series, int item) { return getYValue(series, item); } /** * Adds a series or if a series with the same key already exists replaces * the data for that series, then sends a {@link DatasetChangeEvent} to * all registered listeners. * * @param seriesKey the series key ({@code null} not permitted). * @param data the data (must be an array with length 6, containing six * arrays of equal length, the first three containing the x-values * (x, xLow and xHigh) and the last three containing the y-values * (y, yLow and yHigh)). */ public void addSeries(Comparable seriesKey, double[][] data) { if (seriesKey == null) { throw new IllegalArgumentException( "The 'seriesKey' cannot be null."); } if (data == null) { throw new IllegalArgumentException("The 'data' is null."); } if (data.length != 6) { throw new IllegalArgumentException( "The 'data' array must have length == 6."); } int length = data[0].length; if (length != data[1].length || length != data[2].length || length != data[3].length || length != data[4].length || length != data[5].length) { throw new IllegalArgumentException( "The 'data' array must contain six arrays with equal length."); } int seriesIndex = indexOf(seriesKey); if (seriesIndex == -1) { // add a new series this.seriesKeys.add(seriesKey); this.seriesList.add(data); } else { // replace an existing series this.seriesList.remove(seriesIndex); this.seriesList.add(seriesIndex, data); } notifyListeners(new DatasetChangeEvent(this, this)); } /** * Tests this {@code DefaultIntervalXYDataset} instance for equality * with an arbitrary object. This method returns {@code true} if and * only if: *

    *
  • {@code obj} is not {@code null};
  • *
  • {@code obj} is an instance of {@code DefaultIntervalXYDataset};
  • *
  • both datasets have the same number of series, each containing * exactly the same values.
  • *
* * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof DefaultIntervalXYDataset)) { return false; } DefaultIntervalXYDataset that = (DefaultIntervalXYDataset) obj; if (!this.seriesKeys.equals(that.seriesKeys)) { return false; } for (int i = 0; i < this.seriesList.size(); i++) { double[][] d1 = (double[][]) this.seriesList.get(i); double[][] d2 = (double[][]) that.seriesList.get(i); double[] d1x = d1[0]; double[] d2x = d2[0]; if (!Arrays.equals(d1x, d2x)) { return false; } double[] d1xs = d1[1]; double[] d2xs = d2[1]; if (!Arrays.equals(d1xs, d2xs)) { return false; } double[] d1xe = d1[2]; double[] d2xe = d2[2]; if (!Arrays.equals(d1xe, d2xe)) { return false; } double[] d1y = d1[3]; double[] d2y = d2[3]; if (!Arrays.equals(d1y, d2y)) { return false; } double[] d1ys = d1[4]; double[] d2ys = d2[4]; if (!Arrays.equals(d1ys, d2ys)) { return false; } double[] d1ye = d1[5]; double[] d2ye = d2[5]; if (!Arrays.equals(d1ye, d2ye)) { return false; } } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result; result = this.seriesKeys.hashCode(); result = 29 * result + this.seriesList.hashCode(); return result; } /** * Returns a clone of this dataset. * * @return A clone. * * @throws CloneNotSupportedException if the dataset contains a series with * a key that cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { DefaultIntervalXYDataset clone = (DefaultIntervalXYDataset) super.clone(); clone.seriesKeys = new java.util.ArrayList(this.seriesKeys); clone.seriesList = new ArrayList(this.seriesList.size()); for (int i = 0; i < this.seriesList.size(); i++) { double[][] data = (double[][]) this.seriesList.get(i); double[] x = data[0]; double[] xStart = data[1]; double[] xEnd = data[2]; double[] y = data[3]; double[] yStart = data[4]; double[] yEnd = data[5]; double[] xx = new double[x.length]; double[] xxStart = new double[xStart.length]; double[] xxEnd = new double[xEnd.length]; double[] yy = new double[y.length]; double[] yyStart = new double[yStart.length]; double[] yyEnd = new double[yEnd.length]; System.arraycopy(x, 0, xx, 0, x.length); System.arraycopy(xStart, 0, xxStart, 0, xStart.length); System.arraycopy(xEnd, 0, xxEnd, 0, xEnd.length); System.arraycopy(y, 0, yy, 0, y.length); System.arraycopy(yStart, 0, yyStart, 0, yStart.length); System.arraycopy(yEnd, 0, yyEnd, 0, yEnd.length); clone.seriesList.add(i, new double[][] {xx, xxStart, xxEnd, yy, yyStart, yyEnd}); } return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/DefaultOHLCDataset.java000066400000000000000000000223471463604235500275410ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * DefaultOHLCDataset.java * ----------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import java.util.Arrays; import java.util.Date; import org.jfree.chart.util.PublicCloneable; /** * A simple implementation of the {@link OHLCDataset} interface. This * implementation supports only one series. */ public class DefaultOHLCDataset extends AbstractXYDataset implements OHLCDataset, PublicCloneable { /** The series key. */ private Comparable key; /** Storage for the data items. */ private OHLCDataItem[] data; /** * Creates a new dataset. * * @param key the series key. * @param data the data items. */ public DefaultOHLCDataset(Comparable key, OHLCDataItem[] data) { this.key = key; this.data = data; } /** * Returns the series key. * * @param series the series index (ignored). * * @return The series key. */ @Override public Comparable getSeriesKey(int series) { return this.key; } /** * Returns the x-value for a data item. * * @param series the series index (ignored). * @param item the item index (zero-based). * * @return The x-value. */ @Override public Number getX(int series, int item) { return this.data[item].getDate().getTime(); } /** * Returns the x-value for a data item as a date. * * @param series the series index (ignored). * @param item the item index (zero-based). * * @return The x-value as a date. */ public Date getXDate(int series, int item) { return this.data[item].getDate(); } /** * Returns the y-value. * * @param series the series index (ignored). * @param item the item index (zero-based). * * @return The y value. */ @Override public Number getY(int series, int item) { return getClose(series, item); } /** * Returns the high value. * * @param series the series index (ignored). * @param item the item index (zero-based). * * @return The high value. */ @Override public Number getHigh(int series, int item) { return this.data[item].getHigh(); } /** * Returns the high-value (as a double primitive) for an item within a * series. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The high-value. */ @Override public double getHighValue(int series, int item) { double result = Double.NaN; Number high = getHigh(series, item); if (high != null) { result = high.doubleValue(); } return result; } /** * Returns the low value. * * @param series the series index (ignored). * @param item the item index (zero-based). * * @return The low value. */ @Override public Number getLow(int series, int item) { return this.data[item].getLow(); } /** * Returns the low-value (as a double primitive) for an item within a * series. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The low-value. */ @Override public double getLowValue(int series, int item) { double result = Double.NaN; Number low = getLow(series, item); if (low != null) { result = low.doubleValue(); } return result; } /** * Returns the open value. * * @param series the series index (ignored). * @param item the item index (zero-based). * * @return The open value. */ @Override public Number getOpen(int series, int item) { return this.data[item].getOpen(); } /** * Returns the open-value (as a double primitive) for an item within a * series. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The open-value. */ @Override public double getOpenValue(int series, int item) { double result = Double.NaN; Number open = getOpen(series, item); if (open != null) { result = open.doubleValue(); } return result; } /** * Returns the close value. * * @param series the series index (ignored). * @param item the item index (zero-based). * * @return The close value. */ @Override public Number getClose(int series, int item) { return this.data[item].getClose(); } /** * Returns the close-value (as a double primitive) for an item within a * series. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The close-value. */ @Override public double getCloseValue(int series, int item) { double result = Double.NaN; Number close = getClose(series, item); if (close != null) { result = close.doubleValue(); } return result; } /** * Returns the trading volume. * * @param series the series index (ignored). * @param item the item index (zero-based). * * @return The trading volume. */ @Override public Number getVolume(int series, int item) { return this.data[item].getVolume(); } /** * Returns the volume-value (as a double primitive) for an item within a * series. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The volume-value. */ @Override public double getVolumeValue(int series, int item) { double result = Double.NaN; Number volume = getVolume(series, item); if (volume != null) { result = volume.doubleValue(); } return result; } /** * Returns the series count. * * @return 1. */ @Override public int getSeriesCount() { return 1; } /** * Returns the item count for the specified series. * * @param series the series index (ignored). * * @return The item count. */ @Override public int getItemCount(int series) { return this.data.length; } /** * Sorts the data into ascending order by date. */ public void sortDataByDate() { Arrays.sort(this.data); } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof DefaultOHLCDataset)) { return false; } DefaultOHLCDataset that = (DefaultOHLCDataset) obj; if (!this.key.equals(that.key)) { return false; } if (!Arrays.equals(this.data, that.data)) { return false; } return true; } /** * Returns an independent copy of this dataset. * * @return A clone. * * @throws CloneNotSupportedException if there is a cloning problem. */ @Override public Object clone() throws CloneNotSupportedException { DefaultOHLCDataset clone = (DefaultOHLCDataset) super.clone(); clone.data = new OHLCDataItem[this.data.length]; System.arraycopy(this.data, 0, clone.data, 0, this.data.length); return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/DefaultTableXYDataset.java000066400000000000000000000477361463604235500303350ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * DefaultTableXYDataset.java * -------------------------- * (C) Copyright 2003-present, by Richard Atkinson and Contributors. * * Original Author: Richard Atkinson; * Contributor(s): Jody Brownell; * David Gilbert; * Andreas Schroeder; * */ package org.jfree.data.xy; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Objects; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.DomainInfo; import org.jfree.data.Range; import org.jfree.data.general.DatasetChangeEvent; import org.jfree.data.general.DatasetUtils; import org.jfree.data.general.SeriesChangeEvent; /** * An {@link XYDataset} where every series shares the same x-values (required * for generating stacked area charts). */ public class DefaultTableXYDataset extends AbstractIntervalXYDataset implements TableXYDataset, IntervalXYDataset, DomainInfo, PublicCloneable { /** * Storage for the data - this list will contain zero, one or many * XYSeries objects. */ private List data = null; /** Storage for the x values. */ private HashSet xPoints = null; /** A flag that controls whether or not events are propogated. */ private boolean propagateEvents = true; /** A flag that controls auto pruning. */ private boolean autoPrune = false; /** The delegate used to control the interval width. */ private IntervalXYDelegate intervalDelegate; /** * Creates a new empty dataset. */ public DefaultTableXYDataset() { this(false); } /** * Creates a new empty dataset. * * @param autoPrune a flag that controls whether or not x-values are * removed whenever the corresponding y-values are all * {@code null}. */ public DefaultTableXYDataset(boolean autoPrune) { this.autoPrune = autoPrune; this.data = new ArrayList(); this.xPoints = new HashSet(); this.intervalDelegate = new IntervalXYDelegate(this, false); addChangeListener(this.intervalDelegate); } /** * Returns the flag that controls whether or not x-values are removed from * the dataset when the corresponding y-values are all {@code null}. * * @return A boolean. */ public boolean isAutoPrune() { return this.autoPrune; } /** * Adds a series to the collection and sends a {@link DatasetChangeEvent} * to all registered listeners. The series should be configured to NOT * allow duplicate x-values. * * @param series the series ({@code null} not permitted). */ public void addSeries(XYSeries series) { Args.nullNotPermitted(series, "series"); if (series.getAllowDuplicateXValues()) { throw new IllegalArgumentException( "Cannot accept XYSeries that allow duplicate values. " + "Use XYSeries(seriesName, , false) constructor." ); } updateXPoints(series); this.data.add(series); series.addChangeListener(this); fireDatasetChanged(); } /** * Adds any unique x-values from 'series' to the dataset, and also adds any * x-values that are in the dataset but not in 'series' to the series. * * @param series the series ({@code null} not permitted). */ private void updateXPoints(XYSeries series) { Args.nullNotPermitted(series, "series"); HashSet seriesXPoints = new HashSet(); boolean savedState = this.propagateEvents; this.propagateEvents = false; for (int itemNo = 0; itemNo < series.getItemCount(); itemNo++) { Number xValue = series.getX(itemNo); seriesXPoints.add(xValue); if (!this.xPoints.contains(xValue)) { this.xPoints.add(xValue); int seriesCount = this.data.size(); for (int seriesNo = 0; seriesNo < seriesCount; seriesNo++) { XYSeries dataSeries = (XYSeries) this.data.get(seriesNo); if (!dataSeries.equals(series)) { dataSeries.add(xValue, null); } } } } Iterator iterator = this.xPoints.iterator(); while (iterator.hasNext()) { Number xPoint = (Number) iterator.next(); if (!seriesXPoints.contains(xPoint)) { series.add(xPoint, null); } } this.propagateEvents = savedState; } /** * Updates the x-values for all the series in the dataset. */ public void updateXPoints() { this.propagateEvents = false; for (int s = 0; s < this.data.size(); s++) { updateXPoints((XYSeries) this.data.get(s)); } if (this.autoPrune) { prune(); } this.propagateEvents = true; } /** * Returns the number of series in the collection. * * @return The series count. */ @Override public int getSeriesCount() { return this.data.size(); } /** * Returns the number of x values in the dataset. * * @return The number of x values in the dataset. */ @Override public int getItemCount() { if (this.xPoints == null) { return 0; } else { return this.xPoints.size(); } } /** * Returns a series. * * @param series the series (zero-based index). * * @return The series (never {@code null}). */ public XYSeries getSeries(int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Index outside valid range."); } return (XYSeries) this.data.get(series); } /** * Returns the key for a series. * * @param series the series (zero-based index). * * @return The key for a series. */ @Override public Comparable getSeriesKey(int series) { // check arguments...delegated return getSeries(series).getKey(); } /** * Returns the number of items in the specified series. * * @param series the series (zero-based index). * * @return The number of items in the specified series. */ @Override public int getItemCount(int series) { // check arguments...delegated return getSeries(series).getItemCount(); } /** * Returns the x-value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The x-value for the specified series and item. */ @Override public Number getX(int series, int item) { XYSeries s = (XYSeries) this.data.get(series); return s.getX(item); } /** * Returns the starting X value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The starting X value. */ @Override public Number getStartX(int series, int item) { return this.intervalDelegate.getStartX(series, item); } /** * Returns the ending X value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The ending X value. */ @Override public Number getEndX(int series, int item) { return this.intervalDelegate.getEndX(series, item); } /** * Returns the y-value for the specified series and item. * * @param series the series (zero-based index). * @param index the index of the item of interest (zero-based). * * @return The y-value for the specified series and item (possibly * {@code null}). */ @Override public Number getY(int series, int index) { XYSeries s = (XYSeries) this.data.get(series); return s.getY(index); } /** * Returns the starting Y value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The starting Y value. */ @Override public Number getStartY(int series, int item) { return getY(series, item); } /** * Returns the ending Y value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The ending Y value. */ @Override public Number getEndY(int series, int item) { return getY(series, item); } /** * Removes all the series from the collection and sends a * {@link DatasetChangeEvent} to all registered listeners. */ public void removeAllSeries() { // Unregister the collection as a change listener to each series in // the collection. for (int i = 0; i < this.data.size(); i++) { XYSeries series = (XYSeries) this.data.get(i); series.removeChangeListener(this); } // Remove all the series from the collection and notify listeners. this.data.clear(); this.xPoints.clear(); fireDatasetChanged(); } /** * Removes a series from the collection and sends a * {@link DatasetChangeEvent} to all registered listeners. * * @param series the series ({@code null} not permitted). */ public void removeSeries(XYSeries series) { Args.nullNotPermitted(series, "series"); if (this.data.contains(series)) { series.removeChangeListener(this); this.data.remove(series); if (this.data.isEmpty()) { this.xPoints.clear(); } fireDatasetChanged(); } } /** * Removes a series from the collection and sends a * {@link DatasetChangeEvent} to all registered listeners. * * @param series the series (zero based index). */ public void removeSeries(int series) { // check arguments... if ((series < 0) || (series > getSeriesCount())) { throw new IllegalArgumentException("Index outside valid range."); } // fetch the series, remove the change listener, then remove the series. XYSeries s = (XYSeries) this.data.get(series); s.removeChangeListener(this); this.data.remove(series); if (this.data.isEmpty()) { this.xPoints.clear(); } else if (this.autoPrune) { prune(); } fireDatasetChanged(); } /** * Removes the items from all series for a given x value. * * @param x the x-value. */ public void removeAllValuesForX(Number x) { Args.nullNotPermitted(x, "x"); boolean savedState = this.propagateEvents; this.propagateEvents = false; for (int s = 0; s < this.data.size(); s++) { XYSeries series = (XYSeries) this.data.get(s); series.remove(x); } this.propagateEvents = savedState; this.xPoints.remove(x); fireDatasetChanged(); } /** * Returns {@code true} if all the y-values for the specified x-value * are {@code null} and {@code false} otherwise. * * @param x the x-value. * * @return A boolean. */ protected boolean canPrune(Number x) { for (int s = 0; s < this.data.size(); s++) { XYSeries series = (XYSeries) this.data.get(s); if (series.getY(series.indexOf(x)) != null) { return false; } } return true; } /** * Removes all x-values for which all the y-values are {@code null}. */ public void prune() { HashSet hs = (HashSet) this.xPoints.clone(); Iterator iterator = hs.iterator(); while (iterator.hasNext()) { Number x = (Number) iterator.next(); if (canPrune(x)) { removeAllValuesForX(x); } } } /** * This method receives notification when a series belonging to the dataset * changes. It responds by updating the x-points for the entire dataset * and sending a {@link DatasetChangeEvent} to all registered listeners. * * @param event information about the change. */ @Override public void seriesChanged(SeriesChangeEvent event) { if (this.propagateEvents) { updateXPoints(); fireDatasetChanged(); } } /** * Tests this collection for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof DefaultTableXYDataset)) { return false; } DefaultTableXYDataset that = (DefaultTableXYDataset) obj; if (this.autoPrune != that.autoPrune) { return false; } if (this.propagateEvents != that.propagateEvents) { return false; } if (!this.intervalDelegate.equals(that.intervalDelegate)) { return false; } if (!Objects.equals(this.data, that.data)) { return false; } return true; } /** * Returns a hash code. * * @return A hash code. */ @Override public int hashCode() { int result; result = (this.data != null ? this.data.hashCode() : 0); result = 29 * result + (this.xPoints != null ? this.xPoints.hashCode() : 0); result = 29 * result + (this.propagateEvents ? 1 : 0); result = 29 * result + (this.autoPrune ? 1 : 0); return result; } /** * Returns an independent copy of this dataset. * * @return A clone. * * @throws CloneNotSupportedException if there is some reason that cloning * cannot be performed. */ @Override public Object clone() throws CloneNotSupportedException { DefaultTableXYDataset clone = (DefaultTableXYDataset) super.clone(); int seriesCount = this.data.size(); clone.data = new java.util.ArrayList(seriesCount); for (int i = 0; i < seriesCount; i++) { XYSeries series = (XYSeries) this.data.get(i); clone.data.add(series.clone()); } clone.intervalDelegate = new IntervalXYDelegate(clone); // need to configure the intervalDelegate to match the original clone.intervalDelegate.setFixedIntervalWidth(getIntervalWidth()); clone.intervalDelegate.setAutoWidth(isAutoWidth()); clone.intervalDelegate.setIntervalPositionFactor( getIntervalPositionFactor()); clone.updateXPoints(); return clone; } /** * Returns the minimum x-value in the dataset. * * @param includeInterval a flag that determines whether or not the * x-interval is taken into account. * * @return The minimum value. */ @Override public double getDomainLowerBound(boolean includeInterval) { return this.intervalDelegate.getDomainLowerBound(includeInterval); } /** * Returns the maximum x-value in the dataset. * * @param includeInterval a flag that determines whether or not the * x-interval is taken into account. * * @return The maximum value. */ @Override public double getDomainUpperBound(boolean includeInterval) { return this.intervalDelegate.getDomainUpperBound(includeInterval); } /** * Returns the range of the values in this dataset's domain. * * @param includeInterval a flag that determines whether or not the * x-interval is taken into account. * * @return The range. */ @Override public Range getDomainBounds(boolean includeInterval) { if (includeInterval) { return this.intervalDelegate.getDomainBounds(includeInterval); } else { return DatasetUtils.iterateDomainBounds(this, includeInterval); } } /** * Returns the interval position factor. * * @return The interval position factor. */ public double getIntervalPositionFactor() { return this.intervalDelegate.getIntervalPositionFactor(); } /** * Sets the interval position factor. Must be between 0.0 and 1.0 inclusive. * If the factor is 0.5, the gap is in the middle of the x values. If it * is lesser than 0.5, the gap is farther to the left and if greater than * 0.5 it gets farther to the right. * * @param d the new interval position factor. */ public void setIntervalPositionFactor(double d) { this.intervalDelegate.setIntervalPositionFactor(d); fireDatasetChanged(); } /** * returns the full interval width. * * @return The interval width to use. */ public double getIntervalWidth() { return this.intervalDelegate.getIntervalWidth(); } /** * Sets the interval width to a fixed value, and sends a * {@link DatasetChangeEvent} to all registered listeners. * * @param d the new interval width (must be > 0). */ public void setIntervalWidth(double d) { this.intervalDelegate.setFixedIntervalWidth(d); fireDatasetChanged(); } /** * Returns whether the interval width is automatically calculated or not. * * @return A flag that determines whether or not the interval width is * automatically calculated. */ public boolean isAutoWidth() { return this.intervalDelegate.isAutoWidth(); } /** * Sets the flag that indicates whether the interval width is automatically * calculated or not. * * @param b a boolean. */ public void setAutoWidth(boolean b) { this.intervalDelegate.setAutoWidth(b); fireDatasetChanged(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/DefaultWindDataset.java000066400000000000000000000332311463604235500277070ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * DefaultWindDataset.java * ----------------------- * (C) Copyright 2001-present, by Achilleus Mantzios and Contributors. * * Original Author: Achilleus Mantzios; * Contributor(s): David Gilbert; * */ package org.jfree.data.xy; import java.io.Serializable; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.List; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; /** * A default implementation of the {@link WindDataset} interface. */ public class DefaultWindDataset extends AbstractXYDataset implements WindDataset, PublicCloneable { /** The keys for the series. */ private List seriesKeys; /** Storage for the series data. */ private List allSeriesData; /** * Constructs a new, empty, dataset. Since there are currently no methods * to add data to an existing dataset, you should probably use a different * constructor. */ public DefaultWindDataset() { this.seriesKeys = new java.util.ArrayList(); this.allSeriesData = new java.util.ArrayList(); } /** * Constructs a dataset based on the specified data array. * * @param data the data ({@code null} not permitted). * * @throws NullPointerException if {@code data} is {@code null}. */ public DefaultWindDataset(Object[][][] data) { this(seriesNameListFromDataArray(data), data); } /** * Constructs a dataset based on the specified data array. * * @param seriesNames the names of the series ({@code null} not * permitted). * @param data the wind data. * * @throws NullPointerException if {@code seriesNames} is {@code null}. */ public DefaultWindDataset(String[] seriesNames, Object[][][] data) { this(Arrays.asList(seriesNames), data); } /** * Constructs a dataset based on the specified data array. The array * can contain multiple series, each series can contain multiple items, * and each item is as follows: *
    *
  • {@code data[series][item][0]} - the date (either a * {@code Date} or a {@code Number} that is the milliseconds * since 1-Jan-1970);
  • *
  • {@code data[series][item][1]} - the wind direction (1 - 12, * like the numbers on a clock face);
  • *
  • {@code data[series][item][2]} - the wind force (1 - 12 on the * Beaufort scale)
  • *
* * @param seriesKeys the names of the series ({@code null} not * permitted). * @param data the wind dataset ({@code null} not permitted). * * @throws IllegalArgumentException if {@code seriesKeys} is * {@code null}. * @throws IllegalArgumentException if the number of series keys does not * match the number of series in the array. * @throws NullPointerException if {@code data} is {@code null}. */ public DefaultWindDataset(List seriesKeys, Object[][][] data) { Args.nullNotPermitted(seriesKeys, "seriesKeys"); if (seriesKeys.size() != data.length) { throw new IllegalArgumentException("The number of series keys does " + "not match the number of series in the data array."); } this.seriesKeys = seriesKeys; int seriesCount = data.length; this.allSeriesData = new java.util.ArrayList(seriesCount); for (int seriesIndex = 0; seriesIndex < seriesCount; seriesIndex++) { List oneSeriesData = new java.util.ArrayList(); int maxItemCount = data[seriesIndex].length; for (int itemIndex = 0; itemIndex < maxItemCount; itemIndex++) { Object xObject = data[seriesIndex][itemIndex][0]; if (xObject != null) { Number xNumber; if (xObject instanceof Number) { xNumber = (Number) xObject; } else { if (xObject instanceof Date) { Date xDate = (Date) xObject; xNumber = xDate.getTime(); } else { xNumber = 0; } } Number windDir = (Number) data[seriesIndex][itemIndex][1]; Number windForce = (Number) data[seriesIndex][itemIndex][2]; oneSeriesData.add(new WindDataItem(xNumber, windDir, windForce)); } } Collections.sort(oneSeriesData); this.allSeriesData.add(seriesIndex, oneSeriesData); } } /** * Returns the number of series in the dataset. * * @return The series count. */ @Override public int getSeriesCount() { return this.allSeriesData.size(); } /** * Returns the number of items in a series. * * @param series the series (zero-based index). * * @return The item count. */ @Override public int getItemCount(int series) { if (series < 0 || series >= getSeriesCount()) { throw new IllegalArgumentException("Invalid series index: " + series); } List oneSeriesData = (List) this.allSeriesData.get(series); return oneSeriesData.size(); } /** * Returns the key for a series. * * @param series the series (zero-based index). * * @return The series key. */ @Override public Comparable getSeriesKey(int series) { if (series < 0 || series >= getSeriesCount()) { throw new IllegalArgumentException("Invalid series index: " + series); } return (Comparable) this.seriesKeys.get(series); } /** * Returns the x-value for one item within a series. This should represent * a point in time, encoded as milliseconds in the same way as * java.util.Date. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The x-value for the item within the series. */ @Override public Number getX(int series, int item) { List oneSeriesData = (List) this.allSeriesData.get(series); WindDataItem windItem = (WindDataItem) oneSeriesData.get(item); return windItem.getX(); } /** * Returns the y-value for one item within a series. This maps to the * {@link #getWindForce(int, int)} method and is implemented because * {@code WindDataset} is an extension of {@link XYDataset}. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The y-value for the item within the series. */ @Override public Number getY(int series, int item) { return getWindForce(series, item); } /** * Returns the wind direction for one item within a series. This is a * number between 0 and 12, like the numbers on an upside-down clock face. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The wind direction for the item within the series. */ @Override public Number getWindDirection(int series, int item) { List oneSeriesData = (List) this.allSeriesData.get(series); WindDataItem windItem = (WindDataItem) oneSeriesData.get(item); return windItem.getWindDirection(); } /** * Returns the wind force for one item within a series. This is a number * between 0 and 12, as defined by the Beaufort scale. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The wind force for the item within the series. */ @Override public Number getWindForce(int series, int item) { List oneSeriesData = (List) this.allSeriesData.get(series); WindDataItem windItem = (WindDataItem) oneSeriesData.get(item); return windItem.getWindForce(); } /** * Utility method for automatically generating series names. * * @param data the wind data ({@code null} not permitted). * * @return An array of Series N with N = { 1 .. data.length }. * * @throws NullPointerException if {@code data} is {@code null}. */ public static List seriesNameListFromDataArray(Object[][] data) { int seriesCount = data.length; List seriesNameList = new java.util.ArrayList(seriesCount); for (int i = 0; i < seriesCount; i++) { seriesNameList.add("Series " + (i + 1)); } return seriesNameList; } /** * Checks this {@code WindDataset} for equality with an arbitrary * object. This method returns {@code true} if and only if: *
    *
  • {@code obj} is not {@code null};
  • *
  • {@code obj} is an instance of {@code DefaultWindDataset};
  • *
  • both datasets have the same number of series containing identical * values.
  • *
* * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof DefaultWindDataset)) { return false; } DefaultWindDataset that = (DefaultWindDataset) obj; if (!this.seriesKeys.equals(that.seriesKeys)) { return false; } if (!this.allSeriesData.equals(that.allSeriesData)) { return false; } return true; } } /** * A wind data item. */ class WindDataItem implements Comparable, Serializable { /** The x-value. */ private Number x; /** The wind direction. */ private Number windDir; /** The wind force. */ private Number windForce; /** * Creates a new wind data item. * * @param x the x-value. * @param windDir the direction. * @param windForce the force. */ public WindDataItem(Number x, Number windDir, Number windForce) { this.x = x; this.windDir = windDir; this.windForce = windForce; } /** * Returns the x-value. * * @return The x-value. */ public Number getX() { return this.x; } /** * Returns the wind direction. * * @return The wind direction. */ public Number getWindDirection() { return this.windDir; } /** * Returns the wind force. * * @return The wind force. */ public Number getWindForce() { return this.windForce; } /** * Compares this item to another object. * * @param object the other object. * * @return An int that indicates the relative comparison. */ @Override public int compareTo(Object object) { if (object instanceof WindDataItem) { WindDataItem item = (WindDataItem) object; if (this.x.doubleValue() > item.x.doubleValue()) { return 1; } else if (this.x.equals(item.x)) { return 0; } else { return -1; } } else { throw new ClassCastException("WindDataItem.compareTo(error)"); } } /** * Tests this {@code WindDataItem} for equality with an arbitrary * object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (this == obj) { return false; } if (!(obj instanceof WindDataItem)) { return false; } WindDataItem that = (WindDataItem) obj; if (!this.x.equals(that.x)) { return false; } if (!this.windDir.equals(that.windDir)) { return false; } if (!this.windForce.equals(that.windForce)) { return false; } return true; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/DefaultXYDataset.java000066400000000000000000000311241463604235500273450ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * DefaultXYDataset.java * --------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.DomainOrder; import org.jfree.data.general.DatasetChangeEvent; /** * A default implementation of the {@link XYDataset} interface that stores * data values in arrays of double primitives. */ public class DefaultXYDataset extends AbstractXYDataset implements XYDataset, PublicCloneable { /** * Storage for the series keys. This list must be kept in sync with the * seriesList. */ private List seriesKeys; /** * Storage for the series in the dataset. We use a list because the * order of the series is significant. This list must be kept in sync * with the seriesKeys list. */ private List seriesList; /** * Creates a new {@code DefaultXYDataset} instance, initially * containing no data. */ public DefaultXYDataset() { this.seriesKeys = new java.util.ArrayList(); this.seriesList = new java.util.ArrayList(); } /** * Returns the number of series in the dataset. * * @return The series count. */ @Override public int getSeriesCount() { return this.seriesList.size(); } /** * Returns the key for a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * * @return The key for the series. * * @throws IllegalArgumentException if {@code series} is not in the * specified range. */ @Override public Comparable getSeriesKey(int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds"); } return (Comparable) this.seriesKeys.get(series); } /** * Returns the index of the series with the specified key, or -1 if there * is no such series in the dataset. * * @param seriesKey the series key ({@code null} permitted). * * @return The index, or -1. */ @Override public int indexOf(Comparable seriesKey) { return this.seriesKeys.indexOf(seriesKey); } /** * Returns the order of the domain (x-) values in the dataset. In this * implementation, we cannot guarantee that the x-values are ordered, so * this method returns {@code DomainOrder.NONE}. * * @return {@code DomainOrder.NONE}. */ @Override public DomainOrder getDomainOrder() { return DomainOrder.NONE; } /** * Returns the number of items in the specified series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * * @return The item count. * * @throws IllegalArgumentException if {@code series} is not in the * specified range. */ @Override public int getItemCount(int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds"); } double[][] seriesArray = (double[][]) this.seriesList.get(series); return seriesArray[0].length; } /** * Returns the x-value for an item within a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item index (in the range {@code 0} to * {@code getItemCount(series)}). * * @return The x-value. * * @throws ArrayIndexOutOfBoundsException if {@code series} is not * within the specified range. * @throws ArrayIndexOutOfBoundsException if {@code item} is not * within the specified range. * * @see #getX(int, int) */ @Override public double getXValue(int series, int item) { double[][] seriesData = (double[][]) this.seriesList.get(series); return seriesData[0][item]; } /** * Returns the x-value for an item within a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item index (in the range {@code 0} to * {@code getItemCount(series)}). * * @return The x-value. * * @throws ArrayIndexOutOfBoundsException if {@code series} is not * within the specified range. * @throws ArrayIndexOutOfBoundsException if {@code item} is not * within the specified range. * * @see #getXValue(int, int) */ @Override public Number getX(int series, int item) { return getXValue(series, item); } /** * Returns the y-value for an item within a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item index (in the range {@code 0} to * {@code getItemCount(series)}). * * @return The y-value. * * @throws ArrayIndexOutOfBoundsException if {@code series} is not * within the specified range. * @throws ArrayIndexOutOfBoundsException if {@code item} is not * within the specified range. * * @see #getY(int, int) */ @Override public double getYValue(int series, int item) { double[][] seriesData = (double[][]) this.seriesList.get(series); return seriesData[1][item]; } /** * Returns the y-value for an item within a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item index (in the range {@code 0} to * {@code getItemCount(series)}). * * @return The y-value. * * @throws ArrayIndexOutOfBoundsException if {@code series} is not * within the specified range. * @throws ArrayIndexOutOfBoundsException if {@code item} is not * within the specified range. * * @see #getX(int, int) */ @Override public Number getY(int series, int item) { return getYValue(series, item); } /** * Adds a series or if a series with the same key already exists replaces * the data for that series, then sends a {@link DatasetChangeEvent} to * all registered listeners. * * @param seriesKey the series key ({@code null} not permitted). * @param data the data (must be an array with length 2, containing two * arrays of equal length, the first containing the x-values and the * second containing the y-values). */ public void addSeries(Comparable seriesKey, double[][] data) { if (seriesKey == null) { throw new IllegalArgumentException( "The 'seriesKey' cannot be null."); } if (data == null) { throw new IllegalArgumentException("The 'data' is null."); } if (data.length != 2) { throw new IllegalArgumentException( "The 'data' array must have length == 2."); } if (data[0].length != data[1].length) { throw new IllegalArgumentException( "The 'data' array must contain two arrays with equal length."); } int seriesIndex = indexOf(seriesKey); if (seriesIndex == -1) { // add a new series this.seriesKeys.add(seriesKey); this.seriesList.add(data); } else { // replace an existing series this.seriesList.remove(seriesIndex); this.seriesList.add(seriesIndex, data); } notifyListeners(new DatasetChangeEvent(this, this)); } /** * Removes a series from the dataset, then sends a * {@link DatasetChangeEvent} to all registered listeners. * * @param seriesKey the series key ({@code null} not permitted). * */ public void removeSeries(Comparable seriesKey) { int seriesIndex = indexOf(seriesKey); if (seriesIndex >= 0) { this.seriesKeys.remove(seriesIndex); this.seriesList.remove(seriesIndex); notifyListeners(new DatasetChangeEvent(this, this)); } } /** * Tests this {@code DefaultXYDataset} instance for equality with an * arbitrary object. This method returns {@code true} if and only if: *
    *
  • {@code obj} is not {@code null};
  • *
  • {@code obj} is an instance of {@code DefaultXYDataset};
  • *
  • both datasets have the same number of series, each containing * exactly the same values.
  • *
* * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof DefaultXYDataset)) { return false; } DefaultXYDataset that = (DefaultXYDataset) obj; if (!this.seriesKeys.equals(that.seriesKeys)) { return false; } for (int i = 0; i < this.seriesList.size(); i++) { double[][] d1 = (double[][]) this.seriesList.get(i); double[][] d2 = (double[][]) that.seriesList.get(i); double[] d1x = d1[0]; double[] d2x = d2[0]; if (!Arrays.equals(d1x, d2x)) { return false; } double[] d1y = d1[1]; double[] d2y = d2[1]; if (!Arrays.equals(d1y, d2y)) { return false; } } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result; result = this.seriesKeys.hashCode(); result = 29 * result + this.seriesList.hashCode(); return result; } /** * Creates an independent copy of this dataset. * * @return The cloned dataset. * * @throws CloneNotSupportedException if there is a problem cloning the * dataset (for instance, if a non-cloneable object is used for a * series key). */ @Override public Object clone() throws CloneNotSupportedException { DefaultXYDataset clone = (DefaultXYDataset) super.clone(); clone.seriesKeys = new java.util.ArrayList(this.seriesKeys); clone.seriesList = new ArrayList(this.seriesList.size()); for (int i = 0; i < this.seriesList.size(); i++) { double[][] data = (double[][]) this.seriesList.get(i); double[] x = data[0]; double[] y = data[1]; double[] xx = new double[x.length]; double[] yy = new double[y.length]; System.arraycopy(x, 0, xx, 0, x.length); System.arraycopy(y, 0, yy, 0, y.length); clone.seriesList.add(i, new double[][] {xx, yy}); } return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/DefaultXYZDataset.java000066400000000000000000000347361463604235500275130ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * DefaultXYZDataset.java * ---------------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.DomainOrder; import org.jfree.data.general.DatasetChangeEvent; /** * A default implementation of the {@link XYZDataset} interface that stores * data values in arrays of double primitives. */ public class DefaultXYZDataset extends AbstractXYZDataset implements XYZDataset, PublicCloneable { /** * Storage for the series keys. This list must be kept in sync with the * seriesList. */ private List seriesKeys; /** * Storage for the series in the dataset. We use a list because the * order of the series is significant. This list must be kept in sync * with the seriesKeys list. */ private List seriesList; /** * Creates a new {@code DefaultXYZDataset} instance, initially * containing no data. */ public DefaultXYZDataset() { this.seriesKeys = new java.util.ArrayList(); this.seriesList = new java.util.ArrayList(); } /** * Returns the number of series in the dataset. * * @return The series count. */ @Override public int getSeriesCount() { return this.seriesList.size(); } /** * Returns the key for a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * * @return The key for the series. * * @throws IllegalArgumentException if {@code series} is not in the * specified range. */ @Override public Comparable getSeriesKey(int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds"); } return (Comparable) this.seriesKeys.get(series); } /** * Returns the index of the series with the specified key, or -1 if there * is no such series in the dataset. * * @param seriesKey the series key ({@code null} permitted). * * @return The index, or -1. */ @Override public int indexOf(Comparable seriesKey) { return this.seriesKeys.indexOf(seriesKey); } /** * Returns the order of the domain (x-) values in the dataset. In this * implementation, we cannot guarantee that the x-values are ordered, so * this method returns {@code DomainOrder.NONE}. * * @return {@code DomainOrder.NONE}. */ @Override public DomainOrder getDomainOrder() { return DomainOrder.NONE; } /** * Returns the number of items in the specified series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * * @return The item count. * * @throws IllegalArgumentException if {@code series} is not in the * specified range. */ @Override public int getItemCount(int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds"); } double[][] seriesArray = (double[][]) this.seriesList.get(series); return seriesArray[0].length; } /** * Returns the x-value for an item within a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item index (in the range {@code 0} to * {@code getItemCount(series)}). * * @return The x-value. * * @throws ArrayIndexOutOfBoundsException if {@code series} is not * within the specified range. * @throws ArrayIndexOutOfBoundsException if {@code item} is not * within the specified range. * * @see #getX(int, int) */ @Override public double getXValue(int series, int item) { double[][] seriesData = (double[][]) this.seriesList.get(series); return seriesData[0][item]; } /** * Returns the x-value for an item within a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item index (in the range {@code 0} to * {@code getItemCount(series)}). * * @return The x-value. * * @throws ArrayIndexOutOfBoundsException if {@code series} is not * within the specified range. * @throws ArrayIndexOutOfBoundsException if {@code item} is not * within the specified range. * * @see #getXValue(int, int) */ @Override public Number getX(int series, int item) { return getXValue(series, item); } /** * Returns the y-value for an item within a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item index (in the range {@code 0} to * {@code getItemCount(series)}). * * @return The y-value. * * @throws ArrayIndexOutOfBoundsException if {@code series} is not * within the specified range. * @throws ArrayIndexOutOfBoundsException if {@code item} is not * within the specified range. * * @see #getY(int, int) */ @Override public double getYValue(int series, int item) { double[][] seriesData = (double[][]) this.seriesList.get(series); return seriesData[1][item]; } /** * Returns the y-value for an item within a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item index (in the range {@code 0} to * {@code getItemCount(series)}). * * @return The y-value. * * @throws ArrayIndexOutOfBoundsException if {@code series} is not * within the specified range. * @throws ArrayIndexOutOfBoundsException if {@code item} is not * within the specified range. * * @see #getX(int, int) */ @Override public Number getY(int series, int item) { return getYValue(series, item); } /** * Returns the z-value for an item within a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item index (in the range {@code 0} to * {@code getItemCount(series)}). * * @return The z-value. * * @throws ArrayIndexOutOfBoundsException if {@code series} is not * within the specified range. * @throws ArrayIndexOutOfBoundsException if {@code item} is not * within the specified range. * * @see #getZ(int, int) */ @Override public double getZValue(int series, int item) { double[][] seriesData = (double[][]) this.seriesList.get(series); return seriesData[2][item]; } /** * Returns the z-value for an item within a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item index (in the range {@code 0} to * {@code getItemCount(series)}). * * @return The z-value. * * @throws ArrayIndexOutOfBoundsException if {@code series} is not * within the specified range. * @throws ArrayIndexOutOfBoundsException if {@code item} is not * within the specified range. * * @see #getZ(int, int) */ @Override public Number getZ(int series, int item) { return getZValue(series, item); } /** * Adds a series or if a series with the same key already exists replaces * the data for that series, then sends a {@link DatasetChangeEvent} to * all registered listeners. * * @param seriesKey the series key ({@code null} not permitted). * @param data the data (must be an array with length 3, containing three * arrays of equal length, the first containing the x-values, the * second containing the y-values and the third containing the * z-values). */ public void addSeries(Comparable seriesKey, double[][] data) { if (seriesKey == null) { throw new IllegalArgumentException( "The 'seriesKey' cannot be null."); } if (data == null) { throw new IllegalArgumentException("The 'data' is null."); } if (data.length != 3) { throw new IllegalArgumentException( "The 'data' array must have length == 3."); } if (data[0].length != data[1].length || data[0].length != data[2].length) { throw new IllegalArgumentException("The 'data' array must contain " + "three arrays all having the same length."); } int seriesIndex = indexOf(seriesKey); if (seriesIndex == -1) { // add a new series this.seriesKeys.add(seriesKey); this.seriesList.add(data); } else { // replace an existing series this.seriesList.remove(seriesIndex); this.seriesList.add(seriesIndex, data); } notifyListeners(new DatasetChangeEvent(this, this)); } /** * Removes a series from the dataset, then sends a * {@link DatasetChangeEvent} to all registered listeners. * * @param seriesKey the series key ({@code null} not permitted). * */ public void removeSeries(Comparable seriesKey) { int seriesIndex = indexOf(seriesKey); if (seriesIndex >= 0) { this.seriesKeys.remove(seriesIndex); this.seriesList.remove(seriesIndex); notifyListeners(new DatasetChangeEvent(this, this)); } } /** * Tests this {@code DefaultXYZDataset} instance for equality with an * arbitrary object. This method returns {@code true} if and only if: *
    *
  • {@code obj} is not {@code null};
  • *
  • {@code obj} is an instance of {@code DefaultXYDataset};
  • *
  • both datasets have the same number of series, each containing * exactly the same values.
  • *
* * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof DefaultXYZDataset)) { return false; } DefaultXYZDataset that = (DefaultXYZDataset) obj; if (!this.seriesKeys.equals(that.seriesKeys)) { return false; } for (int i = 0; i < this.seriesList.size(); i++) { double[][] d1 = (double[][]) this.seriesList.get(i); double[][] d2 = (double[][]) that.seriesList.get(i); double[] d1x = d1[0]; double[] d2x = d2[0]; if (!Arrays.equals(d1x, d2x)) { return false; } double[] d1y = d1[1]; double[] d2y = d2[1]; if (!Arrays.equals(d1y, d2y)) { return false; } double[] d1z = d1[2]; double[] d2z = d2[2]; if (!Arrays.equals(d1z, d2z)) { return false; } } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result; result = this.seriesKeys.hashCode(); result = 29 * result + this.seriesList.hashCode(); return result; } /** * Creates an independent copy of this dataset. * * @return The cloned dataset. * * @throws CloneNotSupportedException if there is a problem cloning the * dataset (for instance, if a non-cloneable object is used for a * series key). */ @Override public Object clone() throws CloneNotSupportedException { DefaultXYZDataset clone = (DefaultXYZDataset) super.clone(); clone.seriesKeys = new java.util.ArrayList(this.seriesKeys); clone.seriesList = new ArrayList(this.seriesList.size()); for (int i = 0; i < this.seriesList.size(); i++) { double[][] data = (double[][]) this.seriesList.get(i); double[] x = data[0]; double[] y = data[1]; double[] z = data[2]; double[] xx = new double[x.length]; double[] yy = new double[y.length]; double[] zz = new double[z.length]; System.arraycopy(x, 0, xx, 0, x.length); System.arraycopy(y, 0, yy, 0, y.length); System.arraycopy(z, 0, zz, 0, z.length); clone.seriesList.add(i, new double[][] {xx, yy, zz}); } return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/IntervalXYDataset.java000066400000000000000000000125341463604235500275510ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * IntervalXYDataset.java * ---------------------- * (C) Copyright 2001-present, by David Gilbert and Contributors. * * Original Author: Mark Watson (www.markwatson.com); * Contributor(s): David Gilbert; * */ package org.jfree.data.xy; /** * An extension of the {@link XYDataset} interface that allows an x-interval * and a y-interval to be defined. Note that the x and y values defined * by the parent interface are NOT required to fall within these intervals. * This interface is used to support (among other things) bar plots against * numerical axes. */ public interface IntervalXYDataset extends XYDataset { /** * Returns the lower bound of the x-interval for the specified series and * item. If this lower bound is specified, it should be less than or * equal to the upper bound of the interval (if one is specified). * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The lower bound of the x-interval ({@code null} permitted). */ Number getStartX(int series, int item); /** * Returns the lower bound of the x-interval (as a double primitive) for * the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The lower bound of the x-interval. * * @see #getStartX(int, int) */ double getStartXValue(int series, int item); /** * Returns the upper bound of the x-interval for the specified series and * item. If this upper bound is specified, it should be greater than or * equal to the lower bound of the interval (if one is specified). * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The upper bound of the x-interval ({@code null} permitted). */ Number getEndX(int series, int item); /** * Returns the upper bound of the x-interval (as a double primitive) for * the specified series and item. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The upper bound of the x-interval. * * @see #getEndX(int, int) */ double getEndXValue(int series, int item); /** * Returns the lower bound of the y-interval for the specified series and * item. If this lower bound is specified, it should be less than or * equal to the upper bound of the interval (if one is specified). * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The lower bound of the y-interval ({@code null} permitted). */ Number getStartY(int series, int item); /** * Returns the lower bound of the y-interval (as a double primitive) for * the specified series and item. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The lower bound of the y-interval. * * @see #getStartY(int, int) */ double getStartYValue(int series, int item); /** * Returns the upper bound of the y-interval for the specified series and * item. If this upper bound is specified, it should be greater than or * equal to the lower bound of the interval (if one is specified). * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The upper bound of the y-interval ({@code null} permitted). */ Number getEndY(int series, int item); /** * Returns the upper bound of the y-interval (as a double primitive) for * the specified series and item. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The upper bound of the y-interval. * * @see #getEndY(int, int) */ double getEndYValue(int series, int item); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/IntervalXYDelegate.java000066400000000000000000000373541463604235500277050ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * IntervalXYDelegate.java * ----------------------- * (C) Copyright 2004-present, by Andreas Schroeder and Contributors. * * Original Author: Andreas Schroeder; * Contributor(s): David Gilbert; * */ package org.jfree.data.xy; import java.io.Serializable; import org.jfree.chart.HashUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.DomainInfo; import org.jfree.data.Range; import org.jfree.data.RangeInfo; import org.jfree.data.general.DatasetChangeEvent; import org.jfree.data.general.DatasetChangeListener; import org.jfree.data.general.DatasetUtils; /** * A delegate that handles the specification or automatic calculation of the * interval surrounding the x-values in a dataset. This is used to extend * a regular {@link XYDataset} to support the {@link IntervalXYDataset} * interface. *

* The decorator pattern was not used because of the several possibly * implemented interfaces of the decorated instance (e.g. * {@link TableXYDataset}, {@link RangeInfo}, {@link DomainInfo} etc.). *

* The width can be set manually or calculated automatically. The switch * autoWidth allows to determine which behavior is used. The auto width * calculation tries to find the smallest gap between two x-values in the * dataset. If there is only one item in the series, the auto width * calculation fails and falls back on the manually set interval width (which * is itself defaulted to 1.0). */ public class IntervalXYDelegate implements DatasetChangeListener, DomainInfo, Serializable, Cloneable, PublicCloneable { /** For serialization. */ private static final long serialVersionUID = -685166711639592857L; /** * The dataset to enhance. */ private XYDataset dataset; /** * A flag to indicate whether the width should be calculated automatically. */ private boolean autoWidth; /** * A value between 0.0 and 1.0 that indicates the position of the x-value * within the interval. */ private double intervalPositionFactor; /** * The fixed interval width (defaults to 1.0). */ private double fixedIntervalWidth; /** * The automatically calculated interval width. */ private double autoIntervalWidth; /** * Creates a new delegate that. * * @param dataset the underlying dataset ({@code null} not permitted). */ public IntervalXYDelegate(XYDataset dataset) { this(dataset, true); } /** * Creates a new delegate for the specified dataset. * * @param dataset the underlying dataset ({@code null} not permitted). * @param autoWidth a flag that controls whether the interval width is * calculated automatically. */ public IntervalXYDelegate(XYDataset dataset, boolean autoWidth) { Args.nullNotPermitted(dataset, "dataset"); this.dataset = dataset; this.autoWidth = autoWidth; this.intervalPositionFactor = 0.5; this.autoIntervalWidth = Double.POSITIVE_INFINITY; this.fixedIntervalWidth = 1.0; } /** * Returns {@code true} if the interval width is automatically * calculated, and {@code false} otherwise. * * @return A boolean. */ public boolean isAutoWidth() { return this.autoWidth; } /** * Sets the flag that indicates whether the interval width is automatically * calculated. If the flag is set to {@code true}, the interval is * recalculated. *

* Note: recalculating the interval amounts to changing the data values * represented by the dataset. The calling dataset must fire an * appropriate {@link DatasetChangeEvent}. * * @param b a boolean. */ public void setAutoWidth(boolean b) { this.autoWidth = b; if (b) { this.autoIntervalWidth = recalculateInterval(); } } /** * Returns the interval position factor. * * @return The interval position factor. */ public double getIntervalPositionFactor() { return this.intervalPositionFactor; } /** * Sets the interval position factor. This controls how the interval is * aligned to the x-value. For a value of 0.5, the interval is aligned * with the x-value in the center. For a value of 0.0, the interval is * aligned with the x-value at the lower end of the interval, and for a * value of 1.0, the interval is aligned with the x-value at the upper * end of the interval. *

* Note that changing the interval position factor amounts to changing the * data values represented by the dataset. Therefore, the dataset that is * using this delegate is responsible for generating the * appropriate {@link DatasetChangeEvent}. * * @param d the new interval position factor (in the range * {@code 0.0} to {@code 1.0} inclusive). */ public void setIntervalPositionFactor(double d) { if (d < 0.0 || 1.0 < d) { throw new IllegalArgumentException( "Argument 'd' outside valid range."); } this.intervalPositionFactor = d; } /** * Returns the fixed interval width. * * @return The fixed interval width. */ public double getFixedIntervalWidth() { return this.fixedIntervalWidth; } /** * Sets the fixed interval width and, as a side effect, sets the * {@code autoWidth} flag to {@code false}. *

* Note that changing the interval width amounts to changing the data * values represented by the dataset. Therefore, the dataset * that is using this delegate is responsible for generating the * appropriate {@link DatasetChangeEvent}. * * @param w the width (negative values not permitted). */ public void setFixedIntervalWidth(double w) { if (w < 0.0) { throw new IllegalArgumentException("Negative 'w' argument."); } this.fixedIntervalWidth = w; this.autoWidth = false; } /** * Returns the interval width. This method will return either the * auto calculated interval width or the manually specified interval * width, depending on the {@link #isAutoWidth()} result. * * @return The interval width to use. */ public double getIntervalWidth() { if (isAutoWidth() && !Double.isInfinite(this.autoIntervalWidth)) { // everything is fine: autoWidth is on, and an autoIntervalWidth // was set. return this.autoIntervalWidth; } else { // either autoWidth is off or autoIntervalWidth was not set. return this.fixedIntervalWidth; } } /** * Returns the start value of the x-interval for an item within a series. * * @param series the series index. * @param item the item index. * * @return The start value of the x-interval (possibly {@code null}). * * @see #getStartXValue(int, int) */ public Number getStartX(int series, int item) { Number startX = null; Number x = this.dataset.getX(series, item); if (x != null) { startX = x.doubleValue() - (getIntervalPositionFactor() * getIntervalWidth()); } return startX; } /** * Returns the start value of the x-interval for an item within a series. * * @param series the series index. * @param item the item index. * * @return The start value of the x-interval. * * @see #getStartX(int, int) */ public double getStartXValue(int series, int item) { return this.dataset.getXValue(series, item) - getIntervalPositionFactor() * getIntervalWidth(); } /** * Returns the end value of the x-interval for an item within a series. * * @param series the series index. * @param item the item index. * * @return The end value of the x-interval (possibly {@code null}). * * @see #getEndXValue(int, int) */ public Number getEndX(int series, int item) { Number endX = null; Number x = this.dataset.getX(series, item); if (x != null) { endX = x.doubleValue() + ((1.0 - getIntervalPositionFactor()) * getIntervalWidth()); } return endX; } /** * Returns the end value of the x-interval for an item within a series. * * @param series the series index. * @param item the item index. * * @return The end value of the x-interval. * * @see #getEndX(int, int) */ public double getEndXValue(int series, int item) { return this.dataset.getXValue(series, item) + (1.0 - getIntervalPositionFactor()) * getIntervalWidth(); } /** * Returns the minimum x-value in the dataset. * * @param includeInterval a flag that determines whether or not the * x-interval is taken into account. * * @return The minimum value. */ @Override public double getDomainLowerBound(boolean includeInterval) { double result = Double.NaN; Range r = getDomainBounds(includeInterval); if (r != null) { result = r.getLowerBound(); } return result; } /** * Returns the maximum x-value in the dataset. * * @param includeInterval a flag that determines whether or not the * x-interval is taken into account. * * @return The maximum value. */ @Override public double getDomainUpperBound(boolean includeInterval) { double result = Double.NaN; Range r = getDomainBounds(includeInterval); if (r != null) { result = r.getUpperBound(); } return result; } /** * Returns the range of the values in the dataset's domain, including * or excluding the interval around each x-value as specified. * * @param includeInterval a flag that determines whether or not the * x-interval should be taken into account. * * @return The range. */ @Override public Range getDomainBounds(boolean includeInterval) { // first get the range without the interval, then expand it for the // interval width Range range = DatasetUtils.findDomainBounds(this.dataset, false); if (includeInterval && range != null) { double lowerAdj = getIntervalWidth() * getIntervalPositionFactor(); double upperAdj = getIntervalWidth() - lowerAdj; range = new Range(range.getLowerBound() - lowerAdj, range.getUpperBound() + upperAdj); } return range; } /** * Handles events from the dataset by recalculating the interval if * necessary. * * @param e the event. */ @Override public void datasetChanged(DatasetChangeEvent e) { // TODO: by coding the event with some information about what changed // in the dataset, we could make the recalculation of the interval // more efficient in some cases (for instance, if the change is // just an update to a y-value, then the x-interval doesn't need // updating)... if (this.autoWidth) { this.autoIntervalWidth = recalculateInterval(); } } /** * Recalculate the minimum width "from scratch". * * @return The minimum width. */ private double recalculateInterval() { double result = Double.POSITIVE_INFINITY; int seriesCount = this.dataset.getSeriesCount(); for (int series = 0; series < seriesCount; series++) { result = Math.min(result, calculateIntervalForSeries(series)); } return result; } /** * Calculates the interval width for a given series. * * @param series the series index. * * @return The interval width. */ private double calculateIntervalForSeries(int series) { double result = Double.POSITIVE_INFINITY; int itemCount = this.dataset.getItemCount(series); if (itemCount > 1) { double prev = this.dataset.getXValue(series, 0); for (int item = 1; item < itemCount; item++) { double x = this.dataset.getXValue(series, item); result = Math.min(result, x - prev); prev = x; } } return result; } /** * Tests the delegate for equality with an arbitrary object. The * equality test considers two delegates to be equal if they would * calculate the same intervals for any given dataset (for this reason, the * dataset itself is NOT included in the equality test, because it is just * a reference back to the current 'owner' of the delegate). * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof IntervalXYDelegate)) { return false; } IntervalXYDelegate that = (IntervalXYDelegate) obj; if (this.autoWidth != that.autoWidth) { return false; } if (this.intervalPositionFactor != that.intervalPositionFactor) { return false; } if (this.fixedIntervalWidth != that.fixedIntervalWidth) { return false; } return true; } /** * @return A clone of this delegate. * * @throws CloneNotSupportedException if the object cannot be cloned. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int hash = 5; hash = HashUtils.hashCode(hash, this.autoWidth); hash = HashUtils.hashCode(hash, this.intervalPositionFactor); hash = HashUtils.hashCode(hash, this.fixedIntervalWidth); return hash; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/IntervalXYZDataset.java000066400000000000000000000073231463604235500277030ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * IntervalXYZDataset.java * ----------------------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; /** * An extension of the {@link XYZDataset} interface that allows a range of data * to be defined for any of the X values, the Y values, and the Z values. */ public interface IntervalXYZDataset extends XYZDataset { /** * Returns the starting X value for the specified series and item. * * @param series the series (zero-based index). * @param item the item within a series (zero-based index). * * @return The starting X value for the specified series and item. */ Number getStartXValue(int series, int item); /** * Returns the ending X value for the specified series and item. * * @param series the series (zero-based index). * @param item the item within a series (zero-based index). * * @return The ending X value for the specified series and item. */ Number getEndXValue(int series, int item); /** * Returns the starting Y value for the specified series and item. * * @param series the series (zero-based index). * @param item the item within a series (zero-based index). * * @return The starting Y value for the specified series and item. */ Number getStartYValue(int series, int item); /** * Returns the ending Y value for the specified series and item. * * @param series the series (zero-based index). * @param item the item within a series (zero-based index). * * @return The ending Y value for the specified series and item. */ Number getEndYValue(int series, int item); /** * Returns the starting Z value for the specified series and item. * * @param series the series (zero-based index). * @param item the item within a series (zero-based index). * * @return The starting Z value for the specified series and item. */ Number getStartZValue(int series, int item); /** * Returns the ending Z value for the specified series and item. * * @param series the series (zero-based index). * @param item the item within a series (zero-based index). * * @return The ending Z value for the specified series and item. */ Number getEndZValue(int series, int item); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/MatrixSeries.java000066400000000000000000000145151463604235500266160ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * MatrixSeries.java * ----------------- * (C) Copyright 2003-present, by Barak Naveh and Contributors. * * Original Author: Barak Naveh; * Contributor(s): David Gilbert; * Zhitao Wang; * */ package org.jfree.data.xy; import java.io.Serializable; import org.jfree.data.general.Series; /** * Represents a dense matrix M[i,j] where each Mij item of the matrix has a * value (default is 0). */ public class MatrixSeries extends Series implements Serializable { /** For serialization. */ private static final long serialVersionUID = 7934188527308315704L; /** Series matrix values */ protected double[][] data; /** * Constructs a new matrix series. *

* By default, all matrix items are initialzed to 0. *

* * @param name series name ({@code null} not permitted). * @param rows the number of rows. * @param columns the number of columns. */ public MatrixSeries(String name, int rows, int columns) { super(name); this.data = new double[rows][columns]; zeroAll(); } /** * Returns the number of columns in this matrix series. * * @return The number of columns in this matrix series. */ public int getColumnsCount() { return this.data[0].length; } /** * Return the matrix item at the specified index. Note that this method * creates a new {@code double} instance every time it is called. * * @param itemIndex item index. * * @return The matrix item at the specified index. * * @see #get(int, int) */ public Number getItem(int itemIndex) { int i = getItemRow(itemIndex); int j = getItemColumn(itemIndex); return get(i, j); } /** * Returns the column of the specified item. * * @param itemIndex the index of the item. * * @return The column of the specified item. */ public int getItemColumn(int itemIndex) { //assert itemIndex >= 0 && itemIndex < getItemCount(); return itemIndex % getColumnsCount(); } /** * Returns the number of items in the series. * * @return The item count. */ @Override public int getItemCount() { return getRowCount() * getColumnsCount(); } /** * Returns the row of the specified item. * * @param itemIndex the index of the item. * * @return The row of the specified item. */ public int getItemRow(int itemIndex) { //assert itemIndex >= 0 && itemIndex < getItemCount(); return itemIndex / getColumnsCount(); } /** * Returns the number of rows in this matrix series. * * @return The number of rows in this matrix series. */ public int getRowCount() { return this.data.length; } /** * Returns the value of the specified item in this matrix series. * * @param i the row of the item. * @param j the column of the item. * * @return The value of the specified item in this matrix series. * * @see #getItem(int) * @see #update(int, int, double) */ public double get(int i, int j) { return this.data[i][j]; } /** * Updates the value of the specified item in this matrix series. * * @param i the row of the item. * @param j the column of the item. * @param mij the new value for the item. * * @see #get(int, int) */ public void update(int i, int j, double mij) { this.data[i][j] = mij; fireSeriesChanged(); } /** * Sets all matrix values to zero and sends a * {@link org.jfree.data.general.SeriesChangeEvent} to all registered * listeners. */ public void zeroAll() { int rows = getRowCount(); int columns = getColumnsCount(); for (int row = 0; row < rows; row++) { for (int column = 0; column < columns; column++) { this.data[row][column] = 0.0; } } fireSeriesChanged(); } /** * Tests this object instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof MatrixSeries)) { return false; } MatrixSeries that = (MatrixSeries) obj; if (!(getRowCount() == that.getRowCount())) { return false; } if (!(getColumnsCount() == that.getColumnsCount())) { return false; } for (int r = 0; r < getRowCount(); r++) { for (int c = 0; c < getColumnsCount(); c++) { if (get(r, c) != that.get(r, c)) { return false; } } } return super.equals(obj); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/MatrixSeriesCollection.java000066400000000000000000000226721463604235500306350ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * MatrixSeriesCollection.java * --------------------------- * (C) Copyright 2003-present, by Barak Naveh and Contributors. * * Original Author: Barak Naveh; * Contributor(s): David Gilbert; * */ package org.jfree.data.xy; import java.io.Serializable; import java.util.List; import java.util.Objects; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; /** * Represents a collection of {@link MatrixSeries} that can be used as a * dataset. * * @see org.jfree.data.xy.MatrixSeries */ public class MatrixSeriesCollection extends AbstractXYZDataset implements XYZDataset, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -3197705779242543945L; /** The series that are included in the collection. */ private List seriesList; /** * Constructs an empty dataset. */ public MatrixSeriesCollection() { this(null); } /** * Constructs a dataset and populates it with a single matrix series. * * @param series the time series. */ public MatrixSeriesCollection(MatrixSeries series) { this.seriesList = new java.util.ArrayList(); if (series != null) { this.seriesList.add(series); series.addChangeListener(this); } } /** * Returns the number of items in the specified series. * * @param seriesIndex zero-based series index. * * @return The number of items in the specified series. */ @Override public int getItemCount(int seriesIndex) { return getSeries(seriesIndex).getItemCount(); } /** * Returns the series having the specified index. * * @param seriesIndex zero-based series index. * * @return The series. */ public MatrixSeries getSeries(int seriesIndex) { if ((seriesIndex < 0) || (seriesIndex > getSeriesCount())) { throw new IllegalArgumentException("Index outside valid range."); } MatrixSeries series = (MatrixSeries) this.seriesList.get(seriesIndex); return series; } /** * Returns the number of series in the collection. * * @return The number of series in the collection. */ @Override public int getSeriesCount() { return this.seriesList.size(); } /** * Returns the key for a series. * * @param seriesIndex zero-based series index. * * @return The key for a series. */ @Override public Comparable getSeriesKey(int seriesIndex) { return getSeries(seriesIndex).getKey(); } /** * Returns the j index value of the specified Mij matrix item in the * specified matrix series. * * @param seriesIndex zero-based series index. * @param itemIndex zero-based item index. * * @return The j index value for the specified matrix item. * * @see org.jfree.data.xy.XYDataset#getXValue(int, int) */ @Override public Number getX(int seriesIndex, int itemIndex) { MatrixSeries series = (MatrixSeries) this.seriesList.get(seriesIndex); return series.getItemColumn(itemIndex); } /** * Returns the i index value of the specified Mij matrix item in the * specified matrix series. * * @param seriesIndex zero-based series index. * @param itemIndex zero-based item index. * * @return The i index value for the specified matrix item. * * @see org.jfree.data.xy.XYDataset#getYValue(int, int) */ @Override public Number getY(int seriesIndex, int itemIndex) { MatrixSeries series = (MatrixSeries) this.seriesList.get(seriesIndex); int y = series.getItemRow(itemIndex); return y; // I know it's bad to create object. better idea? } /** * Returns the Mij item value of the specified Mij matrix item in the * specified matrix series. * * @param seriesIndex the series (zero-based index). * @param itemIndex zero-based item index. * * @return The Mij item value for the specified matrix item. * * @see org.jfree.data.xy.XYZDataset#getZValue(int, int) */ @Override public Number getZ(int seriesIndex, int itemIndex) { MatrixSeries series = (MatrixSeries) this.seriesList.get(seriesIndex); Number z = series.getItem(itemIndex); return z; } /** * Adds a series to the collection. *

* Notifies all registered listeners that the dataset has changed. *

* * @param series the series ({@code null} not permitted). */ public void addSeries(MatrixSeries series) { Args.nullNotPermitted(series, "series"); // FIXME: Check that there isn't already a series with the same key // add the series... this.seriesList.add(series); series.addChangeListener(this); fireDatasetChanged(); } /** * Tests this collection for equality with an arbitrary object. * * @param obj the object. * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (obj == this) { return true; } if (obj instanceof MatrixSeriesCollection) { MatrixSeriesCollection c = (MatrixSeriesCollection) obj; return Objects.equals(this.seriesList, c.seriesList); } return false; } /** * Returns a hash code. * * @return A hash code. */ @Override public int hashCode() { return (this.seriesList != null ? this.seriesList.hashCode() : 0); } /** * Returns a clone of this instance. * * @return A clone. * * @throws CloneNotSupportedException if there is a problem. */ @Override public Object clone() throws CloneNotSupportedException { MatrixSeriesCollection clone = (MatrixSeriesCollection) super.clone(); clone.seriesList = (List) ObjectUtils.deepClone(this.seriesList); return clone; } /** * Removes all the series from the collection. *

* Notifies all registered listeners that the dataset has changed. *

*/ public void removeAllSeries() { // Unregister the collection as a change listener to each series in // the collection. for (int i = 0; i < this.seriesList.size(); i++) { MatrixSeries series = (MatrixSeries) this.seriesList.get(i); series.removeChangeListener(this); } // Remove all the series from the collection and notify listeners. this.seriesList.clear(); fireDatasetChanged(); } /** * Removes a series from the collection. *

* Notifies all registered listeners that the dataset has changed. *

* * @param series the series ({@code null}). */ public void removeSeries(MatrixSeries series) { Args.nullNotPermitted(series, "series"); if (this.seriesList.contains(series)) { series.removeChangeListener(this); this.seriesList.remove(series); fireDatasetChanged(); } } /** * Removes a series from the collection. *

* Notifies all registered listeners that the dataset has changed. * * @param seriesIndex the series (zero based index). */ public void removeSeries(int seriesIndex) { // check arguments... if ((seriesIndex < 0) || (seriesIndex > getSeriesCount())) { throw new IllegalArgumentException("Index outside valid range."); } // fetch the series, remove the change listener, then remove the series. MatrixSeries series = (MatrixSeries) this.seriesList.get(seriesIndex); series.removeChangeListener(this); this.seriesList.remove(seriesIndex); fireDatasetChanged(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/NormalizedMatrixSeries.java000066400000000000000000000110241463604235500306330ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * NormalizedMatrixSeries.java * --------------------------- * (C) Copyright 2003-present, by Barak Naveh and Contributors. * * Original Author: Barak Naveh; * Contributor(s): David Gilbert; * */ package org.jfree.data.xy; /** * Represents a dense normalized matrix M[i,j] where each Mij item of the * matrix has a value (default is 0). When a matrix item is observed using * {@code getItem()} method, it is normalized, that is, divided by the * total sum of all items. It can be also be scaled by setting a scale factor. */ public class NormalizedMatrixSeries extends MatrixSeries { /** The default scale factor. */ public static final double DEFAULT_SCALE_FACTOR = 1.0; /** * A factor that multiplies each item in this series when observed using * getItem method. */ private double m_scaleFactor = DEFAULT_SCALE_FACTOR; /** The sum of all items in this matrix */ private double m_totalSum; /** * Constructor for NormalizedMatrixSeries. * * @param name the series name. * @param rows the number of rows. * @param columns the number of columns. */ public NormalizedMatrixSeries(String name, int rows, int columns) { super(name, rows, columns); /* * we assum super is always initialized to all-zero matrix, so the * total sum should be 0 upon initialization. However, we set it to * Double.MIN_VALUE to get the same effect and yet avoid division by 0 * upon initialization. */ this.m_totalSum = Double.MIN_VALUE; } /** * Returns an item. * * @param itemIndex the index. * * @return The value. * * @see org.jfree.data.xy.MatrixSeries#getItem(int) */ @Override public Number getItem(int itemIndex) { int i = getItemRow(itemIndex); int j = getItemColumn(itemIndex); double mij = get(i, j) * this.m_scaleFactor; Number n = mij / this.m_totalSum; return n; } /** * Sets the factor that multiplies each item in this series when observed * using getItem mehtod. * * @param factor new factor to set. * * @see #DEFAULT_SCALE_FACTOR */ public void setScaleFactor(double factor) { this.m_scaleFactor = factor; // FIXME: this should generate a series change event } /** * Returns the factor that multiplies each item in this series when * observed using getItem mehtod. * * @return The factor */ public double getScaleFactor() { return this.m_scaleFactor; } /** * Updates the value of the specified item in this matrix series. * * @param i the row of the item. * @param j the column of the item. * @param mij the new value for the item. * * @see #get(int, int) */ @Override public void update(int i, int j, double mij) { this.m_totalSum -= get(i, j); this.m_totalSum += mij; super.update(i, j, mij); } /** * @see org.jfree.data.xy.MatrixSeries#zeroAll() */ @Override public void zeroAll() { this.m_totalSum = 0; super.zeroAll(); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/OHLCDataItem.java000066400000000000000000000131571463604235500263360ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * OHLCDataItem.java * ----------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import java.io.Serializable; import java.util.Date; import org.jfree.chart.util.Args; /** * Represents a single (open-high-low-close) data item in * an {@link DefaultOHLCDataset}. This data item is commonly used * to summarise the trading activity of a financial commodity for * a fixed period (most often one day). */ public class OHLCDataItem implements Comparable, Serializable { /** For serialization. */ private static final long serialVersionUID = 7753817154401169901L; /** The date. */ private Date date; /** The open value. */ private Number open; /** The high value. */ private Number high; /** The low value. */ private Number low; /** The close value. */ private Number close; /** The trading volume (number of shares, contracts or whatever). */ private Number volume; /** * Creates a new item. * * @param date the date ({@code null} not permitted). * @param open the open value. * @param high the high value. * @param low the low value. * @param close the close value. * @param volume the volume. */ public OHLCDataItem(Date date, double open, double high, double low, double close, double volume) { Args.nullNotPermitted(date, "date"); this.date = date; this.open = open; this.high = high; this.low = low; this.close = close; this.volume = volume; } /** * Returns the date that the data item relates to. * * @return The date (never {@code null}). */ public Date getDate() { return this.date; } /** * Returns the open value. * * @return The open value (never {@code null}). */ public Number getOpen() { return this.open; } /** * Returns the high value. * * @return The high value (never {@code null}). */ public Number getHigh() { return this.high; } /** * Returns the low value. * * @return The low value (never {@code null}). */ public Number getLow() { return this.low; } /** * Returns the close value. * * @return The close value (never {@code null}). */ public Number getClose() { return this.close; } /** * Returns the volume. * * @return The volume (never {@code null}). */ public Number getVolume() { return this.volume; } /** * Checks this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof OHLCDataItem)) { return false; } OHLCDataItem that = (OHLCDataItem) obj; if (!this.date.equals(that.date)) { return false; } if (!this.high.equals(that.high)) { return false; } if (!this.low.equals(that.low)) { return false; } if (!this.open.equals(that.open)) { return false; } if (!this.close.equals(that.close)) { return false; } return true; } /** * Compares this object with the specified object for order. Returns a * negative integer, zero, or a positive integer as this object is less * than, equal to, or greater than the specified object. * * @param object the object to compare to. * * @return A negative integer, zero, or a positive integer as this object * is less than, equal to, or greater than the specified object. */ @Override public int compareTo(Object object) { if (object instanceof OHLCDataItem) { OHLCDataItem item = (OHLCDataItem) object; return this.date.compareTo(item.date); } else { throw new ClassCastException("OHLCDataItem.compareTo()."); } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/OHLCDataset.java000066400000000000000000000107341463604235500262310ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * OHLCDataset.java * ---------------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Sylvain Vieujot; * */ package org.jfree.data.xy; /** * An interface that defines data in the form of (x, high, low, open, close) * tuples. */ public interface OHLCDataset extends XYDataset { /** * Returns the high-value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The value. */ Number getHigh(int series, int item); /** * Returns the high-value (as a double primitive) for an item within a * series. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The high-value. */ double getHighValue(int series, int item); /** * Returns the low-value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The value. */ Number getLow(int series, int item); /** * Returns the low-value (as a double primitive) for an item within a * series. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The low-value. */ double getLowValue(int series, int item); /** * Returns the open-value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The value. */ Number getOpen(int series, int item); /** * Returns the open-value (as a double primitive) for an item within a * series. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The open-value. */ double getOpenValue(int series, int item); /** * Returns the y-value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The value. */ Number getClose(int series, int item); /** * Returns the close-value (as a double primitive) for an item within a * series. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The close-value. */ double getCloseValue(int series, int item); /** * Returns the volume for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The value. */ Number getVolume(int series, int item); /** * Returns the volume-value (as a double primitive) for an item within a * series. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The volume-value. */ double getVolumeValue(int series, int item); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/TableXYDataset.java000066400000000000000000000040321463604235500270060ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * TableXYDataset.java * ------------------- * (C) Copyright 2000-present, by Richard Atkinson and Contributors. * * Original Author: Richard Atkinson; * Contributor(s): David Gilbert; * */ package org.jfree.data.xy; /** * A dataset containing one or more data series containing (x, y) data items, * where all series in the dataset share the same set of x-values. This is a * restricted form of the {@link XYDataset} interface (which allows independent * x-values between series). This is used primarily by the * {@link org.jfree.chart.renderer.xy.StackedXYAreaRenderer}. */ public interface TableXYDataset extends XYDataset { /** * Returns the number of items every series. * * @return The item count. */ int getItemCount(); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/Vector.java000066400000000000000000000070441463604235500254400ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------- * Vector.java * ----------- * (C) Copyright 2007-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import java.io.Serializable; /** * A vector. */ public class Vector implements Serializable { /** The vector x. */ private double x; /** The vector y. */ private double y; /** * Creates a new instance of {@code Vector}. * * @param x the x-component. * @param y the y-component. */ public Vector(double x, double y) { this.x = x; this.y = y; } /** * Returns the x-value. * * @return The x-value. */ public double getX() { return this.x; } /** * Returns the y-value. * * @return The y-value. */ public double getY() { return this.y; } /** * Returns the length of the vector. * * @return The vector length. */ public double getLength() { return Math.sqrt((this.x * this.x) + (this.y * this.y)); } /** * Returns the angle of the vector. * * @return The angle of the vector. */ public double getAngle() { return Math.atan2(this.y, this.x); } /** * Tests this vector for equality with an arbitrary object. * * @param obj the object ({@code null} not permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof Vector)) { return false; } Vector that = (Vector) obj; if (this.x != that.x) { return false; } if (this.y != that.y) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = 193; long temp = Double.doubleToLongBits(this.x); result = 37 * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(this.y); result = 37 * result + (int) (temp ^ (temp >>> 32)); return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/VectorDataItem.java000066400000000000000000000064461463604235500270560ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * VectorDataItem.java * ------------------- * (C) Copyright 2007-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.data.ComparableObjectItem; /** * A data item representing data in the form (x, y, deltaX, deltaY), intended * for use by the {@link VectorSeries} class. */ public class VectorDataItem extends ComparableObjectItem { /** * Creates a new {@code VectorDataItem} instance. * * @param x the x-value. * @param y the y-value. * @param deltaX the vector x. * @param deltaY the vector y. */ public VectorDataItem(double x, double y, double deltaX, double deltaY) { super(new XYCoordinate(x, y), new Vector(deltaX, deltaY)); } /** * Returns the x-value. * * @return The x-value (never {@code null}). */ public double getXValue() { XYCoordinate xy = (XYCoordinate) getComparable(); return xy.getX(); } /** * Returns the y-value. * * @return The y-value. */ public double getYValue() { XYCoordinate xy = (XYCoordinate) getComparable(); return xy.getY(); } /** * Returns the vector. * * @return The vector (possibly {@code null}). */ public Vector getVector() { return (Vector) getObject(); } /** * Returns the x-component for the vector. * * @return The x-component. */ public double getVectorX() { Vector vi = (Vector) getObject(); if (vi != null) { return vi.getX(); } else { return Double.NaN; } } /** * Returns the y-component for the vector. * * @return The y-component. */ public double getVectorY() { Vector vi = (Vector) getObject(); if (vi != null) { return vi.getY(); } else { return Double.NaN; } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/VectorSeries.java000066400000000000000000000124631463604235500266140ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * VectorSeries.java * ----------------- * (C) Copyright 2007-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.data.ComparableObjectItem; import org.jfree.data.ComparableObjectSeries; import org.jfree.data.general.SeriesChangeEvent; /** * A list of (x,y, deltaX, deltaY) data items. * * @see VectorSeriesCollection */ public class VectorSeries extends ComparableObjectSeries { /** * Creates a new empty series. * * @param key the series key ({@code null} not permitted). */ public VectorSeries(Comparable key) { this(key, false, true); } /** * Constructs a new series that contains no data. You can specify * whether or not duplicate x-values are allowed for the series. * * @param key the series key ({@code null} not permitted). * @param autoSort a flag that controls whether or not the items in the * series are sorted. * @param allowDuplicateXValues a flag that controls whether duplicate * x-values are allowed. */ public VectorSeries(Comparable key, boolean autoSort, boolean allowDuplicateXValues) { super(key, autoSort, allowDuplicateXValues); } /** * Adds a data item to the series. * * @param x the x-value. * @param y the y-value. * @param deltaX the vector x. * @param deltaY the vector y. */ public void add(double x, double y, double deltaX, double deltaY) { add(new VectorDataItem(x, y, deltaX, deltaY), true); } /** * Adds a data item to the series and, if requested, sends a * {@link SeriesChangeEvent} to all registered listeners. * * @param item the data item ({@code null} not permitted). * @param notify notify listeners? */ public void add(VectorDataItem item, boolean notify) { super.add(item, notify); } /** * Removes the item at the specified index and sends a * {@link SeriesChangeEvent} to all registered listeners. * * @param index the index. * * @return The item removed. */ @Override public ComparableObjectItem remove(int index) { VectorDataItem result = (VectorDataItem) this.data.remove(index); fireSeriesChanged(); return result; } /** * Returns the x-value for the specified item. * * @param index the item index. * * @return The x-value. */ public double getXValue(int index) { VectorDataItem item = (VectorDataItem) this.getDataItem(index); return item.getXValue(); } /** * Returns the y-value for the specified item. * * @param index the item index. * * @return The y-value. */ public double getYValue(int index) { VectorDataItem item = (VectorDataItem) getDataItem(index); return item.getYValue(); } /** * Returns the x-component of the vector for an item in the series. * * @param index the item index. * * @return The x-component of the vector. */ public double getVectorXValue(int index) { VectorDataItem item = (VectorDataItem) getDataItem(index); return item.getVectorX(); } /** * Returns the y-component of the vector for an item in the series. * * @param index the item index. * * @return The y-component of the vector. */ public double getVectorYValue(int index) { VectorDataItem item = (VectorDataItem) getDataItem(index); return item.getVectorY(); } /** * Returns the data item at the specified index. * * @param index the item index. * * @return The data item. */ @Override public ComparableObjectItem getDataItem(int index) { // overridden to make public return super.getDataItem(index); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/VectorSeriesCollection.java000066400000000000000000000237231463604235500306310ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * VectorSeriesCollection.java * --------------------------- * (C) Copyright 2007-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import java.io.Serializable; import java.util.List; import java.util.Objects; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.general.DatasetChangeEvent; /** * A collection of {@link VectorSeries} objects. */ public class VectorSeriesCollection extends AbstractXYDataset implements VectorXYDataset, PublicCloneable, Serializable { /** Storage for the data series. */ private List data; /** * Creates a new {@code VectorSeriesCollection} instance. */ public VectorSeriesCollection() { this.data = new java.util.ArrayList(); } /** * Adds a series to the collection and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param series the series ({@code null} not permitted). */ public void addSeries(VectorSeries series) { Args.nullNotPermitted(series, "series"); this.data.add(series); series.addChangeListener(this); fireDatasetChanged(); } /** * Removes the specified series from the collection and sends a * {@link DatasetChangeEvent} to all registered listeners. * * @param series the series ({@code null} not permitted). * * @return A boolean indicating whether the series has actually been * removed. */ public boolean removeSeries(VectorSeries series) { Args.nullNotPermitted(series, "series"); boolean removed = this.data.remove(series); if (removed) { series.removeChangeListener(this); fireDatasetChanged(); } return removed; } /** * Removes all the series from the collection and sends a * {@link DatasetChangeEvent} to all registered listeners. */ public void removeAllSeries() { // deregister the collection as a change listener to each series in the // collection for (int i = 0; i < this.data.size(); i++) { VectorSeries series = (VectorSeries) this.data.get(i); series.removeChangeListener(this); } // remove all the series from the collection and notify listeners. this.data.clear(); fireDatasetChanged(); } /** * Returns the number of series in the collection. * * @return The series count. */ @Override public int getSeriesCount() { return this.data.size(); } /** * Returns a series from the collection. * * @param series the series index (zero-based). * * @return The series. * * @throws IllegalArgumentException if {@code series} is not in the * range {@code 0} to {@code getSeriesCount() - 1}. */ public VectorSeries getSeries(int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds"); } return (VectorSeries) this.data.get(series); } /** * Returns the key for a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * * @return The key for a series. * * @throws IllegalArgumentException if {@code series} is not in the * specified range. */ @Override public Comparable getSeriesKey(int series) { // defer argument checking return getSeries(series).getKey(); } /** * Returns the index of the specified series, or -1 if that series is not * present in the dataset. * * @param series the series ({@code null} not permitted). * * @return The series index. */ public int indexOf(VectorSeries series) { Args.nullNotPermitted(series, "series"); return this.data.indexOf(series); } /** * Returns the number of items in the specified series. * * @param series the series (zero-based index). * * @return The item count. * * @throws IllegalArgumentException if {@code series} is not in the * range {@code 0} to {@code getSeriesCount() - 1}. */ @Override public int getItemCount(int series) { // defer argument checking return getSeries(series).getItemCount(); } /** * Returns the x-value for an item within a series. * * @param series the series index. * @param item the item index. * * @return The x-value. */ @Override public double getXValue(int series, int item) { VectorSeries s = (VectorSeries) this.data.get(series); VectorDataItem di = (VectorDataItem) s.getDataItem(item); return di.getXValue(); } /** * Returns the x-value for an item within a series. Note that this method * creates a new {@link Double} instance every time it is called---use * {@link #getXValue(int, int)} instead, if possible. * * @param series the series index. * @param item the item index. * * @return The x-value. */ @Override public Number getX(int series, int item) { return getXValue(series, item); } /** * Returns the y-value for an item within a series. * * @param series the series index. * @param item the item index. * * @return The y-value. */ @Override public double getYValue(int series, int item) { VectorSeries s = (VectorSeries) this.data.get(series); VectorDataItem di = (VectorDataItem) s.getDataItem(item); return di.getYValue(); } /** * Returns the y-value for an item within a series. Note that this method * creates a new {@link Double} instance every time it is called---use * {@link #getYValue(int, int)} instead, if possible. * * @param series the series index. * @param item the item index. * * @return The y-value. */ @Override public Number getY(int series, int item) { return getYValue(series, item); } /** * Returns the vector for an item in a series. * * @param series the series index. * @param item the item index. * * @return The vector (possibly {@code null}). */ @Override public Vector getVector(int series, int item) { VectorSeries s = (VectorSeries) this.data.get(series); VectorDataItem di = (VectorDataItem) s.getDataItem(item); return di.getVector(); } /** * Returns the x-component of the vector for an item in a series. * * @param series the series index. * @param item the item index. * * @return The x-component of the vector. */ @Override public double getVectorXValue(int series, int item) { VectorSeries s = (VectorSeries) this.data.get(series); VectorDataItem di = (VectorDataItem) s.getDataItem(item); return di.getVectorX(); } /** * Returns the y-component of the vector for an item in a series. * * @param series the series index. * @param item the item index. * * @return The y-component of the vector. */ @Override public double getVectorYValue(int series, int item) { VectorSeries s = (VectorSeries) this.data.get(series); VectorDataItem di = (VectorDataItem) s.getDataItem(item); return di.getVectorY(); } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof VectorSeriesCollection)) { return false; } VectorSeriesCollection that = (VectorSeriesCollection) obj; return Objects.equals(this.data, that.data); } /** * Returns a clone of this instance. * * @return A clone. * * @throws CloneNotSupportedException if there is a problem. */ @Override public Object clone() throws CloneNotSupportedException { VectorSeriesCollection clone = (VectorSeriesCollection) super.clone(); clone.data = (List) ObjectUtils.deepClone(this.data); return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/VectorXYDataset.java000066400000000000000000000053721463604235500272310ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * VectorXYDataset.java * -------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; /** * An extension of the {@link XYDataset} interface that allows a vector to be * defined at a specific (x, y) location. */ public interface VectorXYDataset extends XYDataset { /** * Returns the x-component of the vector for an item in a series. * * @param series the series index. * @param item the item index. * * @return The x-component of the vector. */ double getVectorXValue(int series, int item); /** * Returns the y-component of the vector for an item in a series. * * @param series the series index. * @param item the item index. * * @return The y-component of the vector. */ double getVectorYValue(int series, int item); /** * Returns the vector for an item in a series. Depending on the particular * dataset implementation, this may involve creating a new {@link Vector} * instance --- if you are just interested in the x and y components, * use the {@link #getVectorXValue(int, int)} and * {@link #getVectorYValue(int, int)} methods instead. * * @param series the series index. * @param item the item index. * * @return The vector (possibly {@code null}). */ Vector getVector(int series, int item); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/WindDataset.java000066400000000000000000000050651463604235500264060ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * WindDataset.java * ---------------- * (C) Copyright 2001-present, by Achilleus Mantzios and Contributors. * * Original Author: Achilleus Mantzios; * Contributor(s): David Gilbert; * */ package org.jfree.data.xy; /** * Interface for a dataset that supplies wind intensity and direction values * observed at various points in time. */ public interface WindDataset extends XYDataset { /** * Returns the wind direction (should be in the range 0 to 12, * corresponding to the positions on an upside-down clock face). * * @param series the series (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item (in the range {@code 0} to * {@code getItemCount(series) - 1}). * * @return The wind direction. */ Number getWindDirection(int series, int item); /** * Returns the wind force on the Beaufort scale (0 to 12). See: *

* http://en.wikipedia.org/wiki/Beaufort_scale * * @param series the series (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item (in the range {@code 0} to * {@code getItemCount(series) - 1}). * * @return The wind force. */ Number getWindForce(int series, int item); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/XIntervalDataItem.java000066400000000000000000000064331463604235500275240ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * XIntervalDataItem.java * ---------------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.data.ComparableObjectItem; /** * An item representing data in the form (x, x-low, x-high, y). */ public class XIntervalDataItem extends ComparableObjectItem { /** * Creates a new instance of {@code XIntervalDataItem}. * * @param x the x-value. * @param xLow the lower bound of the x-interval. * @param xHigh the upper bound of the x-interval. * @param y the y-value. */ public XIntervalDataItem(double x, double xLow, double xHigh, double y) { super(x, new YWithXInterval(y, xLow, xHigh)); } /** * Returns the x-value. * * @return The x-value (never {@code null}). */ public Number getX() { return (Number) getComparable(); } /** * Returns the y-value. * * @return The y-value. */ public double getYValue() { YWithXInterval interval = (YWithXInterval) getObject(); if (interval != null) { return interval.getY(); } else { return Double.NaN; } } /** * Returns the lower bound of the x-interval. * * @return The lower bound of the x-interval. */ public double getXLowValue() { YWithXInterval interval = (YWithXInterval) getObject(); if (interval != null) { return interval.getXLow(); } else { return Double.NaN; } } /** * Returns the upper bound of the x-interval. * * @return The upper bound of the x-interval. */ public double getXHighValue() { YWithXInterval interval = (YWithXInterval) getObject(); if (interval != null) { return interval.getXHigh(); } else { return Double.NaN; } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/XIntervalSeries.java000066400000000000000000000123231463604235500272610ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * XIntervalSeries.java * -------------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.data.ComparableObjectItem; import org.jfree.data.ComparableObjectSeries; import org.jfree.data.general.SeriesChangeEvent; /** * A list of (x, x-low, x-high, y) data items. * * @see XIntervalSeriesCollection */ public class XIntervalSeries extends ComparableObjectSeries { /** * Creates a new empty series. By default, items added to the series will * be sorted into ascending order by x-value, and duplicate x-values will * be allowed (these defaults can be modified with another constructor. * * @param key the series key ({@code null} not permitted). */ public XIntervalSeries(Comparable key) { this(key, true, true); } /** * Constructs a new xy-series that contains no data. You can specify * whether or not duplicate x-values are allowed for the series. * * @param key the series key ({@code null} not permitted). * @param autoSort a flag that controls whether or not the items in the * series are sorted. * @param allowDuplicateXValues a flag that controls whether duplicate * x-values are allowed. */ public XIntervalSeries(Comparable key, boolean autoSort, boolean allowDuplicateXValues) { super(key, autoSort, allowDuplicateXValues); } /** * Adds a data item to the series and sends a {@link SeriesChangeEvent} to * all registered listeners. * * @param x the x-value. * @param y the y-value. * @param xLow the lower bound of the y-interval. * @param xHigh the upper bound of the y-interval. */ public void add(double x, double xLow, double xHigh, double y) { add(new XIntervalDataItem(x, xLow, xHigh, y), true); } /** * Adds a data item to the series and, if requested, sends a * {@link SeriesChangeEvent} to all registered listeners. * * @param item the data item ({@code null} not permitted). * @param notify notify listeners? */ public void add(XIntervalDataItem item, boolean notify) { super.add(item, notify); } /** * Returns the x-value for the specified item. * * @param index the item index. * * @return The x-value (never {@code null}). */ public Number getX(int index) { XIntervalDataItem item = (XIntervalDataItem) getDataItem(index); return item.getX(); } /** * Returns the lower bound of the x-interval for the specified item. * * @param index the item index. * * @return The lower bound of the x-interval. */ public double getXLowValue(int index) { XIntervalDataItem item = (XIntervalDataItem) getDataItem(index); return item.getXLowValue(); } /** * Returns the upper bound of the x-interval for the specified item. * * @param index the item index. * * @return The upper bound of the x-interval. */ public double getXHighValue(int index) { XIntervalDataItem item = (XIntervalDataItem) getDataItem(index); return item.getXHighValue(); } /** * Returns the y-value for the specified item. * * @param index the item index. * * @return The y-value. */ public double getYValue(int index) { XIntervalDataItem item = (XIntervalDataItem) getDataItem(index); return item.getYValue(); } /** * Returns the data item at the specified index. * * @param index the item index. * * @return The data item. */ @Override public ComparableObjectItem getDataItem(int index) { return super.getDataItem(index); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/XIntervalSeriesCollection.java000066400000000000000000000252571463604235500313070ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------ * XIntervalSeriesCollection.java * ------------------------------ * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import java.io.Serializable; import java.util.List; import java.util.Objects; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.general.DatasetChangeEvent; /** * A collection of {@link XIntervalSeries} objects. * * @see XIntervalSeries */ public class XIntervalSeriesCollection extends AbstractIntervalXYDataset implements IntervalXYDataset, PublicCloneable, Serializable { /** Storage for the data series. */ private List data; /** * Creates a new {@code XIntervalSeriesCollection} instance. */ public XIntervalSeriesCollection() { this.data = new java.util.ArrayList(); } /** * Adds a series to the collection and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param series the series ({@code null} not permitted). */ public void addSeries(XIntervalSeries series) { Args.nullNotPermitted(series, "series"); this.data.add(series); series.addChangeListener(this); fireDatasetChanged(); } /** * Returns the number of series in the collection. * * @return The series count. */ @Override public int getSeriesCount() { return this.data.size(); } /** * Returns a series from the collection. * * @param series the series index (zero-based). * * @return The series. * * @throws IllegalArgumentException if {@code series} is not in the * range {@code 0} to {@code getSeriesCount() - 1}. */ public XIntervalSeries getSeries(int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds"); } return (XIntervalSeries) this.data.get(series); } /** * Returns the key for a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * * @return The key for a series. * * @throws IllegalArgumentException if {@code series} is not in the * specified range. */ @Override public Comparable getSeriesKey(int series) { // defer argument checking return getSeries(series).getKey(); } /** * Returns the number of items in the specified series. * * @param series the series (zero-based index). * * @return The item count. * * @throws IllegalArgumentException if {@code series} is not in the * range {@code 0} to {@code getSeriesCount() - 1}. */ @Override public int getItemCount(int series) { // defer argument checking return getSeries(series).getItemCount(); } /** * Returns the x-value for an item within a series. * * @param series the series index. * @param item the item index. * * @return The x-value. */ @Override public Number getX(int series, int item) { XIntervalSeries s = (XIntervalSeries) this.data.get(series); XIntervalDataItem di = (XIntervalDataItem) s.getDataItem(item); return di.getX(); } /** * Returns the start x-value (as a double primitive) for an item within a * series. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public double getStartXValue(int series, int item) { XIntervalSeries s = (XIntervalSeries) this.data.get(series); return s.getXLowValue(item); } /** * Returns the end x-value (as a double primitive) for an item within a * series. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The value. */ @Override public double getEndXValue(int series, int item) { XIntervalSeries s = (XIntervalSeries) this.data.get(series); return s.getXHighValue(item); } /** * Returns the y-value (as a double primitive) for an item within a * series. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public double getYValue(int series, int item) { XIntervalSeries s = (XIntervalSeries) this.data.get(series); return s.getYValue(item); } /** * Returns the y-value for an item within a series. * * @param series the series index. * @param item the item index. * * @return The y-value. */ @Override public Number getY(int series, int item) { XIntervalSeries s = (XIntervalSeries) this.data.get(series); XIntervalDataItem di = (XIntervalDataItem) s.getDataItem(item); return di.getYValue(); } /** * Returns the start x-value for an item within a series. * * @param series the series index. * @param item the item index. * * @return The x-value. */ @Override public Number getStartX(int series, int item) { XIntervalSeries s = (XIntervalSeries) this.data.get(series); XIntervalDataItem di = (XIntervalDataItem) s.getDataItem(item); return di.getXLowValue(); } /** * Returns the end x-value for an item within a series. * * @param series the series index. * @param item the item index. * * @return The x-value. */ @Override public Number getEndX(int series, int item) { XIntervalSeries s = (XIntervalSeries) this.data.get(series); XIntervalDataItem di = (XIntervalDataItem) s.getDataItem(item); return di.getXHighValue(); } /** * Returns the start y-value for an item within a series. This method * maps directly to {@link #getY(int, int)}. * * @param series the series index. * @param item the item index. * * @return The start y-value. */ @Override public Number getStartY(int series, int item) { return getY(series, item); } /** * Returns the end y-value for an item within a series. This method * maps directly to {@link #getY(int, int)}. * * @param series the series index. * @param item the item index. * * @return The end y-value. */ @Override public Number getEndY(int series, int item) { return getY(series, item); } /** * Removes a series from the collection and sends a * {@link DatasetChangeEvent} to all registered listeners. * * @param series the series index (zero-based). */ public void removeSeries(int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds."); } XIntervalSeries ts = (XIntervalSeries) this.data.get(series); ts.removeChangeListener(this); this.data.remove(series); fireDatasetChanged(); } /** * Removes a series from the collection and sends a * {@link DatasetChangeEvent} to all registered listeners. * * @param series the series ({@code null} not permitted). */ public void removeSeries(XIntervalSeries series) { Args.nullNotPermitted(series, "series"); if (this.data.contains(series)) { series.removeChangeListener(this); this.data.remove(series); fireDatasetChanged(); } } /** * Removes all the series from the collection and sends a * {@link DatasetChangeEvent} to all registered listeners. */ public void removeAllSeries() { // Unregister the collection as a change listener to each series in // the collection. for (int i = 0; i < this.data.size(); i++) { XIntervalSeries series = (XIntervalSeries) this.data.get(i); series.removeChangeListener(this); } this.data.clear(); fireDatasetChanged(); } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XIntervalSeriesCollection)) { return false; } XIntervalSeriesCollection that = (XIntervalSeriesCollection) obj; return Objects.equals(this.data, that.data); } /** * Returns a clone of this instance. * * @return A clone. * * @throws CloneNotSupportedException if there is a problem. */ @Override public Object clone() throws CloneNotSupportedException { XIntervalSeriesCollection clone = (XIntervalSeriesCollection) super.clone(); clone.data = (List) ObjectUtils.deepClone(this.data); return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/XYBarDataset.java000066400000000000000000000255401463604235500264720ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * XYBarDataset.java * ----------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.general.DatasetChangeEvent; import org.jfree.data.general.DatasetChangeListener; /** * A dataset wrapper class that converts a standard {@link XYDataset} into an * {@link IntervalXYDataset} suitable for use in creating XY bar charts. */ public class XYBarDataset extends AbstractIntervalXYDataset implements IntervalXYDataset, DatasetChangeListener, PublicCloneable { /** The underlying dataset. */ private XYDataset underlying; /** The bar width. */ private double barWidth; /** * Creates a new dataset. * * @param underlying the underlying dataset ({@code null} not * permitted). * @param barWidth the width of the bars. */ public XYBarDataset(XYDataset underlying, double barWidth) { this.underlying = underlying; this.underlying.addChangeListener(this); this.barWidth = barWidth; } /** * Returns the underlying dataset that was specified via the constructor. * * @return The underlying dataset (never {@code null}). */ public XYDataset getUnderlyingDataset() { return this.underlying; } /** * Returns the bar width. * * @return The bar width. * * @see #setBarWidth(double) */ public double getBarWidth() { return this.barWidth; } /** * Sets the bar width and sends a {@link DatasetChangeEvent} to all * registered listeners. * * @param barWidth the bar width. * * @see #getBarWidth() */ public void setBarWidth(double barWidth) { this.barWidth = barWidth; notifyListeners(new DatasetChangeEvent(this, this)); } /** * Returns the number of series in the dataset. * * @return The series count. */ @Override public int getSeriesCount() { return this.underlying.getSeriesCount(); } /** * Returns the key for a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * * @return The series key. */ @Override public Comparable getSeriesKey(int series) { return this.underlying.getSeriesKey(series); } /** * Returns the number of items in a series. * * @param series the series index (zero-based). * * @return The item count. */ @Override public int getItemCount(int series) { return this.underlying.getItemCount(series); } /** * Returns the x-value for an item within a series. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The x-value. * * @see #getXValue(int, int) */ @Override public Number getX(int series, int item) { return this.underlying.getX(series, item); } /** * Returns the x-value (as a double primitive) for an item within a series. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. * * @see #getX(int, int) */ @Override public double getXValue(int series, int item) { return this.underlying.getXValue(series, item); } /** * Returns the y-value for an item within a series. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The y-value (possibly {@code null}). * * @see #getYValue(int, int) */ @Override public Number getY(int series, int item) { return this.underlying.getY(series, item); } /** * Returns the y-value (as a double primitive) for an item within a series. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. * * @see #getY(int, int) */ @Override public double getYValue(int series, int item) { return this.underlying.getYValue(series, item); } /** * Returns the starting X value for the specified series and item. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public Number getStartX(int series, int item) { Number result = null; Number xnum = this.underlying.getX(series, item); if (xnum != null) { result = xnum.doubleValue() - this.barWidth / 2.0; } return result; } /** * Returns the starting x-value (as a double primitive) for an item within * a series. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. * * @see #getXValue(int, int) */ @Override public double getStartXValue(int series, int item) { return getXValue(series, item) - this.barWidth / 2.0; } /** * Returns the ending X value for the specified series and item. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public Number getEndX(int series, int item) { Number result = null; Number xnum = this.underlying.getX(series, item); if (xnum != null) { result = xnum.doubleValue() + this.barWidth / 2.0; } return result; } /** * Returns the ending x-value (as a double primitive) for an item within * a series. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. * * @see #getXValue(int, int) */ @Override public double getEndXValue(int series, int item) { return getXValue(series, item) + this.barWidth / 2.0; } /** * Returns the starting Y value for the specified series and item. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public Number getStartY(int series, int item) { return this.underlying.getY(series, item); } /** * Returns the starting y-value (as a double primitive) for an item within * a series. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. * * @see #getYValue(int, int) */ @Override public double getStartYValue(int series, int item) { return getYValue(series, item); } /** * Returns the ending Y value for the specified series and item. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public Number getEndY(int series, int item) { return this.underlying.getY(series, item); } /** * Returns the ending y-value (as a double primitive) for an item within * a series. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. * * @see #getYValue(int, int) */ @Override public double getEndYValue(int series, int item) { return getYValue(series, item); } /** * Receives notification of an dataset change event. * * @param event information about the event. */ @Override public void datasetChanged(DatasetChangeEvent event) { notifyListeners(event); } /** * Tests this dataset for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYBarDataset)) { return false; } XYBarDataset that = (XYBarDataset) obj; if (!this.underlying.equals(that.underlying)) { return false; } if (this.barWidth != that.barWidth) { return false; } return true; } /** * Returns an independent copy of the dataset. Note that: *

    *
  • the underlying dataset is only cloned if it implements the * {@link PublicCloneable} interface;
  • *
  • the listeners registered with this dataset are not carried over to * the cloned dataset.
  • *
* * @return An independent copy of the dataset. * * @throws CloneNotSupportedException if the dataset cannot be cloned for * any reason. */ @Override public Object clone() throws CloneNotSupportedException { XYBarDataset clone = (XYBarDataset) super.clone(); if (this.underlying instanceof PublicCloneable) { PublicCloneable pc = (PublicCloneable) this.underlying; clone.underlying = (XYDataset) pc.clone(); } return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/XYCoordinate.java000066400000000000000000000107271463604235500265500ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * XYCoordinate.java * ----------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import java.io.Serializable; /** * Represents an (x, y) coordinate. */ public class XYCoordinate implements Comparable, Serializable { /** The x-coordinate. */ private double x; /** The y-coordinate. */ private double y; /** * Creates a new coordinate for the point (0.0, 0.0). */ public XYCoordinate() { this(0.0, 0.0); } /** * Creates a new coordinate for the point (x, y). * * @param x the x-coordinate. * @param y the y-coordinate. */ public XYCoordinate(double x, double y) { this.x = x; this.y = y; } /** * Returns the x-coordinate. * * @return The x-coordinate. */ public double getX() { return this.x; } /** * Returns the y-coordinate. * * @return The y-coordinate. */ public double getY() { return this.y; } /** * Tests this coordinate for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYCoordinate)) { return false; } XYCoordinate that = (XYCoordinate) obj; if (this.x != that.x) { return false; } if (this.y != that.y) { return false; } return true; } /** * Returns a hash code for this instance. * * @return A hash code. */ @Override public int hashCode() { int result = 193; long temp = Double.doubleToLongBits(this.x); result = 37 * result + (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(this.y); result = 37 * result + (int) (temp ^ (temp >>> 32)); return result; } /** * Returns a string representation of this instance, primarily for * debugging purposes. * * @return A string. */ @Override public String toString() { return "(" + this.x + ", " + this.y + ")"; } /** * Compares this instance against an arbitrary object. * * @param obj the object ({@code null} not permitted). * * @return An integer indicating the relative order of the items. */ @Override public int compareTo(Object obj) { if (!(obj instanceof XYCoordinate)) { throw new IllegalArgumentException("Incomparable object."); } XYCoordinate that = (XYCoordinate) obj; if (this.x > that.x) { return 1; } else if (this.x < that.x) { return -1; } else { if (this.y > that.y) { return 1; } else if (this.y < that.y) { return -1; } } return 0; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/XYDataItem.java000066400000000000000000000156171463604235500261540ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * XYDataItem.java * --------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import java.io.Serializable; import java.util.Objects; import org.jfree.chart.util.Args; /** * Represents one (x, y) data item for an {@link XYSeries}. Note that * subclasses are REQUIRED to support cloning. */ public class XYDataItem implements Cloneable, Comparable, Serializable { /** For serialization. */ private static final long serialVersionUID = 2751513470325494890L; /** The x-value ({@code null} not permitted). */ private Number x; /** The y-value. */ private Number y; /** * Constructs a new data item. * * @param x the x-value ({@code null} NOT permitted). * @param y the y-value ({@code null} permitted). */ public XYDataItem(Number x, Number y) { Args.nullNotPermitted(x, "x"); this.x = x; this.y = y; } /** * Constructs a new data item. * * @param x the x-value. * @param y the y-value. */ public XYDataItem(double x, double y) { this(Double.valueOf(x), Double.valueOf(y)); } /** * Returns the x-value. * * @return The x-value (never {@code null}). */ public Number getX() { return this.x; } /** * Returns the x-value as a double primitive. * * @return The x-value. * * @see #getX() * @see #getYValue() */ public double getXValue() { // this.x is not allowed to be null... return this.x.doubleValue(); } /** * Returns the y-value. * * @return The y-value (possibly {@code null}). */ public Number getY() { return this.y; } /** * Returns the y-value as a double primitive. * * @return The y-value. * * @see #getY() * @see #getXValue() */ public double getYValue() { double result = Double.NaN; if (this.y != null) { result = this.y.doubleValue(); } return result; } /** * Sets the y-value for this data item. Note that there is no * corresponding method to change the x-value. * * @param y the new y-value. */ public void setY(double y) { setY(Double.valueOf(y)); } /** * Sets the y-value for this data item. Note that there is no * corresponding method to change the x-value. * * @param y the new y-value ({@code null} permitted). */ public void setY(Number y) { this.y = y; } /** * Returns an integer indicating the order of this object relative to * another object. *

* For the order we consider only the x-value: * negative == "less-than", zero == "equal", positive == "greater-than". * * @param o1 the object being compared to. * * @return An integer indicating the order of this data pair object * relative to another object. */ @Override public int compareTo(Object o1) { int result; // CASE 1 : Comparing to another TimeSeriesDataPair object // ------------------------------------------------------- if (o1 instanceof XYDataItem) { XYDataItem dataItem = (XYDataItem) o1; double compare = this.x.doubleValue() - dataItem.getX().doubleValue(); if (compare > 0.0) { result = 1; } else { if (compare < 0.0) { result = -1; } else { result = 0; } } } // CASE 2 : Comparing to a general object // --------------------------------------------- else { // consider time periods to be ordered after general objects result = 1; } return result; } /** * Returns a clone of this object. * * @return A clone. */ @Override public Object clone() { Object clone = null; try { clone = super.clone(); } catch (CloneNotSupportedException e) { // won't get here... e.printStackTrace(); } return clone; } /** * Tests if this object is equal to another. * * @param obj the object to test against for equality ({@code null} * permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYDataItem)) { return false; } XYDataItem that = (XYDataItem) obj; if (!this.x.equals(that.x)) { return false; } if (!Objects.equals(this.y, that.y)) { return false; } return true; } /** * Returns a hash code. * * @return A hash code. */ @Override public int hashCode() { int result; result = this.x.hashCode(); result = 29 * result + (this.y != null ? this.y.hashCode() : 0); return result; } /** * Returns a string representing this instance, primarily for debugging * use. * * @return A string. */ @Override public String toString() { return "[" + getXValue() + ", " + getYValue() + "]"; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/XYDataset.java000066400000000000000000000077501463604235500260500ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * XYDataset.java * -------------- * (C) Copyright 2000-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.data.DomainOrder; import org.jfree.data.general.SeriesDataset; /** * An interface through which data in the form of (x, y) items can be accessed. */ public interface XYDataset extends SeriesDataset { /** * Returns the order of the domain (or X) values returned by the dataset. * * @return The order (never {@code null}). */ DomainOrder getDomainOrder(); /** * Returns the number of items in a series. *

* It is recommended that classes that implement this method should throw * an {@code IllegalArgumentException} if the {@code series} * argument is outside the specified range. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * * @return The item count. */ int getItemCount(int series); /** * Returns the x-value for an item within a series. The x-values may or * may not be returned in ascending order, that is up to the class * implementing the interface. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item index (in the range {@code 0} to * {@code getItemCount(series)}). * * @return The x-value (never {@code null}). */ Number getX(int series, int item); /** * Returns the x-value for an item within a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item index (in the range {@code 0} to * {@code getItemCount(series)}). * * @return The x-value. */ double getXValue(int series, int item); /** * Returns the y-value for an item within a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item index (in the range {@code 0} to * {@code getItemCount(series)}). * * @return The y-value (possibly {@code null}). */ Number getY(int series, int item); /** * Returns the y-value (as a double primitive) for an item within a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * @param item the item index (in the range {@code 0} to * {@code getItemCount(series)}). * * @return The y-value. */ double getYValue(int series, int item); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/XYDatasetTableModel.java000066400000000000000000000131111463604235500277650ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * XYDatasetTableModel.java * ------------------------ * (C)opyright 2003-present, by Bryan Scott and Contributors. * * Original Author: Bryan Scott; * Contributor(s): David Gilbert; * */ package org.jfree.data.xy; import javax.swing.table.AbstractTableModel; import javax.swing.table.TableModel; import org.jfree.data.general.DatasetChangeEvent; import org.jfree.data.general.DatasetChangeListener; /** * A READ-ONLY wrapper around a {@link TableXYDataset} to convert it to a * table model for use in a JTable. The first column of the table shows the * x-values, the remaining columns show the y-values for each series (series 0 * appears in column 1, series 1 appears in column 2, etc). *

* TO DO: *

    *
  • implement proper naming for x axis (getColumnName)
  • *
  • implement setValueAt to remove READ-ONLY constraint (not sure how)
  • *
*/ public class XYDatasetTableModel extends AbstractTableModel implements TableModel, DatasetChangeListener { /** The dataset. */ TableXYDataset model = null; /** * Default constructor. */ public XYDatasetTableModel() { super(); } /** * Creates a new table model based on the specified dataset. * * @param dataset the dataset. */ public XYDatasetTableModel(TableXYDataset dataset) { this(); this.model = dataset; this.model.addChangeListener(this); } /** * Sets the model (dataset). * * @param dataset the dataset. */ public void setModel(TableXYDataset dataset) { this.model = dataset; this.model.addChangeListener(this); fireTableDataChanged(); } /** * Returns the number of rows. * * @return The row count. */ @Override public int getRowCount() { if (this.model == null) { return 0; } return this.model.getItemCount(); } /** * Gets the number of columns in the model. * * @return The number of columns in the model. */ @Override public int getColumnCount() { if (this.model == null) { return 0; } return this.model.getSeriesCount() + 1; } /** * Returns the column name. * * @param column the column index. * * @return The column name. */ @Override public String getColumnName(int column) { if (this.model == null) { return super.getColumnName(column); } if (column < 1) { return "X Value"; } else { return this.model.getSeriesKey(column - 1).toString(); } } /** * Returns a value of the specified cell. * Column 0 is the X axis, Columns 1 and over are the Y axis * * @param row the row number. * @param column the column number. * * @return The value of the specified cell. */ @Override public Object getValueAt(int row, int column) { if (this.model == null) { return null; } if (column < 1) { return this.model.getX(0, row); } else { return this.model.getY(column - 1, row); } } /** * Receives notification that the underlying dataset has changed. * * @param event the event * * @see DatasetChangeListener */ @Override public void datasetChanged(DatasetChangeEvent event) { fireTableDataChanged(); } /** * Returns a flag indicating whether or not the specified cell is editable. * * @param row the row number. * @param column the column number. * * @return {@code true} if the specified cell is editable. */ @Override public boolean isCellEditable(int row, int column) { return false; } /** * Updates the {@link XYDataset} if allowed. * * @param value the new value. * @param row the row. * @param column the column. */ @Override public void setValueAt(Object value, int row, int column) { if (isCellEditable(row, column)) { // XYDataset only provides methods for reading a dataset... } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/XYDomainInfo.java000066400000000000000000000041541463604235500265010ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * XYDomainInfo.java * ----------------- * (C) Copyright 2009-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import java.util.List; import org.jfree.data.Range; /** * An interface that can (optionally) be implemented by a dataset to assist in * determining the minimum and maximum x-values in the dataset. */ public interface XYDomainInfo { /** * Returns the range of the values in this dataset's domain. * * @param visibleSeriesKeys the keys of the visible series. * @param includeInterval a flag that determines whether or not the * y-interval is taken into account. * * @return The range (or {@code null} if the dataset contains no * values). */ Range getDomainBounds(List visibleSeriesKeys, boolean includeInterval); }jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/XYInterval.java000066400000000000000000000104151463604235500262370ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * XYInterval.java * --------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import java.io.Serializable; /** * An xy-interval. This class is used internally by the * {@link XYIntervalDataItem} class. */ public class XYInterval implements Serializable { /** The lower bound of the x-interval. */ private double xLow; /** The upper bound of the y-interval. */ private double xHigh; /** The y-value. */ private double y; /** The lower bound of the y-interval. */ private double yLow; /** The upper bound of the y-interval. */ private double yHigh; /** * Creates a new instance of {@code XYInterval}. * * @param xLow the lower bound of the x-interval. * @param xHigh the upper bound of the y-interval. * @param y the y-value. * @param yLow the lower bound of the y-interval. * @param yHigh the upper bound of the y-interval. */ public XYInterval(double xLow, double xHigh, double y, double yLow, double yHigh) { this.xLow = xLow; this.xHigh = xHigh; this.y = y; this.yLow = yLow; this.yHigh = yHigh; } /** * Returns the lower bound of the x-interval. * * @return The lower bound of the x-interval. */ public double getXLow() { return this.xLow; } /** * Returns the upper bound of the x-interval. * * @return The upper bound of the x-interval. */ public double getXHigh() { return this.xHigh; } /** * Returns the y-value. * * @return The y-value. */ public double getY() { return this.y; } /** * Returns the lower bound of the y-interval. * * @return The lower bound of the y-interval. */ public double getYLow() { return this.yLow; } /** * Returns the upper bound of the y-interval. * * @return The upper bound of the y-interval. */ public double getYHigh() { return this.yHigh; } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYInterval)) { return false; } XYInterval that = (XYInterval) obj; if (this.xLow != that.xLow) { return false; } if (this.xHigh != that.xHigh) { return false; } if (this.y != that.y) { return false; } if (this.yLow != that.yLow) { return false; } if (this.yHigh != that.yHigh) { return false; } return true; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/XYIntervalDataItem.java000066400000000000000000000102421463604235500276460ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * XYIntervalDataItem.java * ----------------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.data.ComparableObjectItem; /** * An item representing data in the form (x, x-low, x-high, y, y-low, y-high). */ public class XYIntervalDataItem extends ComparableObjectItem { /** * Creates a new instance of {@code XYIntervalItem}. * * @param x the x-value. * @param xLow the lower bound of the x-interval. * @param xHigh the upper bound of the x-interval. * @param y the y-value. * @param yLow the lower bound of the y-interval. * @param yHigh the upper bound of the y-interval. */ public XYIntervalDataItem(double x, double xLow, double xHigh, double y, double yLow, double yHigh) { super(x, new XYInterval(xLow, xHigh, y, yLow, yHigh)); } /** * Returns the x-value. * * @return The x-value (never {@code null}). */ public Double getX() { return (Double) getComparable(); } /** * Returns the y-value. * * @return The y-value. */ public double getYValue() { XYInterval interval = (XYInterval) getObject(); if (interval != null) { return interval.getY(); } else { return Double.NaN; } } /** * Returns the lower bound of the x-interval. * * @return The lower bound of the x-interval. */ public double getXLowValue() { XYInterval interval = (XYInterval) getObject(); if (interval != null) { return interval.getXLow(); } else { return Double.NaN; } } /** * Returns the upper bound of the x-interval. * * @return The upper bound of the x-interval. */ public double getXHighValue() { XYInterval interval = (XYInterval) getObject(); if (interval != null) { return interval.getXHigh(); } else { return Double.NaN; } } /** * Returns the lower bound of the y-interval. * * @return The lower bound of the y-interval. */ public double getYLowValue() { XYInterval interval = (XYInterval) getObject(); if (interval != null) { return interval.getYLow(); } else { return Double.NaN; } } /** * Returns the upper bound of the y-interval. * * @return The upper bound of the y-interval. */ public double getYHighValue() { XYInterval interval = (XYInterval) getObject(); if (interval != null) { return interval.getYHigh(); } else { return Double.NaN; } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/XYIntervalSeries.java000066400000000000000000000143211463604235500274120ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * XYIntervalSeries.java * --------------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.data.ComparableObjectItem; import org.jfree.data.ComparableObjectSeries; import org.jfree.data.general.SeriesChangeEvent; /** * A list of (x, x-low, x-high, y, y-low, y-high) data items. * * @see XYIntervalSeriesCollection */ public class XYIntervalSeries extends ComparableObjectSeries { /** * Creates a new empty series. By default, items added to the series will * be sorted into ascending order by x-value, and duplicate x-values will * be allowed (these defaults can be modified with another constructor). * * @param key the series key ({@code null} not permitted). */ public XYIntervalSeries(Comparable key) { this(key, true, true); } /** * Constructs a new xy-series that contains no data. You can specify * whether or not duplicate x-values are allowed for the series. * * @param key the series key ({@code null} not permitted). * @param autoSort a flag that controls whether or not the items in the * series are sorted. * @param allowDuplicateXValues a flag that controls whether duplicate * x-values are allowed. */ public XYIntervalSeries(Comparable key, boolean autoSort, boolean allowDuplicateXValues) { super(key, autoSort, allowDuplicateXValues); } /** * Adds a data item to the series and sends a {@link SeriesChangeEvent} to * all registered listeners. * * @param x the x-value. * @param xLow the lower bound of the x-interval. * @param xHigh the upper bound of the x-interval. * @param y the y-value. * @param yLow the lower bound of the y-interval. * @param yHigh the upper bound of the y-interval. */ public void add(double x, double xLow, double xHigh, double y, double yLow, double yHigh) { add(new XYIntervalDataItem(x, xLow, xHigh, y, yLow, yHigh), true); } /** * Adds a data item to the series and, if requested, sends a * {@link SeriesChangeEvent} to all registered listeners. * * @param item the data item ({@code null} not permitted). * @param notify notify listeners? */ public void add(XYIntervalDataItem item, boolean notify) { super.add(item, notify); } /** * Returns the x-value for the specified item. * * @param index the item index. * * @return The x-value (never {@code null}). */ public Number getX(int index) { XYIntervalDataItem item = (XYIntervalDataItem) getDataItem(index); return item.getX(); } /** * Returns the lower bound of the x-interval for the specified item in the * series. * * @param index the item index. * * @return The lower bound of the x-interval. */ public double getXLowValue(int index) { XYIntervalDataItem item = (XYIntervalDataItem) getDataItem(index); return item.getXLowValue(); } /** * Returns the upper bound of the x-interval for the specified item in the * series. * * @param index the item index. * * @return The upper bound of the x-interval. */ public double getXHighValue(int index) { XYIntervalDataItem item = (XYIntervalDataItem) getDataItem(index); return item.getXHighValue(); } /** * Returns the y-value for the specified item. * * @param index the item index. * * @return The y-value. */ public double getYValue(int index) { XYIntervalDataItem item = (XYIntervalDataItem) getDataItem(index); return item.getYValue(); } /** * Returns the lower bound of the Y-interval for the specified item in the * series. * * @param index the item index. * * @return The lower bound of the Y-interval. */ public double getYLowValue(int index) { XYIntervalDataItem item = (XYIntervalDataItem) getDataItem(index); return item.getYLowValue(); } /** * Returns the upper bound of the y-interval for the specified item in the * series. * * @param index the item index. * * @return The upper bound of the y-interval. */ public double getYHighValue(int index) { XYIntervalDataItem item = (XYIntervalDataItem) getDataItem(index); return item.getYHighValue(); } /** * Returns the data item at the specified index. * * @param index the item index. * * @return The data item. */ @Override public ComparableObjectItem getDataItem(int index) { return super.getDataItem(index); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/XYIntervalSeriesCollection.java000066400000000000000000000264341463604235500314360ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------- * XYIntervalSeriesCollection.java * ------------------------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import java.io.Serializable; import java.util.List; import java.util.Objects; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.general.DatasetChangeEvent; /** * A collection of {@link XYIntervalSeries} objects. * * @see XYIntervalSeries */ public class XYIntervalSeriesCollection extends AbstractIntervalXYDataset implements IntervalXYDataset, PublicCloneable, Serializable { /** Storage for the data series. */ private List data; /** * Creates a new instance of {@code XIntervalSeriesCollection}. */ public XYIntervalSeriesCollection() { this.data = new java.util.ArrayList(); } /** * Adds a series to the collection and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param series the series ({@code null} not permitted). */ public void addSeries(XYIntervalSeries series) { Args.nullNotPermitted(series, "series"); this.data.add(series); series.addChangeListener(this); fireDatasetChanged(); } /** * Returns the number of series in the collection. * * @return The series count. */ @Override public int getSeriesCount() { return this.data.size(); } /** * Returns a series from the collection. * * @param series the series index (zero-based). * * @return The series. * * @throws IllegalArgumentException if {@code series} is not in the * range {@code 0} to {@code getSeriesCount() - 1}. */ public XYIntervalSeries getSeries(int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds"); } return (XYIntervalSeries) this.data.get(series); } /** * Returns the key for a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * * @return The key for a series. * * @throws IllegalArgumentException if {@code series} is not in the * specified range. */ @Override public Comparable getSeriesKey(int series) { // defer argument checking return getSeries(series).getKey(); } /** * Returns the number of items in the specified series. * * @param series the series (zero-based index). * * @return The item count. * * @throws IllegalArgumentException if {@code series} is not in the * range {@code 0} to {@code getSeriesCount() - 1}. */ @Override public int getItemCount(int series) { // defer argument checking return getSeries(series).getItemCount(); } /** * Returns the x-value for an item within a series. * * @param series the series index. * @param item the item index. * * @return The x-value. */ @Override public Number getX(int series, int item) { XYIntervalSeries s = (XYIntervalSeries) this.data.get(series); return s.getX(item); } /** * Returns the start x-value (as a double primitive) for an item within a * series. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public double getStartXValue(int series, int item) { XYIntervalSeries s = (XYIntervalSeries) this.data.get(series); return s.getXLowValue(item); } /** * Returns the end x-value (as a double primitive) for an item within a * series. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public double getEndXValue(int series, int item) { XYIntervalSeries s = (XYIntervalSeries) this.data.get(series); return s.getXHighValue(item); } /** * Returns the y-value (as a double primitive) for an item within a * series. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public double getYValue(int series, int item) { XYIntervalSeries s = (XYIntervalSeries) this.data.get(series); return s.getYValue(item); } /** * Returns the start y-value (as a double primitive) for an item within a * series. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public double getStartYValue(int series, int item) { XYIntervalSeries s = (XYIntervalSeries) this.data.get(series); return s.getYLowValue(item); } /** * Returns the end y-value (as a double primitive) for an item within a * series. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The value. */ @Override public double getEndYValue(int series, int item) { XYIntervalSeries s = (XYIntervalSeries) this.data.get(series); return s.getYHighValue(item); } /** * Returns the y-value for an item within a series. * * @param series the series index. * @param item the item index. * * @return The y-value. */ @Override public Number getY(int series, int item) { return getYValue(series, item); } /** * Returns the start x-value for an item within a series. * * @param series the series index. * @param item the item index. * * @return The x-value. */ @Override public Number getStartX(int series, int item) { return getStartXValue(series, item); } /** * Returns the end x-value for an item within a series. * * @param series the series index. * @param item the item index. * * @return The x-value. */ @Override public Number getEndX(int series, int item) { return getEndXValue(series, item); } /** * Returns the start y-value for an item within a series. This method * maps directly to {@link #getY(int, int)}. * * @param series the series index. * @param item the item index. * * @return The start y-value. */ @Override public Number getStartY(int series, int item) { return getStartYValue(series, item); } /** * Returns the end y-value for an item within a series. This method * maps directly to {@link #getY(int, int)}. * * @param series the series index. * @param item the item index. * * @return The end y-value. */ @Override public Number getEndY(int series, int item) { return getEndYValue(series, item); } /** * Removes a series from the collection and sends a * {@link DatasetChangeEvent} to all registered listeners. * * @param series the series index (zero-based). */ public void removeSeries(int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds."); } XYIntervalSeries ts = (XYIntervalSeries) this.data.get(series); ts.removeChangeListener(this); this.data.remove(series); fireDatasetChanged(); } /** * Removes a series from the collection and sends a * {@link DatasetChangeEvent} to all registered listeners. * * @param series the series ({@code null} not permitted). */ public void removeSeries(XYIntervalSeries series) { Args.nullNotPermitted(series, "series"); if (this.data.contains(series)) { series.removeChangeListener(this); this.data.remove(series); fireDatasetChanged(); } } /** * Removes all the series from the collection and sends a * {@link DatasetChangeEvent} to all registered listeners. */ public void removeAllSeries() { // Unregister the collection as a change listener to each series in // the collection. for (int i = 0; i < this.data.size(); i++) { XYIntervalSeries series = (XYIntervalSeries) this.data.get(i); series.removeChangeListener(this); } this.data.clear(); fireDatasetChanged(); } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYIntervalSeriesCollection)) { return false; } XYIntervalSeriesCollection that = (XYIntervalSeriesCollection) obj; return Objects.equals(this.data, that.data); } /** * Returns a clone of this dataset. * * @return A clone of this dataset. * * @throws CloneNotSupportedException if there is a problem cloning. */ @Override public Object clone() throws CloneNotSupportedException { XYIntervalSeriesCollection clone = (XYIntervalSeriesCollection) super.clone(); int seriesCount = getSeriesCount(); clone.data = new java.util.ArrayList(seriesCount); for (int i = 0; i < this.data.size(); i++) { clone.data.set(i, getSeries(i).clone()); } return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/XYItemKey.java000066400000000000000000000106221463604235500260220ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * XYItemKey.java * -------------- * (C) Copyright 2014-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import java.io.Serializable; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.Args; import org.jfree.data.ItemKey; /** * An object that references one data item in an {@link XYZDataset}. This is * used internally to track the data item that a 3D object is related to, if * any (and later that link is used for chart interaction). Instances of * this class are immutable. * * @param the series key type. */ public class XYItemKey> implements ItemKey, Comparable>, Serializable { /** A key identifying a series in the dataset. */ private final S seriesKey; /** The index of an item within a series. */ private final int itemIndex; /** * Creates a new instance. * * @param seriesKey the series key. * @param itemIndex the item index. */ public XYItemKey(S seriesKey, int itemIndex) { Args.nullNotPermitted(seriesKey, "seriesKey"); this.seriesKey = seriesKey; this.itemIndex = itemIndex; } /** * Returns the series key. * * @return The series key (never {@code null}). */ public S getSeriesKey() { return this.seriesKey; } /** * Returns the item index. * * @return The item index. */ public int getItemIndex() { return this.itemIndex; } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object to test ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYItemKey)) { return false; } XYItemKey that = (XYItemKey) obj; if (!this.seriesKey.equals(that.seriesKey)) { return false; } if (this.itemIndex != that.itemIndex) { return false; } return true; } @Override public int hashCode() { int hash = 7; hash = 41 * hash + ObjectUtils.hashCode(this.seriesKey); hash = 41 * hash + this.itemIndex; return hash; } @Override public String toJSONString() { StringBuilder sb = new StringBuilder(); sb.append("{\"seriesKey\": \"").append(this.seriesKey.toString()); sb.append("\", "); sb.append("\"itemIndex\": ").append(this.itemIndex).append("}"); return sb.toString(); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("XYItemKey[seriesKey="); sb.append(this.seriesKey.toString()).append(",item="); sb.append(itemIndex); sb.append("]"); return sb.toString(); } @Override public int compareTo(XYItemKey key) { int result = this.seriesKey.compareTo(key.seriesKey); if (result == 0) { result = this.itemIndex - key.itemIndex; } return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/XYRangeInfo.java000066400000000000000000000042441463604235500263260ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * XYRangeInfo.java * ---------------- * (C) Copyright 2009-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import java.util.List; import org.jfree.data.Range; /** * An interface that can (optionally) be implemented by a dataset to assist in * determining the minimum and maximum y-values. */ public interface XYRangeInfo { /** * Returns the range of the values in this dataset's range. * * @param visibleSeriesKeys the keys of the visible series. * @param xRange the x-range ({@code null} not permitted). * @param includeInterval a flag that determines whether or not the * y-interval is taken into account. * * @return The range (or {@code null} if the dataset contains no * values). */ Range getRangeBounds(List visibleSeriesKeys, Range xRange, boolean includeInterval); }jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/XYSeries.java000066400000000000000000000752521463604235500257170ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------- * XYSeries.java * ------------- * (C) Copyright 2001-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Aaron Metzger; * Jonathan Gabbai; * Richard Atkinson; * Michel Santos; * Ted Schwartz (fix for bug 1955483); * */ package org.jfree.data.xy; import java.io.Serializable; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Objects; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.Args; import org.jfree.data.general.Series; import org.jfree.data.general.SeriesChangeEvent; import org.jfree.data.general.SeriesException; /** * Represents a sequence of zero or more data items in the form (x, y). By * default, items in the series will be sorted into ascending order by x-value, * and duplicate x-values are permitted. Both the sorting and duplicate * defaults can be changed in the constructor. Y-values can be * {@code null} to represent missing values. */ public class XYSeries extends Series implements Cloneable, Serializable { /** For serialization. */ static final long serialVersionUID = -5908509288197150436L; // In version 0.9.12, in response to several developer requests, I changed // the 'data' attribute from 'private' to 'protected', so that others can // make subclasses that work directly with the underlying data structure. /** Storage for the data items in the series. */ protected List data; /** The maximum number of items for the series. */ private int maximumItemCount = Integer.MAX_VALUE; /** * A flag that controls whether the items are automatically sorted * (by x-value ascending). */ private boolean autoSort; /** A flag that controls whether or not duplicate x-values are allowed. */ private boolean allowDuplicateXValues; /** The lowest x-value in the series, excluding Double.NaN values. */ private double minX; /** The highest x-value in the series, excluding Double.NaN values. */ private double maxX; /** The lowest y-value in the series, excluding Double.NaN values. */ private double minY; /** The highest y-value in the series, excluding Double.NaN values. */ private double maxY; /** * Creates a new empty series. By default, items added to the series will * be sorted into ascending order by x-value, and duplicate x-values will * be allowed (these defaults can be modified with another constructor). * * @param key the series key ({@code null} not permitted). */ public XYSeries(Comparable key) { this(key, true, true); } /** * Constructs a new empty series, with the auto-sort flag set as requested, * and duplicate values allowed. * * @param key the series key ({@code null} not permitted). * @param autoSort a flag that controls whether or not the items in the * series are sorted. */ public XYSeries(Comparable key, boolean autoSort) { this(key, autoSort, true); } /** * Constructs a new xy-series that contains no data. You can specify * whether or not duplicate x-values are allowed for the series. * * @param key the series key ({@code null} not permitted). * @param autoSort a flag that controls whether or not the items in the * series are sorted. * @param allowDuplicateXValues a flag that controls whether duplicate * x-values are allowed. */ public XYSeries(Comparable key, boolean autoSort, boolean allowDuplicateXValues) { super(key); this.data = new java.util.ArrayList(); this.autoSort = autoSort; this.allowDuplicateXValues = allowDuplicateXValues; this.minX = Double.NaN; this.maxX = Double.NaN; this.minY = Double.NaN; this.maxY = Double.NaN; } /** * Returns the smallest x-value in the series, ignoring any Double.NaN * values. This method returns Double.NaN if there is no smallest x-value * (for example, when the series is empty). * * @return The smallest x-value. * * @see #getMaxX() */ public double getMinX() { return this.minX; } /** * Returns the largest x-value in the series, ignoring any Double.NaN * values. This method returns Double.NaN if there is no largest x-value * (for example, when the series is empty). * * @return The largest x-value. * * @see #getMinX() */ public double getMaxX() { return this.maxX; } /** * Returns the smallest y-value in the series, ignoring any null and * Double.NaN values. This method returns Double.NaN if there is no * smallest y-value (for example, when the series is empty). * * @return The smallest y-value. * * @see #getMaxY() */ public double getMinY() { return this.minY; } /** * Returns the largest y-value in the series, ignoring any Double.NaN * values. This method returns Double.NaN if there is no largest y-value * (for example, when the series is empty). * * @return The largest y-value. * * @see #getMinY() */ public double getMaxY() { return this.maxY; } /** * Updates the cached values for the minimum and maximum data values. * * @param item the item added ({@code null} not permitted). */ private void updateBoundsForAddedItem(XYDataItem item) { double x = item.getXValue(); this.minX = minIgnoreNaN(this.minX, x); this.maxX = maxIgnoreNaN(this.maxX, x); if (item.getY() != null) { double y = item.getYValue(); this.minY = minIgnoreNaN(this.minY, y); this.maxY = maxIgnoreNaN(this.maxY, y); } } /** * Updates the cached values for the minimum and maximum data values on * the basis that the specified item has just been removed. * * @param item the item added ({@code null} not permitted). */ private void updateBoundsForRemovedItem(XYDataItem item) { boolean itemContributesToXBounds = false; boolean itemContributesToYBounds = false; double x = item.getXValue(); if (!Double.isNaN(x)) { if (x <= this.minX || x >= this.maxX) { itemContributesToXBounds = true; } } if (item.getY() != null) { double y = item.getYValue(); if (!Double.isNaN(y)) { if (y <= this.minY || y >= this.maxY) { itemContributesToYBounds = true; } } } if (itemContributesToYBounds) { findBoundsByIteration(); } else if (itemContributesToXBounds) { if (getAutoSort()) { this.minX = getX(0).doubleValue(); this.maxX = getX(getItemCount() - 1).doubleValue(); } else { findBoundsByIteration(); } } } /** * Finds the bounds of the x and y values for the series, by iterating * through all the data items. */ private void findBoundsByIteration() { this.minX = Double.NaN; this.maxX = Double.NaN; this.minY = Double.NaN; this.maxY = Double.NaN; Iterator iterator = this.data.iterator(); while (iterator.hasNext()) { XYDataItem item = (XYDataItem) iterator.next(); updateBoundsForAddedItem(item); } } /** * Returns the flag that controls whether the items in the series are * automatically sorted. There is no setter for this flag, it must be * defined in the series constructor. * * @return A boolean. */ public boolean getAutoSort() { return this.autoSort; } /** * Returns a flag that controls whether duplicate x-values are allowed. * This flag can only be set in the constructor. * * @return A boolean. */ public boolean getAllowDuplicateXValues() { return this.allowDuplicateXValues; } /** * Returns the number of items in the series. * * @return The item count. * * @see #getItems() */ @Override public int getItemCount() { return this.data.size(); } /** * Returns the list of data items for the series (the list contains * {@link XYDataItem} objects and is unmodifiable). * * @return The list of data items. */ public List getItems() { return Collections.unmodifiableList(this.data); } /** * Returns the maximum number of items that will be retained in the series. * The default value is {@code Integer.MAX_VALUE}. * * @return The maximum item count. * * @see #setMaximumItemCount(int) */ public int getMaximumItemCount() { return this.maximumItemCount; } /** * Sets the maximum number of items that will be retained in the series. * If you add a new item to the series such that the number of items will * exceed the maximum item count, then the first element in the series is * automatically removed, ensuring that the maximum item count is not * exceeded. *

* Typically this value is set before the series is populated with data, * but if it is applied later, it may cause some items to be removed from * the series (in which case a {@link SeriesChangeEvent} will be sent to * all registered listeners). * * @param maximum the maximum number of items for the series. */ public void setMaximumItemCount(int maximum) { this.maximumItemCount = maximum; int remove = this.data.size() - maximum; if (remove > 0) { this.data.subList(0, remove).clear(); findBoundsByIteration(); fireSeriesChanged(); } } /** * Adds a data item to the series and sends a {@link SeriesChangeEvent} to * all registered listeners. * * @param item the (x, y) item ({@code null} not permitted). */ public void add(XYDataItem item) { // argument checking delegated... add(item, true); } /** * Adds a data item to the series and sends a {@link SeriesChangeEvent} to * all registered listeners. * * @param x the x value. * @param y the y value. */ public void add(double x, double y) { add(x, y, true); } /** * Adds a data item to the series and, if requested, sends a * {@link SeriesChangeEvent} to all registered listeners. * * @param x the x value. * @param y the y value. * @param notify a flag that controls whether or not a * {@link SeriesChangeEvent} is sent to all registered * listeners. */ public void add(double x, double y, boolean notify) { add(Double.valueOf(x), Double.valueOf(y), notify); } /** * Adds a data item to the series and sends a {@link SeriesChangeEvent} to * all registered listeners. The unusual pairing of parameter types is to * make it easier to add {@code null} y-values. * * @param x the x value. * @param y the y value ({@code null} permitted). */ public void add(double x, Number y) { add(Double.valueOf(x), y); } /** * Adds a data item to the series and, if requested, sends a * {@link SeriesChangeEvent} to all registered listeners. The unusual * pairing of parameter types is to make it easier to add null y-values. * * @param x the x value. * @param y the y value ({@code null} permitted). * @param notify a flag that controls whether or not a * {@link SeriesChangeEvent} is sent to all registered * listeners. */ public void add(double x, Number y, boolean notify) { add(Double.valueOf(x), y, notify); } /** * Adds a new data item to the series (in the correct position if the * {@code autoSort} flag is set for the series) and sends a * {@link SeriesChangeEvent} to all registered listeners. *

* Throws an exception if the x-value is a duplicate AND the * allowDuplicateXValues flag is false. * * @param x the x-value ({@code null} not permitted). * @param y the y-value ({@code null} permitted). * * @throws SeriesException if the x-value is a duplicate and the * {@code allowDuplicateXValues} flag is not set for this series. */ public void add(Number x, Number y) { // argument checking delegated... add(x, y, true); } /** * Adds new data to the series and, if requested, sends a * {@link SeriesChangeEvent} to all registered listeners. *

* Throws an exception if the x-value is a duplicate AND the * allowDuplicateXValues flag is false. * * @param x the x-value ({@code null} not permitted). * @param y the y-value ({@code null} permitted). * @param notify a flag the controls whether or not a * {@link SeriesChangeEvent} is sent to all registered * listeners. */ public void add(Number x, Number y, boolean notify) { // delegate argument checking to XYDataItem... XYDataItem item = new XYDataItem(x, y); add(item, notify); } /** * Adds a data item to the series and, if requested, sends a * {@link SeriesChangeEvent} to all registered listeners. * * @param item the (x, y) item ({@code null} not permitted). * @param notify a flag that controls whether or not a * {@link SeriesChangeEvent} is sent to all registered * listeners. */ public void add(XYDataItem item, boolean notify) { Args.nullNotPermitted(item, "item"); item = (XYDataItem) item.clone(); if (this.autoSort) { int index = Collections.binarySearch(this.data, item); if (index < 0) { this.data.add(-index - 1, item); } else { if (this.allowDuplicateXValues) { // need to make sure we are adding *after* any duplicates int size = this.data.size(); while (index < size && item.compareTo( this.data.get(index)) == 0) { index++; } if (index < this.data.size()) { this.data.add(index, item); } else { this.data.add(item); } } else { throw new SeriesException("X-value already exists."); } } } else { if (!this.allowDuplicateXValues) { // can't allow duplicate values, so we need to check whether // there is an item with the given x-value already int index = indexOf(item.getX()); if (index >= 0) { throw new SeriesException("X-value already exists."); } } this.data.add(item); } updateBoundsForAddedItem(item); if (getItemCount() > this.maximumItemCount) { XYDataItem removed = (XYDataItem) this.data.remove(0); updateBoundsForRemovedItem(removed); } if (notify) { fireSeriesChanged(); } } /** * Deletes a range of items from the series and sends a * {@link SeriesChangeEvent} to all registered listeners. * * @param start the start index (zero-based). * @param end the end index (zero-based). */ public void delete(int start, int end) { this.data.subList(start, end + 1).clear(); findBoundsByIteration(); fireSeriesChanged(); } /** * Removes the item at the specified index and sends a * {@link SeriesChangeEvent} to all registered listeners. * * @param index the index. * * @return The item removed. */ public XYDataItem remove(int index) { XYDataItem removed = (XYDataItem) this.data.remove(index); updateBoundsForRemovedItem(removed); fireSeriesChanged(); return removed; } /** * Removes an item with the specified x-value and sends a * {@link SeriesChangeEvent} to all registered listeners. Note that when * a series permits multiple items with the same x-value, this method * could remove any one of the items with that x-value. * * @param x the x-value. * @return The item removed. */ public XYDataItem remove(Number x) { return remove(indexOf(x)); } /** * Removes all data items from the series and sends a * {@link SeriesChangeEvent} to all registered listeners. */ public void clear() { if (this.data.size() > 0) { this.data.clear(); this.minX = Double.NaN; this.maxX = Double.NaN; this.minY = Double.NaN; this.maxY = Double.NaN; fireSeriesChanged(); } } /** * Return the data item with the specified index. * * @param index the index. * * @return The data item with the specified index. */ public XYDataItem getDataItem(int index) { XYDataItem item = (XYDataItem) this.data.get(index); return (XYDataItem) item.clone(); } /** * Return the data item with the specified index. * * @param index the index. * * @return The data item with the specified index. */ XYDataItem getRawDataItem(int index) { return (XYDataItem) this.data.get(index); } /** * Returns the x-value at the specified index. * * @param index the index (zero-based). * * @return The x-value (never {@code null}). */ public Number getX(int index) { return getRawDataItem(index).getX(); } /** * Returns the y-value at the specified index. * * @param index the index (zero-based). * * @return The y-value (possibly {@code null}). */ public Number getY(int index) { return getRawDataItem(index).getY(); } /** * A function to find the minimum of two values, but ignoring any * Double.NaN values. * * @param a the first value. * @param b the second value. * * @return The minimum of the two values. */ private double minIgnoreNaN(double a, double b) { if (Double.isNaN(a)) { return b; } if (Double.isNaN(b)) { return a; } return Math.min(a, b); } /** * A function to find the maximum of two values, but ignoring any * Double.NaN values. * * @param a the first value. * @param b the second value. * * @return The maximum of the two values. */ private double maxIgnoreNaN(double a, double b) { if (Double.isNaN(a)) { return b; } if (Double.isNaN(b)) { return a; } return Math.max(a, b); } /** * Updates the value of an item in the series and sends a * {@link SeriesChangeEvent} to all registered listeners. * * @param index the item (zero based index). * @param y the new value ({@code null} permitted). */ public void updateByIndex(int index, Number y) { XYDataItem item = getRawDataItem(index); // figure out if we need to iterate through all the y-values boolean iterate = false; double oldY = item.getYValue(); if (!Double.isNaN(oldY)) { iterate = oldY <= this.minY || oldY >= this.maxY; } item.setY(y); if (iterate) { findBoundsByIteration(); } else if (y != null) { double yy = y.doubleValue(); this.minY = minIgnoreNaN(this.minY, yy); this.maxY = maxIgnoreNaN(this.maxY, yy); } fireSeriesChanged(); } /** * Updates an item in the series. * * @param x the x-value ({@code null} not permitted). * @param y the y-value ({@code null} permitted). * * @throws SeriesException if there is no existing item with the specified * x-value. */ public void update(Number x, Number y) { int index = indexOf(x); if (index < 0) { throw new SeriesException("No observation for x = " + x); } updateByIndex(index, y); } /** * Adds or updates an item in the series and sends a * {@link SeriesChangeEvent} to all registered listeners. * * @param x the x-value. * @param y the y-value. * * @return The item that was overwritten, if any. */ public XYDataItem addOrUpdate(double x, double y) { return addOrUpdate(Double.valueOf(x), Double.valueOf(y)); } /** * Adds or updates an item in the series and sends a * {@link SeriesChangeEvent} to all registered listeners. * * @param x the x-value ({@code null} not permitted). * @param y the y-value ({@code null} permitted). * * @return A copy of the overwritten data item, or {@code null} if no * item was overwritten. */ public XYDataItem addOrUpdate(Number x, Number y) { // defer argument checking return addOrUpdate(new XYDataItem(x, y)); } /** * Adds or updates an item in the series and sends a * {@link SeriesChangeEvent} to all registered listeners. * * @param item the data item ({@code null} not permitted). * * @return A copy of the overwritten data item, or {@code null} if no * item was overwritten. */ public XYDataItem addOrUpdate(XYDataItem item) { Args.nullNotPermitted(item, "item"); if (this.allowDuplicateXValues) { add(item); return null; } // if we get to here, we know that duplicate X values are not permitted XYDataItem overwritten = null; int index = indexOf(item.getX()); if (index >= 0) { XYDataItem existing = (XYDataItem) this.data.get(index); overwritten = (XYDataItem) existing.clone(); // figure out if we need to iterate through all the y-values boolean iterate = false; double oldY = existing.getYValue(); if (!Double.isNaN(oldY)) { iterate = oldY <= this.minY || oldY >= this.maxY; } existing.setY(item.getY()); if (iterate) { findBoundsByIteration(); } else if (item.getY() != null) { double yy = item.getY().doubleValue(); this.minY = minIgnoreNaN(this.minY, yy); this.maxY = maxIgnoreNaN(this.maxY, yy); } } else { // if the series is sorted, the negative index is a result from // Collections.binarySearch() and tells us where to insert the // new item...otherwise it will be just -1 and we should just // append the value to the list... item = (XYDataItem) item.clone(); if (this.autoSort) { this.data.add(-index - 1, item); } else { this.data.add(item); } updateBoundsForAddedItem(item); // check if this addition will exceed the maximum item count... if (getItemCount() > this.maximumItemCount) { XYDataItem removed = (XYDataItem) this.data.remove(0); updateBoundsForRemovedItem(removed); } } fireSeriesChanged(); return overwritten; } /** * Returns the index of the item with the specified x-value, or a negative * index if the series does not contain an item with that x-value. Be * aware that for an unsorted series, the index is found by iterating * through all items in the series. * * @param x the x-value ({@code null} not permitted). * * @return The index. */ public int indexOf(Number x) { if (this.autoSort) { return Collections.binarySearch(this.data, new XYDataItem(x, null)); } else { for (int i = 0; i < this.data.size(); i++) { XYDataItem item = (XYDataItem) this.data.get(i); if (item.getX().equals(x)) { return i; } } return -1; } } /** * Returns a new array containing the x and y values from this series. * * @return A new array containing the x and y values from this series. */ public double[][] toArray() { int itemCount = getItemCount(); double[][] result = new double[2][itemCount]; for (int i = 0; i < itemCount; i++) { result[0][i] = this.getX(i).doubleValue(); Number y = getY(i); if (y != null) { result[1][i] = y.doubleValue(); } else { result[1][i] = Double.NaN; } } return result; } /** * Returns a clone of the series. * * @return A clone of the series. * * @throws CloneNotSupportedException if there is a cloning problem. */ @Override public Object clone() throws CloneNotSupportedException { XYSeries clone = (XYSeries) super.clone(); clone.data = (List) ObjectUtils.deepClone(this.data); return clone; } /** * Creates a new series by copying a subset of the data in this time series. * * @param start the index of the first item to copy. * @param end the index of the last item to copy. * * @return A series containing a copy of this series from start until end. * * @throws CloneNotSupportedException if there is a cloning problem. */ public XYSeries createCopy(int start, int end) throws CloneNotSupportedException { XYSeries copy = (XYSeries) super.clone(); copy.data = new java.util.ArrayList(); if (this.data.size() > 0) { for (int index = start; index <= end; index++) { XYDataItem item = (XYDataItem) this.data.get(index); XYDataItem clone = (XYDataItem) item.clone(); try { copy.add(clone); } catch (SeriesException e) { throw new RuntimeException( "Unable to add cloned data item.", e); } } } return copy; } /** * Tests this series for equality with an arbitrary object. * * @param obj the object to test against for equality * ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYSeries)) { return false; } if (!super.equals(obj)) { return false; } XYSeries that = (XYSeries) obj; if (this.maximumItemCount != that.maximumItemCount) { return false; } if (this.autoSort != that.autoSort) { return false; } if (this.allowDuplicateXValues != that.allowDuplicateXValues) { return false; } if (!Objects.equals(this.data, that.data)) { return false; } return true; } /** * Returns a hash code. * * @return A hash code. */ @Override public int hashCode() { int result = super.hashCode(); // it is too slow to look at every data item, so let's just look at // the first, middle and last items... int count = getItemCount(); if (count > 0) { XYDataItem item = getRawDataItem(0); result = 29 * result + item.hashCode(); } if (count > 1) { XYDataItem item = getRawDataItem(count - 1); result = 29 * result + item.hashCode(); } if (count > 2) { XYDataItem item = getRawDataItem(count / 2); result = 29 * result + item.hashCode(); } result = 29 * result + this.maximumItemCount; result = 29 * result + (this.autoSort ? 1 : 0); result = 29 * result + (this.allowDuplicateXValues ? 1 : 0); return result; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/XYSeriesCollection.java000066400000000000000000000553051463604235500277300ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * XYSeriesCollection.java * ----------------------- * (C) Copyright 2001-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Aaron Metzger; * */ package org.jfree.data.xy; import org.jfree.chart.HashUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.*; import org.jfree.data.general.DatasetChangeEvent; import org.jfree.data.general.Series; import java.beans.PropertyChangeEvent; import java.beans.PropertyVetoException; import java.beans.VetoableChangeListener; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Collections; import java.util.List; import java.util.Objects; /** * Represents a collection of {@link XYSeries} objects that can be used as a * dataset. */ public class XYSeriesCollection extends AbstractIntervalXYDataset implements IntervalXYDataset, DomainInfo, RangeInfo, VetoableChangeListener, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -7590013825931496766L; /** The series that are included in the collection. */ private List data; /** The interval delegate (used to calculate the start and end x-values). */ private IntervalXYDelegate intervalDelegate; /** * Constructs an empty dataset. */ public XYSeriesCollection() { this(null); } /** * Constructs a dataset and populates it with a single series. * * @param series the series ({@code null} ignored). */ public XYSeriesCollection(XYSeries series) { this.data = new java.util.ArrayList(); this.intervalDelegate = new IntervalXYDelegate(this, false); addChangeListener(this.intervalDelegate); if (series != null) { this.data.add(series); series.addChangeListener(this); series.addVetoableChangeListener(this); } } /** * Returns the order of the domain (X) values, if this is known. * * @return The domain order. */ @Override public DomainOrder getDomainOrder() { int seriesCount = getSeriesCount(); for (int i = 0; i < seriesCount; i++) { XYSeries s = getSeries(i); if (!s.getAutoSort()) { return DomainOrder.NONE; // we can't be sure of the order } } return DomainOrder.ASCENDING; } /** * Adds a series to the collection and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param series the series ({@code null} not permitted). * * @throws IllegalArgumentException if the key for the series is null or * not unique within the dataset. */ public void addSeries(XYSeries series) { Args.nullNotPermitted(series, "series"); if (getSeriesIndex(series.getKey()) >= 0) { throw new IllegalArgumentException( "This dataset already contains a series with the key " + series.getKey()); } this.data.add(series); series.addChangeListener(this); series.addVetoableChangeListener(this); fireDatasetChanged(); } /** * Removes a series from the collection and sends a * {@link DatasetChangeEvent} to all registered listeners. * * @param series the series index (zero-based). */ public void removeSeries(int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds."); } XYSeries s = (XYSeries) this.data.get(series); if (s != null) { removeSeries(s); } } /** * Removes a series from the collection and sends a * {@link DatasetChangeEvent} to all registered listeners. * * @param series the series ({@code null} not permitted). */ public void removeSeries(XYSeries series) { Args.nullNotPermitted(series, "series"); if (this.data.contains(series)) { series.removeChangeListener(this); series.removeVetoableChangeListener(this); this.data.remove(series); fireDatasetChanged(); } } /** * Removes all the series from the collection and sends a * {@link DatasetChangeEvent} to all registered listeners. */ public void removeAllSeries() { // Unregister the collection as a change listener to each series in // the collection. for (Object item : this.data) { XYSeries series = (XYSeries) item; series.removeChangeListener(this); series.removeVetoableChangeListener(this); } // Remove all the series from the collection and notify listeners. this.data.clear(); fireDatasetChanged(); } /** * Returns the number of series in the collection. * * @return The series count. */ @Override public int getSeriesCount() { return this.data.size(); } /** * Returns a list of all the series in the collection. * * @return The list (which is unmodifiable). */ public List getSeries() { return Collections.unmodifiableList(this.data); } /** * Returns the index of the specified series, or -1 if that series is not * present in the dataset. * * @param series the series ({@code null} not permitted). * * @return The series index. */ public int indexOf(XYSeries series) { Args.nullNotPermitted(series, "series"); return this.data.indexOf(series); } /** * Returns a series from the collection. * * @param series the series index (zero-based). * * @return The series. * * @throws IllegalArgumentException if {@code series} is not in the * range {@code 0} to {@code getSeriesCount() - 1}. */ public XYSeries getSeries(int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds"); } return (XYSeries) this.data.get(series); } /** * Returns a series from the collection. * * @param key the key ({@code null} not permitted). * * @return The series with the specified key. * * @throws UnknownKeyException if {@code key} is not found in the * collection. */ public XYSeries getSeries(Comparable key) { Args.nullNotPermitted(key, "key"); for (Object item : this.data) { XYSeries series = (XYSeries) item; if (key.equals(series.getKey())) { return series; } } throw new UnknownKeyException("Key not found: " + key); } /** * Returns the key for a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * * @return The key for a series. * * @throws IllegalArgumentException if {@code series} is not in the * specified range. */ @Override public Comparable getSeriesKey(int series) { // defer argument checking return getSeries(series).getKey(); } /** * Returns the index of the series with the specified key, or -1 if no * series has that key. * * @param key the key ({@code null} not permitted). * * @return The index. */ public int getSeriesIndex(Comparable key) { Args.nullNotPermitted(key, "key"); int seriesCount = getSeriesCount(); for (int i = 0; i < seriesCount; i++) { XYSeries series = (XYSeries) this.data.get(i); if (key.equals(series.getKey())) { return i; } } return -1; } /** * Returns the number of items in the specified series. * * @param series the series (zero-based index). * * @return The item count. * * @throws IllegalArgumentException if {@code series} is not in the * range {@code 0} to {@code getSeriesCount() - 1}. */ @Override public int getItemCount(int series) { // defer argument checking return getSeries(series).getItemCount(); } /** * Returns the x-value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The value. */ @Override public Number getX(int series, int item) { XYSeries s = (XYSeries) this.data.get(series); return s.getX(item); } /** * Returns the starting X value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The starting X value. */ @Override public Number getStartX(int series, int item) { return this.intervalDelegate.getStartX(series, item); } /** * Returns the ending X value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The ending X value. */ @Override public Number getEndX(int series, int item) { return this.intervalDelegate.getEndX(series, item); } /** * Returns the y-value for the specified series and item. * * @param series the series (zero-based index). * @param index the index of the item of interest (zero-based). * * @return The value (possibly {@code null}). */ @Override public Number getY(int series, int index) { XYSeries s = (XYSeries) this.data.get(series); return s.getY(index); } /** * Returns the starting Y value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The starting Y value. */ @Override public Number getStartY(int series, int item) { return getY(series, item); } /** * Returns the ending Y value for the specified series and item. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The ending Y value. */ @Override public Number getEndY(int series, int item) { return getY(series, item); } /** * Tests this collection for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof XYSeriesCollection)) { return false; } XYSeriesCollection that = (XYSeriesCollection) obj; if (!this.intervalDelegate.equals(that.intervalDelegate)) { return false; } return Objects.equals(this.data, that.data); } /** * Returns a clone of this instance. * * @return A clone. * * @throws CloneNotSupportedException if there is a problem. */ @Override public Object clone() throws CloneNotSupportedException { XYSeriesCollection clone = (XYSeriesCollection) super.clone(); clone.data = (List) ObjectUtils.deepClone(this.data); clone.intervalDelegate = (IntervalXYDelegate) this.intervalDelegate.clone(); return clone; } /** * Returns a hash code. * * @return A hash code. */ @Override public int hashCode() { int hash = 5; hash = HashUtils.hashCode(hash, this.intervalDelegate); hash = HashUtils.hashCode(hash, this.data); return hash; } /** * Returns the minimum x-value in the dataset. * * @param includeInterval a flag that determines whether the * x-interval is taken into account. * * @return The minimum value. */ @Override public double getDomainLowerBound(boolean includeInterval) { if (includeInterval) { return this.intervalDelegate.getDomainLowerBound(includeInterval); } double result = Double.NaN; int seriesCount = getSeriesCount(); for (int s = 0; s < seriesCount; s++) { XYSeries series = getSeries(s); double lowX = series.getMinX(); if (Double.isNaN(result)) { result = lowX; } else { if (!Double.isNaN(lowX)) { result = Math.min(result, lowX); } } } return result; } /** * Returns the maximum x-value in the dataset. * * @param includeInterval a flag that determines whether the * x-interval is taken into account. * * @return The maximum value. */ @Override public double getDomainUpperBound(boolean includeInterval) { if (includeInterval) { return this.intervalDelegate.getDomainUpperBound(includeInterval); } else { double result = Double.NaN; int seriesCount = getSeriesCount(); for (int s = 0; s < seriesCount; s++) { XYSeries series = getSeries(s); double hiX = series.getMaxX(); if (Double.isNaN(result)) { result = hiX; } else { if (!Double.isNaN(hiX)) { result = Math.max(result, hiX); } } } return result; } } /** * Returns the range of the values in this dataset's domain. * * @param includeInterval a flag that determines whether the * x-interval is taken into account. * * @return The range (or {@code null} if the dataset contains no * values). */ @Override public Range getDomainBounds(boolean includeInterval) { if (includeInterval) { return this.intervalDelegate.getDomainBounds(includeInterval); } else { double lower = Double.POSITIVE_INFINITY; double upper = Double.NEGATIVE_INFINITY; int seriesCount = getSeriesCount(); for (int s = 0; s < seriesCount; s++) { XYSeries series = getSeries(s); double minX = series.getMinX(); if (!Double.isNaN(minX)) { lower = Math.min(lower, minX); } double maxX = series.getMaxX(); if (!Double.isNaN(maxX)) { upper = Math.max(upper, maxX); } } if (lower > upper) { return null; } else { return new Range(lower, upper); } } } /** * Returns the interval width. This is used to calculate the start and end * x-values, if/when the dataset is used as an {@link IntervalXYDataset}. * * @return The interval width. */ public double getIntervalWidth() { return this.intervalDelegate.getIntervalWidth(); } /** * Sets the interval width and sends a {@link DatasetChangeEvent} to all * registered listeners. * * @param width the width (negative values not permitted). */ public void setIntervalWidth(double width) { if (width < 0.0) { throw new IllegalArgumentException("Negative 'width' argument."); } this.intervalDelegate.setFixedIntervalWidth(width); fireDatasetChanged(); } /** * Returns the interval position factor. * * @return The interval position factor. */ public double getIntervalPositionFactor() { return this.intervalDelegate.getIntervalPositionFactor(); } /** * Sets the interval position factor. This controls where the x-value is in * relation to the interval surrounding the x-value (0.0 means the x-value * will be positioned at the start, 0.5 in the middle, and 1.0 at the end). * * @param factor the factor. */ public void setIntervalPositionFactor(double factor) { this.intervalDelegate.setIntervalPositionFactor(factor); fireDatasetChanged(); } /** * Returns whether the interval width is automatically calculated or not. * * @return Whether the width is automatically calculated or not. */ public boolean isAutoWidth() { return this.intervalDelegate.isAutoWidth(); } /** * Sets the flag that indicates whether the interval width is automatically * calculated or not. * * @param b a boolean. */ public void setAutoWidth(boolean b) { this.intervalDelegate.setAutoWidth(b); fireDatasetChanged(); } /** * Returns the range of the values in this dataset's range. * * @param includeInterval ignored. * * @return The range (or {@code null} if the dataset contains no * values). */ @Override public Range getRangeBounds(boolean includeInterval) { double lower = Double.POSITIVE_INFINITY; double upper = Double.NEGATIVE_INFINITY; int seriesCount = getSeriesCount(); for (int s = 0; s < seriesCount; s++) { XYSeries series = getSeries(s); double minY = series.getMinY(); if (!Double.isNaN(minY)) { lower = Math.min(lower, minY); } double maxY = series.getMaxY(); if (!Double.isNaN(maxY)) { upper = Math.max(upper, maxY); } } if (lower > upper) { return null; } else { return new Range(lower, upper); } } /** * Returns the minimum y-value in the dataset. * * @param includeInterval a flag that determines whether the * y-interval is taken into account. * * @return The minimum value. */ @Override public double getRangeLowerBound(boolean includeInterval) { double result = Double.NaN; int seriesCount = getSeriesCount(); for (int s = 0; s < seriesCount; s++) { XYSeries series = getSeries(s); double lowY = series.getMinY(); if (Double.isNaN(result)) { result = lowY; } else { if (!Double.isNaN(lowY)) { result = Math.min(result, lowY); } } } return result; } /** * Returns the maximum y-value in the dataset. * * @param includeInterval a flag that determines whether the * y-interval is taken into account. * * @return The maximum value. */ @Override public double getRangeUpperBound(boolean includeInterval) { double result = Double.NaN; int seriesCount = getSeriesCount(); for (int s = 0; s < seriesCount; s++) { XYSeries series = getSeries(s); double hiY = series.getMaxY(); if (Double.isNaN(result)) { result = hiY; } else { if (!Double.isNaN(hiY)) { result = Math.max(result, hiY); } } } return result; } /** * Receives notification that the key for one of the series in the * collection has changed, and vetos it if the key is already present in * the collection. * * @param e the event. * * @throws PropertyVetoException if the series name is already present in * the collection. */ @Override public void vetoableChange(PropertyChangeEvent e) throws PropertyVetoException { // if it is not the series name, then we have no interest if (!"Key".equals(e.getPropertyName())) { return; } // to be defensive, let's check that the source series does in fact // belong to this collection Series s = (Series) e.getSource(); if (getSeriesIndex(s.getKey()) == -1) { throw new IllegalStateException("Receiving events from a series " + "that does not belong to this collection."); } // check if the new series name already exists for another series Comparable key = (Comparable) e.getNewValue(); if (getSeriesIndex(key) >= 0) { throw new PropertyVetoException("Duplicate key2", e); } } /** * Provides serialization support. * * @param stream the output stream. * * @throws IOException if there is an I/O error. */ private void writeObject(ObjectOutputStream stream) throws IOException { stream.defaultWriteObject(); } /** * Provides serialization support. * * @param stream the input stream. * * @throws IOException if there is an I/O error. * @throws ClassNotFoundException if there is a classpath problem. */ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException { stream.defaultReadObject(); for (Object item : this.data) { XYSeries series = (XYSeries) item; series.addChangeListener(this); } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/XYZDataset.java000066400000000000000000000042731463604235500261770ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * XYZDataset.java * --------------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; /** * The interface through which JFreeChart obtains data in the form of (x, y, z) * items - used for XY and XYZ plots. */ public interface XYZDataset extends XYDataset { /** * Returns the z-value for the specified series and item. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The z-value (possibly {@code null}). */ Number getZ(int series, int item); /** * Returns the z-value (as a double primitive) for an item within a series. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The z-value. */ double getZValue(int series, int item); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/XisSymbolic.java000066400000000000000000000045421463604235500264430ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * XisSymbolic.java * ---------------- * (C) Copyright 2006-present, by Anthony Boulestreau and Contributors. * * Original Author: Anthony Boulestreau; * Contributor(s): David Gilbert; * */ package org.jfree.data.xy; /** * Represent a data set where X is a symbolic values. Each symbolic value is * linked with an Integer. */ public interface XisSymbolic { /** * Returns the list of symbolic values. * * @return An array of symbolic values. */ String[] getXSymbolicValues(); /** * Returns the symbolic value of the data set specified by * {@code series} and {@code item} parameters. * * @param series value of the serie. * @param item value of the item. * * @return The symbolic value. */ String getXSymbolicValue(int series, int item); /** * Returns the symbolic value linked with the specified {@code Integer}. * * @param val value of the integer linked with the symbolic value. * * @return The symbolic value. */ String getXSymbolicValue(Integer val); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/YInterval.java000066400000000000000000000065641463604235500261210ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * YInterval.java * -------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import java.io.Serializable; /** * A y-interval. This class is used internally by the * {@link YIntervalDataItem} class. */ public class YInterval implements Serializable { /** The y-value. */ private double y; /** The lower bound of the y-interval. */ private double yLow; /** The upper bound of the y-interval. */ private double yHigh; /** * Creates a new instance of {@code YInterval}. * * @param y the y-value. * @param yLow the lower bound of the y-interval. * @param yHigh the upper bound of the y-interval. */ public YInterval(double y, double yLow, double yHigh) { this.y = y; this.yLow = yLow; this.yHigh = yHigh; } /** * Returns the y-value. * * @return The y-value. */ public double getY() { return this.y; } /** * Returns the lower bound of the y-interval. * * @return The lower bound of the y-interval. */ public double getYLow() { return this.yLow; } /** * Returns the upper bound of the y-interval. * * @return The upper bound of the y-interval. */ public double getYHigh() { return this.yHigh; } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof YInterval)) { return false; } YInterval that = (YInterval) obj; if (this.y != that.y) { return false; } if (this.yLow != that.yLow) { return false; } if (this.yHigh != that.yHigh) { return false; } return true; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/YIntervalDataItem.java000066400000000000000000000063701463604235500275250ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * YIntervalDataItem.java * ---------------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.data.ComparableObjectItem; /** * An item representing data in the form (x, y, y-low, y-high). */ public class YIntervalDataItem extends ComparableObjectItem { /** * Creates a new instance of {@code YIntervalDataItem}. * * @param x the x-value. * @param y the y-value. * @param yLow the lower bound of the y-interval. * @param yHigh the upper bound of the y-interval. */ public YIntervalDataItem(double x, double y, double yLow, double yHigh) { super(x, new YInterval(y, yLow, yHigh)); } /** * Returns the x-value. * * @return The x-value (never {@code null}). */ public Double getX() { return (Double) getComparable(); } /** * Returns the y-value. * * @return The y-value. */ public double getYValue() { YInterval interval = (YInterval) getObject(); if (interval != null) { return interval.getY(); } else { return Double.NaN; } } /** * Returns the lower bound of the y-interval. * * @return The lower bound of the y-interval. */ public double getYLowValue() { YInterval interval = (YInterval) getObject(); if (interval != null) { return interval.getYLow(); } else { return Double.NaN; } } /** * Returns the upper bound of the y-interval. * * @return The upper bound of the y-interval. */ public double getYHighValue() { YInterval interval = (YInterval) getObject(); if (interval != null) { return interval.getYHigh(); } else { return Double.NaN; } } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/YIntervalSeries.java000066400000000000000000000124041463604235500272620ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * YIntervalSeries.java * -------------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.data.ComparableObjectItem; import org.jfree.data.ComparableObjectSeries; import org.jfree.data.general.SeriesChangeEvent; /** * A list of (x, y, y-low, y-high) data items. * * @see YIntervalSeriesCollection */ public class YIntervalSeries extends ComparableObjectSeries { /** * Creates a new empty series. By default, items added to the series will * be sorted into ascending order by x-value, and duplicate x-values will * be allowed (these defaults can be modified with another constructor. * * @param key the series key ({@code null} not permitted). */ public YIntervalSeries(Comparable key) { this(key, true, true); } /** * Constructs a new xy-series that contains no data. You can specify * whether or not duplicate x-values are allowed for the series. * * @param key the series key ({@code null} not permitted). * @param autoSort a flag that controls whether or not the items in the * series are sorted. * @param allowDuplicateXValues a flag that controls whether duplicate * x-values are allowed. */ public YIntervalSeries(Comparable key, boolean autoSort, boolean allowDuplicateXValues) { super(key, autoSort, allowDuplicateXValues); } /** * Adds a data item to the series and sends a {@link SeriesChangeEvent} to * all registered listeners. * * @param x the x-value. * @param y the y-value. * @param yLow the lower bound of the y-interval. * @param yHigh the upper bound of the y-interval. */ public void add(double x, double y, double yLow, double yHigh) { add(new YIntervalDataItem(x, y, yLow, yHigh), true); } /** * Adds a data item to the series and, if requested, sends a * {@link SeriesChangeEvent} to all registered listeners. * * @param item the data item ({@code null} not permitted). * @param notify notify listeners? */ public void add(YIntervalDataItem item, boolean notify) { super.add(item, notify); } /** * Returns the x-value for the specified item. * * @param index the item index. * * @return The x-value (never {@code null}). */ public Number getX(int index) { YIntervalDataItem item = (YIntervalDataItem) getDataItem(index); return item.getX(); } /** * Returns the y-value for the specified item. * * @param index the item index. * * @return The y-value. */ public double getYValue(int index) { YIntervalDataItem item = (YIntervalDataItem) getDataItem(index); return item.getYValue(); } /** * Returns the lower bound of the Y-interval for the specified item in the * series. * * @param index the item index. * * @return The lower bound of the Y-interval. */ public double getYLowValue(int index) { YIntervalDataItem item = (YIntervalDataItem) getDataItem(index); return item.getYLowValue(); } /** * Returns the upper bound of the y-interval for the specified item in the * series. * * @param index the item index. * * @return The upper bound of the y-interval. */ public double getYHighValue(int index) { YIntervalDataItem item = (YIntervalDataItem) getDataItem(index); return item.getYHighValue(); } /** * Returns the data item at the specified index. * * @param index the item index. * * @return The data item. */ @Override public ComparableObjectItem getDataItem(int index) { return super.getDataItem(index); } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/YIntervalSeriesCollection.java000066400000000000000000000246321463604235500313040ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------ * YIntervalSeriesCollection.java * ------------------------------ * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import java.io.Serializable; import java.util.List; import java.util.Objects; import org.jfree.chart.util.ObjectUtils; import org.jfree.chart.util.Args; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.general.DatasetChangeEvent; /** * A collection of {@link YIntervalSeries} objects. * * @see YIntervalSeries */ public class YIntervalSeriesCollection extends AbstractIntervalXYDataset implements IntervalXYDataset, PublicCloneable, Serializable { /** Storage for the data series. */ private List data; /** * Creates a new instance of {@code YIntervalSeriesCollection}. */ public YIntervalSeriesCollection() { this.data = new java.util.ArrayList(); } /** * Adds a series to the collection and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param series the series ({@code null} not permitted). */ public void addSeries(YIntervalSeries series) { Args.nullNotPermitted(series, "series"); this.data.add(series); series.addChangeListener(this); fireDatasetChanged(); } /** * Returns the number of series in the collection. * * @return The series count. */ @Override public int getSeriesCount() { return this.data.size(); } /** * Returns a series from the collection. * * @param series the series index (zero-based). * * @return The series. * * @throws IllegalArgumentException if {@code series} is not in the * range {@code 0} to {@code getSeriesCount() - 1}. */ public YIntervalSeries getSeries(int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds"); } return (YIntervalSeries) this.data.get(series); } /** * Returns the key for a series. * * @param series the series index (in the range {@code 0} to * {@code getSeriesCount() - 1}). * * @return The key for a series. * * @throws IllegalArgumentException if {@code series} is not in the * specified range. */ @Override public Comparable getSeriesKey(int series) { // defer argument checking return getSeries(series).getKey(); } /** * Returns the number of items in the specified series. * * @param series the series (zero-based index). * * @return The item count. * * @throws IllegalArgumentException if {@code series} is not in the * range {@code 0} to {@code getSeriesCount() - 1}. */ @Override public int getItemCount(int series) { // defer argument checking return getSeries(series).getItemCount(); } /** * Returns the x-value for an item within a series. * * @param series the series index. * @param item the item index. * * @return The x-value. */ @Override public Number getX(int series, int item) { YIntervalSeries s = (YIntervalSeries) this.data.get(series); return s.getX(item); } /** * Returns the y-value (as a double primitive) for an item within a * series. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public double getYValue(int series, int item) { YIntervalSeries s = (YIntervalSeries) this.data.get(series); return s.getYValue(item); } /** * Returns the start y-value (as a double primitive) for an item within a * series. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The value. */ @Override public double getStartYValue(int series, int item) { YIntervalSeries s = (YIntervalSeries) this.data.get(series); return s.getYLowValue(item); } /** * Returns the end y-value (as a double primitive) for an item within a * series. * * @param series the series (zero-based index). * @param item the item (zero-based index). * * @return The value. */ @Override public double getEndYValue(int series, int item) { YIntervalSeries s = (YIntervalSeries) this.data.get(series); return s.getYHighValue(item); } /** * Returns the y-value for an item within a series. * * @param series the series index. * @param item the item index. * * @return The y-value. */ @Override public Number getY(int series, int item) { YIntervalSeries s = (YIntervalSeries) this.data.get(series); return s.getYValue(item); } /** * Returns the start x-value for an item within a series. This method * maps directly to {@link #getX(int, int)}. * * @param series the series index. * @param item the item index. * * @return The x-value. */ @Override public Number getStartX(int series, int item) { return getX(series, item); } /** * Returns the end x-value for an item within a series. This method * maps directly to {@link #getX(int, int)}. * * @param series the series index. * @param item the item index. * * @return The x-value. */ @Override public Number getEndX(int series, int item) { return getX(series, item); } /** * Returns the start y-value for an item within a series. * * @param series the series index. * @param item the item index. * * @return The start y-value. */ @Override public Number getStartY(int series, int item) { YIntervalSeries s = (YIntervalSeries) this.data.get(series); return s.getYLowValue(item); } /** * Returns the end y-value for an item within a series. * * @param series the series index. * @param item the item index. * * @return The end y-value. */ @Override public Number getEndY(int series, int item) { YIntervalSeries s = (YIntervalSeries) this.data.get(series); return s.getYHighValue(item); } /** * Removes a series from the collection and sends a * {@link DatasetChangeEvent} to all registered listeners. * * @param series the series index (zero-based). */ public void removeSeries(int series) { if ((series < 0) || (series >= getSeriesCount())) { throw new IllegalArgumentException("Series index out of bounds."); } YIntervalSeries ts = (YIntervalSeries) this.data.get(series); ts.removeChangeListener(this); this.data.remove(series); fireDatasetChanged(); } /** * Removes a series from the collection and sends a * {@link DatasetChangeEvent} to all registered listeners. * * @param series the series ({@code null} not permitted). */ public void removeSeries(YIntervalSeries series) { Args.nullNotPermitted(series, "series"); if (this.data.contains(series)) { series.removeChangeListener(this); this.data.remove(series); fireDatasetChanged(); } } /** * Removes all the series from the collection and sends a * {@link DatasetChangeEvent} to all registered listeners. */ public void removeAllSeries() { // Unregister the collection as a change listener to each series in // the collection. for (int i = 0; i < this.data.size(); i++) { YIntervalSeries series = (YIntervalSeries) this.data.get(i); series.removeChangeListener(this); } this.data.clear(); fireDatasetChanged(); } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof YIntervalSeriesCollection)) { return false; } YIntervalSeriesCollection that = (YIntervalSeriesCollection) obj; return Objects.equals(this.data, that.data); } /** * Returns a clone of this instance. * * @return A clone. * * @throws CloneNotSupportedException if there is a problem. */ @Override public Object clone() throws CloneNotSupportedException { YIntervalSeriesCollection clone = (YIntervalSeriesCollection) super.clone(); clone.data = (List) ObjectUtils.deepClone(this.data); return clone; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/YWithXInterval.java000066400000000000000000000073701463604235500271010ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * YWithXInterval.java * ------------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import java.io.Serializable; /** * A y-value plus the bounds for the related x-interval. This curious * combination exists as an implementation detail, to fit into the structure * of the ComparableObjectSeries class. It would have been possible to * simply reuse the {@link YInterval} class by assuming that the y-interval * in fact represents the x-interval, however I decided it was better to * duplicate some code in order to document the real intent. */ public class YWithXInterval implements Serializable { /** The y-value. */ private double y; /** The lower bound of the x-interval. */ private double xLow; /** The upper bound of the x-interval. */ private double xHigh; /** * Creates a new instance of {@code YWithXInterval}. * * @param y the y-value. * @param xLow the lower bound of the x-interval. * @param xHigh the upper bound of the x-interval. */ public YWithXInterval(double y, double xLow, double xHigh) { this.y = y; this.xLow = xLow; this.xHigh = xHigh; } /** * Returns the y-value. * * @return The y-value. */ public double getY() { return this.y; } /** * Returns the lower bound of the x-interval. * * @return The lower bound of the x-interval. */ public double getXLow() { return this.xLow; } /** * Returns the upper bound of the x-interval. * * @return The upper bound of the x-interval. */ public double getXHigh() { return this.xHigh; } /** * Tests this instance for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof YWithXInterval)) { return false; } YWithXInterval that = (YWithXInterval) obj; if (this.y != that.y) { return false; } if (this.xLow != that.xLow) { return false; } if (this.xHigh != that.xHigh) { return false; } return true; } } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/YisSymbolic.java000066400000000000000000000045571463604235500264520ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * YisSymbolic.java * ---------------- * (C) Copyright 2006-present, by Anthony Boulestreau and Contributors. * * Original Author: Anthony Boulestreau; * Contributor(s): David Gilbert; * */ package org.jfree.data.xy; /** * Represent a data set where Y is a symbolic values. Each symbolic value is * linked with an Integer. */ public interface YisSymbolic { /** * Returns the list of symbolic values. * * @return The symbolic values. */ String[] getYSymbolicValues(); /** * Returns the symbolic value of the data set specified by * {@code series} and {@code item} parameters. * * @param series the series index (zero-based). * @param item the item index (zero-based). * * @return The symbolic value. */ String getYSymbolicValue(int series, int item); /** * Returns the symbolic value linked with the specified {@code Integer}. * * @param val value of the integer linked with the symbolic value. * * @return The symbolic value. */ String getYSymbolicValue(Integer val); } jfree-jfreechart-cb8ff67/src/main/java/org/jfree/data/xy/package.html000066400000000000000000000003101463604235500256010ustar00rootroot00000000000000 A package containing the {@link org.jfree.data.xy.XYDataset} interface and related classes. jfree-jfreechart-cb8ff67/src/main/javadoc/000077500000000000000000000000001463604235500205615ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/doc-files/000077500000000000000000000000001463604235500224265ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/doc-files/BarChartDemo1.svg000066400000000000000000000453361463604235500255360ustar00rootroot00000000000000 Antidepressant Medication UsagePercentage of adults aged 18 and over who used antidepressant medication over past 30 days, by age and sex: United States, 2015-2018Source: https://www.cdc.gov/nchs/products/databriefs/db377.htmMalesFemales18 to 3940 - 5960 and overAge Category024681012141618202224 jfree-jfreechart-cb8ff67/src/main/javadoc/doc-files/FlowPlotDemo2.svg000066400000000000000000000345651463604235500256210ustar00rootroot00000000000000 Selected NZ Exports Sept 2020Source: https://statisticsnz.shinyapps.io/trade_dashboard/BeveragesUSADairyTravelMeatServicesOther GoodsUnited KingdomWoodChinaAustraliaFruit & NutsGoods jfree-jfreechart-cb8ff67/src/main/javadoc/doc-files/NormalDistributionDemo2.svg000066400000000000000000000675221463604235500277020ustar00rootroot00000000000000 Normal Distribution Demo 2N1N2N3N4-5-4-3-2-1012345X0.00.10.20.30.40.50.60.70.80.9μ = -2.0, σ² = 0.5μ = 0.0, σ² = 0.2μ = 0.0, σ² = 1.0μ = 0.0, σ² = 5.0 jfree-jfreechart-cb8ff67/src/main/javadoc/doc-files/PieChartDemo1.svg000066400000000000000000000301531463604235500255360ustar00rootroot00000000000000 Cognac Exports 2019NAFTAEuropeFar EastOtherMillions of Bottleshttps://www.cognac.fr/en/press/cognac-shipments-and-harvest-2019/Other = 13.6 (6.3%)Far East = 59.5 (27.5%)Europe = 37.8 (17.5%)NAFTA = 105.6 (48.8%) jfree-jfreechart-cb8ff67/src/main/javadoc/org/000077500000000000000000000000001463604235500213505ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/000077500000000000000000000000001463604235500224435ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/000077500000000000000000000000001463604235500235445ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/plot/000077500000000000000000000000001463604235500245225ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/plot/dial/000077500000000000000000000000001463604235500254335ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/plot/dial/doc-files/000077500000000000000000000000001463604235500273005ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/plot/dial/doc-files/DialPlotSample.png000066400000000000000000001002321463604235500326560ustar00rootroot00000000000000‰PNG  IHDRq„ÀØ€IDATxÚì]xW׿ÿ F©;m–ÚÇW/(\‹¶Xq ¥8ÁÝ,h‘4hCp×àžIwh9ÿ¼w¹Ó™Í&k³š“çyŸìÌÞ¹;såsÏ=’-áÔb0 †o"7ƒÁ`0‰3 ƒIœÁ`0Lâ ƒÁ$Î`0 &q†ï ŠlÙÒáÑG¥gžy†Ê—«@‘‹£2½Æ™ß3ú¾| þXDE¾ýŽüqñ|_|Q¦†Oç±É`g8Nâæèѽ§W¸µûò5,ZIÙ³g·ølLä &q†a$¬Š^cøïyÓ}y?þXT}ޱcÇÑœÙóÔãÿýïCŸ &q†c¤wšæÍýƒrç~]ý®QÃÆV‰8tÔú裄j GŽôÒK/‰ë:ê‰Ûz_ÀžÝ{©NºôüsÏ«÷P¯^}ŠÝ³?Ãúñ"(V¬¸¸ïœ9sRƒú éØÑ“BRþꫯÅy¨qš6  “'Oéê9xàˆ8ÿÚ«¯‰ßÃÿ–-[éž9#”,YJÜêdziï ªŸ &q†S’1–ô–$CK×@’ÌHZ®R¹ª!$ní¾@àZ‚×â7Þ }{Z¬ÿÉ's¥+_¸ð·‚”Íσ°e ê÷Þ{Ïâï}òɧâE`O_ìÚ«^Ÿ'OŸ &q†s$~âx¼ú¤ÅÌ®‘d6iR¸8Þ°~³Ž$$ñŒî +Ï7l!ε j­žkÓ¦­Åú«UýYHØS¦LÕ¯T©²ø-í ê©§žRë@}òüÏ?ÿ"ê@]òÜo­ÛØÕÚûïÝ»/O“8ÃyµüR©-×@mÑ­kaeaïµÎÞ×ÛoçWÏ9|\œ±Ês¸'Kõoݲ]U×hϯ[·1Ýyíï}ðþêùµkÖ‹s¸FžƒjÅÖ~о@ÞÊ÷M“8ý’8°|ÙÊ ÕîÄ-©?´€¾ÛZýöœ×þž¥²°<±¥°jѪt¦O›Éc“Á$ÎpžÄ3²–°tMHÈH±‡s%Š—¤%QË]FâÝWFæzö¬ì9/Ÿ×Y‡¸¿˜M2˜Ä^Bâý£Û´“:f[ êsÕ„Q$žÙ}½óÎ;º{p¤~{Î[S§Ø²99|XˆZV4<&Lâ ÃíÄ!ÁnÞ´5SBƒZCž[¹"š d‰Ûz_ØH”ßwD®µdÉH'î(‰k7M¥ŽvcS»‘j ;¶ï¦‹(ûý÷?ðxd0‰3\CâíÛu´Jh°­6¿N°:f­á$n~_™™üA‘$“ElBZú=¼0¬™ž=³çãñÉ`g8DâÒIÒ%œklQE€@afµ 6éà„£U-à;gIÜÚ}Ib…©TÐIc…Pºt¡†1Z"¯Y@ ØÐÅý½òÊ+BB·ÅF\û’cg0‰3 “8ƒÁ`0˜Ä ƒÁ$Î`0 &qƒÁ`g0 “8ƒÁ`0˜Ä ƒIœÁ`0Lâ ƒÁ`g0ÌÜÙ‘®líÚ " ò]FÌœ­bì˜09bT¦ CÓ§ÏT¯‰ŒŒuHú:ÆíÍ`g0ì„:5|… A={ô¢-ZRÍš5©dÉ’ôã?º+V¤õP³fÔ¥KW‘M¾¼K›Ÿ“Á`gø5Ž=!$h©}ú¨eüê-Z$êÆïà÷¢¢¢(""Bý!C†ˆEëÖ­©zõêT¦L™LŸ©lÙrÔŠFŒE³gÏ¥M›¶R\\“8û)ô÷6¿ i9#Rûù矅ô "3fŒ b'Hÿ'Nœ¨%¤d”•r‰%ܪJ„ uJ½zõÄË÷Lƒ ¢°°0ñrÙãÞñσ^BuêÔ¡råÊe@ìe©eË êÝ»PË@ÄÄÎ`g¸usñ?R§N’«”!ù€AƨArË—/ÿA€  öíÛ ²ËŒô-¡J•*T£F jÑ¢ýö[âÀƒÂÂÆ)’óÔ 1aÂDµ,СCGQ$f¨iPwñâÅm¾5VÎAâ s¹z€T)ÄûEý–^Få‰÷€—!6Vyœ1˜Ä†*HŒ]»v£jÕª¥# è5j$T ¬•+W 5Ôª3#/s)ª—áÃC”k§+u,¡õë7ÐîÝ{(>>nݺCÿýÀmÀï>}†öîݧ´ÁfZ¶l9Íœ!îÄgÊŒìñ2¹ r‡Ú¤rGÛ`5 ß’ÄŽvîØ±Mœ0‰6¬ßÄãÁ$ΰXÚ/‰Z¦HË©~ýú-6@̦gÏž-T óçϧaÆ iªÌ‰º­B„ÃiñâHÚºu›B”‰ aÞVˆóŸDJÊ9ñ¢Áó@ÇÞ¹sçL í¤¶ßš5ktí‡Uókj×þU¬6`Ryâxƒ¹Íz``s¡r“Ï&q†nN.[¶B¸‰Ã|MKÜêÆ/jèb¡>Ñw™2e…z"ii—˜\=¬tV¬øK¨·ÌuêØ(Fÿ¡¡v ^ÌZB/]º4uëÖ]Œ¶Gggx9àê PÕ«×ÐMv8¬` .Íÿ ohË€ p~õ구"ñbÕ TXXÁ^ÞœÐÑè_˜1bc¦Ú—3^Ö€Iœáe@0&H[Ú J˜ªAz“‘˜ÔæªÁ°aÃiË–mbÏDé[€z+4t´î…ŒþE?Ë—6Ô.P¿hM?1N0^0nxþ0‰3<¨2®Ö%æ:S¨K°¼†ÉÜŵ›eøÜ«WoÚ¼yݾ}îÝû‡áعs°í‡*L»a ›t¨Y >ø0—Î1~0ŽXÕÂ$Îp£ÊáZ±4ÖZ0@‡-¥nX™À[P~ͱ:(ßÅÐ¥KW³ ±eË–MEVyæ«Woкu(8¸«"q—Ò©R°q éããE«;Ç÷W¬jag¸0ÐÔСÃu•X"ËeóܹsÅÄÔJÝXfϘ1“’“S” þw–ƒžÄ³ÞóŸ;w^óD<­tޏ5ˆÎˆq'.­ª‘‡ÆvçLâ #ÉúKí*Ä)‘&fظÔJÝÂ6lØD·oßõyö÷‡—À¶m;ĸК,bÜ`üHÕ›VÕ‚ÿwLæLâ '¢"^†–¼a7 ‰““O+A!&Çĉ“éÔ©Ót÷îßÇÅ‹Wœ®äéìõž¼joè—Äij4yr¸.%Æ6¾e8iɼG^´gÏ>ž—Lâ [É™o`ß«õ¦”Á¦ ïaÿ©6ýñǺuë®W„$¬_|‘RSӘľО}öYC^lFãeáÂÅ‘K›9 zsì«@XÀ¦¨TÏÁ¢¥oßþLæLâŒÌ6,‘ÈW’7$ X–ÀîºK8uh7¢Z·þM!õ5^EÞZ|ûí·´xñŸLâ à¬óÅ_xe?P½ýþ{[3 ¦^ª_Æ¡”ÌAæ§¼Ê$ÎиÅ#™6F &´4d¤%oHG;wîöZB@báßoçÑ{0ŠÄý¡-lÆ„­ÕÈ\f^Ò’96ØAæ—IÑoÀúõ›Ä&¨Ö¢¥cÇŽ"ø–Ô™ÿ»S‰Æ›À1Z˜Ä³V®ˆ¦_­£³€u$sµ íØ±Ûí9-íŠÓ×Cìl=¾NâW®Ü ^x‘ÎKsºwßûîÝ{3”Ì‘VRò»Zµj‰qÍó›Iܯ¤¸HA¦u°@ˆQK–¼×¬YïÉ[ORÒ9Ÿ#o”Ä}‘áàÉ"Rôi%s¹ +)„,Ææºü¹B9¥“¸ßKMí¦¥Ü8yÃéB; š6 ðˆäm¹Lö€?cêÔéʘ)íñûÀ¸„ÊOk…ñ‹qŒñ,ÕЛóæ'“¸ßù*µªlLblj´ö¸ð®\¹2šnܸC·oß÷8Âç âð†{Éêøé§JÊŠm‚×ÜϪU«u^ 0ÅÆ'tæØüÔªX"#£˜˜Ä}×dPëi Õ ¼,±Eª3)µÀƒ.$d„°©ö&â8wî=ó̳táÂe&RâòåëBµuölŠWÝ×¥K×hôè±jÀ-l~J öw´±{08(“¸OQá¤Õ‰Vu×zYÂô,.î´W©çáÍ/Ò„„³ I÷PÝù1¾eÔD­Š¥R¥J"Óó“¸×{[Bê$XȺ§ d\‘R9–¢X’fÕ‰Ïð¿òš5ët*¨ a* 5‹VÞ©Sgöúd÷^é»öÒ Ö&XZ"Ê ´:´]3–¢Yq ÎðoÕØÕ«7)"b¶*•c>hçthÃHåÜIÜk,O ]˜oò`óR+ÀdðÈ‘tëÖ}·@n†¹ë÷Þ‹)S¦QÉ’¥Ýö{ÇŽÅSÇŽu+RÌ ¹™/Ï7nÜ„víŒea÷|V¦Æ‚î‘à u F³tØn|ñâ(º~ýŽ_O\†÷ÂS/ô¥KW¨{C˜Æ £uëÖ ß­TÎ,Lâ‰wÒ¯oÇ%¤ @ëÅÖ³goá¨á‰‰›’bZBŸ?™‰, ãÒ%“jíÌ™ü>T8}úôÓ…–€®æˆÐÕ¹Ò£Û•3‰»/Iì_¥YÌe|o)]@:_±"Ú+&0Ã^ä«W¯SW­Ð•ÃR R9tæ˜GrÞDG¯fžaw°#½.a÷ Ë8‚UI˓޽û)Rpݼyßilݺ‹S ©‹ÁpÉÉhíÚM†Ô•–vúõ JßH'SDÌ#鵌y„ÀpÌ7Lâ†GÔn^"¢›Ì‚"°#¿hQ”a'0°=þøãT§N=&†Ç€qˆØ3 42L X¾A5V¥o¨%1¯Æ¯Î+„«ÀücbwØ5‡§™”d¢†¶mÛªê“+ËÄ«ÊÀ¼g(š4 P'âQ_½zËðß`02CÑ¢ÅÕ1hZ[ÿµk·iäÈPƒã` W¸P_²õ “¸C>B%jx[ʼƒrs1#"#—Ò÷\‚Ó§“…u‰œDÓ¦E¸ì·|ЯæÏÿNºógϦR¹råé‰'ž 'Ÿ|Ré¯ê””a=ö–Ï*˜3g:öž|2;vÊe¿õJùò&G¹Ê•+‹XåØk‚°$ Ù¯Ø9ˆIÜæÀU-Z´Lg}5Š\æaxôhœË'RÿþƒÔ‰”7o^A\Là÷èÊ•[Ê ©ŠhóïºuëIÁÁÝ)ïŽÀ¤IS©S§à ë²·|Viß÷Þ{_{جwõoÆÅ PÔP¶#G õ <=å¼C˜f¤Å$nÕ|°Aƒ†ªD€XÉHˆÎ&¥rì®C}⯓ÉðÝw?(/Ñx‹$À®]ûtmXªT™ ë²·|V€§„‡K—nÐ!ÃtÖ+ °ñ)DZžœI èµ®u*À™7o!]»v×p”)SŽFç’º³*@âæçŽ?M?üð#=öØc¥K—*±Œ®³¥<Ã>ÀÖ+œK—n^wTÔr•Èa„`Nä0AÌjéß² ‰#ƒˆô¶”Aw`…Ò¤IU­²zõ— ì'LËË3gRy¢3ü))…oÑ#ñ.û XŠÉ}+Äï—¹<¥«>æyVÊ”-«¸´‡Ý©  —ß ¶¹|€33² ‘»ú7¶mÛ­š ZšÓY‰È³eE‡t¨^½>|’'ƒácÀæ)æ¯\]c^gE"Ï–Õ\ëÄS­ÚÏÂjäêÕ» ß$|9¤SôîÌJDž-«xÆÅà‰À`ø6ΉùœU‰Ü/I‘Îä2+#?sæ;VŒœ_IIiªDsbóðÒÈÞÞ`÷Èh„ZPíÀáZíÍüøc1Ú¶-V=NK»!â²Užá;xöÙçÙjû¹/Ž{æbÛRç©S)ª’»€´DÞ­[w&qoÀØ1aªÛ<Ç#˜• ;ðÇãéòå;†bñâåôò˯ˆå§Ñu[¤^¼x+Ý9£Ê3|©©×襗^ò¹±‚y„ù·VöçŸkˆØ7ˆ´hKÝPI;rkD.£Ž1ŠIÜ“˜7÷:˜8q¢.!²ñìÛwÔ¥Ï l9çhy†ïꎻúäX±e>U­Z]øÏ?×´«îƒOˆÐ2ú!x"<<\¿1s6“¸'°$j™HÑ„N@€xt̰aá—,YJ9Þê—™hì™hö–gø& :¨X± ]¸pÃ/ÇJñâ¥W¨PÉ¡ë7nÜ!x|ž|!“M€O˜ÄÝœ•^†ž”â‘ØA&tX¶l•ßNVKË[kKd{Ê3|PT¯^‹ŽKô»±rîÜúþû¢‚À‘¬Ã™ºV­Z¯æ ‚À2gçÖ-Û™ÄÝe‰R«V-ÑðíÛ·œ˜%J”çæÏôë [¼xIJN¾¤äEŠ|oXy†o!..™5  ;öùÝX9vì4(ð!ýßÿýßäÍÎ×¹páUú–9;¡b1åÓ­å“+>EâÚ´j°ÿ„í'6)*V¬(Î;‘.]ºã×èÒ¥uïÞ‡ÒÒn „†Ž§NºVžá;Xµj#.\„ŽIð»±rð`åË÷– ðwß}Ο¿aXÝ&„«‰a‡›àizèk6ä>Eâ!ÃGˆ†iƒ¼aû)¿[·^táÂM¿Ÿ¸˜°ßÿo&šR¥ÊR||Š® –žö”gø&rç~]ÍL¯…¯•èè ôôÓO ýÛo秤¤K†ÿFÏž}Ô˜*R”¦‡ƒ awþøc¡º ‚úË ˜ áâ%$'_æ‰Í`ø8`¾‹ÍT8Ì%?ã’ßÀ×¼yK5;¤q¤k”ûj³gÏe7›6mU-QäÎr·nÝÔ¬a~îÜu¯¸¿E‹–©*\¨T´¾(ØŸc·ˆ(†ƒ=ôàxÊä]»ö¤ÔÔ< Dxøl•À¡>ÉŸ??}ñÅ—””tÙ«îûmÚd soŽxèU$¾kg,•.]Fg>`À5;ÏéÓi< D—.=éÕW_£ ~¢—_~™¾úê+áNæÌE¯»WŠ2½[Ÿ>}ÒÙ#“x l.*00P˜ùDDD=xñâÅiçÎCtáÂmƒáCHI¹NMš4!ªT©"$ðï¿ÿ^9ÎMGŽ$zí}ïÞ}D쿤‡x‹-òSs&ñÌÌ +T¨ &6EÂcœ›6mOÃÇpöìe*UªœP™üòKuzþùç…$ûÒK/û„P6kÖ5í£ä$éáéMf‡^AâXžHsBxcjÃC¶lÙZlz\¸p‹Á`øNžL¡‚¿1Àa €ML8éÁ|Æ>ó­[›¢¦ÂJEï-î=ñdzy“¹1µ …åÌáçxR0>„-[béÍ7ó*RkeEÿ…^|ñEAäO>ù$M:Û§žåĉ$úé§Š:·|„ÿÀqÓ¦"𒌨 ' ¥K—z…GH9ì‹g¼Õúõë'Ž¡KsD’]>1¼˜ÀÂýçùè‚•¾µ•ÄñBxðÌ36½ÿâŒÒ?ÿ\‹>úèEo'qPïèÓO?¥¢EKBžÎ¹Qu—4h¤s2dˆ8†å§rsz„ćþ0aMáÔ#ÝZáÔ³iÓJM½ÅÈÂ8Þj™{_¦+ 3-£%ñÌÊ]þc)Ýÿôsë÷•x‰ûç!ŽO¡/¾øŠ¾ýö;êÑ£‡JàÀáÌó©Òž‰~Ø^Û·< Ò¹ÈexlðZ– q¤Z“yì¤ÉN£F¦·Û AÃy‚duO4IÙç’ȬÜß;ÓÍm !q[ê‹Òº-/Glì BI@àˆ» ‡‰0È-þw(ÞÛiÈ‘jnNí^Îy"¥›ÛI›xX8ó @ä¦\™åéäÉsLd~Nж”³EʶEz¶•Äú½¬ ±GEÅBèºAà0µ“8lÁ‘\åСÓ~=Žãã/¨¶ã°RѦtkÕªµ“ø¢…‘j˜GlfÂÞ²ZÕjâÜ‚K•ºÉðSœO¼Hÿ(“?mÛ~«eoüÞI‘Œ˼¾øÔ‡’qj†eô$žÉ} É?Ùé{ð|¢>¥^ëÃ9s"EÂV­ÚP¯^½c:pDüƒM8Üé³ÂxþóÏ¿t¶ãpË—™€Às~Iâðl’‰ $Þ^x“›Ê´¤¤¤ktîÜM†ãV:ÖÊ]š¿”î}ò¹Õr©§/fú½–Ä3­'.ÕêoAZ¿_CûöÁ®k×î‚À1g¥$þÌ3ÏÒüùK²ÜxoÉMNð™v“sܸ þEâx+I“B¹™‰€ãNº1Áùl%\[%UkR¶;IÜÖç³G ·öBðœVú B…*T¤ÈÔ³g/AàØÀ“@ ϲã>8¸§à±ßZÿ¦Æ}r·É¡[H|ì˜0ÝnîܹsÕ8áð¢bô}¿ÿAº{Ü&i\HµNJÙî&q[¥u[¤ðóûãéï7óº¥ Eœroðr,[¶õîÝ'ÓK/½D}ú ÎÒcüsð›49Ï™¼ñGrQ,F>dó‡Q N))7mÂ1ErYºt­ÍåîÅ•1SèNÑ’”ræªÕ²ç.úeØú\wJ–¥+ÃÆzísìÛOŸ|ò9ýüs AÞ’À‘‘0¼1›7ÿÍ/û1&f+:”hsùaÃBŸ5 h&ø <‡öï¹#¹²ËI|ð !ºø(ˆ;`ŠZXžŽ9ksC5j¨TÙè×_ˆAÆÄé^\ž:—R­´»·““¯¼ìÎL¥K3xŒÀ^{íujܸ™Jà˜¯¯¾úª0 †1Â{ï½G5kÖñ»¾q׫ׄ{ìqªQÃöçƒãøÌR\•ù6‰Ë(…x+-X°@—ôxÒ¤™v ¬9rž|2—²œëC ~*Ñy•t© ô¼JÐí2?eZ$/Ôʪ‰ÛÎr[Þç=:¿çx¦ånU«)T@øïζ\¸p¥ pDÕJà0DtQþP¡oéŒ +._ž¥GôôÓϨسòŸ%x­~½úªºLáê(‡.%ñ~}ûë’=Èð¿üRƒNJ³K•‚7¤¶·š4)‚ Â…€D¨Õ-_7-s"g·Jä™®x”ñ¬m遲G¹å¾†)+¨çžû×(\¸°jŽc¸Óþù~%rÄ(‹Þ™ˆ³—æñ†…>‰SÍÏ>|VYꔣÇ‚ræ|’*Uú™öïOȰ{Ë» ÿ<ýŒ*Áݬ^Çi"!‹eú‘¤LË%O5‘½/† hOk/D´;œ…Îí:fWÝ;v§{Ë–m)W®\b>!à–v.ùêÜLH¸”΋‰3p žôJG¤B#eüøñ:×Ó‘#=¿,;­Nùò•Ɔùw:t£ví‚…‰0jÔ$úM‘r3ªËÞò®$p­jÃ"·Bà ÏDnOù¦ÊàÒ©S—t*”ì%rx¼ Wüºuu™ÍÀ›F$pšÄ§†ËÀVµu7óšøø‹tæÌu»pèÐYAà%òAƒ=zÎî:2HÜüÜ?” ãÇSÕã“'/ˆÐŒê°·¼»q­™™D®¼Ï*÷(¿ORÚØ[îÕQÜ+?¥F®Ÿ“w£{ï¼/>+¢˜®T&æ×ÚRÆ—€ù‚0²A̓,ªPÌ­P`êŸ>}ÇïýÅ_¢½{O©Çà $^ö–¹‰ë¥`9dÈÚ·/Áî:ðLÒÜP º2¯øÓã$Ð4@ÜLHHˆ¸¹¶mÛŠãáÃÇ8ÝÁFxF$ŽÍ”NzÑéÓW†«mâ,¡óÁ¤”e9HZU£<œì™•ñ`Iÿ¥òÌß|SD!çÞ68¼1óåËGµ O÷ó½í*5_ƒÜà4×Z8›‡Ó)¯U«¶.z£FÄñ¬Y‘”˜xáf$í>I·Š—:ð¤íGÕóWÚ§'ò³Ïr›ù ¶l9¤ñÛbÏH…P⫯¾JGàÒ³Dî×麲 ÑŽ †û0¾iƒúp¹(3ÅÅ%¸ŸÄ¥m8Ò6ÉTDØÐÄáøñ ÜiDÚÈItÿ„+6‹DÆ 4µHà  ?ü0Ãóã§Ÿ¦äþ#(1þ2·¥‡÷ï')ƒÈááŽã•+¢ÝOâ2ñnfРAâ¸{÷¾Üa^K’ö•νuaOo)ª#zGq¥CQ>_˜¢w¤Á±yy[Êx}û8C·J•ªxažŸåÒçŸ7oyºLôZU T(üq:r‡öÍ7óÐŽµ±}áŸܩW:"?£H–v×Ó¾;]T–èøŒzð†§O]1•Qþÿód®t×ÙRÆÛñ÷ /Ñ•VMÎF §,Ýà’ç‡ÉÜ믿I]º¤'pÒ·%ÇÜ|öÙç(&fy/B|üeååj ‚ÞÔî%.Zé>ïÙÃL¾_¿~ºr†pG¹Ž®9.õ¦'òϾ¤3±§"p-‰Ã‘FGX9Iw­-e¼x†‹G‰ÏÉ QÞýß'†>\Ü%ªY³¾pT ¶,¿÷Þ{‚ÄÍ ! ‘82rµÛÇÃ:† £ËA,ƒbwéê>¯T©’øQ,p5jÔÇ\Ãäz‡Ûüùðù†ùÝÛLäæÁ§$ ™Z•Dm(ãíÏ`ˆxþJ¦¼TË–ýIŒ§#pl\‚À?ýôÓt:pÄ/B¶ú fÙõ<©³þ㊉ÜõX·nÏÃÄñ¿£™ƒqÆQ©ØMâØEÅ Ô¬YS8tâÂL¦F-±TàNr=0áÈÊ"O6Îa"7'uü¿ýCq:?y®øŒûƒþ×¼¬-e¼7ËWúl!‰¯Ü"V2F<¬Òöü™päéÝ»E‡ ¡%‡Ý1\ÄM‹žO ÛP«–)çüùóÖ©c:^µÌõ$ÃtüvUñã2O߀!”pá&œ‹0M¼Ô)ó®ëˆIéˆøàªZµ&b"w=¦($ú’B¦}û²Hà™©P€/¾øB!€Òv¸+ÆOVÅe%‹”=DwYuü‘*éøco²ˆlŽd³—Kéàƒä¦ö>øÎ'¨xñ2"täÖ­Gx0x  Zyðä“Länôׯ¾š›:wîj‘À¡& aFþõ×_Ó‡~b·$ÈpÎrXp°Þʼ*Ë+<ë2—±R¦M›&~´Aƒ¦¤Ÿ‹z‹½ñFÚc§6«HÙ)‹W»ýwχ͠=ÎDîbüúkcz÷Ý÷2ž#µ$@WÎäå›ìÞÉ €6m?hÐh‡V@&ÚbÁŸØÔŸ‚WMŽ@µ]Câûö?½MLLŒòÆ!íž=ñtêÔ5‡±oß§®÷W\­ HôZú”¸ã„[;uÂlzý_ÕÊýW_§³ëör¿@Ù²•E¢^½úd(g¦BJó•W^ +îÄ}MyÉàe~ýç_¹ß2èGg®o‚?Á£ZNµ7}ú:$ÃEÞ˜ ®2üþR'̪3ó1I¼ŠdÊ8nÕªµñ$þÓÀW2GœôÒŒŒŒáÎp!’þÚJ·¾ý1Ýĺ]°ÖÝEäé%òÜ?v *j°@iß>Ø¢̼“™oL´š¼I“&âGÆ7-4hÂáF`RA/žN_9¥[~Ÿ‰Ü~tTúëõ×óP§NÁ8T,¯¾úšòy¨¡÷vvé&Ý8R÷]X0s+š6m!øt̘1‚_À̽¸M$ŽèZZW{µ°o᭚ $oHà  ¯tço§L]¨×‘Cµ²6–ûÅê)Rî»ï~¡Š–À‘}ÇÃÌðÝwߣ:u\#0I (¡÷Þr„ûÍ2d´N/.]ð±i‰KW{¹ƒúkí_z­àNð I¹C•b‰ÈÍ%r&rýù§Ÿª)’uA Ãc.eT  þAÔéŠ{…J%ia ÷›±xqŒê‚~ˆˆÇU«T5†Äa¯ˆ ¥-ãÊ•+…>ç`.w•‘aI"O\›åÛ%66Q„‘-Sæ'§ ¼`Á‚ôÁÿ£’yÌù1пØ_ÄXX¾|¹ÝöâVI|ÞÜ?tY|&Ož,Žýµ>w€ 8Õgîõì‚D]ý™%›²lßEGï17n‘¡ о}{«ŽIýöÛïЮ]ñ^õŒ§ø…â`ã!,,Lï ?s¶ó$.CÏBŽÊ¡·3 ¾AÜø. ð¿Ÿ{n~_‚Î £SŠTçí÷œ<ãOÝF+¢â\—åú.RyfäÁ j—)CGÒ†ÌÞ˜¹s¿A›7ñâVÆaêð‰t£dÑ×LäÆcÐ }\*ɳ¶„¦µJâ 4Ô½!Zµjõ0^ÊŸÜø.š0 ð›ÅÊøÌ„ô—–ÈϬؚeúl†ò"ƒ a``P†Ä `ãÊšŽd+Ï=÷<­ñÕ”N°J ^¶2ðÌ›·BŒ‹–-[ê4u~­ã‰=zB§«AåUªTÇ;wÆÑÉ“W.Düþ$‡¯M\º™î|ø©)iÁ»PÒìeâ|Ê„9ôÏSO‹ó÷Þ~W›_kKs$ÍZš^µ²x­ß÷Qûö=évíÚ;S‡£ÎóÏ?Ÿ)Ce oÌYJ[¦K}—=»Kû/3Ä:ÇóÑÅØ½û”àÚ*Uª žÕî=8pØq—ñÃa‚ŠaŒnÚE­Å ïåq§Œ)>Ÿ³BdgÇç[…¾§”q³ÄçsX"+¿ùµ¶”±„³ Vé$r'ò&MZÑ+¯¼F:uÍ”À¡B£Nf@?þ8M™òGºßI îO©ÃÆ»¼ÿž…LÙ&î FŽ‘Óa‡R•éœ|‚‚ÚÚusÙ” mî47áø%Jž8WHd8þ牜âœüIzͯ±¥LF8³d£ßù!E2­T©}üñçÔ³§u ÜšeÐjèÐqé~+yÚ"ºV­¶]}ãLÿ1‡³<^Õª®1.p<}úLÇI¼g^êàÓ¡‡„ŒçNóÄKdšÖ¡—8'—åXr›_gK™¬Jäû÷'Ñ÷ß— ¯¾*L}ûösZ…b25|ºwh±ÿîæŸN튷«oœí?†g^Õ&‰€s¥i³³»ã$Ø\çä#ÝAAÝwýøƒG3MheÉnMJ³¥ŒÝDžóI±"ðåvÜ´é0ý÷¿Qõêu¶.?ûì³™8,¾òæÍG 6·ø{çަ+õšÙÝ7FôÃýˆŒ\#ÆK³€fºpß7qœÄeäÂèèhQiù‡9á6nٞ˔gA ”:u[•Àm1#þûßT©Ru:z4ÍâoÞø±4%M´»oŒê?†{“Rh| §q\ºL¦ 3$ñ={ö©!Q!ˆÇ*T¤cÇ.r£„“Ϲ¤Þ3 bÄæ&–Ò°RI\¸FœÇf§T±üó̳b’ËkpÎZ{‘³G'‘ÂåÌœ•>ÕGS¦,6à¿ÿÞ)SR–Ñ­©P€/¿ü’¾û®̤ÿ…DmFðîî?kˆÛ—ÄóØ@€_ÅfæÊ•‚w‘‚Ç۷ﲟė-[¡óÔ”þüuë6äÆ6ÀApw>úŒ.7i%&[ÜîÓ~÷œ‰Q…í¸JäÙs¸”XŒDÿþ¡ôÒK/S»v­8<1­©P€¢E‹Ñ矕){+@ÚX\lÑnüFìw0‘é¹9cÆ Á»Hb²X‰²ŸÄeøY LT6räHqܹs/nlžöKäëöÓ½×óè$ò$EÂõæ{îÚu ½õֻʘïn“þꫯZ%pÄÄÈ“'íöÁ—uV:<‰îÝû›2Þ+|«:Æ~‡»'.Fº To35…Ž¿ÂpNîMòÛgKX¹#=‘O˜ë•÷Z«V#úßÿ>¡¾}ÚDà¶Hàð·€]ùªU{|¶O8ÇóÔ…3Ʋ𜙅J†$Þ²E¸ ;¦8ž5k‰òc— ‡¿án³óÁc=$r︿ I•(Q^˜Z³@ѳÂÞQfåPå…^¢?ÿÜÀ】!æÌYf²Pif²P ÇMì'ñÕM94,X *«U«–8Þ°á 76Ãp"O:Áã÷µyó1úðÃÏ”q^’zôèi³^±bÅLËÁ4÷©§žV¢ÜÿŒL~Ï"‡1xÞ›Öb‹gË(fJñ‡1SS•I?þC‡Îsc3œÆÉ] ér†&‡NóØý,]º…ò*÷“Y&zs3BX¡X#pý‹/¾$¼1¹ßÖ~ÏZâ^ð²Í$¾uËvq(*AbäÍ /3† ~;tD‰ÜÝ÷1oÞ*…h_V$æ6VÉ[ëJoÀ¡jeK×®ƒ¸¿6CšÊ*U«VÇàe›I\šÊÄÈÒsæ…ÜÈ #qbgÝþð3ùá¹~£ÜöûcÇFðºÚDඪP:þw©víÆÜÏ »Ð¨Q€à[èÃÁ¿ކã%QËl'ñÉ“Ãu;¤!!!â¸S§žÜÈáøþsÜ"×Z­î òàà”;÷›Ô½{/›%p[T(ÀçŸAåËW£Ã‡/d>Œ=ËcÙ tëÖߢeà¸ql'ñÁƒ†è²ÛË@,C‡ŽåF6ˆÀao{ã»â”2p¬ ±¬Þ&q›é$rAä½Bì®›“S§.Vˆ4„‚‚:©À16ׯ?$ÊկߜÞyç}êÒ¥›Ín‹|óM!úúë"~Oà·P]/Q^ø70‘ƒ‰‚o±šÿ" Ž‘eÍf‡M¢)Zaˆ¨KHOœ8—޽Ì0Çw$¿þci1Žï9Ëm²#!‘§ôeõº3–(KÎ6T ÀG”#G‹!Aµxî¹çèµ×^§† ÙDà­[·¦\¹rÙDàØ„Ê—/?íPžÅŸûêØ> ¢ôщ­'y^„É“ç ¾…à`‹­¸EoÕªµ¸ñÃµÑ §O´ëfÌ'wPäÅ®#ò[ŸÔyj—ËŽAŸ~ú¥nŒaÃñ›o¾iί°gÏžØß)R¤ˆðªÔ–ýõ×Eð}H=™IàˆaaÀK—.­ÔŸ›V¯Þ—%ú DÎcÖxî›9s‰àÛÀÀ@Á¿'š$sð²Í$n‚Vf^^¼xwÃ#ùù¶=ÕïA’… ÿ¨N,Lþ6nÜH÷ïß'k ¾}û —uäÏŸ_¶£8’?ÿü ½›ûᢢ6 ¾…pþE•Ì~²eæè³hÑ"QÉ/¿ü"Žá.ÌÌp‘ßüªˆŽÈÓ‚:)ä;Jä¡”R÷èÑ£éöíÛäÈÒ¤I*™#=̹ìÕשSG¹§\B­Ã}ÇpkÖ˜"ÈV©\E—3#‡‹$.}Ï•”*UJïáe?ÃÍËu­je ‚Ù³ ÂE’ .—/_D,¥r„‰µU‡ª™î'MZÀ}Æ0àYð-‚¥áô#€l"ñÇãEalР@VpäÈ%í8¾ã” ò¾ýâСCmR›Øû7nÜ8uc´@6ÅM eÀ€1ÜW CQÌÌc„Žcð³Ußµ3Vç­)EùŠ+sã2<‚Q!STŸ9s&¹òoêÔ©*‘W¯^=S·û7ßÌK-[vä>bŽ*Uªé¼6±ç‚cð³Í$Ž‹´XªU«ÎkŽîMáv°ýµKH» Õ#F;þ gÇï=õÔSBÚ¶DàÈY­Z:xð<÷“ÇvŸáv0à[ðîüùóu®÷–2ü¤#ñèèÕ¢0t„ÚŒ> pã@à°­…åÅÅFAtfÜ¡.à¶ÉlYYFÕ»óB ~÷ã?NçNÿöÛù©L™JLàI;1|1¥5oO·¾ø†þ~ê&r¾ÕZ"ÚHÛ¶"Ú:‰£ ìP7% ˆ¾ÄpGw¡ÓSÓ…ÀötósÓ Ç9n›ô=z¦jBhÔ&¦=›Ò¦H’xÁ‚_ ÛôØØ”,ß?GbÿJÒQbØ:¶ý]¾µdæm‰ËàWLâî#unË(X° QØt{â¨øý·ß~ûajµ’ôæ›ùhÆ£Ü?"çv0­uA°d®MKA°Ò‘øìÙsEa8:àâÐÐPqܱcOn\†Û0gN´j ©Ø°?—Òx¡BßRÞ¼ùiÕª½Ü? —£K—>º\›ð[ÀqÄÌÙÖI…,ùí£Rn\†»Ð¼y{Až‡žüƒë¾I¥ó­]{û†á»$Þ¹s:tèƒá|üñç‚<áJïÉ¿ØØØ‡*•w¹_nøÖa;&L0@÷ë7Âí²{wÕ¬Ùˆžx"'=úècT¾üÏ´uk¼ú=>ÿøcá.2åÊUU&ýñ ë³·<Ã3X¿þ¨°ÕƆ¦+œzìý“nùkÖäþñ„„LýåÌÜ÷4W€oM¼ÛOð0V„8?[%ñ‘#F‰ÂØÈÁÅøã!C”Ê/ºuë6Sn~8pA qãߨtéJê÷­Zu¡–-;©ß8ŽÛeXŸ½å½§"–Óí÷?¤9¡»yߦӓˆó‰cfÑ?O=m:ÿÖ;âØüZ[ÊxÓ¦E‰Iˆh„Þð‡è‡¸Ÿ æ{×8PÚéÎ;ˆ¾ÄûÓ8p«Wï§/¿üö!‰_ôY®ßZâað³O‘øÓO?+Pïߟ*$ry\¨ÐµE÷ýwߕȰ>{Ë{+@ܧf,ŸAà÷_|Y|¾ñõw”:S|>;x<]S$ ókm)ãiôè1LLB„“õ†?Xjá~p_ÞÔN÷Ÿ‘ޝÚ+>ãÿ½WrûÕ8pß~[Œ–-Û‘ŽÄ}+\Fâ^´ Ö‚òKØZŸlsŸW®°„ô†?Ä#Çýྼ©î¾‘Ž­9(>ã?Ž]=\1†BëÖ])8x°ÊAÎÌ}{ËÍy~KâXÒ4oÞA=Ρ,ÍËX:çhy_ ñKU~¥„ñóÅçÿÉ®ûî…g³¥Œ·8È“I(¨ ýö[wÚ·ï‚@ïÞ£”%o§ ë³·<Ã3ذá¸WÚ‰ÇÄàþñ ¿|™+ú÷µh':ÆqÍNz»½#^y%·Å·«üäþµ² „Ù!ðý÷¥hóæ¸ ;Ó–ò ïÀG}æU›ï½W€ûŇIÜ׸|kÉcsúô™Î’xƒá4kÖNLª   ¯ˆÒ AKî†Û‰ÛävŸQÃzpã2ܘŒA¥·goˆbˆûá~aø‰Ëxâˆ_«'Þ´iKÚ¿?Áp¤û´§ã‰òIAî†[Ѭ™>žx“&Ml'žQf&qc`ɦ–ÛÅ2¦LùÓ+2ûLœ¸ûƒÇ³[¾u8³OF964àÆe¸eÊTDŠœ—žÈ±YªTEî†Û¾u8ÇfFÙî«V­ÎËp;6l8F/¾ø²G²Ý?÷Ü ÂJûán€oÎv/I¼bÅŠâ⨨¨‡Ç•iß¾4Ãí1beÏžCë¤I“\JàS§Nªø­aùýAåÊÕïBˆC¨Æ1øÙ*‰Ÿ8/ +VL\ ààÆex p¼~”]ñ7nÜ8•À[¶ìÂíÎðŠ+.8wÍš5‚ƒK”(!Ž=aÄâ+ˆ‰‰”*UJoÛvš˜áQ"—ùÏ?ÿl˜é!êÁ|I03< ð,øÄ þ‘KAÚ_[$ñÕkˆ -Z$*Á„Áñò廾±­[ùÀpŒ£œ9Ÿd wxHϰçvä.ýPϼñÆ¢>Ô‹ú¹ÎbÇŽ$‡¯]¹rn_Rª´«V©j;‰4 °hÞ2þjÚ»÷‚ÍØ¸ñ$õïFE‹–£§žz†¶lI°ëzÖ/ßC_ýƒ*9ñsçδsçN›ÈûÈ‘#4pà@Õ„(Xð[Z²d·/Ãilß~V“Ï‹ Z}úŒyr¸¸¨}ûö¢Hä¦ãÜȼe0X%èBtéÒWðí°aÃÿvìØQ7Áv—A°tE?å×_PlìƒÁ`¸uë6Ö¿ È0øU†$¾uËv×æÒ¥KÅqٲ幑 Ã…(_þ'·¦t¹/ÛLâð *næ1N“ÃÏnhƒÁpÀ¯Ò±Çœ{-ykfHâZ‡Ÿ ˆŠjÕª%Žÿúk/76ƒÁ`¸àWðl5ô3pôÉ”Äa“h² Ÿ¬ÓËL™I{ö\`0 †Á¿‚gÁ·à]èÅÅqÓûI¼woË;¤ŽåÆv!önJàv`xïøÜv–ÛÁ…:t¢Î2PÚˆÃwÇnŸ>ÍbeíÚõàÆ6˜´OŒ[@)MÚÒõO¿ö¶Lä o%pø7Ü,ð)¥ÖkIq£fѾõ'¹m DçÎ}u¹5ƒƒƒÅqhèûI\š"£„6ÃÌ ÷ì9Ï0{·ÑLŠʤˆP&Å n†÷ŽÙM§,§¸m ‚4/D¼ð.r›,U¢ì'ñ={ö‰‹Ë•+'*‹ŽŽVÍ wîL¡Ý»Ï3 @ìÖ3.­ÿ`ÔNº÷ÂËêñÉôw®§éAŽGèvÞwıù5¶”a¸‡þØH7ßÿˆ<ú(]-T”öÿuÀ+û/vã)î/~iØV®¼[¡B… 3úX%qà§ &{E8*¡ã8*j7¸àl«ît㟋äµòܵ/¿£¸áÓÅçSýÂèÊ÷¥Ó]gK†kqó¿ŸÐ±ð¥âóáùèrÑrÜ~Ž+LYÕÊ”)#øùÄqé2—à‰6×…¤•Ñ §O_æÔÍnä··[Ð}íÞyNGâÿ<‘SœÇÊÿ¿sæJw-e®¤h]Ÿ<ö8÷Ÿ—c«“«ê™3Wè,S¤§|F!hm"ñž=z‰Jzõê%*0`ÀÃãáv“vXØjÒ¤-}úé×"4#¹û %ñÿÉž)YØZ†áZÜzç¿txî:Ó˸÷hµ¸ÿ¼—Àz¡‘ëÖmA#GF(œy®:ú÷ý0Ð`?]àÁÌ,S¬’xÄÌÙ¢’   Q)â‹ã¸iÓV´k×y›°e‹þáFŒˆ µkOØ|=Ãy€ÄågHt»vœ3ï0Iiæåm)Ãp-Ï\%ôÙЉ'7m/6À¹ÿ¼6˜„ÕÆÿVqÎÖëÛ~ |‹P´&ÍÇLÇI|uÌZQ Ò³éÒUýÅ®‡‘s'ÛXƒ^vZÇ&6ºðù„òB…þÔ¼¼-e®Å8_½‘ÇúoDÄГº=åÁ«Ú˜)Èì#69WD;NâÚ*Ë—/W©Råáfç!î("¾OÝyó-Cˆ\Kâ'‡L:SœûûégÅ$7/—Y†{p\‘èî¾ú†Ä/–ªB±ëã<Ò ðÛùÞ¥S=C¹_\ˆÕ«<Ì«YEð,¬SdÌ”;Nâ@à u">T+8ž0a!7¾ °ï¯ƒtå»Rtµp1Ú¿|/· ƒÇd@x¸IËÑ*¨•àY„;Áq_ë5޶JâÈ&!R‹Ê±É‰ã.]úsã H=¾º…ÐîíÉÜ& ¯[ÞÍý&í9Êía0zôúp³›àYdõÁ1Ÿ8Mâóæþ¡óÜ”YjÖ¬K;w¦2 ÆîõñÜ ¯Åž5ǹ\€:uLžšHŠ žmР8†q‰Ó$¾k§É½D‰ÂøŽ?RW³nÝIîƒÁp6Ä+œúïÞ#x|‹cð¯Ó$À:Eëô#wM§Lù“;ÁƒØ»òøc·Ãaì_´Ç‡1}ºÉɧzõêº8U™Å·›Äƒ»t•Âø\Y«k×AÜ À±±ЕïJÒ?>FW¿úžÛ„á0ÒÊþ,¬X®*FÇC"¸M<€>}Lbî[ëTÙ¡}GãH\†¥•‰“¥Óô8;v¤2Ü€]ëâ)¡óaî…I§ÅÁˆÕÜF »»â=ÈžC7–î¼ö&%¶î)Æ·‘{аa àÓ1cÆè›Lœ0É8ß´i«˜yß8zqèqÖ¯çÎv%öý¹‹’ë·!?ÍÉ@8ÐÃá˹­vcÿüMB·4®þÉù$¥Vo,Êp[¹7ž¦%J> ,¥ 4¸víãH\ÑpöìÙ⇠¿1¹„®àÎpy/ÜF*ÔL')©¬j}ž` Ã…”:-ĸ²Dè—~(Kæ¬ç¶rfÍŠyè_Uð*rËà™E.tˆÄ¡ŸAåƒ ?Ö¹sgqܳçpî ‘¸¥¥î™–]i÷êãÜF רìÚ°¨²;<1ŠÛÈèÛ7T§2dˆÉé§Uk²•›m&q©¯S§Ž.L"ôâÛ·§2\€‹Å~›—džGÐŽ-IÜ. ·àèÈ9t¹HI!H\ûäkn¡Aƒ@‹á¾mÕ‡ÛEâûö•C;F­-ãÊ•zuŠÀšûg¯·ÓcðÀÌÕܰqc¢S×GGûŠÒG«·Å>ÜnjÕª­{kH¯¢±cç)7uÎ.ÄÄ£^½ÆÐ?”!×­‹³»ƒÁð°!‰Û… £îÝG >³·ð&ø< >E^Mƒgíáe»H|äˆQ:ÿþaƉãß~ v¸‚ƒ‡Ó_æÁ`0| nx‘"%DÁ›àOèÁÁ§àU‘é^áY—‘¸Œ/^¹reñ£‹-zè’_’6m:CÛ¶³6œ¶«<Ã2v®:ÆíÀp|ü¬ãv0kílGð¥4-œ?¾àSéžu‰k]ðeàòš5k>4=\Ãéfœì5†n¿ñ9Ãa¿•÷ŠS¤In÷|)]íá{"Ç~c¥J•È^N¶›ÄehZé‚/CÓvîÜŸ;ÇMؽl¿°¸üM1Úëñû96t:ýëi‘ËñvÞüâØ‘2¾‚Ø…ÛéÞ /[}6_hWŒ¥ðð‹”;÷ß4aÂ%ž¯|©UMKW{[BÏ:MâHd E[S§R©T©*mÙ’Ääé Òw|ðpÚ¾ù¬WÜÓÕ‚EèØà©¦ÕAï±t©H)‡Êø[v£ë>¦ŸÖžÍ—Ú«:ø!ìZyØéºžþa^þê«óœµð$øR«Jé6Ž—D-s=‰ùY¥ÂzL ¤Û¶%Ùt¬üGb^GÊøâ‚CÄýkI<£góµv1B-t]õ‚DÎóÕº*îöP¥Øã¥é4‰÷ìaR¡ôë×O§RéÔ©?mÝzŽ‘Åðà?ÙõÇ9q¨Œ/,eíÙ²Z»,[vžr»'ü‘GЪUçynXø< þDœðg£FÄñ¢…‘î#qˆü2 \ëø3~ü"î,/–Æîú­Ë_¥£C¦‹ÏG‡Íz^GÊø*‰gôlÞÔ.®£G_RÕ(-[^ç9˜Ààɺuê Þ„JZÀ¥Ë8¤Jq˜Äì¢âfÐ7¢:þpgy‡Cç ë wù±S„n3øþSÏ22'»ÌÊø:‰gôlÞÒ.®ló'Ÿ| vL˜¸‰Ö­[ëÒ¶Õ¨Q—6m:ËçFìˆ6-™™ÀÖˆããÅÙºÖ­K¡G5©Q>üð·¯ÔªU_—†­mÛ¶â”Îð°S$¾gÏ>J±T©R´råJN±B… âÆæÍÛàôCG0Ð$êÔ ¢œ9sÑc=®ÜóO´|ùAõ»¿þ:JEŠ”ß=ñDNeÉSY÷½9ì-ïN"çÉÂp×8yë­û‚À{ì2WSªcÕªãJÙÒÁ›ææêÕ'n«… ·©{¢££îÒÞ°³†“8Ð4@Ü66µ:vìG›7§8Œ=ÆÐo¼¥tÊ1§êZ¶ì¡¼õúÓÆIé‹/Ѝß7iÒ‘7n¯~ß½ûhjРM†õÙ[žÁð7@u"Õ(}ú\q¸ž!Cf¡Ê¨¹fôÜŒ‰9Iyó¾C]»Žtª½ºv,x±}ûö‚'CCM}7nBÎr°Ó$.3þÔ®][g3þêÕqv?lTÔ>úöÛ’ôÍ7Eiñâ=† ¸Ü¹ó*÷¯;—#Ç#êç/¿üžfÍZ¯oØpFùýbÖgoyoÃv^Œ ïÃ6…pÜñ;þkNøõ×wœª U›6} ›k®˜›ÎrÒÚµ§¨T©2‚¥I6|lÀ›àO“øáCǨdÉ’º$ÊPÜãxøðiv¿õ }wîd"÷?GDÂNJŒÖ°páyUŽ WÎÖ÷ãåñæÊõ´˜CP{:3×\97¡xíµ7iùòCv]7jÔ¬tš tð&øÓã$ÀÆ7Ù¦Mq“PÜ›2TÔwhùâªâ~üñœB_6}új‹Ryfç-ï Ø¡H—¾E´º¢´Ó #ëõ± n"¡;[ß /¼,Tø òíØq(5lØÖkç¦#ê]ó M¨Tœµ 7œÄ¥Í8ìÄa/Ž N©´ˆXíuƒoûwÞùŸzüŸÿd·«ãí-ï R¤ï“Ê g‹‹V8 ïZm!"á;%Fkøïï©öàP©¸âÞAä v™›³g¯<>/._¾\‚8’üÁ¥$@Ao2— K5Ô¼y{Ú´)Å-0ßáΨÜúõgDÇÊc,·ÌËX:çhyoÀÖU'½úþÆbÛÊc†ÖW¹òMUÞ°áu—Þ;ˆØ_æfëÖÁƒ]±¡i8‰GÌœ-nHðà”œ"Fî’}Ê%{ Ï=÷¢²B8 ¯]{ZäÄ“Ç_ý£ò–ŒS׬9EŸ}V8Ãúì-ïKضò¨_<‡¿bëªnÿÍÞ½/«^¸ðmCë~æ™çi•æ™07óäÉïssÙ²ƒÂ[»¡)3£/½ŽÄO6¦ XãuÆìíÛ÷öèÀÿõ×–Ô¨Q;Ú°á¬@½z­ä÷Mšt fͺ¨ßwî<\”Ϩ>{Ëû vDíËpkDï™Î¤j Ž ío­Ýo¾ýíX´Ûm÷5`À%¡>¿ðÂß )[Íšb>ɹԼyWj×n€_ÌM˜Yk÷ 'Ož¬ÚŠƒ/½ŽÄdiÆMèvaanˆ KOMõë•7`#zä‘GÅÆfíÚ-tßÿùg¬°Ç÷@áÂ%hÅŠ#º2&õŒíå} —¤†Y-w¾t51«ñŸ%wç%ëÔr5èAö”ö]«åuM—¾.J›•1íê{[° UµDyöÙhåÊs†ÿ$åŠëˆy„Õ1Øæ&øN&B–V{2,‰½ÙìÝJâûö!Mšâƃ‚‚LσÓÆÉ /…$‡Më3-w¤ïDR×Ö N´Äíç ž®kÏ£½ÆY½F¾l]y_þyNxLž#ÇqÌýe;ºu&x¯e‹–‚Á‡8†zžî^KâÀàACtK©/Wî'ápÃì}Ø}BØc™žY¹mKÒÝç_V GHŽVHŸa©¥ª©mzï™çiûŸ{3-ïo¿ú&m]qÔ%÷³bÅ9zúi“)aöìDaaiÜOvzý *ëò-€E6´¾ýÉhÎ5œÄ!—.]FnQ.#z÷í²† _EK’áAä8‘[+s¾x%»È†aÌ_Ž~¬`õWøsÏý£šrÑïÚ{…ÒvãÇ/qYý}ûŽÕ9÷Èšpî1Z w ‰kF ŽŠV7×SìØÙ` îÊ•ëÑ£>FeËÖ`RpÌÕ(¶,û¾Õ¾æÞµëe¿lëjÕ =z… µéOƒ8•)SN§ 6Ô¹Ç-$¾}û.¡ûó”Æ[´h!¤K—Á´aC²ÓX»6Q©³»p×ÕÚ†·Äúÿbë’ô’"·‹ñ0_él‹Üë¶ß^¾<=ûcO™²Š²gÏ¡òÅOô9°«µTÑêÆáA¹aC’MX¾üU­ÚP· ’oÏž=ÃÄn°­u1ìÃÖ%èÂåUÉÛÄõ+Ÿ‹…K(«žX—ýÎĉÔÇ<ò€FŒHËrm éûõ×ó¥#sèË—,±‡£ ^ÓêÂ¥E ö]ɳ.%q­¥Š\^H»ñúÒúõI6aÊ”h]CÐ…V®–óæm±¹‘ ‰›ôÞÕ•Â.žì †ƒ ¼ªºÒçÊõMšÊí¢ *ê€À±âÏØzÝlK#Er8›?Ó+HAÏeL¹ÌÀf'Ž›5ûÝæ†Z¶ìˆ°<áÁÆ`8ŽB…n«*÷×^û[™W)Ü.Vþ t[Ë#R«6õxÎäàXΤ'q`ì˜0ñ 4ЙÝàÜôé1¯…‡‡‹ãJ•*ÑÑ£'ü‡ÄÉ“MW³fMo<::š*V¬hŠê5rŽÒI Ã`4kvU%o˜vèp™ÛÅ „†ÎüU¡BÁg q$ŒÇ¹qã&»¸Õm$—@5ª×8hÐ ñÀ#GŽÇ•+ÿ¢4B à DG'QwUæ™)1•ÛÆ ÄÄ$Þ…„„è²ö€çŒŒî5$,Z©ÓA"‡a<Îõè1’ƒaBBÒè‰'¨þé§w©sÛ‡^½BoÕ«WOðrgʼÂà9wòª[IhóÛïéÜñ¥ÉáüùÛx€0N@k}õI›6W¸] Æ‚;U“Âùóç C°?ðZ«V­ÉÝœêvß´i«h<06Ð0Í1…nüÖ®Mb0vbøð4ÕûRªOÆ»Àmã˜Q¤Ÿ͘1CÍ'¼víÿ'q`èÐáé69+W6Ž1b¶Òg † X¸0™>ùä_Ý7œxŠ¿EýÅmã Œý‡à)eH“B¹™ ^óŸz„Äaz#M  ",,Lr¸bÅqÃ}É’Ã<ø~‰fÍ®ˆÔizéû<·Ë—3¬.ðR… •O3F·™éN“B¯ q 22J<¼¥˜ãmÛö2¤ÑƒƒCEp&r†?D-cËàUÕ«_géÛ#du§N!†Ô×¾½)Ù 2•Aƒ uZŒ˜9›<Å¥#q mÛvº¸*2t£É)h¥ÒPg‚±T¨P úê«iÞ¼×Ã`x#^{í_ÏËÏ?¿£AÉÜ..æ‚iÓVgÆžäQ’8¢–)m2Ë?~¼nyR±bUåMzÜîÆ^¶ì˜¾ÛµJ«V%ð@føÂÂÎ "ÇnÛÐ¥K¨]½xñ~»¯U­ª÷q™8q¢8F”VWG)ôj° 1éÂËÑÊ•+u¶ãP«8Òa r¸ C‹¨¨Ã]×®]_M8xJõ›>}&yšC=Nâ@``s]ò,W¤Z%,,’ }ãMpä´t¼<®¼'.W3”I›piÐ4@x¢3‰?Lå†àéZÛñaÆ©Ö*þyˆV¯>Ëð" o¼å÷âÈ3òô–?å^x\y–,9¢Z£@Í+\Éd®J|ì“$`Y"ƒÉ`¹¢ÍÉÙ¸qkTLâLâ ·¢yóŽjm¨QbbbÔ }èç-Üé5$®U«À„Ç\­2`ÀdXLâLâ ·`Èéª ´T£Èœ™à)oâM¯"ñ];cÅn/ ê­P‰%iÖ¬MJŸax¼Äíï#qWÞ€yó¶«ICCCu<5Š+3×û<‰k­Uð”É•ep˜Âò„Úÿ·wåÏQ•éú–¨#—Á)Ë‹”÷ªÃUÇѵä彊ˆHBH¶IÙCV²˜…=@²¯$d%!F¢T¼È.ÃEQ‚ËXþþdYÖwûùš÷Ëé-K§;}ºûMÕSésú¬}Þï9ï÷®LâLâ g`pð† \gÒnMkpeRÛ8°+=Cþ`Ò>[ê¬ÈnÒQi†å2\ ½‘¸=÷ 7g¹r=ââ²÷ ¦láëׯ—ë’“R„ùR—$ŽÁÁÆ·all¬|"5Ÿêõ¾óN­Ã^oï—,ÄN ñóçÏ‹çž{NÜu×]âÉ'ŸgΜ‘ëüñGoý‹ ÌÿN:%xà¹ïüùó岫HüÊ•+bÁ‚òZžyæ)“½&q—×ââzÕ»víR\UÅ-I@IG ;¤b3øeÄm65sØÃ+)i>ø¹HÄ}îÜ9ùþðÃËÏmmm"&&fÌ}ÊÕÓÓ#?777 ___—‘8ˆ»««K~ÆýÌ™3DZ÷Á$>)Çx-(¨sØ1Oœ¸ ìàè8¾A?`êupöƒa¡W®Ô-‰G³x3¢™„1ÑOœ>}Ý!~ï½÷;T ˜Ä­ÿýôÓObÞ¼yò3¶-Z$fΜ)žzê)qùòe‹ígÍš%~ýõWùÿï»ï>—›SpýýýR£vè}0‰»lÜ‚G(­žìà˜ù#Üy:»Ö{$‰Q;¢åda£BüøûïÿÓn!?¿nJÇñFØCâ(§088¨4ô?þX~þá‡$šÿ͘1Ãdæ[$nÏ=L–ÄùåIÈ0™ ††CïÃpL–+׌_Š^¬Sé½deº5‰£Hœ Ú·$œ·mæYv=¸ž㔌 Üù$þóÏ?ËJ•6·ùÍo~c±ÚíD5ñé q­}ÜÚõNé>˜Äí&rŒcŒg{öOL̿ݬ}¥ì“©M«G}½…º%‰SZ>ÅSgiØ")ìgÏž*»‰œ‚sIöcد_¿na+¿qã†Ò`±ùßÒ¥K•& kÛL‰ãzAÞøQf!‡Ý“¸Ý°w“#áÌðUhË}À×ß7 Ü݂ĵñãôƒ#ŸÕÕC,Ð:$qµè ˜ `z€i6eà(Ÿ·#‘̳gÏ–è*¿xñ¢tnâz¥òé§Ÿ:ö>˜Ä§µµge¡±ÈÞ1Õ´úÚzá.Üè6$ N“úÛ¡~ø‚‚¹î­·Þ–E߇†¾÷ df–KRЮëî¾*^yå ÔþqÏ=ÿjø]VˆŽŽË61ÙíµÐ[œ¸=¿¡ÞâÄí¹‡ôô2ñûßÏ5¼Dîóç?/**θDÜ à *l•““£ÒPŸ_ðŒÞíànKâ7þqKÕW!G'ì­Xçï¿Fôõ}íñÞÒrQ<÷ÜB ß´i§Ø°!Aœ9ó­DJÊA±n]¬ÍãLv{&q}‘xYY¿að‚hjº Ÿ_ZÚ!ÙŽÌòà.?¬^m Œ@=J”L+yÆxÑ­HÜ–£(Ø.ƒò×l§OÿãIüÅÿÛ0üЂÄ_xa‘¨ª:«–o‰—^zÍæq&»=“¸¾Hüõ×Wа° ‡=ߩȃ;¼ª<€7´aË7nr;wK7wtÒtže"÷ÐÐ(ÃòˆGøæÍÉ"**O~6'ñ™3ï•”ù:[ÇšìöLâú"ñ9sþM?þ¾ÃžïTäAï $™"QÐä¼þ Gæùó„;ò¡[’8Ï1etRÁvصüWúß.™lÐ$¾1å÷LŸÿüçEj$®ýþÎ;ï´ØÇÚ:{·×Bo$nÏï©7ŸìõãY6‹¹s—vìW_].ºº®ºDôðäÄ×ÇWùÓ(î‰âQ$´·u¨ˆ4.ŃAí_ê—šZâ4¡èêúbZ…°·÷+ñä“ÏŠ¶¶K6IüŽ;fLjNv{&q}‘8ž¿ÏÑÓsM¼÷Þ·"6¶P¼öÚJ—ȃžÇSFÆÛÁoÉÐdêÐC‘(zè“éµ$nžš’‘x@’رÐÑBQTÔ*fÏ~Èi‚g’‡uo¾¹ZdgWZl§]6N‡¿w½Û›“¸ž`7‰ë“½~<«µ "G”Š+äaªŽñ”—Wçðcgg—)¡ÖÐÄIÑ;xàpwt{rsG³®hª„"6Däyy%p¤ú:Cà&Cìæ$ÀáÙ×w]-÷÷ßúÓB›Çœìö }NGí2Hfw”gŒ«ÂÂEà”$¨ %ÌÌÌžÀAâð(#4h,"/*ªw[‹ØµË7î¡¡©r0 %·CƬï?ÙíúBFF¹=¿ÈÈ\+î®òàÈñU\Ü(mݶ5™Ü1ÅcIœj‡n6†¡® 5[†Ó“ÞÆ¹¹†õßÛ…ÎNã”/7·Îîc8 qírkëçâùçÿKN©—_~ÝpÝWmî3‘íúˆû·¿ý|~HÔ©©ùЭåáwŒ¦JŒ7{‘Ÿ_¥œ‚ÀÈ-¡PÂk_^žÂ}Câ 9‚÷©  …xCO…È™8 çb*㬴´E8…ƒÀ×­[§üêÕk“xÏ£H|rDþƒÁð ”–6{{$‰xP”øÃDÎ`0ƒ.]º"<‘ï<’ÄÏ?»<.‘ge•‰ÁÁï †¾®ñ|à©\ç±$>‘SåC`×®ý< 71Ëyyy^GàOâc¹6üpçÎ10pËéO?<†§£»ûÚ´œ'%¥Ø"ÜÛÜ+H|,"?|ø°"òmÛâEÿM§ ·P÷yºœÁp?öØ|ÑÔtÑiçÀ8 Û©Jn Š·¸×¸9‘#^]<ðàëëëeM£÷z»L~÷Ý_^,ââŠrlCOHJ:(,øÙŽ8üؽ½×ÅæÍQªÔÕLBÉ o$p¯"qªENᇠîÖÖVÕ¯ÓÇÇG5–8qâon#Ô †á ¥ãrÕª`«ã—j¡¬YïUîu$nž¢¯mJ®¯o€¬ÓìÁëêº&}ôqÑØx‘7ÃkÐÒb4vt\uÈñÐ •Ö ™´–À‘JïI™˜LâcýóâãÔ”Œ¥¢PûŠG“øîÝ5&Ï25µÌeׂñ‘l¢dÑØLLLTëQÜ:Ó3‰; Ý]½ªggll¬zÛ#ñ7ÞPϺºsÓ.Ì'N\’UÝh`ACêë»É$î¢ûötmœf|Àý÷?(š›?›ökhh8/×ß6k¾¡bÀAâDàhÍØÑÑ%˜¿˜Ä†‡ÏÉþ{æ±äÚÐ¥¥KßÇŽ L«@¿úª¯Ë•=ĆˆœGùwÙÞë±ÇþSjyÖöˆØ-fͺO:Á*+Ï ?¿-rŸ‡zD”–vZl¿cG¾,½Š}ðÙÞóîßß+óÔS äúºº¿JMûî»ï‘û>ñijRû¶õâ²uÿæëlo"×ê ˜+ ‹½=­ç¯¬Ë–ùªP`´[¤ðõëÄGæÙ†™·˜Ä-ñÉ'ŸËJgä§ÈhæÛ·oWS8Øé\aFqåôv2$ž“Sg•øˆm#ˆÌ|y¼@zúÑ)wáÂ¥r=HÛü»¹sÿÃa$n~¾‰\«^ÌwÓ%w99åªJDx„ê €qH9!t×®ôLâÓXÊžnšÊÁ6N¶¸ÌÌLEä°“Ãû~úô·NAk«©Fô—¿¼í´sÙ º6óõüãËr}nnƒ\.*j¿­Õ-·ØÚhuõ9µœ°Ï  ËÏÐPÍ·÷õÝ,—ñË _{λråV¹\[ûW‹ë§ëAc„±îu"ëlo"×êJ˜Ï›š>sÚ¹0ŽÂÓ5õŒv©1‡¦dÒ  ÷ÈR²LâNÂþ}”V µ“WWW«¸ÔåËýäôÏ‚mn›tæ r4‰ƒü¬i™3gβط¿ÿÖ˜ËæÛ76~*—.Êe˜?ì9o}ý‹ëÞ»·[¬Z!ü›ç·—ÄÍÏ7‘ku%Ì•ˆW^ySœ:uÓáç©®–yd&¡p_xJJ ;0™Ä§†ÞÞ>EذÏQ|*ìåááá·k7,‡E_߈Ã;;»F†yÑJI)ÓEâР­ÔwÌ—ìÆ[®¨8k“Äí9/!22_®_»6FžÃïí½1¡ý&z­®†µ¨(G?+k4|pË–-2OƒüPdÿ†³¡¡‰ùˆIÜ~\øøe'Ç´Ž4 ;;[…!†„„Ë GiA0Ÿ¤G‹ È¾ »ïD÷èò’%AR[‡yËpNå¼hÀXsG~~“Íóww_]]_™1L"¸&Ÿ&ñ‰\«@3B¤Ñ;j6ˆq²iS”&#SÍt‘ÀCµŒØþÍ$îÐÄ ÔÔ4%tiiiJèUºþ’%o‰¢¢fÀþÖ!ˆ‰)2ÿ3‡ÏÑ5˜®OI9bUË|á…Wmî;ÑesdfVM鼄9sæZ˜5ð¿½ý ù=¢eè;Ø‹±îé§_²y]Žøô€ŽŽ/¥ööÞtÈñŠ‹[UôÉŠ+ /ÍZ<””d’Àã­˜LâNDsS«Š'×VB’‰BDò[¶Ä´ ï7Q‚²ü..®D<úèÒ<€pÀåË7Š'þns߉.ã¸<ð;qÿý³ ¿ùÞ)Ÿ—PZÚ%æÍûƒÔ®Q¬ìøñåvþþaòûŒŒJi#Æ÷O?ý¢\W^þÔ¨q®‡ž'÷{“¹VOf—ááIjŒÄÅÆ©0^Œ# ã…y’íßLâNC ^g5zE;\²d©Ô:<ÈõðÂ`èFíÛGÕ*B™$‘ÈCÑ'#:Ë<Ã$>=• ò÷(­EyP‘œžˆfÑjå'O^åÁÌ$îu€ÜoÛ6š"£œ—';vìPߡޑ7V dw1Ui¤•£X½ŸŸŸÊôÌÌ<"ºº¾}}ß0ìBòþ-ôÞÞ‘].åÞŠ»Ü$ Ÿ)ê ÿÆ|Â$î2Àù¢uz"³“Ba+‡VNΚàà­¢ªj˜:Ãcù†œÓx@åA²}c¶ªÕ¾·nÝ&Í“Ì#Lâº@{[‡ð¹]{vqdš‘VÞÜÜ,V¬R O{ûUô f™‘‘éJaAÄ"·´¶o­ö v^2‰ë²ý[ff–ƾEE°€ÐQN“œ8pôääTÊ©'“ÃM'yyÕÂ××_5.ÎËË“³PJÜ¡Ä8³ÖK—®0_0‰ë_+÷÷P¶òœœW¡ÖÓ {÷vˆS§¾a0Ü ôŠÀÀ%Ë(ZEJ ärùÇwðAûf~`w«BZÐÊiz‰¸òúúz5½D $;Ð 090tÈéúõ£Šˆß ?)Ï$Û0£PÚ¼1#3‹ W1‰»/úûdú0 4ŠÚ÷õõ)m&Š-’ÕÐð “Cw€\†…fUBn ”éáƒ(Z5êÈæÈ&qω`AŠùX3± n2ÊoÒÔƒ ::[´´\bò`¸­­7(…Šœá×IMIUQ'æ¦d]¢ (Ç}3‰{d1­ÄÄJ놉E? =!>A3X–ˆ¤¤"qòä—L&Œiä.-í€Ì>&åòI3Iʇ ”yÙ§6.Aœûßó<Þ™Ä=ï ™˜Xñ©µ—Ã9¥ÈIéé‡Dsóß ÓÓ[ †SÑÒrYìÞ]¡Rå¥Ó2"BµJ#»÷æÍ›Õ÷(E¹æñÍ$îU•««kEÀí(J¢–p@[[›ôøÓ÷ÐÌ““Kde=&†£¹ÒjÞˆy$“ r$삨“òòJ®8È$îÝuX`/§D9%WYŸ¤õ`«%óøø|QUõ!“cʨ©ùH$$ª TçD;;„ÙiÉ$Î'본¨DÕ-G$€¹™SZm}`õê"?¿Þ@ô_ˆžž[ †Uœ<ù•س§QÊ ÉähgâN©$h†èèhqòFåNζdgL‚ÌssóU³fcÍ•p“- ¡]Ú8s8A15®®þˆI‹¡€¶s ª,HqÞílÏÜæ ùC™X&o&qÆšP3?›8›ÐÖŠ l!N·\AÛk×nyy5¬{¹ÖìJí¬mMЙd†ü"oØ¿QëGKÞ;®2È$Îp ™—–î3q€Èêpæƒ6Lš‹/1hñ©ÒvÞÕu“ ÎÃQV†† ™&Q&0ËÅDÇÈôxzù#ÓòàÁƒRŽh;ÈäŒÉ›IœáDh]m½Ô¦´Ób4oÖF´`€ZÓÎ}|üEbb‘8vì}ÑÙyÓ°Ï-† ²òC‘’²O¬Xhò¼­iÝÈ‹Ö dØrÅK&qÆ4Æ™£Zâöí‘&ƒ¦ôú$m‹œTHë§NC„U«BDRR‰¨ªúˆ Ý Q[{Q¤¦0qRRì6HñÝ$ø¹ÐšLŒ¹ ‘Rޏ¶7“8Ã…8ûÁ°´_Rx"åÏÎÎ6ÑÎ1anAd˲·–™ ü•+ƒD|üÃ@?k ô†ýF:Deå°œIùû›Î°–-[&’“’åóÕ¾ÀÑMõ½µ&È ärÃã‡Iœ¡3SËÑ#ÇLL-”Öon;‡3öQØÏÍ  Û·§‹’’vÑÒr…ÉÓ…8qâªØ·¯[DEe›¤Áq# Ï‘Š©‘) ý^͵nÈäƒM&Lâ 70µ ‰3BµŽP8;>†iµvÐCsCV(ʈ®6! Z´ô²²÷ÄÉ“_‹®®†“ÐÑqC”—Ÿ;w–ˆ  Í&$l4­’1Ýи©,=C9zºjŸ9ò  u£$2›L˜ÄnšÖ›gLL¬I˜"þÈ‘#&„N6ô¢¢"bA"ˆt ‰ÉÉ{ Úý)ÑÚz•Éw hk»&ìii‡äïŠß×ü% ç4œ“xÑjM%xnx~¨Q¯uR’­t8-žIœáA@w•ã•U"ts¨ ¡SV(¢Y´‰Tëeþþþ’‡Æ›/Š‹Û¥ÉÚºm-ûøñs¢´´SjÚëÖ…Y%m___ißF™bªÙM@ã˜Æ0£¢~­£f³Mòù"QŒåIœááøü³Ë2¤ ›Ö!j$å éEª¿Vó#Gb‹á]³f…¦ntœ-•‰F‘‘"'§F>ü®hn¾âuöÑ£C¢  Qþ lm¶¤¹‰¶mü®ø}µ¿7eQÂ9‰–gZâÆo¿uë6IܨUÏrÍ$Îðb Sot"×:Å oÛ¶Í&Á@S‡ùeïÞ½"..N¬\éo•¨(F}Ó¦h“#Éæ˜ÚÚ RCuW²njº$_R¸ÌFP >k/7 ÿC­x˜Gð’4×´©œÌ$˜ùøø˜½ —Èç„çÅ}+Lâ «NQôE,,,’ÓssBsgF"2×Ô‰„Pæ–¹i‹à7lØa8vޤ‰üüFÃþâСwEUÕyÑÞþµèì™6à|55¤3wïÞny=GåõáEäçd“¨IK†-;22Rö¡„IJîi®iã%…6€"¨_ÒÝÕË6n“8cr8þ‚4» Â!00Ђ`à E( )¸µZÀ ‡ŽE°óâg܆ ¤ýw,r·FöÈJ„¹Ž@ ""]š,€„„"‘’²ß&`‹¦m2Ž&‰Ƕf§¶…åË—K²Ž‹“&hиO[¿ÖÃA‰mQÐLIBÀï mf’áás,‡ &q†c«+vtt‰ä¤‹ P­M¦h  5[¨–à± ú5B{ÏÌÌ”ñëh ¶%t‹ð÷_5®&ïhà>pÈ€ 3¼dÖ+¢vð"Q›GõØš‘`Ì^ЋÒÚ}€ÈÑ›&ø*XÎLâŒi-ÌòA­i4öñ±­YƒÜa2 )ÈÛšIÆÙÃ.`_ÄJƒ$a¯'àÅY-à܈î íñâÀq\ Ž=9›kÖ tÜÎÌGÖ4l"l„{"†¿¡¡IÎt8~›Á$ÎÐ)ØôÇH ™‚ÚFk&<¨ ADÆ€haGË4ál œÄŽHšf x­]»Ö"6ÛaG툖pZ[ÛdxÄí³Œ0˜Än8äP«äŽÎE0Ç ã³Í`g0 “8ƒÁ`0˜Ä ƒIœÁ`0úÃÿ³è®û<ë‘IEND®B`‚jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/plot/doc-files/000077500000000000000000000000001463604235500263675ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/plot/doc-files/PieChartDemo2.svg000066400000000000000000000371171463604235500315070ustar00rootroot00000000000000 Pie Chart Demo 2OneTwoThreeFourFiveSixOne (34% percent)Two (8% percent)Three (21% percent)Four (14% percent)Five (9% percent)Six (15% percent) jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/plot/flow/000077500000000000000000000000001463604235500254715ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/plot/flow/doc-files/000077500000000000000000000000001463604235500273365ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/plot/flow/doc-files/FlowPlotDemo1.svg000066400000000000000000000443571463604235500325300ustar00rootroot00000000000000 Migration PatternsSource: https://www.data-to-viz.com/graph/sankey.htmlLatin AmericaNorth AmericaWest AsiaEuropeAfricaEast AsiaEuropeSoviet UnionNorth AmericaSouth East AsiaOceaniaLatin AmericaSouth AsiaSouth East AsiaAfricaOceaniaSouth AsiaEast AsiaSoviet UnionWest Asia jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/000077500000000000000000000000001463604235500253525ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/category/000077500000000000000000000000001463604235500271675ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/category/doc-files/000077500000000000000000000000001463604235500310345ustar00rootroot00000000000000AreaRendererSample.png000066400000000000000000000572331463604235500351760ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/category/doc-files‰PNG  IHDRXr5˜^bIDATxÚí½ l×½=\éꪺªªªRUUUUU•ªªªªªªT]UU…"ø“›~ù’¶J”Ü/¹‰’&¢‰ 6ÆØø…1oðÛ¿büÀ?À166Æ6ø&8`ƒÁqÀà„ÜüIƒ I.i"¸ìÏk“íŒÇç13gÎ9óX ýt˜ãó˜Y³Ïž5¿½öoMAA¶âk¤€ ‚ ‚‹ ‚ ‚‹ ‚ ‚‹ ‚ ‚ À"‚ØÉ}ík ‚ ‚‹ [055µHhà97Ï«¯¾*~ò“Ÿˆ¯ýëâ_ÿõ_Å7¿ùMñç?ÿYTTTˆ/¾ø‚‹  ,‚ ¢‹ìììEB#''ǕDzsçNñ/ÿò/‹ŽGßûÞ÷)°(ö‚‹ ág?ûÙ¢‹û/~ñ ×GQQQHaH¼P`AE„íÐ"ó£Íþ¸i˜ð½÷Þ“ÃZ²dÉñæ›oŠÿýßÿŸ~ú©`ßýîw)°‚ À""ºÐþøÇ??ÿùÏCêEÄË–-[Ä~ô#)Î.\¸ þò—¿ˆþð‡Òõoÿöoò³322¤ØÑâå—_¿ÿýï¥O ¯…PúÁ~ žzê))ŒŸ«Ý¯_þò—âîÝ»‹^wãÆ ñë_ÿ:èñܼySüõ¯ßøÆ7d$&&.ú ³ûk„3#Ù6‚ (°‚p 0¨.äˆPÄú‹ÿï~÷»Eb ¶¶6¤ BëÖ­[A?Saã·¿ýí‚÷8pÀX'§û.ˆžp~4³ûk„3 ,‚ À"Â#ÐÏ„ÈËË 9›0œ¸8wî܂픔™±)))Yô¼VäA”àu˜á—œœ¼(Èiߣ)hF`„zQjfÝè#‚‹ ‹ÐÏ~x0Th‡ É,G£ý{}}ý¢×@ FÌ;eVlÊÈá;FGGåßÿùÏÊer¾ÿýïúž`Ï[Ù_£Ç¢ÏþAE„‹0==½àBŽzOð)àÿúL”& '0„¦ý; {655Él>·¿¿¾Ö ÷laßô&s£b™6»*¹{ÞÊþ=%þTœSÍ´üÉO~ÂKXAAAAAAAAAEAAEAAEAAEAAP`AAP`AAP`Ž>y^ÇŒk¬‘+·s¯óÉ÷:ù¸ˆøž“÷ß_<úè£rY+,Ñ„Gö[Xaõ½¾ùÍoÊ“Gí:düQ:·£÷#ǡֱ‹<(¿ëøiµñüèèè‚çñ:<ßÚÚêÚsèe东ØÞˆø´µ‡zH~ǵÏ^nž8*µhí÷¿ÿ}[V,V¼ññÇË}úÎw¾³àù¤¤$ù<>Öâ[ßú–|þÖ­[žXNosVE"–?Dwb_FåpüÇü‡w÷ÿþïÿ._£€;û_þò—ò¹o|ãâå—_Ÿ~ú©¡Ï Æ_ýêWâë_ÿº|,**2u\VŽC=¯`8yò¤ü|ì¾ÿ?ÿó?½''êµýýýòøðšßüæ7â½÷ÞÇ¿þõ¯åsøÛôôôügöôôˆßýîwóÇÿ9rÄÔy‡¸€€øîw¿+¿ã·¿ý­(-- ÉA¨so„ë`Ÿgçq™Î >à ?øÁäs?üá矛™™‘ÏáõFÛ»‘ÎTû|¨sbõû‚qŠC3û¤¬áÚT¨ãWÿïèèm_ýîµ7ŽFÚ’Ñöf†ÏpmÜ OF/fÛy°ãQ™ZôOZü×ýׂŒ¬‘}2ÓW©÷à·óÔSOÉcÀçý]˜='FûŒPýùO~òùüÅ‹åö¦M›ä6>¸pá‚ÜþñlûõÇÈu†+À‰„2W'ý§?ý©ÜÖž`uâÐh1œˆ!Dˆ”@bL 4 46íëñœö3Õ]jhŒøÿc=&ßWRR"·ÿú׿ú\=ÐÉâï;ÿâ‹/Äììì¢}w\VŽÃ膑ðÃÁsbbB>‡N´€ŽA]ØõÏ¡ãz{{çyAǃÀÿµŸc„õžÎÎN¹ Ñ„‹‚‘ @ ÎÂÛp»ŽË,þö·¿-¸È Ëmt˜ÚaÂÆÆF¹×mïfV¨sböûÂqŠC£û¤G¨×šX/¼ð‚ü-A´¨aÙúúzSÙ©p¯±Âg¨6n†'£}“™vîx222ä6.úÚöŒçÍì“™¾J½¿\—p>!B´¿·HΉ‘>ÃHþüóÏË¿¿öÚk *ƒ]VV&·!Híºþ0ƒåp,h999r;///ä DƒÀs¸;44\Põ ­çë÷¿ÿ½|^ݽàoØÆ]ˆ‘ÏÕC}Þ¹sç ]8—Ñã0Úð—.]ºè‡¬o8NŒc`[í·úLu·LMMìØBñƒÏÓŽÑ‹s ÎÂÛpÇj×q™…º»W¿#%¸¶lÙ²àù¿üå/r»½½ÝR{7"0B³ßg¦}é94ºOz„z­¥ÅåË—eíXVø ÕÆÍðd´o2ÓÎ.þ؆DF%ÔM‰‘} ×W…:ŸÈtEzNŒôFúseµA†߉ýÏ¿øÅ/äß‘yÂßñ:»®?X‡ÚPxÕpU£0Ú©Jó#‹ÒÝêN>T' aJ{j‡B}n°Ï t!7:Übå8Œ6ü`û§}o8NÌtZú ^ }V³cŒðƒ»kµ „v˜ÌŒ‡ÅȹÕq™Å?ÿùOÙñÁ ÀÏø£ýHþwç.pAÀëîܹcèÜZi³¡Î‰Ùï3{ƒµËpíD‹P¯µ*°ôm»–Y>õq3V~¨høÆ¡ñ9J™‡ûѨa£`™pŸkôÇcô¸¬‡Ñ†êÇ­žÇI<Ö7äPŒš…ª½«4{q6rn*°ÔÅ;¼$Úa5¬‚aCœKøÎŒ¶w+m6Ô91û}v ¬píD‹P¯µ*°T#ÜÓl{3Ëg¸6n†'«}S¨vnäxôK;YÃÊ>YXúóÉ9±K`Š—•+WÊ÷âøÑ'$''ËçaÁ±óúCå``ØB™A w(Ÿˆ•j FkäÇ‹ž×Í|®0PâoHùF2Übö8‰;tžx 2‡Á¾#'V:-•×ò‚”z¸¡´PÏ#Å®Oo›9_F^î5v—ÙŽ+%%E¾^e!ÔƒúþŸýìgò­Ñön%ãꜘý>³çÐÈo#P; ýk ïUž8 ÉÛÙÞÌòi†ËpYX˜¤=Ÿ‘œ#}†Ñþ@ù°ÀÄ‘IªÝB<Ûyý1z¡ÀŠ1ÐA©™*PÅZÀ Ž»ü]Ý)˜ý¡ª‹‰jäj|:Üãúê® 3KÔIÝõ‡û\=0sHÕ(ÂqáæC£ ÜêqhÅf‰ƒš¹ Ó&îaâT?Rõ¹á8±ÒiaHX}/îHñÝÊ8ª7ƒ‡âöPfQ˜KCqìsœÛpŸg×qYXê<釨µ¾þ†&ܹÕC™oáÄoÂüÕW_5|NÌ~ŸÙsì·®è/xÁ^käøÕÿÕЄÊÏþsÙ×iÁÈq…{Y>õq333#שÂûñZíÚYF^ z2–À÷ ÊñË/¿,>ýôSÃûŠjÃX“R-›„uôJKK»ZÈT­±‡H±U€ .ÈmT6ÃU¤Ç ªà£â³¾’:–øÀó¨Œ `ŸQ­Y}þäÈ‘°pmEhL-Ç-ªŠ³™ãÕF0„jcÁöü`ɼÇÈþmËúï0rBñjôü„kFÎG ýÐóèÿ¡^c„W=Œs s¨E¨ß´™ üXÒé׿þµä ÕÄ-óž ,Âã –<ÁzhX‚Ï¡£WÀ BDûwµ,‘ú ,¦‹:Dµö>×èkÔrGX« ߣ–cùë_ÿjx_ÕÚnXI tÈ ÖH|íµ×ä6ÖtÄ6:s ¬¬l~I £ßoÇ1è/.j¹ ÕIc© ¼âïWk¼áØñ„âÁìz…Z@üâo¢JD`íKí{¯™;üPm,Ø~+qafŒ´eíÿœƒP¼š9?¡Ú…‘óa5ëî5FxÕÂÌ1ëÏ¡¡~Óf&†ÆùÄZ®x®¾¾>®<X„–èàC­zŽC­%ì3°h­Z ÔèkÔ*ðXù]}ZÏÐ辪•ØUV*ÔB£X{ ߥ²jacd6ðw¼Îè÷Ûq z ㇿ#óࢠ]É^}§ö˜§¦¦äsÈXXjýNí±ú÷„;^«C(ú6l¿±~›VàÙ£mYûÿpç ¯VϾ]9ÑXfy5sÌús¨G¨ß´Ù5$õ}2Mñ䉠À"|&°ôÏãŽÜI#Mt¿ÑEXC‰0ýkp! ”†7³Ø+îlÕöw¾óñ—¿üE¼ÿþû]-dúƒü`>;€ ñ7¼_ýßè÷Ûq Á²Ȱi…ŸºÐª P Ï4³˜±êXô?3ÇkT`…kcF÷;Üþ˜i˲PÁÎA¨ý³ëü9ÑXFÎs Qdå˜õõ›¶*°ô}O¼x"(°Ÿ ,u÷Ò¹sçæï¬Cu`êÎ-Tgª*v7kd_1d‚¬†nÔó*CʇµråJ¹86t’ÉÉÉòùŸþô§¦¾ßŽc„þð‡ò³1¼Š‹Â÷¾÷=[.fV/’F×è…4\3ú™áöÇL[ÖG¨s eä|DK`9ÏÑX¡~ÓV–¾ï‰Oás¨³ ×·téRïÑÛzã»Uq‚!…PCo€òaáqñUau'ª2òývƒBvvö¼§EeÚÔ°†`0D¡ޱ’ýøÍo~#ÿöÞ{ïY>ÞPÛp¯±"°Âí™¶¬ÿŽPç Ôþ9?FÚ…‘óa•Ûp¯1ržµˆä˜CAÿ›6Ò®}Ïððð‚¾'–<1£EEP`Í?ÿ³ŸýlA§¡¼KúìÎ;ó ¼ èü0+Êèk`bUw§ê"†NËè¾¢cÇ{5¬Ãi0h¥±±Q>×ÐÐ0ÿ\ ™F¡¾ßŽc„ÙÙÙà 0ú*`˜ ÏaÒ†À`ÀVÔ–òbÊ«¯¾ò{÷ìÙ3ÿ¹øLdn`ø7s¼þ¦fÈC¸6f”¯pûc´-úŽPç Ôþ9?FÚ…‘óø×¿'ÜkŒœg-"9æ@b-ØoÚH»ÖNVP}f_B,i7V ܇û^L‡Ç0)>þ#”ÙÐ{WB/1ƒRú!-µ13|…Ú£m9Øw„:¡ö/Üù1Ú.Œœ=ñ¯ÿ\#¯ wžÍ¶I£B#ÔoÚH»V¿odµû¢D[¬y¢À¢À"ˆˆEÓâöbçΒ쬬¸íƒº kg_ñž‚ (° ,·™ ½¯%ÚÀZ{{»~AfOÕÂ0*ÏAìÁóAX„ Ò󑾆x…À*QÇ(”ơUŒç öàù  ,‚ ‚ ‚‹ ‚ ‚‹ ‚ ‚‹ ‚ ‚‹ ‚ ‚ À"‚ ‚ À"‚ ‚ À"‚ ‚ (°‚ ‚ (°Ì!;;{Áößþö7¹ˆk¼cݺuŽØrA>ȹ äƒ\Ø þXéééâøñãqÙÙYGì¹ ä‚\ òA.ìõë×S`Å+ÆÇÇùc äƒ\ rA><ȃÁ`0 3X¼ã ä‚\ rA>˜Á¢Àâ˜9¹ ä‚\ òAï8Èù ä‚\rá3õÑG‰ÌÌL±|ùrÉÉÉâƒ> ‹Á`0 –U¼ð ¢½½]Ü¿_Fkk«xî¹ç˜Áâù ä‚\rAeË–-“ÂJ‹¥K—ÒƒÅ1sòA.ȹ ä‚Ë*ÚÚÚD]]èêê~ø¡èïïÛ·og‹wäƒ\ rA>È–U@T­\¹RŠª'žxBú±>ùä“€ÂJ…R᪓€Gns›ÛÎÛîéccäƒÛÜævøí15³33ŽØW ¬W^yEĮ̀pàÀkþ0ƒÅ;òá¾èè……3¢¹ù*ÛÛ¹ AcèèQ1ÞØ(®ŠÙ;Åä©SÌ`YE ¿=X3'Þ‰––1‘—wMìÜ9+ªªþ1'¶N±]°] òñU ˆÓííbª²R\›U³šøïÑQ ,«@Y 0»ïÙ³GŠ&f°x÷E>ܵµ¤°RQRrCìÚõŽ`»àï„\øÁ¾>qnÿ~q¹¸x¨ÒÆåŽ ,«¸}û¶M˜Mˆ€+‹u° ÷Do﨨˜Z ®´ÑØÈ *ƒá×8uø°˜¬©×òò‚ +“ÕÕX¬äÎ ¹ ˆîîQ\|% °BëÁÿ¯‰®®“lü} ŸðlÕ[ÍÍârIIXQ¥ë{öP`q-BzÈùhk;#òò®Í\Áƒ¥þ_Z:íë¡BþNÈ…øéîjkÅÕü|SÂJÅíª*éÑ¢Àb‹A.|ˆý™ &®f°Ä믟e»` ñq¢¿_œiko——[UÚ¸QR"†{z(°ü,° ¿Fÿ Q]=RX d»Ž" †b¸»[L44XÎV Ì0¤Àb‹A.|ÅЇbV QA¥Ï`!jj&Ù.ø!nå#D‰;¬³¯¿NE;rá>TñP3+­Kø,¶ þFîáC_4Zñª*9ã‹,¹ðÍÍo…õ[Í`=xþ²èë;ÁvÁßÃá|Œvtˆ‹ÕÕQÉVË`ÁËEEƒáy¿†ô¬ø­ÂECÃ9f0P<´¼üíˆDT° –ªÕÙy’í‚¿òá>Ìf ƒ½½Xô`1È…÷øU<Ô–kcñwB.œÈJ,Œµ´ˆé²²¸‰*½ £qžIHÅ;rA>lpÅCíË`ù«6'äÂI|œì쌨 h´3XcÍÍXô`1Þ #ÅCíŽüü«¢·wü3ÑÎVõõIá‚lÕ5‰ª@q¾¾ž‹,¹p?0³WV^²]<É`!P¸”í‚¿ò¥lUW—8_Wç¸lU¨ Ö¥ÊJ ,z°äÂÝ|Àoe¦x¨,?ÕÆâï„\Ä’_Μ.-u¼¨ äÁB½- ,f°x÷E.\ËDMAÁLÔ†ÿŒf°ÅÅ—e&í‚¿òY¶Ê‰Þ*³,D<}¦Àb0–cÿþs1÷[…‹úúó<7 †ofº,[.`ħÀb‹w_äÁ5|D³xh$,‡Ÿb»ào„|¬[%³Uq¬[Í Ö™ÖV ,z°è îàÃŽâ¡Ñò`©€Ì‹µ±ø;!vðœg›šâVe=V,ÄDC–,Y²$`0ƒÅ»QòÝèê:iz±æxd°¾d»ào„||Xp2†k:!ƒ…5)°"ÀÍ›7ÅóÏ?OƒÅhi³­xh,"/ïš8ztˆçŽáë@¶êÜþýžÍV…‹wví¢ÀŠ¢w®1ƒÅ»QòÀ¢ÊñJV3Xjc]d»àoÄ—|œêèÙ/y«¬d°püXʇË>øàñâ‹/ÒƒE?ùˆBôõ ŠŠŠ©¸f¢¬x°´ÑÞ~ší‚¿_ð1oÕë¯û6[ȃ…éî¦À²‚mÛ¶‰¡¡¡ ÂJ…²1*ÅÇxl_¹r%®ßï¤í™™¿‹)iL&ãâÆqߟS§&¥Y$ˆ•IŠõvEÅ­ˆÞ_Uõþ\ûz×íC…ßؾvþ¼olWÞxCÜèï—~Þ¾q踖—'³7*‹ãÇíÿ©©Y°ýþØX\Ú««bÅŠ¬ƒåòЪ,.¾"ײãºrÞ.먫cm,/ÌÚ3¾ÎÒ0Œ&eL·gƆ,+\ÌÄiÑgVrg¸ÒÛSVö¶hm=#úúNWÎI¬‹‡Æ³6Vw÷0Ï;ýV ŸfXR`q-BÏû­ìÌR@¬¡F“× Jƪm¼þú[Rt8YÙ™ÁBTV^b»p™ßŠf°"åB‹kzÒo…‹Zt}6Lñ¾é S|´Û8ª©¹àЬ“],m´µ½Éváp¿Õ¥ÊJzŽèÁ²‹·š›)°˜Áò¾ß*ÚY UêÁÍÇÑlÈö•–N»fXÏî –Ê|ÂwÆvá¿36Ì`EÊE<}¦ÀbD-PA;žk×aè 3>ÅóáC¿U¸À„ ¶ gÅéöv_.íˆ~LUVR`1ƒå}¿U¬²ú)ú¯¿~Ö5Y­h´ ”ºpR}+'´ Nf°œï·bƆ|DÊÅ•âb ,z°¼ï·Š•Ï&Päç_õõçEW׈o¼6úšcn‹h¶ââË®JöbŸaÄoEÏù°ƒ‹X/úLŻјû­â‘Á \êaZ´´ŒIâÕLüVN­oå”¶ÑÐ0Î –ÃýVÌØ;¸ˆõ¢ÏX [¢­íL\ýV‘fµPêÁéY-³ï™—ÖŒnm¬þŽcgÚÚè·bÄ4Ðæ(°˜Áò´ßÊ)¬`Y-',ËiÛ€ßÌéõ­œÔ6JKݱŒŽWú ³~+flȇ\ ÝQ`у劀©¨˜r¼ÏÆjV«¶öBÜf Zm8'N\OÐ m£¹yŒ¬(Ç`_Ÿ˜ª¨ çˆ¬¸pq±ºš‹,oû­œœÁrJVËJÛÀ'LÛ^‹UÛ€¨vzm,7÷‘ø­˜±!vpëEŸ)°¾ò[¹9«Åsýpó2:ô[1¡ãÚ\¤ÀbËÓ~+·e°ïïå¨fµŒ¶ ø†êë'\YßÊ©mÃÉË踱ϰÃoÅŒ ù°‹‹áž ,z°œ:±Ëoå–™hð<ÙÕ2Ò6 î*+§<ŸUŠuÛ@¥{§ÖÆrSŸ1ÔÛk›ßŠž#òaX-€‹,Gù­°¾Ÿ—²NÏj…küVW|1l¶…°™ÁŠÌo…ÊÙÌØ0ƒå4.b¹è3#d '½=ÎÈjñœÄ6:;O²°c--ô[1jk)°˜ÁŠ ø¦—³NÎjjð[Åòœø9ƒõ 6Ö´ãªü;½Ï˜hh`Ɔ,Gsñvy9V8\½zU¤¤¤ˆåË—Ë Ë=~+/y°¢•ÕÒ· ´òò·}™IŠgÛØ¿ÿ=Xð[ÑsD>ìâb¦°+nܸ!žþy111Á – ýV~Ê`ËjáÂ*«¥mªòb}+7´ Åb=Gf°âë·bƆ|ØÉE¬}v¥À*œS ###ô`ÑÛãú¬VuõEÑÑ1¢¾Õ›žYò†µ±è·b0œ§¦À †§Ÿ~ZЬ‡~XB0ݾ}›¬4ýš¥pbV m£¾þ¼§ë[¹©m +ûŒ…³036Ì`¹‘ ÜP`ÁC=$ÆÆÆÄýû÷Ž{÷D]]HMM (¬Th‘‘‘!½ ª³Âc<¶1ÔÏïÇcoï Ø»÷]ésQ2<Æz{ïÞÏãúýNÙFµøææ«¢»û3òñåvMÍÿÄ}êëo‰·ÞšˆûïuæÝ161×þëü™3â£ÖÖù‹áy‰õöÿÔÔÄõû¶M>¾Úþ|ïÞ¿ÑÛ“ß‹k–ZË–-cË‚ß ™f)˜µ!á38ã¾’Âl£xíâkbàø@ÜüV—’)aƆ|Xå"V‹>»R`=÷ÜsâæÍ› žûÃþ@–‰@éú­ 3qMtuů6VÝù:±sv§Œú‰ú˜? 4ÒoÅðBà&+šššæîjKæ}WgÏž»wïfËàÚuuu癥`Ö†\Xˆ²²iùŠù ÑX³V%7JæEÖÁ7ÆæûÄùº:fl˜Áò Xô93 ][«¼¼\šÜ14±u÷î]ÖÁ2PßÊ©µ”¼^‹|x‡‹Xׯ:|ê°È»–'EUÕ?ªæžëíˆz}+fdÝ'ÖÁòî¦Àb%w[¢§gØÑk×1cC>ÜÂÊfà÷“ßíp(˜)˜UÚ ¢p¦PôõF廇{zâRߊò .b±è3–u– fx±f0l«5õßm߉>Qry¡  åo—‹þöwŒvtˆ™‚^¬žsû÷S`1ƒY45uE¡Jflȇ۸ˆfm,¦ÊK•‹Ä”>ƒ¥¢f²Æ¶ï>ÛÔ$=*ÌØ0ƒåe.&kj(°¼,°¢éÁÂ"µÕÕ“ôÙÐwD.¢……3¦ñ6Õ“Õ…”Öƒ¥–±ÈŠ'Âô;Y]MÏ=X¾àbº¬Œ‹,óµÓvíz‡Y fmÈ… kc5žk *¢‚e°"5½=*ÞÙµ‹f°|ÃE,}¦ÀòX Nî¬yaf0b¡Ö’4(½°óÚΰ¾«`‘5_t˜›u²«K^lxafø-0K–‹,cóÁ7][<”òáV.°B_߉ÈoŽFºDÞÕ¼*TKÅ®wv‰ÞAcŽ7tmñPflÈG¤\`2=Xa£¾~ÂÕ ÓsD>ÜÌEccd7K(µP|¹8¬x åÁÒFåTeØåt&êëÅ5zŽèÁò1X€‹¬àS¹ûEeå%f)˜µ!q®…µ=-­®0'„ÊÞ.3$œŒd°TÀËèûûúÄ¥ÊJfl˜Áò= Xô`_¬¹¸ø2/ †¢¢bÊÒ2:5j,{®ÂEÛ™¶Å‹5;¸x(ƒ˘š»Ñ ÀbËóÅC™±!^ࢥeÌÔïøõ³¯›Lf2XrfáÕ¼yӻ׊‡2cC>"å+P`уµpá׿1ךÙé9"^æ7=½½Æjc:}ÈôŒA£¬Ëé\)Çö¹ÖÌNÏùˆ&Ñ\ô™ËE, ?ÔÖ^`–‚Yráà@ß°3OvÉ’ fÅ’Ù ÖÎk;Ä®ñtÑÔ“,fòv0cì ¹ˆá¢ÏX. -+›æ—Ápym¬îáî 8G+rg¶‰²3kEÕH‚ŒŽCëxñe0tq¦­ËϬÇO‰ââ+ÌR0kC.\˜|‚åª-à\úN©eÑd4ƒ•ÿÎfQqzͼ¸Rqbßzfl˜µ!šol¤Àò««½}Ô‹5ÓsD>È…~ñEå-àl·«àí¢òdâ"q%c8AŒ—o¦çˆ,rñe\¬®¦Àòc Å Ý\<”òáo.ÖÆj˜hˆxØ/\«äB–¨ $¬4Q3¸Z\*ÙÊŒ 3Xä"Ê‹>S`90°ìFuõE^\ —Gié´œœÒ2Ö]ÏÕÕí¢ôìºÂJû{“Å•‚¼3|×òò(°´X²dIÀðB«§gX®mÆ,³6äÁ\ìi?!ò®åÙ"¤e°ò®låüVᢵ+U\ËeƆ,r1ÜÓC¥X^ô`:tÚSÅCé9"~çb[Ñ´X3X+¶ÍäÚ"°ô,ø­*N­6-®TmΠçˆ,ßsqº½ËkK›Ájk{Ófvflȇ_¸Ø‘?#’{šDÂH•Xw¶Ôö VáTNp3»‰x³:‡f°|ÍE´}v­ÀZºt©Œ'žxBTTTˆ/¾øÂµ¬úúó¾1³3~‰uíoHq¥bãÛöÕ¾Ú5‘ÖÌn8†Äù²-¼03|jk)°áæÍ›R`åääV*´ÈÈÈÃs*ƒ„Çxl_¾|E.‹;t ƒ¨;u?n×Ôü¯Ÿ|ÞÎÝ6#^Ë}WìÉ¿.ʶ^E.ÉG§ookïk‡*DÒñÝò1íÔk"ýÔnQp1G”]Ù*ö\Ï—E—6˜ÚFT͉«½o¥Šš/}WxŒt»õd¦xwÑüÝ?†XTÀ©Û·æú}7í/ùˆÝöÿÔÔ˜zýÍ––¨\ï=1‹ðþýûbÙ²e®Ë`MN^ç>=Gä#غ~›¦EEҨ؛ú–¨JqM䵈Ôî¼€±­?;¢l„Q•]™«¦w7-§CÏù°‹‹™ÂBf°‚áÎ;â±ÇsÀêî~‡b‚ž#ò ªL<)KÍšÓ®WE›;Eê‘ü  Q2˜bYÕX˜-h&:¦ÑsD–/¹ˆÆ¢Ï®X‰‰‰bllLf® ®Š‹‹çîø«\'°ÊËߦ˜`0t±+}ÜU+¥½"ýââ ‘ut›¨IŒªPŠ$†÷nàEšá»8uø°7ÖgŸ}& ÄÃ?¼`Fà+¯¼"Z[[þ¿££C$$$ˆ‡zHüéO{öìqÝ,B¬SVZúyAeƆ||¹Û¯ŠÝ)c‹„‹2Xå)ÇEf[iXq¥bÇ@–#3X*ÞªÜÈŒ 3X¾âb¬¥Å+777`ÐÉÉI)˜üPɽ£c”>zŽÈÇ—‘·õŠ(O~3 xqº«rõØÐXeX\!ÒºsEÙp’£ån¿Õ—±;½Ï¶ƒF¢àDš£½Xjfáù²-¼˜3<C½½ÞX¨‡ååB£Zÿ³6Ì`y•»‹‡Æ3ƒƒ‘ÔÆrJ Q?$¦Š·2cà –§¸íèðžÀ‚¸jnnö´ÀÒú¯è;¢Ës|˜(ê V”f ‰­ýëÁÒÆîµq[N‡ž#ò .ÞšÓ!ž5¹ëÅ—–ÖŬ 3X^âCÐÌî´ ÖÖŠ}qW*JSÁRÑþÆ:fl˜Áò v.úì…ÌUZZš¸}û¶gýW /Fþ–˦‹‡:~Á¢–¸Š«¯jc­v¾k.ú›²xagx"¦*+YhÔmKï¿bÖ†,/ðQ˜3õ⡱Î`lëˆÉŒA#±­?Ûñ¬xÍ,dƆ|Dƒ‹+ÅÅîXáj_y½–ÞEß¹p;%6›ÙàÁ*Éîi]ŽWjc9̓µhÍÂòÍôуåz.ìZô™+Nþ+fmÈ…kùØqM쎡è‰U 8g*q”¸Bd÷nuEKîßàj1]´f°\Í…]‹>»~ˆ°§§'¬(s‚À¢ÿŠá…°«x¨ÓåÖ7—9N\©È=žé /–Z³ðJ×,d¸7δµ¹W`ݼyS$$$ˆ¥K—F”Á—Æx§ ¬@þ+fmÈ…ÛøˆWñÐXd°¢¹Æ ‘Ö“+ʆ“ŸÁÒÎ,¼–ËŒ 3Xîäb¼±Ñ½ ‚¢è¡‡²,°®_¿.EÚgŸ}æxÈEß¹pEÙcâ·Š‡+· ÕÑâJŦcíÁÒÅÑæ zŽèÁr%««Ý+°yäÑÚÚ*‹‹._¾\Ü»wO>ßßß/***¾¥^yåqëÖ­y—“VCÃ8³6Ì`¹“(uJ+Vk ÚfxIwÀBœ¬ÛÀŒ 3X®ã®EŸã"°04q<þøãâòåË þIIIâêÕ« ôÁ„• -22æî¬fgå#HÀc4·d$ÔEr{×ÿ[w]üj[ÿw?mWÜ¥ù×ý{üÜÞUøß²x(²HJèxe»<{@®1˜v´@d+’r8ÎÁÛ™Gwˆ¢¡,±ëTŽ|Ì?žîìít1°k¸ß&ƶî'7ÔÉG»¶§·Ê‹)2ê¢êÇíÿ®ß-fªò}{üvoß®®¶åú…¡=ìšš*öîÝ+ÿ?11!‡ ­ÎHtb+˜ÿ ±£nR$ŒT‰”#-bCÝI±3÷š‡Ä².<˜É5wA/ÚpIfN˜ÁН™½"ŽkF;ƒ…ƒ™w¹&s5ïÅ:Zà¾}ž±à;í¢.iî‚¶¹Ü—YxÜÆª6жÎTY"þ¼–#)2kmÿ›²yÃ==îÌ`uwwK‘äää,I/½ô’¥N" æ¿Blí’KEÒ‰Z‘ÑÒ#¶NûJLìJ_ä³™zWÆ„\‚…,ï§«"iP¬½ÜuBE.}¬È•û½þ@¹œ©v1,†²|#.•l½2åbÛÁÖ©¬=‘$zZ2äkéÁ²§ÛÛÝ_¦áÎ;Rdahð¹çž333žXÁüWˆ”¡Æk>†«Djg›ØX5æù¬ÌÓ¡²0V£æRÁÆ·}•ÕŠW+VÅCã™ÁÚT[ëJ‘âÖ –Šœ}¯‰ÊÕCQkYâÚÎ\Ïf«Fkræ³Uk™aÚÜ"߯E¹ÝšÁ²cÑç˜ ,Û?þøcÛka9Y`UV^ x!Û²ûB`q¥‹¤z‘ÕÔ/¶y/«•¿ùS™¬wà׬VTcûU±{ÝYÏÕ·Z´€sù>× /Äæêú¨žßöubfG¾g„ÂÅ]³UVBeµ&K·Ðo .Ì݈¹J`A!S•™™)FGGçî^]‹0”ÿ*óõcbÍéC" ‘8T-Öê›+Æ=‘Õ’MM%3Y ˆ²Ò91¿iÚ³Y­Xf°ò¶\åñ[E3ƒU°£ÝU3½–ÁR±³¸9ªíbr¯¸¸u—k/ìÈ4!ã?•©*ú&j¤É¬VmŽ˜ÉßÁ Vx»¼Ü] ÂJ[\ô±Ç›»”,˜Eè%ʕܻ_¤¾µ×°ÀÒÞ»aï°Ø‘7ãÚl‰þ‚nÕgƒªâfôšÐŠ• C¯ñ(kVIÎÇ­1è'–>P#ší¢fõ xkc¥ë²U]mér¹!+*+5ÒTV ßM–NèºÏƒõù矋ÎÎN‘˜˜¸ Ðè /¼ 80_ÛÊ +˜ÿ ¥ ”Ìd°ÅêÁiŠÇp£›ÄCi€¡¨H³•_šâó·\fË`;Ðo Öîô>‘þF±'„‰2Xò8æÄ.ÎK´ÛÅ@Ö~Çg«0ëï@÷ÚÈ׌°Ê¿—f ÚU,ÒEŸãjr‡˜jnn+V¬˜ZFÊ4¸E`ó_­ßw""a(Rº›”zpºpÈ<ýE{מyPêÞª€‘ ¿U kŽ÷ƒ™­»ér` LFyÊñ¨·žŒ1³#ÏQ`¼|sDÙªh†ßg jãÔáÃÞXìùÂ… âÙgŸ5µ¡“Vÿ‰ þ«µÝlÉ`3ÅÃßåDS|áœè©Œa­#iŠŸtn4ÅG+ƒåÆÅš­¶ ÌXËnªô”(ñJKņÆ*Ë3 Í´‹–”#âí…q½Xcìá½öd«¢‘ÁòÒ D»2Xc--îXŸ|ò‰èèèX0\è• V0ÿÕÖâ)Y†bȪËP ÔÃáƒ"§úMG˜âáõ 5c0šëÍ©R¨ïä¯V4EIÇ7—«[•ÿ nUSO²ã†ìâ™Á Èè!³¬–]¬H}¦ÀŠ‘ÿjGÁér’ ŠVVKÎVóÈÅÝIY-#~7£C²^‹-¯í¥‰3 #ˆa1¸¾!l•õ=ƒÞ÷VE#«…Y”Èø¹Ñ‡unÿ~ ,§¬`þ«œšÑÅ#‡e°ìÈjÁ÷cehªÆ^¡XfµÌf°`ÖGÝ//Чpmë3ýšÁ2:³0}FWz›¸¶3÷ÿ&w§œ-wðpªc†Ý”Á æÕB0ÚuµìÌ`MÖÔP`9E`ó_!#¤/Žð`™©«ÕÙ2«•`A7y°ÌÖÕŠfVËŒ«hý¤+ë[ÙÑ6 ¶¿áúœéÁ²6³0Z}ÆëÙmâØþlÑзÆU™"'x° ‹ÁÁÕ2#­ˆvz°¦ËÊ(°œ"°‚ù¯Öô7¸2ƒ¬Z|VSÿ¬֜Јdù•—Îv“Õâ£Õ2”ÁšãÜÌD¯e°üXŽÁO¬ù™…ëÆ¤ÏÀ‚à›êjäwfÝ.J‡’]%°ÜÁ ¶"ªÅÛ¹¢¬H}¦ÀŠÿ ‹1»QH™ÉjíÊ8çKïO,³Z‹üV.[òÆÖáÚµÇEf[)ýJ>Ì,Dùh­U™[Ô"õ‹Ä]O®(:±Ž>ªEý@’èoÊ’e/œæÃêí¥ÀŠ·À æ¿ÊÚ?ØßäÒ Ö¢ŒÖñR‘q¨Dz&ÊR|•ÁŠVV+TËË~+#mk×å4TûRlø-ƒ5?³°¹lÑÌÂHú ÔÛÚZ¾O¤u†ýîíýëEåH"3X±ŠáÑÖ™*ƪ6ZÎjÙ™ÁBŒvtP`Å[`ó_%m (L\åÁ «ËvHGòåůhS§©Y@nò`Å"«̃…‚¨•>ËVéÛÆæêzßfsüæÁZ0³°®fAŸbºÏ˜{o~îÁ€Ùªp±ñØ&Qáp‘å&–Ñ€ÎJ©;=Xˆ·š›ý#°Z[[ŪU«Ä²eËÄòåËEjjª¸~ýzÜV ÿÕÖ’KÁgè¹<ƒ•8T!Rz‚ŒÍdµ¼”Á²#«µ(ƒ5'Ðü¶äM ¶±£ä€¯‡ËüšÁR±µ¬ÑtŸ¡²UjŒä»7ôneÃIÌ`¹ €©Ý,«‹>»R`%&&б±1¹Ô¢­­M<ûì³qXüWë÷ð¤ÿ*q¸R¤íì¿ÌjaÆWlkÛ87«…JëF³Z~®o¥¢Í¾š1Èù;ÊVné9õÕ¶¶™ôž¢d0…ž©80õ²>.IÀc¤ÛŸÏ:àÈ­¯Ÿ™ÏTAL©Œ•ÚN«ùw'n¯éß%ï¢ÑÑ«»éH·×*ÅoˆêÔ“ó .u‡ê§mj-ß4-ê·ÍøòøõÛ˜F¿µ©ÎÖöææmä£HljÙ#r ZņÖʸíÌïÕ§×Jq£2HxŒÇvýXJ\¿ß ÛÈj ·mŸÖÖÌg±ðÁéöÅÁAÓúÀõ¬»wïJÓûÊ•+ãšÁ*.^<Ì“ÚÕê)ßUÒ‰ÝQ/*ˆb(øç÷¬ßC}¬©õ]!Q†ûEIw­¡?ÊAÂ¥vg°ÆZZüëÁÂŒÂx ¬žžáEâj{á;!‡ÝæÁZ=ˆ©Íù1ó– î îNõµoüZûÉž´5ƒr]ÁÌÖÝôуå*.`~/:‘J–CâP_†íËÊLBW ¬ääd111!g~þùçÒƒÑ/ÕÒ2¶xqçÚѰ¢Å-¬Äá ã3mö– ƒ±iOØÞçëÚOžÎVewËÌe°l}GäÂ-\À—¯¢¤~ö`ââ|Ù[ÖÅêj¬žžYª3}ôQQXX(îܹ7US3¹xxððÁ°ÂŬJ±¶·Ðw£ÙM•rж²Z^Ï`¡;Î%Î)³6Ì`y‰‹MÇ6ŠŠ‘ÕÌ`Å9›×{ ÓVu¹¤„•Üã!°ôþ«y3"q¨Ú¾+[f ÚݱvÊÂ¥½ô+¹,‰ÜRµ—%ž÷e¹m±h¯Eí‰$1“o_­¬kyy¦gR`EÁ•S3jH¼8=ƒ…ƒN¿UY-¯0õTkîÜ džÆ*fm˜Áò ¨—ËÅ¢™ÁZÌÅHý[³X#ÝÝX±XüWi; '{°VŸ(s•ŸB-˳{]?=X Å9Á¹¡ïˆ,¿r+_=X‹¹Øß›lyÁè@q¦­+–Kï¿ÂðàêÁWg°V•‹Ôž|×ÞbQXd+>Ù*,ô"²v-Q¬ ¹p;Xǰ<Êë2ƒ˜‹±ª¶ ¬ñÆF ¬X ,½ÿjså9—¯1ˆœ½Ñ‰ËeyJ›äôúŸbTb× ú²­]©q›IHe³ÿ*½­Ë¸ÇÉi¬Ötòݨ,õPSû €©K¼ZnÉ`Å¢ (³6äÂ+\À—•<¬Xúцl[zº¬Œ+V+ÿjMƒaAã4VÜÓC ¥÷_e5õ»sÁã»}ÝQgﯔe¼Vê!ÖA †Y_Öú²bµ>a­=ëžno§ÀжÀ ä¿ZslŸë2XÑ^cÐMw£éíÅbûîýŽX–'Þ,p.œP”Yráe.ìòe1ƒš‹æî[Ö¹ýû)°¢-°ôþ«­ÅS¦ÅM¼=XNš1è4?…*`Š Ž_ïF£TWksu½mY-;2X»2í­b¦‚\‹ð±þè6C¾,f°Œsq®rsDë­æf ¬h ,­ÿ ‹;'U»`ÁJ‘ÂÎ9fY-¬Ë¯–š Hoƒá-_VÑ f¨ìŠ7Ú×Åd&!V„þ«œšQë^¨f°Ööñn4Y-T‹/ÚÔiºˆ©Ù –Óg²m ryìÈbË?Úp‚¸T²Õ²Àšª¬ô®Àjkk bÙ²eâá‡7n}ôQL–Þ•v°Ó²è‰•kMÿ.Çw^÷Sd¶•ʬVYê€m,¿Ì¤ïˆ\‹ð¾,z°ÌqIɆ+ÅÅÞX‰‰‰bllLÜ¿_Ü»wOìÝ»W¬X±"&Kë¿ÂðàêÁGg°’Ž—ònÔIq$_ä4T‡Íj…Ê`¹q& Û¹ v.±³}Ñ;Ì`™ã%fò¬—l8ÑßïŸ!Â¥K—ÆD`iýW›+Ï9Úwå”epAîÀ•ˆ»Êj¹}& ƒÁ°Y`öäÒ—aœ¬Û`Y`:|ØëìÙ³2«m¥÷_¥·uE6tÅ –\ÇMµ¥ü|gþeVK»à´Ê`ye& Û¹ Ñõe1ƒe>›w {­e5ÖÒâ}544$^z饀,+ZdddˆÙÙY1>>.IÀ£‘ížži)¬JJnˆªªˆÔÁ¦y¡?•LF·ÓÎí‹èýÁ·÷ˆôã¥óáWpòvV‰«ö7ZÛðjå—¶‰²²n±¡µÒ÷|Èá¾b_¿v[ÿèg>Ð.øûx°½sp›¨;›1/.àAR"ÃÛû'2 ¿þíš\ñª*q£¤D '<Ùž9t(¬~pµÀÚ³gÌFݾ};&³µþ«Íå㑛ϣ”ÁJvá28¼%ä‚\‹ÈøÈ9¶™Ã†&³yÓ,e°.VW{7ƒu£{,Ë4hýWYMýŽô]­(egÃ`0>ެ£ÛäСՅ£ýÕC‰–Ö'¼\RâM5::6ke·ÀÒû¯ÖÛç¸ –S—ÁáÝ(ù ä‚\ćdµ O¬•#‰Ì`‰þ¦,ÓëZ^^Ø™„®XK–, ÑXÚúW[‹§lDvÖÁÂ28©=î1Èú>äƒ\ r=>PÚÁ/Y-³5Áê’ĵ\óÄ#Ýݬän‡ÀÒú¯²ìγ-ƒU)Öör½9Þ“ rA.ÈGøðºWËÊŒÊÑšÓët{;–Kë¿J9Òâ(ß•Mí ƒÁˆÁÒ­ý-õc´I1-°ÎíßO©ÀÒú¯¶M‹„á*Çd°Ü° ïFɹ äÂÙ|lèÝ" N¤‰ xµ¬Ö»¸ËÜú„“55X‘ ,­ÿjCÝIÛ2O‘z°Ülj§Ÿ‚| rA.œÇGzÏN±c`½Ø=´Æ7,]mé¦ÖtYV¤Kë¿Jíl³¯¤B¬Ä¡rW›Úy7J>ȹ ÎæcýÑ­"ÿxºë²ZV3XXŸÐLɆ™ÂB ¬H–ò_í(¸2'lªãî¹rÛ28 ƒÁpo «¯–›³ZFcx¯¹õ ‡z{)°¬ ,­ÿÊÎáÁH2Xk=˜çÝ(ù ä‚\8Ÿxµr2EÅÈjÏe° }kL•líè À²*°´þ«uíoØ*°¬x°’ûKè Ÿ‚\ rA.âÊGZw®cg Zõ`©«ÚhX`½ÕÜLeU`)ÿÕŽ¼Û‡Íf°¼¼ ïFɹ äÂ|8mb$,D[gªa5ÑÐ@eU`)ÿÕÆª±¸ú®V–³a0 †sE^O®ØÖŸí~¯Öp‚¸Tb¬dÃTe%–¥õ_¥ì´afƒ,ijï)àÝïFɹ äÂ| Z<²ZñX1Ò ¢÷@¦!u¥¸˜ËŠÀš÷_å^Iõ¶ ,C¬áJ‘r´Ðóýäƒ\ rá=>2zvȺZ±\1R–*Ù0“g¬dC°EŸ)° ø¯6Wž‹Ê°Ÿ‘ –_–ÁáÝ(ù ä‚\x›MÇ6ŠâÁTWd°'ë6D´è3–ÿUz[W\|W^6µ3 ß5w d9ºÔâ@÷ZCëL[–¥õ_­éoˆy+éÄnÞ}ñn”A.ȹð,ëºsÅæ¾Q2˜âÈ â\åæ°k¼±‘ËŒÀRþ«ÍåãQËPó`%ÁÔžï«Î~ òA.ȹð/Ù½ö-Ëc‡KÅíë ¬‹ÕÕXf–ò_e5õGo0Pk¸R¬õ©w£äƒ\ rA>•zØÒ—QS;3X(Ù0]´ÍÒ¢ÏXaüWkŽí‹©ïj­OLí ƒÁ`)`Zç¦ýMY!Öµ¼< ,£Kù¯¶OE×Ä®Ë`­ØÅ»/ù ä‚\]©3LmÍ`ÍEý@RØõ ‡{z¼%°fggÅ#áéövï¬%K–̇ÝKù¯RŽ´Ä$ƒ•8„epòy÷ÅN’| rA.ÈG„¥ìÎ`!Z»RM/úìú!Âh,ø¯¶M‹„áè{®ä28ì( ƒÁ°Tê!VLC­Ox¡¶Ö_ ÂJ…rxq||\’€Gµ ÿUIÉ ‘wpb>ÄG çÙ½2V'Ö+’w_>ª;¿mgöûúøÉGðíŒÞ"ß·µ­‚|ÉvÁßùÐn#«U8´^ì=›>ŸÅÂ#† íØîiÉ7JJÄ?ªªä#„•Ú~¯¾~‘ž`+ˆÿ*ÚÃÈ\¥³R;ýäƒ\ rA>ly† ñf¢z(Q¼S¸=`k¦°C„áüW; ®ˆÄ¡ê¨‰«Õ'ÊDJO=ôSrA.Èùˆé=;£2lxbßzË>S`ð_m¨;Éõ ƒÁpyìXo«ÀÚwlMÐ’ §¦À &°Tý«uíoDA\UŠä¾Þqðî‹| rA.ÈG ¹Øxl“­ K+Ù0¦›Iè‰2 áÊ5Xð_íÈ›±}x~«µ½E3§€| rA.ÈG¸Xt›m¾¬ƒ‡—l8ÿ¥Ñ•܃ø¯6VEÁo•Ï;Þ}‘rA.Èùˆ#vú²¦Š—l˜Ô-úL¥ó_¥ì¤ßŠÁ`0 ú²‚ÆÑæŒÅ‹>—–R`Xð_axpõ`MTüV¼ã äƒ\ rA>œÁE¤¾¬ºãIb&Çu‹> P`ò_m®<¹ßj¨\¤-ä˜9ýäƒ\ rA>ÌEæÑ¢t(Ù²È:Y·aQk¤«‹+ÿ*½­+"q•tb·HíÉçï¾È¹ ä‚|¸€‹´ž\¹x´ÕÜrÑg ,ÿjMC~«]lØ ƒÁ`øÈ—5YºeÀ:·?–Þµ¹|Üš¸®ÉÇŠyÇÁ»/òA.ȹ .æÂŠ/«³--èLB ¬/ýWYMýüV¨oUh¹pÌœ\rA.Èùpf}Y{Å•‚¯Ìîo—•Q`éýWkŽí3ç·:¾{îdäóŽƒw_äƒ\ rA><Ä…Y_ÖPCö™„X_ ¬üªóô[1 ƒÁ0íËjè[¸>á`o/–ò_­ßw"ê~+Þq òA.ȹ îâ¨/ëÍêœy5úåLBß ,ø¯Rº›Ã/y3ˆúVö6`Ž™“ òA.ȹ ÎæÂˆ/«µ+uѢϾX¯5œ Ãá׌ÔoÅ;rA>ȹ äÃ\¤uçŠÂë •l8_WG…ØÒt‚~+ƒÁ`0ac[¶¨I (°º[¬O8UQAÿUêáƒK0 WˆµQN¹òŽƒ\rA.ÈùpÁ|Y5ƒ«åú„—‹‹)°<-‡ªø­ÊDJOô+ÇÌÉù ä‚\÷q‘yt»Ø5´v‘ÈÞû`}ÂÁ¾> ¬’CýQ©oÅ;rA>ȹ äÃÛ\¬ëÎùÇÓ¬ý½É²d}öµÀÊê:¤W•bM?ýV ƒÁ`0ŒÇ澜C†oUngZ[Ý)°îß¿/Š‹‹ÅÆ¥xBܾ}Û”Àê9:$VÖ|é·ªŒhÉÞq òA.ȹ þåbýÑm¢l8I ¬ö7Ö‰ñ†w ¬†¹?pàÀüvSS“(,,4%°êzz¾ò[Å©arÌœ\rA.Èùðé=;EÉ`ЍNgëÊÝ)°^~ùeqóæÍùí>úH<ýôÓ¦Ööcm1õ[ñŽƒ\rA.Èùð>yÇ3D÷ÁîXË—/_4d¨N +Z¬X±Bü?Ï?)þÏþ¿bÙÓŠåsÿÉGns›ÛÜæ6·¹ÍíH¶öQ‘à>µdÉCÏAA¸®Ê`AAP`Ä+¯¼"nݺ5¿ Ö“O>ɳCA–U466Ê™ƒ øAAá÷k½Y ƒÁ`0Ñ× ,³u°‚ ‚ ˜Á"‚ ‚ À"‚ ‚ (°‚ ‚ (°‚ ‚ (°‚ ‚  ,‚ gãÂ… Öb%¶ ,B‡»wï’„/± ¢®®N|üñǾçÅs«ªªDIIÉ‚¢º~ÄÔÔ”ä‚xpýóŸÿ,.]ºD.æ¸ÐÖC$dÁí^xýç\ÿY]]-JKK]ÕR`Ùˆ§Ÿ~Z^<Ø1<¸hìÞ½[Ö7{öÙge­3¿ó±gÏQQQ!ùøâ‹/|ËGww·xôÑGEyy9'W|ð¹ ®ÀGJJŠx÷Ýw}ÿ;ÀRýç½{÷(°üF]ºt©xüñÇ}-²>ûì3ñ§?ýIþ(rrrÄÉ“'}ÉÇ;w$“““óÏeffúº ˆŽŽ±råJߊ¬@âjffFœ>}Ú5;Ìî¡C‡äÿ1022"†††|É…V\ííír4À@[ÀïD{=AŸá–,–MÀEÄØØ˜l~½€ÖÖÖÊ4®===¢¿¿Ÿ|| ,…¶‚G?BlwuuI1‘µmÛ6¹©_ðᇊGyDŒŽŽÎßœ­ZµJ¼øâ‹"55U<÷Üs¾kYYYò˜á7zæ™gDrr²HKKÏ?ÿ¼¯¸@ÛЊ+Ãc+V¬ðeÿ‰ßÚ†FBÐ.¶oßîŠþ“Ë&hˆJdMOOûއ½{÷.¸ÛP‚B]LüÎÄ.¸C‡øzùå—}ÇÉ™3gæ3Wø­,Y²Df9ýÖ. ¨fggÅSO=%×cÕþ ˆù ðä;vL‡577Ï?àÀÓë¿yéZ¢€?šÝq …à†È<{ö¬ì/ÑW º¢ÿ¤ÀŠ´" &o¨nܱû r¨ ð;¸xjýÕW_õÝ0:Kd­p—ŽßÄ&2YðWø­-ÀV€! -p—þÄOøŠ‹7nH+ÚÈfùàúvâà¸!²qÓÑÛÛ»àoðr:yrV D2~CZ[[çÅîÚýÌ…þ¢â· )A‰™Qømà7¨áBÜ¥ú —/_^ôÜõë×åïÄoèìì”ã2UÀÿ_zé%ß÷0¹#»çgà†™+dôàyv2(°¢ ¿‹+žŠ«Åâ mþ4¿Þ‘+q¥À2'B¼ÿþûR|úÕ³ˆáA ™ŽK“;&DBrá×Ì?011!ûLˆn®'åX>ERR…x๡¸úJX!Ýá½ @™ #£éwA¬LÌø`ø˜x”½ñ[†W Â🞞 ,‹0R×ÉO6Cñâ~WáÚ2z~© æçúgf¹@ÆÆO|±m˜ãÂ/Ù+/µ ,“ÀL˜.á €ŠÖúȇ¿ù ä‚\rA.(°,3µ3 å¸8ù ä‚\ òA.È–E ž“RÕh˜…1qxüX£„| rA.ȹ X¶4,e¡mf¾àyòá_>ȹ äƒ\ ,“P¦;œ|ÌþÒÖïêÆÒŸ|ò ùð!ä‚\ òA.È–I`( ™a,k â„céÔâÀ´j,s•½U!Mòá>ȹ äƒ\ , @å\¨g,¾  0"6Ö–CÙ~Äää$ùðä‚\ òA.È– È1amY~Õ´+{“ÿñA.ȹ ä‚\P`YÖÚGK–,·nÝZôw¬jî'.òÁ¶A.Èû ¶ rARSSå´P4,ƎFuÂüÆ…ê0ÉÛ¹ ì3Ø6È–%LMMÉ5Ÿôþï§õã´\¶ rA.Øg°m ¬°Ð–áÇÊíZ52ý*…‰†ðÌ3ÏÈ ÝÝÝžç#~áƒmƒ\ ölä‚Ë0Sõ6Ô‰GQ³ÄÄDQ[[+îܹ#fffÞ…yµ6‰–p\x¶ rA.Øg°m ¬À¥K—ý cW­Z%êêê䢓Úýª!ø…P\x•¶ rA.Øg°m ,‹ à‘Gííí!_744$ w¥¥¥ žGý¿ñŒ ¯ñÁ¶A.Èû ¶ rAe± ¸ªÅBeÃp¨ËáÕE7ÍòA.üÁ¹ ì3Ø6È–a b,€J]b­## ë#º ó#äÂû| rÁ>ƒmƒ\P`™†^-«†€®!rá>ȹ`ŸÁ¶A.(°"†Ñ†@>ȹ ä‚| rAņ@>ȹ äƒ\ ¬ø7„ÖÖVA>ȹ äƒ\ ,‚ ‚  ,‚ ‚  ,‚ ‚ ‚‹ ‚ ‚‹ ‚ ‚‹ ‚ ‚ À"‚ ‚ À"‚ ‚ À"‚ ‚ (°‚ ‚ (°‚ ‚ (°‚ ‚ (°‚ ‚  ,‚ ‚  ,‚ ‚  ,‚ ˆ¨¡··W$&&ŠåË—‹eË–‰GyD$''‹öövß±dÉAXAø7n”¨¶¶Vܽ{WÜ¿_tww›LXAP`AÌ¡££CŠ¢;w.ú[RR’ ˆ®„„±téR™áÊÎÎ7oÞ\ ®´¡088(^|ñEù¾‡~XäææŠÏ>ûlþïgÏž+W®”GöìùçŸ=ôЂÏ8sæŒ| 2lüÿôéÓ‹ÄÝ¥K—Ä+¯¼"? ÿß²e‹hll\p\ÕÕÕòù™™6‚ À"‚°/¿ü²&ÄÆ­[·lC\ÍÎÎÊ Wkk«|žÓ‹- Œð\zzº|_[[›Ü.((Ÿ˜˜ÛÏ<óÌü>à{µŸ566&ÿŸ––&…ÿÇsgÚïV qçÎ)´JÐ}òÉ'òoˆØ‚ (°‚ lć•a=ˆˆ¼?”ÀZµj•|îúõëóïÃ62YJ´i…R ÏRŸq§ðî»ïÊçÉÒ¾~jjjpÊÉÉ‘Ï#ûôôôÈííÛ·óäAD|²A+©©©âñÇ—ÃtzAH`aÈ/Ðð¡zú»>›¤}M°}ÄsØ`ß ¨ìWJJŠÜưf AGA„mx饗e‡´¢JAe‚öîÝ+._¾,îÝ»gH`©!»`Ãq¡Ä“ xòÉ'å~ܾ}[~ÖŸþô'žx‚ À"‚ˆ8°À¥Eaa¡,ÕLäèEM ×ÀpŽçൠ„+VÈ¿øá‡A?[ bXPCކ³#•‡ %%%<ñAE](á!òÅ_Èç0CO+Zž{î¹B©¿¿‘¨QÙ°éééùçFGGåsø›Q“““Rx]]]òï™™™Ò€Ž,føi?Ãyê5Ȫáuø “{ ܸqcÁÐ$fAEutvvJùòVa–fþA˜á‘¤Œí‹D Ä×ÓO?½`è€ Ÿ ¿Þ!:ÌBT8tèxöÙgåçÂ+õÁÈÏÐèµ¥ø?>W!\ .QÅߟzê)žl‚ À"‚ð”ÀBí,»ÐÔÔ$?sÏž=$˜ (°‚ ¼ ?ŽŒŒH#<†ÿ²²²¤Âò=v2½‹  ,‚ ÏÅGá©RUÚ1œ—]ÀgaxEU ‚ À"‚ ‚ (°‚ ‚ (°‚ ‚ (°‚ ‚  ,‚ ‚  ,‚ ‚  ,‚ ‚ ‚‹ ‚ ‚‹ œüúÚ× OAXElßAP`/@ÁöMXÁ A°}AðDlßAE/@ÁöMXḠPvvvØðxÌÞ>f ,‚ À"G,ñÄA#Ð…÷“O>¹¹¹âá‡Ë–-+W®¦÷cÉ’%¶×ìì¬xä‘G ‰‡ìŠcnkk r_°O7n}ôQècñÏ ÇÜÚÚ*V­Z%÷eùòå"55U\¿~‹ (°Â+%%E444ˆ{÷î‰û÷ï‹ .Èçâ \ÄUDC`9í˜ÅØØ˜ÜìÓÞ½{ÅŠ+lXN>fDæ³Ï>KEXá µtéRy †ºº:‘““#6oÞ,ßûöíyôÙgŸ‰§žzjQf#Ø{FFFævã ™µÀßíÈ–XXN>fí>Ú)°ÜzÌXAE®X¦©®®‹.ÀÈxÔÖÖÎoŠíÛ·Ï_hÇÇlj¡PïyôÑGå0¾CDñXN>fàìÙ³2Ãc§Àrò1#«vèÐ!¹XAEžXÈ:àÂø‡?üAfà…ùàƒäßž|òIqçÎù×â‚©|Qzñ£¶C½çÏþ³8}ú´ác‹–Àrò1 ‰—^zÉv–SY cŸ?þøc ,‚ À"o,-u€ÿxíÅO¡.¼¡Þ399)’““Åã?.ºººâ&°œzÌ{öìéééóCmv ,'Ÿç»wïÊLŒ÷XAEžX ðΨLD0ßN° o¨÷(|øá‡r昖SŽÃp0}A¤ËiçY¿?XAEŽXfë#a&™òå à­A€‰¹©©Izd”NÐzs]xC½ž¦ñLÐv ,·óèèhج•׎.µ?Ÿþ¹ô`©ý¡À" ,‚p”À²‚öövi.†/Ô`Ò^ìq!†ßõÕWçk»ð†zÏ–-[äw ûqòäÉÂ*ÐГ]pÚ1‡nóê1÷ôôH#?Þ S|aaáOAP`„«A°}A¼lßAP`/@ÁöMXÁ A°}AðDlßAP`„­ ÃËAAA„#ðÿ;Ûv•iâIEND®B`‚BarChartDemo1.svg000066400000000000000000000457201463604235500340620ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/category/doc-files Antidepressant Medication UsagePercentage of adults aged 18 and over who used antidepressant medication over past 30 days, by age and sex: United States, 2015-2018Source: https://www.cdc.gov/nchs/products/databriefs/db377.htmMalesFemales18 to 3940 - 5960 and overAge Category024681012141618202224 BoxAndWhiskerRendererSample.png000066400000000000000000000362161463604235500370340ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/category/doc-files‰PNG  IHDRXr5˜îrþfee©›nºI=òÈ#jÿþýêóÏ?÷ `9ÕÙÒÒRµ{÷nuåÊ•”©›?üáf`¡ä,kimmåXE”×®]ôù_|1èý‡z(èýo}ë[Aï766½/ë°ìõË_þRedd8žÃwÜq‡'÷Ç ?íŠÀe[[›¯ë¤@¤µžXÀBIX_ûÚ×8V6âÜÌŸì±ÇïýûßÿÖë:ÿùÏ>#ÙCó{ííí ƒ/°_ÿú×aŸÇ©XFñ+d…ã3BòTã.ÁÿÈ‘#A¯Kw Ç*²F|pp0$¤Jw¡]`xõÕWŸ‘Œ‹ù½Ë¢¿ýíoã@µ¨¨HýéOÒ籫ØÌ™3} X†Þÿ}µ|ùò ÷¦M›8w,„,äÆ}¢ Öo¼¡séŠ0ƻȲ¼nÖêÕ«ƒÖuçwêà'eΜ9AïýèG?rü½ÕÕÕêûßÿ~`›\e² ªíŸëTPP K}}½ívž{î9=æI¶QXX¨Ç´DÓˆK·Ÿù;Æ8 Ÿÿüç¶Áø=—/_Ù}Í>…úí²íÛ·ëõçååéý•qE }Öꋜ¿f…ëu;rþʺŒº!ãÝ~ó›ß.̤«VÚ yO>#pm»`! ù°¤Ñ4¿ÞÒÒô½­[·:6pò¾! ^FÁ(¿ýíoÇeÉä3]iOÔòúë¯;~^‚›õ5kð±vÍÅÒˆoܸ1è;FùÁ~4VÆøÿý÷߯ߗî@ó÷d=±ì“ÝoïêêÒÙh»Ô¬Ðyß}÷àæÀŽc¢$˜K†/Ôú­€J‹/Žª;Ìcé¹g·O“XržY·m(oìÞ³ûÎ/~ñ °vïÉ Ñ´~Ȱ" XŽE²-N·\ùËÕ«ü5¿þæ›o¾c†… Žk1Fe„$ÉöäjzË–-ã2‘îŸP¿S“@Ÿd@¢iÄ¥Ëψä·Ë•»ñšdŒÿKàHùéO²ë0Ò} €¬ÇÿèÑ£úuéfZ¶l™#`uvvŽƒã?þXöÏþó¸ )ûÜÜÜ2˜F ³’y±ËºqÞ[¡Ûç^,p0Ñwä÷™ß—ã7‘Œõšè8FÓnXÀB¾,£ëî_ÿú—þŽõ–èÞÞ^ýú_þò— ×%ínfHºY¢‘4ÐNcŬۑßjÿdþŽõξ'NÄÔˆ_¿~=([ Çàí·ß*+¼I—qDæÏÈz¢Ý§P¿Ý yRÄ?«_¡¾/]K¡Ž“ æ·ƒ«WÆ´vë—즵ÑNÖ}ˆö¼çÆzî…ڧɺ+Ôî·Eêu;„[/DœÞ“®ÔXÚ XÈ€eH²(Öî-¹b™»´Œ±%v´|ά¿ÿýïê¶Ûn·Ípº ŒÈ8i¬¥[ÇšÁpGfÄê;¡ö-–F\ºýÌûºk×®À²tq‰ÌsfÉq6ƒƒÑmí>…úŽŒ•±Ë6ÈX##ø…â2†Ë¬‰ºí€$ÚcëV+œcèÆ¹çDšÁ2êb¤Þ„ÚŽÓ± õ^´í€…,ä+À2 ËüžÀÑDYƒ‰2f¸5Ö%”d ± Àޤ;)ÒnëØ7q븫d…ÖÿøÇ׬ÿÏD»O¡Þ“ŒƒÓXœÃ‡‡X2ðÛ¬‰æ£rsÚk7g¸¥#ýnž{ñ,éf3¿¿téÒ¨¼‰vÿÂɘFÒnXÀB¾,é–²kø&#ƒ%ïM$3˜Hyúé§ÇýÆXËzË¿¸uì‰]·ŠuÀ¿4¸X"ÉTÉ0»l–Sž5P›—[Ï»»úÜ r·ŸulŸÝö$Cjd £9†nž{ñ,ë€zyj@4Þ¸ Xd°€…¬/aÈœU‘"ÝX"ëX ¹å[$*qK!, æñ>¡ê ØMÀ2wçI‘ß±6âĬ¿Ý:¶êÂ… !»j¬AÐ-ÀÍ²Ž…qê³ð–1YÆ=«ÏrǨU²ß2Î,Öck=çŒy°d¾H~“ÜÅ&Sƒ„s¬B½îæ¹g•SÖ4Òz+ç«õøË|jÆØ¯H½q°¢m7,`!_–S‘9nDÖîãzNwYg5—®Hï"´Ž‘`b7ag,0bz@Æ#ɶdšbiÄ¥‹Æú}ɶ˜e@€ &‘#˜ƒÛ­™6óØ/»y£¬~H÷¯HºÓ¬JyÔ’€¢KyÆ¢q»¿Òn޲h»$C½îæ¹g•Õ÷wÞyÇÕz{üøñÀ÷"õÆMÀЦÝpã `!O–\ùšåÆ1+yK¼!À¢žX–'»­€ÕÔԤΜ9C!Àò‰'nøI=ÒÀãÂ&,Ú. Àò`UUUiÈ*++Ó݃LCCCÀb\€EÛ•´C,+ဵtéRÕÕÕ¥FGGÕÈȈ:xð Ú¶m›-XŬ††=¶«§§G ù›Èe9Cã¹=©qÝžªôÔñv°Üø}²ÊxñÄ+~„»l7Öç&`…½=—ËKþÈAˆçöŒzâÆúÜô$Uý žø°ÌÐ*))!ƒÅ,2Xd°h»RÖ2Xd°b¬5kÖ¨ ×–-[`Q!,—»?, À°ˆ')X­­­ª¹¹90»[½ð Àr1xX€`OR`šët /½ô’ä.]ƒ[ÃÃÖWçx°, Àbê Àb&w‡°, À À°, À°æx`X€`XÁOðÀ°,) À¢žÌñÀ°, À°æs<°, À°,‹`Ž'x`X€`XÔ O, À°,‹`N0Ç À°, À°æx‚'q,&®°, À°æx`M`O,‹ `Xs<°, À°,‹`Ž'x`X€`QOæx`O,‹ `Ì æx`O,‹ `Xs<Á À°, À¢žXx`X€`Xs‚yÜæ@ À°€`Ì]¬'€`X À°æYEâIêV__Ÿ*//ùþñãÇUQQ€E…°,‚9€…'Iì €G 8ÅN===jûöíÀ°,‚9žXV4 eÕ¥K—T]]ºqã€E…°,eÊ”)®ê ÁÀÂâI ÖÐЪ­­Uƒƒƒ! À¢BÁ"ƒE0'˜ã €`EX›7oV/^tÌp`e³ôØ.éb”$¹,g [ës³B¸µR!¼t¼Ý,7~Ÿ¬ÒÍýu°í—±l–kÇÇÅz? Oâu¼Ý æ‘lÏ-OÂÞžKžÄ«>¸å ñĽeß–ylÖDã´È`qÅ‘è –[wþD|åI‹l Ù¬X2X‚9žà €å!Àºqã†jllÔÓ(˜©¶¶Vµ··ÇXf­¬ÿ”ÌÌ©Ž5/oŽ*(¸ Àòù¬ô¬Ð-w¦Ò,‚9žà €•´€õüóÏÛN³pîÜ9õðÃGý¿t7­Ñ-8{öm»l,+eKàê•W^°,O5R/.Z¤nÉ ½Èz´°OÈ`¹̳§;ß!—ž®ï.$˜3u€`…=ÈÝ <€•ÈFª0?ß6{e.7žø;pÈ+§sàöÛ¿ïj0¿ç¿ïQ™SCwæÌÌQ ›¦Œ'ñ¾ÓÀò&ôºUR°$sµ}ûv544`Xži¤Âm´ñÄßCÆóH÷“í€óÌ×ï"”‡oçÞ‘k Y¹jîê¹ãÂM0'ƒ…'Þò„‰F,‚¹ƒ'³óò&„«‚ÌLõÀ°æ{2TQ¡æä婜ã°ò32Ô«‹ã ÀJOæÿd¾Êº9Kßd¸Ñå–,=ñkù‡åxB=¡‹Ð+€•›óˆ©ËËÕÜü|uçhÞgæäèîC¹ƒpxåJ‹À`ùÜ“~ÿ€Jϲ¿ÐJKOÓcæRyv}ê‰Gk``@ÕÕÕ©ââb2Xd°’"pH&Kº å®BiPç¨æ… má O–ÿ<™þÝ鎹rg§tâ õ$¡€%;( µtéÒ˜«¯¯O•——›Okß¾}jÏž=[2îL°æxB0'pàÉDždäe8VZFš›…'Ô“„–@Q{{»†¡ÒÒR522¢_?yò¤Ú¿ÄcºÌ:|ø°jkk ,·¶¶êgXs<°xÂìúÔ_–t \‰***Ô|ô^¤ƒçͪ®®Ö]†úûûUUU€E0Ç‹À'ž‹'–k€%㯤{O´mÛ6uèÐ!ýÿÞÞ^Ým `IFÌÚeh}Í+£˜ÕÐР[OO>@òw²–Ý æánÏ­ îþºÌãáÇdVXÇ'ÄœKóç¯UYY·~9™åT5{ö#úu§F*œýu°&Û·êIÄÇÇÅz?Ìõ$í—[žD²=·< {{–w=qWÈAîÆ” v³ë'S<‰ä÷†ò¤¢bH-XðŒÊÏ/übžÀ‚»ÔÂ…Í1Ç·ëI<Ú¯„Ö[o½¥!K´{÷î î¾õë×ÇXvc¸œÆu‘Á"[’HOq¥L‹+s<‰nv}Èžž3²2ò3Tᣅ)?»~yù‡c`5OååÍÖñʱÉΞ¡rsïP……UjåÊa2Xñœ¦áúõ벤kpÍš5êÂ… qÉ`X–W=™;÷ÿŒ5LéϽ+ Oà‰@ÖüµóUö´/gןš©nûÞmjñ«Ì®/™++yt”]»%O@X¼øwÖd– lÿôÓO]4+`ÕÖÖªÁÁÁ 1X«V­°¬¤ñ$Te”ôô,<°ðO<剹[0T™6m€5Y€%0$™ª;wªÎÎÎÀ@w7ëÈ‘#úÎAó]„€•4ž„u§žÌñO<ä‰d¯ŒnÁP%##Àš,À°2O.úÐC©æææ »cyôó`X~ðd¢F*--Oæx‚'žò$ìi,¬ÉƒõÙgŸ©7ÞxCÕ××M4ºvíZ=•¹{/UgrsÉußôé*/㋉í 23Õ#³g«þ+¬¬›o^àØ@åå݉'s<ÁOy"w æäÌœ ƒU`Åk»ÀÔ+¯¼¢jjj é4 ~¬ê»ïV¹ãg N+3rrÔ'Ë—X>¬Å‹;TZZ¨Y£ÓÔ¢E/à ÁOðdBOä¢üÿŽÅ”[³²¾xœOzºúæÍ7«ƒ÷Ýçº'‹½¨rs¿âXéjöìX‰xØóÙ³gÕã?žÒÏ"|qÑ"uÛD…ÜòçäÛB–\Ì®˜Ð»>,Á˜»U¬ä¬xžn{"’›³œŸiYÀj€…'Ô Àr½¡r³‘°¼ Xžcÿâu¼£ñdö—ÝONEŒÀ`X€`X€Å¤–s À°, À°ã‰Ü5˜îü×i<–À°, À°,kòòJà°, À°, Àrðä'óçÛf± 23õŸ,_Nà°, À°, ÀŠÔ“7—,Qß›1CC•ÀÕ-YYê±yóÔ¥$pX€`X€`8æõÀ°,Ÿ°,€`QO,‹ ,‹ÀA0°¨'€`X€`XõÀ°, À"pXõÀ°, À"pÌ,ê €`X€Eà°,‹z`X€`8,<¡žX€`X À¢žX€`X‚9€E=°, À°,G’N `QO¬8èâÅ‹jëÖ­ª´´T À°,,‹z`Å +W®¨'žxBõöö’Á°,‹À`XÔË 555©3gÎÐE`XÀ°¨'–[ªªªÒUVV¦»˜†††lÁÊ(f544¨¾¾>ÕÓÓ£ü¬e7ƒ¹[¿OVéæþºXñðc2+¬ããb#åÆñc¯ãβ›#Üí¹UÂÝž[#^þ¸å‰›¿OVëæþ¹åI2Å“H~¯›mWØÛs±žÄ£ýò`-]ºTuuu©ÑÑQ522¢<¨¶mÛF‹ ,2XdKðOÈ`‘ÁаÌÐ*))°, À"˜ã žXV´Z³fzmÙ²e€`Xs<Á ÀŠV­­­ª¹¹90»[½ð €`Xs<Á ÀŠE/½ô’ä.]ƒ[ÃÃÀ`Xs<Á Àb&w À°æs<°, À°,‚9ÁO¬I,· €`X€E0'˜ã €•¤m€`X€•à`žlWæ€EÛ`y°¼Ö°X€ÿz‚'€`Xq.rƵ!°,‚9€`X´]€`%sV‘FŠ`Ž'€`X–ï+ÞžÐHÌñ„qq€`X€E0°ðOh», À°sm¤ð„`Ž'x`á €`Q!ð„`N0ÇÚ. À°æ4TxB0Ç<°,‹``á ÁOð„v O,‹ 's<Á‹¶ À°¨xB0'˜ã žX€E0°ð„`Ž'x`X€E…À‚9žà €…'€E…À‚9ÁOðÀ°,‚9€…'s<Á À°æ4Rxâ`îµÇ-á €`á €`Q!ðOðÀ°, À"pÐPá žXx`Xƒ`Ž'x`XÔ<ñ3`utt¨ºº:URR¢ÊÊÊÔž={T?€E…ð¤'1ƨ ‚'Ô‹¶ À°&Mõõõª««KŽŽª‘‘uèÐ!USS`Q!’Ö“¾¾><¡žXx`XÞSqq1€E…HZOzzzð„z`á €`yKÝÝÝ:«`Q!’¹¡Âê €…'€å>}Z­_¿Þv –y~³t—Œ‘5¿‰\–34žÛ“ ×í©JOïp–å Ås{W®\‰ëöÄ“dòC–à¯í#nÛS•ž:Þn–›¿OVÏãaÔ“di¿Œv‹x¿eßVKK‹ÎF q!WŒÁÂê ,2Xd°È`Å*¡GèÎ4 TÆ`á õÀ¢žX– êììœ0k`Q!ƒ…'Ô“Ø‹ÇXx’b€UTTd[,*,<¡žà €…'3¹ÓHÑP1‹z‚' ð¤ÒÅ€`XT2XxB=ÁŸ À°4TŒÁ¢žà €`Xƒ õO,ü žX‚1XxB=Á‹z`ù°bGÆ\¨d°ð„À'€€`X^Cà` žPOðÀ¢žX‚ žXx`X€`8ƒE0Ç+©ýpq„ €`X©8¼8.Ž ÁO¬dÌ`¥zÛ`X Á<õij†XÔÚ. À°<_È`Ì èxB À°,‹1Xs žX)ßvX)X J$ƒ`Ìñ?h»,‹ñ>xÂx²%ÔêIÇÅ¥º'¾¬êêj500XîïïWUUU,<¡à žPðÀŠV¥¥¥ãº ­¯Xñ(¿ ¨¦P( %嫨¨(¬×ŒñYVÀzê©§ÔÏ~ö3]¶lÙø?Ë,³Ì2Ë,³Ìr¸Ëuuu©ÁB!„J´<Xµµµjpp0h ÖªU«p!„BV´:r䈾sÐü¿±±çB!`E«HçÁ²Ê<6‹B¡P( %Úâ+ÀB!„"ƒ…B!`!„B! !„BÀB!„°B!„€•z÷ÝwÕÚµkUqq±ª¬¬Tû÷ïŸð;vòŠbFO&OòäûòòrêH‚ÕÑÑ¡±QRR¢ÊÊÊt]‘ “ñ$qjooW›6mҞȓA¶mÛ¦.]º„'ÑñãÇ“æ·XÑ| ~øaýDr‘Tè^x!©+ÅáÇU[[[`Y&‚mjj“K~ŸQ¨#‰U}}½êêêÒ####êСCª¦¦O<â‰àÇO< Ù§íÛ·X(2íܹS;v,äûr"W#êøÃ¸@i>á<¨vïÞ­ž}öÙqY#Y‡\IC²téRýº4îÍÍÍú*ZŠdžä5c;7nÜP«W¯ÖËrugH P¹¬®®Ve¹2¯ªªÂ“z’ŒW¬©â‡!Ù<Á< –€¢¼/ß°PD’îšÏ>ûlÂϽóÎ;úê$T”“þÀåS§N©½{÷êÿKêûèÑ£úD]¸p!ð}I!¿òÊ+«¶×^{M½ôÒKmWC"YwwwàŠB*¡’ýaÜ~ô$™Ëï~ˆä;¬ðÄžÈ:Í €'ñ÷DÀÎüìa E¤HNãJÁî{ò°ëëׯA1Öæ±Ç Të÷yä‘ÀU†hxx8Pù¬ÛÊ$W%"ùj|‚Ý>%SÅð£'É X~÷ãôéÓjýúõI5ËÏžÙÉÜ|úé§x’@O6oÞ¬.^¼˜´m€å«ŽÏ?ÿÜö=9édð«¤v%d>¹¬'š5ÕkNùš+“õûv'¬‘·{oãÆ?”üÁò›'ÉžÁò«---jÇŽIwˆßëˆÀdkŒïáIbpãŠD–;ZÌ'—œèæ´ªœ¤r·žq‚ËíøF¿¹ÜÁ'ß—õÈë2ÒÜo.ß3úÍ¥ÝÜonÕùóçõ¶?úè£ûtäȽNCòÿÆÆFP###a–Ñeª;Î žÜ,ѪU«ôïÒëzøá‡1! !„&OmmmAc¢ÌjjjÒS5„‚+ÔØ}FœËk2ÖÊN555úý«W¯†\·ÑE(Ý‚†¤ËÑ®‹ÐNrw¤1†Kþ677cû{Ÿ,„,„<Xª²2d± ¼×®]SÏ?ÿ¼*++S%%%jãÆêÈ‘#ÿŽ¢¢"WögttTíÛ·OíÙ³' CCCްá°ËI±Ï†úúúTyyyX€Uéð/ö¹££CÕÕÕéß"¿Iüîïï°°ò`mݺU>|XŒŒh¸9{ö¬~-Q’ßÒÖÖXnmmUMMM®–×öÙ£L`ymŸëëëUWW—þ-ò›:¤jjj,„,„üXÅÅÅ:È…ÒÁƒÕîݻճÏ>”M¸qã†Z½zõ¸ÌF¨ïœ9sfìgTꬅ¼o§êêj500X–¬FUU•«€åµ}Ž4C `yyŸÍ¿ÀBÀBÈ€µiÓ&õòË/«ÞÞÞqX2,Ÿ:uJíÝ»7h{zzÆÓw–/_®.]º¤·ÓÞÞn»?¥¥¥AËòYëk±–×ö9€åå}uww묀…€…/K²—-[¦3Û¶mS—/_Öï­ZµJ]¿~=vŒ1BV0–¾óÈ#¨wß}7bÀp‚ŽhËkûÀòò>Ÿ>}Z­_¿ž1XXù°Ì’¬ƒŒ…‘`gS»âx¾sîÜ9µeËUQQ¡Ž;–° –×ö9€åÕ}niiQ;vìy#€…€…PÒ–!;cd"BÛ x¾cèêÕ«!¡©¶¶V –%«!Ù’É,/ìs¼Ë+û,]2Ð=Üó!`!”0ÀŠt~$¹“Ì—#EÆÖHFA$ƒ˜å.>¹ËK$Ó˜ÇæØ^§ïȘ0-¯É h;ÉÔò}CòÿÆÆFGØHö}ް’}Ÿ;;;§ß°°ò `E££GêÐ2.G22‘9ðI – ’¼¿aÃݽäx¾óÜsÏémHöãwÞ±ý=‘΃å‡}6Öe×Ýæ×}vêb°°JzÀBˆó! !DBœß! !BœßX€âüFÀBˆ„ç7BÀBÈÕD¡ø¹ „,„B!Oèÿõ»U=]õIEND®B`‚CategoryStepRendererSample.png000066400000000000000000000267511463604235500367400ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/category/doc-files‰PNG  IHDRXr5˜-°IDATxÚí݈T×ý>ð@ùPJ)¥ÿ”RJ)…PšRJ)”RJYde­Ab Z´ŠVkµÚ]D”¨‰h"IÑD’FBD«ˆVÑ*´¦¦›E«5˜D«h´¬h÷|ó¾ùÞíìììîì8»;;óz–7»wfç×3çÞóœç<÷ÜT €À °, °êò ýÀ= ,€ºÀ™3gÒüùóÓƒ>˜>ýéO§ÿû¿ÿKŸûÜçÒc=–6lØîܹƒ¤!DGGGúÅ/~‘¾þõ¯÷à¿©©)-X° :t¨®Û@`Ôž}öÙô©O}ªW'WX_úÒ—8C„'Ÿ|²_îû⬖9l›ª¥Ï¢­X÷矾¬Îý~;Vi„“S)ÿµÊi%mŠÀ °êÿüç?³©›ÂÎ$¦¤þö·¿¥ÿþ÷¿éßÿþwÖY~ñ‹_$°†ßøÆ7zðòûßÿ>uvvf÷ÅÚ_þò—´råÊôÕ¯~uTpZi›"°,€ºÁòåË{t$ßùÎwÒÝ»w{ýßåË—Ó÷¾÷½îí9sæ¤ÿøÇYž&r5Ñ¡~å+_I“'OÎ:Ò:¬R×É“'³ R‰xÎÏ|æ3é[ßúVö£S.D¼ÇñšùÿF'÷×1îÝ»7M˜0¡û}ÇïØŽÛzÏ!ž~úéôµ¯}-›úúþ÷¿ßãþµk×v?öøCûâûBñ4Zp]‰¸_N‹Ÿã_ÿúW–Ÿ Žâû3fL–©ª6Uüú×®]K¿úÕ¯Òg?ûÙ¬ÚÚÚz=Çý¶Ãâï´\^€Àè?üá{t ;vì¸ïÎ=jÏž=ƒ›6mê7¯¢àúõëÝÏ×ÒÒ2è)µ%K–ôû¿qïùG?úQí⩽üàÝýÉO~Òã¾\}!rH…ÿûå/9{/Áa±JN‹ï÷Q|[¸N!|†£M…è)¾mÕªUUm‡Åß)@`Táhv åž)øío;ë¼ÂˆÇ,^¼¸—k1Pšãïÿ{/¡Ï»~ýú’hëÖ­½:ÉèôÃqéë5¡*¼=œ‘xø]xûŸþô§²;ï˜Æ+æïìÙ³é?ÿùOaÎJB©˜»ÂŠç Á\”rªÅi©ç:pà@IŽÂ¡Š6UŽ`Qx?íp4æÁ,€Qˆ¦ÔÊEtp…ÏÏ[®˜:ujɹø9cù‚RîÕÁƒ|Ÿþô§=n%ÿøÇ?zÜÓ…}=×¶mÛz ¥˜~+vX¶oßÞã¶ø|ý!ž3¦ßêüCL„ˆ Nû{®…·÷»ß’6UüúñEþl v5˜vXÎwJ`X#æ`E~&²+!\b*§øysÆÛ¾ð…²Ü‹¼³,~­èTzÈè”zLq‡ÿ7˜N¶X„ŠüOámå®_Â,^±@)¬3f §ý=W1GÁÿp8X¥^»ø{l;,W8XÀ}!BÆ…I©°w1"<üùϾjK ´VÒ@™r^£?We°‚£ßüæ7{ ÂQ%n`p¡ùp˜ Ÿ;ÂÞCÁé`xÈEª¤Mõ÷ú}Ý^I;$°,€aAœ™U<ýS*ë§ÈŠÏž‹E2#T©À*v—J½~I%¯Q-+Ë*ôÕ¹‡²K!\¯¾ÎÎ+Ε 4åU)§ý=W8P}qT­6U‰Àª¤XÀ° :öâÎ$–;8vìXv¶ãl¹üŒ²þÜ :¥bW%G\6¥ðö?þñ½g¼¯Rë»ï¾;à{(Î`Åò¥>¬¾"¨/ÇèÂ… e ›xüÌ™3³}>¥Ù bñV¼ÜCµ8íïó¾ñÆ}rT­6U‰Àª¤–ûöÅ+Xe#\–ÁL'ç{Bàg‘JuJŧý¿ùæ›Ùí1ÕS¼@ä‘Âõ‡)2LqöXþœ±þUáÿGÐ<þ/ÄI_ï¡ø¾83.0˜³ûC± ‰ŠàzY§2¹jooNK½`7oÞÌÄQKT£MU"°*i‡å~§}ñ À P¼8d©ÊóDý-+Ð_çÕ×:TXz¡xõï¾þ7ÖnŠ…3úß|áÈ^¿Üu°úCœÉXüÿñ™ÊAñt^_Ÿ%–X*NËz?ûÙφ¤MU"°*i‡å~§ýñ À §ÅÇÊØqé–蘣S— Ü™p?òÜR¸ ‘³É;ïX(³?÷(G<.:Å|a͘âyðÁ»ïé´¸?¦Á"g¯¯¥x_…+sðÁéç?ÿyúŽç‰Õ¼><àÙnñ>yä‘+¹Çv9+¹÷‡XÔ³ð#|]Nî)S‚Áoˆ—µç³ø\,Z´(?¾äc«Éiñç §0PFðxâ@QºF¥À QlQ5éãy¬[·nÉ`™ÇË}Tp‚ûN[±£à¥N¬yóæ¥óçÏwoïØ±£ß«VË`ÉàetŠ œØpRûœŒ&%ƒ5Jå­d°”ª?1¥j¿F“À’Á±,CL"ì¾qãÆL4q°Œ ð­Á‰}Gq°8XâÆ™hг £"‹%ƒ%€y#œØw” – –•ܬ·'öœp°8X–RòF8QJÉ`X,£M¼pkpbßÁ ÅÁª?%€y#œØp"ƒ%ƒE`Yá„[ƒœØwpÂÁ"°d°”’7‰R2X2X,#+úÛ>wî܈¾~.&j…ZÙj‰ØŽv2\¯ç`¡Ê­Z;žÖÂþ3ªV|ˆ¹sçZK†¤.\ ¼àD¶F '2X5+V¤'NXK†¤.^p"[#ƒ…¬ÇùóçÓÂ… ­ƒ%C‚¼àD¶F)¬j¡­­->}ÚJîFà,m',:N8X®E¨ƒÀ ^p"ƒ%o„,Ë'xÁ ‹+Á©Á %ƒ%C‚¼àDK),–¸N/8á`qkpÂÁ"°dHp‚œÈ`i'8Á ÅÁ2²â`i+8á`qjðB`É`Éà/8‘ÁRJ‹À2Ç ^pÂÁâÖà/– ‰\ ^´œÈ`Éá…Àâ`s°´œXÜœp°d°dHp‚œÈ`)%ƒE`ã/8Á ‹SƒKK†DK[Á‰ –‰ Ë'xÁ ‹[ƒ%C‚¼àDK)E`q°ŒÀ9XÚ N8XœÅÁ’Á’!‘Á Nd°äp"ƒE`ã/8á`qkp‚KKá/8‘ÁRŠÀâ`s°´œp°¸58á`X2$8Á Nd°äp"ƒE`s%ð¢­à„ƒÅ©Á %ƒ%C‚¼à„ÀRJkðÞ{ï¥%K–¤–––¬8XFà,m',…Ö}àòåËiÆŒ©££CK†DK[Á‰ ñ€¬j`ݺuéèÑ£2XFà,m'·'¬jaÊ”)™È7n\6=‚éÆ%…U^…X¾|y¦bs¢ãw½nGQÏŸ¯’íàD•.ícàýg’Ÿ’?Ú‹mÛ=·G¥À3fL:~üxêêêJ÷îÝK›7oNK—.å`YádÈ÷Ÿ¬Æåľƒ—ºv°B`"„ÖØ±ce°dHdpR÷«3Xĉ}/u-°¦OŸž®]»Ö㶇~˜ƒÅÁ2²Â ‹ƒ¥pÂÁªÛ·oOëׯïÎ]8q"½ôÒKÖÁ²ŽRu/°jq¬FâD©º_ëå—_ÎBî15bëîÝ»,–‘N8X,…–kÊ`)¼È`É`Ùwp"ƒE`q°Œ¬ðÂÁâ`q°ì;x!°d°d°”’Á’ÁRJ‹ƒÅÁ2²R,–}',KK6/2X2Xöû^,–‘•'N8X,û^,,,¥,,¥d°,–‘^8X,ûN8X– –l^p"ƒ%ƒeßÁ ÅÁâ`YᄃE`ÙwpÂÁ’Á"°”’Á’ÁRJ‹Àâ`Yá…ƒÅÁâ`ÙwðB`É`É`ÉàDK˾ƒ,Ká…ƒÅÁ²ïà„ƒE`É`)%ƒ%ƒ%ƒ¥ÅÁ2²2²Â ‹ƒeßÁ KKK6@É`É`Ùwp"ƒE`q°Œ¬ðÂÁâ`Ùwì;x!°d°”R2X2XJX,–‘N8X,ûN8X– –l^d°d°ì;8‘Á"°8XFVxÁ ‹ƒeßÁKc ¬¦¦¦’%ƒ%ƒ¥” – –R2X÷!°8X,#+œp°,ûN8X *°d°dð"ƒ%ƒeßÁ‰ Ö¨XÍÍÍYMúø°aÆtçÎËÈ ',– «¸víZ&°V­ZURXåUˆ‡z*;p6JEƒ Õž7,Û¶j¸Š_½nKÅA9í)8±Ù¶Ýs».Î"ìêêJcÇŽå`Aà¥ÊÎ+qk8XŽ'xiÜe:;;Ó#<"ƒe/u&°d°FÇþƒû^êD`µµµ¥ãÇgÎUˆ«^x!½òÊ+,#¼p°8X,…V¥Ø³gOjmmMcÆŒI>úhÚ¸q£k*U‡Ë:X£„'œ(UŸS„ÖÁ2‚À ‹ƒÅÁR8á`¹¡9p<È`É`É`9¦àD«ºëöíÛiíÚµiܸq= 7o^jooç`Aà…Àâ`q°SpÂÁ,ž{î¹’×C±/a•W!zê¡ì€ÐÈ?YÏÑ #–hoùÈ¥V·Ï;7l¯—óRË|Äï¼FêõK6©øi¬]hàÂI¯ö3ÉOÉŸ‘>¾Äqv¸^oDÖþýû3‘XµjUüÕìÙ³|ü¢E‹Ò{ï½×c ¬ò]‰FÊKÈáEÞ'²zÚIÃd° ÑÙÙ™‰¬˜œ>}z:þ|Åkj5zk´,Y#¼ÈáDVO;‘Áª¢ÀŠ`ûÍ›7«¾ ƒ ÖèX²Fx‘7‰¬žv"ƒUEB(œª+V¤cÇŽuÝëY`q°85x1 Ç K;á` ©À aU¸¸è#<’Ö¯_ßã,ÂzX2X²Fx‘#Á‰ –’Áò ÖG}”öîÝ›ÚÚÚz,4:sæÌ´cÇŽî3­äÎÁâ`áÅ(',턃UBL½öÚkiîܹÝB«œe\‹PKKKÞ'2XÚ‰ V8yòdš6mškr°8Xx1 Ç K;á`Ýnݺ•öìÙÓcº°Þ,,Y#¼È‘àDKÉ` ¹Àº{÷n:xð`v ÁÂÀ{d°bº0Viç`q°8Xx1 Ç K;á`•‰|AÑâ³ËY\TKKKK',íD«Ÿu°Bà=z´jë`q°8X,·'–vÒ°ÖîÝ»³Kã 7d°d°^äHp"ƒ¥Ôuk$ÀÁâ`qk8XÜœp°¬†9‹°– –¬^äHp"ƒ¥È`X,N ^8Xöœp°8X– – –¬^äHpB`i'2X,!£pœp°´%ƒ%k„9ÙœÈ`É`X,£M‹[ƒ–vÂÁ’Á’Á’5‹ Nd°´,‹ƒÅ©Á‹Q8NpÂÁâ`X2X2XòF2XòF8‘Á’Á"°8X,K'–vÂÁ"°d°dð"G‚,íD;!°8XœN¸58á`q°,,,,,y#œÈ`i'2XUG{{{Z¸pa;vljiiIK—.M/^ä`q°8X,n N,턃U)ÚÚÚÒñãÇSWWWV;wîLÓ¦M“Á’Á’5‹ N,íD«šhnnæ`q°8X,n N,턃U Ü»w/íÚµ+›2”Á’Á’Á’Á’7 ¥È`Ý'ššš² uóæÍ’Â*¯B,_¾<#9W²ñ{¨¶óŽ×Ë;Íþ’Ÿ’?ÙQ²ªœö¥U”þ®ãG©ísçÎèë—ÚÖ"JÿŒä÷í¤VÚG£OF½ƒu÷îÝ,ô¾`Á,¹Ù¼(U–Ru“ÁŠ3 e°äjddkê½p2:2XÚ‰¶2*ÖâÅ‹SGGGváG}”e°B4É`ÉÕÈÈÖÔ{ádtd°´meT ¬dK5Ä™ƒ&LHëÖ­K,‹ƒeŽK;á`¹¡ !¥d°” ÅÁâÔàÅ('8á`i+– –¼„ – – Nd°” ‹ƒÅÁ‹Q8N8XÚ ‹À’Á’5‹R2XJX,#+Ë',m…À’Á’Á’Á’ÁR8‘ÁÒNd°,§/Fá8á`i',KKÖxQJKX,‹ƒe®8XÚ K‹À’5‹ Nd°´,‹ƒÅ©Á Nì?8á`i+– –HÖ/JÉ`),‹À‹Q8N8XÚ ‹À’Á’5‹ Np"ƒ¥­X,#+Ë',m…ƒ%ƒ%ƒ%k„¥d°” ÅÁâÔàÅ(',œh+– –l€ – – Nd°´‹ƒÅÁâ`q°ŒÀqÂÁÒN8X– –¬^”’ÁR2X‹Sƒ–ý',m…À’Á’Á’Á’Á’!Á ¥È`U;wîL­­­iìØ±iܸqiõêÕéêÕ«,‹ƒeŽK;á`Uж¶¶tüøñÔÕÕ•îÝ»—¶lÙ’æÎ+ƒ%ƒ%k„¥,%ƒUM477s°8X,–8N,턃U-œ8q"sµJ «¼ ±|ùòl6':~Õvñï¡|½\` ×ëUº}ùòåš{“üôù3’ßÏpî?£e»÷Ÿ‘ÞþðÃGöø1)©ÕèûϨXGŽI³gÏ–Áâ`Yá'8Q\=V5°qãÆL,ݸqÃ:X2XJ)¥äÒd°î¡B#èn,–'Np‚öØcéôéÓÈ(à¤pmDø„“X€{æÌ™È(:ξúê«éÅ_¬¹ã,5HL™2%ë$ wñÒK/eë™M›6-[Û '¥7¦ 6dœÜ¹s§áÛÊþýûÓ„ ÒË/¿lÇ!®JâÒ¥K8éC\'K–,Iï¿ÿ>R öŸXùqöÞ½{ÖhD,~ÚÜÜœ&NœHdýܾ};=úè£YCϱjÕªôæ›o6,''§Nê¾mÅŠÚÌÇxýõ×Óž={Ò‚ ^d•WçÏŸOo½õVMuÃp|wíÚ•ý3GMGŽiXN ÅU`÷îÝÙlA£#ÚFì?…}OSjÉÅ"°è$cç?~üxöÅê0SÚ´iSfÍâÀéСC8)@\ú)ÚNünäéÂÞûöíË„yˆ¬gžy&»i£áÊ•+iüøñ騱c݃·… ¦Y³f¥¥K—¦éÓ§7l;yòÉ'³ÏY£©S§¦Å‹§'žx"͘1£á8‰vR(®1%6wî܆ï{b߉¶’#fM¢¬Y³¦f޳Ö P.ÌEÖ»ï¾ÛМlÙ²¥Ç"yÇ“”‰­èb$âkΜ9 ËÍÛo¿Ýí\Å>ÔÔÔ”9žÚNBP]¸p!Mž<9»&ká}qé°FDdôþüç?gSa¯½öZ÷í;vìôµàê­ßɃ’F»Gß<ç‰'²ãjK>\3ÇYë>P(²"Øê9FæŽÖÖÖlš,€—O:ËÂÏ?þü†îˆƒa¸V1"}'Dg8Y‘ŸhÔ¶±ƒ˜ö)DŒÆ'MšÔœ\¾|9˺F»(F¸Y21^ÜfÁAˆîŒøàƒL„6rŽ1Óƒ1uúÎ;ïd!÷\q‚|‚àÃŒÉ'èèèÈŽ­!£¢ï ‘N`r,Z´ˆˆ(Bälˆ«žÂ*lì˜öˆ€7@)Äò1'!ñ ÂÝ‹Ðrì?1 ÿC,‹Ó¨®o)Äta„ÿ—-[V3ƒk ´žS#.ª9'±@`£‰«8 W¯ÑÖkôµÐ*á$œíÊá¥Ý«ÑÖV¬>g±D 2æüCfp‚œà'8Á N¬A"Î ,<«iݺuÙ|7Np‚œà'xÁ U!b§\!Ç—g<Å\wäuíœà'8Á ^pB`Ý÷—«(ü2qVKÜŽœà'8Á ^pB`•‰<@_dœñU¸VO(è¸dÁ­[·p‚œà'÷Á N´•Fà„ÀúqJg,Jóºq½Áøòâ² ±®Fœ:—²ˆ«tç hâ'8Á Nî—Í›7ãD[©kN^`ÅŠ¸¡„㢚X1Ôr|©q=¹X‚?êÔ©S8Á Np‚“*òò׿þ'ÚJÝrÒÐëõ×_Ïæw —ØÏ¿Ô«tã'8Á Nð‚œX ®óˆë555¥ëׯ÷úŸ¸Zy#ò‚œà'޳8±ÿXaéҥݧwÆ——u)\‘=¾à¸X£ò‚œà'޳8±ÿXƒÆ™3g²ë8å(üRãËŒ¿ñšq…¼à'8Á‰ã,Nì?VŸÈ(‹+²ªâXn¿ÐŠŒ/uêÔ©ÙÙ û÷ïoNâ'8Á Ngqbÿ!°z!Î:ˆµ3âKŒÅÉÚÚÚÒ¦M›Rggg:þ|‘D¡r®çõF 9 Ä Np‚œ8ÎâÄþC`õú2OŸ>ÝãöXùuáÂ…Ù,qñÈâ¥öó/µ‘8ˆœà'8qœÅ‰ý‡ÀʾÌñãǧݻw÷ù?GŽÉ‚s/¾øb¯ûb-ŽFä¤?^p‚œàÄq'öŸXñeÆBe±òk(æÏõ‡X_£Þ/¢9XNœà'޳8±ÿXe"V/3·!ãºE}©q£R#‰Fæ¤ÞyÁ Npâ8‹û5H+ßüK/­¿/´žQ 'õÎ Np‚ÇYœØ¬ûD¹_j#'8Á Nð‚œX¾Tœà'8Á NpB`Õê—ÚÞÞ®uã'8Á ^p‚ €À °€À °, °, , €À €À °,€aÇÁƒS[[[jiiIcÇŽMãÇO‹/N»wï.û9ššš² ° á±zõêLmÚ´)ݽ{7uuu¥ýû÷Z0X@`|Œ={öd¢èÙgŸíuߢE‹²Ê¢«µµ5577g×Ê•+Óµk×zˆ«ÂÊqøðá4kÖ¬ìqãÆKÏ=÷\º}ûv÷ý'NœH ,Èî÷lÆŒi̘1=žãí·ßÎþ'¶¨øû­·Þê%îNŸ>æÍ›—=WüýôÓO§mÛ¶õø\¯¾újvûùóç5  ú˜3gN&LJ‰ëׯ÷ØquáÂ…ÌájooÏ·‹œB„0ŠÛ–-[–=nçÎÙöÚµk³û;::²í©S§v¿‡xÝÂç:~üxö÷O<‘ ³¨ø;n qVøÚ¹0‹êììÌ„VT.ènݺ•Ý1ÞXUGˆJ¦õBœ„˜‰Ç÷'°.\˜ÝvñâÅîÇÅv8Y¹h+J¥ž+Žw9Þÿýì¶p² ÿÿÌ™3=„ÓªU«²ÛÃ} 8p Û^³f/€ÀYnPˆ•¥K—¦‰'fÓtÅ‚ª”ÀŠ)¿RÓ‡ùÿå÷»I…ÿÓ×{ŒÛâ}ôõÚÜýZ²dI¶Óš¥XUÃìÙ³{¹C…¢*GîmÙ²%={6Ý»w¯,•OÙõ5ןxª†À <þøãÙû¸qãFö\>ú¨/€À:ìØ±£G&ªëÖ­Ë–jèK䋚Rÿó¸-²V¥0wîÜìþ+W®ôùÜùaL æˆ)ÇRS„¥gGæ®ø½~ýz_<0´È…G‘;wîd·Åz…¢eúôé=„Ò¡C‡z‰šÜ {÷Ýw»o;vìXv[Ü—‹¨S§NeÂ+°oß¾ìþ+Vdôp™â ¿ÂçŽé¼üÂU‹ÿ‹¿K…ÜKáòåË=¦&ãìB °†{÷îÍçy¶*β‹3ÿBâ ¿Iy°}Æ ½DMˆ¯)S¦ô˜º D*ž;òVñø˜¢‹³sìÚµ+M›6-{ÞÈJ]ºt){ŽÂ}áRQñwVsssÖÁõ…Í›7§U«V¥ßýîwiåÊ•éÆÝ"èöíÛiòäɽœ¾sôèÑ߯¤Ìµˆû«á–T"°jù3¾Çj ¬Ñú™ , `T ¬˜¦yõÕWSGGG¯8M›6uo>|8­Y³¦»£}çwz‰¡þ3a„l(^'¦ˆFJ`Õògœ8q"sxª)°jù3‡«¶k×®ì=X@]¬p¢c|øá‡3!²0—.]Êî{üñÇSggg÷ÿF‡™ç¢ŠÅO¾Ýßc{ì±ôÖ[o•ýÙ†J`Õòg>räHš={v¿¬JV­~æ|*8°›7oX@}¬B„ëùŸèà ;¿âê¯ãíï1§NJ‹/N'NLûöí1U«ŸyãÆiÙ²eÝSmÕXµü=ß½{7sº"xO`Xµ'°^=¥•+û®¸¿ Dv&w"úÊíôÕñö÷˜W®\ÉΫ†ÀªÒGñÏÓpú.¯ü³²ŸŸ¸4}ÏÅï‡À °jK`U€8“,ÏåDE¶&\”@„˜·oßžed±tBa6§TÇÛßc"³é¸-BÐÕXõð™;6 kUoŸ9®üý|ôÑGY+?0êÖîÝ»³pqärÂAˆ5˜ ;ûèˆ#o÷ÏŸ?¿{­¢¾:ÞþóôÓOg¯îÇ›o¾Ù¯°*5õT-ÔÚgîoº­^?ó² <6BñëÖ­ë‘é"°,€Q-°´o t@ } о,€ö @`耴o °ªÚ)UÏ@MàÿågKˆæâçIEND®B`‚GanttRendererSample.png000066400000000000000000000444271463604235500354040ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/category/doc-files‰PNG  IHDRXr5˜HÞIDATxÚí½oL]Wzö=RUUUUUýRUUUU•ª~¨ªªªÔUUE B¦ -9Á‚’¤¦ö@]Ž3vR(.Äžš:ƒëÁ‚˜0ÄŽÿƒšÁ3$fðÄc»ãøIqí!¯Ox͘¾à (ë͵ô¬Ó}6ç?çÀ9‡ß%]‚³÷9{¯}³9û·îuﵿbB!„PRõB€B!`!„BX!„BB!„°BôÅó•¯!„,„PDý÷ÿ·éíí5Û¶m3¿ýÛ¿m~é—~Éüâ/þ¢ùå_þeóGôGæ¯ÿú¯ÍØØXÖ÷Ý»wMMMùýßÿýÀ1ÿê¯þª)--5'Nœ0‹‹‹YXþöË:æ_ûµ_3yyy¦¡¡Á|öÙgü3 `!„’¡7n˜?øƒ?yö[ïÛh0ˆw}89rÄüÂ/üBÄãýÍßüÍ´¬DÚËßWpyæÌþ)°BkÑûï¿o³T±\|7 ,RXßüæ7ã>ÞÍXÎ@BB(Aý×ý—ù­ßú­  «†¿õ­o™Ÿýìgö="1/¼ðBÖÖOúS;4æýÌsÏ=g~üãÛ¡RÅEö¿ñY XN÷îÝ3EEEAë~ý×ÝÌÌÌðO‚€…ŠWßøÆ7‚.ª¿ó;¿«PrC‰N/¾ø¢ùË¿üK;¬¤Ú%A‹Mu\‚•hx]ÀÿæoþÆüʯüŠu]]]\Y—XÖ‡Ò׿þõ ÷ýñÿ±YZZZõ>Õ#ýéŸþéšÚ¿Ö ø>l~÷w×g&ẕ@Ù_üÅ_­WM–W·oß¶µx:O\}ÞþáÚx JÃíG®mýÞïýžýœêݾýíoÛ÷êø4T«óJëôÁ­_]õS¯µ!`!”VRÖÆ{!|ë­·Ö”EñûÒ¥Kß/hð/ó_ÔSXþçžÐpX²ÛKŒüГJÀ¬ø÷íÔÕÕ±^M õäÉ“ûѺPŸinn¶jn0pª¯¯xÌZ°Jùk¯ž={×çuw¡AYe)öíÛ·*3l¸‹q¼`ïp™ÿ¸½w ®(Cµ?Ù1Jô˜cùŒÚç]¯8I~øá* Ññ´··‡xj½¢ÅÑ}Êjßúé]þoÿöoüC#`!”ÒPK¸ n"Y]ø¼ïÓö#msrrÒÙGúL*+ÒqÇ(±¶-1ÒÔÞá·T–ÿ=®m;vì ¤þcÑð^¨ý\¸pÁ.?wî\Ìë4”*ýÕ_ýÕª˜Kÿþïÿ´\Ã…! ¡´Ì`Eª£ uaV’êƒtÔpY¨»£]àýéDj£2X±´?1J–ÖšÁR“¤‚÷X²NÈÂí'R¬Â­Sü5i¡ÞïÚŠ°Úp©øÚ{‘RM¬€¥mMPÏ”‰w%°üu?±JÇÛþdÆh=KÃlÞõ999vy´¹Â¢ÕˆÅÚ†PëbͲFË"„,„ÖM*ÈŽå.ÂP¾?û³? ZvèÐ!3??Ÿ€¥»ý¼ïÿ“?ù“wê.AÄ'ÚþdÆh=Ë_P¯»LCe‘BÅj-i,„,„2Nÿðæ~Òmò>×úX²wk¹s/ÜòdÆ(Þ˜ÄX‘<44øœ†:ýÞ××g3YÊ$i"Z7C²Ë?l©»%î"DÀB(í¥ ¤ÿ‚În²Kÿt©¬Á 7R¬ëÃÉ?áh(«0=Ñö'3FñÆ$QÀúçæ’zzzVÍ~¿5X‘Ž•y°°J{é2 ‹i8H°¥á-Y3”«6G3x+kñôéSû~e/TËä.ºRòg’XÚ—`EûpÍš <Öõ‘¤Ûþ5Ûºf×± ŠƒŽ×Õü$ÒþdÆ(Þ˜Ä XúœŽWClšQ=Òãq4l¬}ª¶LwC*V:6Íõ¥º™é“ X.“µuëÖ ™Üõš™Ü°B!„,„B! !„BX!„BB!„€…B!„,„B! !„BÀB!„BB!„€…B!`!„BX!„BÀB!„°B!„,„B!`!„P–ê¹çž 877טݻw›„·…°BÀò@Ñââ¢9{ö¬]öúë¯XX!„Ö XN‡¶Ë½™¬+W®˜ÚÚÚ@¦K633´¯ÆÆÆLUU•ý\~~¾9zô¨YXX øX!´¹ëÞ½{vùË/¿X&¸zðàYYY1çγëµ,Ò¶>øà»ìÕW_µŸëïï·¯;Fð°Bhs–`HËóòòB~NësrrlV*Ò¶öìÙc—MOOmW™,„€…B›°æççMCCƒÙ¿¿)..¶ËýŸ µ­-[¶„>¤V ! !„6`}üñÇv¹î(”Wz}úôi»nyy9&ÀR–KËl! !„65`577ÛåW¯^µ¯5èŸÿ³¡Þ³k×.»lrr’`#`!„Ðæ,ÝÝ×ÛÛk— ²œ***‚@idddÕg«««íkÈ;MLLØeZ÷øñc»ìÎ;¼BBe5`¹‰FU|®;5%ƒWSSS’\aû‰'V–àkûöí«Šãoܸa·©z,}¾¤¤ÄÞ…ˆ°B!„,„B! !„BX!„BB!„€…B!„,„B! !„BÀB¡ +FXeÐEÛ=\׫úúú¤]Ðûûû³jkÖîÆÆFóùçŸÛuz o[[›]öúë¯[ÏÎÎF]çôàÁSPPÑñ÷l¾X‹ø†×ÜÜœ9zô¨‹â£s]åI¦ˆÿÿœŸÝÝÝ)ík ¡ŒºÀ———›?þ8°LÏoÓ²d}1ÖÕÕÙG—èKnyyÙœ>}Ú¼üòËv]OO9sæLà½}}}¦µµ5êºxá$þ‰¾‡ø†—: :NÅEñ¹}û¶]–Lÿÿi¯Žû“O>I`k ¡Œº°«G¯^¾Sgggàá»NeeeöypÅÅÅfttÔ.Û³gO`½¾ðÔ³ŒUÚ–ôâ‹/š™™™ÀrõFõl¹hëRõ¾Ñ€êX"­#¾Ñã s3œ”qihh0MMMAY ³²%Û¶m³Ï8|õÕWm6ŒøG>‡ÕQó¸ö ˆÚÛÛmæIVFIË$=Ø[$éþýûö5±°Êø ûÓ§OíEd~~Þ^ŒJKKílÆÇÇíƒs¥®®.sóæMûû­[·B„’>£ž¨ä}@¯5·,Ò:‹øÆ"uÔaÐC¡ý ¥¬†Îa§±±1ÓÒÒÔñpÿ‚­cÇŽÿÎaÅÉûàn·\ë>{ö¬=Nùüù󦣣î{óÍ7í:I?õ^b `!”v})*m®ì”»„û²ÉÉɱ?§¦¦l/TÒÏééé¨û{ï½÷Luuu n"^ ˆuÙf,â,e¤M………6Ó±ÿ~óèÑ#»NÃà(ïE×ÕàøY™–Xês6sü];•éÛ±c‡í¸y—«ãæ2VÒÒÒR ³¦šþ6’†pÄÄÀB(+.ì‚% ‡¨7èRõnÀIÅ¡&T¯Ïû%ä ä÷îÝ´ÍPõ §N²C-ÞâRzññML:U³£‹²?Žþ˜†:f7Eü#ŸÃÒÀÀ€ýΈvn»˜êØ‹ŠŠ,tmݺ•ï ¡ì,KÞº*·Nà¥!õHõÅäýŒRûç΋:<¨ªŠSýÚµk—yòäIàµz£Ê,D[—m_Š.+è~Æ›Á"¾±Kw ¹ŒJ¸ú¬P,ÕÿؾK¤ššóÁ–+[åÏ`ycªZ8Õh90#ÖBYX> æsë44¢å®Åû™»wïÚž§>N«n‰vR‹†&ô»¢Œ´.›¾U׿ŠzUŒ;<<¸¨~(`ßðrÃM®îGuWÊ|Hêè˜ÝE_™[o –Š­]¶C™ãÇÿ8KñÔP¡·KÇèþªµr5XÒÈȈÊu7Ñk ¡¬¬pëtÁן¬ ”ÿ3UUUQ÷n(f-sׄÛf¦Hàª!’ÊÊJsïÞ=»LCµŠ§2YÏ?ÿ¼½°G,â^.\°YYÅY™+«÷u>+“¡õʸ¸†ŽUÃån¾%eV‰œß%Êp{ï"Ôñ*¦²~WËIõpú;,,,ð]`!„4¯P¬w"” „,„PÊ¥^a¤áA„2Q®N !`!„BX!„BB(a©¨kbŽˆ5€…âK‘X#bN¬,B€2á‹cŒ1ÞhX!„Bd°B!„,„B!`¡L‘œ[ìøêW¿º)½wïÞM{ìÄš˜cbN®­­°Pv–2ûƒü`SZ~ݬÇN¬‰9&Öéä×^{ ÀBV¶øÖ­[|±kbމ5€…€…1ÆÀ°€E¯kbN¬1€…,ê&0±&æ˜XXXô:‰5&æÄÀBÀÂcŒ,`ÑëÄÄš˜cb `!`Q7A¬‰9q ÖB½Nb‰9±°,`aŒ1ÆB½NbMÌ1±°°¨› Ö˜˜k ÀB½NL¬‰9±Æ°’X?º|Ù<«¬\åŸïÙÃÆc X‰xâÒ%cÊÊVYE¯“>q æÄXÀJ°._6æ…VùÙ®]ÔMP£Bˆ9±ÆÊ=÷Üsë XƼôÒ*?û»¿£×IŸ8sb¬tÔŽ;ÌǯïÝ»gâÑ£Ge?6Û·o ¡À#h‘srrÌÖ­[M[[›YXX°¾ôØùóÆÔÖ®òÏ÷î5ÍÍÍ;vŒ/Œ1ÆV:¨¥¥Å ^Ÿ;wÎÎ… ËFFFì<ðHZfggMGG‡9|ø0€õ¥(Àzå•Už=pÀî×Y#zôð11'ÖÀJŽŽšÆÆÆÀëC‡™ááaûÓéèÑ£ö}.”YÚ¶m[l¸ ”³Sww·ihh0MMMO±@ËÒÒ’Ù²eKÈ}.//›ööv“ŸŸo­l—–¹÷ö÷÷Û÷æååYЙ››‹ú¹k×®™²²2»Oµ×IËrssMqqqP Ö°¾70`ºêëWùÛû÷§%`Q7A¬‰9&ÖÖ¦—ࣴ´4ðz×®]öguuµYYY üþôéÓ\¨WËaOOéêê ¼³³Xk~~ÞBM¨}ž8qœ={Ö¶O>þ¼Íx¹÷öööÚÏk`KCgÑ>WTTd¦§§íreñü7%%%XW®\ ©p&ƒEsb¬4Ree¥­¹ššš²'éôéÓöäV†gçÎaA(`•——[Ðq¼D,µEÙ%×ÿv„.óä²]áàGïsûŒô9­»~ýzÄ8©> ÀÂc `¡˜$˜Ñ° ê®>ùä»ì³Ï>3fbb"аüÆþáÃpEîÏ?ÿ¼9sæLLûtÒ0ÞZÖݹsÇìÛ·ÏfÍ\Mš2Z>Õ0¡†½Ã¡½NbMÌ1±°PD½÷Þ{¶FJY+¯T¯¤eÚK°”rÃŒk)÷¯SÖÉŸ‰ò'ú3Xn]¤Ïyï˜LIªãÒ£†GuXºÉ@ÙÑ|ñâEê&¨QÁÄœXc+]ôÅ_X˜ðÞM(]ºtÉìÙ³'h˜/`¹:&o{___jôÏk V¤uª¥Òv]-•ꪼ5X÷ïß I ˜ãÇGýœjÅTH¯6*c%ihÑÕe ´6 °._þ‘©¬|¶Ê{öüœ^'=|â@̉5°ÒY*nÿüóÏWš«À=ìx_ ÐÜzÞBwÕbi(®¦¦&À,›†5uÇŸ¬ß•rï­««³ËÕeá£~NSBh™²n*h—4lZXXh­ãØ(Àºti"Ô“r,dñ‚1ÆÀB)×ZæâZ‹R›Ášõ¤œ/¡ø½NzøÄ˜k `¡ÔËÍ•M€500êI9æïþ.=‹º bMÌ1±°J{À:~,Ô“rÌÞ½?OËGåÐë$ÖÄk ¡ ¬†zRŽ9p`–y°0ÆXÀJăƒC_n¯u•¿þõf,zøÄ˜k `!k3L4Jݱ&æ˜XXXô:éácbN¬, X<‹cŒ1€…¬5yttÔBV4 Ñ뤇‰9±Æ°¨›ÀÄš˜cb `!`Ñë$ÖÄœ8k ! cŒ1€`!‹^'&ÖÄk+óµQHΦx¥`]½zÕtuuÜÓÓCÝ5*˜˜k ÀJ¶îÝ»gêêêL^^žµ~ÿøãc¬Xl- ¦;årssÍØØ€•äiÖ{úzÄš˜cb `e½>|hJJJÌÈȈYYY±ÖíüÅÅŶwLÀZ‹íÊõÀÊlÀÂc `e½šššÌùóçW-ײÇ€áìÙ³¦¬¬Ìf¸sssu^;é½Ê8 Ô”}ò‚‡~ â***ì{ª««ÍÔÔTØ6 ú¶nÝj–––Laa¡}í…™þþ~³mÛ¶m‹´naaÁ®“–——M{{»ÉÏÏ·nkk³Ë$µÓÁæýû÷íëXŽÀ¢×IŸ˜cb `mR˜ùùùUËZç€áôéÓö}‚›ÞÞ^›MŠ%ƒ5>>n3d~Àêìì4333öõð𰩪ª »›7oš†††˜Ü¸q#hßjk›€Êµ-Ú:ýs:8qÂB¤Ëâ 0;::ìº7ß|Ó®“ôSïå8,ê&¨Q!æ˜XX›TʾD[ç—IŠuˆ0''g`ù3Tî=¡ÔÚÚj!L´¯Ãí[Y'/ƺ®´´4±rÇè€I ¶ÿ~û{}}½™œœŒé8,zôð‰9&ÖÖ&•@IÙªh¬H`æ_?==mk¥Ü¢¬Bm/¤i;nhovvÖ¾Žô¹p`i]¤÷ ‹ŠŠ,ti¨2Öãä.BŒ1ÆÖ&•ê¬4tæ—–©>+\+䨮IŸúô©…“µ–îpô×yÉwïÞ ›¥R=T¼ë”­òg°Ü{% QªFË[dí8Ó°èukL̉5°R,a $Ttîê4'àðÞEè¦m„tww›“'O¶¡ì޲9Þº.½vuOk¬S§NÙ:*¯T¦.÷9ž»LÓÀÀ€9~üxLë¼R]U___ ªµr5X’î²T¶Ï²ÇrœuÄš˜cb `mb¹y°¶lÙb½wïÞ »úT[¤$­T<¼ÙÕE¹»ï\Ѻ`DÖðÓZkçÎÐsRÛ*++Ÿsm×þ•eZ\\Œi?»¥;] ô»²XN*”סw85ÚqXô:‰51ÇÄÀB©Hµ[é4=€…1ÆÀB#e›Y·™K™7e!5ÜJ¯“>&æÄÀ°PƉi¨› F…˜cb `!`Ñ뤇‰9±°,`e `aŒ1°°èuÒÃÇÄœXX°¨ÁÂÄš˜k `!k]½ÑÊ¡×I¬‰9&ÖBYXÎ?þîwÍ­·ßN+ßx÷]¾ô0ÆÀB(sëÓO?M»8éaÞ|éÑÃ'æ˜XXe.`­ÍéXÊðé9„ëí£GÆüÞ7ß|“/AjTˆ9&ÖVâ5X}}}æäÉ“AY/py¥º#ÿ:·­PYšh€i_±V<ÛˆXN?^U—æä†Eý¾{÷nÔ}ªö¬¿¿ß<}úÔ¶9T\ta×PŸ ×.ÿ~µ9ë»ßý±yûí[ëî þ#%Û}÷Ý|aÒÃ'æÄX«/Î*wuDº° ºÜP HÝÕ4iØÊMé á¬ÊÊÊÀ¶^|ñÅÀ°ÙçŸn³Ñ+Ò¾b¬x¶á_æê—œT¥"|mÛ•òêÔ©S¶̫ӧOÛã¶O ]ºz)V¨¸ÔÔ®‡Fl—·n+R›yTNêÅcv0ÆÀ ‹‹‹fÿþý»ïT8®Ú&–×ÔÔ Dr q)[¥»ãt›·~HÅÜZ÷üóÏ›¨€i_±V<Ûð/ÓZ~~¾µtøða{7ž²b*B¥;wZ˜ñÊÁf´} @uס¬6‡‹‹¿(>T»Üg¢µ9ëàÁOÓíI9krm-€EŸ˜k `¥Ñ\Rhµnß¾rx0›æÁjlü4Ýž”³&ík5*ÄœXc+ÉRö%O†õf#`55}’nOÊY“_yås³CŸ˜k `¡Í¤t¬×^û(Ýž”³&ÿýßÿ'S<`Œ1€…¬ýÇú§ºe˜Í¿úêm‹>1'ÖÀB2I)5*Äk ! À¢‡‰9±°°,Œ1ư²þQ9o¼ñƺì‡ÇìÐÃ'æÄXÀ¢nkbމ5€…€•º^çã̳ÊÊ´ñÍwÞ¡‡‰9±°¸œ#+³=«‰­Òè9<·Þ~›¿ ÆÀârެÌîuÎ~íkiõž[}}ôð11'Ö—sMëýìF+¾º‰ÙƒÓê9<·Îœ¡Fsb `ë)rnn®)..6G5OžÓÞÞÈøø3Q~©8^ãÝž†âbÍ`E¬xÚî–mÛ¶Íô÷÷›§OŸÚvøßëms,Ç`Åßë¼|ù²…˜tõÕ«WéácbN¬,”zÀR-Ó•+WÌöíÛEÞŽp-//Û×/wõIª[Ò” Z¦L‘w{Ê^p.ZðàÁu, ñ¹Z+µÃ-ÕæHÇ`­oÝÄ;ïÜ4••Ï2Þ—/ÿˆÎoL¬¬ÍXÞy¦B̓¥"pÕ)©.«¦¦Æ‚‹¤ét7ž2@*$÷Ž F@¢íVVVšÛ·o¯Êr¥°Tð^XXh­ö»å¡Úé¬õíu¾ýö­tzºN¾ti‚>ç7&ÖJ­”EY(™&+õî뻕NO×IØ—/Oð÷ÄX(5RFHÙ"e®4Djn)‹^§×gÎÜJ§§ë$ì2Xœß˜XXXiR7ÑÝ}-ž®“°›š¾ôXMˆK ç7&ÖBÖ†ô:ßzëýtzºNÂ~íµà)*tó=|ÎoL¬,„¬ ñ¹s¿ŒkkÆûµ×’Xc `!‹^皬ù©ÒyrÒDM‹ók !kÃê&,jT¨ "ÖÀB½N‹ ç7&ÖBVz[ó¥ F²Í£££ü}1ÆB½NbMÌ1±°°¨› Ö˜˜k ÀB½NL¬‰9±&Ö°,Œ1ÆB±ijjÊìØ±Ã>71Ôk+;{—/_6]]]¿ûî»Äsb `¡øôÜsÏYçææšââbsôèQóäÉ“5m/™š››³mÊÏÏ·`³{÷nÓÛÛ›’}ùõòË/›;wî„} `egÝD___ÐT Äsb `¡ÄèñãÇæÌ™3´fffÒ¢}õõõ¦§§Ç,//›••sûöm»l=$èŒôÀÊÎ^g¦=|bN¬1€•!€åtúôi›5rêîî6 ¦©©É^pfggíòk×®™²²2›YÒzÿöF‚5­¯««3999÷ ™ŠŠ -ÕÕÕvø-䬢µ=‘} ÚÚÛÛmvLnkk³Ë¼™½p°²×™Xc `e`iX®¨¨(.ªCq3---öw½gzzÚйsç‚¶§×.\À‘ Æ­ÓÏÎÎÎ@–lxxØTUU…lßž={ì{'''WÖZ÷uâÄ söìYû9Y³…ëb.6á†$,2XÄšók Å n8¬¼¼ÜÌÏÏ– D ì便¥æúõë!·§bðp@äß§Þç2N~)[& +,,´mÚ¿¿yôèQRö¥ö»Œ•´´´dJJJ,j°¨ÁÂÄœXX(ù€% r iˆLßûöí³CsƒƒƒAÛ Lá 'Ö‚ueË4|©a¾dì+Ô:o€µ9{ʆ ªœ¿óïkL̉5€…ÖXêÁ«6ÉeyÂÕ@y‹ãóòò‚¶§áÃX³JñÞè¦IX뾑þ –`ÀÂc `¡¤–ê”ôÚíÛ· ÙUà.àr ¢´­«ÁRmÖ‚]¦bwïöZ[[M¿­?xð`B€¥;]ý•¬š0L2ö¥,›Û¶ê±¨Á¢×I¬11'ÖJ `ÉfÛºukÈy°5ªÅÒðYMMª“>l³IÊrAˆÀKà¡íVVVÚéüY®XàEÃ5*t×¾µ¯ÆÆÆü­u_‚FÝ9¨íÊú]Y,‹º b‰9±°PÚKÙ¡‘‘‘U ’éû°èukbމ5€…Ö]Êz)3¤l’†í\æ)Ó÷`átp¦<Òc `!`ÑëÌ_¼x‘ Q9¿‰5°€EÝN¦¯^½ `q~k `!‹^'&ƒÅù‰5€…€…ÓØ<3c `!‹^'&ƒÅù‰5€…€EÝDzûæÍ›ÜEÈùM¬1€…,z™å]¾l&.]J[ÿä‡?LëöE2ç7&ÖBÖ&õ³gÏ8¹S$Î/Œ,„¬MÚë|VYiŒž±‰“nÎoL¬,„¬MZ7ñl×.c^x§Àœß˜XX)S¸Çûžtm;€E¯3Ó½PWgÌK/áøÊ•+AæüÆÄÀŠ[£££&77׌m`iÎÙXñ´ÃÿÞd€•þùÞ½ÆÔÖâØ;½„Ìù†q––æ‹ §ÖÖÖ¤B@cc£9vì˜ý¹Q¬dQ6,zÑ<­‡†8€SàL,²*ÄÀŠábÛÐÐ`–——Ëž>}jêêê’z!^YY1[·n5KKK¦°°Ð¾ö_ðõ³¿¿ßlÛ¶ÍäååÙ‹óÜÜ\`ÝÐЩ¨¨°Y°êêj355ØFYY™]^\\l3e±À…~ooo·û*))1ãã㦷·×¶¯´´Ô\¿~=¦v9uwwÛX655Ù/ÊÙÙÙ¸ömáŽß›™ó¶'TLB½×ýÔ9 væçç[·µµ΋hñ°²»n¢¹¹yàÔ˜óë,¬óçÏÛ‹§€j~~Þܿ߂DAAI`i"@ƒ»èÞ¸q#$` <Ô˜ F/·®³³ÓÌÌÌØ×ÃÃæªªjÕ~/‚˜XKûDLNNZpð¾Þ¾}{Lí’zzzì‡Nmii‰k?Ѷéø#Á°?&á†Oœ8aΞ=kQÖ¹á2œ±ÆÀÊÎ^'€`‘U!ÖVì™;wZ°Ú±c‡™žžNêБ†uQ–ƒ†ý™'ˆÚj '''ä¾Â-X‘>ç^ÇÒ®òòr `Þö…k{¸ýij ÿñGË6Fz¯{­lš7“©l£³Xã`e§, c+A ¨”IÑqaa!éµ9®rÃjöÒëh€%)Ûn[¦¶«®KÛÔ0\$؈Xá^ÇÒ.ÿÐ[¨!¸Xöë6¢K¤˜¬å8£ÕrXÙÙëTÆV•®~ã7Òº}ñ˜óë ¬pòp÷µêÞ½{!·}÷îݨ,ÕE»ÀkHSÃvªSf%Õ€ª]ÊþxëÊâ¹k/‘mD;–H1 ·e«ü¬Xâ`Q7‘.±~ç›æí·oáuð÷¿ÿYR¶388Î9Ìw €•¨N:ek޼:}ú´­éñƒŒjÀÜÔÀÀ€9~üxÔ ¼†Ñ”±qõQ©¬híRqz___Pôá­ŸŠe?ñlÿLC¼ÞaÝH1ñ¿×[ƒ¥ý»,Õcyk°,zéké“yúè£8‡ù.a¬D¥Ú.Á‚Wº­RßðŒŠí·lÙbïbÓm‹‹‹Q/ðªíÒy² ÅÃÖZ†îbi—ö­:* «ÕÔÔ &Öýij ÿ2Õµ¹»ÿ¢ÅÄÿ^ï]„ºsPÇ)ëwe±,œ)®¬|ÆÓw2ÌÎâ»u!—>ÿüs{û½.®º0z ®Óm.'Ú•ž°èund¬wízÆÓw2Ì'OX|—d)` ®~ò“ŸØß9”åIöD£Ñ$°KG¥k»,ê&pp¬kkçyúN†ùøñ›¶£ïü½ï}sšï’ì,w—˜¤‰@Vúcº©°èufJ¬÷îý9OßÉ08p>hš ÕrNó]’€¥»Ä&&&,T ®Ü¼KR¤ù¤°pºùàÁé//س8ƒ|èÐ g'`én8ï° »kíÎ;fÏž=°èufL¬™5ó `ñ]’5€¥BöîZ{òä‰].¸ÒÝfXÔMdJ¬,‹ï¼©¦i@½NL X|—l*ÀÒÃ{kkkm+U" céƒc±&8æ\ÆYXº ¤TÐ`!‹^'±ÆÄœXXIî>_— CÛÕ]y·oß^•åJ°mm•ÀpmsCZ®¶„Ú^¤ÏúëÈôS¯™¦º bMÌ“±ƒ?õ)A«üöÛ·ˆ5°ÐÿÌaå‡l€E¯“XóxÜØøi¬O Zå¾¾[Ä3Ñèf–²CÊp)s¥!B—u°0Æ›ÝÍÍŸÆú” U>sðÀÚ$°èukbÿáþO¬O Z円ޠÇ]¼x‘Xc XÔM`bMÌÿñ?Šõ)A«|èPðôÙzW%ç7€…€E¯“Xó¸üÏÿÜgêë»ò¡CÍ›°8¿,„,Œq\îèèHx’U¿™ XÀ¢‡‰51°8¿,„,ê&ˆ5N~Ì,Îo !‹^'±ÆI޹žßšèc‚ü¾zõ*±Æ°0Æc !‹^'±ÆÄœXXXÔMkL̉5°€E¯kbމ5€•™âA×ñkjjÊìØ±Ã>S1Ôk ãõµ º»ººîéé!.oÀÄȹ¹¹¦ªªÊܹs'm (ܶ¼m...6G5OžbÎù7 `I+++fddÄlß¾=í3XÞå?6gΜ± 533“í®¯¯·½Ôååe×Û·oÛeë!Ag¤×uÄzsç7±°ÒbyyyAë»»»MCCƒijj²_³³³vyEE…=™¤û÷ïÛ×Þí]»vÍ”••Ù Ž>m{’ ¤¤¤Ä~¦¶¶6&Àr:}ú´ÍEÛO¨vy·§6Ö´¾®®ÎäääÞ344dSÐR]]m‡ßÂAŽb­í‰ìKÐÖÞÞn³cr[[›]æÍì…3€E¯“X“ÁÂÄÀÚ€ –2A^¨fÀillÌ´´´Øßß|óMsöìYû»~ž8q"h{EEEfzzÚn÷ܹsQ·§}÷õõ`ááÇq–†å´Ïhû Õ.·=½¾páBŽ5n~vvv²dÃÃÃvH5”öìÙcß;99¹ ´Öº/ÅYñÖçdMÚ§–ÃÅ&\ ,Œ7`a `mp –ÜØØh¾øâ‹Àºòòr3??”á*((°¿‹Ô÷ïßLx/襥¥æúõëAûŠ´½mÛ¶à*ž!ÂPÃa‘öª]n{*Dþ}ê}.ãä—²eºÂÂBÛ&ÅéÑ£GIÙ—ÚïÓÒÒ’ÍúXô:‰5€EÌ9¿¬4Ì` :víÚtÑ4Ô¤÷)¤ üÖ­[WmO…Õûöí³C`zÄB´í…xK@å@#Ò~µK L‰´Ï+eË4|©a¾dì+Ô:o€EݱN/+­,³³²ÓÄœóo², Ñ"Ö8ûKRQø7…âÞº(ѹ«e’tס†ÁFGGWmO5P ö3**¶=k«KÐñÙgŸÙ4z,€¥:%¥àu÷£+d´ŸPírÛkmm5ýýý¶ ZðàÁ„Ë ™º:)Õ„ `’±/Õ`éØÜ¶UE Æ8Ó@Ä[RALð¦¬ÅÅE[3ä`Ep š& CÕÔÔØ!/ï°œ– XüÛ;|ø°ÍÚ(›4>>T8j{Ú†2b:ÓgÞ{ィó`é½Ê|…š+Ü~BµËíGmxh»•••vz–+xQñº ݵoíK>ϵîKÐ(Õveý®,€E¯“XãL‰ùÛoß2êßʵµ³Äg'`¡Ðró‚ùA$Ó÷`Q7A¬‰ùF»¯ï–Ù¹ÓXïß?K¬1€µ¤¬—2CÊ&iØÎ;OW&ï À¢×I¬‰yúd°~lþöoõ+¯|ô(!•@k ÀB+ c¼Q~ë­÷¿+cýê«ÿ4…êS‰€`!‹>&ÖÄúè#ÎaÎo ! ã4¬K—Là1æ::øb !‹^'±NCÀº|Ù˜^ÈHtò$ç0ç7€…V+ÜC,‹º b½^þ‘륗2Ò·ÿõ_mqºó÷¿ÿ}ÎiÎok­`"çääØçæ½øâ‹æÎ;)…·ÏdBQºV<íð¿7™Ç`Ñë$ÖPäþÝïS[›‘>à@Ðt ™|·ç7€•v@°²²b†‡‡íCŠS 5É¢l,2Xg8`?oáË0Ÿ?tÀÂV*@¥L–Sww·ihh0MMMöŸnvvvUÊ»²²2“››kŠ‹‹ÍèèhLûÕïííí&//Ï”””˜ññqÓÛÛk Mii©¹~ýzà}ýýýfÛ¶mö½‚†¹¹¹UÛ‹ÔæXömCCC¦¢¢Âguuµ™ššŠ;&¡Þë~.//Ûvæçç[·µµÙeÑö`Ñë$Ö?ÿS¸ 63Íç7€•ä ÖéÓ§ÍÉ“'íëžž;1œÓØØ˜iii‰)#xÄÄ XAÄää¤ïëíÛ·½o~~Þ¶U°uìØ± íEjs¬û‰¶ÎÎN333c_+ãWUU•PL ž8qœ={Ö£¬Y‘;::bÚ?€Eݱ°,Îo+j°œ•eyôè‘]§¡BÁŒÀ bâR]W¬€ésîµÿ}#×·.R›cÝO<ÛÐ:ïvâ‰I8ÀR6Íe¬¤¥¥¥˜EÛ?€E¯“XXç7€•†,e…4ì ¾üC_þ‹ýôô´ill´Cb†‹‘¶îu¨í) åO¸6dzŸX·íX"Åd-Ç­– ÀÂÀ°0€•fEÙ®K™AW¬ŸUm”íéÓ§ös©,ey”qógµ9ÖýijhÇ)&áö¯l•?ƒå?N‹^'±Æ™ó¡¡÷ÍÛoßÊxÿûcœßV‵¸¸hk’êëë…ÞꟋ½ÆŸ½5XEEE6Cã¤a4½v™°TÖýû÷Ù¶süøñ ÷Djs¬û‰gþeñÄÄÿ^o –öïj°Tå­Á°¨› Ö8Sb.(Éé1HœßVÂ5X‚£Gî˜sߪIÒUMMM îts׺#OÖçÂÖZ†îêêêl†MûÔv‚Bÿgµ9Öýij ÿ²xbâ¯÷.BÝ9¨ã”õ»²XYb3-æÊþdèÓƒ‚|éÒç7€•½b¶væÁÂg–ûúneêÓƒ‚|ùòO+{åŸ XdUˆ5Nÿ˜ç;ÿ+SŸäï|ç{æêÕ«vÚydd„óÀB5X|±kb¾1îêz?SŸäC‡Zƒî²Ô£œ°È`ñÅF¬‰ù†¸§ç‡æÀÙŒ÷k¯ý3€`!`aŒÓÃNË–¹Á, !‹¬ ±&æ€`!`QD¬q6ÆÀ°èácbMÌ“ì‹/šæææ¬ðo¼ø}hhˆóÀB5XcŒ,„,²*į{ÌÇÍlm-ãGo¾É9`! À¢.ˆXóø~üƒuy*OSÓ·LsssÀšxÀBÀ¢‡O6ó,-rÿp]žÊóÚk­AÓAhú !‹ê01ÏJ_º4fêë»Rî×^ûG ÀB=|L¬‰ùæðÅ‹7dBS ! cŒ³ÖšëÀ°€`ÑÃ'ÖÄ“Á°°¨Q!Ö˜˜§¯ïÞ½»!èÑ|‡B=|²)˜˜gu¬ÿã›ß4ŽIªßÏ¢©,`aŒ1ŽÛÏž=KúwöÄÄ€`!‹>&ÖÄ|óÆúYeeÒ@8qé€`!‹L¬‰ùæõ³¿ý[cvîLª'._°¬ØõÜsÏeå¾,zø˜XóÍëÿoï^cYIô»ÿú¯¦««+à«W¯XÙX¿× .á>ëßG6V<íð¿7™Ç`aŒqjüó/¿O“ý|œÖ/Á#[§f°R(‘¶™ìýe`‘Á¢‡O¬11OÿX_ß»×|¸{wRýÆ¡CÖf,o&KOô®¨¨0¹¹¹¦ººÚLMMÞWVVf—›ÑÑѸK¿···›¼¼m{£‚‚‚ uåååÀ¼íô¿'Ú~âÙ†?цý"½×½V6Íe¬¤¥¥¥°Ðîï`aŒqj `XIÏ`…[7==míP˜†ßb)Xá^‡Úž²Pþ÷ÄZ°i?ñýG:–p±YëqFûÛXôð‰51Ç©‰µF4ªJktÀ°lM”†ëž>}j3*ëXÊò¨®ÉŸýQ¹k/‘mD;–p±‰´e«ü,ÿqXÔ¨kL̳7Öï¾{Ã9ò å°Ò°4|¦L«‹J%`Ý¿?04600`Ž?ô§÷õõEÿÞú©XöÏ6üËŠŠŠl,œÂÅ&Ô{½5XÚ¿«ÁR=–· À¢‡O¬11ÏîXôÑGërí°Ò°”ÊÔx² Ä£ÖZ†îêêêÌ–-[ìÝuºÓnqqqÕgÔÕQiX­¦¦&1±î'žmø— îþ‹›PïõÞE¨;uœ²~W ÀÂãÍᎎ’ý4ž°PZÍu• °èákbŽ37ÖßþöGÉ~OHXÈJÙ`Q£B¬11ÏöXŸ8q+ÙOã iïãyd !‹>±ÆÄ±ÆÄœX‡ðûCCfâÒ%kýžNm°€EÝ&ÖÄgd¬ÕN§tk3€…,z˜Xsœ‘±~päHàY8úÀBÀÂc¼VÀ:v̘^°ÖïB1hjjÊìØ±#ðØ ÿk‹>±&æxsÇú~[›1/½dý­­æÊ•+_½zÀB髹¹9sôèQ“ŸŸoÁf÷îݦ··×®Kõ©_~ùesçΰ¯,jTˆ51Ç›;ַ𛩭µþÁþýAÓ0¬Çó,”°êëëMOOY^^6+++æöíÛvÙz(777âk‹>±&æxsÇúÖ7¾xØà°PæHP#° %oKV\\l³\uuu&'''ðž¡¡!SQQa·U]]m‡ú$A[{{»ÍŽÉmmmv™û\$XcŒQá&°PZkÏž=¦³³ÓLNN®-:çγŹõ(·N?õù™™ûzxxØTUUÙßOœ8aΞ=k?'Ÿ?ÞNþ à" IXôð‰51Ç›3ÖÊXÍÎΚ––SXXh3Pû÷ï7= ž‡ƒ/?é}.»UZZÈXIKKK¦¤¤À¢F…XcbN¬,´y4==mNŸ>m‡ù¼Àã€)ÀжÎ[g`ÑÃ'Ö˜˜k m¹iðÅœÁò.S¶ÊŸÁR€…1Æx==4ô¾¹tiÀBë{¡«¿’UÌ.€ñOkk«éïï·ë•V>xð`L€¥,=EÝm[õXÔ`ÑÃ'Ö˜˜ë•,´nRñº Ý5t§ÌUcc£­ËòÏ‚… VVVÚ©òòò¢–²WºsPÛ•õ»²X5*Äsb½ž>rä} ÒVÊDŒŒ¬‚žT À¢‡O¬‰9&Ökñ±cìw,”v*//·Y(e®4Dè²\Æãtw[Û}ûÄï£vd mjXôð‰51ÇÄz-nn¾eŸ¸ã¿ÀB€E ±&æ˜X'±&æ˜X§‘,`aŒ1ÆB½NbMÌ1±°°¨› Ö˜˜k ÀB½NL¬‰9±Æ°,Œ1ÆB½Nb‰9±°,`Q7‰51'ÖÀB½NL¬‰9&ÖBÆc ¡ô¬={ö˜¯~õ«cŒñ†¹¶¶ÀB!„ÚHX!„BB!„€…B!`!„B! !„BÀB!„°B!„€…B!`!„"ÿÃ~å+x“!`!„RXˆ¿?BÀBqEüý°B\`„€…XÄß!`!„¸À"þþX¡ô»ÀþìgÆÜ¹ÞZŸ=÷ÜsIk,ÛJt‘>·N¡JªB€…€…J5`9cLYYxk½OsssæèÑ£&??ßlÙ²ÅìÞ½Ûôööf=`%ª€FGGMnn®(°°BXõõõ¦§§Ç,//›••sûöm» À ¿ÝÆÆFsìØ1ûÀBXX«>¯LŒÀ*”]ííí6»•——g<¸Ð ™ŠŠ ûùêêj355ø\ww·ihh0MMMæõ×_7³³³u‚¹’’›-«­­ @C(xµ.‘m'°«­[·š¥¥%SXX»Hñò:ÚñJe_6B±-..¶3 ! !”A€µgÏÓÙÙi&''WÖ©S§Ì‰',8øA@Ÿ™™™±¯‡‡‡MUUUrºººïÕ0ZKKËÿmÞÓ×רÞÇã¬D·LÀºyó¦<éÕW_57n܈¯PÇ °œÆÇÇ-4XX¡ ,e€)ÊÆ(c²ÿ~óèÑ#»®´´Ôfj¢€À,''Çþ^^^næççƒÖØß·mÛÖb¬D·LÀjmmµ@) Ú×NáâµÀ’\l,„,„P†–WÓÓÓæôéÓvÈ/Ò? …r¬i]¢ÛN&`ièN788ÕëXö`éï ú.m[C‘ŽÀBÀB¥9`9©†IÒÐT¨ú¬Hp ,N¸š.Õ.ù׹Ϲ,M$ÀJtÛɬ{÷î…¼»wïFŒW¨¶D:^eãúûûÍÓ§Oíö,„,„ÐF–Š¡_=¼ÿo±´WºcÐÕ_ɪsRm‘ÔÑÑa®^½`©Ý[ õàÁƒ@T[[›­•Ò~>ûì3ÓÜÜøÜ‹/¾zûüóÏm—,Ýv(%*[cåŸÂB?µ5R¼¤¢¢"›™rŠt¼öÔ{u,- ! !´‘€•€.\¸` ÝU¥Ì•†¦Üy‹‹‹\4L¥uÞ»â"A— MõRÚfMMM,l¸²7ÊF½÷Þ{Ïé.DÊkÝóÏ?oB‚E"ÛN–vîÜi¡Î+µ»²²2b¼$ÕkéîB9Úñ ¼T'ëx,„,„P†âï°B\`„,„XÄß!`!Äñ÷GXqEüýBB(©X¼¹°B!„6¥þVâ´ Í+QIEND®B`‚GroupedStackedBarRendererSample.png000066400000000000000000000455241463604235500376570ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/category/doc-files‰PNG  IHDRXr5˜KIDATxÚíÝ pTež÷ñ©ÚÚÚÚÚÚÚÚª­­­­­­-o3#Œ Þ5`0Äp‘K¸ &@0‘€‘ &1(2 HÈä «Ë‹3¼¨ B"*"† WqÈ$„Ûà¤éÿûüŸî“é4}'Áîðýuý+éëé眧Ïùä9§O~$„B!¤Uó#f!„BÀ"„BX„B!‹B!„,Bââõ£µ¨ö>]–!„,’ ©®®–gŸ}Vn¹åù»¿û;ùÛ¿ý[ùÇüGIJJ’ììlÙ¶mÀŠaºþÏqJçñ?ýÓ?É Aƒäã?Ž‹ùzèÐ!™4i’Ü~ûí-úÀÀeùòåòý÷ß·`…Z&)))2gÎ9sæÌMµX°`p&‹ÖÌÌ™3ƒB ÔÊöfIjM`ùWEEÅÚ^}õUù›¿ù›ïñßþíßâXmµ\—•••7Å:`÷îÝ—?!‹£#‘"`µ°~üãÿ`}àµ×^‹ºÜ Àrª½#«¾¾^þó?ÿ3¢ÏÝ%¤»¯fÍšeWÀëú7äš«W¯Jyyy‹ÛuwÜ‘ãÇÛ]c¾ïEw ÿþ÷¿·ïóÂ… `ÿú¯ÿÚ®åäðáÃ’ššÚâ¾þç–ºººv»HKK³íœ>}:À"‹ÖŠÿnH; 7Ê‘™™)>ú¨ÝÍ¢xÐøüÇÈСCíÆ;PjkkeêÔ©Ò©S'ùû¿ÿ{û<Ý-¥ÏùÍo~vãøð÷¸]߃“ƒÚcÌŠúºúú:t3fXDøgþüùöx4}¬>GÉiÍ ¹ÿ}þ#XÑÎ?ÿé(Ž´ ÿõ_ÿe—q°hû}Ÿ§ó¾©©éšÇi¿¸çž{‚NO2qâDù‡ø[¹¹¹×¼F[´)š×hPæß—tùû&Òþä?ý£E_Ëé[z¼Û¯~õ«fxë®Zí zŸ>FqëŸÍ›7[:óQêu½=Ú8£—ÚÞxƒ3!‹$t0¾+Ôÿ÷—iӦɦM›Â#R`…Ûí¢¯ï=É$%šÝSþêÖ­[3V¯^òø"Ý0ên'zày,»L£ÙëFÕ÷öU«VE<Í?ÿûý,>ø`L»Ãü§§èñ¿Í%mѦ¶–bÅÚN¢éOî ô=À\è>ÝïD?›¡Ú¬÷Gš}ûöÙvèÝéÓ§X„´fòòò‚®¬uå«à%K–Õµ2þÙÏ~f7˜úW¹þÕî?)q¢ß`ôÝ`ýË¿üKó·kjj¤G!¥èú^×ÝY'Ož´=pàÀ5 }OÚ¦@¦ 6\³aÕÑÁi«c}æÎ{Í󢙑L'XtäÅ„¥µÚ¥˜¸mjí‘E¾?ßûu>ÅÒŸ¢9Ö+Ü|ôGŸŽê´õ§ïí¾£½Áâ{ÜÕ»ï¾{]ó’€EH€è(•˜P¥Ç?þñQm¤|£‚`Ç 6¬Å}Î.'Š;ßÿéú »eË–æÇŽ1" üßîŽÑ8Ç£z­¶<˜ZçÁŸÿüç˜æ_ éè1^¡F èëIJAõŸž"Y׋渲ÖjS[Ëÿ1Î{‹¶?ùOÇÁŒ?äCݧ#»šþýû_3Ï5_|ñE‹Ûuwa¸8ýüù矿îyIÀ"$Dt®˜ý7¸¾5f̘ˆWÆ:â£ÇËèFAwù”ø>^Ï=ä{{cccÌhÑu}£»?"޳ÕãYüûi‹ ¹¢QÏ/æ{ŸŽèÄ2ÿ®gÃØZ#Xq=}"š6݈,í±ô§`Ó 5¯‚ݬoú?Þy¯­ E!­]YëÇÅÅÅö¯pß•¬¼ÉFJŸï¦P+íhGQB½¦Dí›pçvòŸ®ÿãÛrC®Èò½OwÆ2ÿ®XþÇýDz t´»ëÚ²Mm,ÝÍæ{¿ŽòÆÒŸbm_ ûB}N¢ýF*À"‹6ŽîúÓ3xŠîŒfŽ=ÀÜ÷v=‘©žú!Øãýÿ27ŠèX±`jû¿v cÉ|ã }[nÈýç‰3£×,ý¶Ÿïó:wîpéqhz<^¬ÀjË6µ°ü¨×Ó—ÄÒŸZXŒ`°H"uN/PÆŽkÿjwp£ÇºèFÅÿ›y¡F‡ÂH[ië¿bñ½ýí·ß¾æ}:#^Çÿ`i=&Ë9ž)’×Ö ½žûIsÿý÷·x¼ž©-6äŠ×Q£Fµ¸O¿²Ëü»`)®ýŸ«óB¿8 Ñù¨ßbÓo—Fò^¢¥l6…uŒv¹èòöï3úM[çØ¯hûSkËÿ,=UD eÉ1X­‰UB!×ù—¬|ëg£ëÔ'Ÿ|bo÷?NE7Zþ@ûï>òÿ sp¹~Ð9mB°÷¬£+þÓÔ“£{m=ÞLGô/ý¶¢óÕyÿéôÜMú8=¹­Ï®ç@Šeþ]ï†1Ð9¾býæ^°ÛÛ²MÁúak-—÷ß?d_ ÕŸZXþ»-õÛƒšX¾E°À"¤ã¿Û!Øéôkèþ vNžP§~¶òÖó@]Ïy°üÿQ­¾–žâASVVöµ×Ñ‘Š`ÿ6¤-¥##Nb™×»aô?áh ÒÓcV[¶)X?¼Þå¢(ô?7W´ý©5ª­±œ `€EHFw ê_àzš=¨Ýùf—þÔ]‚zfu*þÑ¿ÚuÃ霬Ô93µÞ®Çö8!½ßÿ¯ï@+ï#GŽØçéq@ÎûУuË÷àë@¯£Óô?Ù¥ï®=K¼¾Wm“¾¶¢Qߟƒ¦£T¾g×Q3ý»óþõu?üðÃV–snÝíã?2Ëük £~í_ç…žI\§íœ„Rñ§}$’oT†úÖd[µ)X?Œv¹èó´½Úot41Կlj´?µ6°œ‘,퟾gr×뱜É`€E!„B!„BÀ"„BX„B!‹B!„,B!„€E!„°!„BÀ"„BX„B!‹B!„,B!„€E!„°â'³fÍjq=;;[ž}öYŠ¢(Š¢¨˜kòäÉË7/½ô’|øá‡EQEQ1×Ë/¿ °EQEQ `QEQ°EQEQ `QEQE,€EQEQÀXEQE,€EQEQÕNuöìY),,”””[yyyrúôi{ŸÛí–ÒÒR)**²xÒjhhXEQE¬P;v¬¼ûî»SZ6lÑ£GÛûÊÊʤ²²²ù±RRR°(Š¢(ŠX¡Ò³gO +ß$''ÛŸ™™™RWW×b´+==`QEQ°BeãÆ²fÍyï½÷ä»ï¾“mÛ¶ÉÂ… í}ºËÐ7 1ÿÛX9å›3fHmm­TUUÙ¤?¹Îu®së\ç:×£¹žÀRTé?eVT 4Èuþüy{_RRÒ5t#XE%b]¾ü€ùÃñQSÏ›ZeêSïá»L½fj¸©ÛLýÄTGSw›êbª›©½ÏínJ÷´©q¦Æ›šäÖTSy¦ Lššmjž©…Þ빦2½ÏO3õ¸©ûå/¹åF1‚•(ÀÊÊÊ’šššæëzÌ•ó_«#ÁXE%b¹\wš5Õæ²M­4õ©wíµË”oª‡DÜbêvS?5õ3Sw›º×ÔýÞç>fêq]cšêc*ÍÔ@SƒM 35Jv5•aj¢wZ¹¦^0•¯cýºö45×Ôïu]?ë~ª©î¦ºZä±Ü(€• ÀrŽ· t›â«¾¾¾Å1XƒXE,€EQ+Tô´ º›Ð¡Zµj•…‘¦¼¼Ü~sÐ÷[„ÅÅÅ‹¢(€°( `…Šž×Ja¤ß&ÔÒc±œc°8EQNíßÿ–|ñÅRSëLm6µÓÔ¦v˜zÇÔ*S¯šZì­o-ñÖRŸZæ­ÞZå­ÕÞZë­uÞªðÖzomôÖzïýk½Ï_á}ý%öý,ŠXœÉ¢¨íéîÝø¯6u Î02ÔT_ý*ŽÅˆËÕ`QÀXE,€°( `,ŠX `QÀXE,€EQ `Q°À¢ÀX°À¢(€°(ŠX‹¢À¢( `,ŠX‹¢(€°E,€EQÀX°V»¬ªª_Ê©S¹¦›Zoj—©S¦>6UfªÈT¦·&˜Ê2•mêyïó¦šzÑT¾©é¦ M½ljŽ÷¹óM-0õª·t:ŦJM½nj©©e¦V˜Ziê-ïõRï㋼¯—o§wìX.Ë `,€EQ `Åw;÷¤™‹÷™š`êMSûâhåŸáþS¦zxß'+€°E,€°°E,€°°E,€EÇXÀX‹¢ÀX‹X‹¢ÀX‹X‹¢À¢°À¢(€°ÀX `QÀX `Q `QÀXÀ¢ÀX°ÀX `,ŠX `, `¬vÒbæGª©S妪M¹Mí2õš©á¦n3õ;ßÜî»MÝkê~S›J2õ¸©S}L¥™hj°©a¦F™k*ÓT–©lS¹¦ò¼Ó,45ÛÔ÷7õ´·/05Æ»tüœw¹[n…¦¦˜ï}®¾Ö¦²}æÜ¹>,·› XIIIK£ ”––JQQ‘Å“VCCÀX `,€°ÀŠ&uuu2fÌû{YY™TVV6ßWQQ!%%% `,€°ÀX+šË–-[ìï™™™\NΞ=+ééé `,€°ÀX+Òœ>}ZÆ×|=%%¥ÅýºËÐÿ6VNùfÆŒR[[+UUUvéO®G~ýâÅÔ„˯ý^w¹:$°ÂµïêÕŽ ,úcUBëâÅ4Ö'×q=áµ`ÁÙ±cG‹ã³³Å#XŒ`1‚Å#XŒ`1‚ÅVQ)N˜0¡Åm‘Ž`Űöî-“/¾Xbj©Í¦všúÂÔSï˜ZeêUS‹½Uâ­%ÞZêS˼µÂ[«¼µÚ[k½µÎ[ÞZï­ÞZï½­÷ù+¼¯¿Ä¨|9À¢ÀX‹jïÀ*,,”ýû÷·¸-++Kêëë[ƒ5xðà¸Ö•+]½9V”Ý"ZQ, `,€°¨VMMäää\s{yy¹ýæ ï·õ x€°ÀX `,€&¹¹¹òå—_^s{" `, `,€°VÜž¦áz°+‚¿þÏÊUçãÛ¦ª,»[½ËîÎË.Ù»ìúz—ÝÓÞe7Ü»ìÆ]'F³Ó¼x±ÀXëÖ±cÏÉ©SÓM½nêS{L2õ±©2SE¦2½5ÁT–©lSÏ›Ê55ÕÔ‹¦ò½¯ShêeSs¼Ïoj©W½µØT±©Rï4—šZfj…©•¦Þò^/õ>¾Èûš/ÚiêñÌ `,€uCKÿZx`ýÐéãÆ=æóóÀjÇÀòlç{7Ò{7ÚûLý¯wúr+n¸#Ýh¿î}ì|ïkå{§“m¿´t3ËÓ¦”8^o ‰z½°ÀX `µ[`y6Üú^^ó¶M¼ )75Ýû9þ!0’éíï}íMu²ÐX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀXë:€uèÐÏM»™zÉÔS»L3µßÔzS‹LM´m×Ò¹s©¦ú›hjˆ©tS#M65ÎT†÷ñ“L=o*×Tž©iÞiššmªÈ”N{¡©Å¦ŠM-1µÔ{}ž÷ñS½¯õŒV]ÝP€ÕŽõýÄNâ.xDÜ+²Å½u¥¸k>5ËÎ-î¯v‰{S‰¸§‹;óVqO¼CÜÏÝ)ÄÛYÜS v^|ÀóÜéIâ.L÷¬^â.ê+îùýĽðiq¿2Ä<˜¸‹G‰»t¬¸ßÈ÷²‰ži­Ê÷[yâ~»@Üë ÅýëÙ⮜'î =×Wšû_ÏðL^ªyýîâ~¡›¸²;,€°ÀXÁÚÔÅûüe¦öÆÁÊ~†·Í™Þy’æg÷Ç<*°dëNÓ¦‡ÍûÊ6ÓYiÖŸz×»L›JL›ÒM›n1mºÝ´é§¦M?3mºÛ´é^Ó¦ûï÷<÷1Ó¦ÇM›RL›ú˜6¥™6 4mlÚ4Ì´i”iÓXÓ¦ Ó¦‰=ÓÊ5mzÁ´)ß´iÆ ]yš&™6-Xà¹>Ù´éÙg=ÓO5ý»»™ï] ê:2‚°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX+LŽ=*Ó¦M3ë‡[NôŒÆ¥¥¥RTTdñ¤ÕÐаÀX `,€°BåÌ™32fÌ©®®¾æ¾²²2©¬¬l¾^QQaÖ9% `,€°ÀX+TL;wî x_ff¦ÔÕÕ5_?{ö¬ù¼§,€°ÀX `¬PQ0)²zõêew*ŠœÝ€¾» ]†þ·,€°ÀX `,¿ôèÑCöíÛgñär¹dÍš5æ3ŸoïKJJºæñnsŽÏòÖ ³¢¨­­•ªª*;ƒôg[]OD`…kßÅ‹© ¬pËëÊ•~ ¬Xúc"+\û\® ¬pËëªK¢+–þ˜ˆÀ ×¾DÖÅ‹ia—W¢Ëåzì†lßXþ£T={öd‹,F°Áb‹,F°Áb+ÖŒ=ºÅqVš>º’0ÉÊÊ’úúúÇ` Ö•ÀX `,€°À ýfà’%Kš»Ú¿¿,]ºÔþ^^^nï÷}lqq1ÀX `,€°À —eË–ÙƒÜu× b«©©©y— çÁX `,€°Àú°ÀX `,€°ÀX `,€°ÀX `,€°Ú%°n½ÕÓ´o¼mÖUUñÕ‡ ñ¼¦Nã³Þx`,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀXëFëÒó‹;ßÔŠlqo])îšOí‰RÝ_í÷¦q/Nwæ­âžx‡¸Ÿ»SÜ9w‰;·³¸§š•Ö‹ˆ»àqOOwa²¸gõwQ_qÏï'î…O‹û•!æùÃÄ]Œ`,€°ÀX `,€°ÀX `,€°â XIfe¨4n·[JKK¥¨¨ÈâI«¡¡`Ý(`é‡þ¾û<î7 °ö틟6éãuúºbîÑÃó>;,€°ÀX«XÁRVV&•••Í×+**Ì2.X+v`i›Ì†Ï>™ÖÞ½?üÊ^¯k›ue¯óDç‘Î3‡ÀX `¬VV¦éäuuuÍ×Ïž=k–o:ÀX `,€°À ¬ääd[ƒ ’åË—Ë÷ßoïKÑäÝeèÀX `,€°À ­R`Í™3'èèV Ûœã³ü5Ã,˜ÚÚZÓGªì ÒŸmu=®}õñ ¬pËëJ¿~ ¬Xúc"+\û\:$°Â-¯«fù&°b鉬píK4`]4ý"ÜòJ4`¹Ìϱ}o7ß"ÔQªžÚáÁb‹,F°Áb‹,F°Áj\ºtÉ,4û{VV–Ô××·8k°.$€°ÀX `,€<¹fÆî3nR\éiV¬Xaï+7>ýæ ï·‹‹‹ÀX `,€°V¨lÚ´ÉÌ»ÉfÙC  «V­j±Kó`,€°ÀX `q&w€°ÀX `,€°ÀX `,€°ÀX `,€ÕªÀÊÜz›ÌÚÕKÞú8þ¶=WeÕùêO»dÓ7%²xoºdn¹U&n¹CžÛz§äl»Kr?è,S?ì*/~ô€ìxD¦œ$…;“íëíî+ó÷ô“…¿Z^Ù;Dï&ÅŸŽ’Òc岬j¢¬¨Î–Usí4ßþ¢@Ö*”_5[*Ï“ _/´×Wšû_7uïPûš:)Þ+SÌO€°ÀX `¬¸Ö­æ’b./˜ËÛæR%žõÆ.s)1—ts¹Å\n7—ŸšËÏÌåns¹×\î7—‡Íå1syÜ\ôuú˜Kš¹ 4—Áæ2Ì\F™ËXsÉ0—‰æ’m.¹æ¢ÓÌ7—æ2Ë\æšËsÑë“ÍåYsb.úš:{Ìå¡+í|ëòåËö›}½zõjqP=ņ ÀX `,€°V´Y´h‘…•SN>ÿüsû­@€°ÀX `,€etäjãÆv?­ÿ¿±ÑS/,€°ÀX `¬(£ÿ¤Yqåÿ¿ûî»æy°ÀX `,€°¢È¸qã,dNœ8a¥Øª®®6Ÿå {Q€°ÀX `,€e¶oßÞâ,ßÚçlÀX `,€°VtÙk6R999v— w¥ß ܽ{7§iX `¬õõSwKCßGäBAŽüyÝ*iªö¬7þ²w—\^^"çÇ“ú{o•úûîú‡î”úGgWièõ€}î¹§’äÜ€d97¸—4¦÷•Æ‘ý¤ñ™§åü¸Áöùç'’ 9cå”L¹ðb–Ö…Â)rñå<¹XT ^(—^™%—~1W.•.°×/ÌÈ• ¹öù#ž’sý{HÃÝäxßÎ `,΃°ÀXñ ¬;]wÚ’ndVšË§òi\mÐtú©æÒÝ\ºšKG7#X `Ý0`Û=èÚ€°ÀX `,€°ÀX `,€°âka‰é`‡X `,€°ÀX­•“'OšõR.ÀX `,€°Àj­èù°8Ñ(ÀX `,€°V+âjýúõ `,€°ÀX«µr÷ÀX `,€°ÀŠX:r5Ý|hÀX `,€°'X `,€°Àú€îÜWœ `,€°ÀX `,€°ÀX `±‹`,€°ÀX«Ý«®®Î,óÉ’œœ|Ý#Xï¿ÿ~‹çèéJKK¥¨¨ÈâI+Ôó `,€°ÖVæÖÛ¤hw_yû‹ùèø:©müÌn³¾úÓ.ÙôM‰,Þ›.™[n•‰[îç¶Þ)9Ûî’ÜºÈ v“i=(Ów<"…w——wö”9»ž”y»Såç{úËÂß’Eæ¹ÅûFJé§ÏÈë2ä—UåÍjÓGæÊ[ŸçÙi®;T(¿þj¶Tž'¾^(åæúªÏ§ÈÒª ö¹úZ/ïJ‘ü›év–‚í©+Ñ€¥¨Qõ0¹ëV•Ùè7}ŸSVV&•••Í×+**ì¿àX `,€°~H`)Ft~ë<\g.ŸÉga—]gsÑùø€¹Óç#X ¬Þ½{ˆ ¬ÜSÌJËårÙÛ·mÛ&Ë—/è5Nœ8aGÁ._¾ÜX™¦“뙓³gÏšuh:ÀX `,€°Vû–îT\iúõë'GŽiq_¸è.¿¬¬,©¯¯o>€Þ‰‚Íÿ ñþ·,€°ÀX `µ;`éÈSmm­ý=ßtäµk×Úß«««ínÃp™:uª=z´Å7ýê6çø,`Í0*}oºûQgþl«ë‰¬pí»¨O0`…[^W̉¬Xúc"+\û\:$°Â-¯ŽW;&°b鉬píK4`¥]L »¼ X¹»!Û÷ X¿ýío-²4sæÌiqüU†®¤b<åC¢`ýzq’l›ù¨xëy9ºm•Ôõ¬(ë¾Þ%_o.‘Ý¥Ãå½ìÛä7ÏÿD~;¥£ü.ïnùÝ‹]dkA7ù/=hŸûáìòQQOÙ>ÿIÙ± Uv¾Ú_>ùÅ ÙU<Ä>Ïë£eï/ÇɾåãåÓ_M²ÓªZ3Uª×æÉgë ä`E¡|±~¶|¹a®zg½~`u®ì]–aŸÿñ+iòáœÇeëôûåfÝË#XŒ`1‚Å#XŒ`%Âi.]ºd‘¥»G-5551cˉï®Cç¬Áº"ŒC`u½ÒÕväDXQv3—HV” `,€°ÀºÁÀÒÛ[ý\V¾À*7>ýæ ï·‹‹‹ÀX `,€°Ú'°B:RUh:êîÝ»›toM`%Òy°ÀX `,€°®X +ß“‹¦™Õ’%KZ|‹ðf:“;ÀX `,€°V«ƒuåÊÙ¼y³Yÿä¶8ÑèX³‚Ò„ú?°ÀºqÀ:–üS97è ¹øržüùÿ¼-MŸ{V”Ù»K.//‘óã‡Iý½·Jý}wHýCwJý£wIC÷ÎÒг«4ôz@ú>"çžJ’s’åÜà^Ò˜ÞWGö“Ægž–óãÛ矟4J.䌕 S2å‹Yr¡ G.N±Ó¼XT ^(—^™%—~1W.•.°×/ÌÈ• ¹r>3ݾ¦N£áñ{åËÑI««¾ëm¶Ï\~³ÔöAMÓŸÉ• årqîtiñÔµý±Gix¢›4ô~PR•sý{Ø>}nhoûøÆÑ¤qì Û—ÎO)糟±}ë åB~¶íkAûâ‚™rÑôUí»ç' ·¯¥¯m§•t·ì™;`¬ø9È]1µ~ýz³=™Ð ­HNÓ°Àj}`Ýæ¾Í®\ãuE©hW”+q¥n}/¯™‹¶Í~äÌ¥Ü\¦›ËSæòC`$Ó\†›‹¾–¾öƒæÒÉ\2O1‚°âôŸ=9þ¼lÚ´©ÅîBF°ÀX `,€°¢LSS“lÙ²Åþ“fßÞõ,Ý]ê `,€°ÀXË/Î Eý¿EËÉEÀX `,€°–Ïy°1;wîlµó`,€°ÀX `Ý´Àz÷Ýwí¿Æ‰§¬ÖÖ–ÙiòõÈäø¼‰R·áM¹ôÅ> é‹Õ»äë^“ÚüaòeßÛäË´Ë¡ä«Aä«¡]äð°®òõ¨åÈ3È‘qÝå›ñ=¥&ëI©ÉN•£“ûÉ^xZþ0m¨Ô —c…cäø¬qr|N¦œ˜—%'æÈ©ESäÔ/òäTi|ûÆ ùvÙl9³b®œyk¡½~rQ®}¼N¿frš|“™lßgõðN `,€°Vûý!ÀjÀzòÜ“rŸ¹è‡ûMsÙ'ûâ¦Múx¾®˜{˜‹¾ÏŽÂÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°n°êê$®žÝÅ5u²\}{µ¸«<ÀrïÞ%WKKÄ5"]šî¸Eš~z»4ýì§ÒÔùgÒtïÝÒtß½ÒôðýâzìaqõxL\)Éâê"®´¾â&®!O‹+}°¸F×3£Ä•1V\ã3ÄõÜDq=Ÿ-®rÅõâ âz)_®Îœ!WçÌ’«óçÊÕWØëú~\™ÏŠkøPÏk&'IÓý]¥>é^€°ÀX `¬ø–bDû¯¾Æjs9 â®M}Í%É\t#ÚÅÅÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `¬› X6lœœ³­ê))))fû“/'Nœðœ¡Ùí–ÒÒR)**²xÒjhhX `,€°ÀX¡’k6,ûöí³˜ÒÚ¸q£Œ9ÒÞWVV&•••Í­¨¨ýŸc `,€Õ€Õ©÷äá!'%{öeee£|zðЧMþ,%«$}ò·rK÷Z¹=ù˜ü4å˜ü¬Ïq¹;õ¸ÜÛï„Üÿô ûÜdž”ÇG””gNIŸŒS’6á´ |î´ ÎùV†å~+£òÎÈØü3’1ý;™8ó;;­Üy”~~Vò•¿ø“Ì*ý“Ì}£^,¯·×'›ûŸ-øÎN?5ó´tqRºö?!úX `%ò.Âäädû3Ólpêêêšo?{ö¬¤ë?tX `¬ö0‚u§ËþjýŸÒ+WŠ|úi|ýÿj¾voíîÚý;vt,€°X.—KÞyç»ËP£» }£#\þ·,€°ÀX `¬ IJJ²¥Hjlll¾-ÐãÁÊ)ßÌ0kŠÚÚZ©ªª²3H¶ÕõDV¸ö¥^LM8`…[^WúõK8`ÅÒXáÚ×ÁÕ!á€nyuìx5á€KLD`…k_¢+íbZØå•hÀzÌõØ Ù¾·‹¬¦¦&{Ð{¶®Áb‹,F°Áb‹,F°Áj½è7 5YYYR__ßâ¬ÁºÖX `,€°ÀXÁ“——'ÕÕÕvtêÊ•+ö,…‘¦Ülðô›ƒ¾ß",..X `,€°ÀX¡òþûïÛS5è7SÍ'YOÃpéÒ%΃°ÀX+nõãƒ$µ&[ N•ʺú÷ä³Ë_ÛmÖ®‹ÕòÚ×ɰÚ|¹í˾òã/ӤáÒé«ArÏWérßáòÐ×£åÑ#cåñ#™òÄ7¤OÍsòTÍó2ðèTô‡dØòeä±—ä™c3%ãøl™x|ždŸ\ ¹'IÞ©_ØiÎøö ™ýí2™{f…,<ó–Ìüv©L=µX&ŸkŸ«¯Õë›,yäëg¤ËWC¥ÿ–"€Å.BÎä°ÀX+ÎG°nõÌo‡ëÖyV!á–][®F E¦L?Þó\}­'žyè!OŸéÓçÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°V«kËì49œ~¯Ÿ“)gÿ{¹\üb¯=0õòןIýo~-§—¼$5“Óä˾·É—i?–C:ÈWƒ:ÉWé÷Èá÷É×£’#c•#™Ë7žšçúHÍóOÉÑ©å/ ’?ä“c/”c3Ÿ‘ã³3äø¼‰rrA¶œ\”+§~‘'§J äÛ7fÈ·ËfË™såÌ[ íõS‹¦Èñ¹¤¶`¸ÜO¾ßS¾õ T¸;¦þØá÷#í®z@ìÛÚ$U—‡=W„ízx˜<øõ(yäÈ3ÒýÈ8éùÍxy²&ËÌÛïèdyú/ÈÐ?L“áµ2æX¡Œ;>K2Ï‘¬ó$çäB™r*øA¸z]ÒÕƒvõ ^}M†N³û‡y `,€°ÀJT`i›º˜‹>™¹ì•½?øÊ^¯k›ue¯óDç‘Î3‡‘´)àÖmn»rÕ•ñÛo‹TUÅ׆[7 úš:{îÑÀ€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX­¬~%ÿ-,_%ÿw³¬Øû©ì;yÚ󃟒]»exåF¹­x‰üøµ×åÎ%K¥ÓëˤËÒåÒmÙ¯äÁ«äÑ_­–î«ÖHÏÕkåÉ5ë¤oY¹ô_÷dPE¥ ©üo¾~£ŒÞø?2îþW2ßý¿’õ¿ïIÎæßÈ”ßüNòÞß"¿Û*…[?Ù|(ó>Ü! v|l¯çþæ}ûx~¿uòø[kíû¼«dÀX `,€°Vœ`=yNî»Ïóá~óM‘}ûâgå¯×é?õ”HbßgÇŽ°ÀX `,€°ÀX `ŰîXüK»·A÷ ¬­ª–ªo¿Ÿ½ïü_Z¹Á¾¦N£ë²Ò}qÀX `,€°Vœ`ÝêéGñºÞ2$úõÀX `,€°ÀX `,€°ÀX `,€°ÀX `Ý,ÀÚ¸q£™y“M'ë)½zõ’¢¢"9{ö¬½O®+--µ·)ž´ÀX `,€°V¨äš9»Ïl¹S.—KÖ®]k:Ó{_YY™TVV6?¶¢¢Â,䀰ÀX `,€m’““íÏÌÌL©««k¾]G¶Òu ,€°ÀX `¬È³ÿ~;ª¥IÑ%äåò¿-^€õðóÉÃù;${Eµ¬ÜZ+ŸÖ4xÎòÕŸ¤dÓ7’¾x¯Üš¹EEî|n«Ü•³M:ç~ ]§~(¼ø‘Ýó9þ!0’™)2|¸çµôµ|P¤S'½ýÀX‰¬Ý»w=õB¹ùôé7}¿EX\\ °ÀX `,€°BE© T w,€°ÀX `,Îä°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀXíXwý”ô8×_r/ÌÕ®Mž-Ú®¿ì•’ËËeØùñrký½rGý}rgýCrWý£Ò¹¡»tmè)4ô’G ~Î=%ÉçH¯sƒ¥ocºôk)O7>#ƒÏ³Ïu~’Œ½#™¦HÖ…%çBL¹P(y_–‚‹ERxñç2ëÒ+2÷Ò/dÁ¥R{]ßOÆ…\I?Ÿ)©Ã¥û¹~Ò­á éúÍ@€°ÀX `¬8Á2Ñþ«¯±zµÈñצ¾}õäÉžh—..€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX+þ€ÕçXºtiê&®ñ²ì꛲×íiÔgîƒR~õ×2ÝU(O¹úË-MwÈíM†WM†WM†WM†WM÷ËM†W®$éî2¼rõ’']†W®~Òßexå"C]Ãe„k´Œq“g]™2Á5IžsåÈd×TyÁõ¢ä»^’Wgʬ«sdîÕù²àê+öz®ëÉtM”a®‘’æ É®¹¿éa¹ëJW€°ÀX `¬8Á2mêÒÅóüeËDöîýáWöz]Û¬+{':tžé<Œ¤M `,€°ÀX `,€°ÀºQ©­­•Þ½{·¸ÍívKii©Y}úÍAßo,€°ÀX `¬hNÓà{º΃°ÀX `,€Å™ÜÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `,€°ÀX `¬„–Ûí–ÒÒR)**²xÒjhhX `,€°ÀX±¦¬¬L*++›¯WTT˜…\°ÀX `,€°bM¦éýuuuÍ×Ïž=kp:ÀX `,€°ÀŠ5)º„üvúß°ÀX `,€°¢H’®©"¸Í9>ËXÍÒ{ñÅmååå5ÿÞ×Ç{Ñ|( ä™g Ìt L‡È—‚ýY`>¬2rd¾ùàÈ Až<8ßT}ŽVzz¾©[Æiå›I­‘#=Ï=ºÀÖ˜1ú3ßNKkìX­|Ó ledhy®ëýúx}¾¾–¾¾NoĈü°í›8qš}¬>üø|ó!(ðiS¾}_m“§=C†x^È¿¶ÇÓ¦|Û®¿¶Éó|§MÚÎ_Û”oÛõ×6åÛv9mÒÇëó69Ó ·¼&Nô¼O}þ„ ž¶xÚ”/YYf9z^óé§ó}–•gyýuYy–—¶gøpÏã‡÷¼ŸQ£<í5ÊÓž¿¶ÉÓ}}møqùÞåä´Éóx}¾§Mžéé4béú~ô¹:½xíúºúú:]ýü„kߘ1Óìã=Ÿ±øïú~Ã-/]îž6åÛåä´éùçóírÒþài“§?j{<íÊ÷é‹7®?êkÆÒõó¤Ï?Þ³¼òó=ËO?sú>ôýFÒµ}žvzú£¾?O;ó›û¢§žþ¨íó´3ß§/z®ÿµ/4^tZ:Ýçž ß÷£¯Ÿ•{l‹Ï—ÎO?pÚ”o×çá–—ó~ôõãµ?zÖžùùì³ù7dû®×'«ònæ,B!„:q¬¬¬,©¯¯oq Ö`g$„BX±¥¼¼Ü~sЉþ^\\Ì’#„BÀŠ5ÑžË?¾ÇfQEQEÅZí X„B!Œ`‘6 zÇŽ´ƒvÐvÚA¤?Ò€uófäÈ‘ö´ÉÉÉ2nÜ89xðàu¿Þwß}×jÏùþûïe„ a_3žÛ¡_÷Öo–jùžõ?ÑÚ1mÚ4Û†ž={ÊÆé‹í¼/Òé¬ã£/¬Œ?Ö«W¯æßõßþèöz^/ÚST„zŽÞ×£GÛi¹N>ÿüséÝ»w·£¦¦¦ù}ÒÛg_¤?ÒY7ÆG_X š/¿ü²Å‰Éª««%77×Þ®WªÌ5Û·o·ÿH?ÐS§N•óçÏÛÛ÷ìÙ#cÇŽµ·gddØ×ó]ßë|ð}í¨Ë–-³÷é_%ÁNæÿZ‰ÚÍÞ½{C„˜íЕÌêÕ«åwÞ¡/¶ã¾H¤?²nŒ¾°4[·n•ùóçÛßOŸ>-999¶Séíú»ó?9"£G¶C£Ú‰´Óþö·¿•£GÚ!ÓcÇŽÙǽÿþû²`Áû|ýé;½®º1b„–ÿ}¡Þc¨û¥z~´ìììçLK´vlÛ¶Í®`´Ñ|S–¾˜x}‘þHdÝ}`%hôôÚ)t¨Y;¥v çöM›65?néÒ¥-ö+¿úê«¶ƒ¾ñÆ-´î<ÏÿùÎu}¼ÿþi½oóæÍ!ßc¨û¡‡¶§ñp¹\ ¿û"ý‘þȺ1>ú"ÀJÐÊîÝ»ÃÞ®M;ªv ÚÚZ{¦zý ¡¤¤ÄÞ§Ñ¿ t¨VŸ7sæLÙ°aCó_z@¢Þ®w^ÇwZ;wî ú§OŸn‡vµúáÖ¿¼®\¹Ò.–‡þµ© ž8q‚¾ØŽû"ý‘þȺ1>ú"ÀJФ¦¦Úo¢„»];Žî³ÖooŒ3Föïßoo׎¤JoסSÝ·­ÏÓ¡[} =ØoÉ’%Í·Ÿ_¯¼©©Éj_§¿û§OŸ>ö¯­@÷'B;|ÛÈí˜4i’}ÜÀeË–-ôÅvÞéôGÖñÑ!„BÀ"„BX„B!`B!„,B!„€E!„B!„BÀ"„BX„’ Ñ.BÀ"q½¡rªµ¢ÿ6Aÿ•þï,ýÏïZþ±§þ 6”Ä·FÚ£é7ôGKŸÔHìŸiÓ¦]w¡?,ˆ@Ì)++“ÊÊÊæëöVù¦ªªÊþ0V $–>Ísè$–þ¥ÿŸOÿïžý_}zÛõöú#À"7ñFnРAöFõë×Ïþ*ç~ý‹JÿC»Þ§ÿǪ¦¦&àkeffJ]]]óuý¿WéééÍ×õÿ]éS¿|ù2+²¬Y³FæÌ™#óæÍkñ—¾>gýúõ¶¯¦¤¤ÈK/½$çÏŸ§?’Vë“ååå²hÑ¢æÛV®\io‹d]©}ièСôG€EHàÜ'Ÿ|" h¾_W0ΊAÿY§þGõ@Ñ žotHܹM7YYYR__óè¹9€¥é¯^½ºùúöíÛeáÂ…ÍÏY»v­\ºtÉö/ÝðÓI«õÉÆÆFÛOœ>¦ÿ¤Xd]©#PÁBX„œM=Þ¯+ç¾H^˹mêÔ©rôèшF/ÈÍÓ÷ƒ¥»ctƒæÛçz÷î°ß455IŸ>}è¤UׇŠvÝ…§£Sà£]WÒa…bêð´|éìzqnµRˆæ/´@SV"ô½Háª?ê®ú#iÍ>©‡A设ÜÜ\©­­y]IXä&Žó×—®L6nÜh‡Çõƒ °|‡¸5zŒŽFD³q%KwÉhŒä9:‚¥:ú#ií>©ß&ÌÉɹæ¾hÖ•ôG€EnÒèñzðºFwÁè_fºÂЕG,ÀÒãatX݉þìøV $XÐܵï¸\.{]G|Ár¾á¥÷ëcW¬XA$­Þ';f׉þ÷E³®¤?,r“EWº[eìØ±røða{›¼®Ç²héAƱ+Òó¼°!áú€öAýë^ûé¤I“š7t:⚟Ÿ/={ö´ýxÙ²eÍ£?’¶ì“Î}Ѭ+é‹B!„,B!„€E!„°!„B!„BX„B!‹B!`B!„€E!„°!„B!„BX„B!‹B!`BHk%))ÉV<$##CNž<É<#„,BHën´JNN–qãÆÉ{ï½—PXˆõõª««eÚ´iA_³W¯^ÒØØðþ+W®Hzzz› 'P{!‹’`ÀrrâÄ ™:uª½móæÍíXùùùRUUô5{öì)Ë—/xÿÒ¥KÛ =‹€EiGÀÒœ={ÖÞ–““Óâ1_~ù¥deeÙQ.'{÷î•ììlIII±¥¿ïÙ³§Åë}þùç2yòd‹}LQQQ‹éƒƒÿí|ðݧ¯£?7lØÐâq¾I“:ZjÞ¬X±ÂN¯®®®Å}:òµjÕªk¦n~8?|ø°Åó¸ÂÂBihhÙžHžKX„8–sû AƒZ<¦G-¿oß>ûûôéÓåòå˶ôw½mÿþýö1‡² 1b„ÔÔÔØÛ+Ñë“O>ižVSS“œ9sÆ¢-\;B¥¤¤$ä®P}½óçÏKïÞ½å7Þh¾]w *4µ½ÑÎçñÛ·o—Ë%n·ÛBQoS4…jO¤Ï%„,BHëØ±cö¶yóæµxŒbI7êNt„Ko¯­­½æ¹:r£qv7:À4ÝH€åLëÈ‘#QA1Xô¸ª¡C‡¶hO ×Ô¬Y³Æ"ñ»ï¾³×KKKeýúõAßc¨ùè}*–œÝ‘‘+Üs !‹gÀ:zô¨IMMµ»ÐBáEÑ FºëJ£~½î™hìubÖÚµkm…›7±0`€,Z´ÈîÔÑ8ç}øN7’ùéîÐhŽÁâØ,B!$NåìLKK“… 6Ö\/°B=&`{X¡8ÒÑ«`ßô–FG¬tþ <Øî¢‹æ=,B!ä&V,qv‰én0':êå»KLF×ëþ癊dtÊ÷1&L¸fZ‘`/PÞÿ})..ŽhÞø¢Lq¥ï#Ø{Œd~DФ@íX„,BÈM,=®Jo×o±]ºtÉ®¿ûsµeË–æÇèýúm· ´xMç@p%RÈ(ÆôtßÇè7õ÷™3gÚƒÜëëë[œ¿Êœ~Ã.\ô±Á  X‘Ì›HæG¤H Ô€EÀ"„ÜÀrP¡£3:⢥¿ë·é|£ßt8p`ó‰;õt¾¯©èš5k–ÉÒÊÍ͵ÔûOwÛ¶mº«N·zõêæûôØ(礟Îî¸@ÑÇåååEC„,BØ8Bø °aã@Ÿ!B!q¹qؽ;t݈ìsi‰‡b „®ÖMRRRØûB=`°‰ûÃ-·„®H7ŽÎíçÏŸ—E‹I¯^½¤gÏž’-ååå!ßÓ-a.¦¨â:ÑÎho>øàINN–íÛ·_\¼¹0uítt¹úgÚ´i©­€Õšý"Ôó!‹,ÝÈ–••‰Ëå·Û-´·µ6°.1«¨¨HŠ‹‹íÏ6|sQkðàÁräÈ‘æÛ¾ûî;{[$˦-ÕZX„,Bâ X:Ú¢°ŠnóÞzÀ tŸïFûòåË2tèP{]¸dÉ;Ú¦UZZjos»qãFûØ””y饗ì蜓5kÖÈœ9sdÞ¼y2kÖ,ihhhu`é|LKK“¦¦&éÓ§O‹ùºsçN4h%Ô÷áßv½O—E¿~ýì(XkKG%u¤ÒÉÊ•+ím€¤à0`€}¯“'OŽè>ßÇD:¯Ã+Ð< 6C€,B!7X999vc[]]1´n$°ªªªšo_¾|¹¬_¿Þ¾O­wÞyG–-[Ö—.]²÷)¶t$ÉAÁêÕ«›_Gwß-\¸°Õµÿþæ¾oß¾}Í÷¥¦¦Ê‰'ì{Û°aCжòÉ'0­ ¬ÆÆF Ogþ 8Ðþî¤ÊÊJ©¨¨h†ë±cÇ"ºÏùͼŽtËwžšŒ`°‰;`éè‚nuÄEG òóóåôéÓ­¬`ÇÚ„–oÎÆ]£#EΆ×ÿ±ú¸Þ½{ÛßuW˜bÂw¤É¹¯5URR"[¶l±¿¿÷Þ{öºï{ß³gOD˦G­,‚S¤£A>ý絎úÎãHïs~F3¯£9Ë™'æ#À"`rÃlc­˜òŽ ¬]»V222âf+Ücv„»/ꃩc–îºrvK*\õº“Ï?ÿ\òòòìî.Å—ï{Öù®Çléãu”)üÈNlÀª©©±HÊÍÍ•ÚÚÚ€ó:šåìù‘ÎëPí 6OÍG€EÀ"ä†käÈ‘-ŽEr6þ#FŒ:M=¾åF+ƒmÐu´ÊK7´ÁF°œûtÔ#Úã̢ч‹C‡µxœ\®`ðÒ]šºOßg[K£ß&ÔÝÂÁæµCæ?¯"¹ÏùͼÕÎpóÄw>,B!× ¬ôôÐåÝe5sæL©««³×Ïž=k¯oݺÕ^×o :Ç_ié14züP¨¤‡¹D³!ÍÌÌlÞ­¦ïM ,=Kwq9ïUÇò=ëèÑ£öw½oóæÍòÆoØëzеï±C:zö¬(gôªU«®9½…Žj{4z\’°¯ÓvF¶œöé.4ç¸"EEx`¥‡©àó_›Òi’~q@µÒ÷ræÌY°`AD÷9?£™×¡ÚlžšçØ,€EÀ"$&`ÅýzþŠ+dþüùö§î*ròî»ïÚ ݦ#Wº[&ì7ì¢L¨ÝFú^ÆgG²t´Ma XºÑÖ¼¾O-ý]G±œÇê®/½]¿a¨ß6üþû«pÔヴ“&M º1Ž5cÆŒiÞíæDÛ6vìXû»Î{}o:£mû¶O©ÇÀiéûlíÓZDsšÅ‹¨¯ËCßëŽ;"ºÏÿ›†‘ÌëPý"Ø< 45º»Ðùv)À"`rC€u3$!ϵEø °aãÏ wÜá3DX„°q „Ï!‹6„ð"`ÂÆ>C„,BnÞEQ×W„€E!„Ò&ùÿü°Ð³àŸIEND®B`‚IntervalBarRendererSample.png000066400000000000000000000363701463604235500365360ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/category/doc-files‰PNG  IHDRXr5˜<¿IDATxÚílwzÿOªªªªªªRUUUU*Uý£ªªªRÿ¨ªÊBF¦D–]"¨¹B~‚@¡œ_°iŠ9s†ûšpp¸>Ÿsö™$=虘8ÅÂv ¤.Æ±ÏÆø ØÂõóý<÷³™ÏŽw×k³k^oé­Ýù±³³3Ïμæù<ó™¯B!„J«¾Â&@!„°B!„,„B! !„BX¡_ùJŒù]!`!4뉕mj oûýʯüŠüÆoü†¬\¹R>üðÃ]×O?ýT6mÚ$ø‡h×ã—ù—å×ý×åÉ'Ÿ”šš™˜˜È:À ÛÆyyy²k×.Xtq÷·û·ü/€…€5;<.€åw}}ý‚¬ç믿.¿ôK¿º.¿ó;¿“‘û)l]ÙÆ ‘ ‹âÿ©°XYYÉ…°°¬0ÿÑýѼ¯ãw¿ûÝ„×g1–s¶C™e`!´A+Ó—›é€¥úßÿý_©««‹¯Ító©Ÿÿüç¶ÉÌû999òŸÿùŸv}ÆÆÆ,€ýöoÿö¢,§žžÉÏÏ™ö›¿ù›2<< `!`!”¹€åŸ®'®^xA~í×~ÍzÛ¶m) ®\¹"ëÖ­“ßÿýß·ðñ«¿ú«òÇüÇRVVfa ly {ö쑯~õ«¶9ì/þâ/b¦¿ùæ›ÑÏê{ï´?ÿó?·ã‹‹‹å¯ÿú¯m³’~¿ÂÉïýÞïIQQ‘…’¹VÐ4k®ßïߺݼÓÿôOÿT>|8c9Úôä¶Aªû7ÝëžHÌ̧õW3]k²¼J4Þüߣõjº¬?øƒ?°ŸÓº¶·Þz+ ÒÚ$«ûV§é< ±~>}ÚB Û^úªÃ:ÀBÀB–µžýãü'³ÙNG ­ÒßÈÈHÜåùO¦þ¦±¿ù›¿‰~ö‰'ž˜1ïlë¨>yòäœKOÂÞñGŽIx%òýþmð—ù—)5“¥{ÿ¦²îé,…ÿw8%oAÓ‚>óÚk¯YP š¦78mß¾=ô·éôÇ)Œ€…¬O¦î$”èr;;;gœ`4pðàÁ¸'žÙ¾ÿ¿øEÌ Tßß¹sÇfp4càm¦sMGò'bA@¿[³%%%32@©V_}õÕÛg®ßï·fdü™—Tö"û7Ýëžl60(Nu=üÛ#•xK¦Ök¶íå‡>Í êwë«wü{ï½`! ¡Ç°º»»m·³ÕÅ[îš5kA@O<ÞñÚÜoYZßäoFô×áüà?Ÿüä'1ãtžxò¿ÿ7͵Èýk_ûššRíJ@F;ôž0ë·~+æÎ2w‚ÓBl)§²nºNÞiº>éþ~'=P¼ê¹îßùX÷t–6³y§/Y²$¥xKu‚¦…eÍXÀBÀJèsþ«ú ;ÝR=ÉhïåñNžšMròßu¸sçN¹wï^ÚË¿Lw"Mç÷;éÝ~Þéög¸mµM âSý]ó±îé,áüo¼‘R¼¥°È`!`!+í€åÏ8éãZüµR~é [ûpJö$ã¯òº££#:_¼ÌB:K ïýM”z‹º¿ßIãŸG·a{{»®õ_zwÛïþîï&ôñÆÏǺÏ3aËÐ~°ü1¥=Õ»¯dã-€å¯ÁÒ®"‚ö5XÀBhƒU²'ÚÙüîDî|ñâE;^›˜¼ãµãK}ŒŒfôÊþܹsÑ[ãS9Éh±²~½ëÍ+]Žž¤ƒà,À ³ö™”îï÷J»SHWók¢5téZ÷°˜Iv;Ÿ={6ú¹dã-€åo¶Ô»U³ÝE˜ì>DÀBè1¬xýÿ¨jkkgô:ž®Ç¹A†k.ròw+άxÖLÊ||¿_þGƒ¬…é©îßù\÷°˜Iä;þü}p%o鬰ßÖ€…,„¬¸{ÍèÉX›k\ý‘k"S}öÙgvºÖôè]hÚ<¤'AÍ6iOáÞÁ“9±èr½óêrõ®7ÿºiÍ’;éê:ú³ s,×c·6yï^L÷÷I»Ðm¨=Œëwè6PøPÈÓì«JeÿÎ纇ÅL¼m¬¿K›Ø4;öxœDã-Ý€å2YzW«·'wN¥'w X!„BÀB!„°B!„,„B!`!„BX!„BB!„€…B!„,„B! !„BÀB!„BB!„€…B!`¡ ½òÊ+1Û7o–uëÖá4yÓ¦MlLlbLl>2oݺÀÊÀÚ±c‡üÇüN“?ûì3¶&61&6™ÿéŸþ À°Ÿ»ººØ˜ØÄ˜Ø°, cŒ1°€Å•ÆÄ&&61€`QK€1±‰1± `X˜+1LlbLlXÀÂcŒ1€`q%†1±‰1± `X˜ZLlbLlXi×ä䤹&ׯ.|MÞ{¯]~ö³kÒÙù¹}=yrz¸«ësim½f>ßn_ÝðéÓíráÂ5éîþܾºáO>ù\>üðš´´´ÛWÖïÕåëë•+ÓÃ:Ý}Þ-_¿o!¶gVVii©m¾ÓZ,ŸK—.E+''gÆüAãf›7lšÖ]åçç[ëûññqY¹r¥LLLÄ­Ï"ƒÅ•ÆÄ&^ì±ùÜsÌ9ZäßþM ÔLŸßnÜùà‘š‘íÛÅœ/EV­ù»¿Ó’‘¯}MdÍmzê)‘gžyöY‘çŸY·N[”D¾þu‘^Ù¸QdÓ&-wÞ²EdëV‘mÛD^zIäßùæ7§¿ã[ßšÖé:¿~þ¹ç¦¿C¿“ Vi“`yy¹…*-t¯¬¬4;gÍ‚e°üjll4SZŸ`aŒ1^ì°Y7 š‚Ô;þTÍÖ‰©Z¥{2@aó&³…;­ÁŠWŸ`‘%À˜ØÄd°¬¬¬«W¯šöœ U]]½ãÏ{÷Ÿf¸‚šÃæm9Nz× Þ=èTŸ`ÑŸ ÆÄ&~b3[ëÌ™31°|EíêmfË*Ð$Ú–°RíË+í÷Jû¿rò×gXd 0&61¬Ì,ï gj‹žÜ1Æcj°,`‘%À˜ØÄd°, À¢–cb›€`a²˜ØÄ˜ €…,Œ1ÆÔ`X€E–cb›€`QKÀvÀÄ&ÆÔ`X€E–›“Á°€…1ÆX€Å•ÆÄ&Æd°, S炉MŒ©Á°€E–cb“Á°, cŒ1¦ À°0YLlbL ÀBu.›˜, À°¸ØØÄÄ&€`XcŒ15X€`‘%ÀÄ&Æd°, À¢Îcb›€`q%†1±‰1, ÀÂcŒ©Á°†††¤¼¼\òòò¬KJJäÖ­[vÚÔÔ”TUUIEEEtÃŽŽ.'lÞ°i­­­RXX(ö½SOOÙé›,²›˜ €•}€õœÙJ'Nœ°¤nll”§Ÿ~ÚN«­­•†††è¼õõõràÀÀå„Í6mµÙ;ýýý&hnHQQQt…±¶¶6‹:Œ‰ML €•}€µtéR V^åææÚ×b³'†‡‡c²] DA ›7lšû.ïûÁÁA³ãÖÒDH–cb“Á°²°šššäرcÒÒÒbÁæÜ¹s²wï^;M› ýÍ€þqNaó†MÓ¬U___L«ººÚ®€…1Ƙ,++K¡JkªVš=¥õXwïÞµÓrrrfÌ4n¶yæiÝU~~¾µ¾·ë111·>Ë VÎ^•••Ùô¬»‚ÐW†Sîííe{0œ‘ÃÎsYÞoüBŽ3™·åÒ¥/äöíÛæ¢oL.\¸-?üáæ‚ï¶9>ŽÉ«¯Þ–;¿Ý»oËþýcòíoß–]»¾0ÓnËc²oßmÙ³ç 3í¶:4&ßùŽ~î 3í¶¼ùæ˜TUÝ6ßõ…™v[¾÷½19|X?7½ü#GÆÌ szX§ëüúùÝ»¿×^Óá;ìï,Öãæ\——­€5Û3+k£Ùš=rÒZ©­º(ƒå—Ö€Õhä„Ôg‘Á¢ÎãtÖ¹¼ü²H]È'ŸdÎIL—áNbÅÅØßÔ`‘ÁÊ6ÀòÖ@ùÇ)|ŒŒÄÔN­Ò=ÔâÍ›ÌrÖ˜ÈЬZ¼ú,‹:ŒÓ]ç`aj°¬´K»ep@£™¥#GŽX@QÕ™#ŽÞñç½û¯²²2°Y0lÞÙ–ã¤w ê݃NAõYÆ8Ýu.¦ ÀJ»´?*Ý z7¡Zk±\ Ölý`y+Õ~°¼ÒZ0íÿÊÉ_Ÿ`‘ÁÂøqÌ`=ûì]{óóÛo¿Íþ'ƒ`Ñ“;=¹SK€15Xs¬§ž9½öÚkìj°, ÀâJ c2X± `XƘ, Sƒ`X,LlX˜ €`XÔ`aL €ElX€Å•Æd°,L À°0ÆÀÂÔ`XÀ"ƒ…1,‹Ø°,‹ZŒ©Á°05X€…É`abÀÂd°,`aŒ©Á°¨Á°,‹+1ŒÉ`XÄ&€`X˜,L €…©Á°€E c2X˜ €`Xcj°, `X&ƒ…É`X˜ €`XÔ`abÀÂÔ`X€E c2X± `XƘ, Sƒ`X˜ &6,L ÀB5XSƒ`›€`q%†1, “Á°b”““hÕÔÔ”TUUIEEEtÃŽŽ.'lÞ°i­­­RXX(ö½SOOÙé›,Œ15X5XVög°†‡‡Íyƾ¯­­•†††è´úúz9pà@àçÂæ ›¶Úìþþ~47¤¨¨(:ÂX[[€E c2X,+û«²²Òì¼ìûb³'¸œ†††,)lÞ°i¹¹¹Ññîýàà Ùqki"¤ cj°,j°¬ì¬[·n™ð|t8//oF3 \"ó†MÓ¬U___L«ººZššš+g¯ÊÊÊlp»+}e8õáÞÞÞ9/¯µõ sPè•÷ßï2Š^°¯|Ð%ÿþï½òÓŸÈ»ïöš“\—}=~@Nžì•þ°KNê5˰¯?úQ—œ>Ý+?ûÙ€´´ôJCC—}½pa@Þ{¯WÞy§KΜé•?°¯]röl¯\¼8`¾»×ÄÕô°N×ùõóº<ý¾––ÿagÙ°ó\–·nÝDÖÖþýû³÷xrâ„ ´·KïÅ‹ÒÕÖf/fƒ÷~ô‘t57OO?wNzß}WºÌNé=yRΟ·¯]õõÒ{ê” èrNŸ–®ýHz[Zd µÕ¾v½óŽô¾÷ž \¸ ½gÎH×l_ô{Ξ•.s^é}ÿ}0ßm‡uºÎ¯Ÿ×åéòÍ÷ÜÒu™ãïÕß5×í•­€5ñ“õ€¥WEL`z볂j¶âÕrÅ6Më®òóó­õýøø¸ ˜•211·>‹ VvÖ˜Ýb€IäÊ•éýtõªˆ¶ëÿ&è ñ÷ÿåAâé§§ú§ÖƒÄúõÓŠ ¦ú§ÿ‡˜>Püã?¦v+.~À¾¢‹ Ö<ÛnÌ;Å\-%~0˜Ï­=è²tÙæ»F_|‘,2Xé“â ²³RéÊ`ùÕØØh§&´> ÀÊÎZ Sƒ`=.€E €Uyy¹\¾|9fÜF³õGFFbj§Véž PؼÉ,g‰ ­ÁŠWŸ`eo-€…©Á°À¢ Àú¿vÃlÜ-3Æ×™#ŽÞñç½ûO‹àƒšÃæm9Nz× Þ=èTŸ`‘Áš/ÀZ·îž477Gýî»ï²ÿÉ`X!` ™Ïz§N"ƒ`¥¦mf‹^»v-©¾­ü€•j?X^i¿WÚÿ•“¿> À¢k>‹;µ¨Á°,ý®ˆÙðÞí­Ý Qƒ`Ñ“;€E À"6,ë, ÀÂÔ`XÔ`XVš‹, ÀÂd°,2X€E ÀB5X¦ À¢ À°,²&ƒ`‘Á°, Sƒ`Qƒ`XÔ`X€E À"6,‹ €`XÔ`X˜,‹, À°ÈX˜ €`‘Á°,L €ElX5X°È`X˜ €E À°, `aj°,‹, ÀÂd°,bÀ°È`XÀ¢ ÀÂÔ`XÔ`X€E–ÀÂd°,2X€`aj°,j°,‹, Xd°,L À"ƒ`Xu.¦‹Ø¤ À°,L‹ýO À°È`X€E €…,‹, À°È`X˜ €E ÀZ¬€Õ××g6âvÉË˳vššš’ªª*©¨¨ˆn¸ÑÑÑÀe„Í6­µµU ¥  À¾wêéé1;}3€E €…©Á°¨Á°²°ÌxÆì¼îÓjkkMÌ7D‡ëëëm )lÞ°i«ÍÞéïï7AsCŠŠŠ¢ó(ŒµµµXd°,L À"ƒ`e`iàÄ™b³'†‡‡£ÃCCCˆ’7lZnnnt¼{?88hvÜZš©Á°05X5XVv–‚ŽϲeËló Â‰k¾ó6º¦>ÿ8§°yæiÖJ›(½¬êêjijj°È`X˜ €E ÀÊNÀZ²d‰tttX虜œ”cÇŽ™Zj§åää̘?hÜló†MÓº«üü|k}?>>nf¥LLLÄ­Ïò‚•³Weee¶ýÛ¸¾2œú°6#ÏuyÅų°öíÛ—µûïù<(£?ù‰Œ™ þàÁûj‡Íx;ÝlˆQ³‘ÆÌ†y`vΘÙ8£fCÚx`~ÿXy¹Œš÷cf=xõUûç–Qó~¬¢B˜üØž=2ºk—Œ™÷ÞxCÆÌöýö·elÿ~yðÝïÊXeåô°Nß»WÆÌ¼£ú=æÿùÀ,ûf¾¹þ^ÿk*Ë[·n"ëk¿ÙvÙz<ÉFÀÒ‹þd¯7纽²°æ#~ `Ý¿_*ÍL3P^Ùh¶\cccB€åÏ.-]ºtÁ2X~é:×hä„Ôg‘Á"ƒE+,nXÝÀº¡3ð$6úâ‹d°È`‘Á"ƒ•¹,½’Q°rvºjvÅŠ³~þisæòÖG©žxâ‰(¤ŒŒÄÔN­Ò= °y“YÎZƒ¯> À¢ À°¨Á°¨Á°æ°4s¥õJšò7Åù³SAÒ;újSÂÿÕ]]¾|Y>lß×™#ŽN÷Ϋٲ fÁ°yg[Ž“Ûë݃NAõY, À"ƒ`‘Á°æ°4³£på͹¦¾Ù¤íË j:¿ÂÖÇêËû}©öƒå•ö{¥ý_9ùë³,úÁ°`Ý4ËÔ—ó[o½E?Xý`X™XÏ›­¦@qóæM < 3Ú§ÕzsÚª[ƒžÜ1,‹,€`‘Á°’“fv¼5X^ëݦ À¢ÎÀ°¨Á°RÐ¥K—ÌÆÙb›ø´îJ‹ÊÛÛÛy!&ƒ`‘Á°,2X°¨Á°,j°,bÀÊÀŠ×<¨Öx­Ñú@÷€…É`Xd ,‹ €•xOìaå|îÜ9 Sƒ`Qç`XÔ`X‰è³õ¼x:íÙ³Gnݺe½Óób¾£À"ƒ`‘Á°,2XVZ+^_WÚÏÔvÝ2FwïÞûh Sƒ`Qƒ`XÔ`XjOç~)T¹ÇË„=ûÀÂd°,2X€E À èý\Ÿß§¸q=ºk/îåååæÇ¯>‚F‡,L €E €`Qƒ`% «x…íÍÍÍv} ³>¯ÀÂd°,²€E ÀJPúh-b×z,mÜ`‚¥¥¥…~°05X5X€E €…,2X€E À"6¬ ¬ááa›½ÒÌUP3!€…©Á°¨s°,j°¬àBA*^‡£&ƒ`‘%°,2XV’Z¾|¹466F»b˜œœ´ãµçöݦ À¢ÎÀ°¨Á°’ïËuÏPXX(ׯ_™`a2XY À"ƒ`%)­¿RRV•š=qüøñè…Úl`aj°,ê\,‹,+I9s&úœÁ]»vÅÔ_­×³€…É`Xd ,‹ €•ºîÝ»g!K›µsÑ GèX˜,‹: À¢ ÀB, À"ƒ`‘Á°°ÂºbXÌx°¨Á°,j°,j°¬¬žžž„úÁŠ÷C•ÞXUU%Ñ 7::¸œ°yæµ¶¶Ú» ì{ïú냬,2X€E À"ƒ`-`Å#¯Wë/ŸC¬¶¶ÖÄ|Ct¸¾¾ÞZ²ó†MÓuìïï·õbEEEÑyÆÚÚÚ,j°,‹,‹,+3K‹ÜõÂn·GR¬b³'ôQ?`ÍÇöÈú»5ˤ€¥]=ă·x@6oØ4­»ÊÏÏ·Ö÷ãããôòâÕgeJËô»3ø$6j>C‹ Y2Xd°²/6ýÖýLô¢É`i±¸öà¾téÒ´<ìY³Kº¬…Ê`ù¥ÏUtÏPŒWŸ`-,`Qƒ`Qƒ`X ›Ö#¬òòòÐz¬T:+ÕŒ‘j£ Ö‘‘‘˜Ú©Uø ›7™å¬1Á­5Xñê³,2X€E À"ƒ`Í;`i&H³>“““)}~› ¾ŽŽ›UR¸Òîþõ_ÿÕN«3G½ãÏ{÷_eee`³`ؼ³-ÇIïÔ»‚곬äCæóÍÍÍQŸ:uŠ~°,ú°¬,ˆM¿>l×%Ó½¨Ká(UËP ½bÅ 9räHB}[ù+Õ~°¼Ò~¯´ÿ+'}€•ÂÂ,?S²€E‹Ø$ƒ…³°´þ*Õì=¹XÔ`XÔ`XVvÔ`X Xšñ ËîX,‹,€`‘Á°ÒÜ£;€`eJ €`QƒElRƒ…, À"ƒ`‘Á°,2X+`!‹,‹, À¢ ÀJ3`Ý¿ßvy°lÙ²˜Œ•ö=¥Ý7X,‹,€`‘Á°’”>“*¨Iðª XívÀ°¨Á°¨s°,j°¬$¥™+}0²ö5对Ҿ­,‹ €E–À°È`XIJ!ã:õ–>nÆ=SÀ°¨Á°¨s°,j°¬$ô¼ 2Š›7oZÀRØêîî6' õ¶‡v À"ƒ`‘%°,2XV’ÒNFãuѠϰ,j°,ê\,‹,+]ºtÉÄÒÛ$¨uWza{{;Ý4Xd°,2X€E ÀB5X€E €Elâ ,íë*¨Öj» ¢£GX,‹,€`‘Á°’•³ Ì?22Â]„5X5X€E €•Š¢>|8c¼Ž£,‹ €E À°È`X)f°^6G‡ëׯGûÃêïï7ñ»ÓN°,j°,ê\,‹,+I?>n7 çΰ,2XY À"ƒ`¥¢‹/F»iОÝ7˜`Qðâ.B‹,‹, À¢ ÀB, À"ƒ`›83žEèÈ3€`QƒÅIŒ, À¢ Àšƒ´×v÷ ¹êìÙ³1°¦Ë¬ªª’ŠŠŠh ŽŽ~6lÞ°iú¨ŸÂÂB)((°ïzzzÌd3€E À°È`Xd°¬…¬k×®Ùî>ýôÓ9-GƒAïFôVmm­‰ù†èp}}½ ´ …Í6mµùé]7Ì­¨¨(:ÂX[[€E €`Qƒ`Qƒ`-<`Å»ƒÐ9ݼyÓöÿþý˜Ï›ÀŽ Y RؼaÓ´‰ÓÛÜ©4ÿ¥µ4’Á°,2X,œ€¥MuŲ́=¿»å9åååÍhôKdÞ°išµêëë‹É`UWWKSS€E €`Qƒ`Qƒ…³ó.—Là)àx-è}Ø¸Ùæ ›¦uWùùùÖú~||Üü¿VÊÄÄDÜú,/X9{UVVfƒÛ]Aèë| g+`%û{{{{ç¼½Š‹f`íÛ·o^ãg>‡³°:”ôïužËö:}úä£äâÅ^ikë21úCyóÍ£RYyTöì9jXਔ”5?ëKoßþ¥KK¿ô·¾5í—_þÒ;v|é²²/]^>m]¾³ë4W?«ËšþŽ·b¶Õþýû³*½ÃÙXzÑŸìïÕãf¶?2qø‘–6ëUVVʲeËb@F³Rú èT3` •ÁòK×¹F/Cê³È`QƒE‹ Ö|Y÷»w½2Ñd°¨Á"ƒµ€¥W2AM‚WMÀ®X±"¥&G/¤¹¦CW;µJ?ÎÝŒñæMf9kLpk V¼ú,‹, ÀšÏ: À¢ À²ÒÌ•Ö+iVÈß—Ê޽˨««³wüyïþÓlY²óζ'½kPït ªÏ°¨Á°¬ù¬s°,j°¬hfÇõåÍé£sæX³õƒ•載ö§¥ý^iÿWNþú,‹ €`Íw–àÝwß•æææŒ¶®#€E ÀšgÀzÞ™…vµà:íîî6' õ¶ëzr°¨Á°¨sÁ± `%)ÍìÄ+Tïèè°,2XY `›V*ºté’‰¥-¶IPë®´¨¼½½gXÔ`XÔ`a‹Ø°€E À°ÈXÄ&~Ä€uúôi›¹ÒŽ9wïÞmë°,‹,‹, `›VŠjii™Qs¥wÛ¹>¤,‹ €E XÄ&€•Â݃ú˜í´S¥=žkíÕÞ½{,‹,‹, `›Vªý_ù›µSN}n€`‘Á°È`a‹Ø°RP¼‡.§Ò{;€`Qƒ`Qç‚,bÀ ¬xã,‹ €E–XÄ&€•`%b À¢ À¢ÎXÄ&€`Xd°,‹,€Elb:°¨Á°,ê\0€…, À"ƒ`‘%À± `!‹, À¢ÎÀ"6, À"ƒ`Xd 0, `X5Xu.À"6,`‘Á°È`qò°ˆM À°¨Á°,ê\,j°0€`Xd°,²À"6, À¢ À¢ XÄ&€õˆÕØØhâp‹,]ºTòòò¤´´TnÞ¼i§MMMIUU•TTTDmtt4p9aó†Mkmm•ÂÂB)((°ïzzzÌd3€E À°ÈXÄ&€•}€µÍ_GG‡… uSS“‰ãµvZmm­‰ù†è¼õõõ6Ђ6oØ´ÕæÔßß/7nÜ¢¢¢è< cmmm5X€E €ElX‹£‰077×¾›ÀŽŽ²@¤°yæ¹ïò¾ŒB€E À°ÈXÄ&€•Õ€599)ÍÍͶÉP¥M†þf@ÿ8§°yæiÖª¯¯/&ƒU]]m3i5X€…,bg5`åääXk0ݹs':.h¾xŸ7.lšÖ]åçç[ëûññqóÿZ)që³¼`åìUYY™MϺ+}ál¬dooo×îÝÿÏ|÷˜46ŽÊ'ŸŒÉÏþs9>"ßÿ~ÄlªˆYÝi?ûlÄlŽi¯[1›dÚ_ÿzÄl–ioÚ1?uÚ[¶D PEÌϘÍ‘_Œ˜M1›$"%%³™"f3E¤´4b6UÄ@Þô°N×Ïè2t™Óßs%f[íÛ·o^ãg>‡³°:”ôïuζýó8™˜xpø°Œ<)£—/K$‘Èùóùþ÷%bb3bâÃÚÄ_ÄÄŸµ¹¢Š˜´6q1q1q1q1±1±1WVÖ&Þ"/¾8mó]‘’‰˜˜Œ˜˜Œ”–JÄÄeÄ\íÙanb7bâØ.K—m¾ë#³|olêE²¿W›ìïô g}ëáǶèÝ—/D+¨è¾¦¦&´>‹ VöÕ`ù­Ù!ïze¢É`Qç‚çß™~ 6É`¥UzG¡j£9ŽŒŒÄÔN­Òƒr€ÂæMf9kÌWk°âÕgXÙYƒ`XÔ¹àÇ °ˆMKJJJ¤»»Ûf•´yNk°PTuuuöŽ?ïÝ•••Í‚aóζ'½kPït ªÏ°²³ À°¨sÁ`aKΞ=k»jÐ ‘ÖAi Ý»w/¡~°¼€•j?X^iÓ¤öåä¯Ï°OëðáÃv]2ÙºŽ,<¿Îôã€ú­·Þ"6,zr°²£ XÔ`áÅlbÀ°,®Ä,2X§ÙÄ&€`XÀ¢ c `!‹ °ˆML À°,j°0€E ¦ X€Å•€E c2X€`Qƒ…©ÁÂX€E XÄ&&ƒ…,‹, `Qƒ…1± `X,‹ Æd°,`Qƒ…,Œ1°,2XÀ"ƒ…1± `Xµ5XSƒ`!‹ °ˆML À°,j°0€E ÆÀ°,®Ä,2X“Á°€E °ˆML €`Xd°0€E “Á€`a‹,Œ1€`Xd°0,ŒÉ`X€E °ˆML °,2XÀ"ƒ…1± `ͪ¦¦&süÛ*K—.•eË–IEE… ÙiSSSRUUeǹƒàèèhàrÂæ ›ÖÚÚ*………RPP`ß;õôô˜ã÷f‹, `aŒ¬ì¬mæÀØÑÑa!hrrRŽ?nŽƒì´ÚÚZs÷ËÝoìÍC‡±ÿf˜áÀᬬ#GŽØÌ·ˆ}!2X~566JMMMh},2X89{c SMlb2XxÑe°4´Ðݯæ$?22S;µJ!@aó&³œ5 ´+^}€E °ˆML €•ñ€ÕÞÞ·ë…ºº:{ÇŸ÷î¿ÊÊÊÀfÁ°yg[Ž“Þ5¨w:ÕgXd°0€Elb2XVÆ–BRéË X©öƒå•ö{¥ý_9ùë³,úÁ©ùÌ™3ï÷ߟ}…1¦'wzr'ƒ…1YŒ‰M À°¨sÁÔ¹`LlXÀ"ƒ…1±‰‰M `XÔ`aŒ1Æ€`q%†É`LlXÀ¢ cb›€`‘Á˜,&61€`XcŒ1€`Xd°0YŒ‰M À°¨Á˜:Llb À"ƒ…1YŒ‰M À°¨ÁÂc `!‹ ÆÄ&&61€`Qƒ…1u.›€`‘ÁÂd 0&6,`Qƒ…1ÆÀ°,2X“%ÀÄ&ÛÀ°,j 0u.›°È`aLlbbÀ°,j°0Æc À°¸Ãd 0&6¬9JÛ‹—/_3njjJªªª¤¢¢"z‚ ü|ؼaÓZ[[¥°°P ì{§žžÃ›,j°0&61± `e'`åääDíUmm­a…†èp}}½=A)lÞ°i« xô÷÷Ë7¤¨¨(:ÂX[[€E cb›Vv7ú«Øœð‡‡‡£ÃCCCˆ‚6oØ´ÜÜÜèx÷~ppÐ0ÈZš©ÁÂcŒ`åååÍhôKdÞ°išµêëë‹É`UWWKSS€E cb›xñ–8Þ¸Ùæ ›¦uWùùùÖú~||ÜpÉJ™˜˜ˆ[Ÿå+g¯ÊÊÊlû· p}áÛ¯¾*_¼}挌ý÷Kgg§tž:%ßûžtš`è\¿^: 4Ypê4ðÓià©ÓÀS§§NDÖ ¬ $Yê|ñEé40Õiੳ¤D: PY—–Nûå—§m`Òë4_?«ËÒåšïúwh~ÀJö÷ ,Èöd˜ád‡ý¯l†3eX›lô “ÁJ!ƒåWcc£ÔÔÔ„ÖgeJËo/ÈdªÉ`a²›d°1`mܸQFFFbj§Vi“W€ÂæMf9kÖ¬±5Xñ곬…,Œ1ÆÀJ#`ÕÕÕÙ;þ¼wÿUVVÎ6ïlËqÒ»õîA§ ú,‹ ÆÄ&&6¬¬ë¦ÁÛ]Ãlý`y+Õ~°¼Ò~¯´ÿ+'}V¦–çgºðƒП ¦¯!Œ‰M eî]„\‰aLlbLlXÆcŒ,`q%†1±‰‰M À°¨%À˜ØÄÄ&± `X˜+1LlbLlXÀÂcŒ, ÀâJ cb›À°0µ˜ØÄ˜Ø°,‹+1LlbLlX€…1Æc ÀâJ cbcbÀ°0µ˜ØÄ˜Ø°€Å•ÆÄ&&61€`aŒ1ÆÀ°0Wb˜ØÄ˜Ø°€E-ÆÄ&&6, ÀâJ cb›l ÀÂcŒ1€…,®Ä0&61± `Xµ›˜ØÄ€…¹ÃÄ&ÆÄ&€`XcŒ1€•éà255%UUURQQa!F=::šô¼aÓZ[[¥°°P ì{§žžÙ¼y3€Å•ÆÄ&&6¬ÅXµµµÒÐЮ¯¯—$=oØ´Õ«WK¿Ü¸qCŠŠŠ¢ó(ŒµµµXÔ`LlbbÀZ\€U\\,ÃÃÃÑá¡¡! DÉÎ6-777:Þ½”µk×ÒDÈ•ÆÄ&&6ñ⬼¼¼Í€þq‰Ì6M³V}}}1¬êêjijj°0Æc¼ø+'''¡q³Í6Më®òóó­õýøø¸¬\¹R&&&âÖgyÁÊÙ«^xA¾ùÍoZ—””Dß3Ì0à 3Ì0ÃÙ7¼uëV2XÉf°üjll”šššÐú,„B¡¬¬7ÊÈÈHLíÔªU«’ž7™å¬Y³ÆÖ`Å«ÏB!„ÊjÀª««³wü9éûÊÊÊè°·é/lÞÙ–ã¤w ê݃NAõY!„BY X³õƒå¬TûÁòJû½Òþ¯œüõY‰Ê[›…1Æãì÷¢,„B!2X!„BB!„°B!„,„B! !„BXh¡ôñÇËsÏ=g;^ÕG ¹^ïÃïñF™$}òüòåËÙÁÄfÆHŸ‡ªçXºt©,[¶Ìv3£$#bóQKŸx²eË›úT”ÒÒR¹yó&;ÀB©êúõë²bÅ û”v•þ¡>œõ€¥ë猈ÍLѶmÛ¤££Ãöã799)Ç— 6°³‰ÍŒŠMµ^ ¬]»– `¡TU^^.---q§ë•™^¡é²ÏŸ??^¼ŒcÇŽÉ®]»d÷îÝ3:~­­­µËЫ#ý#/Y²ÄŽ×“ÌÁƒíÕ¼Z;Õqî{îß¿íq_¯®œôȃ:,b3ScÓ‰Gv›Ä&€…¡´ m|||Öù.^¼h¯Øâ‹Ž=Ö^ò÷îÝkßkêùĉöÏ­ÒǹÏkZýwÞ‰^5577Kuuuô;Ü¢J—ùòeû^Çë À"6³56Uú=q"b3“bSaM—é…3`¡$• €¸«§ Ï郶ïÝ»s¥äêŸôaÛî áÿü“O>½òR=|ø0z@ò‡`ôJM¥¯‰ÔXÄf¦Ææ… dýúõÔ`››.æٴ;wî°³,4—+±‰‰‰ÀiúGÔ"\MwkÑ£÷ëÿûÓßÞ4¸÷ãÿ|ÐÊ¥¥ƒ¦é³%U/½ôRÚ„ˆØ\¨Øø@¶mÛft«¼]¾|¹”””ȉ'^FNNŽ5BX¡Ç^ŒŽ=*>”©©)9sæLÒÀ`!„,„2:yò¤…¢×_}Æ´—^zÉÚI¡këÖ­’››k3\¯¼òŠ ÇÀ•×N­­­òüóÏÛÏ-[¶Löïß/÷ïßN¿|ù²lÞ¼ÙN×ìÙ3Ï<#K–,‰YÆ¥K—ì<šaSëû?þxÜ]»vM6nÜh—¥ï÷ìÙ#uuu1¿ëí·ß¶ãoܸA `!„PúU\\lÁ$6FFFb†®>ûì3›ájll´ŸÓq~ÈñJÁHÇíØ±Ã~®©©ÉWVVÚéÝÝÝvxÍš5ÑuÐïõ.«££Ã¾ùå—-˜©õ½ŽS8ó~·3õ½{÷,h©Ðݽ{×NS@ÔõAX!”v)|¤Ò¬§p¢0£Ÿ¬-[¶Øq7oÞŒ~N‡5“å Í JAËrËP¸sêïï·ã4“åÿÓO?§]»vÙñš}S={ÖïÝ»—€…B°4¤°RZZ*………¶™ÎTA€¥M~A͇n>7ÝŸMòÎouœ®G¼ïV¹ì×öíÛí°6kBÀB¡´iýúõ3²C^¨rr™ ãÇËõë×err2!ÀrMvñšãÂà)€¥Zµj•]ÑÑQ»¬+V°ã°BhþÔÐÐSåÕlW ñ Ç5AóhÁ¹ŽÓZ« mذÁNŒ»l×D¨Í‚NÚäÔD$½;ÒÕpéëÁƒÙñX!4¿rà¡ 211aÇéz^hyúé§c@éܹs3 ÆeÃzzz¢ãÚÛÛí8æ êêÕ«¼T---vzyy¹-@×,“Þáç]¶6ç¹y4«¦óéû "÷ Ä4MêÝ…! !„æ]§OŸ¶ç®¶Jï²Ó;ÿ€Tz‡ŸB’+l¯©©™5 _«W¯ŽiºSi”.[ë­ôóÚD§w!:577ËÚµkírµVêÖ­[vÞzoWj}¯Ëuš­.íDU§±³°Bèñ“,í;+]ª¯¯·Ë“åË—'4o’?9ã~sSS“lݺծ‹®SEE… XX¡l¬íÛ·Kmm­LNNÊÔÔ”\¹rÅŽ{”RpqžÀÊ´ß¼mÛ6éèè°ë¢ëtüøqÙ°a€…€…ÊVÀÊÍ͵'öx:vì˜ìÚµKvïÞ-¯¼òŠŒŽŽF!èþýûRTTží3mmmfVÚLNO´æ°2ù7{×ÀBÀBe)`mÙ²EÞ~ûméîîžšå9zôht¸µµUöîÝ…‹®®®0ö™üü|¹yó¦ýžÆÆÆGX™ü›U—/_¶Y- ! !”¥€¥™…'žxÂfMJKKåÖ­[vÚªU«äÞ½{Ñy\]”~ÜpØgž|òIùøãþmóX™ü›/\¸ ëׯ§ ! !”Í€å•fZ´þGOð ‚aŸ¹zõª”””Haa¡´´´<2ÀÊÔß|äÈÙ±cG´yÀBÀB-ÀrÒz!•f_âÕ*ѰÏ8 J^^^FV¦üfmzÔB÷¤ö5BÀB-`½òJ¸}Ò»ç\-’Zë‰4‹¢ÒÂíúúz{g›J»NðÖ#ÁFØg´NI‹ÄuÜÊÈ'QÀJò'gÜonoo›µ°°B™XIêĉ¶è[k‘4‹£}0yOö Zc¤Ó7mÚd›ÔÂ`#ì3{öì±ß¡Ÿ‹/†‚UPs[º”i¿9¬‰ÀBÀBe!`!ö5BÀBqÒe_#„,„']ľFÀBqÒEìk„€…']ľFX¡´žtñãc„€…B!ôØëÿÞöŒ6÷ÆøIEND®B`‚LayeredBarRendererSample.png000066400000000000000000000674341463604235500363440ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/category/doc-files‰PNG  IHDRXr5˜nãIDATxÚì½ ¸Nu÷ÿÿ¹®ßÿw}•$ª;$™2% T‘2dÈ‘)‘ÔÉÐÀÓ¬h‰x$¢‘2Ïd(C)=JÃÃS_÷­ýÿ×q:kíãæl{ÝŸ÷êz]§ûçì{­õÙ÷~íé³]@ qBá@ ,@ @  @ ÁB ¼^‘; Ôµ@ ,Ë“P§ì¿“ÉÿüÏÿ$ (lÕªUrÅŠ±ÈoÇŽÉ^½z%Ë”)¼¿ÿûÿo2þüÉ-Z$ÇŒ“üóÏ?ÓfÌh=iРArèСÉï¾ûΛ±}ÓM7á3ÁB Xö+;S§N=©¹=õÔSÉÿóþú‰D,ÇL^õ…årÚ´ii=¦Y"GŒÏ €`¥§`]xá…'-¯gŸ}6×ïÓ'ÁÊ$]%+7}F XËĆœãÿ÷“S¦L9êû|:îdÄW_}œËú^êÔ©“\·n]ð>ÿý÷@ÀÎ>ûì´¬Ìøä“O’7>êg LîÝ»‚…@@°ˆ¼Û@uïÞ=yÕUW§OX xã\¬X±d›6m‚rÖ¨Y³æQ—OEdÆK/½tÔÏøßfÆG}”ìÚµk²xñâÁ2òåË—¬X±brðàÁÁ_{ï,>úh²D‰Á)¯Tþ&ÿ’%Kÿ–‡¯É9‘òì?Ë~ëXê|,uÈœÖß«ZµjòàÁƒùw|*éâ‹/—ÇrÇw$O=õÔ€~ýúרÉmN©JBØ¿­]»öQ?çþgÜŽ§ìËáëØøoeŽ-¾ÞmܸqGÄ›OÕòXàŸñ¿a¹ÍsçÎ $0³Žü•_ó÷!XaT°ÂN§Ì™3çÈ¿å £³þì²Ë.;ò³ë¯¿þ¨Ÿ±pqL˜0A½ˆ7b?ÿü³ø~²oSù›|áù±œ.K¥¶¼QÍúýñãǧ\çÜÖ!§¸âŠ+R:–}y,=Ù¿—]Jò"§¼,–•ìËÎŒcO9ý,§ßyüñÇÍég¼eÆ}÷ݧæÌ?ê( ÁB Nà‡kåÊ•ƒ !ïmóÞø€þr$3öïßìÙgýùgŸ}–üÏþsÔŠ÷Ây¯Ó¦MÙXðržþyq#¶Ñ>Ö¿9cÆŒ¿lXùè ÁÉ«k}þþ÷¿Ws³)²÷'ë‚Ç›ËD9è#‹üþ²þœëÄq¬ãéX®õ «cvéã#†¼lþšõûï¼ó ÁB ¬ VöàxíZ">’ý¨ß1—õ{íÛ·þ-ÍicŸ}|êDzï|mSÖS4Çú7›6mzÔ÷.\xB7äíÚµ Ä3Õ:‡ÕA þ;©ô>ûò¶lÙL7q,ו¨œòB°²ÿ›Ì÷v¬ã)ûrfÍš•£Èk?ãS©Ù§RàšslÛ¶í¨ïóéB‚…@,>’Ã×Áð‡=ŸÊ~$ûïgßèòQ ¾æ&ë÷-Zü[¾˜872’uCöÞõoòõ,Ù¯ûÉ‹ 9_çÔ»wï£~ÆGtR­sªÈu+'ÉÈþ>ò*§(Ž`ñ¸Heí÷ågTê|"6Š|Û?ׂgçesX2Xþø(Mnî¨ÔîšÌ«œøosoùofÖ6ótë±ô…óåSl|4Q{³ÎäίOäLî, 0Ùg ç) €`!)FNRóŒØ@@°ˆã®»îúËd£@@°ˆƒ¯ákcøú”Lž}öY@ ,@ @  @ ÁB €`!@@°@ ,@ @  @ aT°zè¡£^óÃy»víê5½zõò¾è @_Ѐ¾ä}ûöõO° ”\²d‰×ìÞ½Ûû /}Aoú’W<øàƒ,Ù¼y3>Ѐ¾ 7}`A°Á‚`aï /è @oÐ çÇúÐô /,ö.Ѐ¾ 7}`A°Á‚`aï /è ê€Þô‚ÁÂùqô /è @_ XÇ?þøcrÈ!É  0 ùÍ7ß@°°w¾ô½è +ÕèܹsrÖ¬YÉÇ̘1#Ù±cG X©Fýúõ±ÊõêÕƒ`aï}è zÐVª1sæÌäĉ“óæÍK~ÿý÷ÉE‹%Ÿxâ‰Å*“¬1xðààüpæà¯¾½þå—_¼Î?®¯³E}°¾Xy=oû¼äÔ5Sƒ¯+¾^Ùëõ?®ty™¯W}¸ ýW^÷ÝwÞ×ä`±TõîÝ;ªV­Z×cýöÛo8‚…½ ô /' çÙg ïXgÒïVÏž=“;wî<òzÚ´iɾ}ûB°àd Öaç,–‚•ÓõV¸ {è @_N¢`ý¯óŠŒ,¬3i(X<-Ÿ&äà‹ÝÇH s” /}`A°°Î@°RŒ}ûöÒÄw2|-®ÁÂÞúЗ“(X‡œW@°°Î¤¥`a&w€`A° ‚…½ €¾ /é-XWdl‡`a`A°p~}èK^ ÖW@°°Î@° XØ»@_ú’÷‚õ§ó Ö X,Á‚`aï}è‹9ÁúÃyEÆ6Ö çÇÑ€¾äµ`ýÇy ë  ‚…½ ô /y/XœW@°°Î@° XÁ‚` {è @_Ì Ö~ç[!XXg X,œG_ú’ׂõoç,¬3,ö.Ѐ¾ä½`ýî¼"ã#Ö X,Á‚`aï}è  ‚…u‚ÁÂùq€¾ /~ ÖoÎ+ XXg X,ì] /}É{ÁúÕy ë  ‚y/X¿8¯Èø‚ X,ì] /}`A°°Î@° X8?޾ôŘ`ís^ÁÂ:Á‚`aï}èKÞ ÖÏÎ+ XXg X,È{ÁúÉyEÆ€`A°°w¾ô‚ÁÂ:Á‚`áü8úÐc‚µ×y ë  ‚…½ ô /y/X?:¯ÈØ ÁÂ:Á‚`@^ ÖÎ+ X -«N:9ÁÂÞúÐ ë ëÅÞ½{“:u‚`áü8úЗ“%Xß;¯€`añB°FŒ‘\¸p! {è @_N–`}ç¼"c ëLš Ö7ß|“ìÒ¥ ®Á @°NT<þøãÉåË—‹b•IÖ}0ÈÁÚA‚uÈyCâß,pœ‚õ/ç!X M«_¿~ÉíÛ·C°°wÁ‚`a}‰ƒ`}í¼‚…uÆ‹i X8?~Âk; ÖAç ,¬/,Ö {ÑÖ7$~‡`a}9NÁÚã¼"ãÖ ¤&X:oHüÁÇ)X_9¯€` {,Ö—¼¬/W@°°Î@° X8?žŠ`m#ÁúÃyCâWÖ ë  ‚…½‹(ë?ο@°°¾§`ív^‘±‚…u‚Á© Öç ‰},pœ‚õ…ó €`A°°wÁ‚`a}É{ÁÚ异…u‚ÁÂùñTk+ Ö~ç ‰Ÿ!XX_ X,¬3,ö.¢¬;oHüÁÂúÁ‚`a`A°@^ ÖG$X¿;o€`ã¬Î+2ÖC° ‚…½  ëK^ ÖçÎ+ XXg X,œOU°~sÞØ ÁÂúrœ‚õ™óŠŒu,¬3,ö.Ž]°>$ÁúÅyCâÖ ë  ‚ X,/ÁúÔy @° XØ»HU°ö9oH|ÁÂúrœ‚õ‰ó Ö çÇS¬Ÿ7$¾ƒ`a}9NÁúØyEÆZÖ {Ç.X[H°~rÞø‚…õ‚ÁÂ:Á‚` ÄK°v8¯€` {© Ö^ç ‰o XX_ŽS°¶;¯€`a`A°p~<ÁÚL‚õ£óÖ—ã¬mÎ+2Ö@°°Î@° XØ»HM°~pÞø ë  ‚…u‚Á,ˆ—`mu^Á,ö.R¬ï7$¾†`a}9NÁúÈyEÆjÖ çÇ]°6‘`}ç¼!±‚…õ‚ÁÂ:Á¢ØµkWò¾ûîK6hÐ ‚…½‹.Xß:oH|ÁÂúrœ‚õ¡ó Ö™´¬ï¾û.Ù©S§ä–-[p @° X‚Á¬#GŽL®\¹§±w‘·‚õó†Ä—,¬/Ç)X[œW@°°Î¤¥`µmÛ6¬† §Y˜öíÛÁÂùñ'XI°¾vÞø‚…õå8k³óŠŒU,¬3i(XuëÖMnذ!yøðáä¡C‡’'NLfddä(V™dÁƒÍÏ4lþêÛk>Íêsþa¯‡5Ü+Á*¶§˜‰þÌZ9+Ùä§&É–¿· ¾6ü¶a$¯[héò²¾^²u‰‰õÇmr^1tóÐXÔÇ›o&oÙ2ùS“&Éo6 ¾Æáõo-ZÄêýd¾Þº$ºõɬ`e ­úõëã8qG°>ÈHº=άÁšºf*}ùõçlâ–g‚—#X›Gޤ-³¹dÍÔ©8‚¥EÇŽ“{÷î=ê{7Üp çÇO¬`}å¼!±Ë`v^aF°6:¯ˆ“`&q¹‚S©@Ï?ÿü‘ë®6nܘ5j çÇO¬`}é¼!±Ó`r^aF°>p^‘±2‚µ‰ë‰È¬\ÄèÑ£ƒ‹ÜùÔ ËÖÁƒ!X8‚Á‚`A° X,ÁÂLî ¶‚µk·ó†ÄçFk5 ÖA眳 ÁÚà¼"N‚uÄäŽÕ,Ž`Å@°¾pÞøÌ`ý×y…ÁZï¼"cE|ë¿$ w@° X¸+‚µËyCâSC‚õ§ó 3‚µÎyE\k# ÖŸ$ w@° X8‚Á‚`I‚õ‡ó +L°þ q¹‚Á'[°Ö“`ítÞøÄ`ýÇy…ÁZë¼"N‚õ; X,ÁŠƒ`}î¼!ñ±!Á:༂Á ¬$ w@° X¸ëd Ö:¬Ïœ7˜¬ýÎ+ÌÖçËã!X`í'q¹‚ÁÂ,K¬;¯0#X«WÄI°þMâr ‚â XŸ:oHì0"X«H°~w^Á9C° Xš`ýNârÇ* G°N²`­%ÁúØyCb ‚uœ‚µÊy ‚Á‚`á,Vz Ö¯Î+ÌÖJçËâ!XH°~%q¹‚Á¬8Öç ‰­†ëçfk…óŠ8 Ö/$ w@° X ‚µÝyCâ# ‚Á‚`A° X8‚•ׂµ†k›ó†Ä‡†kŸó 3‚µÜyEÆÒøÖ>; X,\ƒÁ‚`I‚õ“ó 3‚µÌyE\k= ÖO$ w@° X8‚ÁÚê¼!±‚Á:NÁZ꼂Á‚`A°@*‚µšë#ç ¦k¯ó +L°ö’8€ÜÁ‚`áVëCç ‰ÍFk% ÖÎ+8g‚µÄyEÆ’øÖ$ w¬„`A°p  ‚%ÖÎ+ÌÖbçq¬H@î€`A°p+‚µÅyCb“!ÁúÞy…ÁZä¼".‚µŽë{; X,p²k Öfç ‰†ë;ç,V˜`}Gâr ‚…#Xq¬MάoW@° Xa‚õ-‰È,®Á‚`A°$ÁúÆy…ÁzßyEÆâøÖ7$ w@° X8‚ÁÚè¼!±Á`ýËy…ÁzÏyE\k- Ö¿H@î€`A°Àɬ•$X8oH¬7$X_;¯0#X Wd,Š`}Mâr+$êÔ©“#,Á:¡‚µÁyCb ‚Á‚`A° XŠLA°p Ö ¬$Xë7˜¬$X_9¯àœMÖ»Î+â$X_‘8€Ü±‚Á¬Ö:ç ‰µ†ëKçfkóЏÖ¬/I@î€`åB°êÕ«ЪU«ä˜1c’þù' @° X¬“%XóWd¼Á‚`¥ùEî{÷î kèС9ŠU&YcðàÁÁ)²Ì£8üõd½~uÝ«ÉqŸKNýnjðuäæ‘‘¼žùóÌH——ùšó=™õÎíëáOºµÎŠm(ë~d¾k·óŠy΋ýú¯U!y-‘tOֱŋÕÔœ†®‹úo7.¹›Ä!>qKNuuÌ‘j¾Ì‡ó¢[ŸÒâ.ÂÇ'ëׯoòVÃoRQýùó5qkyFÒ­qÞXmèÖ.çfŽ`½ãdîm›-ª,VsŠÓ¬]ô†Sa™+a¯/Dªùî¬cýû÷'›6mjS°¾!Á:ì¼ó5#X«7$VA° XÇ)XóœL?¬Ã¶¨²HÍ)}ë°9 XyýúõKnذ!8rÅrõÜsÏ%ÇŽkS°þE‚uÈyç Á‚`—`ít^aF°æ:™~iÃxÈ•ßWsÊx/‚µšk'‰C*, ë9RÍw'+<æÌ™“ìÛ·o²nÝºÉæÍ›'Çoö.BVŒk•ó†ÄJC‚õ¹ó +L°>'qH…%F+Õ|?‡`ù5“{ïI°þë¼ó5!XËH°V:oH¬0"XËI°>s^Á9›¬·Lß[iÃø_[T^¨æ'ÁúŒÄ!–¸óíõ…H5_f9Ë#ÁÚC‚õ§óÎ׌`­pÞXnH°>u^‘>‚õ§- Ö§$©°8¬?Í‘j¾ŸB°<¬?œ7@° XÇ-XŸ8¯0#XsœLß´aüÕßUsÊXÁZE‚õI0å±³(¬?Ì‘j¾Ÿ@°<¬¯H°þã¼ó5!XKI°–;o0%X;¯0#X³Ì],Xÿ±EåjNq¬IRáý@°þcŽTóý‚å™`}I‚uÀyçkF°–9oH,ƒ`A°òZ°Ø¢ò|Oë€9 XJ8p 9bĈdÆ z®`Ïž=“3fÌðO°ö;o0%XK7$–¬íÎ+ÌÖ,'Ó§=m÷Û¢Ò|5§8 Öv‡TXÖ~s¤šïvkøðáXe’[·n ¦]ðJ°v“`ýî¼ó5!XKH°–8oH,1$XÛœW˜¬·œLŸ[hÃø»-*ÍSsÊx7‚µ’k‰C*¼ëŠÛë ‘j¾Û|,>r5sæÌ`¢Ð¬‚ÅÁs[A° X±¬Å΋!X¬ã¬7Lo£‚¥äÁ‚`ÅR°êÕ«ÈGVÁúþûïÕg ¦¥`í"ÁúÕyçkF°9oH,2$X[W¤`ýj‹JsÍÖV‡TXÖ¯æH5ß­>V—.]ÉÙ³gO X,[[¶lIvëÖ-˜¡Ý;ÁúÅy ‚u\‚µŒëC眳 ÁšédîlGÆ_lQém5§8 Ö‡$©0?¬_Ì‘j¾Ì²t¬eË–u VVøƒ^ ÖN¬}Î8_‚µ˜ë}ç ‰÷!X¬ã¬N&¬}¶¨8GÍ)cAºÖ>s@°BbýúõÉ>}ú§ùº+¾ƒpÍš5þMÓð9 ÖÏÎ8_3‚õžó†Ä{†k‹ó 3‚õ†“éÅ‚õ³-*ÎVsŠ‹`­ ÁÚBâ ï‚õ³9RÍw‹/‚…y°²ÖOÎÌÖ"¬…ÎL Öfçé!XmiÃø“- Öf‡T˜ÖOæH5ßÍ>–tz0û´ ^Ög$X{7p¾,Öq Ö&çfkº“ k¯-*ÎRsʘÁÚDâ sÁÚkŽTóÝÁòL°>%ÁúÁyçkF°ÞuÞXhH°6:¯0#XÓœLÏ6´aüÁßTsŠ“`m$qH…·ÝyöúB¤šïFŸOޤ²cÇ+‚µÀyCâ]C‚õó 3‚õº“¹Ã `UxSÍ).‚µœ¶›8¤Â£‚•j¾ø,X_ýu²_¿~~ Ö'$Xß9oà|MÖû$Xó7$@° Xy)X­iÃø-*ÌôD°¾3+…àù°¼›hôc¬o7p¾,VÊ‚µ”k½ó Î9=ë[[T˜aF°Ö“8¤Âì@°¾5Gªù2K},–«éÓ§û'X;H°¾qÞÀùš¬wœ7$æ¬uÎ+ÌÖT'Ö7¶¨ð†šSÆ;ñ¬u$©0+¬oÌ‘j¾ë|,í÷ì2ä…`ýËyƒ)Ášç¼!ñ ‚uœ‚õš“éq3mÿe ,%§ô¬™‚uŒ‚ÅG®˜Ü·oŸ_‚µëkç œ¯ Ázk®ó†Äã"ðדõºáf¬O7p¾'³Þ¹}=|ãð¤›é¼¡ØÛÅbÝÌ×`-q^1oã¼Ø¯/ü:\°>µE¹IjNC Eý7Œ—\ÂâSÁúÔ©æËlœÝútRkþüùdq :ô¨ë¯ºuëúûýû÷OîÚµë¨) âzk3í]Јéô­¬ûç«ÕcÍÔxì­g,ÈHºNæì/´4âÉÐ:b>‰7 ÁZì¼Â̬WœL—›h ~l‹r¯ª9ÅéÖbZÁ%ªñ8’þû¢¨½¾0Êrj=–ø4MÃþýûÉâSƒ;vLîܹ3å9µâ*X‡©©> –VX ÖNæl¾›å°-®#擘iH°9¯€`A°Âk}vJTãqtX`—QÁ:,çÔñA§Ö#m‹/lÿõ×_Oø4 q>‚µ‰ÿ!jªDÇGhPl÷ÎW«‡Áâ‹-Ùâá«!X¬¼¬ñN&¬í¶(7AÍ)cŽÁzŸÞï!Ï‹Úë sHΩãž ‹©2dHrÍš5G.tOwÁ:HM•¸•k›?p¾Z=VÇE°æ“`Mw2g±`´ÅCW‹ù˜¬÷œW¤‡`5£1¸ÍFk1mcÞ£ÏN‰ª<Ž ‚µÍåœn}À©õH[Áb±Ê:¹hÓ¦M“Ï?ÿüQw¦›`m¤Áÿ'5UâÖ¿Ó Øêœ¯VX Ö4's_lù§-¼JÌ'1È`-&ÁZ輂s6!X/;™Î,X[mqá+jNq¬…ôÙ)Q•ÇÑŸŸµ×æO9§CœZÅé| Öü‘œ;wn²_¿~GM4Ú¹sçä´iӎܘ.3¹³`ýAM•èÀ‚õ‘?p¾Z=b%X¯;™³>¡ý[°` ù$Þ€`A°ŽS°Æ9™ÛšÒüÈŽWs2#XïÒûýCà“"öúÂü!çÔa°Ç‚•5X¦¦OŸžìÑ£ÇÑÊÍ4 Öë5U¢Ã0úç«Õ#6‚õ ÖT'sÖZÑØâ«Ä|Ó o0<ÂŒ`u2`}h‹ _VsʘÁz—>;%ªÎ§÷{@`G{}aÈ9uäÔz,öñaÏ}ôQ²C‡i÷,Âhð罹J´Jƒb‹?p¾Z=b%X¯9™³øbËý¶r¥˜Obš!Áâ †G¤`m±…ÁZDÛ˜ùôÙ)Q…ÇÑ~íEìõ…Ù/çÔ~Sëá`ýöÛoÉ9sæuº0ÝŽ`±`ý›š*Ñþa›üóÕêÁšG‚5ÅÉœÅ[þÛƒ¯óI¼Á‚`§`ýÃÉtjBcp“-.«ædF°Þ¡÷ûomEìõ…ù·œSû ÖÁƒ“ . ž!˜õ‚w¾‹Oò,íé&X¿SS%nñL°8_­«L Öï¶HÁâ †G˜¬1N¦£AÁ*;VÍ)N‚õ}vJT™Gï÷w«‚õ»œÓ-Z´¬Ì E³ßE˜›ÉE­ Öü¿RS%nyˆÅþÀùjõˆ•`ýÓÉœÅ[þj‹ÁµÅ|S o0<ÂŒ`v2ÓüÀeÿ¡æ”1+>‚5>;%ªÌ¥÷û«ÀGEìõ…ùUÎé–ûZÅé> ÎÊ•+O؉¬YÎ+ÒC°n 1¸ÆeF™¬÷h3‹>;%*½Iï÷ε׿9§6œZV Ö:üßSS%Ú ¦A±Ú8_­±¬·I°&8™ÂhEÿÞWˆù$&,Þ#÷ˆô¬Õ¶(ó¢Áz‹>;%Áú^`ùöúÂ|/çÄ‚¥Õ‚•f‚õ-5U¢õ «üóÕêaG°øZ€om‘.‚Å 0#X/8™ö,X«lQæ5§8 Ö›ôÙ)Qi½ßoÖk¯/Ì·rN­ïqj= Xi&XßPS%Z¤A±Â8_­±¬WœLa¾à[Üw…˜Ob ‚uœ‚õ¼“i߈Æà [”y^Í)ãM#‚õ½ßoÖžk¯/Ì7rN­ûC°¼¬µ4ø¿¦¦JÜì™`q¾Z=b#XsH°Æ;™Â|¨úk[Ü{¹˜OâUC‚5Óy…ÁzÎÉÜbT°”œâ$X3é³S¢Òtz¿_ ¬6*X_Ë9µ¾Û©õ€`¥™`í¡¦J´ºŸÅ2à|µz¬ˆ“`½ìdÁÚc ,!ŸÄDC‚Å{ä‘‚u=Áe¶(ó¬ ÁZHÛ˜7è³S¢â4z¿{V'ìõ…Ù#çtóÝN­+k þ/©©­2hP,õÎW«G¬kœ“)¼’Vô/m1à21ŸÄë8ëY'ÓŽk©-J?«ædF°^§÷û¥ÀÊ„½¾0_Ê9ÝÜ‚å•`í¦¦J´dÁZ✯VØÖl¬±N¦ªÞm‹{.ó1#Xï“`ñ)àœMÖH'Ó®!Á%¶(=RÍ)cf|k:}vJTœJïw·ÀŠ„½¾0»åœZõuj=Þ‡`¥—`}AM•hy ŠÅþÀùjõ°#XËiEÿÂé"X|ÊÃ#ÌÖ'Ó–k±-JPsŠ“`M£ÏN‰Š¯ÑûýB`yÂ^_˜/äœX°´z@°ÒL°vQS%ZÞKƒb‘?p¾Z=b%Xÿp2…øZ€]¶è™˜OâC‚ŧ<<"}k‘-J?cB°Þ¥mÌëôÙ)Qa ½ß]ËöúÂì’sjy—SëÁJ#ÁZMƒÿsjªD ¬÷ýóÕê+Áãd ñ¡êÏmÑÿR1ŸÄxC‚ŧ<<ÂŒ`=ãdÚ6 1ø¾-X°”œ2fÄG°¦Òg§D…Òûý\`IÂ^_˜ÏåœZöqj= Xi&XŸQS%š A±Ð8_­Ëã"X³H°F;™@°>³ÅÝ—Šù$^6$X|ÊÃ#ÌÖÓN¦Íu4Ú¢Ôp5§8 ÖkôÙ)Qa2½ßÏ–œc¯/ÌgrN-ú8µ¬4¬U4ø?¡¦Jø(XZ=ìªþÄw×LÁâSaF°†;™ÖFKÉ)N‚5…>;%*L¢÷û‰À"£‚õ‰œS‹ÞN­+ÍëcjªÄMýiP,ðÎW«G¬ë%'SˆUl‹~5Å|ã Ÿòðˆô¬¶(õ” ÁZ@Û˜Òg§DùWéý~,ðþ9öúÂ|,çÔüN§Ö‚•f‚µš*Ö|à|µzÄF°Þ"Áåd ñžÔv[ô­)æ“kH°ø”‡G˜¬§œLëú4çÛ¢Ô“jNoÄG°&Óg§Dy~Þèv…çØë ³]Ωy/§Ö‚3fÌHöéÓ'Y¿~ýdƒ ’É={öÄR°VÒàßFM•hv7 ŠwüóÕê+ÁzÑÉz—Vôm¶è{‰˜OâFë=,>åᜳ ÁzÒÉÜÌ‚õŽ-J=¡æ'ÁšDŸ`mx÷{}a¶É9ÝÔË©õx‚¥G¿~ý’6lH>|8`æÌ™É:ÄV°¶RS%šõ£A1Ï8_­v‹Uoµ ‚uRkž-J=ž‚Åtß*°à{}a¶Ê9ÝÔ‚u£^½z±¬©©MY°æúç«ÕcYœë'SˆUh‹».óIŒ1$XW˜¬'œL«z4çÚ¢äãjNq¬ù´™HŸåøy£ Ì?Ç^_˜圚ÝáÔz@°Ž!:”|ë­·‚S†q¬4ø·PS%šö¥Añ¶?p¾Z=b#Xo’`=ïdÎäCÕ[lѧ†˜)ÁâS‘>‚õ¶-J>fF°&Ðg§D X[Þ9Ç^_˜-rN,XZ= X¹Œ:uê°@ýúë¯9ŠU&YcðàÁÉÝ»w'7oÞ¿æÕk¬MÔT‰&,XsüóÕêÁ‚•—ýÈúú÷5’ûªU ¾þç²ËŽzýv¥’î9's&ïIm²Eïb>ç?ö?j=¾Ÿ81’õ%ìu¨`µ"1.·Ø];©9Í[7ï¤Õ;ë믆Ç¿v;™V×Òœc‹’ª9nQB¬Ç¿7ެ?+ÆÓ‹‡µI`îÙöúÂl’sjÖC¬uó¢[ŸÌÁ:xð`pÑ{ïÞ½c{k#5U¢É]4(fûç«Õ#Ê#X‡iyã/¥÷û¬Â™¼'µÑ,XB>%tj=>ÎȈÏ,¾¦D¢öxÊõ°-š>¨æ—#X_tꤎ÷˜BK¬Ù¶(ùˆšÓø‹åZH$"=‚õ -S¢?k£ÀÛgÛë ³QΩéíN­Ž`¥|Gak9 þ¨©ûР˜åœ¯V(ë-Oâåšô~G*œÉ{RØâ΋Å|J<àÔz숓`w2µ^¦\Ù¢éjNq¬]$XÚq*‚5Ë,XJN/_$×âß ÖxÞ)ë9gÛë óœ –VVH 0 ¹eË–àÂ?þø#¸‹¥)®‚µžš*qcooúç«Õci„ƒÿ¿´<‰q5þðÚ3yOj½-z],æsþ§Öc{œ‹¯)‘¨5Žrý¯-š QsŠ‹`í$ÁÒÆˆ.Xu)×7mQr˜šÓ¸‹äZü¡`½CÛ˜—y§PàB~ZÃzÙgÛë ³^ΩIw§Ö‚ ,¦jà;7nœIlÿþý±¬u|ÞWÀGÁÒê¥`ýAË“*X¼'µÎ!‚¥Õc[œkœ“©5–rýÃM«9ÅE°>'ÁÒÆˆ{D¡…AÁº`˜šÓØ‹äZü±`ãB ùi ëf¬urNMº9µ¬4šÉ} þµÔT‰ï¤A1Ã8_­Q Ö¾VB`Ì%ô~ŸQ8ó-ZÑ×Ú¢çEb>çvj=b#X I°ø¢]‰+ÆP®lÑxšç‡ÚF‚¥÷w…æ×P®3lqÁP5§1ɵر`åB@°Ö ¼u¶½¾0kåœwsj=B°ÒK°VSS%nèEƒâ à|µzD)Xÿæk%F³`=­pæLZÑWÛ‚KÈçüAN­ÇÖ8 _S"qÅhÊõß¶h2#ÖTà|µzD)X?ñ¡|j8ýáµyE_j‹Û«‰ù¿ß©õØ'Áåd.{rýÉ7ܧæÁÚA‚¥÷B³«)ש¶(ñ€šÓ ÕäZ|±`â£îey2᥯Ÿe¯/ÌR9§nsj= Xi&XKXö A1Å8_­Q Ö´<‰çk8ýÙjyE_b ,!Ÿó2œZÍq,¾hWâ²ç)×mÑè^5§¸Öv,mŒè‚uå:Å%«9=_M®Å7 Ö\ÚÆ¼ÈGÝÊðdÂK¦že¯/Ì9§FœZVš Öbjª„‚¥Õ#JÁúžå <*X¯ÑоØ!‚¥ÕÃŽ`=K¹~o‹FLÖ6,mŒø&XÏV“kñuœ‹çº[,ðšQÁZ,çÁòL°QS%v§A1Ù8_­Q Ö·|(_`äÅNxmA^ÑÙ¢{51Ÿóîsj=6ÅI°ø®(‰ËFR®ßÚ¢Ñ=jNq¬­$XÚq*4eÁšl‹ƒÔœFV“kñUÄ‚õ_Ö P†çº[$0å,{}aÉ95êèÔz@°ÒH°ÓàghÀ‚5É8_­Q Ö¿hy#.yxmA^Ñß³E·ªb>çÝëÔzlŒ‹`½K‚ÅíJ\:‚rý—-®ï¯æÄ9Ç¡ö‘`icÄ= ÐôJÊu’-J TsQU®Åîˆë9Z¦Džëî=É…íõ…yOÎéú[Zw!Xé%X Ùš®ëFƒâUà|µz,Žpð͇òž¹(äÙj`-´Eתb>ç pj=ìÖ3”ë×¶¸þn‚õ! –6FÜ…&,X¯ÚâüûÕœž©*×â‹8 OŲP ¬Wí±PÎ ‚å‘`-¢Á¿€š*q]WýóÕê¥`}IË“~QÈ³Õ òоÀ]«ˆù»Ç©õø N‚ÅwEI\:œrýÒ û©9ÅE°¶`ic$\°&Ú"D°†W•k±3BÁz›¶1Ïòu£¥y*–¯¶×fœSÃN­+Ík>5U¢> Öà|µzD)X»ùP¾@ XÚ³Õ òŠ>ß]ªˆùëïÔzlˆ“`ñ]Q5Y°vÛ¢a_5§¸Öf,mŒ¸Á kS®lq~†š –T‹Ï#¬‘|ݨ@ižŠe¾ÀÄÂöúÂÌ—sjØÞ©õ€`¥™`Í£¦JÔïBƒâà|µzD)X»hyOVyôǯЊ>Ï«ˆù°`iõˆ•`ñ]Q5Ÿ¤\wÙ¢Á]jNq¬M$XÚqƒÁzÅçß§æôd¹ŸF,X#øºQÒ|#Ë ÁÒÆH¸`½l‹Áz¼Š\‹ ÖÚÆEz;µ«â$XÚ3"/~˜rÝd‹k{¨9ÅE°Ö`icÄ P¸þ2Êu”-Îë«æôp%¹D,Xñ9%ù2€é`²Çt9§kovj= Xi&XÓ¨©u:РxÑ8_­Q ÖüA(ðP¨`½H+ú4[„–V•q,í‘=D¹~`‹koWsŠ‹`­&ÁÒÆH¸`½h‹Áz¨’\‹õ Ö£´L‰’|À4 Ùë 3MÎéÚVN­+ë]ü¯SS%ê´§Añ‚?p¾Z=¢¬ g‹óz«9=PI®ÅÚkmcá;Ÿ.à£Ô¯ çörj=–ÇI°´‡p_4„r]c‹º]Õœâ"X+H°´1âú+X,%§!åZ¬ŽX°þNË”¸€ROxÖ¨`M‘sªÛ©õ€`…D¿~ý’6lH>|8yèСä¤I“’=zôˆ¥`- Á?™š*qU;#ýóÕê¥`­â‹·U ™™ºÀZÑ'Ûâ–òb>çötj=b#Xó§êψ¬>ˆr]e‹kº¨9qÎq¨ýr,mŒ¸»Ô¤\GÚâ¼;ÕœU”k±2bÁÆSË\ÀG©' Œ8Ó^_˜ÉrN×4wj=æC°Ž=êÕ«[ÁšDM•¸²-rà|µz¼áà_Á§¾V™™:¬I¶hW^ÌçÜ;œZeq,í‘ÕR®+lqMg5§¸Ö2,mŒ¸~ ×±`°E±^jN+ʵX±` å©e.à£Ô“ÁaIrN,XZ= XÇ7n ŽjÅQ°æÓàŸHM•ëà|µzD)XËXî¯2qb§iEŸh‹¶åÄ|=œZ¥q,í‘Õï§\—Ù¢ÎmjNq¬¥$XÚÑëÊõ[ë©ætEe‰P°Þ¢mÌÃ<µŒ@ >J=Qàé3íõ…™(çTç&§Ö‚u ±|ùòd·nÝr¼‹Å*“¬1xðàäîÝ»“›7oŠÀ_óê5 ÖjªDí6<ÈýóÕêÁ‚•—ýÈúz)‹ƒ@F¨` §}‚-BK«Ç¶áÃ#Y_øõÎ_Lî¥å}5thòc;þšùúýûïÐaT-ƒr]j ,%§UCïë±éùç#[_Öö衎×W!¬§mÁ‚¥ä”QA®ÅÊbÅ"[_Þ7.ùO-#P‚RO~¦½¾0äœê4sj=VÍ›Ùöß´`?>8µoß¾ØÞEÈ‚õ 5U¢vkäþÀùjõˆòÖ"ž9^àÞJ!3SxŠVôWlѦœ˜Oâv§ÖcI„G°~®V-y˜–™»Îy„Qµ{)×E¶¸º£šç,Õ㛆 #ëËâNÔ1âîR¨Ï‚5ÜÅîPsº·‚²ÎD|ëAžZF ïD½"ðÔ™öú¼"çtu3§ÖG°rl‡|¡{ܧi`ÁOM•¨Å‚õ”?p¾Z=¢¬÷ù¢z@°´‰ ÄÞè›—ó9«£Së1?BÁÚA‚õ-3'–™a¿ROÊuš-®h¥æÄ9Kõø(BÁšK‚¥×CÁª`)9õ¼P®ÅŒkmcðäÈçñ5c#†¬rNW4tj= Xi&XÏPS%.iòè4ƒóÕê¥`M¥åIÜQ>ä¶óÓÛ{ýM¥Å|κթõx'BÁÚF‚õ9ß!—K „L[éÊuª-®h©æÄ9KõØ¡`½M‚¥w»Bª”ë¶(r«šÓʵ˜±`ÝCË”8¯{F`ðöúÂ<#çtE§Ö‚•F‚5‡ÿpjªÄ%MBf¦N38_­Q ÖZžDPÁhï©7•RK«Ç¼ë#¬Oø±89°èô `+ö \§ØâòjNœ³TM Ö,mŒ¸î ` ± –’S²r-^X°úóÓ'ÎãSšÃža¯/Ìp9§Ë¯sj=æA°<¬Æ¼áœo\k2-O¢{¹ÛÎó¬f¥Ä|X°´zD)X[H°vðcqrà½ÓC&€­Ørl‹Ë›«9qÎR=6F(X³I°´1.XƒmQ¤ƒšS÷²r-^‹X°îæ§O„ Ö`{„–VVš Ö“ÔT‰Cý‘fp¾Z=D8ø'Ñò$ÁÒn;Ï?­èOÚ¢i)1ŸÂœZ¹ Ö&¬­´ÌœXpzÈüd`M²ÅeÍÕœ8g©",mŒ¸n WW¡\ÙâÜöjN,XR-¢¬7hÓŸ>!PŒOi>)p{}až”sº¬¾SëÁJ#ÁšMƒÿ jªDCf¦N38_­Q ÖZžD×r!wEåϰ÷€Ô¦%Å| ·wj=ÞŽP°> ÁÚÂ×åÀ;§‡ÌOV¡+å:Á—5Ss✥z¬‹P°Þ"ÁÒÆˆëªp Ö@[œ{‹šSײr-&G,X}ùé`=!QÀ^_˜'äœX°´z@°ÒL°£¦J\|CÈÌÔiç«Õ#JÁz…–'Ñ%T°îµ÷€ÔÁÒê¥`­#ÁÚȧ¿ràíÓC¦Ï¨Ð…r}Å—6Ss✥z¬‰X°´1âº(‚u¿-X°”œº”‘k1)bÁº‹Ÿ>!PŒ¸=&po{}a“sº´žSëÁJ3Áz”š*qQ£‰Ó ÎW«G”‚õ2-O¢ó…!·ç`ï©MJŠù¾Å©õ˜¡`­%ÁÚÀ§¿r`v¨`u¦\_¶Å¥MÕœ8g©«#¬7I°´1¢ VeÊõ>[œÛVÍ©s¹ã$X|ÄíQìõ…yTÎéÒk!XÞÖ,ü§¦Jø(XZ=¢¬±´<‰Û. ¹í<{Ïïj|˜O¡vN­Çìk5 ÖZZfN¼Å‚ÕG¡üm”ëX[\ÚD͉s–ê±2BÁšI‚¥×YáJ£‚¥ät[¹"¬>´L‰b]”çÃö7*XÊón/­ëÔz@°ÒL°†QS%ª_2qbšÁùjõ˜áàCË“èX6䮨@°Œ=¿ëÆ Ä| µuj=¢¬$X«xÖö˜qzÈôå;R®clQ³±šç,Õcy„‚5ƒK#î6…Ú•(×¶H´QsêXF®Åøk:mcîäÇ{ í¬<6¬öPžw[³®SëÁJ#Áz‹ÿÃÔT‰ê C&NL38_­Q ÖhZžD¸`õ£ýa[ÜXB,­³"¬e$XËYr`z®k´-jÞ¨æÄ9KõX¡`½A‚¥×I!¬{l‘h­æÔ±´\‹—ã$X,„ ô+`¯/ÌÃrN5¯`y%XQS%ª5 ™81Íà|µzD)X/Òò$:” ¹í<_{—`Áò)ÔÆ©õx+BÁZJ‚µ”–™¯ç™Ÿ¬\ÊõE[\rƒšç,ÕcI„‚5K#º`U¤\ûÛ"q³šS‡Òr-þ±`õâç§ í¤<¾¬ïéöúÂ(c»¤ŽSë1‚•^‚õ 5U¢Zƒ‰Ó ÎW«G”‚õ-O¢}ÙÛÎóßE+úƒ¶¸¡„˜O¡ÖN­G”‚µˆkÏÚžSò‡ÌOV®=åú‚-.i¤æÄ9KõX¡`M#ÁÒÆH¸`Ým‹D+5§ö¥åZŒ‰X°zòóSŠvä‡: Üuº½¾0Ê9±`iõ€`¥‘`½Iƒ5U¢êu!'¦œ¯V(ë9ZžÄ-eBîŠ:­·½ÇK4:_ÌçÌÖN­Ç› Ö{$X i™919Èôån¡\Ÿ³EëÕœ8g©ïG(X¯“`icÄuT¨Å‚ÕÏ,XJN·”–k1:BÁšFÛ˜;øù©EnU_Öût{}a”DZոکõ€`¥™` ¦¦J‚Õ×8_­Q ÖHZžD»PÁºÓÞã%BK«ÇÌë]¬ù<r`bþé3.lG¹Ž´E†jNœ³T… ÖT,mŒ¸[jU \ûÚ"ÑRÍ©])¹£"¬üüT"”Ç—Ýyº½¾0ÊãØj\åÔz@°ÒL°RS%ªÔ™×'Íà|µzDù¤óghymË„ÜuZ/{³_¾˜Ï™­œZ Ö|¬¹üA˜ãó‡LŸqa[Êõ[\Ü@͉s–ê± BÁzK#®ƒÂ,XwÙâœjNmKɵx!bÁº–)Qäåé½N·×FyZÈÅW:µ¬4¬™4øï§¦J‚ÕÇ8_­Q –öÐéÖ¥C®)9­§½Ùó9³¥þî(k. Ölž"Æå¹»³lk{á¾ø:5'ÎYªÇ; Ö,mŒè‚Užríc‹sš«9µ.%×⹈«;??U ,éé=O·×F{ZÈ•N­+Íë>jªDåkCn;O38_­Q –öÐé›C«‡½ÉùÁ*ØR÷ Ö¬7ùº¯“?äîβ7Û{÷EõÕœ8g©s#¬’`icĵW¸œëN[œs“šÓÍ¥äZ<¡`½NÛ˜n´L‰sÛ*“?÷8Ý^_m2ëÚN­ ‚ÁŠí¡Ó­J‡\´kQ°ó)ØBw”‚5›k5Ë—ò‡ÜÝY¶•½‡p³`)9qÎR=ÞŽX°´1ânQ°*XJN­Jʵ±`uåÔ ø(XZ= X¹ŒÝ»w'5jkÁšAƒ5U¢RÝÛÎÓ ÎW«G”‚¥=±e©‹vOënoöãëÎó)Ø\Fä´ëM¬i¼Ìxñ´›Ê´´÷ŒÈêת9qÎR=fG(X“H°´1âÚ)\VŽríi‹sšª9µ,)×â鈫 ? ^àÜ6ÊÓ5ºç·×F{ZH-§ÖãmVxÔ©Sçq¬{¨©•® ¹í<Íà|µzD)XÐò$Z” ¹¦ä´nöf?fÁò)x“Së¥`Í Áz–™Ï… V Êõ[„ç,Õã­ˆK#á‚u‡-B«EI¹Ãã$X­•§ktËo¯/Œö´+ X'T´â.XwSS%*^rÛyšÁùjõˆò1Ú3›‡ VW{“ó)‚uÆMú3"_P°¦“`Mæ9¯r`Äi!ww–inï‘Õêª9qÎR=fF(X¯’`icĵU¸ôBʵ‡-În¢æÔ¼¤\‹§"¬©´¹P/h¥LþÜ5¿½¾0ÚdÖ—;µ¬$X,V™dÁƒ§7oÞ¿æÕk~Òy?jªDÅ:Ê-çÝþÚS*a~ßBNœ¯V¬¼ìGÖ×Ú3›• ¹h÷´.ö&ç«_LÌçŒfú3"ß><’õ%Ø)©Q#ù*-3'ž9-äæƒÒÍì=#²Ú5jNœ³T$XQ­/¯÷衎×F!¬Ûmqvc5§fȵxºX±ÈÖ—·ÇKv¢eJ‚%MþÜ%¿½¾0ÊdÖ,XZ=–Í›ÙöG°òú©$XwQS%*ÔQn9oK²âÛ£­üPaÎW«G¤âTÙÓ´dÈE»§Þfoî˜zÅÄ|X°´zLðÖkÕª%_¡eæÄS¡‚ÕÔÞ#ŒªÕQs✥zLðÖ„NÔ1âZ+Ô,K¹v·ÅÙ7ª95½@®ÅcÁR«…27ámùíõ…QæZ¬v™.X8‚•f‚Õ‡š*Qþjå–ó6çÓ`úÓü¾…œ8_­Q –öÈž&%C®)9µ“½¹c®-&æsFSýF¯E(Xÿ$ÁÇs^åÀã§…ÜÝYº‰½GU½Z͉s–êñz„‚õ –6F«›-B«Ér-‰X°:Ò2%Í•¹ ;å·×F™k±ê¥N­+‹Äy'5U¢üUÊ»­‹Ó`úÝü¾…œ8_­Q Ö ZžDãPÁºÕÞ­Í×ó)ÐÔ©õ˜¡`M"ÁÃs^åÀ£§…\Wª1å:ÈU®Rs✥z¼¡`'ÁÒÆˆ»Y¡fʵ‹-În¤æÔ¸„\‹¿G(X¯Ñ6¦-S✛”©sn=Í^_e* *5Z+‹`ýd#‚¥=²çÆ’!íZ,!ŸMôGE)XI°^â9¯ràï¡‚u£½GU¹R͉s–ê1%BÁz™K#¾ Ö%äZ ƒ`A°Òeš†°éN¶`õ¤¦J”«­ÜÕê<Lßڃ߷ç«Õ#ÊIà´ O]rÑî©ìÍS·¨˜OÆú°ÿŒP°^!ÁzŸç–ŸróA©Fö&€­R[͉s–ê19BÁG‚¥×Já¬ÛlqÖõjNJȵx(bÁjOË”8§©27a‡Óìõ…QæZ¬r‰SëÁJ£™Üù9QwPS%Á’.pmYŒÓWöà÷-äÄùjõˆR°î¥åI‚¥]Srj{{sÇ\SḐÀN­Çäëe¬gùq#9ðà©!7”dÁº×•k©9qÎR=^P°Æ’`icĵT¨Qšríd‹³ª9±`Iµx0bÁº…–)–47aûÓìõ…QæZ¬|‰Së1‚•^‚¥=ÙûÂPÁÚiE°8߸<é\›ð´a¨`µ³wksˆ`iõˆR°Æ’`à9¯r`È©!ׯ•lhoØÊW¨9qÎR=&F(Xÿ ÁÒÆˆo‚Õ°„\‹! V;Z¦Ä9•©€Ú¬Ûåœ*×pj= Xi&XÚ“½ËÖRÇÒ¼( ¦öà÷-äÄùÆåIçÚ„§ J„œò8µ­½[›ëó9½‘>ì¤k Öpž ;žrm\Éö&€­t¹šç,Õã•k –6F\ …¥(×[mqÖujN Ηk18BÁšBÛ˜¶´L‰³oÔ¦:Í^_˜îrN•.vj= Xi$Xaâ,{…rÁîM´Atڃ߷ç—qjž^W"düÔÖ”kW[\}®˜Ïé×ëÀF)X/‘`IÑÍ85äÚ¸ ®³7, –’ç,ÕãåˆK#®¹ÂÅ,XlÁ‚¥ätÝùr-E,Xmh™gß <½õiöúÂ(G¯t‘SëÁJ3ÁÒž‹Tærå‚Ýf,XìÁï[ȉóËs¢´ OëçJ°ŒÝy"XZ=^P°^ Á’¢;àÔS·Ô·7lÅËÔœ8g©ã"¬Ñ$XÚq7)‚ÕÞ…ë«9Õ/.×âþˆ«5-SâìFÚê§Ùë £<»³âEN­+Í«35U",é×f´At«íÁï[ȉóÕê¥`õ¦åIÔ;?ä”Ç)7S®mqÕ¹b>,XZ=&F(XÏ‘`IÏxë*Xõ(×Þ¶¨x©šç,Õã ÖK$XÚÑ«$åz‹- ×SsªW\®Å}q,Z·]g›Oµ×¦³œSÅê,o+ì1¥/S.Ømš Á´Ôü¾…œJÇè1½hyמrÊã”–ö. ½2!æ“¿¡Së1!BÁI‚õßêž}O ¹6®Äµ”k/[T¨©æÄ9Kõ¡`"ÁÒÆˆk¦p V;[¾VÍéÚâr-îP°þIÛ˜V´L‰³j7Rj¯/Œòh© ÕœZVš –6méK• v›œCƒi¡=ø} 9•ŽÑc ´é"®)²G~J ʵ£-X°„|ò7ЧψR°ž!Á’AÒûÔS·%®±7}F…KÔœ8g©£"¬I°´1âš*TgÁjk ,%'þŒjqOĂՒ–)qVåÑR-Nµ×Fy´ –VV VØ,»¥j*§;ndÁšk~ßBN¥b4Ë®6]DPÁºÉÞ…¡Wž£ –VW"¬§H°¤²{… V{ÓgT¨¡æÄ9Kõx!BÁzK#®‰Bõ (×Ö¶(|šSóäZÜ'ÁºN»‘êT{}a”'T¨ ÁòJ°´YeuÁ:›Ó[öà÷­V\fÙÕ¦‹K;åqJ3{†Ö>GÌ'ÿuúôã#¬'I°2øŽÁ¸ã”kãίcoúŒò«9qÎR=žP°ž'ÁÒÆˆ‚%Õ¢_ĂՂ–)qV}íF*£‚¥<ù |§Öc6+½K›ô¬ä%Êõ$΢Á4Íü¾…œ8߸L§ÝÍxUñS§4µwÝB­sÄ|N«¯ßÝùr„‚õ8 Ö¾c0ºŸrd±øUöîî,w‘šç,ÕãÙë9,mŒ¸Æ ÕJP®­lQ¨ŽšÓUçɵ¸+BÁšLÛ˜›h™…¯Õ®ó=Õ^_ebÞr•ZV VØ$p%k(§;®gÁú§=ø} 9q¾q™N»›ñÊóBöÈOibﺅ+Îó9­ž~wg”‚õ –4c×PÁºÒÞÝ媫9qÎR=FD(XÏ’`icÄݨP•«¥- ]­æÄŸR-úÄM°Äë|Oµ×F™˜‚å™`i·Œ^PC9Ýq}aL¯Øƒß·ç—[hµ»k‡ Öö«‡–Vq Ö0,i~¡ÛN 9u[¼¶½»;ËUSs✥z<¡`$ÁÒÆˆ»A¡êù”k [ºJÍ©v1¹wF,XÍh™…¯Ñ.C9Å^_eZ“r•œZ–O‚u±2@ÃB4˜þa~ßBNœo\ëVZžD­óBNyX,!ŸÓ®uj=ÆF(XCI°¤ù…:žrê¶x-{ý¸°ªšç,Õcx„‚5‚K#á‚ÕÜ…®TsªUL®Eψ«)-S"\°šÛC¬ +9µ¬4¬°9JJ\¬œîhÀ‚õ‚=ø} 9q¾q™£D»ØþòóBNyœÒÈÞu —Ÿ%æsZ]ýæƒ(ëA¬ž¼‘ʧ„Y<ïr{7\XE͉s–êñd„‚õ –6FtÁ*N¹Þd‹Áº¼˜\‹ VZ¦Dá:Úu¾§Øë £ÌwaE§Ö‚•f‚¥Ý2Zâ"åtÇugÒ`a~ßBNœo\n¡Õ.¶¿¬XÈ#ßõö®[¸ì,1ŸSëê7ü#BÁB‚%ÝþÞî”#‹ç]fïæƒ²•Õœ8g©G(XO“`icÄ5R¨Â‚ÕÌ…j«9ñg„T‹Û#¬I´iLË”(tµvï)öúÂ(Óš”­àÔz@°ÒL°šSS%ί®ì×+Hƒé {ðûrâ|µzD9øµ‚^*X íV,­c"¬Á$XÝh™9Ñæ”#‹ç]J¹¶±EÙJjNœ³TÇ"¬á$XÚq×+T9rmb‹3¯Psº´¨\‹n Ö´L‰BWj—¡œb¯/Œò”²åZV VØÅUÁ:ƒÓ0{ðûrâ|ãr‡‡v-XÍ¢!Œ| ìV¿´°˜Ï©uôk㢬ûI°¤»³nÎ"¾ÅjÚ»6®L%5'ÎYªÇ# –6F\C…ÊFKɉ?#¤ZtX°n eJ‚%^†bT°”»nË”wj=fA°ÒK°´ îŠWUöÆë²`=`~ßBNœo\.@ÔNUÖ(²ÁÈWŸrmj‹š…Å|N½Z?u;:BÁºKz”R‹|!§¢ŠÕ°wê¶L5'ÎYªÇ°ëI,mŒ„ Ö¶8ór5'þŒjq[ĂՈ–)Q¨–rSHýSìõ…Qn )SΩõ€`¥™`iÜWUÙ¯[€S†=ø} 9q¾q¹Q›í7\°êÙÛëS딫õÙ_ŠP°`I7Ërd±h {·œ—® æÄ9Kõx8bÁÒÆˆk P©åzƒ-μL͉?#¤ZD)X¯Ò6æzZ¦Ä™WhgIòÙë £ÜRúB§Ö‚•F‚vâyU”½ñkN§ÁÔßü¾…œ8߸\€¨ª¼¸hÈ#ßµ”kc[\RHÌ甫ôS·£"¬þ$XÒÅÃMó…ˆoÑ‹íº-]^͉s–êñP„‚õ –6FÜu `5²Å™—ª9]\D®ELj«!-S",é&ŠkóÙë £Ü‚¥Õ‚•f‚¥.VEٯÂÕÛü¾…œ8߸œ×NUV/²ÁÈW×Þ^_Bb>§\©ŸºR°ú‘`I×¶Ü*XÕíº-]N͉s–êñ@„‚õ8 –6F\}…ŠE)×ëmQðR5'þŒjÑ!N‚u™v–$Ÿ½¾0Ê5‹¥ËB°¼,í|p±ÊÊÆâêü4˜z؃߷ç—óãÚ‘´jEB6ù®±·×Ç‚%äsJmýÈâ‹ Ö]$XÒµ-×ç ß"ÕìY,u¡šç,Õcp„‚õ –6F«¡- ÖTsâÏ©·D,X×Ñ2%μT;K’Ï^_åšÅReœZV VØÞEÑJÊi¨«N³÷\5†ß·ç—½ íHZÕPÁºÚÞ‡R3Å|òÕÖ,¾¡`õ&Á’N½4ÈÒ—"UíY,YV͉s–ê1(BÁz”K#®žB¬¶(x‰šFHµh¡`M¤mL}Z¦DÁšÚN|>{}a”#¾%K;µ¬4¬¼a(ZQÙXÔfÁê`~ßBNœ¯V(¿v!d•"!Œ|WÙûPºøL1Ÿ|µô CŸP°z’`I7AÔ ¬*öNw”,£æÄ9Kõ¸?BÁz„K#îZ… E(×ú¶(XCͩʹr-ÚÄI°.ÑvâóÙë £\ Á:Î8|øpò¹çžK6,'fß¾}±¬°Á_D¬SíÍéÃðûrâ|ã2øµ#i•Ï Ù`ü­¶½¥‹ Šù°`iõˆR°z`IGêæ ß"•íY,YZ͉s–êq_„‚õw,mŒ¸º å –’FHµh±`Õã‚5´mŒQÁRvHJ–rj= X!1yòää´iÓŽ¼žJIƒ,®‚¥5»He#^Ëès¢ø} 9q¾qüšèU<7dƒñ7~¨p=[T/(æ“ï ]|Ÿ‹P°º“`I®«ÿÒ—s+ÚÛX\PJ͉s–ê1 BÁF‚¥]°Î¥\¯µEÁ‹Ôœø3BªEˈ«.ï|¼XÙ!©õ7{}a”’ J:µoA°ôèÞ½{rïÞ½G^ÿøãɶmÛÆV°´fŸ[^ùPºÂè%ü¾…œ8߸ ~Mô*„ Ö”k][„–V(«+ –´áª*Xì‰ï%Õœ8g©ý#¬¡$XÚq×(‚el}9£ºšS…„\‹q¬êÚ6æoöúÂ(ëË@°Ž+4hð—S†Ù¿gJ°¤•ør¬ëìÁï[È)N‚¥½ò‰ †UÁòùÛåz_žP°:“`]KË̉Z éK¢¼½¾”¸@͉s–êqw„‚õ0 –6FtÁJP®×Ø"D°ø3BªEókmc®¡eJœ*XרC¬8µ¬¨S§N®¾—y}VvÁºãŽ;’÷Þ{oÀ€Žü^¼¾»_¿d¯Ûo?BÏîÝzÝù¶[“nm—¼•à¯:´=òº}û¶É›š7O6h|µðšßwNùðWÎW«Gÿþýó´Y_ç´üÌ×Ý»vÎñýg¾nѲ¥™~d¾nwK1ŸŽÛ«õàqÅú¯ûôê%Ž;ºu×þÚêæVæÖ—Ömnóá×wtï&Ö£ÏwF¶¾ôíÓGŒ¶¾´m×ÚÜúÒ²UK1~ݽKg±½ÿÿmLëKÿ»ïÇ¿¾­Sq|ÝÒ¾¹õ…¿jëK×ÎÔzÜsÏ=‘mÿûöí›þG°@ ⱬž={&þù磮Áºùæ›Ñ@ ¬TcÊ”)Áƒ™Áÿ?bÄt@ +Õ8Öy°²GÖk³òs‚…@ #X@ @  @ ‚…@ €`!@@°lÄÚµk“;wNÖ«W/ÙªU«ä˜1cB'§GÅ-vïÞlÔ¨z£˜9sfðø†úõë'6lLuÂõ¢/'7f̘‘ìÓ§OÐ~*EFFFrÏž=èKŒbÁ‚fÞkº÷…ß[N@°GÅgŸ}<[ióæÍÁkþP5j”ù¥tô騛~ýú%7lØÌ%wèСä¤I“’=zô@_bÔ†E¸C‡èKL‚s8p ÉϳtìK:È+‚2dHrÞ¼yâÏyoƒ÷:š5k–\¼xqŽöž'NL:4ùÈ#üeòÕÉ“'ƒ÷ùünݺÁ÷y#ûüóÏG3žÀ•¿—¹œ$Û´i¼æ=ìÌà@ndiyEH÷Þd瀾 /èKÎÁBÂ?çß·øy–Ž}`!r| í?þýw«V­ öB¤ƃ{„ G^/[¶,ùÄOÿϧfÍš XŽ;wù}>T<}úô#{Îo½õVrôèÑG–‘¹×ÃÁãÆGöèxeKgÁJ÷Þpðïð‡!úŸ¾ðƇÿfÖ úrrú‘õÙ·?ÏÒ±/ü{,…YOyþùçŸ,Dê’¹GÓïñî÷ïß”ýg^ÿÔ¾}û#?ûï·hÑâÈÞÇÁƒ¬dÙ—Á+ ï}pð×Ü\#bY°Ò½7Ë—/OvëÖÍÜ5XéÜ—Ì#|tà×_E_Nr_ú÷ïŸÜµk—éϳtÿÛ»wo X|d ‚…øËÞ…dÞ<¸ø"d6t¾ð5ë`Ì>0µ‹þ²®4Ù?§•/óÔDN?ëÝ»÷‘ž½r£7ÑõfüøñÉAƒÓ3<Ñ—hÖÞñÌßC_N^_Òábêt__2eOMB°Goä¤óã|^š/vå=Y@Úà罄ì{™Ñ¸qcqï‚÷$²ï]ðytiðóÅ‘üáŸÛSP–+]{ÇÞù‚jô%žëLfXÛ`øÐ‹Ÿg>ô…¬5mÚ‚…8:¶nÝ ¶-[¶Ù£àsÝ™{ü:󮢬ƒ‘tÖç<§Nzd ó ™çÇGŽü>ÿþ>_ô˜õü8ÿ^æùq>WžõüxöرcG°ì/¿ü2í+{³fÍ“G­Ò½/ òá¿Ç×Ëðu*¼aD_âóYfõó,û’õ®[–+>8vìXâ¯ÁvéÒ%8ÌÊç¹ùô ÇÂ… “7ÜpC¯Y#ï‘dÞ•‘üoø÷ùðk¯^½Ž¬|—_ÓÁŸçBù裂ÃÁ¼²ðàä½e†ÿŸ÷0´~¯¹ù J‡9JÒ­7é2Lºõ…çXâ¿Þ¸ð+ëõ.èËÉû,K‡ÆtëËœ9s‚; yy|„,3â¤ÿ¢E‹‚•!•àçXOu Ðô}A_Ðô‚•–Á{™³DóáÛTOñÞDZRG 7è ú‚¾ /è  @ ‚…@ ÁB @@°@ X@ ,@ @ ‚…@ ÁB €`!@ X@ ,@ @œÀX¸pa²_¿~ÁCeùá²5J0 9kÖ¬\ÿ:uê  @xÆ Äh„ Ƀ&>œœ?þ1  @@°‚bΜ9=õÔSùYÿþý2ƒ¥«oß¾ÉzõêG¸zè¡äÞ½{’«¬dƲeË’]ºt ~¯aÆÉáÇ'8päç7nLöîÝ;ø9=ëÔ©S²nݺGýõë×ÿ†°1üÿk×®ý‹Ümß¾=Ù³gÏàoñÿ?úè£É)S¦•×Ë/¿|çÎ @ N|tïÞ=“œdãçŸ>ê5ËÕîÝ»ƒ#\3fÌ~¿—]r²‹oРAÁïÍœ93x=bĈàç[¶l ^·oßþÈ{àåfý[6lþàÀ˜1üÿü=–³¬ËÎ3fÿþýh1™B÷Ûo¿?cAä÷ƒ@ Xqƒå#•Óz,',3üûš`õéÓ'øÞž={Žü¿æ#Y™Ò–U”rú[™ƒå.3¾üòËà{|$+ë¿ß±cÇQâ4tèÐàû|ôcÁ‚Áë'žxÍG Xqr‹±¬ddd$›5kœ¦Ë.Tÿo{wøÅÇqüþ4‰¢0¢0{>!Š¢'a×…Rô òIia„‰ R˜J…Š|`D”$¢(AÁÑÑüæ30˺·³îÙZþìý’¡Ûso¯½˜3ß½Ò––üÒ–ýó|r6)þœÐ9jŸÎ#ôÚâg¿:;;ݶ–5Ó¦£££nv(ª|ø`jµZ®€å—ìBËqYᩈ€%­­­î-?NOO»Bx-ÿ]¼xÑ…!ý÷=EÑ Y²Ž  v,}ù¨jªü·´k9Q5^Eѱ´<©/U@À €€@À €€@À €€ü+ÿK%­€€ °>G #ø ` Œàs€€00|ŽFð9X¶ÝÀøé“1Ïž…›úÿ¦OöçYÆúÿOššš2Þlñ#óõ~ó8,€€ 00V*5»] 6õ§ ¶É¶™Á<Ïó+µŠ)UKÁ¦þ¤oß¾™ëׯ›}ûö™={ö˜Ó§O›lû€U«TLÕ^›PSžk‘÷š4r½X @CkÍn/›úÿä¬H]ÀZ³k¡lêOêìì4÷ïß7µZÍüúõ˼{÷ÎíÛîkͨ{mBm-%`mtlf°€¿°íöë`SÞA;>kòãÇÓÖÖæ¶§§§Í‘#GÜlRWWWôœälKjÀZ´ëu)ØÔŸ´{÷n¬BîÝ»çÎãòåËæÒ¥KfuuÕíW ëééq3_{÷î5år¹n¿ÚÍ›7Ý>ÿ>&&&L{{»{ÝŽŽóñãÇèµôZZZÜ{?{ölæ{]´êµ½6¡¶¸É€:¿ÐõÊ{Þ,€€ °æíöH°©3ëÍ›7Ñþ˜/_¾¸ÐóèÑ£†fW*ó6`”‚MýIgΜ1wîÜ1oß¾­ Z wïÞ¶Ÿ?n®]»æ÷õõ™ÞÞÞ(|ؼzõ*wP[°fmÀº] 6õ'iFJ¡iÿþýnÖæüùófiiÉõµ¶¶šïß¿GÏU`jnnŽÎóçÏŸuÇÓþxèÒs4»“öt¼]»v¹ÇšJ†µ¬÷lêÿ݀孬¬¸Ú¦†Ö¨ X'KÁ¦þ}Zw<Õ`éwüñT¯Á ý=¨^õLúååesõêÕÌ€5Ùßoʶ?ÔÔÿ'VÞó&`,Œ¬+W®¸$ÍÍÌÌDûµ\èïÌ+ÒãÇ]¡»–ñôºÝÝÝÑ‚¢À¥Z,õŸ:u* yÕjÕ… Ͳé÷âwj¿ö©é±¯ÕÊ 0 •º[Q5Ozï/^¼(ìk¶2`å=o@ÀÀÀ>G #ø ` Œàs€€00|ŽFð9@À¶ëÀH£Ñ°vœÿEHG¬¤øIEND®B`‚LevelRendererSample.png000066400000000000000000000335141463604235500353710ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/category/doc-files‰PNG  IHDRXr5˜7IDATxÚíÝŒUu~ÿñMš¦iš¦iÒ4MÓ4M“¦4MÓ4iš¦i@1«aënuõ‹â²",,‹P\\W¶²b­h—è‚|Ù‹Ã:°"‹Š¨c— ¬‚K\\ˆŽe•/ òùž÷¡göÎåÎpgæÞùqçñš¼3sŸ{ßçs?Ïó:¯sæ3‰ˆˆˆˆêªÏøˆˆˆˆÀ""""XDDDD°ˆFô@üÌgºU³¿®÷FD‹héµ×^KS§NMø‡˜~í×~-¯ø;®‹Ûšúú<½Ý¿ò¶¢â3ý­ßú­4eÊ”´gÏžAýœÞ|óÍ4cÆŒô'ò'ùrüê¯þjúÍßüÍtÝu×¥Õ«W§sçÎ8Àêí3ž0aBZ²dIêììñcsÇŽiÞ¼yéþáò÷Vô/þþÇüǼÿó?ÿãKŒÑpÓܹs{„‚¢â ` °*kÆ ƒò-_¾<ýʯüJ¯Ëò{¿÷{ðú9D–J¥‘=ÙÔð>ÿæoþ&:uÊ—,¢á¢ØÊ¯â¾Í Xƒ=ñGýéŸþi×ó;ßùNÍËÓŒ€UÔH†¬Zßcl(,¢a Ã‡_ælÜsÏ=éüùóyÅßå·Å}ã1«o(vá¬_¿¾Ûõ±›§‘úùÏžïN*Í1cƤÿüÏÿÌ—'°ßýÝßm À*_¯¯¾úên·ýöoÿv:qâĈ§±«~ñâÅùnå—=½Çè#À"ªÜ5xóÍ7_vŸ[o½µêVò_ÿõ_w»þßÿýß»—ßöWõW]·½þúëùsƤ€ñë¿þëéÏþìÏRKKËe»8*'Ѐ‚{ï½7ýÑýQ{½M²·Ýv[úû¿ÿû|Q¼N€ÆüÁ¤ë¯¿>Œ¾LäñšüÇœ?O,w5ׯ?PPé` t™+?ŸøLËoÿ‹¿ø‹® º\‘S*ïQåó˜|éK_J¿ñ¿‘ל9s.{Žz/û•œ›Z@ðïþîïzu`k]+_'òjñ\Å:¹¶ÿøÿèéØ%½Ûâ>±•zúé§s@*>¯ø—ãúZõñÇ*°,¢U I»wï¾ì>íííUa©r×Sp ]uÕUÝn+&˜Ç{¬×,PLn}ôQ[å„ÙÛ${¥Ý)[¶l© Š"Þ—Ýk½=WLÂåׯY³¦×Çõu™+?Ÿ¿ýÛ¿í×n²Êç è¹Òîâz/{=+`¥ò5 õe]¬v[µÇÜwß}ùø¨v[Ñ ÍŸ?¿×÷·×¢€åÁÞåL°ˆjPl±WºWúÇ„þû¿ÿ»ÛÇuÜ?¶¤Ë·ªÃ‰#+'‘x½U«Võ8¹Ô5=M²þçžOêñá8DH¿Ò͹µ¶¶^6AÇ{ Çg Ù ØýZ©.seUö·üHÁ¾VOÒÈeï«X °b9ª­»}]û’õºÒçU }á ÆkÇïòëŸyæ™+ö©؇cF’`ѨT9õ–µéi7Deä{ßû^úÁ~Ðíº¸Oè†n¨:ÙÇäR~}ìRéi Ó•v#ö¤ÊשÜRíy&OžÜíº8\¾?OõùÏ>={¶nË\ùùÔÚß+ÖòüO_vG tÙëX=­»}]+_§­­­*€÷v[ì2 Åi*?ÛÐÁƒ«Ž›ž¬È••/«£ ` «rÒ)\€j“Hɾð…n×Å}Bå“AoU>÷u-¿O¸L‘é‰ -vqU¾×Z&ëÈÅôôùôgâwoæÌ™=žþ¢ËÜ[ûë`U[*_¯ÞËÞ+úÙŸu±§×éí3éé¶žÖ©ÊûËZMo½õVúýßÿýnþ4"€E4LT™Ázùå—/»O\W~Ÿ¿üË¿ì q²Ãò éw~çwº}ñÈ•ÎÃÔßÓT»O„ªË—«¿¹©Êe®ÇÄ_¹Ë5>¯z.s¹*ó@µ¨ûÚF,{=+v³•ß>vìØ~­‹õ8 ¡¸­7W±‡0œÄâ¨ÏâÈÁá|"`"€E£R•9™¾EX(ÎÞÓänM¡Ê-÷jG³õeríí>•àx÷Ýw§Ó§O÷y2¬<ÅA=&þÊå(&Òz-s¹âh¿J8®ö¹G®,ñý¬F,{=«28ÿío»_ëb=k V[‡‘k‹Sq,¢a¦jçÁŠ lìZ‰ªv¬ø—+•[Ô=ÖÞ½{»îÿ’¥2¯U©˜”ãÑÆ.Ÿ¨ø;2XÅ¡ä=éèÑ£—íJŒ£ÊzºoLȑۉ#Íâ¾1Ñ…ãQùY¿û XáDDþ¨˜@c÷P¥sPëdxüøñüèÈâ¹â3yþùçûXÅ»c7Qqte¡z.s5Eãó“QÆkÄçðîM-GHövtd£–=ž;Ö—xÎâ3,v«öôÇûŠ]láööïqj]ë X…“ëUù™Üãrµ,""""XDDDD‹ˆˆˆ`À""""XDDDD‹ˆˆˆˆÀ""""XDDDD‹ˆˆˆˆÀ""""Xͣŋw»H‹-J&LÈkÞ¼yéÝwßX¶@ôCé‡~(ýXýÕÔ©SS[[[ºxñb^­­­é¦›nXJ)¥”XýÕøñãs°*׸qã–-ýPú¡J?VµiÓ¦ôøã§­[·¦÷ß?íÚµ+-[¶¬*XU®–––|?r±¢ÄïÑt¹³³sT¿ýpY?š³?üá+é•W¥ŽŽãéСCéG?jO/¼p(½öÚñü÷–-íi÷îCÙ}翟~º=½øâ¡tàÀñü÷Ö­íiÏžCé'?9ž^zéPzæ™öüw\ŽÛãþñ¸Ÿþôçú1JßÓV@ÕÌ™3s¨š2eJžÇúä“O8X¶@ôCéÇ(îÇÔ©gÓ7¿™2ºô]_*¥lŽHéŸþ)¥Ï}.¥þ甾ð…”n¾9¢&)ÝzkJ_übJ_úRJ3f¤l^Iiö씾ò•”¾úÕ”î¼3¥ù—K—ãöiÓ.=~ÅŠ£úÁÁjNÀºýöÛÓ‘#Gº.—²Q4;FÀRJ©Q[K¬ªZÞJˈ~(ýà`,ã` @qZ†ØMаûš5krhXÎc¢J?Fo? °î»¯#mÛ¶­«vîÜ©«9ëäÉ“94ÅÑ„Q‘Å’Á²¢J?8XƒXwÜñ|·ƒ¨{ì1ýXÎä°”RJ `)€°lè‡ÒýXÆÀXö¡+ýÐÕ¼¬Ñ X2X `ÙÑ¥,€e|,€¥”RJK,€e Dé‡~( `,€eº~(ýÁXÆÀX¶@ôCé‡~,ã`,¥”R2X `,[„J?”~p°–ñ°–}èú¡ôC?–ñ°–-¥ú¡8XÆÀXJ)¥d°–Xˈ~(ýà`,ã`,ûЕ~臒Á2>À²¢ôC?‹ƒ°ÀRJ),€¥À²¢J?ËøXË>t¥ú¡d°ô`,€e ]?”~p°–ñ°–RJ)€¥À²¢ôC?ËøXK¦Aé‡Ò,€e|,€e D?”~èÀ2>ÀRJ)%ƒ¥Ö0Ö˜1cªÀ²¢J?8XËøXuÒ‰'²s3À²]?”~È`,ã`ÕK÷ßÚ±cÀ²¢J?8XËøXõлロn¹å,uÅzbÿiùÑåƒVñz>w¥d°À‘ºï¾û²Áôb`UT¹ZZZr›³ ñø=š.wttŒÊ÷¿ªsUš2ˆ?ñzõìÇîݳ/ö³iË–séüùóé•WÎ¥<›MçÒôé糊ßg³ûœË&‰óYK_þòÙl¢8—,8ŸÕ¹4þÙlý?—‰óÙŹ¬Î¦{î¹t9nûϘq.{­÷ŒQúþGr?† °žzê)ý%—G `Å›ž>}º£íCÑV­ýز¥=Ÿ~ðƒKëñË/§ô¯ÿZß $îã)­_¿ßøP2X,ãc´:X‹-JûöíXö¡Š~,ãC?–ñ°®#GޤY³f9–5°”X `5\sæÌI‡X¶@8X ¬––õy¾±¨Í›7ë‡â`,–3¹,ûЛ¯ƒ XwÞùØe!^ýP2XK `,[ ,€e|p°–ñ°–RK),°–-ÅÁ2>‹ƒ°À²] `2XËøXˈ~,ãÃøXÆÀXJÉ`)%ƒ¥À²E¨8XËøà`,ã`,ûÐe°–ñ¡ËøXˈâ`Šƒe|,€¥” – –’ÁX `,[ ,€e|p°–ñ°–}èJËøP2XÆÀX¶@K?‹ƒ°ÀR2XKÉ`,°–-ýXƇâ` `Ù‡®d°Œ%ƒ¥ `,[è,€e|p°–ñ°–RK)€¥À²¢8XƇâ` `É4(,€e|È`,ã`,[ ,€e|èÀ2>ÀRJK),°–-ÅÁâ`)À2>À²] `2XËøXÐÛo¿æÏŸŸ&L˜À²ÂÁXÆ `kêììÌÈÍéÀ,%ƒ°”X `ÕC+W®L/½ô’]„¶@8XËøÐ€e|¬zésÙH Èš8qb¾{0€éäɓðžØÿDZ~tù U¼ž}è2XKÆd$|_Õú5Z3XÍЀ5kìØ±iïÞ½éâÅ‹éÂ… éñÇO ,¨ VE•«¥¥%_I ߸¼ªsUš2ˆ?ñzµ,_GGÇ ¼—÷×µ/¼phÈ«½½]?ª\^´è½´~ý‡éµ×N¥?ü0mÞü^ZºôÃô­oJßøÆ‡éë_/-[öa¶Ax*-_þaº÷Þ÷²ž}˜þíßN¥ï|çôbÅ{éÁ?L>z*=ôЇéÞK«W_º·ÇýãñÏ=÷^Ó}_ÅOLêõìÇPVl€èÇè¸<*«\ZãÇç`Õè`),V}*>ûo;¥"±Pï =î_½ú§£ÖÁ­¬fèkÖM7Ý”Nœ8Ñíº«®ºJË>t,€5¨ã£™KKËü°Ò† ÒªU«ºrWûöíK=ôÀ’1‘ÁXƒ:>–ó`9Àjºó`=üðÃyÈ=v l?`Ùá`¬¦t°/~.ÿÎ+ª­­M?–ù`9“»R2X2X¬9sžêÖpñ}þK,€e Dq°8X‹ƒeþXË>t%ƒ%ƒ°d°–ù`,[ ,€ÅÁâ`,óÀXJÉ`É`,,°–-ÅÁÒ€ÅÁâ`,€°ìC—ÁX2X2XËü°–-ýX,€ÅÁ2,€¥” – À’Áòù,€°8X,€ÅÁâ`,óÀXö¡ëÀ’Á’ÁX怰l( `q°Ì `)%ƒ°d°d°–XË `q°ô`™?À²]É`É`,,óÀX¶@‹ƒ°8XËü°–’ÁX2X2XK,€e Dq°8X‹ƒeþXË>t%ƒ¥KK `,€e „ƒ°8X,€eþXK)€%ƒ°d°ÀX¶@‹ƒ°8Xú°– °‘ÁX2X2XËü°–-Àâ`q°–ù`,¥d°d°– –X#HcÆŒ©ZË `q°8XËü°X,ûÐe°– –~,óÀX¶@‹ƒ°8Xæ€5¼kܸqyMÉFÏêÕ«Ó¹sç–’ÁX2X2XK¬zèĉ9`-Y²¤*XU®–––Üæ,H<~¦Ë5Ý¿½ýõ´qãþôê«©³³3íÛב~øÃý©­­#íÚÕ™6oîHÿ÷ÿîÏ®ëÈ×™ž~º#}ÿûûÓÖ­i÷îÎôÌ3ùã·mëÈ& Î´}{GÚ´izöÙŽ´gÏ¥Ûãþ[¶tèG ÷á…CCXíííúQåòPÖÎ;òþ÷lßž:žy&u<˜ûwïNÙ}:ô£Ô±ysÚ¿~}êØ²%uÆçôæýßÿ~êxúéÔ÷Ûº5í߸ñÒã÷ìIÙsíomMÏ>›:³•µcÛ¶K·ÇýŸ{®!ý*ÀŠ Fôcw|îÙçÜùÆõïG\.ëÇëÙ¯w?šñò¨;ŠðâÅ‹iüøñ¬:ïCâ‰ýù—J6þê>¡Ï™“Òí·§tË-)ÝpCÒ– Ö0p°ö?ñÄ¥™MÖô2XW®ö žkЬÉ`q°.ÓéÓ§ÓäÉ“V÷¡,,,€5Ô€5š3Xðd°š°æduïÞ½¹spõÀ¤G}`Õ¹– – ÀjÀͬáX2XMX[²•nv¶ÂŒ;6]{íµiÍš5Ž"áVLà奋ƒÕÜ€Uë˜ç` ΠßóÈ#Ýú9?–]„NÓЬò/«(ýX2XÍ XµŽy¬Áôe××2æe°ÀaÖh, `-æ`Àâ`,€5Â2X£°d°d°V}Æ<Àj<`É`,€ÅÁâ`, `q°À’Á’ÁX2XKK ` @gΜI÷ßš8qb·ÿ+x{6øZ[[K? `q°8X«¯Z±bEVEzã7òÓ.,,Ÿ?À’ÁX2X2X« çjÓ¦Mù‰BË+ç¶X,ýX,€ÅÁâ`¬>jܸq9\…Êëý÷ßïõ ,,,€%ƒ%ƒ°d°Vº%`9ÇŽË+`ëÀÙÊ;-?C;Àâ`éÀâ`, `õQ»wïî–Á*¯øƒKËç°d°– – Àê‡~üã§Y³få»#wG¶··;M‹ƒ°8X‹ƒÅÁX΃%ƒ°d°d°– – À€ÕÓîÁÊÓ6,–~, `q°À’ÁX2X2XKK ` ¯]„+W®Lo¾ù&Àâ`éÀâ`, `ÕKÇÏÆà€%ƒ¥K `É`É`¬z)·åD£,Àâ`, `Õ®6nܰd°d°– À’Á’ÁXõ¹WÂÀâ`éÀâ`q° `õ°Â¹ºë®»ÒÉ“'– –~,,€%ƒ%ƒ°œh”ƒ°8X,€ÅÁâ`¬!¬+ûÊy°d°d°– À’Á’ÁX‹ƒ°8X,€ÅÁâ`,»– – À’Á’ÁX2XM X'NœÈú=;7ŽƒÅÁâ`q°8X‹ƒÅÁXõž©±cǰ¶oß~ÅÇ,,,€%ƒ%ƒ°d°š°&Mš”Z[[ó“‹N˜0!]¸p!¿~×®]iõêÕ5?OtœÚ`q°8X‹ƒÅÁX¬QX±k0à*tÍ5פ·Þz«ÛmµèرcùnÆ3gάdLöï"ýä'«2¸*¥ÿú¯R|UeŸy)½òJ)ûÒ*¥KéÉ'K(—²±_Êc)µµ•ÒæÍ¥ JÙ÷s)ìÖ­¥´m[)mß^JÏ>[J;v”ÒΗ®‹Ûã1›6•²×œ‘}IÍ“ÁX2XKK `õWFѤЂ ÒÚµkó¿8ï6¼’âd¤·gõ£>ê:B±'°*ª\---ùë$¿GÓ厎Ž+Þÿ¿x4û¤¦ jdýÒË/¿ð¡!¬öövý¨ry¨kçÎ yÿC XõèÇPVÀH#ú1”€ëØ@ûÑŒ—‡=`mÛ¶-‡¬Ð’%Kºå¯¦Å|Í;7½ýöÛÝNÁÁª÷n’æt°Þ[´(V€”~ô£ž¿°bŸeñ…“A¬“Ó§_šî¸#¥/ùÒdQùe·Ýv[þ¸“Ù:*ƒ%ƒÅÁ’ÁͬٳO¦¯=¥çžkÌ×mÜ÷¦›R=ïq°ªéôéÓ9dÅ®Á›²OêÈ‘#ý>§À’Á‰€%ƒ%ƒ°d°š1ƒ°°"ØþñÇ7ä$¦,çÁ©€%ƒ%ƒ°d°š1ƒ°°„©Z”Mr‘é(‚ÅÁX,€ÅÁâ`¬~*Àªü䢓'ON«V­êv!Àr¬f¬Ÿe+?+W®”Á’ÁX2X2X ¬/~ñµnËþðÃÎ Ö§Ÿ~šMØOgØœn'š­ž.Ž t&wÖh, `q°8X«® ˜Ú¸qcöaNï­ZNÓ°d°š °d°d°– – Àj˜^ýõ¬Ç7ú_„,Àâ`, ` DŸ|òI6ilé¶»ƒ%ƒ%ƒ°d°– – Àê£ÎŸ?ŸvìØ‘ÿÁòÀ{d°bwaœ¥`q°8X‹ƒ°8X,€U£ŠŠVEXËÉE– – À’Á’ÁX2X«—ó`༔}#Õëí½{÷¦‹/æµiÓ¦lý¸`q°8X‹ƒÅÁX,€UO7`É`É`,,,€%ƒ°ê¡ .ä+Bì2X,Àâ`Nkÿþ'R[Ûú ®JéwJÑtð`)ëI)mÜXJO>YJ­­¥lü”²õ¹”Ý·”6o.e㪔½ÝRö=WJ[·–Ò¶m¥´}{)=ûl)íØQJ;w^º·Çýãñ[·.Ï j `5/`3&¯ø@?þøãª`UT¹ZZZòýÈÅŠ¿GÓåÎÎΚîßÖÖ1d€Õ¨÷?T€~ ýxá…CCXííí éÇO³ÏåÔ·¾•ξøb:uêT:¹gO:µfM:»`A:5o^:9wn:•ý}öî»Ó©lÜžÌöTöwößH§–.M'³e;uï½éìŠéÔ²eéä7¿™NeŸ]µêÒåì¾§²Çþ¿ìïzø=T€µsçΆôc(k ýèì\•-ð”A«çŸŸÑ FÑ¡¬XÇ:>† °Ö­[×°ùsT9XçÏŸÏCï3c%â`q°8X#ÊÁ:º|yJD¥íÛº’ÍúÇÁâ`q°8X¬~(Ž(X2X2Xk €%ƒ%ƒ%ƒ%ƒ5jkÞ¼yéÀù„Ÿ~úi¾"4, `q°E°8X«ŸÚž}Ç©âÈÁ«¯¾:Ÿ¼NŸ> °œËy°VÕ•ìãìqу¢zè!çÁr,€åMœ81-]º4}ðÁKK `É`É`,,€Õ_ÍÉ>í½{÷¦‹/¦ .¤µk×f ™°8X,€ÅÁâ`,Àª§Æ°d°d°– – À’ÁXõÒ¾}ûrW `q°šÕÁÚ¿ÿ‰tøðýÙ—^)½ùf)¦ŽtìX)½òJ)›DJiãÆRzòÉRjm-eßÅ¥ü~mm¥´ys)³R6_–²>–ÒÖ­¥´m[)ã™RzöÙRÚ±£”vî¼t9nûoÚ×/Ì&y ¬'²÷ôèK³Siû­©ÔñÍìe?Çþ5•^ùJöž¦¤ÒÆÏ¦Ò“ÿ”J­×§Ò>ŸJOýs*µÝJ›oL¥-_H¥Þ”JOßœJ[ÿO*m»åÒó<;-•vÜ–J;¿x麸={Ì÷²ÇÎ+{?,Àj¬ƒßYÛ¶}/û»”~ö³ž¿³âû¦ò;«üûê™g.}gU~_Åmqßxì–-tû¾XuÒ‹ÙHš–­õÕ2Xå+K¹ZZZr /V”øíòå—ÛÚ:† °õþ† °bÀdùñ‹G³…2¨õüó3º–¿½½½îýXÕ¹*{•Áû™Qö~V¬XQ—÷3T€µsçΆŒ¡¬z,ÿPVÀH#ú1”€ëØ@–¿3ßCõ}µnݺ†Í£°Ö¬Y“»Q'Ožt!‹ƒÅÁâ`q°8X,Ö@MŽ »Ó4È`É`É`É`É`,,€UÅîŠ+¹V‹ƒå(B€å(BÀr!ÀêƒÆŒSµ–ó`9Àr,çÁX΃°œÉƒ°8X,€ÅÁâ`,€%ƒ°d°d°– – ÀX‹ƒÅÁX,Àâ`,€%ƒ%ƒ°d°– – ÀX,€ÅÁâ`, `,,€%ƒ°d°d°– ÀX,Àâ`q° `,,€%ƒ%ƒ°d°d°Àâ`,Àâ`q° `,,,€%ƒ%ƒ°d°Àâ`, `q°8X `É`,,,€%ƒ°d°Àâ`q°‹ƒ°8X `É`É`,,€%ƒ%ƒ° `q°8X‹ƒÅÁX `É`É`,,,€%ƒ°‹ƒ°8X‹ƒÅÁXK `É`É`,,,€°8X‹ƒ°8X,€ÅÁXKK `É`É`,,€°8X‹ƒÅÁX,ÀX2XKK `É`,,€°8X,€ÅÁâ`,ÀX2XKK `É`É`,€ÅÁX,Àâ`,ÀX2X2XKK `É`¬~)=iÒ$€ÅÁâ`, `q°V=4f̘®X2X2XKK `É`¬:ƒÀâ`q°Ö`:Xû÷?‘Ž]žMà¥l‘Kéç?/Åtž,å×mÜXJO>YJ­­¥¬_¥ìó+¥¶¶RÚ¼¹”õ±”ñK)W¥´uk)mÛVÊÞ~)=ûl)íØQJ;w^º·ÇýãñÛ¶-Í&ñy,€ÅÁXðÊ?ðrµ´´ä»‹%~7êòË[·¦CÙZu¼£#:t(µ¿ðB:ÔÞžŽ¿öZ:”ýèÐîÝéxvßøÝž}±ʾŽ8ÿnÇïÙ“Žÿä'éP¶™ÜþÌ3ùïürv}{F?ñ¸Ÿh̘15]Wþ—ëKÙŠwgÖ©¨yóæuý݈ËwΚ•ægkÈül™?~úJvyN¶æÌÉÀœl2žUþ÷ÿV×ål Ë/ÿoÍ.û;¿\<6{Î/g¯3+{þøý•l èòõ«wfcóÎì¹æ_Zö¯Ü™}v³3†˜“­Ðsòß·Ý6;ÿû—uéòô鿼ÿ²~yùÒs\º,sùò7¬Ñ‹èÉÿöcnv¹!ýÈÖ±ò÷3'û†¨G?î¸ã—ý˜;·þý(î?sæìnË_,GÝÇKöÅß­ÙåËúQÖ“šû‘}þÝú‘ý]þ~fg¯Så~Ìž=?ëÍ¥žÌ˜Ñ˜~ÜqG÷~|5›mÒèC6!ý˜—­· éGVåï§óõèÇ—¿c£èÇœ†ô#ú\¾üs³Ï«¡ýȾÏó~d¿ÖìuÊûëØ@—Ö¬îý˜5ë+8õÜâr-ý¸tŸK÷ç¬÷ü×Óåøîà` ¡F`ÝžüG}Ô-ƒõÙÏ~V'‰ˆˆ`õWëׯÏ3…âïûï¿_'‰ˆˆ`õW}=V¥*ÕJ)¥”RµTS‹ˆˆˆ`À""""XDDDD‹ˆˆˆˆÖð׫¯¾š¦N𯗦L™’V¯^}ÅÇTû·@ÃMñ×'Mš¤C¬M›6åÿÚaüøñiâĉù©Mâļú14jmmÍÿGô#þûÄ‚ Ò±cÇŒ‘a¢íÛ·˜emÖ~IJU+€E}Ò[o½•®½öÚü¿“‡â‹ö¡‡ñ_V#uP4c?â îÝ»7?wÜ… ÒÚµkÓôøOªú1äýˆ ¾1þ»¶12äŠ÷t×]w¨ï­fìÇH†)€5Œ´hÑ¢´uëÖo­‘Ø*¹æškÒsÿûïÈ{¢úÇ<-Y²$}ã߸ìÄ«ëÖ­ËŸ#¶šã ~ìØ±ùõ1á®Zµ*w6¢âä­q]ñ:gΜI×_}~9¶º ÅÄPË?¼i¥ÙûQ(Þƒ~臞üR&q{<~$}o5c?ÕE± íÓO?½âý^~ùå|+¥§0VþÇ{¬ëòîݻӲeËò¿c—D[[[¾B‡Ž9Òõø°’7nÜØµ5ýÔSO¥‡~¸ë5Š­¢P<ÿ¾}ûº¶ôb06`5{?Bñ˜ø‚Ô¡ïGLDñœåž ~O$Êÿ¿íHúÞjÆ~Äã Ëwyž;w`Qã¬Ðb‹¡Úãâ]Ÿ>}ºÛÖA‘ºá†ºFå㯻­Ðùóç»aåkÄ Š­“Pü®%72Ò«Ùûñâ‹/¦iÓ¦˜ V3÷£pÂ)øøã‘!ìÉܹsÓÛo¿="¿·šý;ëĉ9`…³°¨Ï[=‘y¬|H‚0lùÊZ¹âö ,T•¯68‹ÝÕn›9sf×R½¿~4¶kÖ¬I .ìÓÿìÔÆŽ˜ŒÂ(§'CÓ“‘ªnö1RÀ^ìšXÔ'Å„×ÓþóØoØØº¬·Á[•[…®¾úê·>bK£rë#ö³÷48"<B­»£F`5k?ÂŽ`µñ1¼ÆG¡‘4yŒ†žŒ¤ï­ÑÐpÖ&Ož °¨ozã7ò•ñÀ][±/¼Ø2‰ËÅ‘Få+k¬ðåöj¬¬6lèZÑã Åþó•+Wæç‰ë#Y¾ÿ<Wì?}éåûÏ+õæ›oæ¯ýÎ;ï4%`5c?ÚÛÛG”kÕìý˜7o^þ~âù";™•˜$õdx|g´ï­fìGù‘¶W±;ñÑGXÔwEøð–[nÉmØØ»rB;vìHW]uU^1`ÊWÖØb)ŽÚ(÷‰Ç‡=;cÆŒ®ÁGqDÎ#ž?Εòúë¯çvq(S¬¼±ÇHo_2±¬µ|AÔs˜4[?Fú9eš­qž¥˜@b9b¢‰É«<û¢'Có5’7 ›­[¶lÉ0Œ× ‡¬x?‹†µb‹`×®]ù`éb`õu÷é‡~žèÇhíÀjrÅVIqæè°wû»Ë(¶Núbµ“~èé‰~Œæ~,""""€EDDD°ˆˆˆˆ,""""€EDDD°ˆˆˆˆ`,""""€EDDDD‹ˆˆˆ`,"¢iÇŽiΜ9ù?˜4;iÒ¤4oÞ¼ÔÖÖVósŒ3&/""€ED£^K—.ÍÁè±ÇKçÏŸO/^LÛ¶më30,"XDD™¶lÙ’CÑòåË/»mîܹy èš={v7n\îp-^¼88q¢\•W¡Ý»w§[n¹%ÜĉÓŠ+Ò™3gºnß·o_š9sf~{¸g7ß|s;vl·çøñœß'¶¨øûÕW_½ î:”n¿ýöü¹âï{ï½7­_¿¾Ûûúîw¿›_äÈ+À""ª¿n»í¶LªÁÆG}ÔírÀÕÑ£Gs‡«µµ5\\W 9å 0Šë.\˜?nÓ¦Mùåûï¿?¿ýÀùån¸¡kâuËŸkïÞ½ùßwÝuWfQñw\pVþÚ˜E>}:­¨è>ùä“ü¶ÄX"XDDuWÀGvëœÌÄã{¬Y³få×;v¬ëqq9œ¬ÚÊA©ÚsÏpWèwÞɯ '«üþo¾ùf7pZ²dI~}¸o¡íÛ·ç——-[¦ùD‹ˆhh+Ü €• ¤k®¹&ßMW TÕ+vùUÛ}Xܯ¸½ÒM*¿OOË×ÅrôôÚ¡Âýš?~~9vkV:"XDDuÓ´iÓ.s‡Ê¡ªPá­]»6½õÖ[éÂ… 5V±Ë®§Ýq½ÁS=+ôÙÏ~6_Ž“'OæÏuíµ×j<À""jœJ¥R·LT¹V®\™Ÿª¡'È©„šj÷‰Ày\Y«jš>}z~ûûï¿ßãs»c·`¡ØåXma5ÅÑ‘E†+~¯ZµJã‰QcU€G€È¹sçòëâ½rh¹é¦›ºÒ®]».ƒšÂ ;|øp×uíííùuq[Qo¼ñF^¡­[·æ·/Z´( ‡ËGø•?wìÎ+î®ZÜ/þ®r¯¦ÎÎÎn»&ãèB"XDD ×ÓO?΋lUeGþ…â¿€¤"ؾzõêË &àësŸû\·]w¡ÈAÅsGÞ*»èâ(ÄBO=õTºñÆóç¬Ô»ï¾›?Gy€¾üTQñw`"€EDÔüŠÝ/½ôR„Ýwß}wCñï{ê¥pÈ*s\D°ˆˆšVqòÑÈTgi݉‘ñª—â¹b÷dœT•ˆ,""""€EDDD°ˆˆˆˆ`,""""€EDDD°ˆˆˆˆ`”ö™Ï(5l‹ˆш,"ë&À""“Y7‰`™Äˆ¬›D‹È$FdÝ$XDd#ë&,¢á9‰½ñFïÕ` ñËÀ"XDÔ€IlʔޫBŸ|òIZ±bEš8qb?~|š9sfZ¿~}·û=z4Mš4©¦åéãË÷úú›6mJ³gÏίۗ.]š>øàƒ^_ñâÅ=V¹ÆŒ“ר±cÓäÉ“Ó<Μ9Sõ9ã~õR_>ËÐáÇÓ#¬J÷Þ{Ozè¡K¬º|ƒù^jí À"XD£°æÏŸŸÖ­[—.\¸.^¼˜^ýõüºÊɻ։¹¯€ÕÛëÏ™3'íÝ»7¿>n_»vmš>}úkJ•Ÿj€UèäÉ“éá‡Î æÞ†öª¯ŸåáÃûÒÊ•_K?ûÙÍñɦcÇîÊ.û2Èì÷Rk_À"µ€5nܸ|¢¬Xµ¾~ùýë X¡óçÏçŽLq[8@×_}·ûL¬Zµ*wm¢Â)Šëª=¦Ÿå#,í‚«¢Ž»7=øàƒÃæ½ôÖ€E°ˆF-`Íš5+}÷»ßMètXµ¾~hß¾}¹{ÒÀ:}útºæškºnÛ¿ÿe÷]½zuÚ¸qc¾œQO=õTîU{L=ë¾ûîîWEÝsÏ=Ãæ½ôÖ€E°ˆF-`Å.¥eË–¥«®º*w!,XÞ}÷ÝA¬Z_ÿÅ_LÓ¦M«)ƒÕWÀŠ× çñǯú^‹Ë×]w]—ËS8E×^{mŸ>Ÿ¾Ü÷‘GVq°fôê` ö{é­/‹`ZÀ*×±cÇò‘oŠåŒŠ¿Ãù©ššå½ÔÚ€E°ˆF-`Y7‰™ÄȺID‹È$FÖM"XD&1"ë&À"2‰Y7‰ÕuSj¸,"""¢£ÿClTôm8IEND®B`‚LineAndShapeRendererSample.png000066400000000000000000005717241463604235500366270ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/category/doc-files‰PNG  IHDRXr5˜€IDATxÚì½w˜dUµ÷ïã5‹Wïë5+`$ ˜•¤¢^ÃÅÀPTÃkæª3CÎ9## ’¢„™Î9wu圫ºr÷ ÷Ù¿½¾{¯}RUwãÅW}~õÇçé®sN:•ÎùÔ^k¯õœ>_]½ó5…OÑ7¯Ñë^Ö¿PCþ(úå6Dï‚¢¡!†4“ 0´PƒÁ 0u…¿ýu1À,ÔÀ¿fr * åþ ûséñ)zçë Ç§°žO ôz ç©ÑÛô'sUÀϳß×ÐðmùœÃò8‰Qù܈áɸb¾ÆüMŒÚþ“ÛãÁ:˜VÁd@1(ƒÉ™0XˆÕÄB¢ qE(YátD2šTqJ.ŠPªæ¢‚I¦¢‹`ÞŸ¾XEÌE«`6RÑTÁtXÁ·Çg’`Ì¿¨Ï‡Öò9“s0ªjj`2TÁŠ˜Ë‰‘ù‚b&­¯ÁŸ£þù ï1ˆÏRô1>Eï¼¢g*Ìgb¡©˜.˜ÏUü|Óæó6•ý¾&è•ÿó:ë3ؼÿžÙ2è)~ùxDݶ …þŽõNçÀÐ\ Üwï#àüë4돧m—ž¾ ΂le'ÈŠ¢"[¬ƒ\©!ò‹5E¹ ̢½<_ª·¡ öûKŠ‹ ¾“Ãã7Í1%³uH+â™Hd™:ˆkbÙªHäš&ˆgÑŒ"œj€`ªÂé†!’Qðí`Rá×S-Ò„ÓM‘ûÈã¼MH®'rŠ:jBô8)?Ž?^×Ô@@~—‰=–$–mú?˜TÌÅ`*ª(†CŠÁ@ô}ù¹÷»4œËMЯáÛžûÙ¶ucîkÛ¶?Àèõú˜<ãz<Ç}žVÙ§¿úüÞuæ˜ô6üú`$¤¥¿ò¹½l$¨‘ç3b4¨ÑÛãôX1¡™Œ6Á„|oíXÛÕý?Û†ÀMçÎ?mf=¸T³åÁm€Î³Ãóy…¯Fç²`,ÜÃóE08—Ãò< B-I M'ÁÀ¤¢_^爡`ËÁ€oô Ì‹AÚ'Zr?Š+z‡ o"#Ëb0¼¤àûÒ¾%|Û|fòœIðv ¬‘¯?a\EÞŸè—ÇI OÄÁÀxDzA †ZŠ â9]Áê VW°º‚Õ¬®`u«+X]Áz–‹NÞÄv)%ÛY²æÛ‰GÍÇ <±FŽ´hõ/(úäº>,½nPÊX¨€ayÀ¤\Ù!‘¤û$d’a’9zœ© è›L>žŸ’eDQÉ’õ<œ(¾`nŸ¯jèõh(XÒÆ’ g®ÌS7j’N· ŽÈׇàç?0Wü\ KÊ æõPëÇ‚Š )(`:f#5)9ŠùXøäIÙ§OÎ8A'AÍ‚? ‚I)c©&à“| ¡X÷#æcu0®€™…,˜ÖÅŒüb+j`*R,C,O£³0"Ÿ+Ð"4b;©ðº1úKX0ùd3`,©˜-‚þ©4`Q'>¢OÊÐ’<¨?—„ù,è÷ÑÈ –"#7ÓR’|ÅB °,™Ïþü¢‚>Ÿó™”?ø»eÄ]OúüŠžÙèe´_Uc]Däm¢,¦o1䯖'ë6ÉûiÜÛðÅÔ#Xš~»(ºà‘käA±]cäjÞÚÆ+Xjä·ã uŸ~Í~X'’ _^8‰Aó¼ôóÅscYu &?ï1Ȇ”ù €˜˜Mi)6ÓañÈÒçŽÓ{©›}Ié¡& Y‚µõ±>ð‡3׃óNÕlT\yÖià‰¾@•!ùƒÍÎÈtðâ¡Ù<èŸÍA#FM00½)Àç:Kfæê_ H¹¡ež~ÍPx ôE@¯¼®4rÅ£NnãÇ1‚Õ?H®´`™mÌ(”,¾Íï' Ö ÉIÖTÒ#Xü¸]Áê VW°º‚Õ¬®`u«+X]Áz¶‹/&ì¢CVvbѱò¤´h9àõ,|ÑÁm}Qé+æÊ€_ÐÎÔ=²g.DRª`ɠΧ™¯w,u Oˆ°g4¡˜­“«ã1„‹LxT=“7F±Z ‹ç$T¿†ü¿Amã¬@LÌgÁdÄÊSrç>ÍQø0B’U¾hÌÌFÁ|¤,—WÁ\dÌÈûÏ`?‹`JþOLË`|> Ô2MhŒI#Fäñ,TCòKt…–-qÖ¯‘”t0,ΜcgÉ“%²=ÓEÀŸO^È\ô§À€ßšó91adš³r°¬|§>y¼@‡õбȘˆ4˜Œ´À0 £Cšëæ‹Ê_ö:YKø»Á_Öùxý`àçcÄNç‘ ÍäÀT°¢epëÕW‚sNYoÂJÙ"6]}5Hd* K"UhÚD§® eRžžóœç8 e9)W@K‘Zf‰• ':a³„ΖÔÇA¡A,;îãHd,ÑaÁÿéˆJ!±NÕ@(© ð8Á˼ŸûqH´8Ë9M2¾T°Y’¤#UÕÔœÇ éx[L0 ó‰Ó8MÀ–þñá$K°FÜÏùo-Cž ‡Š«çY¹ÅfÕÇ ®.P&̧sgÚÉÒ°¼ßpÐʱ2a0}!¶¾×Š¡9UŒ¯±ìNRNјۖP±|±ŒqÚÅXH1N© 65ê±”`-ƒGx \{Æ:@¹™çC®Ö«Ï=<5#óE#Vƒ )„És•bB°`±L))aÁJ+D¨i\‚5@i’¾‘€ô•&Ô¦÷¥–"EôÇ@¯Ü/¡ÄF.8Ìç ²Ãô÷N*8D¶…]y\ ‡&¤\ ØK>?¾®ó6üÙè VW°þ!Ë}‚·óÏ(X·oÙ&¾ôÕo‰7îúfñ‚¼P<ïyÏ/yé.b¿_;úqÅÍuëÿ¡`=¾mT}Ìñb÷7¿Õ¼/ÝeqèGŠÓϾX¾fÅZÁrïJ‚åÞ¶+X]Áê ÖßP°&(iÚÏì²_ ݳ“ÀnÂ] / i ‘¤ GÀöÙE`…çj¬äs/ŠÛ>™Õä/ï±a…ü4œÌnÂ5M}ô6R®€¼PÖ>ÝXÇoB¤Z 9ɺW>w„QõÌÌv37ݳ5Gu0ŠY‡U+ 梋f¤ÎS,ľXLÎ'À˜¿¤X(€Ñ‰°˜\Ⱦo4SÉ|œuÎÅk’+bÎâ³Vê4:µâ¶kz\5Bƹ:CFÔÅ›kHò2¶F§åk ´¹_g“o¥_oz/fcŠ9)ÅsÈ‘«ùf_Z°ìï …Á”~Ðcp–zGz‚zü-¾\)h|N8GÊqýåW€)VÄ0Í’óU€É9š/3«O‹FŸ bÒ:”hPBù¨,Xî,3’ų¾G‚¦”Ë ^‡ÃŠ1z­%QÐO#c’á°•?e½¯J°x9W-èëƒ$W4Y6÷Qe&ZÖsàûê‘3Sjb*èn¿B‚·aÁë VW°þ!KÕRaÜfÁzë[ßî8öÿþýéÂΣnØðlRlúóýâû?ú•xíëߨ¬¿±`õ Í"h¿Ïø°¸ó¾­b>RS¾¬°sÄ+ÿýU]Áê VW°º‚õì ÇqÍ…WÇW-©ª›¸å ]¦ìBÅÃ{<@‡¸T¡O=CQÏÄãzS&Üç ÇYáB¯ôôÒÌ/»`M+X¼zÚ ‹Î“ê,XÎíÛ]ÄY޶Éã&ø8zÜ´ kºöÑC‚6W±=Žª±Ôc+xɹFü¡3ÉÑ\„U~艡É8õÛsçt8Q‚,«z\¼œâÆÀ_µ½¿*üdr£\9wÖz®˜ ä…®!N=ó"Ǻ½÷ÙW$ -ñ£Åø¥zÿtq~íëÞ >{äWÄ]l“’”2áx¾x&׃r‡¢&ç£"]l‰p,// U°@5Æ$>JL¦™_É•sЦ#uñÕo+ö?ðÃȲÛáŸý‚¸í®‡Å\¸¢ùeàÞÇ)¿þ¥8øÀäý^ y¾øÈÛ·õ‚Re ‘9ù·Än»½Y<ÿùÏoxã›ÄÏõ[oh.[“RQß<úqÐ>$v‘ÇE÷¡ãzýëß ¾ø¥¯ˆGÛn›mØjn¤Üª“·A¼i×ÝðÚ±lý·\¶ÛîÖqüò׿ksu)= íö…'ýôWŽíßýž½¤ì–Ì,YÇ MÅ^{ï«÷å¬PJÍÞ;ê›ÇŠúzžo½ŸûÂ—ÅæŸÄv@ŠÑ;?8éâïÚC¼èE/Æûö²—ý«8âÈ/‹‹®¼Q Tl—•ïÉíÞáÚîðÏ}YœÅbF^˜g¢ sq—bªM^•©‹äºàž!Dt-x8!¾rôÄ.ò8è{ö¾?"nÚ2èO(_~ïOøùñº7î&ž+ßKš/üç±bßè;û2Ûwö âÓŸýŠøÃÝÛ<‚µÚ>÷ØgÇúŸþöl#¿<Õ9zLÛ:÷ß°d™R¡A.Æ:L6uxmÔœÄk^\ûo:ª˜ÐX!<RÔùVôqØvV~XLÇ€ó­8 9b°ŽÍ]¤”‡‹Š’òx‰)Ê¿ÒǸù›Àe§­œƒuö†uàÆ?l$Vв¦bš¨OU,Xœ£dr¯u±Î!)WC¬–W°tVêd¶,‡Ú'¿3#ò<(ì ©äÙ—J0¹þ×ÀXPô> S}®pÓ*tªE‡em8Xý½ã€‹šE¬+, zK×òâëïê[*1‘û±í«+X]Áú§¬ÕB<ç_ñ'#X¿Ùpžc݇?ò #XÿÔ¡Nù8ý\Öjû¿òêM« Ö«_ýÇ}H~ò³_‹ëþp³˜XHÿÕ‚µÚ±]qÝŸV¬ÿóŠWx–}ç¸ï:ësG~iM¡4»`­¶íͷܹ¢`xÐ]ù]Mñù5ÇÊ‚µßûtlÉ›ÐåÀ-X”ØN¬&X«ϵ›n7‚uïƒÛÅË_þŠ·'¹"qÿ×U¶û{Ök_ÿ&ϲWüÛ+Å–ÞPGÁÚgç{ÙßF˜ÜœwÍ+ –{Ÿ¿9íÒ6¥ëCw~¯%…«+X]Áúû – râ¯C¬pa·.ôöY\}¶ŠØ&áØ]ÛÖvÆHÊ\ xCwµ6´—•mÓ9`ËÈTÝ“€în1Ò³B˜ËsúØ·ÓqS-,-T,Zv)s‡Y08y“üW,n½Ã Û ¿ºîX?ÍJ” a;]!W -SèY{®ºc\ Ãfæ¦Þ‡®Fï~|3r" THPCrÁj*¦JùLl—E *¹©¦jVñ:`é×ù­ïØCœ}Õíê=™+‹o}÷'ŽíÞùî=…?½ŒÌ¥£IôÿètH„Œ>ðr!YˆäD¡ú?â={ì)þtÛfO–D$–'œô3Çþßóž=Eº¼äkOƒrm”ªKàÄ“~ÒñbBǰÿϺXÄ ;-ŠO÷ö³¡<ð§—ðÜ®½énùKÌ'jâø:íÝ{ìÁH/> Ö"(»îº«¸÷žÍ¨_uÃÿìXwÀ“ó12å÷Ü/š®ŠXNñ®wï!6Ý|»Hå¤t¥Êâ‡'þÔyÜs/[ˆpuIùæ?yÌçOˆ©¹°w!Âuª–T"¿ÒE u. rÌ®‹ÑòÉzpu)[5`‹”·Õ&ɳöäýh$Š$Š’Ò}Ñ‚8þ„{FÈHਖܞ{ïëàëoórÝCOŽŠ}â,£Î {ìåÜî’kþ,/âU±å‰1ñ½Õ¹› +¬vQîã5?–õ¤ù´Ù–ýüâîÅ÷öKßø®cù·ðËŽ‚åI”—RóÖwî!ΕÕë§n!ñÍï:?o÷^+ –›'§ â…®÷õöG'±o!ãeôÿVùƒÎSŸÊÌøs&N„Ü^hL^À‰‰X LE-¡b)2•ò¹¢º©K¥4€c²QT1#ß{‚÷Á³;…sGB^qãPß¶Zé´Õ¿âvcw]{࡬õŠÛo¹pŠ UnçÊìœÀmdD‡ÒxPÿl OeÀMlxfaÿT pX‘ÈÀ5Gb|.Lu{ª†j÷J`ùõõ)Fü`Æ&¹&ÕÉ„Y[€Ó`úûÆK£]°¸Ó·Ì1¡Êð2঑‰ Éê—ÇKÁÒ’ö·XY3õì/[‘wz»³ŸŸ[Œ¶Û{³'KŠG¬¸·ÛBMS÷`k* ¶Ë/aäÆŒ$ÕÚÌRtæqu”,¬W9]FÖX°xôÅ.WF±¶/(X°¶“\A°*ÀÈ÷£óÙGr8爥Uo˲¦óØzåþ{ÛSª‚Kp_H-mæ=”z¢ß¶ŽN‚\ydœ?Ö+åŠè·½',RîBŸæuÒ¥5Ìsó{·áÒ‹ÛÅôQ~¸-8eŒNªéÒN)-‹Ïz„cý…\*®¹îFDzC=\”*-°X[Å|,V¿ìIÆX¤ëËš "ÿ'’é¼øÈG?¶ê…‚dÎH ÷©XQ¸·I¦Š ]ª‹”"žo*`éxîòبäB¦´¸÷õ³~_üßï}×#|7Ë_­”CuÈ¡Ÿu¬ûÓm[D,[A.“'+k/£Ðœ3å.§@¯™Õc°ÑfTð‰gu¯Â†8ìpçqÜ~×]xÔ{ß$å{T®˜šÙH£‰R°ÊKÀ.ÒÃTD¤,ÙáÑ/•gå•Õ>ªªQ5§ÐŸ0^ó¼A]øÓ~±'z|Ä´œ¢‘ŒCŽø"Šº·»û‘a3ëøôá_DþçPZù’š€÷u1#Ýz&œ5ZÓ\ó,BžÛp ÑÞzÄÈ{ŸS/¼ÁH+—‹Î_îï¬}ï>7É}æû8òkÇ8¶9þ'¿§]r“cÙa_8Ê+SA§°¨ ¯«ð'K‘3rˆ¿j柙¨eiB3ɘ‚ VΖ)Äl ‰ªYƒœ#eF¿t+‘P Œ†êžÙ‹fÔŒÅN²q9s'^ÿÆÝ<¿d‘Óc¬ë6ÝꔩC_þÊzFLX°‚1ñûõ§ˆ#Žø¬ØõM»Š¿Ø»ÿÕ‹¤Œ¸æêëäã Áè$YG}ýè5 Ö„/"~ó» âÐ#>/Þø¦Ý<£3ª.TgÁ:÷”uHp÷„ð´`Q•SFjk¬ÉÙ°øÍo‡Ëã¢<ªvǵ’`Q­-«tÃsi$Ìÿu‚å>–@´ô¿¬þ±€øù¯×Kqïü°`½å­ïð¬{Û;Þ-¾úõcÅÝõ«2 òb¸û[ÞîÙî­r»/u¬¸ýÁ~%WGÁr‹}ç: –W¬šâÁˆ8á秈}æóÈ£j÷]I°Ú•r¸ú¶ÇÛìö–wˆC<ʱìª?ÿ¥+X]Áúû Í™ÚUº~Õ*BzŽ\(—`õt˜±ç͹–Y!Â,àY„ÑñÓ ¸s¯ð<ÃMçOéðžu« –Fä}¤×Òr‡Û –'Ì©E ‚e›•çÄ)IF¼të‚>{!M—XY÷Õ­e´`õŒg@ÿB›æÃò1û!Nê>¦1¶~Ëo-‚Eìþ¶w9¶{ñ‹_jþ'‘°5æ0”£e;ÜÖI°hÄ1V·Âƒ¶Ö5SñÕ¼b¥á\­YäË©Ü9{QUDTÏ@ »áV:DSq£Ž•÷ÁaFÓvGŠÆðTü颳Ág¬\ËÖ–Gïct¡dêQ™™v ‡¹¥”{³ç5ãqx6 g³€sÜŒðhF!“5)JA1*_zXH/+RŠ`Z1=S_ª%fK`:Ñâh´PΡ¹Ž6=rdDK1Y,‰£“1 K½ž¬®`uëI°îë :Nœ4sˆöÿž½³‡¾÷£“÷_I°ˆcŽ;¾ã‰ýøïý@”›¬ýösÎ:üÕ/~)ò…Ò3¬=öØS ŽÁªÈ}ù\#dîðÙZk¿ýœÏýç¿øˆ&‹Ïª`ÑìÂg*Xû¾Ïy\?ûÅÉ"’(ü¯ë…/|á³&XÇÿà$g>Ø^û Ï-Xc³q±ßûZQ°ö~ï~Že'þô7b2ï(XÄý Н}ý±Û›ßæÙŽ*ü“`·?8 ¾ôµo‹]w÷n÷¹ÝßS°¶Ï/:–ÓlÀµ Ö{\3þŽ;éd±uºð¬ÖO>«ó÷ú§¿ï VW°þ1Ë„ô…ÏÝ6Ä!+îòU×Mø5ŽX°œ’²ªø8p†{¦²€“Ü{ýM°Ò};íÛ1kp‚Õñ¾Žý(LXo" : VÏBÓ“ï©P¯×-XÎcvbÞ_Sa^K±¾oŸ–…S°¬×]‹Wáo#Xf Ð 9ÍçÌöx^| 7$>qè'Ë×½aW¬sçªÌÄ—€çbNù@táÍ×À½[éx"Þ¶}@Ô;EC ’;”W-–D³éݽ¾Í€îÏû 8·éßø¦¸íÖ;D¾X…`EÃ1±ñ´3û"©«Ö¤œÕw÷èM¹Pîc+U—‘˜î Ý¥ "•­Ïl-y½äô žåœqª÷‹½ö~¯cù=c¦5L»Ò é\Ís\\½Sˆ°]’;ͼ,U– Öþï?ÀYm`\¤ õ¶‚E5»è8€YÒãÓÈ%ñÄöÏ}>ðÁƒÅ½lq)S ‘¢8ãœKÄk^ó:-RU”æ¾?^õ|þ¸§MM\-ÿÂe®S’úæ¿ yF‚(ô÷¹/}]ÍøÓÖ;ñlÇ3»ÑžD‹•ÕÒ¥`©ånÁêo+EíÅ‹gN]ñ§‡Ëþäkç¹_³NÅ#ek,zÞÅñã«Ý÷zóS>ÕªF¿VíK5C6­kL˜MÉŠUyÚ­jì&©·4YrÖTØÂЦFW×w‰‹ '°‡;Ë×h¸&àNáFŤK>žŒþ‘ðÇs7‚Ëä9€`Á:Gžˆ‡zp(sÔW£óY0<Ÿ#¾<àäsj§C°´(€äò7: Iý+‹GpŒ`™Ç´ ‘»ˆf[ù«[¹`ã)Ðg“ ÏÌF×h”AÒù[JrÜ}ïÌëÀ%&tVŸã~,KÎû²<õN$€éåÛV/?pÜ ¿C ‹ž©ìOôNˆ;ï{̳}ry$Ų¢Pª‰Ývßݳ45›; ¯xÅ¿9‹|ˆÇ{Üs¿FcIÑZVhÑjÈ}k­Žü§?Ý*ÅjÙÈÙk_û:g-¢¥B ¡LGŽÍØ´¸ÿ!ïsÇ "‘©÷ºË¤H]|Ú:ïÈ–<¹n}¢W{Œ3þëßaÝSŒ°\9r¯´È¸fäÙË2LÚF²0Ú¤¥Èj•óí£M 3ÂdF§" ¯H¹f  Ôq{QQóþ»fráÍQ|<“‰e±½wüáìSÀ¥§mçR›Ì$<<¹mÌ`äM>O^ĻÀ¤/ ¦2`&V“þKî;vljÖääl\1s¡ r9åMdj 0í‘X^¡‹ûrÁÞ0•c¡R*r{bn6¨’E ¸¯Ü'ÁE}MK©xÌF«`|hLÉçCÌ%lÍe7¸EÏÖ$ù¢Q0]úhl2Fi”NÞMØÞw¢+X]Áú‡¬÷ôùþf XßúÎÖti'X¿þ·Xægžã¬ŸþôgkÚÿj‚EyU«íƒFªÎ=ïBÈ•]°~ò“Ÿ¶ÝþG?þéÚêBý/ëÁCÍ®µ< Ö ?üñš¶&‚KæÅ×xk,Â]p´”´¾’`÷ýµ=W»`u‚dí¦{ž4‚µÒv¼û©¿‹`µãÏÍ!:«É»$C'þZÁºìÆ-žíO»hSW°º‚õ$XäÈ>ƒo•|&·Y!¦†Éåê‹*ôEº‡j#ùëCx+ ‡Û,Áâ‹z;Ü9E5Ð^°¸•ËŠ÷uÉ'Üd²‹ó›\³íÚsŸMzðü}Àa·~»è¸åÌýüù9pÈÎ&XîÇQd{ܹEÅd PË s_×¶Q²`qn½.R°ö|ï¨ÌNU±iÈÿÅ/~‰8ࠃů{ºi3®ˆ±@Q|û»'˜Ö'TÐóÖ;6{s¤ªK VST« 133çœH4.ZÍeÐlQTCœtÒI&è5¯~µØ¼ùÞ5–+¾].WĦM7Н|ùËb÷Ýw73_ô¢‰ý÷ß_üèG?S3ó¢N9[r?D½Ñ…|ë¹X)…àÞú–·ŠB¹*N8ñGæØ^ýùÜo¿Ç+X‰’ˆI±ˆµ¬.:O\tÚzÏò³Ö­›ïÞ †§ÂâÃ>g^ã7¾iWñç;jÓd¹™‰¥ÊâøïŸèxOn¾õ®¶Ŭ&XÄô\HvøçÌóÝu×ÝÄæ-i+Xœ£Å­Š’¹ ª"¤5MC<ôø€øú·ŽoyÛÛq¼ô B ‡}ö âÒ«6éZXªð¨'쫉Iù9<ú;Ösý÷W½F\¹éno›¹ò¦-â°#¿Š‚*£Ï6åXuÌѽ~s±¼\J¡Ÿo¿Ý}~Sd’gÁ¹Û¬UŽX¦Ö"X/~ÉKÅÇýfüÑñPåuÎkZ‹`±,õ.ÔÄQÇžd^³WÊ×ìÂ?l~f‚¥ó¹¬}[„×sµGH‡!"Míf 6ÛcDK1U@¬ä¾S«Ê#6îpŸZ®ÛuLÁ0u L½%g¡Ìw½(Ûóq –õ”L$vˆ'·‚kÏÜ.9MqÞ©ëÁEg§}Ö ä@…J'ü4Èï ÁÝ#¦üY0(€¹„j|OÌ.¤ó ’bFpݺdQÁ­¶Bs "&(]ãŽE+ªáå Qà÷)¨ð3ËW$Û¡Œ"˜Zd L OƒÙ@øä²Y0ÉT¼¸ž×D-ÿML'>)™ E@37 ÞGW°º‚õw¬~[ÇúÁ… à ¹vÁ"|Ô/PBA9HD±X…|´,¢R,‚VK a,¢!%‡¨Š€r°€^OyY« %ø1åÿõzK!o·¬E)gD™ÑI¦) Á2Â¥ âñœBÊ•]°ø¶Ê@®X°.Bç €äÊ.X‰Â20¿S5IPò7ArÅ‚•âü'ý?È+H~ ‹KÀ.XЦ¦ÕV°ŠtMAÞ&òå%`&1è–:k,œ ³}¢ŽT5x,‚j\|ÁáJÜ&ÙYWõæ„fäßPŒ#GF£ó}Ü%ÚÁMÃË\nÕTùJž~Žûv®Yµ,Ek]¾¦}u¢`íý¾ƒœaícNTÅ$»‚Õ¬Áê4‹Ï–Xî)WÒu'Á¢Äs¾Àn‰nÓ®ÎUO›Ç²ãIGÃg›`édw‡P¸gÆ­"´ –™yGa8$-'í¥H?¾ëµàºP=)‡ÙÜÉáöМ©EÕh ‡YMˆ°`¹áÇ ã‡`éY}þšm ®aåo ×´² –;DØY°t¢ý‚u¢œZ‚“4Çç2`N~è‰`n$w€bu§¨Ô‹‹uP.UA£ù4h6w–#<-’¦e‹•¦)e‡àmˆµv‚j±lË’2E½¥hÔZ B3 k,}{±\eF #™‹ \]Ü09™ÈsR’BÙº qTÊÐí×].R¸gÁ2‰ãM3›o,ÜÒ¸Ó]båhÊrŠ–ÕP˜CƒjæšgyÁbà•Õrƒ:‰\]°Xh¶ÝxÒQb ­Ì´‘!3‚¥…Æ uIT_;Áê0’ÕV°:Œvõ¹^3‚¤ŸSZâ8ŪãÈÙ\ØËZÏ…Fu{Ý2§} åþ&ã€ûWQ«¾8ñ‡SÜ ù'âò Hd+;A©¡¨4þGÔ¤<•J Ô« µô4h¶v–šv#XnÁbª–ŠÀ¬@`©*sßæ’fYQo‚J¡x»¶‚¥q Öâb ” eP©Rm­`‘ŠœJ8×,*€X<8—Gq⹚xdófpýb=ÕÊ»8Gž`‰+.¸Ì3€G«è¤j‰•‚ņG”ø¶}=2ºeÉâ‚GåH øÿ|©ÌLÄB°X™25‚¥Ë<K#çg(çŠH)üÔ#2®˜)¸Ðä$µ©ÁçUçá„jÀL/Ù{ÑÕuiuQµfþ9 _¶¬!†GuÍlA]z¡Lœí`8GÉ=²eÝ^2¬&4«R9îë~<íöeÝ¿©é$nM çóCg›¬=ö±ú±„´8eËɪ¹YmÆŒPq©[Þ”]æÚåZ­†ýXMo<î«ç­–Fdéc¦rl¾pB¬sNQ\sé¥`l*ü± Dr"š\T¤ÊŠHðw×Ó¶D0¤g–0£Xð§ÀÜ| bË5@J~—  û"‘(€””("‘k€XV‘”ˈP(" Q€œL š&¸|KBž+ôQ•øÆÆATþ8%Òe)eRÄ÷ù‚ZeÝÌ}!VVø"ÀL¡Ô Á#h±l t«+X]Áê VW°º‚õO#Xýþ*OSnó«S.è VW°þñËæj'µájÞÛëoYûÐ!"wÑÐÎ’Òì,XºVÏL<Áòäk™°¢•ÿdÉÔ2B9E2]!¼@ÝÂõ¸Ü ¹g" Ì @×ö}+†ÏÁ-Xý+…èܯÇB °` ØÚÛt’>~½óeÅTPWzwîo;$_S@é0›”+‚fŠü*8¼Ãé|¶LšÚ².ÈÙ4ó®Þ\Õj Ô+ µ´C1"ái ïKrÝRKÁÛ˜mµ<•¥ök¶v‚E{–νb9bѪך€kXñ>Êò±ktì5¬m)—*Šr ”¤\ê¹ï,X‹Ô;‘r±rNåAŠføe©T¤òË¢÷É^p©<Á^jËÁâ<Œ N?ŒM@º°Pˆ‘÷Ã"Cµ§2¶ V~L³g½5ŽC°¤„RÓH™[°²ÔÇ&X²L4ùv‚Õp@­l‡ø4˜‹)¸~çàXù7-Ð.¿Ç*Ê„¤aÈä5<‚±j˜NKË­ ¦Ù¯¬ÕöÕI`#pž}X¹Z«³$qû‘–%›æ5h9psK‹†+_iÈ~¸Õɰ­q¯Á’ë”ïä@ •©“¤gþ™:I®Μ:}¬,Ù«<îp›×†Ý¼ßº æŽÅš 9'( èäBu¥ª¦Ndma@;–@µlÒµäBK‹”¦°¨0·±Ì+]D¦X,Vny+!ß–IÞ×Cü Å\\Á25ƒ–&Š) ×6r‡}<‰ë¶Dt믑ߡ¡@ûŠäÒYFV-§œ8ÅÂzœàävû¾‡Š!y[¡¤pM3;„þ,‘² •C(;K“W´Ú‰éʰø …[€Ã¯H"×r2 1Ûð}L{+ÚÑ!;× O+dØÒ³´(~=ëŒøYRÈMˆ5¼ÎzÏÔófñãö0ôÙ¾÷ÆÀÅ×jøNœ½~=¸ý–[?^ÔäˆÇ­k‰Ì"ˆÄs€ä$³ŽÁ”5«T2yP *8DÇ!<#BzrLÔ™TðÌáL©RòûOä—A<œ±… sNBþLà‡¥·4Í4Æ9j ¤óU°01b± È`†²‚-STðLGÆÔÙ DA(˜0Ï“Å1‘©ƒç¬ž U_³`y%­éÁê”ß³’h­&X,N!i¿Õ×¾Î-Xö}ÊX£ZNÁêO“ƒeFݬ|«ÕÊ$˜<¬™° U áÊr='MAå$ƒþª’´ñ &.pǿ̹ŒBÿ\ÐÖ@@Áý™†UÀy(Ü·kr. |‰ˆåw˜ÜªJC±Xm,S‹y¶4$3R®ˆÅò"¨H!–ä²%¬ÛjR®j¬E°„Ü,-Wz6¡•?¥F»JÙ,à‚L)_4Íœë¶Ü);Ü«0ŸNSãJ>ŸÅJT!UK¦O!ß.+ ÈäJ $ïS–2UÆè•,Ár –5J¤¤&SÚ)¡ ¸é’ Áyò—+Ђuöºuà®;7ž­C³“Y…iE£½zfºfªþ€Î|-–$*·Xµ,Z¿h œ5k°®ihÔmäjdÜÄ™ÅÊŸ¨ù¨b:\<3pR~^'åEà>cò3>f+ôééZ[QÌ¡v#;Z®†Ú”)h;êê$XNø"mD‹F¤BÖ…ÙþîcôÈY§Q¨°Ž#ULÓQ^áÙ,onßfIrŽ\±¨ Ù$Å*yÐtâÎß ÚËÈsTÊ-XöÑ1÷ˆ•»O¢5Ê啳¯ã*ó8Z¨8çŠÚ½³‰ðEËâ®k¯m\.=ýÀ%Z¶l¾pž•)¿’.Šl® 2¹EO€ù±“¯€DªbÑ hDJЦço¥ó5P‚D˜r,¥:ˆ¥(¥  \[EyÞ# Åbmˆ£ <ç´{Ë,Â:çè’1å:MOƒD< òöR0‹ ÷í¬&%Ÿ3‘ GA"š”Ï; LQeý¸]Áê VW°º‚Õ¬®`u«+X]Áú-XΡ»±¯kFž;Th“«UŠ®‹´ê(XÓYEÁê{‚ÕIfì3ãxàª‚ÕæñLÃUy‚뀭i˼7Ó9ÐN°úýNLسkb`2 †5S8mRóûxæõR#†©Ãº„[Ù„j8P£š¹hpq¸x4øC»X£:V;7L®PAP …âˆ…à€«fëi€R¢Z,Uœs±X,^|ßj½ x½6%TFŒt¸gùqx[ÙpH¯(‹%¥¨ÚÐè0¯Ï¥Ò€CŠ*ª¥ˆsÊåýë^Á"L¥s)XeºŸ+÷Š)f‹€ËÊoÚÒÅfFá½7þpƒWÎÅ:{ý:Àõ°â4 ‘f#’Xé€4ÃglÂ'>sÈáâE/z1ªÌÓ_>¡™Ù|¦u7o‹C‡¦ 4‡M3hûìD-TŒÎ¥pÏ,ŠË“9È)¨h8ݤ‡Vu1«jgC˜™6™ ÖÁHˆÑ`ùÝ4=bå¬OõÌ m¶¬Õó—¶U?·.ÈK€·1ySZ¬F‚ËÀ^´¤otšeØ^°:Ít –•CäÍ­j/Qí^ wþ”%dꇡõys–†ÂË`8¸ÌëØ.?+Ür‰–;·Ê)c\ËÊÔ´2xå—×µÍÓrážhSSÔ(Õijyó >ªïtûå"Գϖßâ‰Ç·‚´("S¨¬<§äreÍ*’©"àúv…b d2Nf‰L©ºdäm"•È‚¼Üž sšý¼VZ¬ƒT(, ÙàÚVz„Ú¶&¯D<‘ùy êò,d%8Ų¢,ÅŒàófl~dPª.Yr´¨ðJÖ¢—+Õ@&ÙDÚ·s GW°º‚Õ,—`ÑÅ굯}­˜™õw,Úæÿo‚õáƒ?Šcºúºmbå¬K/¿Û’€Ñú]vÙE|üŸçœwñ?¥`¹ ZÒszÉK_†fÃwoù«˽_j•³ËË^.þãÇ‹¿Œ$»‚Õ¬®`ýÓ ÖÃsíg¤µ!²˜8îÓ!D¸ÒãuœÍ6QPM&‰;±{¥}t«N­^)"à¯,,(ßh)Ötlœt®àaÿT ØÖ¹áÇ7'ù¾C“)0²jûpµjn 2SÌÄš`,°Fæó€ªU›¶ò‚EÌÅ<ã‚…ŠE &e‡h a}YQß 8¼V.–ÏÞ³*+hö T¹´¨òDY┢”/…z|žÅÇIçVà7‰…@ÄPAÞŸCt´ž¾ôªÚ^³¤©\i€|&¬ûm‚¥Bƒ‹¾m«Tu VÓ, ‹¹Ȥs 'O^D¾´ HF(ÑxüÞûWqfÁâÛW\xD² ‹Y8Vèú÷Ñ1©Ö8ÎQªT® ¾ð_Å6?ûÅÉ"B3²UqÑ%W™çC’dï% r´fš8<;PÏfd‘â::ž>R¬¨ž•d.¦àϳ WS#ܰ½n7ÞmÊáãÒ#N4IåG¿>ËÞúÎ=;Îð³BqN9r Ö]è%ONÄ×UMÍ?ø±Cœrb’±½Àû±f±)ÌŒ@JZ·ÍT&ÁŒ®Ö-NôˆTh©còz§p¦[°ìÇí*=®ócX"ÖI°ŒÉ×’`!±¿.–¸©ýw«6¯³Wœµw½«ýMØ.gZ°"-àÝÖln>Ì„'i–+}ÆMÀ“7¢Ù%àó'ÄŸ.<\(¿çÄ%§+ø;?80ò… àóGNžwr$Y¶oÉTpX/+…ŒH¥r ›Ê‚Z_-^–‘ë –2÷ù¬\n–”5¢,ωDQž¿îüÀ÷aÁŠ-ø… YŠøñùXKEYÞŸˆù|À–\ç+-’ÜÊ«\߸ÕW.‘:ïRz†„åë>«‚Åå,A°ÍfÓ½ïŒ|iix&²Â£3–`•€[°ú¥Ë‘«ÔN°Ü²%E„0rèûñº‡‡÷Á’Æ3ôœBÕt¡#¨Ñ˹LCßd´,~0yUzÖ Í裙}Ó)0¶ºˆs[þ% ÞMDz˜‰4ÀÄBLÇU1;Â/×±âÓ WUÐHQ,”(ÙgzKÉ’)…`f²-™"E¹AòS° ”–¥ÅJ¤„È¡ÒåL˜O>U)ô§G§þïO~‚¿»ïþf‰$ÔVÎ’$4––BešLó¨”m=F¹j–°Ý»åA±÷Þû ¼öÞ}ß'&¦æÄýþÿÓ2Z748*…±fµÿ¸éñÞ÷î‹ÆÇ{ïó^qæ›Õ,B-e7üa“Øc=qÿ—¼ä%â[G#‚‘4Ä…÷ñÀ_¶‰ýö?P<ïyϽmP»Éy™ âøo-Þ²ûnØÏ _ô"ñ¾÷í/n½ã>)<-Юy0Bzd‰ò.Î:ïR,?ê›ßÆl>΋JIA:ø£‘ÐLDK¶”¼]|éÕâƒ:M£]ŸûüÅè”_$¤ÈÍRâ;ß;Q¼òß_%ž'×ï÷þÅ©gž/¢)Mþ´8î{'9Öm8ý3buÑUï|÷^¢Æ_þú±¢&‹‹?¾yÜIâÿ¼òUX¿Ïû¿Úp¡6fÔbÉ%BVH–QCdúÿú;Ÿ‡ñ⇿Üè­cOü5–ß|ÿ`Ç,Gî“¿aF³ì3ïξâñÛsùÒQlj§¦ó"÷ÖÙWüYn»·Ùö‹G+¶ÎäÌcl<ë ñþ?ŒÏÃK_º‹øôa_ „qb*/¾~ÜÄ¿é×…úýýò”‹Œœ%÷ývǾ[åq´—%WÎT¸s‰sЭ Xœ[Å¡%t&P™10å+¡È²é%À3ò,q]ú`I“§”CGXÄš¶œ+ÁRpé))W·v ¦ñü2˜ö‹?ž{:`Áºôtŧffç}æ UE¾â,)æ‘­t2 òÙ‚"¤Ä&-å…HÊõ 甲¼”¤\É`Hþ0-š=M°ÐpÎ&ß'Š)üP‘‚ÅbåAï‹~¬,Xi}’Ðþ¸ï)‹å¾–l?nyD­D?Ø)J‘H€R¶`¤°Ž€fÛ®`u«+Xm+ŸJ‹“~¤$ëï|$ëÙ,úrßy×½¸ýº×½Nl¹ÿa¹Í²¸ë®û°ì ƒ>à¬o|ýbn>$ò×ßçü"–]yõu«»ïÙ‚Û‡z˜HÊ“Øi§©Q•oó‡`ñÈÑN°¾wÌaÝïz—8sýïÄàð¤øØÇ?ewÝó’¬’mä©`åUÙkï}öÅú¿lðÖÄ\È”zp ÉÕS=#˜¾ñôs°îr Ö§>snßpóRªjâžž€H‘`}ê3‡cÝn¾ µwî¾ÿ ñ>)’$W×ݼë>ñ™ÏŠÉPMœ¼á<Üþê7¿Áúè§Ô}/¹þn$¯oºs+db-‚Õ;_8ѲcNø%.úOd!ÄV))$PŽ¥± …ûü5 ÖV¹ˆýÛÞeë’MêóòÑOå~¹á|Üþò7Ž÷Ö¥fÛÏ¡‘úÏ׫mÿã›ß5AruÏý"2¿øz½÷;è#¸ÀüÉ#pûùº ^×6¼.$;—lÚbÛwÓÚ·<Ž®`u«+Xm«“Ì´ Ù­2˰õ˜œ5™¬<¦8h>\+Fh¦2 w¶Ìr-$í«£0®"¬)8Dè!·ÔôµÛW° Y‚Å÷so§ýØÏ ÕÒ†Ù|:Ï‚C<ë‰Cxá<«©ù4 ¡šO´@ å$šVÄrK€›†b%(.‹TiÈ,îÔˆ͘õŒ¥ˆ‹frˆ°®ëY¡¦•üŸ¨Ö \(+#@FˆZf¿,n«\©B6júþŠ%ND~P: aúÁ 'âö>û¼WüAGˆòÇ,ÁR#J<m –%aüå¦çÂËSP]t›FqJEK°ŠÚ¤aq¹ïщ,Ûs¯½1tþÁ}·ûzE*™‰xF]Ð_òRǨÓÃõH‘‘ž±)påY§.4J#W´ÝÏO:­sz{Å-º Ë<胪~Œ-´‡Â}Ή¢54ŠDëU¾†Ášé§àýpþ–)šW­,Hi_$Q¼Ï‡·©67©:¥íë†E€Ô&Uhp6Zûø!¬»ï‰q)Tô=P¯)ºP»!º}ÇÃ#¶‚ö†ÌLûÑ;â-o¸áží&ßê3ŸUáÑuç\ƒeëÏ»·?ÿ•ÿrÌ¢cYàý°4<Ð;äóâ__þ qõ­9Ù÷õ>ßõø„’“`Ã<gVK¼·}lÒ1"FÛEv€äÈ<ØqÛÕbn,*þå¹ÿ‚×c(ºd^—ÛsŠí8î|l òCç-³ïÄÇw4l«?å,/-í¤Ä¬ìÑ™Øyëµ 8CÑf[KvT¨´³u(sŒ‘‰,{îU;arâ/ÓÊ'ìe\~މé˜b6^)U×Ì.þ˜ןu ¸pãzp™”+âÂ37‚y_ó‹ ›¯)²òÿl púAFž? .Ì9Zœg•Ëä冬””"©áP$å<)´D•ê€B}yÊ%•”ä`‚‹a9KDb )WD)-ʹâÐ ?Ÿ¿có>À¹SØg…qæ‡ñgÿqH6vÁâA¾OW°º‚Õ¬‹8æØã°lÿý÷—¿àòSÁ": Ö¢ # #Á¢°a§‹¿]°r¥eÐI°x„‹{’`´ŒÕÿ–‚µJ‹#¿øeñ©O*þýß_…Çãõ$Xï{ÿæö¿ýŸWНüçÑ¢wt‚Õnݶa?‹Ñ;½6$Xûìw¹M}íŽüÊÑâÞÀŠ‚¥ª¦«¤èû{Câð/}Ë{æ¬Ëþø€-ª%>yø—pûŠ›\Q°ìPÈòáÁ¨côg¥ç⬕¶%¹Ú:]Gr„øä‡?&^ýò—‰½ðEÖz)X4Ze]>ÿÕÿ’ⱬ¸ï®`u«+X˦ZKèÎl³/®»Ô;¦¶T‡Çã&ÁífÓ±Dð¬¾ÞÙ"p‹‰C°Üá6Ç6 Ïz»¨4‚¥SUó`çÄrG¨NÓK!FGˆÐµm°ÙQ(Ýô.TA?5\žL;‹¿ ÜÚƒgrp&RW,d€/&/rôË_ÂÍ–#®FË# ‰tpƒármÙÌðà¤>žqgäI×–*çË &ÅÔm•Ùu’¹ zËY7жaé*\ÙË$¬k+/ÖA>µ†wæ_µ¦°NQ8‘Ÿ=æ×þó(uñüЇEFžT8ÉÝÈ“NÈt VÅ!Xê‹k,JT¯Ñ—·f,g’; Šz¿/zñ‹1¤Íb”JååÉ+ xÖ_®h…¶rň…“à¦Ë.,T¼Ÿ³Ö¯›ïÜ X°Ry5k÷—„@µPåô=÷Úëß6bÕÒÒÌ2¦Â:ï‡DìÈ/~ÿÿò7ëÄCõ‰pÒšAJËïÿxH|YŠÓKwÙÅ,ÏžûàóÚ;ö¬{·\7kŠçêç„FÌ:Ùy8\×4Ä#CaŒ,QøŽïûÎ=öiS‹iÉ™än …Ñ/²|½öõoÂc?2œÀhÐ+_õšŽâaÁ¢ZrøÈ§qûÄ_nt̼ãçBßsh¸fò¶ƒT›ŽF»Â¶ÊíòâÿéÏ©Q¶üqßW‹¡ù¼MÀZRîb_sT®Â6KÏ#R^õÈ]NXŒô¬>®N®¥Åªaåo8k[H$Æä¹‘˜zð!0:—Ãáž×”ê´ïvbʼné,VCQÅpDãI~oyÄÊ+^j;>w³ib’šŽS¥fH|‰:g ùƒ‡(Tžc½½âªÓ×N ¸„fçœ|þÈËsa+cÍLK¹"rR :F°Äž5õ«¨–]‰Ë)D¨&K‚l¾ ÌÌ< ‡#ó "—Ê–/«fž á±4%# k9¼Ç êý8|ì‘™À!LJç‰Cî‰DÖpu+—ë '加IF&D¨gÄóõç9íFbâ_i4HÝwÅ|'Ýš¥g4¤\ Øò˜<‚Åe ìûë X܋дrYaTÉ+ENÁ²¯·ŽI¾I¹ê…`©-Þ–eÐÌl7‹OcK—©ðŒ`½#Uœ«Á-.Ìsү頔+`«©Ã‚5!¿¤Ä¤”)‚gÎFk`f!|ÑŠ˜W_N(bòG°`%SeE<TËåuêµGÈd£HjÄÊ#X¶Ù|Ü’†s : –ž[³’¼Y¬ò¹"°F»¥ÅÈË/,AC+Rø*ö,,[þÍxtÿ’ùâTÔ'>ù)%Br"Ñ/:{.¡²å`ñ—›öeÖW¬/²‘ªÂ¢ù¿@³/i†Ü÷C?ŽeûØ'äc´Äþû€Ûwß}ŸˆÇ2€{meòΜ)‚ÛBÜó‡k—kxëá‰?„`Ý|à âÜ3ÏIJü HdU© Þ_,eñäv4‘LMüvýéXÿ­oÇZŽV5uqô1Ç‹|ìShYÃû‰ØÂ|tîbÏë©O ÏJ¥Q©û6e¸äÂ\¬î{tĬ£{ï«^›ëo{Ää° I¹"¬"ŽJn{xÔÜ×SœÒ.X®™oé+uÒ÷ûþOeѹLGsbGñp‡·Ë‹4‚D"sÃ=ÛLÎÑžú¹\}ëcmJ4‚µ—ÞöšÛ5ÏÓ”-ˆ.›àÐT\l¿ñZ1<_pŒpY¥–äë2f½.Rö2Çñ¨gDÊNçÙvmF\Ben·õêÄ ”b$º†f³àÉ®CÔ•ÚtÅž¶Xéc墠,<žY}^Ì6$í,®g$XºX訔_bŒaÁ’÷*¸ƒ/¥ð§š š­ƒõÏËSÞ`è‰Çŧ­¿ÈbFbÎ*DlúšrŸR•–ÂFDý@2–/7\ØŠCüT9ˆÈô`9Äi.SSX… 9âÁ‚¥zÅæ PÌååóV¸àw«+X]ÁZ£`å²qÄg?o “\wèa*Yú´3ÎÁIgxdR|ç;ß{Ö+ˆ@°úFÅ»Þý„Ÿx²‚uëmw«œ¬=÷ý£¬ûîLì·ßÏH°N8öµŸw¿KløÍ¯ÅÕ—]&ößo?•‹s׃k,‚Ïòó“…/RÀºn¾Ûz>.ÁzÛÛß©góëK®Üä,Ê¥ºåžÇ X×Ý|ŸI\'¹Âº»ƒ`]wó³ŽëòîÂíwí±x°×ÁºþÎÇ $”«tý[!<—ßx¿IÞ^«`Ý*¥ŒB(I±î\#X÷m[p„În¸û©5 ñÈp\¼ùmïoÚýmbëtë(á\$½WÜ×ãDzë䱓x¹ë"³í>âþž,»îŽ'±- í’´éNÖéç_ã,¼.w>çrùX¯‹†‹®¿ÇÚw¯Ë®¿óIõšv«+X]Áò –w^IéÎë¸c!ç`©:X+ÉHÇœ&×ãÁ’¿ø#GÁæ á»–Æ ÉAjô =G¨NC³pΟj—/åóY2¤ÀP=ÝGÊàBŸ¦ÅLÝ`-SpÈp°®à“¾¿§’ iuñ¹*<'áVî&A)N„ÏþxYJV ,ÄÊ _ñtp½¢Lvää†Àpª«–“ÕÈXÁ³H5¤$,3U[³c­Åj˜šUF¤umÙ‹Bw"dÁ’BD˜\(§çØ~V/¦Êz{H¸´Øùe!±zسi²qØaŸÕ£VMáóÇÄ‘_ø’xÁ _>ø£âáG·:‚Fœt›û2 àÙ«lfÿ½O—p úÀÅ#<$Û­¾]|P.Á ^€í_ýê׈S6ž…R&gJ··¡$xâÑ{îçI¹§m?8îÛâÍ»í*÷ñ\ìçmoy‹ØtÓ¦P,B™Ð%›'*ŠdUôþ?ãÜKÅ8ØäSQïS‡!Î<ïrHXáÏ`²&î´!?$¶?ïùâ{?ü©Yï‹×ÄÆs®ûð!ŒêPò÷QG/ú¦“b*R§žs™g]ïtIíÄ•7nût0F`h ×ýì÷ç Gñ÷g]‰¤muß_ýÖ÷Å““¹¶a¯v9GTžfÞQù«¤ÊÏzÿ?¦f‰¾q7Õ–ÅCgÁ"{ 7(^õš×‰Ï}ù[fùå7ÝÇs<)vöýp}+#ï¶ç‰áèqËC#âo‡xîsŸ+ž'×}ôw¬:ZRÀ~ö•,~M¿z´|]¦òb4´ .»i‹Ü·ó5ý…<{>ç"¹ó¨V,—Pñl?¬D¬Q&DGE)Ÿ1°m'"KîpoÚÔØXI°Îɺúâ‹?’sV=ýj« HÔD © ÿq[~îì`)Ô}¿„䊘‰(LßÀS\•‹äÒh- ÖXHa.ȺÒ7O± TÚFP:åÙF´†U›Áúñɪ\Æw~tòŠ‚e¯V¾zþ’+¡Û‘‹µìÄÓƒOK †¤ïñ°õª³ÅàÀ, ¹"ÜÇÈûdÁB¨µáxÍÜ ã]Áê VW°Œ`ÙÚ«twÍž¾’9þ׉âφ`qsw%÷Ëf[I°¬ûiÁšJ÷¾Ú†&Í1,Ó«Œ’Ñ "åäw®S5LèF²#ÔªC·ì Pa«‚ïXpŒN'Õ¦š‹/µ¡!zf ·IIË/Aµ†ˆP0¦H•äÅ®è ˜,dn°<JP$ùÈä1Êæ— q¨®LÉ%Vâà²%Pwû^o…utúèt##>Nn̪ OšÅÇRÄmø6SSÉ"W¬þ’¦’R¼äÉ€°Ú¼8[»ðút*ø1T›:p'bò—¾/ƒ¢†¥‘†´­&ÏJÆXθf ‹V&_\A¿`k<Ê'Àá§ž—H©"x6áù8ËðÁð¸O„Ó 2E,HQ"|1ŵȒÜqËŸA&]üã+«‚-ÏU5~&’ôNÂç¸T¦8ÿ“ä¥Hœhæ1š=«iáPDåõ…àNI)UD"SÁù ˆÅrÀÌPÖ³©¶^uõš J*æ‚€f5ÇÑíAÕÞ#xp€ÃŠ|Þ4‚•HÌz¯.+jNxR –©­˜ˆƒb:# ©h¶vî$òœ¶£?ke e Ü3ñL¼÷Yi_&'i: úæ* ­`qž”«OX§çbÖ;ò±jŠ©¤B —¹OÀI¿-OÌt»×¿–¹÷ßÀB¸ûZ9Å8¿d|‘™Ò_¸‰Ð"›Iš8mÊ[!"E¢Pëæ˜”ü2Y¹,URX¿Ô|*GðZ¸Ål¡»ÅÊ|(µXñ¯€Y¾ÄÝ.F¡÷¯Grh=³Šwº~YاÎR¾’-/¦^¾RôÅ—”lëXJËx–œhQå¾]™Tp¯/ž&]´ÎËÊ“$±0=®=ÿLp®üE{î©ÖL£ ä ™Ø¶}Ðü:ö'ª€«¤ÏJ¹šµõõc!šŽÕÏZº ¢‘&{¹iec…â<“ŠÛÚŒšmê;cºÃ ×í8åiÏbâ‘)µ3Òáî÷g¥áJç>{÷žûzF’:•pã4#XhDLnG°hüq°0í$a1lW3-¶Œ`ñ¹VÏLŒÎÎ,´·é08`å kÁªÖA!W¤Ó"ŸLê“Kð}º‚Õ¬®`u«+XÏ’`9š w«+X]Áê Öj˜ gy{)ê·Í|3!Bfk´­Sµ–¡[°zçõxk—C÷s²‡ûŒ$!/J2•&_J‡ý¬ü)7´­ãBZ°éþ„¿ ¬x¼:޶¬/™¾¨°Xqý“ÍT¸¢˜Kª^Ðð—Î|ÙŠM§ŸŠ:ËÊ‹1±XÿQn(Jõ§µ \WTå2¢Tª«æ‡­¨žE¸XW8güI¹K¥çYM’-Ù*–j ›L+Ÿª¥Ñ¹U´eÀyK\“…g°4¥iˆ[Â3I¨²Î%`¨¡1Á‚Å'S,ž5¹¼Œáûò}X° ¥HÓ 9ê™-|¬¦Þ‹¬‚”+¢,e–¨T—¼ÃÖ,3ÇÞG²iB"LÈ“!qËÕ—ƒsOYÎ?uƒBŸ„·Ü÷€Hw‚<¡F°¢Š©ˆ‚g«N0ẼPÔLØ×G¬0ß8ªjjS(W3BµÞPï­¡q†ÃÖÿVÃæNÂãm<ìn¶;RðãY‚Å5˜–5mj(…–«¯ Û_/°<µ—2K^l9NÁÒyF:7ip<žºú|p÷¹ÅŸ/½ôJñ"Fc;%šJÞ87Ȉåd¹äËslîÙu,OíB„f’¦!„6mMM˜NÞ¦šSQÛ>(wŒ˜Ïƒ§®¿<|ñFpËùŠÇúfÅx|'€ F-±s —Vçm:Š•w?F¬¢NÆcŠÉXLK±"fQZ…ÐY°¸›A0­à0>&Êà¾ë.I¹ºÈ&XÊï9ñø#ƒlfÄ"iE²"Q’­ H$ *שl ÄS%@ÛáPÄè~:¬’rE¥\Û ––¥Xzø}–ÛF¾´`™ð5——ø}1àŸ£¥ò´š €4 [ãf}®ùæA6‘HU©.+<ƒ*˜óŠ+ OÄéŒÈÉëÁ¡AÞ¶+X]Áê VW°º‚Õ¬®`u«+X϶`™sÿ‹a§0_{Ár×xZûã™ Ê3YÀ‚EMI‰vµ¬¼aLgk³oÌøk©äs®+Å¥gë™Ù|ÞÓÉèÃ&IÝ>CIÍ(2÷‘rEŒP£å@ÍÈ”Áäêë!Åt¤ ¸¸â<Õ®’,øS NÉëž‘+µ@¾¬0sõ˜+ØREÜrm‡¢®(Q¸ŽÚTw€jíiP¤Ð… t+tÂIæTèälŠeÁZ´µ-05­t5ßL"8è®úK¡¯RYÁCÜœ¬™-ÖÆ–_ZÂ$NÚ+ ±eª Íž`öµ†!‡­a¨$÷°* k â÷&_QÅbxCªVí®rq°hqåz–W•¯à É<óò‘»îg¯_X°ø$üçoɈd\õ&¦à¤snÅÄ"ä ãÕ%Jjv‡û8IzÄ&2&4d[6Ò&TgÝV!®Á¶‰éûææÞ¬x];n±[’íH¤:UBŠä II,b%Y¶\dIV·%«°©²Nï˜AŸ!¥óí÷·÷o¤ìä¦\ëYœÎ`0ç<ØoK\ÎM³Çö€× ù'=.ŒGÁdê30¦„E°á7þüV„6l(Vü¹ª:sìñ>ôèŠnC„V|tWv¡V™˜?©„I¸<¶ ¤:RxçÐnðûÇ5Ÿ|<âO¤6®nÑú#¸± '¹³·Õ„ú& 3É&˜K¬&`çvÎÝ\õš ’iX¶ –"ðò‡lÕà~$ºïP²¥ùðƒ ¥DLˆÅò š®€U‘©Ì:ˆ¥J σ¤:G µKd5‘4du&¢ÇÕy4Ž$÷4ˆ+!(VV°”” Z°2 +ŒXÐP¢V–`yrH¯,¦Â°È‡©nð³NÇH,-L2pí2DLvwé(¡s®©èf'÷BVúve€íÝh¶½Æ Vëú+C]y=d =Ã+X„Ðõ¬Ð”zˆÝЦKèºVÅÌ ?µ-L¶gUßeuÜ…ÊžìWC>áR°x›Ý®T­zŠWV>Óp•ÀŠUR³$r¥XVr%xņ­f+–[šÊp3œôE¼P®VÕ•+Ýs›Jý˜ÊZäòkÓò WnT×”t¶> –¬HqZ9+;(X%W‚ÍM2 çº*÷duNF÷(¤ê•/f%)­ÄHˆ«€•“‡"%«{òéK‘6ðYÊ[,6Z•Rd³Æöó!\¡'D™’– Ý‚e>UÙ¬5YÁ*—ÖAç\,þ±Ök \(õj0_ X]ä<Æõµupéƒ÷ÁcJ®–r?®NÈÂÓG«ìH7À¢¬by.j,¦jÜÊO³Çj?˜´¥(Ø® [tú5œ¼~K'!\ejw 0֘ܩž³ð8†¥³‚-Øñ\Óì!X<ŽÞmºV|bÝ·]«áeÌó¨ß‹0–¼ .œŸ§”\ ¯*ù8ut<÷ÌÏÀé‹KàÒÊ:¸¬> ãèº]y‡´Ád\3aOôf2±&Û:´p„WtFå½%?ƒ:atUÓÒYȃ §‹ž=> Þ92Þ8¤9ÿ‘,õ:aáù|RÕ{%‹Â×k{—sÕüy'ãMÀ¿' Ö¼+² ¾¸z¼,«Y©†-+\fÔÙÜb <øQ%W;Àc»5G†wO.L€h¼¨‰i"©5MÜÉV,YÔÄ ™­„:W ñDNõ@: AÉDðœšPçjÁÎ&-jRòV±²´ª¶ËWE¨s±(mÙR DVS`ufH®+ø· ¶:sR•`-/OÉ•g+ܾ'œl#*fTN.‘õÁ6—É6Ö@°‚5¬` k XÁÖ”`];Œ0ó½:ÙÛu V®0ß5ë‚’+°¸¬\™}EŠá>'N͘35¡š] >/O§AX°ÆÂÃæÄKbˆPz± ‹íÕãdÛΨ‹°Ò°·ÕRº®I®ƒ•Õ €`™ cÎl¬žD^¨Ô—Fm«®²Ï:`µ]&WÖp@&ĨiàX ó~Ø“‚e›w®7­81T–/V—ðûFå ™b xÒ§EÂpRñ§$&ˆ­â33ù’"" fyÛË5¬|…+­ž²ù:à­—.Ú?\¾Þal/X1Ø+DèFçè×ÌV–ÖÁZ¹ $Ôg+ZlWCµ*ÅX¯5@£¹êMWýÂDÕõu°27Ž ï¬&dïœ#ìñW ]jÎd^Cƒ6¤g.À=£BM4CÍ4{7ϼQÁê¬ ŠT÷}½+Bì’±®ñ'!iì8ŽÞ"u½*´‘Í*oT°:û4…Ÿ_ãxâ ¸pnœ>6 ^?´K‰Ö086´Èï\øÍ“ÇÁ³ÇN€7ý+ðîo4§^{Ã?ó»wÁ©7~N¿þ8ùÆï5¯‡xãMÍëAô}²?ðê+šW^'_x¼ýó§À;òÿÓÇÀ['iŽ> Þ~|œ}b`ÖëJ®„‹J°Æ•\ Ìãr}±zËRg®T»'Á<Ú ý¯Ìmã!¹WÓ†Ù¤f.ÐGްRu ¹·C‡ËÙŸX¿Ü?ä’梻`dŒ¨û…ÕXAÍkLá•XÎ_M–4Jv„pˆ0é­ƒ„:W ©xx½+‘LÙ ,ɵKH«sºA¡NÑ`U{x[Þ‰¤ÀêÜ"(JƒQ6Vfê†ù ìÆìhâ‹‹ £®3B0…×2~ð /"pÞl.ž2"‡ KùaÖŽ?Ö@°‚5¬` k XÁÖ¿±`…û6]ìèu=B&ò »¡§…Ë>Ö<æâ À¥üë V¤èCÕ)T£‘N\÷ôî0ŸMPŸIÛa=«˜¤k*›tÅ‘ {¬¶}I’VjÀ=ÖHÛªw#½OvÅ^Jiºúž¤J@‹Uçè^ÌÃC/ RŦpÓpÕm¤¨É)Ùx?;÷‹oÂðø—°`e•Ì Lú.C~/·Q°Ÿ …(•­„$X≂&YIµ`eÉVRN ñ¼Ùggu`8Ùã#¬L™ðŸV^ Šn©•+³MV½nÙ€`Q3Nîá×Ìþáš`¹´Ö AdªS8‚ßW« °Vª&e?–FË=–û[«hr^áuüÜÆ¾¾÷< 1¯ï]êõî¼ónp÷½ÛÀ]êk¾òºÙv¿æÞ{ïëäž{Á}÷?ào{àApÿiî7l»Ü{Ï}`Û¶·{€Û+îSû ²mÛ6ðÕÿù?À_ýÅÿþß/þ%¸I}ýío~ ÜsßC€¯ëíê÷"üx«æÖ-š[nßn6ÿkî7oÑØ¿ÿÈ­ê1?Rÿ ÜïÍêï"Hx?ØÒMßûn׸ç7lyÐÿ!¹tîã‡ý¸ýO!ð¼7ºÿ-7¾­þþ-àÛ_þø?óEÿÛ_¹ ü/ý¥æ¦/‚oåKà[ê>áïnúKðý%ðÍ/ßäï+šo~ùKà»_Ñ|KÝ'ü“úZàó}ç+šú²Ûoø1ßýЦß>±/k¾­¾ÿ6nÿø§¯ÜÔÁ·þ¿/‚¯ýæûß½Ù¿eë¿ù]ã÷­Î³`ËÓõ^ ÝÞïû^%<÷ß²5„:F{Ÿü}mq׉[ ¼ýÇw<¾û»é/ü¿ù¢æ¯Õß²ð¿¾òeðãÛïÿbø Ùº Èm·Ý~á.°uë=@Ρ8ªó¬p‡:¯‚;îÁs+Ï»[n¿Ü¡ÎÅ‚»Æèóçwo·ÿè'þêº$Øó2Ÿï ϵ[·ÜnÿÉmàžû´û»+„»êóöí?úàuð^õØ~×0^ãîUçê[ô/¶ßæ`É VÒ¶]‘i#4¬>‚õCurY­ifR€-\y9WÁ4—°¦¿¶CaMhòÒtt¯`éÕ/ùzR}¸<¼˜l€•ô:ˆg› o€Lº `Ûf¥(î³íˆ$þÉRkÜz8q½ƒB©8¸Ø†êL’;¯’ÙáHÂãÉ$2À ­4m¯&1&ÒU°º’ ¯bê¶§ÈTÁJ""ñHdÖg[%½Š†a¿`ïªÐ2rNŽKÁu~Ú)–Z %s¸Ì6öõíšEXnKdÕ×ü…—œÙg¥\^bBzëõ¦_k¶W° CµØ®¡*áDEC¶Q4åÿ–f­²ÖËk Z­ßþôxôáíàñÝ;Á¾ÛýSï¾Jë› %Å9ø\œÈ®íl12©ºyêïIp ñly a¸œ‰ÔSÑÀðæ¸f<ÞÔ˜Žî£Q-*‰µº'ŒÛÙ|ì,^1‹mtuNwû0}™B!=K´ÇßP¯§þsøº¨Ã!Ãk…»1«Xœaz[q‹aÁWù§Žï'öìOîÛ NßNß ~¶oxqÿ0x÷Ø>ÿã'ö‚wí>¡9yl/ø@=^8}Lsæ¸æƒcn¿|Ì{j{áÃ'4ÜçGLJÁIõ|öaVàN©í…“êk+Wää‘!ðúAÍùÆl’» J"}âóunï^¡2óC«^ß›mH<ÔûЬdM$4“ꘀp§ü}½Úe'{¨¿a.½ >:{ü|ïÃþÁ! »?{ü(˜‹ÀRT³1$ª`)ªÎ«Ñ¢!’É`.“Yœc˜N@ðÜšHd‡=§½2pýMtÀôŒÌ.Ø>}YIh/º°_¦¤á5¡Äèâ*$v»beÑ+eî:¨ÏéÑ™YQ×@AZ8p媸®á÷62£ÎÕ"YŸK°úÉR/Áê;^&”‹eKn[®ƒó“qÀœ§ðÀäk –ÕF&S༒«ó‚åÛ_°˜Ge0R%‚åzñP°ª#Xìwe›R¦l>W÷m A:ÁÒûpaFî¥×ÎÄ5l,Ç¡ž©|K#Í߉{+‚•h]U|ê¢(”$KžXЬúƒtHQŽéîÂÊB½¯B© òù2Ð~ ƒÉ©¢ä±òÏH[:–ì•‘cAÃÔ±äX^NXª "û½— “FEMjRJ äW¥²k€a¿¼$©¤™Û%(Ø0«©DIª?D C®ö±f š÷g¼(Uê §þ™¯ÖÝÓJÃQD•ŠAÆä( X–a°ßʺ“¢¬DLp‚µáò°dø(˄˰±yœzãUÀ~X‡Ô Y¯_üõ¯ß?¹R$óM°šÑp€³#9#é`ƒ\^ ì…Ç/.¶‚ÊŽnøsVYQ°ÂùF ¬~ÂÓ)X}zW…ÅÊöQºÞˆ”Ë£ê'X7%Í„Í>\ˆpœ–ÐÛQ©¬ÖnðÔ>•¤'ö·Ž< Žª÷…ðüc»ýO<þ äFøà Í;G÷‚÷iNÝNÓœU·½«¶>ÉR¼}døø‰}à#ÃàCõµÀ}œ:>l÷óž’Dû:£Ä ß‚uá£QÛh´_È®Wh°ŸX][¨BÄ› ü¼¶Ký}hø}Ë…ßetTÌýQ°¦ŸKµÁ™w΀göl·‚ÅO/üüç`)QÓ(•d N°Šþjb DåüªH«ó剂•T2$H•µ i<·òWr‡`ý Íc&=#º°ì§• ùr[c†WÛqo&¿˜çôØÒ*kV8eƒe_,¦ÐÄfçKú=†+|ý£`ñƒr&’f‘‰§sc+UÍ@°‚5¬` k XÁÖ@°þý«wâzP¬úWúY Ý}™q3¦RÀ ·i€ðsô –ÙWX°‚!E[5ÖÚÕËÊÜ®Ëu‡–Ü—äîiVÀŽç%Ð_Ž4\ï,CL”SÂ&ð…ãpD¨æ ìwb64\îôrkõ&*5mþb×,›€]Ùm_¦T`”Š* ·­Ho¨lúbqp.W:™¾s`² 5š¤o#TñÕ$`·tYvIëšh¢ VWÓ žZ±´†!ʧþÏÔ{´Ä¥Ó°B:µ ®šP'Oz©"(ô‘ÓÞKçûYôviŽm¶qÕ—¦¨ÀÀaÚY%TÙ€Tr%»Ôܯ+;ËŠV¡Ök"ÜИ>+5ŒnP2]*Û«Ö.)~Ã>Ö V© ®^õÁä… à€ŒËÙ¹ÃUíÜîÿôÈ!+TC…ž¼ñ‚†•®³ÉXÌ´ýÕÜà°ZÞ7e`dÖÓ,ç7ATP–Ôíåk õîJ¸÷TPXr=²8t8Ü¥½Ü)) Ev%ÛÛ„ò è׫´bÕ9.«c›åÒêÛ§‰û÷Á:©DFNîgŽï'öìOïÓ0qü}%*Â)%NƒãÃ;ü?Ù§9¶0¼÷Îñ=à}eíÔqÍÙã"`{€,õµpNÉ”Þ'Kªr|ÏðîÑaÀç;«¶ÞSû^SrõÚAÓ+µ láç¬~"5¿>¬pÿ-WQØì`¢C¾LÏ,† ¡Æ5ü@óîë¿R”À$÷}êú&¼þâK€£¯–b°Í[¡W\“•žXñ¼[ù'ý¬,OîSHÏÿéT$•\ )’R¸Þ†¦úÚY|9â{2’LQPö+M¡#Ž4£`•dtZ¥ÕT v¢I¥ ¢J®/žÁë\©Úv‘¢J6 XvôX,©‘ë…Ù{gñ<ù…pe`/ѹ®ü„ò©‚U…—šipe©oÕb°ÕBèy­`MgÀùÅuÀê WmØrÕEF°lµ`hÔó@D°8·L¾Á²cof2€ûp ÛÈ4»‘3;:d&8om*^9²˜¬ù«©`µ GH¾’@£.–ª ïe³môªLcû®lQ ²Êº…rÀÔ]ãK~¯)²ê+x™(t´'à¨ÎäÓPpb« À2]Y³ê2U™¡‹eAJÝÌŠk¯Puó°L%eB}²ø¼aÁ굂åKcWŸL5 ¿·³ ½‚}ýøGV4hÅkf>݈P ü=ó%;²ÆÊ•)嵂Å*BJV¡ Ö«M;‚e'»YrF› —`1·‹‡›W>±¥pT]`ž”÷˘=»ÁêJ Ô[Ÿ¾'Ø0pEJ»K ÍjZIu®ÞI¾ÙA²Ð^Y“­ld¿-ÉÔ+içÔ§nó Ç,&¿«Ct(0áU©Þ+X×”£®}†è¹°@5¯Axkü[Œp™L]ÌÁzWÉ•ðŠ–Yõ9¾g'xrïnð¶’«·!YFfŽî×üæÀ’¬àµÇ÷æ>½£¶'PÝGÑ!²?®P±+Xg sÁ¸JQ;ªácxl«÷V"ŠH zkÜè ÖH×ÖõïC‡`óÁ„‡³©6X”–>Š·^xœÁzP°ÞzýM϶Û4DãšUÉUD×h4‘ÉX¤¤=N¡f+™ƒ•õŠ ~XM+ù²ù2 Ì°­Í«Z[Á²÷…V£Šê\!Ø +×FS ÈYæKq!ÀV*¹RÑ$(É9šQ[M¨ÛÙïÕ¹[’Þ¹/–iu¬©h ðZPÇ' k XÁÖ@°‚5¬` ëÿ–`ÝHÈîú(é‘q4ÒðsÆÓ„‹!C[!Ø!mÇÄã…™, `…C„—2f1²d›‡F4-,'eM”q‡Ë ræhvˆ–8VFMÆ4ãs)0­‚éD°‰œäZE= ó[²rÑ)»¦¡ëÕ+ \®‚$¡Zë!Xöð°ãXŒ ä3y€7P¥7Ì=â›ß†ÛLŽR¾è†KÛo¦9§/c&²GÔŽÀ¾Tº±g½ƒ”·ظÎîËö­2¨¯sçK—@Jˆ*Ø;Åæ p³Í«ráL[%ÈpŸùc‘ÜýúõÈgŠV¬ºD–¯¿TÈ™*òTR'•¾‚Å*ÂâºÆôÁ**`M S¸Ñ(E‹‚UÊ,JX ¬XVR%°:±µq”Ô׳‡+áà®¶7ÖèÅQÐÞü ðg`RšEˆù°•—æõ ¿†vìyäuä ‘ÕEqõþV<Í\RÃ!¹ñ:J +¶ºÂláÜ«@Ï­®ñ;fpq_y²ù[íM+oTÒúç`u÷\ºÑ¦˜‹wê ¸x~¼«ÄFé`SNæ`ÚXÕwÚpR*ÿDš”ÈRø‹Çv‚'†ïÞ(e§¥_–â”Tüu0l%·Q’(Oï gž"džìc\u¢s2Ÿìƒ'ööÔ’jIá¼ôÁJm7\úÚ¡ÂÞ„›~žû;ïã!û}—dµ{4%ÕL'›`>½ÕõDxýÙg€„q™ƒEÁz÷Íw+¢äJ°Ãž=]¥´ óÁ7™*k¢Àj¾L¾ ̨s¦ çV × YuÎÒ‰4`z‰ûð®¯m’¤úž•‘d©¼ÝƆûŒp1¯8%K¥BqnHj %©°ÖИÆÚ8 ^<´¼päQÃ~ÿù#ÀñG?;øxAÝ&üFm#¼pô æØãàyõõóò¿â7ÇçÉQC×÷À ïó_8°<¿¼¨ŽSŸG`!C”¯ÜÎ|ÙŸLo‚qõ{Çïç:2•èAŸ‘9]Õã±ýak‘ b+°Í”a:Ù,áßÀB¬^~ò8:$‚¥aáÙS‚X¶ "Jª„h$ (U©TÉõT`A< ˜’[Iú­á}¾ÿµ¯ùþW¿ÚÁæî=~%šî+X¬\§4YÁŠt –ÛVS–ó‹"™ðÀêÔ,¢.^»˜VCibÏEûŠ2!I‚œ:Žà±ý·ø!åÕ×Òè”úb+1O¨×o5øX/ß_°0#ýé'a]Û…FÚ‹ySÝûhúŸzÁÿîâßtÓ_ùõ×ãßrÛ=þÎ/c»¿ø‹¿èÉÅ™V¯îßù˜¿åÞ‡ýÛîþWÅCþ鉬]ýz`÷AußÜ~»â¬ºBuÉ¢]ë’9v}!]عÚvi: ¬”™j$+’æç•ýN¨û–ÐÎÅk`z.f£À™nËi=À¢n~’™]gZö³yY±\¶=mÃÞgó´LÜ8<:‡³ò2É,Ð"E:E+¼¢•˯ƒ¬AÞìáq7ÖþYÉÁ•uœ„.ðF XBYó¼°,æ˜][‡÷¼"ÈdJ ë˜MUVÝ'” åÂ+)¬ø+J —`Éj’àªo\°ÂSÚ`mæ`U*5àV°Z]ÏÇÕÊkMÆæ(lζÓRæVÆtnW³qll| >zûMp`Çv¬½êïBxA‰—`Gð¨íAKC᪶4òuÝrEÓÐÔšÞΦ¨lˆ*𾪒–-oæoÅ W@$»æS®1#›8Ž+1 âä)¼ä™²Ê×V-š/V/º}“f}[·pu^¼;Ū³œßUuJÙç©v£¬Q°.(¹Þ=< ÞPâtæÄ#àÄÞààÞ=àõߟï¾?N2 >šLj¦=ÿœá“͹™4ødZóñ”ÎÍf5syƒ:wÏçÁ9Âûæsà‚ßËc„÷GVü3À{ï€?È «Ñ,p‚µ$·‰†ÓꃩŒy@¤#ñü«7ߢ…ê{ßóýýû}ÿ—¿ôýÇ}ÿ?ÀíŸÞz«_˜^ô³êÃrJÉUªC°L~•i½@’U L2ܵŒ-€L;!³:&ms„Èä myÈìÃ’†cvVæ–ADI¢)µýT¡¥É5Û%s Ê71q„ì«Kq‹—Ô>Ò o‚dVóŸB°¾Ëíþso~bZ6(ázúEÿ«_û¦Q€IDAT,;CÌ r%ìyüÿ±ã¿²÷?zì×þ¶ûñ¸½GÔ'ñ¿±Çö˜ºï~uß@°‚5¬` k XÁú㫵ç-W=äûÕªßñ¯Õòý;qÿÆÃ»þÌ‹p}ä*8î%<²ÆÑîȇä_]G°zñ¥/Ý„}w– R°¾ñöOŽÄ•ìl€S£iÿï¿ú5lûïüÀ?5æÙc<=šòÿþ¾Ö㹂‚մ¤Ë„/M“ÐK.$<u¡P|ÁšŒjæ[ý´‹ñ*XVÂjºÙº}3PR8™²Ä7œíGeÂ|r¶¡@Óø³°V×°GeùK5?­Þ˜‚~.Ž X™¥ØP-_X¹l TÖ‚•7&GK!E'›É'?î§rpÌLøvŽìÉ£‘©~M(cV ÔÏ.Øp_+u_‚Õî,yýþœV°”Ðk†"ª[Fx´èP¾(@Ìyã>(7…|)0DŽnj|9Áª‚5%WB9_•@YÖ¢GY*©cÖÔk/0„Xí,-p”?JL{ó*˜‡•XF¡ä_m'=ŠêqBsóSÐRb¤¹-nÛÍ–ÆÉ“Î kšQ>Íf»ƒVËÝÇmùúÚ*ÛëfäÊW@<×VŸ°¢¥þV,—+¥Cw¬ØÃÈÝè‘9’3êÃÀq$.×ü1زó¾®1(Þìt½|ŸÓ'§k<µ ØëíÃCàwÈÁÒ•wGwo¿øõK`*}¥ƒquŽ'7ÔE0ÇkBnSLZ®Þ>&Õ{BjÃVó96 ¦(¶ß°}«äù9–ÉôU0›ý ||êCpòÈnðÁñaÀ‘@¯Òœÿè²zü&OlB9P}èZa¾±¤!$^ãIÇç,;*'áAsÈ3óvymYVïyar!~u`/82¤ÿ†…Ã{‡ÁøÄ< `E¥‚PW’%$3ë %¡@óš^)>…ù‚Ÿ}÷{¾ú$è÷ü'’eV² £SJ„R £®Ó_l*‡Ti+âQ%X’0¯°líb©:4y[¬èôªA6¯fïHVâS–Òêg–f—Á²$!‘—fÞMË4«ù9–-–©ù[îÜæGRë`I½Âr$ç//'+¦ùØÿt‚ua¥æ?þÌKR°D¶„¿ûßÿà?üÈqÿ“ñK‰—kV°d¿r›<7ÿ·Ç¨ŽAß6¬` k XÁÖ@°þÁZíMˆÓgí÷¯ùOÂ…j»ês/ü9 Vgµ]8,x¹]Éݶ•&:³Úg<@ié–µ¶Í¯úñÖmþûSù®ãÙ ÏßùèqË]÷û—f³ØVI_R'Kár”+^î¾ãm+}»ˆ¹è§õÏ·?€Ñ7ÀT@^ŒÔÁ%„›ö5³¡#Ò•½ ØU7’iƒÅe¬¤j ’iVs¤‹môÿÉâM§ËѰÓz¾XÙt”×\ò:e$_ÑØ~ FšrRÕ%Õ}F°´à˜BÓÛˆr$%0ü˜Ï¯VÙ—om'sÛ3D‹VYzF¡j±(m§uÇ:`tÛ+…g¶‡È±#½Ù6—-×ÃK÷0ɉ\•!Â57.Û8yÑòTÌA¥Ö2hÑ*HŽšìY\в­À 9;¹W7 ¦ŠP}-”Šk€ýª¤òP°eøy« êx ž [:ùc}±°˜PÞl R‰xú±=à1TêôÁ=»@<›W>"CÇò4$ Lêû*µÐ9±2ë,ƒíåeáùs¹ªX,ñ ñlÛ_HµÀT\ºÞ†&0xw:Ù‹YÍŠaÑÛ3Jv£g¢¹tÛ>–"eû‰@8Ú€S+QpbgõX; ì×­½HˆïLÈ·(¡˜äþ‡Ç‡ÀﲘŸzTsêâ2˜P#prÏäyó:NÆ7€“…Ο!ü³t'tDÃÉh" C¶¿(cf3àäOƒ³J®Îb@´®||éÀN K ã¨räD«Slû Q¢}ÃÉï9Ê–yì„·óûIÃTºjÐ&µ«÷Ÿ°¨>˜ +¹M0:>~öÈn ‚Åé Çîó P"’%’1c{ÆsöÃx&SÑH*†¢ö‹çtxðÙg¯-XÏéíjÏ<ë§•˜ ™läKm`“Ð弯ˆGâV°Ø =/ÝÜ¥š°Ìt’ ‹¥ÁÊô,‚©„’*!ž«X¶âiƒ'avjÌ+!")4kª†š&U54üÛî¸ß_ޝƒùÙ(XXÉû‹‹IÀN¯¾Ð/DÇ ×\¡êµÚ^Á2’rq* ú·8Ð\X©#ë»?øq q°YàM7Ý„ª>®`ÙŠE%8\¥’ÿ;š€®†n3«NÜwP°äûŽ,#X6ËÌ= Š• ŸZçÕ€À*§¨’'ae5XË7x2ß²rŸ±­Ò0°L´TÙ9YERd’P ¬Q¨¬ðØ‘.úB”/ÖA:áà¸×hÔÌ^ µ"Èç+#KRµa…†Bš‰hWtr%`ódæ¡AÀýs…*—-‚²K5Øð”ŸdÜÜĬÚ.‹í;«¸Þ¹rp»R@κV°” …Lðv+^Èq ç`iÂÕ›²bU@›'X¶ŠðFˬ4éñ:y[ÁÜ.Rè*‚%+n ™ú.0·Ë6-5¹R•Ê:øíSÇÀ#o÷ìzÈ×Âè…KàÊÕÏ¥¨ahbeJcokh¸ºÅû[­Í(UAÁrò¥e¬&9\ W‘Èߥµ‘SŸNÓ7^GçSwÑ™?…yˆJ–„å¬&jF÷DršåìXôÚ`)Ó2H%W Ì¥5 ^,e7«½fS-ÀœšÉÐ W/ºŽÝŒTq‚¿FVl—”\] Ö›ŠÓG4/þì`ó¶¸Âjàfè5mnPÇjKÞïFÀtWÆ…o›Hl€î6F΂¹N&÷iRÆ.)>zãUpFý<àè0xùàn ‚5í]ýVŽºVšBbÔq[?z Ù –‘ð©ÀH©)õžÂ{¹æ ÌÅZÊ^çΧ÷< Žë™ãÇÁòjp Œ&½ àŠ´¾±Íš3뀹Xµç~«Kò¬n`kí¹mSд’+ˆµásEW¢n¡«Œ0`“)n€Õh,LΤºžr刂³¢dGXVrµl¢EÂÔø˜‰Y œO4À\¢f•P ü~NÝ÷ã­÷ûs±*˜Qr–óþô\,ªóPûþÓ ÅG* { Ö¹…²ÿ•ÿñ·¬oïfÿôhÒnwj,éÿ¯¿ÿ*Žÿ;ß¿ÅT êc’,¹o XÁÖ@°‚5¬`}>ÁÊM-øÍ}ùŸ}íëZ°¾ñî÷`Ö-ºÊ076óç+X"Õê!XÍkVa•Ÿ %"«.N¦‡¤2oJ$çæŸÜé¿øîöqnqÝ?üÌËÊã¿ëíþsoGØî“ù²ÿàîƒþÃC”¬¥ü}‡î?zô >ñ+ÛÎG±ŸGýÒì‰_ÛžUòµ´t°ÇhK£s°x»,ÊØÅ™,–uEú¤7—ªŽ&Xñ4ü…//'€ *¤"A£ßÐi= ¡;yC›®O”®¢³ÕvRɧH'2 Œj½Î|&;<<”X ›Qr•1‚Õ]MgúB…(Ö4JT„j T+ /øÌI¢T­wŒõ1S©ƒ|¶ÜÏ  æ“ÙÐ=ÁV6®5:ÈKµ¥ ŽWæZQ–øüåµ:ȧ³`] #0R£yjY ÷£²M2íÏ«s•lQ,ó|öµaN–ù¾Rª‚µŠ¦,=±Š“·¥ŸÛÚ}(!\£Q}|”§jàØ Ù<`åa½± X©G‰yïåÀ¾nXìÞíÛÁ;¿ln~ ºE(…)”‹Å°b«½ÙIð>󘶯 6Ëôøâï†ïÛÍhÔ N˜‰×›üNÅ4·C¦bnÛ…D°±)?±ÒˆÍ€™Ÿ±ªþ¦£2°\‘,4WÞ™ò&H[€a † dLÖœz.aVÆgÉ1È \1-È«Uí`R~…4Wu‚CRf`åÜå àÍC»Áïû'Wž{ð‚ÏP• w&š©„2L‘u$ s†dÛ)4„Õâ±4á[ޱ2Ö¯/T@È(A 2Oëã·ß,† _>° œ{ÿ¼?¥^Á ¤MHŽ®%MתkÈ×D¢IÍ”¥S®€yg,-°˜¹>8ó1!Ï‚„Ùÿê×?ÿ%ˆÄJÀ†²”\ ñTIcBkÒX”£ÊÒsQ¿öø1ÿ³¯½«ß•×]¾_¯wËÕÐîoíØ©ö¥öó@*S^±­‘¿!W« «~"ê.8p4OªÐé|,G³`~j$$íFÉ@‘b®ó’úz ßk&GgÁ´’+a!Ùý¾%sê5&?Þú€?«é™¨f)ïO©}l?Ÿ¬ƒÿ‚uâ¹·üV"%‰ì_þÊßú÷ïÚï¤dJäÙ×üïßr›ÿ—_ü¢ÿ·÷¿ý¡OC®„ JÆPÒ´õ¾ès%ý®ÎLeì,B‘1Þ'¼?•Ö@°‚5¬` k X׬ÌbÒ¯yÂÿôÛßé«{·ùW¿ÿÏ:Ùý»ßõýC‡tÎÕ±cvåêê-·ú%>Ö‚uýawÅ]7‰ã®³yË%ŠË çiÏ£ V'¼½»J ž§’š¥uàkúSÅšök®n…G`ðö `ñ6×Ë$äÏd@¸»3—ày²@…‡Yfd…‡g./§@LÆHˆ0«+¹*HæDºêÀ ‘-j(X©ìRyÐ[°ÂY)5@6™•¡@;ö%«¨D¥h+,c¶ªÎ@á`ÒwgX°³z¯ Ýé9$­z–KØ·!BSèú\™Pe­JJ˜€Iꕎ0Ÿ©À㢚ºM³0x¹Ö UQRl6e¬P¶BP* EÌ#G¶§UhØsEÉ•@I+B’aÁªHgvéÐ^,vrw‚åªmˆÐtˆo •ÈÕ&ظòyÿ 3KdKxþÙŸz£šímvÉTgȯ3Ø%V½0¢,ÞN‰c¿/¾ï²20V‰°êiÓV_ͧ4sê¢LÅoA„…•Œ&.ÕEòÇ\r¥–Æ$çJwg!)«æë¼zÏ 2@Cd¥w‚•Sù€SlØþ:ªq¡Œ&`˜sÉëdÁ0'•ÉŠ)3*h2 0„}”˜ØMÁúý¡aÈÕã»Á›¯¾¦ÓÀ^è9š%x¡¡J© Žº,èá‡K{‘1°Ë>C¢AÂÇÌdv+qMGry0y\Ai:ê}púèæ˜æ%%W¹³Ÿ¨í6T2 ]‚u#¡¿ëI“©ˆ¼±¢ÈÚÐ Û{_G{ñWÏ!°ãÔ[ïc»·ƒÃÃ;­`½øÂË`YÉ•°’¬‚H4§‰R?1ñ+O?ëöÍov‰Uëþ‡üüÇ#¨Èó~sh¯ïÿŸîNîíC~n1aÇž±Óy<™æža²5°4¿l{r±0®®]¡Ç;‹Ñ<˜ŸZñLÓý#X‹†%%=kj6æäÃA¢¦I6ûÐò$‚%áCŤ*aj1ïONÇÿ8©å ýó«ºó¢œ€9 ç`uÉ’ËÁº4›Npú‰UP°4aÁº0•N°(KººOËÚC†VÀ¤ú0f+Ö²RöƒÀ¨'XMMH°ø)¬—`­¤ÖÁÒRHCQ³ÕÜ›¦êJI-5Àøtxü §—wVÆ1ªmè\a’|-‚Õ¯: ùDf2ãI¯`­r°ìþ .K7ïÌer Œ<­fç1±ôÞ´TÈbEªÐ5*ÁU*ºc£`1o‹ÇjÛ(¬7€ŽÐhnF²tŽh¡<•\ u%' ¦©+v5È5ÑÜÔѲ‚e*ö(X£rLSRƒ«òÓ9X%i¥ `WYW£Q Š€FƒãhÖÖÛÀVr†‹’ÒVr%,ÏÌù,•„gž8ø ü±Åƒ¨À*”¹-,G}W°„Å*Œmý`~†õú&œ ®%KšXAcó© «9ÍJ–¨íŠW@zí*Ȭ]9i¡`‰8›kW4õ«~Ù°&¯¹¢"¿ŸZ`ÛµMÀÜ’¬:F!]ÜðM\—+hêX昄H^³˜ÙÁêÅ©ÌbÞш’+AÚ3x|—æðnð‡7Þ3Þ°ãXÔþ;iùŒº°u{%O³Ö0ç¬S´~¯EÎ6׌·;p‚µaó´ì™ôUðñ™À©£Càýc{Ào÷ïŸ=çϨíÊÙdrÜ`ÝèªT/Á²m(:±‚%ǒ؈–›9È3J×´EÿÞçÓš·_y Q¯`h§Í|ýwÁ\´ ”\ K‘èVdç`\ Öøtð½ÎÇ k XÁÖ@°‚5¬?SÁZ™ùåÏô 6ïÿW?yò#%GiÈU/ÁZdA\D ÙÆ@°(X—{ÊN·L…*Jëܶ[šl˜MÉ•À0[Á †û:÷K9»4Ö,WÛŽÒÔê vÜ×-Xæ~%K?Øò€ gÎz`,Ú¬´a ÊPg‰å Œù2D¸¸”.VqØjB‰{Ò}pƒ$« ë•@p¦,Ý3+æ£`åRYðùkä3E°† ‡i(Ê ?ÛtÔôêԗL—|Œî¿•/Ö@Æ+;M PÙÌtÍUÚ¬l Täx׈TÔs ÅlÔ7ݰá~‚µ(I"2áÊ»~‚UTbTD¡ ÏI#PóÚ³¿˜}}ÙÈÕTi š|®$äi›ž†~GnK¡] \X°Ø_‹aNÒhk2ê÷$¼Ë?}x|xú}°”Û¬†ZR-a…d Y7p{ !ͦí?FÂÕn¬‚ 2‚!2+W¬¶3B40+'éÏû|qÈóû"YŠßîß>Q‚5›¾&¤ÙiÊÉZXŽnˆ>bÕóþÏù˜É@uæDˆp¥áL²~ÿ›çÀãêïU@£Qõ¡HxëíÀÔJH~ÐÒRÞÏÿ칞bÕ¸÷?yæ‚¿¯øó+°¨d˜ ¹ÅĺFîS¬ÄŠ`Uò»Ìûu)’ ‹)°ªMˆ©÷Í4A$]ss1»¿ˆz «j»<ÍìjL+¹DêçºnáƒGi$ÈOŒ-jf`N>T˜m¦“¤Ú–[ïxІä'”\ cË"X À*dÊï@°‚5¬` k XÁú3¬Y%…Ÿ>ç_í%V÷o÷ãŒúsJ\D®‚õ' VË¿—zÎ ìw{g’{0DxQ Š`¥&”˜ÞýÜýÕ/+Q.­Ô0]å×O¢z׈`¡º‘Ûþ·<èú^I¬™k –î³#U„ JeÀ.°KË @Ábu¡,§ê%U%Qù:`!C…LvÏ›ÀË­kRP¬¸Ñ!, OêVšLgõ\*•ƒHX7ÝÖ)6ÜW.W™t@~BCݰgÓË$®{ °×•÷qýØl® ÒÉ,°¡As¿ëíÕê,Žs'ì—Õs ìmÕ[°˜ núE™mk¶JÐ –•.Š‰Ù†!<¾¾Y%hÂ҃+<ŠÈI±.D`§üœ’+`žº‹?Cª!ÙB® Ø]ß ×]ðƒýÌò™< _Ê" ^|ê˜M’Ý/£sÉî—>9ØË¿!Áñ7½« û‰Uóô–°Pc¨wVCýnø·4êÏ`Ñ& ¨÷¦à*kuˆ¹"Åî“ÕŒZ²Í0kó~±ÅV¸Ì{„]è)Z’,oF|Èy˜øðè &ï2™v>勆)(6Ëêâ!L^¯2.çÔ‘=àÜÙ@´ÛnLˆ©6‹¤Oˆd[¶ˆ`I’‡‘D¬“ÛysbÅ*C>™a“º`QžÂ}£&ƒ­`yŸ‚K¦»ÓŸ=¶¼t@s!ÂMÐW†nôöä HY¢}}ú†ý¶å("¹î¯ÿügà L^ñ8ÃÛaÏïœüØ?õæi?óÄÏ{‹Õ½ø±Ól§Ée%R±u0·œó+y°¨iÔ}Âü’–Vóš”ëŠ>¿šÕ,$Ár4¢Ra¯ˆ ë¹l[³óQa) DÔ|Xé7L/çÖå'+„Cu]‚5± Fgâ@B„Sò>LC±Z’&S+X2†KÂŒó 0¦¤ub>fÒ€ûøBÿ+'8—úÑÕh´sÜFØÁº<›—”ˆ]¢^‹5»îãì0æsYÁ2’tY䊂յb¥›ƒÚmC¢§Ë —^Áªḵ³¡¨¬Pc?|ZŒkæÕPXLV5ÊÞ ¡±d[·jПb)X<éf ÀÆ£lÖ–Jä@¾¤n/ÕÆ^¼ ¼`ØÑ5J®„54ýÔÍ@Kra– ´Y#û(ž„Ù†xnG^f)ÝíYé£à4õ¼’<¤ËÉŸÇË®T"( vßeÒ´s©¸rEù£¬ØQ2¥*(xYP«»('Zæ/ef¢‚ùV0¬`I „À…ÖÎ4K5à%=À×VFLäKMM×ìG3¯1·ryƒ™Ÿ¨ŸºÝ†mUa `zé`K `S°r’«–‘6 ë€+vÒ0H› ¡TÒªáчwWM¸üá7@[ò¥ÚNÎ(Z %2¤Þhƒ®f¤ý$ªhíڶݺú¯hÉÌÃfŒƒÇÚ08Ñt¹\ÄV‹š1?”¨ðEül Š×&`n…ËŠ¼Éãªd©Ú²yY9g%«Zå6H—4É¢&šÓpÕh^F¨¨óNf#…+šÿLNWîïÙíŸ:¬¹ðþGÀ+_‰|K£žKH4qu› «¼ˆrEî\™F«¬2dóe›Ó‚\,³be[è|£‰¤¡OµÝxÜÍ1´ceLeàå˳àì±=†aðòÁ!ðÉÙýéÌU0ž¸l;Š?A†nhëO¬>Û°:tbµ ^>q<¾s»fh‡ÿøž!ÿÄî~lÿQÿÊ7¿Õ%Võûôcï~ìÏ®äÀtt L(ÁšVÀìRF³èiâU0[¼}^É•0'×;#C³J®„%WÂB$VÒM°œnæ,ÏÌÅü9µ/"µ``ëƒE© VL.fÀøÅ ‚åV¡LÅ¥,-Hü~l* Æg@kRÝ> ÉjƒIõÞ¦d58%+YF°tNÜø\ Œ­Tü‰9põ¸Gˆp XÁÖ@°‚5¬`ýw¬;ò/Üö/~ûk_ë)VÑÓ—üÙåœf XÿN‚Õ%:+ÊJ3@Ÿ0[Plûaá€d†m÷Ùõ(\MYP²!äDp©ªÁTZ2!œ¡ºœ„£zJÒ_LnW²SR¯‡`“ÔY-i‡WÜE‘AyíÔsóµa±’aU†`3Ù5à©×X.þöwb©„—LòÏ—j€? z£•59 !*J"`Š.±ªk8lzuvÖ?¾oìߵݰ<ýøÀ!ÚÍöUP$~ÛŠK å«S°aÑáÑ]Ýß[×£‡ŒõIŒw¢¥£ÞÑ#Mã’ØyÌš° ¶z`…Ž46:¨7Û ª^“5 ÕÖZée%og9VÜ2,Uˆž„×å¼ Þ'š†¦¬É­o‚¹ñ @Ázï°’«c{ÀäåqàB„ éì>Ï4‡U‹NJ¶óM'÷`hp& WÓРëæNÁºFè.,XF4F§càì‰Gû`Q°>>ó‰?£DLpÖu'÷¾Õ~Ir Á W"~± K“•§^}ÍBÛ²¿ÙØ¥E?}ô§þÆ×»Å*÷½ïù¿ß¹ÓM‚©•2˜Tâ$P°&• cÑu0²\°£¦” ܆Tr%L-e5ê>!XÐ0­dNŸ÷ef“ÒÍ{dBË›­d•)Õe`ºÖRŒ^žsž ó¹ V^3i,$Å´·i“Ù¹'Xš)"´‚5—£«ëêgKƒi Sz.1Þ®`õ“¨‘˜“~xNlB«D"=á<&#'nÿM ‘™Ñ·Qh(kvË–{Þ¦ÝïH@ÐF‚2¤Žs̬XV°ÆU„ᬮã1ÞƒU…«Iƒ,õæãU°œÖDÒ5 ‚eç2Ip5ˆÂãåÖ4É(–Õ‰bÔ²âÁ%+æ/×A:é¬t™‹5WXÜÊ’ÑÕ#/[Ñd4"{™B “ù>´‚eE°X™2¢‘άƒdÌ<+e«yš´W”+f/›¯/•Rµ®^t+v:Òâ¶ÓH®S8/-Ü–"§äFH«ç(OéTÁ³,®ÈQ°ÔÏÌëë«aÇ$ñ÷k+/Í*+{Τ4‚•÷ò ¬P¨bôŽ[¡«Ö›€MR½dÊÿձǫ  UÃŒáægæA{ó3àÚXlZ@è\$ E·`i ¬ZµúÒG¬‚­úÌ1ì_y¨E((Xnå)|Ì<ÖÎÕªNãq]\Ýcn–•,#”RÙX­÷Ç߉­ì]3˜•R»B+m$$§RV(åC€äò•5ž|’¿ƒê&˜›¿Ý?NÊ̾c{ÁäÈ8ð*WW®8Ò„óI¢Ð¶«YQ%\‚,““Ŧ޽‹9X¬49X¡œ«i}@Á™Š3'N°v‚N¬.~W€«dû†˜LþÕ‚×X¡º^ãÑ)HV§hÙc1üÙÔ¦Ÿýå«þ•o}»K¬*ßø†ÿî·û¿8ñ”ÿó'ŸVb”Ó,—4f¶$Èæ3)a&:« &”D |¬m»RШÇ,³ê4%«e¬,˜Z-ÞΙÁ²¢f[tæëMËããê…±‘Y +5aÁbõ({d2Fg’`&³©ÞCmC X±âí,óžeV°”¼ V°äñéÖ@°‚5¬` k XÁú¯"XSêú–xõ¤¿ñ£é«ê·¾©Äj‹hçvÿàîí«`ý Öh@ ‚¸ž“¯^÷iÑj‚°Ì@šVëàÒTŒªí„®ê>ƒ ÝKÈádõ‘9„C„î¹›= ,ê}Q°x» öèƒeB’.4¨±Sìc¼ae4…™r.Ì'ê`n! –“U`«3uʉx4,=<–yNŠŽö¼Þö h†hô)á„õ–®ÌÁ³V<Ò9’G¡Ã^ÌMžS.[º²ùR6'K‹”—«‚D4 ¬$CŸ¦jÊ[qµÀû]³Õª…á6†+)%®¢ÑÈ’:^ÁKçȥͅ¢X†*=õš ”Q¾+p9Qöu59p"T‚¬Ž,VOv†ýœ`•÷!á `×?Ë„3%GKÁ&°ÁР,“ƒÅ>Xì fsÐlóL=(š}°*Å5ÿ­ç Ù±°šðÐÎàÂ'çAX°j~c®ëF'}Ân²ʵ꒘^á¹>ƒ¢»…ËTZ¹ „óZ¤3ŒÈïà U{ –Ææo1ˆPu_®*СB¾f Û²:Õ mohLîŸôH묖¦dXÛsc“àE%W/B°vûgŽƒ©‘1ÀÑ@)ù{Ä9H¹N[À~_Ò#~ô˜¯‘°a¶¿`‘žý¯(7Òˆ3,%J®„‘é8ûä~ôÂ:îëÃ3»a\§§wX¯_øOΫâj”Îïéʯê"TÔ!V·þ¤{¤Í~èp÷ÿg{vØ0þ%YÏýâ9@Ñ™ rÝI2·O.d5ѺFÉûkMF*%QÀÖ„4ÛV‹€’&¡Þ™tL,—ÆÉÈPäÈ Y™ao©¹¤?±à;xœU}FZ$_J])jFçÁLº³gÕtª& ÷yy|°Ö´çDÊ…õ¾ì1ªûn`iÑ“cFWÊþØtÌ(¹ø˜` k XÁÖ@°‚õŸT°¦•X%•Xµ{ˆ•¬b-<õ¬ÿá‡#þO÷> ‚õ_H°F®%_6<øùˆ™Hoè,ûu§`1ÜÈ}ªÛF)Y åà¾Í}£ñ é©ÅÛƒU„,v†Qr%8±j õæ¸TŠä>ö Q_ ‹Ò·CXJÈä¨&zù€`IX,ˆé‹Åû™äžIåºBËÉÖœtAUcˆMøŒ]½qÂÖ'oŽ0=³L‚-/ÀL/ä* £ë»ÁJ  ë¥²ë ºš”¥T@–Ò"QŠDº¢‘4`û¶WÈ×yÝÝž!È”TSJ˜ÐT òùmX“]ïM¸ÍIM7 vÈïJBI2aË~û°biöa{ˆy›€Î±+áŽî+VFJï,ôÏ’ÊO›ÜÜê€ÝÞ"ì¬f—`±²Òö¿2Éîìée»ü—«þo¿X=x`èapPÉ–ðæk¯J»–W‚ÅŽé]‚Õ%ZªÂÖµC…×ïôÞM—`Ùdtîë6ìýèÃÇÔhvŸ·§`5˜ôÞöï—‚e 2øûGz€¹“+k2%MZ*s¥¦¼¦GÇÁ‹ûwƒSG†ý³G÷€é‘q­\ü—)µ{‚ÚF¶l¬\ p¤‰ëcdª MÒòLOÁÒ!±Mï쥇=J±âý§ Ö+vÏ|bC„6AÞ†ßÂRÔKˆ®}L“פ·¤õ/9®k¬X‰l¥_?íÏ¥6üÓ'?Ooìä.‚õÒKo€)‘ÅørIcXUÈ„ñqu½äk¶VÀäjIcFÅŒ%ê€Ò6¹Óȱ b®ñ¥˜Œ®kRíNŒ`ÉØš‰… éDl„ ä$¤+ÆV+šñE0&¥sßüÞ$¿›ðâÈÄ›M€™ÌFWHpRö—ŠWX°`TÉå¸ôÔR8ÁÒô¬aé³B¤Þ ôxL8‹yK]ÔK°B+X|ìåé¤f¥ÆâmÐó˜ísñgØVÊ̱‹`a?È­jû7+Á²Ï?—|~Žo`å g~ͨ7ݬzà s¬†m¤¶¼â®X%òMÀ*="ÇH‰ Î&´baª )X^2 JÁ•+ÓÒ~ÒµŸxYE¨‘2§RmuÍ·³csBeüÅâš&_k³+*,Ó$•¤€·(­(ŒXYÁòÖ4ñ,àIž §¼\ÝK{e`s°B•\AóR%Ð!T([H¸•£fÈ£’ü¯¼ËKs­#tWè¼Tt›¬ö¬p›†Œä–)´`ñ÷X™Á2+XlNz­,Žï)*I8£ÐÎ/4¿ç\Òk•š?36î~ذÚµüüÉ'@¹RN°Üø WMhª¯+X›ÝÂÄÕ)³¢õÇV¿y‰nå«Ý%IV¤¹¨¢Õ4Á æw…!,‰V¸BcvjvWÓ[Vt²uˆ•jVšßoRÕɉ,5ìßKRFïÈßayLL€Û NëÒÈ®]Ü›§ ÌÉJ)¡â¹‹ç²XVÃ:<÷q¬›™XŲ¥ôzî[?Yq··»pÚ*B#XgŸ< 1m^SrõKV°®‚IiTŠÒ}-v]BZYú£èØÇ ÖõÄJnK¼v â3“¹ Þ{ómpbh; ÖïÞ<fbU0.ùP¬‰å" <Å@‹”©ìT‚dB]ׄ±„fB‰“ ãukL(Æs`Bš˜*¦DB¼¶mâ9…†žêZ;Sr•aÑq+Jú÷mKDI1sMÁÒP°./Q%t‚ä`ÙíŒÐu —ú‚eÚ8pTŽÖØt ÌʸÏýüÁÖ@°‚5¬` ë?X°®+V¯*±’^‹êÂ- ë¿`u„ÞÂyMV®Zšž‚ÕÔÌgeˆ‚ãžÓ„ è/ê75×\žN€© T¸cÖ°—•>~’äþ¬™ãHh´`µÐÃJþ¿yËJôš¹,O5†LˆTÕÁí¹qK+i`‡;›*TžH¨°n0bÃh:¬æúEiÁJ'² Xqk†ì‚&;`V]à…\:ä¬.Ñ2°‰fAªÕ Y™QÁ¾À¼*O·À°&‘£ŒÁÊR¶ lŸ- wHÕ¤ˆ’„ßÛóbâ)!ñÐKç‡1W‰Uw”V/]"A<¶LIc¿çë›,†d)MÉdÞ + ]T-a åJ/3æ`±‡Xºh+ óJD@WO+-T¶Ñ¨W¹P_«¢yÍ…l¶(X¶Š0ØŸ‹‚•Îæk¹ßµ r0u2Jå5?™Hƒ'Ý Øë°T û†AR‰¾Ðh~ ´(˜QC rtŒ…{]ãò”ºÂr”•?A°ntpt¯\/ V¯Ð`ßç·ÕŠ”Jæ¦mºq:m^;þíñ÷éªVÍ è@#^~PaϪD¾Ò¥M0£äJøíÝàÌÑ!›ƒ5}yPÆ8t:‘Õpè4Áˆ/óa‘ã¾V3 à†?kœ`éÑ=Z°šÛòFC„½K‡™›KÁzÿø@Áú@+ó)à¸6 ìªÔæõ¹Ñm“‹¼ªMÃuÄꇷâ>Ù†û›2°©å[¿} <¡äJ`–ðîÙKÀ –’)é-6¼gäi\]ÄI©”jB©*4ÕzSJº Ö¸’/ar1 lµ¡„|`Œ«í…1%LEŽ!D+X&ì6:¥De!ì6ép.•¬ÈUr5ÚW°(eúx˜Æ!s°PEhŽ¥Ÿ`qT󻘃5&¯›¹Ãýk XÁÖ@°‚5¬ÿ@Áê'VW¿ýO~ö™çýé•5÷˜`ýW¬Nñ'ªëÐ_g8Ï&µC}ñ à1‚`™J¼¹ pý§˜hÞùüV°â®ï•,#6#ÓI0* î †î\_ªvW¯ª±@åàX Ü8–Ј`ikbp³ÖX´®1œ—þ ƒé‚kÆ Ì"4¨ä*¡ÄJ½I„%¯ V¼&X\Éž”â¹&HX\Â;C‚Lò¶½Ÿx"5a1 –°»s«#dÈï)XåµÈ+¹d`1{î0¤Ã .Oîü>_\Óä×A©²Ñæ*®mŠŽïcº³»Ò»Q82–R"$x¨Zj¹!Ø&d(ÛÚž`ìÉe¤¤PnkL¿(vM—ž‡Åfÿì?F‘MÄó@*;A¦bñœŸ” WOºÚœ§’9ÀbJÉ{‚eK+\F°²’à® ,Q°$¬hC¿†fs"W¬ è¬fWˆ‚Åp{,1d˜My P*+Y¯Ÿz<ºs;82ô0àé©9ÐjÖ;’Ü) æû@¯¬^\S´úì¡Õ¿Ë»!=æÆJÄ©·„Ù°žEWvI[ÛmãdÌ„í~®î£n¨5ÝkÒY$àþ¥RX÷]+àÃŒ>/ð}Ím§“©\S—ÆÀ‹ûw3džl'w‚¦ŒÅ¤[»ô·ÊjX!H‰ZÍȨ¯XÆxžº@½˜nŠÕ¼ºͳc·éQ4e1Õd!Áº‘dqÊÑŒ÷)`r4“ÜÙ‹‚õÑûçü¹œÆ• Œ§6ÝJÒ#XŸC¸\(LËQôíó~óŽ{ûˆÕ þìrY_ì;:‰¹2‚1ƒÞb-ÿ_ýWr%<¶Sshx§ÿÁ…Y0[ ²Â3®ÄH˜P×5°ê¾_Î Ô¤Œ8Jº*zö­šŒ–Á„ÜoÂz6ÉÝôÁš”®ñ%†á?S!86«„E:ÊK7w&¹‡*Ù•}\’ö;«ÝÕËÊ…5LB\c31 Iîöw”î„áìõÚÿ莇ìqHÏ.Ç1Ÿì2o+, V´¸ •h]C¤:WŽHPvF"uùtážÏŒ™ ­`¹6 ¹2á ÷Å}wÉT¢e+ÃòÕ,]Ø·lyPÉU Œ«çlc5Ã,ª›vµ ¿¥<“åõÉe)Y ‹I°šªVI’ +åX¹ÆUÏ«€´º€ ÅBžAhW¥¤ZPr€d%CQë)X+Xü>—+6·Á¢èB°²ÉJ¡YÑÉš?‹É±x¡Ö )äz´¬„¦äÓ¹ZÑò”€€ÌÈ)a¸ÚÅ*Æx²Òùv@d;Ç€$$7Næ@\]4„˜´ÓPà¶Lð“}RÉ”`+Õ}B2‘l.Úkõ««]D®òyM.S’3çªÈÌìC#Xùl °ò«c¬>"dRE(y`Ò@µP®ô_Ë9s±XE˜Ï—ýªT*Þ{íUðØŽíàððÃ`ßöàì©Ó ©äª Ár+X-áq4µÀX«µéKW¨åA¸Yiãšs Ù6a£“`{†Ðþ\£QM¸"ÐÒjÛý¹m͹ h¡âÏÀŸM˵i0FíœÌ¶Æ¬.Ú±Wæw&kY9-6íŠWÉYíwéÔ)ðÒc»À™'öúgŽî½w$  –Ûü€¸ìµÀ’× ©:X@¥ :÷Yô”uZ@Þ•^¹ê,Û†@Wvç&iñáJÓdò ˜Hµ£btEàlFsñ£ËàÌñ=€+XoÜ ^ñ%\Îç JÊõk£k&]ptÊTpUÊäF{à±ñ·>ö[[îê»b5£ÄªC¦R›=Z \ñ*xùéãàèÐÀUæ#û†ýó“q0]+M¢ &¥½Ú,JÔ5‘’]Áš’ŠBel Z]ýÄbLÊ8AV%q¿c‹YÀ}ÈhÁIŒ––1%Wã=‹’4­^Ûi¬Ž•ÁØè ˜ä>9Áâ÷æyÌóŽÎ&4ÌÁò‚RÅÇv~/JE°fÌ*ìø\LD”¸.¤,Í@°‚5¬` k XÁúw,Y±jܵ­ïŠŪkµj Xÿ=«;ËHTüËä`…C‡×¬ÐóÚœ(+Y­.qbÿ©.Á2Ç9špŒ…¸žp1kÂH––í 2Ÿ0ƒŠA7ÌÔNOI˜°æ ‹‰u0·˜®Ñ¨òÍZ ?ËTÿØF£ZJ˜ÄžOÉSRèTz¬K¸ÊŒ¡Až°oD°l?,SÁÄï­`yéÁÃE‚òàFåèð¦„È&3·wä/±òÐH2• dNc‡aãq:T˜T?¿¦<é)&òd„'ž.ƒh<’Y%M™Nb^DÓë`%’ÑtÍP‘HÖ©û…¸ü¾2µ€h™ŠÏÌ: `QŽ¥Ñ¨•C±d³RÓCÌŽ»)hòÒßJÌ¡£4ñwÂmø^€+’¿c°2&#uDŒM,V‰Vª€ïƒ|* ù’_o F?ùê$C„û¶o/=ÿƒ° »†¦0Í÷\«ƒª¢†Ü­XëÌß V"†Ã†ááÊ®š±søt'b®|tyd­æ¶®cå¶ÇnÂÁX¶Ñ(1¯‘ùÝð÷Ï÷ >¼˜0µÈl+Uܳ#àí£ûÀ+wƒÓÇ÷ú§ ƒ§öi^{ý`.RÑÜXͶÀ²a%#ÂÕKê‚2m°ÙÔ(ùæÕל§Ñ¡+°(s¤ìí™Mà ãPæÔýCVo¿ñðëÃàä±=à¬áÍÃÃàèîþ«oœ3Ù« \eéM^“P/§x ±·Ïù­ÙÚ%VŸ}ýýÂñŸÄ*\õFákw=ò¬DIxþè@ÁbÿÄÁýþèRÌ*ÆsæZYÁ* ÖD@°&E®D²8ôÙô”â>(X.„è‹¡G—ƒ¥s½ºË0&C“edbÆÛNx "0Š1ÉíR\¾8f½v|-Š•a#¡@ý¯˜ƒ%ïIÛØ4üXSy(9^?ºãA%WM0©dPè%X|¾` k XÁÖ@°‚õo(Xñ·>ê™cõé7¿åŽ>ãÏÍçCûÖKÁ²rÒÕ­Ýô¥ ˆ †(Ên± …‹’~¾^ÒÃý:Áê•c+Ñ ÑtrÚ'ŸŸ½¬û`ñ¾›·*ÁŠ5‹bE$©}£p(TîkVÒDÒu°¼œîÌQ9«êbM¢†ò¤bGÃJžTÖÀþX^0<†œ¹X÷¬pˆ0¯äJ¨Vƒek×{‡}´ô¾²ŠRÅÎ&ØšdòtVc+"åë¬Nx{°– #šñdªlȃ‚eD“EÚF¤5 ®+ê6-ù]¬¤j†º¡ –ÕvÂÒJ¬ªÛ„HR³²šQrU]‚%­óU’L&sÀ r.º¾[ /’ ÷Ø^VÅuÏV¸')(áØ£Ìn§.ÒÄÉX”K5•#B>™ÅBÙ¯5¯‚¥ù%p|ïnpXÈþéñà Ž[¡ 5kÜ{‘„ª­€ÑÇhÅ£¡±=£Œ˜ØÒ"@Jn;TÙ–µp÷t¹­¿H1 }££B²óX)ˆzˆvwˆ”Õ¡R>®×6ö‘ù}2É=/Ãפ²¶ÚÎiX8͵@¼°Æ?ø¼}hxåÀ8+!ÂãûÀS{w‚au^yæ)páä)pþÔYpñƒóàÂçüK#³àòTT3I"àÒ„æbˆK꾋dbœ[ÆWÁ¹ Óàã3çÀGg5¼ýžÿþïß§~ù4xåÀNðôž‡Á)%Œ‚ü|ÂŽ 'vo÷_xñ50›½&S¤÷*T/ê®Pãpà¶¿zfÔ¯mû×.±ºòOßíȱºž¬Mõ=uáÎ\#sið˃ûÀ‘¡àÑšgŽñ§”THŒ/d4¬dXOÄH’Ü™üuIî“K90­DL˜Pr%Ø>Y2Z1µRè˜njBö †µ`±2†YYqy¡!ÌJâ Öˆ’+aÞëÞ–û ðP€ÆfãšÍl& R&žßÛÛSF°ÒM09ŸÒD×mEáœÚÐ-X!™é—SÕ{õ©ó1ÛDj ,X×ˉÒb§[(pu‹ùQ#sY,·JÅ}7»Ž±£:1 k”²k + ˜s5‹|‚–m&:Ÿ¬kuûõrº x¡_• ¶‚EÁŠd4Ñl ²ÍûÔmB2[¬.Lf*€³ët”nhiËæjtŽÁ)I>45«ë•…²Ì>«µì˜™sVì!XvìŒÌ'”ê·’[©’16é`9·TßÉÏ˸üœòsåÜ'n›C ¼ôÿÏÞ{8Ç•žwºúÓ\µöÞª½µU»×e[N²lK¶’¯-i¬‰9ssŽÃ$H€ÃaºÎ9ç€häðìûþ¾ïýÎéÓgì[wwlµª ;7ºOŸóœïMUPD.UÇΕÂÊÔª0ås&Ë œ¬ƒ(‰!qbÂéiŒW®Óã‹BôÝ…ðýÍ‚Pº¥  ˶5³ B‚,ùXRo$Xç‡Ù‚U¦á)Æô¨\+É—«62þÄ´ZÐ’â\Á²«[BjÕ:ÑjÒ÷Ę´£­J²ÆðÆ^YTèËο"jÕ¦Õ&¹bÊô½3'÷î»×¯R±´{óLd@»óÖ!p‚.×ßbÿMÝRád«bÜ€s© #-³_‡åÅÇ^=[\¦]‚{EIÞc§‹Æ2U·nI\iUj¹û±šYR[à¼C"ƒ6 ¼â=O¿E\-(âå7`bì)ؽÜØµŒÜb8¼e-ضn8¹u¸½{¸³kƒbïFpÜ· ØîØ ÷oæ2Ý6ˆÛ5ù>Ûÿ—IòÀþ­àÞÞMàîž ŠÝëÁÝ­]ëÁÐÅðþÍà&½OFV¬ÚîîÛÒözáÊ ‚%Ï^i²Ç£¬t›sU%ñð•ÕþՇˮXÕö¶Ár÷óé|¢Þ×qÏÁ[èjtÉøJ_‚§ãptÛz°gý°mâ衃Ö$í·˜)^5â|(=ÇÈ‘,0“›Å+X¼’ELƪÀ­®D¥ ,%`“®0Áq“•3,~ Ø«F"XùË4§e):VÇ^N CŠä1îç`‘b¤zp‚äŠ í"X¾|/Ý‚•hžJ²ÆÁÒï£/X}Áê V_°ú‚Õ¬¾`ý;+>:nµ—Y±z÷ío#ÇÊÏÔíú‚õû%X’?å­å¤KDçëVæ!§ŠrCP_L°e¾Þãݯß#Xz³ÈÒDª[ zú`é¿Á¾]±|V[,ò¯æLÿ‘}€Œô¼ Ó—/öXaˆ`Ȩw+MâÀäꋦÉ_®¾²ãü«Æ"¨´–€TŸIÕY}§T¸É݈Ð4•‘6Ü´’û/‘\•µ`™f¢"Ñ’>I:Tf8­¬4‰ cWE)¤¿T’Å9Ž—@ ^aÚ™„µ0Úø™@¬™WH—ü3 Hr¢e !ÄxVF±:o+Çý†€+Ä\ÍËUH^•z¸¿#ù|E°¤ß˜ô¸âï¯WFD° ÊyXÄ4WŒ"Ó8w@­\"XrŸÀ>¸W‹eP'É›{侷Ξ;׬³d­·Ã¯_¼s‹ïLhÐȽ©ZtÓY‘Þ÷B=y^-[\Ü‚ÕÎ[¹Áî²MwM¨N…9WªÖužÌ|ÕsÊû\®¹¯yÞ¦BBÍ’Ÿ)'*iþqÏE æõØ0°s-¸±{%ù‘ȇ·¬ò=ž¤63J‚¯'ñp|Ù+N^ç«ÐTεÒå’%—`õäuU²u?&P~>2oZöÒïÙºZqöÄ)ËŸ›&D¨G娂¥HšˆšÐ¡îgÅLÑ팔ɽÒL¹*-!2ÓÀ4TÈí"ܹJò÷r/)O¸¸²ÏYÝgÂu&¿«©xíÁB¯PÉsÈ눽žŒqÃ×É{’q7rÙçàG?·ËãÏ~’5¦/X}Áê V_°ú‚Õ¬¾`ý;+þx’Äê£åW¬8yÝWXæq}Áú½,“¾’`9+ñtBø×­ÌãûŠ`É€féÆ>îšë#{Œ=\Yñ:X"XnišXN°\¯#=µDøX°¼CòDüè§Z“Ü„«(tµÀTzNAàª; J(/.XÉRˆHHwåTªL—véJΘ!*t€%‰b2tFäDBi%¦Xi)Š5ÐhÙÕ%ìdUYA°²E0í‡3»Ð«ÊÌ$¹j€;µ‹XIçöÚô(7<›­éÉÃHP+Wn ݱG~ht?,%XŠP¬ ‚ô£e¹YÐ#XÑ 9K‡sCdè6@"ÆÄss A’Åp’»Ý«LɯH`ž;γ0êîûö¨ƒã,3[å -ý¯ÜU„Ü…Ý~’²VC‰D‹+F[‡T,öqfL²µ–»UJÐh´Ím’ìþÅð}°‹ÂŒŒäjÂ{·nƒÖœÖ’îò¦ß—9aèê ÖîÂt¨ov#]ꥇW·Øt–Å®’u1=¿"n¡êýõþÖÜ£¦¾–ÈÉ{3¿×9`ú©é´Ts¸Ì•K׬ YÞ*k ÆTþéðŸTv ý›1!@y¬o3¯*"E€ÇH+-p&É]‡$ÝÉçNÁòrWxÂH˜þììž’‹ 1þÚÅ^Á’÷!zErÅLL%‡%l)Ïá.,®de¼ 謥®pã·–[uR³W¬‰ôû12Ã2¤s°^ûòÀý|¿ÝG.Z÷?±þðÿÈú£ÿúY?úç_ZƒÏãJæè=üvãNëgÿºÆúé/Æž‡­ñ@ùW¿]¿ÃúÙ¯×XÿB×3cSeõü©Žõ» ;»n{ì+¡²ç *X°xUŠs¬Xš~ü³MË_( d$Žˆ–´iˆð*‘(rc¿Pl,3/[>иgÅéQ7Re–.Í€dASlSŽ­GÉäse€¾a|]Ájé³~ M u¾‹n<É”ŠuàÓQ&AbªÜü¨5r{6]f,ŽcTM¾®0óÏr Fe•O²&ù&¡x„SMá¹ÜøÍ^ÛV€åŠ›ìÑYb¡Â|´9Ó^CÎø#ùYE¢ ’t°b$_.–,›QG"{ùjG£EKV°t[ „ V„¬p ®‘T]Xãê@¢áhëFr°Džz‹sŸt“Ê Ý^Á Ö,¡šžUˆP—‹ÀÛIsf Ìv,à÷øÀþë€45”³æsÇŒD6MsÛË#£ ´D8K¶ã*½—*Æ )ÜòÂs1 îÛV*{•LŸœèšq –ä=q%ž[°Ü+IrÙ“lññ 8îã+y=¬q^½"<¾$¾G°œb×-XYÀ‚å¥ç`D°ä±ßÁúÞÿÙºp÷ eæ÷°çè%ëO¿ýW¸ß¶ý'¬‡Ï™¬‡ÎX®ÚÁÚº÷¸µƒ.ËëmçÛÖmWÛwÂÚõÉ9sÛŽCg­Öoï V_°ú‚Õ¬¾`ý ÖÓ¬¥ü¡’¥¿û;ËھݲN²¬={,ëûßWm¾÷VûÃÕïY±J÷ˆP_°ú‚µŒ`Ù2äÄTðe– Á­€ë9ð8áqžT ¤p –»!¨¾ý¿ü—?Äÿê»ß·F½óü¼yëÿùã?±^Oåè¶ïY&²ê==tÛŸ|÷ýkzÜc~\Æ~Üómú²äsu ý7áÔ¢õ“Ÿ}d7A°Ð²ìâq ¡Ø\½&a½"°pT÷Hf®KÈô‚© «·M¸Mòy$ %ùLr» 8.æ+ î+>P;’«bvغŠPÆ¡´fzšê¯v7åJHˆ°Œ¾j†jz{-Ì="å<™™éèÜÛ+«»Ë¾ì/ÊM…œ„%K³öI…¾,‚*/‚—£OÁgÛ7é)…áŧ›×ùþŽm]œR4¼³ éÝ×zŒTÑõÌýŠ!’!°_³o“AÆÙ°01Rq(Ï/‰ƒû6‚ûô†C„Ãy.éØ.ÉïnÑ’÷µ‡¤ãÅÆ-ªèÇÛ­÷þÃ…t¿òáÓZ^†ì¡ŒTÑá/GO&‘/·`¹»…Kò»ý‹Žpš–=løÆÙÓ`7ýîv¯[m’ÜwlX <7üÉDH’»‡äŠ™JÖÑ2ðrxıŠIb7&ÉîZ¬L_*ÚW-X|È‘75­àwb*Ù=á?ý7ñàe#X®Ð éÆ®…Ë›šãÞ€`‰äºBƒòÙIÏôÁâDw"XZ&©EÊ]E(’6Å…o<*‡2˜òüÒì[,6Nܳü (YMæk¢ïÏÏg*ýD°ô$t‘2#GÙ`㬿ý‡ã~¼ê$ñà=è,_V­`±`Éë™U*õ8¾N*=éy\g¦¸gNÑR‚¥À?þéoìÆ“á,p¶P¨Õ  9XUmPŸV”èàÀ˜YrZ^ºw¶z 9×h±’ë«Õ&(ÓA’i.W6®WÆLI»^­\–"ÉÃïÅ.1×£qxLNË^ !(– PPT¸úO7 •<›BEa¹Ñ#m`* Î¥bLõSº‚шñX3TÐäö™:}_ÑŠ‚+SˆÏƒ$¤ìí,Op'ø:–h&SH.]ß=A; Æ^ ›¡D…D« ä½§J ÉËðöAHÕ¤¬ìåóö¿MÅeC!ßsµÒµÚ,¨×|[½µ¤Ð«/æ1ºÁh‰Ž‘|¢zs°@ȼÈLº¨ÈV€È“ŒûÉ”f@<–S¤ëf£ÌiL’37NÛÖ¬Vèó©#GÀÔdØTƒÆÓ9HfA<™‰”"©I¥ó½dºI“ :ÉÒ6 ´€¥Óü\0>îccO@®XMnäÉí(Ü•€uÇç&B¥W¹D¤Ì*X«ÓE£µÜ¨ŸåW¨º*…H˜T<ºK„l/mŸ“¿[£Â~'N¼_°ÎžU‚uô¼’¨ÂÂW®6¹e©[°z¥kÒ9†e…ö]¹@z£´ ’qF;éûÚ‰,Í–M`ô¹Ï^)Ò"eªu–ì7S!È"¥ËGråK,³‚%U„º=ƒ?QNÁš$¹b<œß…Q9jöáJ2ãáŠýPˆÈ¸ï+׋`yRÝbµœ`½&¹zí,É›âÇýàŸ~n]¹ÿCš_ÄfƒÅaB~üÇO¡_—Ã|\Eø›UÐëãý§¬íϘçÜþ W~Œ×Æãè²á@õ¸è6¹<™Ö8r°xcñsN¡cýä皦•áHÈPUÓ ³6¤Ÿ³i¤ÝCj” E`v´sK`¶½TO"ݱüØ ÙW9ç†á½Br÷V2R‚„uV±P¨P2•QJ åÀcС”biº ÎQ‘ª@©+”"R©âˆÅ @ªð¸oO4ßMˆ~¼L Z2nÈ]ˆðŸÎ›$¹ºŸ‹,5Ëòõdº $÷×g¾Ü‚B7ŽõÑý˜)lÊãrt˜ÑT$rµ¢î³%R/v€'¦H¥«@zy¥3Uä1eÚv*œZÁ°nú,ËMP®Ì€JuT‘{§î#£w$„%ÒüUBµ¥Ú(TÛ¦÷Z‚$Ë =x\†]Ë`l9¡ÑÎ2œiHfÄ5#×®ö¼ÇV„@:ï•Sì0ñDÄâ©.â‰L7ÉŒÓ$€åA"•q!™V$,VFÌæ `|Ü$½7¦É•“„Þ¦Ò·C‹šîáév%¢ÝËÌ=˜ÚŒÙáßt{¹Öª™³‚ÓÏ«pf•ö%€¶—*zÆÍé%—ª,Xq|&RTIª˜PÅ/Æž‚+Û× ËIþóéÖu`‡y9ä´a 8wè¸yá¢âòUÅÀuwh Üz¬w|Ÿ¡á¹58òTñð9¸Gÿ¾‡ëž{Ÿ*F¾·¯ €k·¬/«»7ƒŸ³Ûî,‚e7$U죿ëÆáã*ë»ßíMpwæ`ýà¬ÈDBåBV+©„sI”GµÐ;†å=ãXÜãZÌA^÷î’}ÝÅý»ÖîõŠƒ»v€gž˜—)Αrä`9Òcpü‰&Áâ0žù·úißÌLqبý¨Túé=1SΟ:çJ†LO’\1¦_”Kš<Á‚,–žåÄGkë•‘+%rÓÝ,ÔýXÎ,ý«‚Ž÷²²d‰`©ç–aÏ$S¡pKá7B°>=ÛúÞþ•ƒÿíÿþïh§ð4Ú0+jv¬ß‚ÑaÕl49oývƒºí_~õ;0æ/«×Öûù¿®6{â+õ«/X}Áê V_°~OëÌÙËVáÇÿ¤Â„|`Yív¯\mØ`úaIê V_°þ=‚åutHwây&é\'Œ÷Hš[´^§æÁx¨LˆÐÐý ªeG³¼œSÅÒF_cFèèçôjT°dT€a£9„{£`Á ð½Ì;­%ë_~ñ[+AJ&/‚b}¡ ‘ S9Ç=xôޱÕ~L©|Ì´; =¿ä‚G’,Ù1»»;ÁªÔA¥XÓŽ0Ÿ w¸Æ±H•¡ªÐ"Á¢Ç1¨p“ @S)Õé -—¥R«XR(ÁÒôª¢X™"X‰ü àAÉŒTäq¨OŠŒÀ¤êÀOrÅD Wó¦JÓNN·Çy£U…"ªw(Z°¤WŠ'Rj§°lÁÒˆ`Ñ›Á2‰<,š‡>gíaÒ1–­'àÏ€X¶âN$Ò\qÈîIhÒÅÈ•fA^!׺wV&_¹BCûÏhÔ0i©V̤K *€ ’É zÍ ^{†„v„#Y‰¼×HvF‘žp¹XfDI²˜‰óúñ@*—n\ù Hø™R™„’ž Gã ¤I81±xDcŠX<£ Q)¡’ðb‚dŠ‰Å“Š˜"K€(ý[DM›ÌäÇ3†‡€`8 š³o ‡Ë€s×ïÉŒìÑBåêðÞxO§v#Mº0Áà*SHRmT¶ d[Ðp©>ŠEÀUÆ~Ú71O'‚`äù$pЫϟƒK¯öC÷s ªaÏ[Ö®ûvî/è€ÇøKKÀ„®*t°/+‚åwŠŠ"P~ ‚$wŠ/A¨Ü‹ýØ·]ø« éZ(-™ßÃ<»|Œìݤg—G rR=±ŸäãÚÑ“Öìß|×îmõïXÖ®]*çjÿ~³rÅ}²ºç ¾G¬zèNÊž*8¥–gë £iÁ§}sn×6 ‚µKÃCž™ñ@ÆœpšŽíz?)‰ê”aГÙ9€ÞX*„`éäuIˆ×!B,s»~ÞïÚU„*©}"\S´=N-#X"$,VÒ“ËÝSªW°Zàõ3@…¿7nÝ.]¯¼~P~ ’ü/ǽÖý#àÖ­pæÌypöâUðdø>¸¸s¸KrŨÑ2ºŠpËz°yÕjpù³"abÌè“â‚‚«à wž‘cåÆ)$]m \ !Í}\-zFŸäßܽ/žMY¡’jF·`=@›‰MÖa’ò~ìèmõçötr_½Ñ “\ù\¹Q=U‚+ V·4-·ºån°\SKŸ;÷HËæKoH3ØëV_uæÔY0.Ú³uƒOi— +XfUJ¯pM±\Iû½Rå‹•ÁJ9X"X~®d¡ÊÙ¢#ûtÙÿÊX•äÉË«\:§ËÝòÀÎESÈ>œäŠ +V.Á²Gåè,^yb9r6uç…éç0²[T‚Å#yYµòÒI©×Ÿòžå=ö«/X}Áê V_°ú‚õŸZ°Föm´Rÿw]CSÆ­ðdÆÊ_¾cUŽž·Šg¯[ÑñIJ!»¾`õëß%X®Üj°£™²íØÈuZº<†n)rÁJ¶z@³L+—P ÁõØqHZ8Û40ãô<@‹…”¼šSaA釔"©b$ÿ%Ç;QÝÖ9kB°`«jgÉ;Å_ýúCSqWÊ€YÒ7ï%Œ`ç?IsÁv{Q1;ê¥2˜[P\Í:«M‚eÿ[KW{ˆ¬ 5’+¦Z®Yyâê›#ïÍ>HhiâFŽD>[èƒ$!A,çSªu€ˆV¾4­à±=D±¶`e+Š´® ´ÇÙt7_e¤2PªÿùŽY¾–Šü`¼ôce¤Êô[Ñ?xü¨õu2LTäIøOÆUˆ`y#UàÌÏr –—ä Мñctí,H®?–Ö¡E´DNDÔã$¢‰2ÁŠ&I¶H®˜t©D¬$T˜NW@*W"XY½2RéÇ¡?&™*‚X,¯È4€ˆHŽä@„þF¤)†P'|hB$—L$Õ°«&5±t'ÃÀ7î"V‹Ko€Ïç·>ü9ÐÜädܾ{¼¢Ç1R(•‚ÉtÎÈ—© ä*CBr³" &!TNBቖ&¬†#ÀR¹_áôÁÁaðøÉ +KÛ7#Õ±’¿–¯ÌéëÖÛ$ÕVÝÛ_KK˜®•JEùNx§¬#îƒÛ·oƒsçÎOÞ`¼|é’\Ý·o€+—/O?= .U\ݵ Hˆ!5·txÓ°gÛVðÔŸ¾ò[0‰ý+ UL9Âð"N¦!¤nÙ#BÎyYúCéê:ï4…ÇwÌes½ã 9•iƒÏ/žö®öoò·±`eÿåG]«T¥K·‘§æî—Ô#:ÜàÃ~ËæTu¿Ž}Yp?F¦Žæ™"»Ï^L#[Ö‚$W`Í*pùÊMÀ‚%Ÿ‘$€‘œ+³ß“W22G‡'¹V¾hˆ@õôÁÁ’,‡`ùRÓÀôÁÒ!•ò«¥Gå¸ó™lÁZ“éà}9 XÒœ´'D¨––Àif¦ ë½—‡ÿž;k2ž${N ö«/X}Áê V_°ú‚õŸ^°Â¿ùY—\M}´Êô«/Xÿ¿ –YæëÁ>ðLš¡ÈÒ;J'Âg…åË“ã*ÂE &yfn™ÄyVô¢â8×ó‹à™„y:2r0õq_$NŠ&bEE¦:ŠÜ³‰i,€2íà˜Š†‹G‚ðÈN:ýÕ¿~dÄ©œ/ÓM[ÓÝÇfƒsgçÞ€¶@‚Ä4J DjÉ!X šeKcäJ'¿Á*ÕB„"}ºS¼éÒmB  X™Ò­Û™ä.Umf8oE£/g ME^‘«tŒXqx•‘îçR(•˜¾HH¯)Ö&#X´0v8O‡ ô6c*ùqzŒ›“(I®&–,o²&I®&]‚%‚'U!’Ô)?ps;/¥sØÙÑK¶¯x^ÁU‘L2? âÉ20ÝêÓU#õ"ûÒ3LúQ™`±¡ÈUAŽ…X?FžC+‘*É+åð$‡)Yú Óæº 'a¡H˜ûdf53 ÊÔ„iÇËýqP®µ@»=={wì²¶¯Y>ݱ Ý7î€Sg΂Ï?ÿä‹e€Îíº¿U&WF¸t,I”—°Ÿ„Ãz_¡(ðÃÀ)øßN  [w†F@”>SFúM±äBtu±&ÈwWÀ°jUè!'(îÊÏ ‡ÛëÜÁ $é÷Ç<{lŽŽ‚‘#àÎí{àê•+àð'‡ÁéóÁú¼˜Gá}3·è3e.^¾Ž9 îÜ.íØîíÙ´+í>Ù¸ Ü·xÒm  å™®åøÝȸ •u4J4ºÇ½tºDÅ-&‰Üˆ”+¬èLçXÉ­/®_÷÷¬öä¿ojݯ­wú§F®â?ÿ…uþÌE#Xï*·p­4|ø«*§¾†pÙrêz}Ç}Õ/ÁØ£§àbFB„;Ö®·î>“\ý'ûXÚG2Æœ¬²0qXÏ–ö¾N‡ ýñ ðÅjÀŒÙq –TrøPBeStÂÌH,_¼Œ`yQxh¿#¬$X2ìZzz^ûAˆŸKcÈ8¡^ÁRß»ôÁšàq9“Z°4â ,ý·‘\MiÁ’Q9!zMFþ®osJN˜ ý ˜€nØèÏõ —äDyV‚p¯3 ˜Ü˜¤Ìž[­ M"¿Êα’DˆüÁ2Q^1ÁÊɼ•*w@«Vtã¹y<KÛy%G –Œ£aÁªÒ«ÓªQã/õ¡©ª+ò¬3BªVš)ÆyT\ Ø…–¥:ÉU‚åÊÁš_RAèÄ.õVåÜvÖ4(“\1üj<G‚ÔÛÀ®t% ¬Ï·/aÁâ19Ü¢a2Pùç,¿cEKDOKV©$ËÃ9fºÝƒ¸ˆüý}Áê V_°ú‚Õ¬¾`ý§¬gÛVwÉUç¯ÿÒ:µê7«¾`õë›`ÙcGTÎ’ôü çæ÷ê,ý¥™¶úZ|¼9ÉßZ°Æ“³Šž*Bó%’¦ó¨LUã2˜×á\.B*E¬’¥yÀý—rE¾6 u…4ê“Ê9–4|$û€Kò-¤ŠÐŒ!ÑUAMŒ»™…8Ù}­€ÎÅrç`µ;K \XF¨VøÊcr˜rpcDiv*y 庢B·):Àæ V‘¤ŠÉ–f‘:p0Œ§+ FZ†EC`Ëv$ý©‚´£eü™6ðpH—˜"1Êsˆ–„ù&â5 a>Arì8‡Ï„âMEª dç.ò 7ã§¢÷Ô„5ÑÒI F‹ R˜f¸tªbB¡ÉrˆÈgX® Xó ©"Òvú3=Ú  ÐX¾ÎbrÈV†û ê;i6-ýÅ¢‰Gò@¤ÉzÒâä ¤Ÿ©¡ä4ÒmN|þ ðG*V€ä•‘q¸"Kl¶¼¾8wî8¸}—uïÀqðòäU0vwÜT|úéQÍ04tË5#nÒW«@'' }¹¨3£rtW£ytϬXHX1I×1ÒÕïH´|A0úøspå³k`b*²•°‡}σ<Ÿ„ð~§°-,C·p¬˜sµRx¬°ÜPÞnì»K¤D®7Ö3Úf˜a’+fŒä±ýÝ¿6rõöÛf½ áú”þ¾sç®»“â«Súß3JÅÝ,³ço+öŠ•Ü7Lûа>~1‘²F7 9Ü6B´bîÑöÈp_/fûÚÕ@šÃ޽ /ç[eg/USÑ*˜¤c;#‚å£ý-ÐcĸòOŽû^”jB/íÏó÷ȨXQÁû]¢›¢}ã!ñbÌH3Ý×L„Ȍ֡[;ë²y‘ü2ÏD„øs5÷]Ðt‹V°¼&|i ‚*/õäo¸oVa©k[`Á‰š¢}(3ÉaJÚ¯2"”¿¿/X}Áê V_°ú‚Õ¬ÿ‚õ`÷:«þß%Wž Z¹E_°ú‚õ¿[°¤K¶CB_czÿäæíÝQÅ¥èhÔe‘'[Š­‰d[®€)º}*gWÚaFéÆî*©\t’ýíü?ýaŒ%јw|"X2ˆ9¯)°lá@§»°ëT,TÎÕÕø›Ÿÿò7´Óœ¹Tˆ`É Ýwl'LÏ«¶IP×ãoê¥ÊÀÞ>X‹®±9Ë –¢Zk)t¨°Þê8†Ój\Ü¥¿O©Ö…lð}ä³àƒ#]Øeü‹TJe\$YjÀ¬+éQ%”%¼&ó'¹ áNBzÇ,a>© œÊ/{P¡Â.ÁJL+Œ`©µ ï¥gô£f¼Ô.b¥‰5…9Ž—$²' ЏC°$QL²¬HóA—Q@’+ÆÈSžd?w¢È¸‰‚F*Ö2¹†"¯He*€;²›Cº~œ+™T DX™´’*INç.ìAHÔ4ÉUøCNµ@(©ÒçÈLÑo ð÷EòªP]š9ÑŸ‰¥2 Oƒ)_<&)aî?°žÝV ?££OÀõë7Á§GŽv10pä eƒtƒ/+ -Z"`†rðm’,_ÐÌÍÍKµç²ÞišÍiàòk|V8/çàÈ [^2)]T žö7ND´x»aû œ>}?~ˆT9k``ÈcΞ=öíÛŽmß îîݸgÔ(‰#!³g/9ˆ}ݰØûp‹×{Cp¦÷«êΙ ¯ìÒõýùØÀ‚Uý-WœÜ>¾é#ÈsdÓj#Xv’»ŒVyX0ð5†»k¹ÛäÀ/i F°JŠ0íS#\´ÿ •4EÅÀù³`?}_ÌÇkû¶mÏý9àÃÀæ60‚¥CtÒËÊC¿if2VVèðŸûJ!ÿ›ûYÑ êB„ú9åoÕ·›~Y9§`µ€7^~>Ùå“^IB—¤téÎÌÚ}‹oAoÒ¹–c=…Å3!ÇmnAv –‡=#É=‚eÇ}õ{“D|w’»-X*DÈ‚%Ü{KFÈÈjàç²tþY¤2𬽢dV¡¶$‘)ÒAÁÒÕ]zš·à®fœt¼®¬pHÙ¼´·÷ÓÁ˜‘ƒ¼T°¥yf`E‘Õ˜\"ݦAÎ.¥BŽ‹Wøß:Øýü—š|‹lª¤¼ºARÅ´fî6 ŒÌœ™]Òhtf¶f{¤Ê–1yŽ•FoÔë3 ’,†«íf 3/mFÁÕ‘L¹ $YùhÐ}e›èå,=ŹlE{ž`„åŠàu˜S—·s÷zF™14JÎ¥Á]÷ª”’í©¼ÂË3yv É#¹"SÒâÃë2‘1/‹r°tåŸ&@€D¨9‡ØVX¢ Î9ˆºâ17£Ð‚%+ZI ¯Þ‰`Å5rÙ-Xél H#V®4gZòy;,þ„4M“\¥‚U²¥WÚŽÈ>™i€h¬df 4xòXŸô Ð΋ ÒY"#MRÃi.¿? |ÁòÅê ËItŸ/Ï^¼ÏŸ¿¶^¿R<}ú ß§OŸÇŽŸGŽ®"”êÁ¬&W(.s¹XÑ” ÅR,½ùh¯²*Õ ˆÇb —ËùX 'ÆÇ?à‡ RHû ùþSÜ:£¬ö=Òl×JƒGÆÀ7üýwï‚#M«…A’+æöÛàÚµk@V®> dµŠëÞ½{@¤LsîÜy°ÿp|çVpoß&0âh4z`ý*péòM*¿+å(}­ª“`-¿*ÖÓüÑq[¨ú%xõôHüüŸºÚ1„>ú€Äj³¬£›Vý¬Å•_~Å ;+ž“tþN˜¾ŒˆU´8"´-1rÒ‡ëèv&D¿gæÚ±OÁ^ú¾˜×®‡vï^(/ªõª´ïmªlÁÒ9¬ztŽÈ“™?Hûw©Ê÷Ñã_—¨!ÇŠ1ß·°²‚O°µ`øè™¤“[ÆO²ÅØ9X N?eçëê<¹@Weß’ÝbCËêóU·¹c«W==t¢Éˆ`…ʋﭔ`-[°fèýæ€|¿}Áê V_°ú‚Õ¬¾`ý§¬Ôá£]r•ùÙµ\õ«/XÿËôÉÈtãi4êε²EKB„’ƒ¥+·dçÕ„ËÀ®SK”>~Èê¿! 9XêÆ“øé`̨á¿„¬Ýdè`×…>@‰x™-Ý_ˆ ÿæ*:°ŸýâC+ËrÅSE #c¸_3ÝRH(Oåb-‚iqôæA%_Ó3ó@ò¶Z$[‚ÈX¬Íʘõ:µZÔ+ g£QVlkô뉜ÕmP¦÷ §KW;I¥š|6R!—,uÿeüüCá8:*òdhrÇÕcJWÊ6$?䮜*úr –›¡úèHe“êy¦rD¤Ö$?Ž·] (U°Aú13¡THØèÜ*© Œq#M&U)&–ÍêH’0I¸4UVH.N¦²Ìe"4‚•µKr±¤êL+«é3&‚ÅÕ‚Y>pãõÚ ]E˜ÌÔA<^R覢I¾‡Lóߣ{X…x ;d{Žç[ –Ó°˜¡`FA;èË©CP#ñG’Šp„¸É'!½§ž={Eò0 ®_»Μ= ŽŸ8 Ž;.\¼ ¡pö»Jg™l¡ /[ÄJ #.,¾¡pœ;y ìÙ¼Ù{ܽs𘠳=yòáƒ'ã N;OFrT%g5B¢§.Í6Á_<œcÅܽ{œ8qˆ, ‘()î *îÜׯ_çÏŸ—.]<0!Â;wîyÌ… çÁÁCÁñ]ƒ{{74Ý$Dxñòu&¹ C°–Ï»z_.ÒŠUwŽJº•š9~Õ¨Ÿ¢%Ó*ráªõå·¿mäªöÃï[£ÜhÔ%XÇ–,ÉóùêêÁ…÷üÝö`fõÞ€ˆ#¼MˆH•1ÚW0ɲ"NÇ,PœS°Ä ¦ºÂé¸tpØCrÅ|¼ZqâÐ!àì=ä¢G°tActxÏ xæœUÿæO7»ñ2´_eä9ø6bæ×&¼Ñ ¡[„D°¼$=“$WÌŠ’¤+9X“$WLÈ%C˪¼^’! ý«Þ+XÝ!Â`i,'X!Ú7„ú‚Õ¬¾`õ«/X}Áú.X©ñ¸õå_ü¥‘«Æ÷ÿÖÓ ü}Áê Ö7N°ÌÀ[ fܳݘ>ú@ù^ÁŠ”=.A÷"ÒÒ$Ã{%äd¾c÷'ÒïÑãÏ?½O&ˆ¾]v%$‡udذ{„‹ Â%!C%X*)•«¾8D(Ý›S‰<àäw¦ÜìîºR(ocuæ@…Ì4 cˈÖb¯`IÈP#aÅF½©¨*ÚŽð¢[´lySÏÙäÔ\U˜/îþ.£<äÀ/‚%b% ¹b&¹ã/EÈÛÃYípÀbRQê‰ÕÁ²‰ê"PF°TwaùËí¦Ë³£Ï•—ú  ¹èAo+"Ý"XaÚ0 ü-ó@BÊI®@s ‘©‚,"r´=0)ûÉö¤I—"ò">)yCHk6Wíé“$—ó5Eš{‹1‡0¡Ä¢]y‹æA§29…07,gÒ++Êž‰3gÏ]›·€O6n·÷'Ïß]§÷Ã|öÙ5°oÿðà™„Šo€=…@WÛqÈŠÇ™ÞX Œéquïî=pæÜY°oï>pãú-kxxHWv‘¥7n€ .€O>ùH«ááaƒ„ oÞ¼ ._¾zݵ8“ÜEB$Dxáò à®$>ÿ&ÃsW,¹f¹¢>&Nû“·ßµ8Ï篬±]kͰg•#ã –êƒå>Àe﫯”/»2PdAª“c„Œ‹—î*d-)žáÂ}jK H¿]æÌî-`'}_ÌÖÕŠKgÎÙ_ûùþ<¦Œ¤š@öÝ"X’B!á=,¼j£Iª·cÒ0ôþ9¨¢¦ Ï"T~ü™Yà”?/ãþüC$¸G&É\’Æ{kxÇý@õŸz£Yr¡®Á’×aÏ¡’]h„JW: $X¬ XtBÊ„þµe͌ʱ…j¹|«Îò胥[°ÌŠWjÁ²ÇŸÈЇ:0›•-IÒè4ä¨ö’ÛYÅT°´!0RÁ¦ )ŒÙj\Â>§àÊ+v΄-}`dÁ’Ê,™û–¤3vF*¥ÕCµ9dFa·\©™„¶`•€,-@Î18³=¨ŠC«¹ù%Ðl4UÅš’vçoI%¢ëµšÓó V(ƒäo)$‹Gà0ܰ5áXéALÓÐÀוOåZ¹Ê+dÖ¥¬`9Ë›Wˆ8y¸Ü5=kš‡ÚÏ壑·ÏE°dl‚ˆºVˆW¯zK£s¬ì¿sDIT˜$ #Ò”àYz…*)h)3ד„0ñD˜7$X2J¥L2ËpÅ*#& \S$L ×a2ŽªX3nG#«]I®päU7TÒûE{ %Y&‹ç!ÒB¢Píw[tº²…i[ìøµøùò3 I‚\m¤s-pëÊepûê5ëå‹W€«ó˜W¯&Àëq/‹ÅS *ÄRæºXB!-¤¡hÿÎ@Æ€¾œ “&¤2“ðâÅË`ßÇÛÁÕ]‡Àóã—ÁÓ!ðų§Ö…‹—€Tâíß¿Œ¾Œ©lZ.¿H°ãÑà16Œ´T¼w\¼xÈs_%Á’U(­»÷¬¨ÉŠÕÑ£GÁ¾}ûK¯b1¼Ƙ\,DFrÝïÞîìÝ Föo22rpýjpíÖ0ðÛJó÷¾ŽH­¼JµÐ“ÛdÏ™ënk «Ur?_IÉY¤ óÁ¯ív ýëÅÇ«±Çâ8☳(ÍTOm^e?I™¯“wX¡åJU„vãëÓrÅÄHšb’…©F–VC"ZX$àß19Ç>ÞvmXD°n|v$*‹ÀÏó_]‚%+X¦´±IVD–¨"ÔQ¥d]ArÅ8Çø3&@ûaÆïÈ…òÑ>™™$¹š„`©±;FftžUXÃmi jWõiy•ç&¹b^?¼Zh ––"-i+ –sË–¾åk嬖åõ§€¬Zö«/X}Áê V_°ú‚õN°¸ ÌìïÖ¹úòÏÿÜŠÜ6áξ`õë›#Xºy£»BЛ³ñhLÐ U]‹]Ø£MíÞFzš¶Ÿ®÷;ÈR}&‚Ý‹ûrÍ‚p¾ 8Ï p2î“Ár•‘>^tWLÑ,ç¡Ì‚X^¥Çw¡óµbE V¢ *À¸ÔÏ~ñ+[lÉÁ‘¡ôžª“°2tÙ0=zB„Ë– 2Ô]ah71mÔ "œ›c?ÞôäÒÏá͈ðÕ %Àƒ©›:$ÊÅSØ obàãw#beWþéíHz[™û9î[Pxy¹—°C„Ë@UÕEz@3‡ùG­«SDÐ¥BPªêBɈ¡gZG¡›ùE Šm/L•’e#\ &&™©Ø¹PåY ¢nD„„ÉDHØ5Ÿ¯Ye/׿z†‹he²5 ¢•L—7QËWfzØ0çv1éT dø:¾Mz¿¡jQU+f3eP(4€lÏÒ„V.çseP¤¿OÆ»˜<=pLdYP^•æ[0pñ ؼj•µsãzpêè0ñÚX ˜ÉU’•r9MöT)šËú±Ns^Ž%ÒFºÁøü‹§`tä!xyk¼º7^>8œwùÒepãæ ôùD „J_3ÆŒré˜ì@˜jT+‘&iºÿÀpýîkøþ#044†5*á“ü­;w~¸žPáàäéÓ@ªÌönèƒEBˆ`]¿ý¸ëëôêE~ѽŸÞ׬tÙÆžÅ%‡`éç¡]ƒäØôºúö·­øÕk|*n ‰ã’Ü"Xg·pŸ¯‹ÀLMøéý‚Õ-a .æ,È~Ť"”ì•DYSj;§s¾ gjK¾±Æ_L€O6¯{H®˜-«Vƒ»÷@†dŒ `…òm$abLSP©”ÆŸ©0á¿tÓ®Ô¹U"i=Mhµ`ù¹9)n_è‘ ,I3é j™r –34ì,i4:ÆŸ½J°\a>×÷)¡ïÉHx¦9XZöl)“ç²ûc©a÷¨Îöø@ËŒÊé V_°ú‚Õ¬¾`õë?‚`•O_éjÇP¼6¹ê V_°¾‘‚e'ª¯@¾ÓÕÈë zx@/îÓݧÈ>à.XžäŒBhæ€|‘,RŒô&’ƒyXwÕŽ9B8ñ‚"@ #2&}°bô8‰¹´Q%º¤JpÍ—Ó¨ç`ÁŠäTøC0?ýà7V¦ÔÙt Hõ }@Á‚5£Ñ!¹r®zK‹Q«½èò,tÎIª×À$¹ãq é ¯3¿ØE³ÕÒ—‹«Á]â }J'j Êç+ËË2@™C&ÝàJ\I§1#É•ê>K@ÈÊ#M=‚¥j9»S»Ÿ“í‰-&bè!c‡‚c¹&'« Ç séMÃãs€uÙ HOéšÎ‚ebWg%1Ò » €IDAT¿Pnl*ª6(«V½9j+ÏW yÎdJ€?ËØ¦¢PQä²EåänBF!ÉogWÿb¾ª¥˜n-itE¬o eP­ÏÚáo}ßZcÓ`W­~ ž?þì\·ÊÚ¶V±ebôÑ#¥A0œÐÄ@ˆ«‰`8j‡†h¢-e,cŠPáÅ4„¢àÕ¸ø‚A'@ 1×ILé!õd2å/{(± U‘楃sdT1<†èyI¤?tè ¸2pßzüÜd¶ÝK…%ÙýÌ™3`×®]€eKKPá­[7ÁÉÓgÀþ];ÁÍÝÁ’¿•„ 5sãÎ}©¼føð²½’ Âo‰Çdi Ý E—œÈ0^…;±Ù½NfèV¬D®êŸžÁÁr|2 FlÑH/µfÎm]m:u˜K=‚µô¹Rï9ä•ôÍÓiŒCœ$¥À\®hôegµ{©¹ž>ò]í%!f¶®Qܹú‘`…ó³ DÛ#c ––$].U†ÒkÒ‡Q9º·¡,ºì*ÂŽBB„:Ü@’û òFK@áMøÏ–bŠÄJ°ËºÓ]âõs{H:™÷ –\/‚å eAW,—`ÙIõv8“˼W•C‚åõ'@`IΊø¤^”ÎQ&f劫Ãò‹=+Y]É©\O¼ ¼Á<0g5zVœXi¬¼â•EEí•©.´FÎüáчËÄç^0m2ôxÉ­2%øº4ßœ.³–•3,0Žyçè@ù3ä`)Á’*B3KN›‘,US×=å eÚ5Ð)gsÀTõ‰D9W°xá2Ø+PJÀì,Ü13ç–ÀçlÍ-Ú#yôê×ôtTKeÀcz¤9i«s fg`æaå;@æÑMñ¤uÂÏ£lôÌ0óÝËx›BG!+¢ºL×lK¼½™™cj¶˜¬`y“MÀÏïG³Q‰ù/jlÁ Е‘¦™R#ß¿TÐ…içò»bUÏK ŒúaÁªƒ¯Þ홌¹BÍÌ|•ñ7²*Uª´@D¦€*ÍyP)×Ló׿ì&°"ã•r”+Ó X¨ƒJ}®Gê¥ò³\¬#EZè¦yÛÃ÷º`¤¾L·35’rÆÈ½k5µF2È4šm»a®Þ^§§ç@%›’'83ÿ„ƒ1p`ó:3Âc熵àùÓç@V˜DŠlâ ¬d©ü)–-…–°HT¡eÌ´‹ÐDùyb ­`0 <“> cp^½zFFF7øyðHþÒÀ3_8o‡°s…d–}pñxð伇†äU;vœ½tÝzI'ŸŒ4ypȪ”4•v {öì‡2ïttpóQæÖÀ-pš„Œ‘™„Ÿ‘\}ÁÚb=Ú¿ܼܰ= ¢µ· H¿ÿ Ƶ¨ƒŠ9Øá÷7´ïe¢ô¹0¦™&òh{…$èlÚiæ¾i$߯ÔÉ'“ÖÛï|×ÈÕìšMôš‹È§‘ƒä¯^-—ƒ¥óÌX°NŸ: ÜãPÌAÕŒ´éEîcΤH”RØÓ"¨bG²Õy°Ü}œ÷ËW55Ž*,‚1mFŒJ›†íô½1Ož¾RýŠå±hÀÈ ÕÏxÛMD¥BЖ4iNÛ‚å—@€N`çãæ+XjΫÉÁâ(‚©"lYÁò%*@Ú€Èg+Û¯*É,BÉurWÊöeæÜ¾ö•sõFÑ#Zê·)+&y$O+"åGΕ«…·(J°¤I¬¬‚Mru¦/¤Š°/X}Áê V_°ú‚Õ¬o¼`ż©.¹êüËV8Ñ4 Ë}Áê Ö7V°|¹–¥h­.L¾Ô¼F}ðöí"TrY7å냱/˜1úÁÅ‚1z3L²ªHÕß‚L}‘64…Œ§Hp> !ÃRcÙi Sùz”hc,ñ‰‚&__²ñIÕ†„”X°xi–›B–›‹Ö¿úÈä¹d“9 ×J£ ì¼+Ew+-XúàVÍ\R¸B…]#s$'«G°¦µ&hÏ¿qÜç hk$4(‚Õ¢÷ÉÔ+5ÀÏ7Í!>°N/©jK”ñBÈÀd‰µ!=`÷z¤âOz¨y#e •'’·¢òXT˜E–|§xG@Øá…0!ˆ‚]%(?n¬o/,Øô3Òp3šm nSË¢ œ?$ÛF†wˆÜ/­X·EŠ«u5 #C¶«õ6`¡bDZª•º]aJßÓÒ¡á|¯3Šæ¨×fÁôìkºý´ºŽiÔ¦A•D›‘×í å¨0­–*ÀÞnÔ6!÷5!èR p(y†nŸqÜÇV&ä1-¼Ö«Ri‚3v[Ûè7ÅìÙ¼¼z=© üª˜’#éa‰)D¸LÞÉVaŨBß&L^?“H¸§OŸiüyçÎmÀ¹OÒ»êÎÝ»àÀCàY°Ü!B»ºÌÊú™?$'êþð}p—ž—9þ8rì¤5¯ƒGcOH’TJeàÅ‹‹Õ„ryïrùÎpîÂy°wÏ^pi÷f $D±oÃ*pýÆ=ª¿Q:1Þd<ŸŒ€GOžY÷†GÀÀàCðx<bôûc¤”V¬áƒfohPòkrÀÒ>cñG?1rµôý´"¡ $(\yÐ5Z =˜¹1«¨Ì(òµEàìƒU¨Ï#‰½ÞZ²~ùëßš„â|:œÕ‚Œ ù8KТeBDºsºŒ½©kÍöŠ•ì.Iîê ×+XoI·`-(ôP„¯IcpÖ’'!«JsH_$©ãaÇL$S\<*(üô]+Tu ÿÉÈ¥pˆÜ ·™þw(¯dš ˜ª çº{Ǩâ•€ˆŒ‘0ýeô(›D¡©ÈÖA¼È½ÒZ]D¹÷•–wªà›B/4*NgJV‰¶¦ÂÝðY„ôv&ß§|¾ .B¨5ìÊOú¬e›1Fò´;o@³9Z­90Ýh–`–h'Fxô}¤»¿]qúV³dÂxnÁa—çâjT¦AâÍ´fÚæõä6yoålHo69a˜¥m›8wÊÚ¼z Ø·m3ðù‚ •Ê‚D2 ’$[L:I¾ÎE‚{aqO¬X˜p¢&¤aa»wo >átD“y‰§€„ _ONtÅb`llìÝw<§“FB„=cRŠŽîä|`ከt <"X’„þÙgWwfæÏÞ0ÊC ÿ‰”É g©&dÁÁr?æÒåK@*"OïÚ œ!ÂýëW[w@¹õx ðèó'àáÃpÿþºßmpæô°oÿ>0øð)H‘X0ÑRHÊAÄqà³C2yð8™öºMv;†?ÿ +ûÌG÷혢”I㌂$QL¯`).n[m=}¸ìòœrâæÔÇ e'RulW ªQ©ÌT"K9YÞŸVçÌ}2üï*ËÕ,ÈVÛÀåÆBÖ·Ïž;×­"X{¶lããA Ç´OnÐc°¢Ùi`Òó³ œšA’g&À=³U 8§ XFŠÌ;©ÕÏé•ãzIœyé{e’ÏE°¢»ŠÐU5(chL…"‹ áñÄF¿n°ØQ˜P³®PuL†²@ÂËáêë…/ F_5a0öZó*lýí?ü„þOžL€—¡¢å §A´¶bµ/Á·ò­/-FVŽLù»–¥6öE «M1Ž‹—ìxµÝ`NÍI|5Q¥ËM(<,÷§ÁkŸbÂÏõÁÊ‘-2ÅúH¥ ÏMëvÙ{UÓp´I *i­ 93"  Wñ Nøå¯?"¹š…L4Xªbe\š–=óO¤EÞ‡–ûö®¹ƒîf¡®r@¬óB¦Úör©·Qr¹ôsÐù[3ŽU4É’œ²j]Q¦Ï…É Sœé ÏÐR„Šà>ð˜#WQLˆWˆ$™ÉñùˆçêÀTÔè³<9ëS7 ’()HÌÙéÙr> ÒùàêH»4ZåVÉÎ.W™Qd« P›Sè ¾7‹mÌùîEšdª5ÓÓ$;Ì<É3 ùéh‘¯ùŽ¢Õl^9"ZiОë‘™ëhæ¼N“dŽ‘Ös·§`©¼;s_×ê¦Ùµp~“ËA&“a®$ž>{FFF­áû#`ìñ0>îq’¬8d+ ¢ÜÒ˾@Ð:tè0ødD錕‘}[N$»r¹¢Ùš•Ì×Á㧯ÀÅ«7ÁDjسӺ+–ó"·=Ÿ’O%¢%«RG³îÓŽš‘j«‡it“R]M(cpD´¸uƒä^¹KVΤ-Ä'‡?‡wn÷I°FlÒ¦áÞAHåÁ£Ñ1Å£‡À^Q°®´1’K¶}ûÇ`Ͼý ”ªýfi¼YfeHKö Ó‡Žuµc(Ü·âô{æy~f‰Ž/ÌÔT¸KÄJÚ4\þx­uŽäŠqØE¨df 9©Ó£mâܶ¦¬0cl4f.©®±ÊÖ9?VUˆ`é¢cQÇ$½¯áV(u•ÌpD&]lëG?;H®˜ÝšOöì>_Èþ,É9æŒ6¤‚‘ÜãPª|ô=1¦m·dÐ+üÒh4¬³O—m^7ºKf–T-Š`…\L!¯i…6 Ò,”W›Ê f%mÒ!GUª•S§+@éûc¼ä ŒÇþdƒ¶ë10<ú j†Æ^‚O§¬ïüý­‘'ãàÆõpùÊ ëÊÅÏÀíÁQ0òÔ ú‚Õ¬¾`õ«/X}ÁúÆVùêÝ®v Ó— W}Áê Ö8Á’™ôΈҎ'Špá<ˆWMã²,É #Ã'“$=Œ Y~(€‡_L‚[·‡éE¯‚ó'N€ëWo€¡ÁG`øþ¨bXñpä øìê5ëîÀ0@N!³‰ (³8MÏ›þTu „ñªÓs L1S¨Ì‚\I! Y°XÒjÓJ.~ùë{KÂ}"M=cqf:+ V¹Pæ1:¤4³ ­sÄ”ë@ÂŽ ÷cºêÍfÔJ5Ð-Xªç‘äbÉç]›^¥Ú, ºÒô—´=¼2¾'gA÷ 'Ê ÅyvL…¶¡ª"Mgd4M¦Ø¥æ[Pl.)pý[+™©Jœ¢!#9îÆœåé7V¹¥¨Ì¼UÌ*ªôÝ3åRÔZ‹ ®©Õ¦íÜ9]]gÆiÁ˜ž™SÔ CRÂð¿¥²S¤EäK¡jµfÈ Ö<=Ç<$G=¶ŠÑó:ÍZÌÎv€-FvEi½RÓ0Ï%a@¬r°äõISÉ值w[Ò¾W/¬]kWƒC»>‘hˆ =þü pñÒpòÔÅÉÓ=œ>sܺ}øüAJf€„‡†î[Ÿ9 |œÁ}îŒ IOœî< S{MøCªÜÜ}‘ä~F¬œ²¥B†r@xI'’Œ„òŽ<"M.\´._žpˆ4‰,IXQÂ~ΰàãÇ<ÆÝKúnIXq7É.s{‰Ç¡­àÐÆUàæg×À³/TUÊ j;îÅõÉ'‡ÁŽ;ÁæÍ›À‘c'€TðJHœ– ‡×Dh9A+Œ<ï’«™Ýé÷Ì9N/Û¹O‰ú[à÷E;Kr¯¤™êÕ×ô–T„‰Xñó3‰Êʤ4’seŸêÜ+Or$p5 É«*Ð1§Ðè˜cœP¨+ò„ Ÿ¿¸'ÁÚ¹Vqšä– Çr@„/ÉclI7@Çç Á \‚åçœ,ò&˜n*$WßÇäFÉõ"Xœçªó¤|$WÌ]ÏH%¢ êœ)Ó7‹‡&‡ó g`3}ÖŒ|gF°&ý ]ÉZ•vN1—å¶¢:›•vŠ“ Rx Š¥ +MxãK«[ ZÿĪo­ÿ÷¿³âtÃUš ÷l|5O&¢`ðÑ Ð¬¾`õ«/X}Áê ÖÿqÁJ?ñ"×Jäj~Õ:+ÅmRú‚Õ¬ÿ¨‚Uã±)->`Î3V‡Qx‰²ÚZÓtb ¥ðpì8I?&æìñ“àÚµÛ`ðÑsëó—!0H»Û­ê´à!º< ¶¤ÓV0ºcÁšòû­»×ƒÙ™,hÏ)š¥)ОÏùÅ2è,–ÀÂBÁjwæ 0U^†9P#abX°8YÙé2 V¹6JÙ2‘šæ¤f¦rp’ÇT `’¡g—€yÌŒSt„î!ÒF€t£ZE¤y×°iûyuKË[£> äý̶íÞ&œª«%¥ï—Z Tøñü<3oh»XÅÆJÎ<ÿ›Ñ;”L¦ ÊtSBÏ­E ÛW¡2£(Öˆôç2å‘>Í% £bd0·¼¾lÏ…j Ë Àïµ¢©ÑgÎÔg5üýUúL™iîûä©&‡ëD&: ÝÉå™™y` Öp –È’‘«Î"˜áð CÈ`Íι„ R´Z$JÌtµlÁZóó‹¶ä5š ÕTHhRäI.Kˆðý‚•òÞå5DÖ¸çÛñ½»À¡=ŠÛàÆ[@äéĉ“àÔé³à48Nž: NœT9v\¾|Hx1O· =úäšo€“U…(c%…„ ù„R zìÐBwHЮ6SUfÎîäÒ3*XhIØžLTÈ’(®$‰Ã„W?»Œ>cò#܃œgï+*í.͉èHF¿} H­}û÷‚3»·XcG6­ÏŸ££ÀÝ{w„Ož< _f2 ³‡I®é9è#1cLâ8=/c’Üù·TY"r>NŽçw]‰('7A’*&¬™ ¦`qwu'"X!:9g$ ¿˜º ¾\ÚgYï‚wÖ'À²+\׿³Ë:ÞYGè;Î91’ŠÃ0½vXƒF'wý·IÇy_¶eù褈‰!Á¶'æ[2&¥ÚRäëó G_,S›}geòU0öpœ£sæø1ðø‹W ^˜2ý›W'"ùJ–AšÎ`˜$6#g<ÿ)Ó›c¦Û%ëíÛ#ÀzwXa} ÞÑöÜúåúwïŽÀ\ã¨7g:ÇJòŒš³ ,–É­ù –ˆD9W¦ ÌÈ•1Ò-X ]ȪW‰ÏØy[êso¶ z¡nÐùP2ŠGr£H®ª¬€°iIj¸žšÑ<ºZQ«JrU…`-­(X²ê'+Z5’+¦ÑhƒåVÝD{SrÐ̨ çjŸº,¹eÒâ@>_<É±ãÆ›5Úa3_¦Xž²ê&]"Q¥PãœFý½ÒøÓ¬Dê K‘&^Abš• RÌ/,j–ZNfgç–H«Ñ4R$÷ùê,(f¦[ŠÙ6!r>FÄi%Áj·€-X æuD¬äyíçÔ+júoiVª@ Ö¢FÝG䯒ˀ…ÿÅÞ{¾×‘]wºúÓì™Vkî|˜{ý\4²­`Y¡åV²ÔmusHÀ0‚9Æfh6I9ãäœɺkýö^»êÔØ-Kº,ïà$œTUoí•è²eÇkXYy|¿ÕÓÞ®^½ .žof¥JËScc(,EIcd¬±œ:­xòä)ü-Î1Z Ä€ˆ³œÄ8'3]4ù.’)gü3<IÏ©4ã“´PÉŠ¬]e–¦ºŒówôê¿´÷|À)_ ܾ}ˆ`9W§H„˜A£AÇØ‘0Éß’U//§P¹‘Ûtuw‚ë×n‚“'O‚Cj¬žúÝà|Ý>ÐÛÛ$Oìúõ ©© 9rìÛ_cíÚ½È8¡‘Ñ)âêVBV€£¼Îû¤ãÆÔ‰tÛœx"ÖÚ/~i·cxçgVd1¨[¡A>/Ƶ[x¹•1þb”Wî.á: V3}§˜n#Á ­Ã “*ù¡É1E3QnÔ|A9†‰pIÃQÁ¬tq% “ W§D¸ÐÊ!j·xJ@?]7JòȜٻÈ –V[k'˜ã*iTJëïÝBÄšb¹"&ãÀˆWms;®ÚfñšƒI>V,R“Ü„™›1ë¬Q͘? L#PÝb”„8Knû‚Ä‹‘v’£%òd€NûPIÈH®•,Ü÷‰ö]Öë½äûÀkk¿âõ`Yš×µ ù[ßÞzuÔZJ†€ÉõÒ"7 ©SÏãçï}bMÑçÃpž8 a|1>¦èûÁˆ8þEÖ©¦ËÖÏÞý…õÖ[_·¾ñÿa}üÉk~irõw÷wë""UW÷޵mÛ÷I¾É:\þêÕaºîÇÖ¦Oj}úÑO¬>þÄš_ôU«"XÁªVE°þR+šµV>üØÈÕ«ïߊ’LHŸ¹Š`Uë¿´`ehC`$’̽¶¹}ëžuòP=¸|î¡/3#¹O‰¼ÂéW€{X1 \EHX _lÈÆ Û½û‹_[·"ä /ßÑÖO~ú#,ÛA¦ô›ôÚªÖëzpîÜ»ÖåË¿¤ËêÁ¥K¿°ø!½aõ´s~Çjn~×*Fƒ3g¬ªªjô¸bDf’t0blÁRâó»?1‚òA†®c’)…-Eê~ Gc•xYXò{àoÄЍ£Y¥Î,ht8S‹A„ä*"aB"™±{q¹›Ÿš|1-…ñX8ËÈ‘&‘QˆèIÉD"$‹ïk£r‘$çËôÝÒ%ÿÏ®n,b$ycâ‘8fšò™Å’+ äÙ­‚?"Ñ à°%#¯%–È‚h4z•×)2оd¶`¥b)`äBKU2¶Š,,,ÊÙC y…ú,ÍK!lc¼t¬|DßæDÕ°›âI38tÌ,Å€êÈ•Û~Þ›¢Ë»ŠOtáP ñùÈ4è»óŒLzÌIÅøRLzÒ`œ›Á[Œé_È!s¬Q’«QVŒ“Ä1Ò$V¤IB„¸ÝLLÑöÅÑ j`ó nŒN½€õšÄêõ~…v‚×V­ í†ZÀaCo* ÆC/AY=ÏwI°¤ÿÚ‹)/]"Á›“Èíäç¼þ"‹‰¤áäKðÖ[o}©`ýä'ÿ‹U›73­¶¾óÿ ÁúÉOþo+ÙakvnÞúö·¿]¬Š`U«"XÁú ¬bý‘¹Êµtj¹ªVE°þJKv +«ÐA¹zá8Q¿ß"c¹åW ÅUSµ²$i8šZ,]°½a¥ -ŠDe½‘ІƄ8áÓŽnðÖcë—¿ú9½è¬·ÞúoàÿïÿË:vì§Vqå€õŠÞ¼o|ã¿Ó›zÞ Åë×ú2ú¾~UccGA"·Þ~ûm[¬$i\‡‡”`Ùaºß}@‚Ë C%èõ0öC!ƒw£qЦRyç#¹b"t»ˆÜžÑ½¦8ì%¿Á"kêÎôŒì°d(p$œü:ʺÌëÞ\F"t¸-ϧð˜ŠFWçxIÄ7ä¹c8wOfºÅ;*ÈÐ5\#C¦%é[ºˆç1ˆzÕtšW¡ºŽ'é±,yÉŒ"‘Ò¤¹ßÔóf"Fï1#rX&X‘H9Æ•Ž'Z1 âvxOI… †#¹1y‡œˆødsÀ!Á4Âpêò$‡úD° "=Ë:„§+Mr•vVš¾³LÞÑ+Ëþ¿Ë@*Óñçj$®°R¦LC®T÷wù¿v"»¬`¤è¹ˆ¨‰Œe3yà›£cã ­£œ?Qsɺ@2Å\¼tÈÝ-M‚HÕ9nÑkàÄwâLC¾}¨Ñ>k ýý‘‘DF´²k ‘} $;D'x^Ú1ó$HÌ,÷?ŠMHª`=<Œ>¶f*«BPÂ&•®‘ÌkðÅÈ8yr‡òJ idD¨$4(ˆxñmíäyõxî¤w8¦¯Hÿ-î¡ÕAÅܾuÈø KÍÍàøñã fï>P]µÜlé°B´=1ÙââIe}솜³%‰àAú¼˜É±i0px<´˜*Bôûª²Z÷o±®?f"Ë@YBv¨° )-$Àsµ!DK‰—ôÌ’û˜þ~¦ÏŸÝõÝ&fƒY0ÏáK.W÷ãEŠ;ÃàÈÎÍ fÛ&P·s+¸÷ð0=¹·0 #Ü_Ì…À£ è¾ZZZ|îOžO›H˜,Ýk"¤Ÿ*dÜ]6Ycô¿,ŠÔÒd’Üõí•\IˆP'¸›P’¶‰ÈKð—ao3P!ÂýšZM ªº¬×'-_*ì~[¥¡I–, {¦0FÛGI>ŸO,é6/÷ýÚË—–ÅLNLƒãµûÁ•sç@Š´_r€ÈTiº £jR§ËüÁ8ð.€V8^¡dâyÐÖ9hýà_þÕZòÓqFÁEDb{¬£$X[·~ÅòÅ¢e‹~çË,ùùjŸUŒ©T —ÉÙ«ä@±X ÈÍJª™|Üh4É¿'Ìsçy…¤Ú.ˆ¼%…ä£9à]ôƒ½f ›£®‡äÙ9 ª}„HS˜äJ‘ G[ ;_K¿N““µÜ‚…ÏO€ÜMJÍ(-K2þ%•Lg‹w•›É¢çËăaPà*:®¦Cõ¿R(¾ò¸‰h äøçíü.‘D–>yŽÑPL¡W¨ä}ˆAvóôIh$8/NVÝäñm”œˆˆ˜U*ÎwŠÙ+Rîœ(yݶ`É Ö2Hò V¡¤9§`çHéF£$0ŒÉÁÊ/;ž‹FW$f¸)CÏ-ãXÁræ`É}$KVÊìü1½’¦%* ‚L:K'3/lóAzæÖ­ûÀ–¡‹àâÅfÃáÂEpþÂ% B%•î•,ν:G’ÆœuHܬsŠÓ§ÀÐÐmP ?ù>»G@™†À¦"Wªu•Àsžì‡Ð’s~tNªä…prè¬hU1óÔÕ$’gÉÒäÅä,èëë"F,I²"%l$Vr¹iZÚß¿¡`I%¢´o¸5, ƒ»wïz{zÁåËWÀñc'Á¾}û@UU¾}ð6ŸÉ¿qú®‚l$]-kÉ×, ß±,G¯«•=û¬H$‹ý¢i#ûOT1“ñDòfö¬—« ‰é‰Y0Ä-ˆÁÃ;Gvi4Ú¶«u™ÄŸ™&YžFãPÝö„ESM(²$ã}¦¿ öJWém%OFêLëŸ èF=Æ4çóq_H!ÍTç"kV_{7f°"XõÕ;Áãs@Z0ð("fœdKªöÑõŒÌ¼ÙÒ¦Ð-f½þþŒ\­}ò{+ËšýbE°*‚õW%Xƒ´Ñ2‡kö€[Cƒ@vRùå׿ÌCvÞ™\Q£òmä¶fÇÅ92)u€÷úc`aÞ ñœFíÈÂɼõÀê¸GÓ2ˆ$3ÖÂÌEŒÖƒÕâI+?bý¯ÿõ z3Y?{ç›VØwÐZ¡7’ xëh›ýŸxƒöSºÎ[kåbgÁìü¼õÍo~Ó,_‹´H(OòFÎróþï>¦Iø–€Gé0AÚÈ™mä@7/õÇrF¨dG¤ËÉ%#&Dèñ˜0v0EM¡yŽr€Š8¬’JC]%)•f±–©ŒøÃ d˜´ E+§‰’f–"…åµ²f™¶pé< “à!ÃD‘¤€)ìRÒ*H,é­$ÏËY©(Uš1’&FÚpÈwPÄ2O+ôX¡dC/–ÆŸZ D°2ñ0¥¯g‘q –ü-¯×"´ÃIó8n•×™Ne€éƒER&¹["aeҧÈÉX di»dJÄO¿Ï™T d“ P\Yr[yO¥±î’7jÍñè"bt!Fæ’àÎãIÀ¡@š5¶P?ˆ„¹KÁ‚°iÁ:ç šË/(š×aT0®^ç{“̬3h]*l¹’º¤W^y¯É+2a|êçíTöô€ sº±à 488kÕ¯)͹±’±7ë…Ý9WnÁº}GqçÎmp÷îp›DIze]»v?qìÛWª«ªÁí;÷Aqõ%à1Lv•:ëÁç—üÖëÙ½®^ýòWV*S½ýtº…D`åx Ã=\mQp…3;³íUÚL5¡îƒÕ¶³u…¾;ŒT{‰`‰XM‡ »:tÚ4CULkir‹–ˆÕ›pçxÉÿ‘0£„(%—‡Ÿ[÷•«àI³w«âhíð|>&æ‚`”„йóð©ÕÛ7ÚÚÚŒZjmm7nÜRé{òä 0pë‘5MBÁŒ-„Á„7Æ9` –Ê«’j±ЛKç`m$Xs!k„>;C„&T· ¤ªOV¿øâ)˜ Æè=K‚i:.2“¡¿g#06=¾xò9¾ÿÐ|¾ò\¦HÀ§0ZG…JYœ¸Šp>¶ x!ˆ™ðf¬ñ)×%ö!XÝ<ñ~.@b’/¬Ÿ½óÖðÀ§V"R¹:°ÿë衃`møwë|ÓoŒ`?ûïVÍžŸ@°Î6üÚ:ßøk#Xg¬ÝtV¬Š`U«"XÁúÿY°’9ëÕüÎÈ‹VnÑg7O®VE°þ«v×v‹´íI<Îpâ¯kˆ$ »CJ²¡I²;÷@  _$9Á<½b*9á{£^WÍ´Ýôûß[?øþ÷¬¿ÿû¿·þáþÁ:rôˆðÎh,lUWWYŸ|ú‰õÑÇZ}ô‘5¿°@;¼¨‹Ç­ê={¬O?ý”®ûˆø½5¿vŒQ;PI:gÁ ;v’ïýÇÇ–?˜K‹Aˆæ¤j=¼‘œå8¼À„³À³àF®¸Á^–¨°ƒ/‘Ç·I*$ì ÆA8œqÇPkGS(áE-Z±XHo/µ\_°$D(`K‹ –éd®Ãˆe!CW©H 9$VX±‡C°$D¨’祊¯°üÈwÑ„|}¾dlP"•²“—Äü$K‘ˆ&ž³t.wIaŽÄˆIók$L•‡èˆ4K—$¨;“Èa¾rÁ²{PÙ‚UZµ'ïo†^;“Íæ€iƒ>XZìLõ¢Jr·«Õs6ÏÄ ,Û]ßc$ˆŒ?KÁ,˜ZL‚³102O§ãÖ³™™UŒÎ¥ÀçãÐ|å:8OòRe'ªŸMg/(´H¹“ÜM,‡HIÒ|ÙßZ°$ÉýáÃÇ`eõ•£Û})î"9ø›tÖ.ØÒ9EÒQe«*P—õäETo«áDÁL9>\Y÷î‚þþ> B„]ñ×Û׫p‰–;Dø&Á’AÍ2úöí[@B†ü8R}yòÔ p`ÿ~°»º ÞË+¯A‘ÞGÛ—Ù_¨m-Íáü¼½íIK–¶Óµ»ì¤öüG+?2Aßq~ßWT›±T2žK‡`<^&¦ÇÎÄ—A(cÅ9>Zí>\ Ú÷mµnÐw†™ánÛ±U*ÜX°T¨]ù¹šá¼RÌ}ÍeËšõ.ÓèÇŸáäø=dZºÿËA~:¸bµ45€úí›ÁÞÍ›@ñc@&ܺÿttõ‚6¨ööv … "Z-7oéÐßÜÜ Ž9 :z†­éè Äôq’+f‚ÃÜGK*ÏG€Ö8¿Ir7}°¤ŠPÝV®7U„&DH·›S‘'Éö:!Þ$»ë‰ #G «yßt_-Ó…]䙎¾Ò…ýÙ“QÐÝ;hõÜÝšÎÞ[šÛ Þ“üçX]=C §{Üù|Üzüt ŒÓóafy|ñµ‡wïYÌêšä@iÎî‘›¢V22tar¹`æ±^:qRV§­ñ‰yðýf°o ôöƒ[÷ÁàÀ0èW Þ¶.ÐŽ“¹5x$¹üžxÂ@ä(B"K(¢ ·Uæß’ªE˜6Ô0âûj¥‰ £UpöY´~K‚å dÀüBx#E°DD)ºÜ6\°¼!¾­'” s>ä0–8®œL¹Ž<H–ý·=PIi0¡Pp¾›Œü±[;(Ñ2MLãYPšƒ¥…YãÎÁ2+X$­ŒäJåIVrœgåÈ iïËIÚ1:&甬¢>#Nçz%KÄËTƒé^Å“ßã±H¦óÀ¬¾éƒj2™‰XŠvå£ýÿuþT6¸Z2Ȫ‘–£*Ð]E¸‘`™Ê¼xÂÎÁr­`I.”¦ä`ÉlB~?¥¡çò²BäO+ƒX"üÑ,˜÷Ñq!FXžˆ§ÓIM<'s ¹Ýó™ITܶ†L€î[_€‹Wn‚s$N@KD©IÑxö,<*÷ÌAS!è¬ ÅJr³Hàù[f®’`Ù­,VKWJ9ߣ|Ô>Îä’J‹¬Ô¯+wÅ©©Dt­”¢}JB#+Ï:gSœ¦ù$xö| twwÕ¶a $ËÝ–ÁÍ›Ë-Z·èwfp@ÑÕÕ ®\¹b?qÈŠ•Œ½¹vý*(®¼y:Ña$_1[pîÿÕßR½)û aõðÑ’ŠÁÂíûŽ©²m Zv¥½‹nù ûú}§Èw+âøÂ<éjƒõ;ܪMGw‚¡ÃU »n‡u²f¼ÿH;YÑš¤}7#2%Â3©Û.'æ¾iJ+­p¹®.ÓÒ ÷™âÙzû¶òÿE&|YëòñC vë&°Ç6p¡±t÷ €¶ö6ÐÑÑ :»:׫À¤ÌåË—Áù ç@mýAp½£Ï<7Yušä7[°¤Mƒ\/ó ÇyÖ¤nÃ` VL°ˆ±`EWß´Æè6£Ó~`·åC·‚®¾[Vëµ› ¥­´uöƒŠ`U«"XÁªVE°þ,‚µ|íF‰\-wvë÷²"XÁú,3´V‡Ìx‚ nåÐÉÈçVú€(F` Ü»ótÜl7¯\µZ𝀾ÎNðù“`rz,,…ÁÌ\LÏÁø\КõDA œ²ãâ1 ÷ŽBÿ(GÂm´S+¯ÎÓ2¦ÿ–Ê?4X°Ôï<Š!oýûûY ¾4˜™ )T½KÅbPÈ‚ùY?( !F<º9hä9JETü1 ¢ÅùY~0}¶Œ”©ÐáF‚…~W’3!£oŒ`©¥~90¹«U~È• ‰`á| ÷–ŠÉI*¬N”AÂéTH(OvÖ™ì2pVÊX›X&Œ4i±³+•€¨13"`údBBJÙH†B@¤¦À½«Ü‚åªê“×eÂŒ.ÁJbسjXjW?–ü¥× tˆ0Lö,øH2ü´=0SKQ0:Ïfâà‹éx6c#ùSî{6^xÁÝ/æÁà½qÐ9ø¹ÕÚó\kR´)n´€æk­ ¬ÊoÁr‹•„÷Œh9Žšq9®*B8i4:00œZtŠ~aeCá’Ï_¤û³¹O2L4S9­¾‹f–&ž±G0™“}B$ù["$¾@ôôô*·`mÔ`ô«4•ªB AJ…  Û–^GÇŽ³öî; ¨©U;w€+/€D,¤’|¹ø }D •‘.“2¢Þ³¢»ù‹f¬–{_c7 V½í^|ª ° Õf^Z‘hŒÜ}$UŒä^ ªƒ‡v¡ÓK×Ù¾ ;xLø2@†?›AÄÇfÊÅFbeò‡BëàÊ/š­ ™ÍÄ^–s‡€3‡êÁ•+Í@z˜uvvh:5]rÕzÜlmQèªAɹ»tñ8ÛÔjkkÁù+-ô¼Š`l!¤Ï•³y$8ÿ ’¥ÂcþrÁâê@fœÇôå!BÅèlÀ!Xê½2}¨ÂEàΙs$TóÑ¢¦L3-ZÒHxbÆÆFÇÁbŒÄ+öLG×4¯5*¬8yeýâýß[sñW`Š<p.Ø´Œ‘1wŸÍŠ`U«"XÁªVE°þ¤‚•1a½þîw\­ÖÖ[ÚU«"XS‚…J°åµ²Ê*»Ë¶]å$ËÅ>o ÷õƒFÚP™«çσgÏÆ€O Ž$@:÷ Äy !Õ"’ĸÎté‹)$YTÄÃ3·¤bOÂ}ÎDñ0ÝŽ ñ¨U+•D!!<,ŸÖ# ~ýÞGÖŒ' &éƒ`æI˜˜9zC™YNfs!×Íñï 3C_F‹CŠ¥`xè`éåDyN˜(®ND…¢Nžõy£ ’,ƒ™9 ‚åîþ.C¥ÝU„ÎÕî17"XòÝIY¯–Á9Î…å"[i’+¦tÔ‹–).̸ˬœÃóy ¤+¹–;Ù^®OFâÏÉd—KÈ%D˜ ¹b–i»`D°8áÜ$é»ÕuÈÎÝKËî¢n÷Ä2Õ{Ë‚”^^¾P,81S‹ Úñ(žMG' 3Æc^81#óŠg3QðÅTÈz0â·O‚¾;Ï@{ßCÐÒu\kWÛ5mÖÍöAEGhéíÝCàÚÐșϖ÷¶Bµ ®ôãy LCc“¢¡QÓTŠ£²Ð-eÒ÷êæÍV%ágVèýdŠÎù¦bUaÄÊœD–Šv¡`×ÍuRM[X)ÁÈ„UÔfÔÔªFm’,‘¶ƒ®®.#X},WLoxShp#ä6ô¸ŒTÏÓ¾™9zô¨Ù·×Ú»·ì«®5;·‚½[6Ö3Ç€fD½~OçMO;™Þ E)Ë3‹Öëù¡‘«—›·X¹tÁl³,g…â+ ,rB/“81žÉqõ:\ ßôŒõ¤ý pÄÖHˆÐ zÖU„},X;7ãõõ`—2þÄ-Vo,[¨6¸Oø ’årlˆ­“[žY7¯6ƒŽŽvÐÕÝl¡*,IlouT ¶\1×o\vhð¿|ïSs½xw·Ÿ˜\fB=ó5s tïXôŽd™„JªÖºÛÚ@у ›>8f~Áä¬C䌛ø…Bi`d@Ï”’~^½b$3šdÎf2iÙñЋd–XHˆ¹ð‡s @¦@#º¢¦`Êz¥)Ýb8¯)x:1G_†K'È£Ü/~û±=€¬š1%»þõá¸ô¤¯ÌåÞ,˜˜™5ȱyúݽ&æ¡çÏH³½Å¥ðxc c¹Ê*©r²ªœi4ñ‡@Éü@Wµ¨äVHÅ\<{:†?ÝÃOA[ß#ëFçp•å‰iT´€ëš–ÅÍÎAÐÚÙoµ³Lm} ¥CÑÞ=zú†À–VpõÚ påÊ5ëÒå+À4ml§OŸMàìÙsàÌ™FpêÔëĉSàÉ#9X·nÝñx ¬¬¾òB®´ËÜH³’%cŽ$ÿMŸL–¬¤:$+_°Woe[XOösu"bË–BFM±ôÊO˜¾Lß$‹éE¡½êô¦£5'•¼›kW¯ƒ¦ÆpèÐ!°wß>ÅÞ=Ö‰ý5àXõ.°wÇfP³Mqqï60p´ ¯oœ··\£C= ¸àµÂ#ãÖËýØ^¹úÕ¯¬Àø¸å[X°ü KÀ7¿hÿ® ,zÁ䃻àá•sŠkgÁÐéZÐ{h·Y±ê;¤‘<² ˜ö úòáÃU€çß¹YAï#² +(R±VÞ"Àù·¾MhÜ·C\écH¿¬Tù2àþÓqÐAŸ=ÓÖÚf¾²2Õeè’s%bÕÖÖªhm5ÛåÍ7»jðÜÙ³àLÃp¤“9|ø¨õœNì˜)oˆÓódLãÏÅ(0Õ„~’žÈ§ßÁBLyâ`6²d¦ç·™É©EkjZ±ÄMe#ì`øÆW€,°,ŽO€H"gf(G99|ù›œ„ñûÃ`ibpÌ Õ¢i`»H¢Ä,Ðs`æÉ~õÞGhÂÌÌxÁ,¿¦ñY Ò'#¶*‚U¬Š`U«"XÁú£+45k­þûoì•+­ÐÏ WÁªÖߤ`IÙ‘ÈÁdeí5ðÓAøDÝpóÊ G€, ËPòR.¬òÁP x½a ¡@yHª'"A2Y|‘䇙¡7Ÿ‘ðž|ÞˆãdJ¹—Q™dÒ/#háJã=.¢Ê‰™'ff<)0:ËÕzqðt: $Ü'U{#s)ðb6¾˜Š€G£>ëÎçs ÿþè| Zºïkí·mƒ%Ühï'!Ttž÷É´v÷ƒ6&¦¥½´vö€öîk`è.xüä9Ÿ‹³3 ÍÕŽ<ÖG#éaB ‹%@<–O<2âãØázÐt¶\¥?ÓÞÑnõ ‘ç£ÀŠ©,-.kD¬JBÏZœ]8{¯mÌÚº˜1a…*Í'!kÀæ2'(R™«·#;ô®®—0Ùâ’Ÿ]@B:]]up•F£½½=[¸DÂä+ÏS§Nºº: áÀ$VÌ¥ú=ÖÀÅ©êí`/7²$ömß ª·«ûw€kû¶K{·Zö(šk¶ÿO+WßùŽÕO"s­¶ÚºR· \­­×ø'ÿ v+ôu—÷í—èqÁÞ-àrÍvÀÏáʾí@ž“Ü$dèn4:@rvb×fpì`=°‡K3KÕki ½”VKd©l°ÜÇÕ4SC]¦Bf3±5` Õèè­--@‘2BÅùU’cÕÑá«6`‚-¾ nܸ¤¡è… @SS8sæ 8|ø0Ø_[g}>3þ4çWŒ,ó:I¾SMÈÇV,]é76’v#a¶…è2t Ùi5?ëöØ$UMÖÄRk@ªv—^<I:þ¹›ÝÚ=îtsaÚ>™P0¼Ó3@5V˜žk±e€Ðc”%pÙúõûŸ˜>l³,WÄœ/nÍLÎÅ%@¦"XÁªVE°*‚U¬?J°¦~ùo%íîoúrU¬Š`ýM –TA‰h-¯¾‘Hœ¬¯µ®œ; d'³¼bÙp×u•(]ÚÄô<`‚å º åKX €SªDŠä²ú ˜úS©§ÃRT¡ÀX€¨±X)f œIŸ¢D° –ê{"ò4X)…®&ƒ+`Œ› ·`ÉÐ97%¦2Q¿.}pÌ"IÕ"’Ü€TË€m« ÇXp·s"M‚õ†:ËÁÆ„µh$¹ßTŒ+Û2 ÈɪZŽDRL• –$}K,Ý Z¨QHxMB„©TäXnP€¡o[t„q´|IÂ}–'d‹ŽêW…ô”’.é%Ý×5R&Ï5ƒeNØåÞ?zDT„„T–¥‚i0µÒý‡ô@ H)©ÜSDÁ“!ðpd Üz8zo?í}ÀM¨ë$OÌ’¦+ºNÅÜìPpXéèÆkíì[—®ÞApûîCðųQ •5~„—ÁÊÊ+`:ÓÇc`yå%°‹eÔgÅ#y–W×ÀËWúüà(È™½[·€Ý›Ým­`e™Ö_‚µµW`ye éq /¼ W’{™°¯WYhdw»ÿ“ê¥pwƒ/)Ñ•rî Dw忞E´“d12 ûÚµ« ½½ ¸…‹“šå€*‰Ë'NœûöíÕÕ{ÀÁ}ŠŽƒU€»žß>¬8Y½HhPh$¹búIX˜ƒ»‡Ûúîsïÿ²D®¦>üµÕÏÝÔîý‡vhvªËëwšß w(ä6ü¿˜CŠ~’#fUêºÁÃU L¬$¹Ý%X,a§voGjkÁ Ú¿3Òµ\’ÍË*Ø¢+.Ér„¥³¸„Mo'ÕGÊ™´~ï‹ ÐÙÝ ZZÚ€HRWW'èìêVtÚÉìr]Ggp‹•Àbå”+¦¬ï•.x°}CCñÛO¦À\0 &–’ RÆ„¹Œdw¾n†^?3ÉIòüÚgC`z1 æIV˜Å˜"X‹ Â$œË®)áJR&NBÅ,<{¸¢T¡¦ù„}é Y:f0:Ycü3³€¯“â”Hj¸bŸ&VA€øíï>µÂÉU žkÀò’ÖÂì"¦VA õ|Mv ²ƒL§3àÜ©“àüɦ"Ï%d²¹5C°0PåÈAþñÓ¸.NÓÎ:‚;%§^ ò#-3º;Û$NŒäY™|+ν╬Žö/]±r®\‰`É÷Er¯.]ºä»bçEž"XUUÕ´zæ¸rž˜ò¦Á$½&fJÏç›ZŠ)H®·%"ibfƒ0=ç KAà'qñC` @Æë¼t'’\™ËÇ/ÚV@N‘ν©t,<}2¨"×'Ö²Â,‹R ÌUéD$¾©)€Ö+z¦²,FÄ9g9cÏõeézÿƒOÌßþ¥†“–wnÄ3+ z~Ì%XÍtò­o}»"XÁªVE°*‚õW&XOv|d½ú¶-W‘û±5thgE°*‚U,#XR™¥Çß\»xœ:X"á˜U\}L>}ð~“X½õÖ[Öø#k˜vð¡`ÂòqxãmV€,=C,„ @¦‰ÏÖ™ZÎ!5nr6“¾œÂ_(jÚ¯¡’ëD œá<†K~g1z÷½O¬Qo¼˜ ³l¬q •’*AÅ(ýOF¦‰Ëÿ—ætöDv[(E %÷ÌVT±ä‹_ ¸"Ér•.–…)äK#¹"rP…£ ïm᯹špœ>È:söì!È®,ݼӮÌ[%¹PºÏš4ÚL‘¥ X’K¥JS\Gï*‘ù?ÅåW@ru$+A-&çF°i CŽGfâÀî-åU|<è˜o’GõhÔ î~>úŽG µû.¸ÖqÛ4é¼Þ.(ºÉ•{,OZš$Ìw³½´ð϶ÐÑÕº{AÿÀððÑ3061 |þHÐk—ÞBâÍ‘8å O*ì''W¦â1œgg‡~õ( ¬pÈõîp c‘tV-aÅ|ôÞ¼ªHª˜=ÛGì‘HÌZ¡Ï‘QL„£_–K’+ÅA¥1èæ¡®·[žþ±’´[°£Ê(1#Tfrél>wØ7õ}ìíOõ‡ŠÆ²`t| ´¶¶[´ÚKÆLGgÛ\¼pœ>~®=nÖWY·*N’\1{y˜ðv;Dذg¸wlè!Ùa†vm²Ö¾ó#W‰~߬ãán«¥vè?Xäテ»@kýNâÅÜ¬Ý úÜeÝ8 ûÞ¤û1ý¨¬².ï߆0¡B…E´ìjB…]E¨è£û4ìÞ ŽÒëg¤1¤4—´…JƸhyªy¦-Vê6³ÑW ÝÌòó±yÐÝ7XtLˆWçQ™V%¡@<ê8ÄJcª×ɹrç]IhÇ"1’{å,ÉÁ:vì(`ÁºÉû b>˜S¾4°Ÿô©ššƒÙÅ(˜#¡’J¿SŽ ¥øò@KþžJXO°ä1ƹ ”†÷”`©ßYÞ}ïS#X"”¤>‘¨r±Z+•,bÔW/èþÌ $®œ¡ÂY/ K’ûå=òÅòŠ`„è‹Ëðûœ"™eä3’/§ôà‘wŠäŠI83~lºUëð‹©îÓhY‘äs åå ãeÝuxHÄ'Ó[ªè踭ËÝKüî“jÕ²ý¥¯W<–ÒmÁŸ“‹ 0JRĨäsˆ>«( ïi±’$ôÏ—À­Gœ€þ ´õ=7:ï‚«m·4C@FËÈH™ƒV[×.è­]} t.½¦Ú5]=àÖ݇ÖÓç`vÞ Bá8ˆG¢@Þ;©ì-°db•$ý½ÿŒ"-¹¥/˜H+ £>U-¹¡`‰D™ÁØjPÊÙµ_?Æ2‡þˆ§÷ï‚}[7©\Û¿}+˜·VÖ,`‹“ê­÷¥"ä}c‹Vio?·€}µÇ’¯Œ…Q‰ë%åsÖ±³V¡ÁÒáåNÜ#zrùW`yõ5˜œœÒûŠE÷ôt9°Jó‰“'@]]=Ø»w/¨ÙS ZH††î'I¢˜½Û¶€=n¶ÎìÙî¯}$CÉý‹‘+^ÅØý©ÕBÒÔ{p'h©SôÑã3­t¦—äŠQ¿ï"T’Ì~®c$œØR» ©žàêAIZ—ävwHÐtxï%¡kªÚD°Æ¹Ï!1Ã=© X*4(á¾I)*„•P‰ŒI«‘Ù¸ýˆ‹©õ®uqwa·L]ŽP KÑÊÝÙ9<ØÖÞ$VîР$·‹`;w¸C„<>‰aÁºpù:˜õ%ÀŒ/¦ü0YÓ$WÌœ'xK0¹¢Hä€ßaH83¦-¯ˆH®¼>àÞÆLU»–¨L6|c#€§pØûI(ÝŽå˜ orJ*ˆu‘—Þ^Ó\Ô¢‡›ðѧf zý ÂSj<^L€TóÏ™ƒ{vƒ¡¾^ ³§²¹ò 炌_™÷„¬ß¾÷Ö׿þõuÃ…>ÜZôDÀaìv E0£WqœB4É+=¼âC—=Ö`l:&ýE`¤EV‡RfV¶ôX;ª[°ÊW°Ææ"àËkÕÄç…1ÿ2Á’ñ“Á",´jÐ+tó–*JieŠA0œQ &©…7ÙX°äË"‚•¤ÏœÉ—ˆ‹BV6ä ·ªÉ’T12oOÍ¢Tù5R6¿LuF./ä ‰©Ì+©<ÔÈ#«NfF¡n 3'}ÜÈÍ›Üþ€y6² eV£X˜´8½Ð¿Ëx™Çc~pï‹0ø` HŽTKÏ}p½ã6¸ŠY|RÁ7nhZèL¯9AÚ!Ø™âü¨žAÐ7p|öð xöô˜›÷€` Ò¹"ÉË+ ¥ý"G"KòžIî3gH+ i¾ênM rlW‹fmfœ®´t†ÎQA"Vf$ÎÇ[š‡«w‚’+fÏ&ÅpŸµ¼öd Eàνz“TÙU{²ƒ–3Òu¤È-\^çª&”Çr>‡²†¸ 9ã6;l-SîýiªdûÕM›õJ–?CCÃ@ÞÍ—.Z§Nž‡µj´eس·ÔÕì—ëCGªL։ꭠ†äªFKÓP½Ü=^ ÂïüÈÈ'·?Úþ±(Y•²« ÈßNÁ’ªÀô;#%‚%Š-v©b¼¶o‡5@Ï›±Wª”h¹W²Ìåt¦‡þß¹ê-@r°Æ<)0]S‘‚F­ZI»i+É­âÙsÌíÇ£ •å‡h!©b¤ñgWW·¦ËTš*AÓ4´ˆ`É};Ú;ò­:mím µµES*X"U²jÅH[ɽù6+X àÔéSàèÑ£€¿7'¸á/1»KXeÁb|,,†Á’?||²«GáEy6'ò…@”ö?ŒiÊ+Ç']Ý¥ý]˜¤…ÙèDȸ3¾ñQÀ-vd1À½2mŸø«c\Œä*æ¬|ɉ˜n·R,…›øñ§æú/¢Üqg#i^côvü'¬ß}øÑó±*‚U¬Š`U«"Xÿµ+ð›wKÚ1<ÛüÕ[¿«"XÁªÖF‚ÕríªÅœ:|Äâ`š†¢*­¨Ð±Ç8OˆËàí·¿a5»„¡Ãœ]/ »xˆ³‡ÞpfÁÒˆk'[óš–¬PшÇ$ò¡D€tXO7ø´ó™t?*–’ü§’,…sUZÝ7nX¥!B%X/0=;¤G×ãÁ`D¢ sŸXB×pî‹'A˜cë„HTÁ‘ë%c}ü¡$˜óÆÁÔRŒÌÆÁÓé„&êï)qÍ€\ÑG<Œ€‡/¼àÎç3 ÿî «cà1¸Ùs\븮s~qƒt¶ šAÇRÁÇÕÖ=¨àß‘+UÞëèRtv÷ƒ[÷ÁgYãsÀŠ©ü”!¸擪Æ$½Lª$AåÔÉßI&ÎÃRr Ö9“1Ú±1Iú¼·Èß ú\T»*íäóK,3bv";Enþj÷ªÒüuh9Aßæü‰#`Ï–M`ïfEsS£Û¤{ºs lÒ¹Žžôî’ÛØì7 æ× +–æ^m(XFÞè=.(Êò¶ŒX­”ä[9EË-]vå¡úl¦¦€ »®%9`jjjÌm÷ø“#G€ý{÷‚$;ŒÈÊðA»ÂîTõ6 b%¹XÒhtî÷ï—ÈÕøû¿²…é€Ât –üÍ×IÕàMºœ 1cnÒã0FÀè:¦ž/ƒ!z_UÙ9W‡w‚²ªB=:çÉÓEÏUëx]£ý 3]ÓòâÐWlÌ'^*è˜õÅØèêé-­7AGG°{X©!ÛîÜ8ìt¡ncÆÝhLÞU‡ÛÀ›ª׫d±’Ü«¯,XGŽ‚šš}V]}=˜›ž±x„c ®^gþÓ¾›I$óæ{›J倌f“Ô»êW63âC·óùl·F”\£­¤‰µr8Ë?ì¬x(üS“ ¿N²D®O˜x{üð£OÌvöù@,#¹òy#ù–2ÁŠp©¥._¬VE°*‚U¬Š`ýuÖЇ¥½®¼¿ü™Õê\‘ªVE°*‚µ¾`IµˆŒ¤ÞC’¬ÅíçÜg‰ßÌTD“…Ùüæ·ÿîÖ,YL8¦òò¡«Ù…jÁ¹P3A…ôšš\G`D¨D°ÌˆšõK#áC¹-QË }=+°bþŸ3É}l>̈W¡;d¨.+,I”—û–Uê÷A¡FJr»?VèØN„ü1`‹&‚éXK;k =®´`É^’£}þ 54tœ»p œ¢5#CyÏ46Ù(ÏŸ¿ ¹d]¼XÊ…KŠK—š5W@3]®\wîn¤SJ’ÏŸÏ$Á³™™‹ƒsI02µ¾˜ûÏ–ÀÐÃiÐ{ç9hë}®wÝ&ù5nt(Ì(™®!`’Ð¥šOWðµ¶÷˜aÇ]½C`øÎgà‹ç`fz x}ÀtΑp^\Q#°þlDÌÍÚ] «~°^wýN`þ¦ÿÍÜ<°Óê9¸Ü8 ööß;ÁÍýŠ’#æò¾mÖ½A¢{vT –TjѺE¯é¤Ç¿°g Á÷¤ÀltM³â«`l>hÿÈÓIDï¢í”„]båBÊ ªÑ8í.œÚm±º Üb%aA,þˆt_¼xl$X'OÇû÷ï·öTWñgOA†OàÒy³OËò”B*òÑ$à"‘’t: "H†Ã L€4\i%iadd›<–»R]ÒB3S€Ë>!TÕíî‰ F°è90éI€}Dq­}âëuÁÒñû@p÷‹90øÙ(èD~Ôgà:¯h"Á™i€ýX™`­é6F«ff/ –\n^S,iE@ŽÇµáy¨Çø“Ö—us¯VE°*‚U¬Š`ý׬úÍŸXñïþ³‘«âwÿÉú¬v»YµªVE°*‚õk¨ÀbäA$'¹Š#$H‚•Ö$"V!Í— Ö¬'n†<Ά À-XÎÜ«I]Éç§1ºóbÒ&‚EÅ:Â3IB6YÒ—j¹”’iè"X«æoUE¨kl! ¤7Šê¹:ÇJþFI®F‚5TÌ’\ÍŠ`…‹@*-}oÙ»ìç^X‡ ‡€TÚ£/ìsìŒ;G*ç :s°Ök0j‡ ݦ·å2& ËwävˆÐÎÏ*m0ºžT•^ö¦JCäléê«x*gõô©,¼~ý8î8|ä08JrŘñ4ܤ“ClGªJr°¸7Yàûß3rµòÿc=Ú» Õ†GwƒáÃöx©8\¥0š5r½Î³b¤q¨ }¯5æo3Gý/Têë¤jQª%·Ln+áOÉ3c9»¸g8Y_æƒð|rH~•ÈŒ Vî„PuMW·æ?!W_E¬Ür%bµQÕ [°¸ïÕÆ!Â& ‚u’äŠ9rø8P[kí$¹bîݾÌØ3ÝÜÙTÓ¶Î$ îŸ'$y¥±@¤¢1`çg*¤êœCƒ¯˜m×ôeTÿO*¨Ý!BN;È_) k¥Ècè> zŒ-Xå!Béé ~ôñï1"Žá¦¨L<·¢‹‹@$T^ןD°‚?X˜@¬xõ…åª"XÁªVE°*‚õ—/X,Wã?þ±‘+οêùäÓΡ"XÁªÖ X½t é0YÿIÚÑ2äê.9 HR»V0–_¾‚• ¹Š9’ F’Ú%É[Ëît¾R&Ec<0‚¥»¡›äó° ï•„ìD†dÌMxLÔ}Y°ìÇQ‚5Ærå¬È2( û±Ü²'R8>3ް`ib{Ñîܮlj`Eè ÈH÷Û(ÉcX9{¨³c¤@©çæ½ ««H(ïä)GÈÏ…È“VCChl< ˆ3g„OòcRí$beÂŒgNƒ“tÙ…+m@úLÝà‡ö:4í µktt+ÚU|í½ EÓÎ!?¢«w ß¾ž<3s^+J ™Ñ úà'‘bL˜M‡Í#¡¸©ÝˆP4‚ËQõ™(*ôe±äJ ¡pJÍ‚pD“´ïM¯‚é¹ññãÇ€˜ŠÞ±ÎŸ7 B‘ ªTyì8É¢çÊ„Iö¤°Â <%ñô^D•R *ûïlÍò ƒŒ“t=H+.:j¶l{6o±._8 ÜÃ[Ý"â­ìFUëT®ÛSËT –v_·«ßÐCË„/%Tèzn®~XöÉÐJùˆ+Ýû­¼Ó{éø.l¹sç.hmi7nÜ—è€ÊðIS[³H•_¿#|&£r¾ûo%ín}ðžuzÏëæÝàêþí yïëâ¾mŠº*p©Vs@£ÿ¾¨1—;®sß§¹V³7¸P½4ïÛ®ïßAÿ§â€bàð.`äìðp›ä‹$!dÚX°tˆðTýpç³GÀ%©RØ ±R‰ëÝÝÝ@ÄI„ë ~Áz“X}YrûW¬¦¦&Ðpæ Á:|ø0à Õ$WLoO/>ˆ°±`Ù”¯2•K‘[°˜Ÿ¯+Xªù\ù –z¬‰uKZKl$Xf‹˜åÁbXá@(¡U’0¼˜h<Ò¨ Ô‚eG‰Õú¹ZY:<þü)pˆ/Ñ•‘mðÀþýàÒþ]`ÀÑœóTõëÞû¿)‘«;¿ü…ud÷ÐBBÃtÝîݸl=»x–üŠÅX\ô‚%Íâz,xÀ‚W1ïQ,øÀØÈ8¸ÓÖzšŽ‚ë¶[휳UgÓq7<\̬B=¨õÀër]5h¾xÈŒ@3ªF*»{AwwƱbõ%+WˆP}ÙÊ•û3ýª¹WŒS°dDN™`éJSÙˆ`ÕÕÖY;wíÍ—›45Î\1&ÏŠÛÄp»½’•IgLN•-XAà,³‚¥[ÅyTŽK°Üâ#-d,×ÒÈSÆø¶ÒÕ.w%³˜TÑK›®N´+u®WAá-%Xk@7sSæðÂÑpRÁ’>MK‘<Aض«Æê|P¬Š`U«"XÁú ¬¡ß½g½r´cxþÎO­ší[+‚U¬Š`ý1‚ˆ¤,&–YQ+èÐB;U: Ó–1¹UÅb¨æ5³<¨<«ûÏf­Ÿ¾ó®51¶Æç2q|Šcر™{U†TüqÏ+î}5nI’Q6gó¦<)§héžë –/«på`¹+KpUŽñhf6¦¹÷Õ:‚É æÁ<½‡Œˆ«m,µø@ÏDcPÚËH7ÕK¢KžèìêsÑQ‚¤Ê?å,SUxÚ!Jú2Éß:vì8\+gÅ!#áÆ“'NÑF{´wõÉ›ê„L Xƒ·??ãSóÀŒ™œ²H( 2™"¼S˜ñX `8¹]EI¦Bp$#Ñ´Â&ÜÅÒEü¶$ùIX@(­g€4Ö•áæºŒñ,…ÌïHøCà£û3‹Ka…7¦ û0žPÊòÒÿbDÎ&¦½ ã4:¬ãôž3›¯#|aúÒ`aÞ<¾ðÑuŠ,çµ´^~Xê¢y#aÁHFˆ“Ç¥ó{&gyœD$’á¨BNêž>úÔnÛ önÝ„ƒ;366äsuŸ@¸s²ÞtåM¹r¤²ùõ$I5œïc~ùµBò_ôAÌ®èd‰}™„M%Ÿæ&¹Ü3óF™ì…“àî•‹VOí6 ý­D¬$T8t¸Hþ–XÛ‘ýVëÕf "%>"UŠ”ËÌÈ› DëO•så¬Bƒ"Vë –4uyÁ:sæ ªï#GŽ€úº:«ªºHÃڠϲé4ñá’,xLÚÊš¬X0Ò±8(e£û`%Kú`IÖª -X:·ðü ÀÿÇÝhÔc¥ñ“>X¾ÉqPàP¤ §×ý¶lÁRðóT‚¥ÂŸ\ñÈ$éõÄ<^ ÍOåÿÿYk†dá­·ÞªVE°*‚U¬Š`ý ÖƒýÛ¬•úG#W‘ï}×:¸åSÈUE°*‚U¬?R°Â$SŒôZ Ä ¤a¡ r/&ÂC{F’±ç‚E`5kÐÏ*P°Žœ¾¨k–‡%‡ˆ…ÜÖ$·›P í­æ§Û3£s!`'›— VyøN…7.TÊß$\?ÿÓ2Á’Ç*,ªTÏÅî§…aŒÎ@Yˆ0h#‚%ƒ°%D(bk'L«ƒ[„d€¹J9vв³›˜œÍÍW€]ØhB|vˆP‡ ¥U–¸¾NbüQ:È3RUè®P”¥h–°k7ÚÁ#`|rx–‚@ÂN’ÜŸáq2DÚn‘ÞN2â)™Yr_ydŒC {…b –‰'d[ð³ ºÌ./ߎå$¤Ñ’"Oü@¤…Ã…ò»Hšü-x¼… ¥ÅðIÀ"yŒÁ8É#;æÇó¯='f‘‡†û3`f>æ}I°ÄÓp»,ðGr`‰$ø¢ô¼rÀ'S`‰^ä×ÃÄr -`ÞE¿‰çÇ„B I#ßY %ß» ìÝÆÝ7Á¾>P 8óea7§,åJ:¼Û2æî5åÄî;¥ÂÆR i£«øÜ8îë–$·´Ùa?=è áD†t…DÍ}é÷…E°Ô*QûÚÕkàì¹³àÐáCàpÍp»v»•ÿÁwíF¢$ZG6¢ÆäìØ¢`á%ºÎÜÝþE!&ÔézÎök*–Ái ÎÔy¥›©¢Ôß‘\ñ5˜xpßê«ßì‘8jèó0÷¾âjǃU@®ï¡çÍt¶ÞtŒ¯)¾l„ª§Sa«ÇAw ¶¤}y"»;$ø§ ~™`qhÐ="Ç-X"”bö\_W1LLõž=àų砘ÏH ‘I$@*¤b¨Dôl¦¤ŠP†Ä»¥Ić{d•õÁÒ·)û[ËŒgt°`±7'S‚ªñKDÂ@Æì” –z]E‡d!X+$XŸX++«@+OXI¿” –—wþ¼sw!%ÎÝ 4³RÌÓ$Ì›Üóþ‡Öø\Ä'AadÖ“YÁ’Æ›®Õ¨‰’vZ’Hê˜Q5f ;—Ks L»uHXyNVy–ù__"XîÕ1û”ÿ·`MÑógd%ËŒÊaÁÒãƒdœ+ #E3*Ǭ$ú£@ÎêìJ.{^¤¬ŒMÌ‚Ó,R„ˆäS•äT™Ü*iËàȵ:Ó¸n£Qù]„Jëm¬Ìñã'43=ñxðø)‰Bdò/A,–!&‘^öJHÄSÓ6 p, ¸-#Uo¡H ƒ €<"©‚uL`¼¼jÈR²~ºŒ1òD—yY| ‘ÙN? ‘‹0òUr°dKKDW®Œ`Ñ÷›òwá¹KW‹䊘'¹b¦éûÇÌ’\1r›¥°B$ÊÃÏˣן>z~Œ—äŠY$¹ZtH£ŸWõ½¢ç¥×.b%¹›!úL|ouƒÓh< .Ÿ:ölÞdUk.ÓÁ€1+=_2v&³^~”YÚ@¦¤Ez¹l;²EKZ$”ŠViÕnéÿ)Ï›rW@º« ËsÊʬ–æ‚ñãDb)ÐÓÓD°n\¿Îsî qŒOlˆÚ½5Výž½Vô§?*éuu}ËÇfTξí[ÀÞ-Š'Ã@ª°2Ž÷×^),*ô($wþš}â˜ÛhÞ í„ÊÈU{Œ“Xn }ëÖéZ0pp'0£qíh±Ûê>wHž•s•©L¨zºF ”Tu«TB'øÏÖ²rõ‡æ^9Ë]=(‚ÕØØÏ[°ŽC‡a\³s×NÐOß-¦HŸ1#"’$QaÒ$ •Tüe³à,w£QÉÁbÁŠ‘¨0¦5Škª¹¯^Áò%Xº:QV©u•¢¬zIµ¢<ÿÄ€PéÖî•,›Uëc¬`­©ŒdÁJÐóex\š‰¨ãÏ&Xo½õuëW¿ýÀzølöKëhãeë'ÿöKÜçíoüëý·XŸMt§õekKÕ~\öÛ>žÍ@°¸Ö–ÝúºÍ„•ô¸¯û€®›ŽT«"XÁªÖß¼`M½ó3#W¯¾ý-«ó“ß•Ì"¬VE°*‚õ'¬½^ itÛ<jèð<çÓB@( ÷‰4IÈl*L?b OHÎÓd¸ø6?}÷WÖþÏpŸ1zü}GÏX?ø×ŸBZöm°ž¾@÷ayÉ[u'ÎZ¿ßº ‚u@®ÓUwꜵiW5A·•ë&ø9ðu'Î[›vîÛ°šP –ý·ÊÁÊ‘Cyînœ ¯窛 QêÛÁš yD¬œÈ!,ÉuóEÁhøè`ÍH91"#ÒhrnÁ¸±'#ᾦ¦s ‘hÐ4jššÎ‚ÆÆs@DLË„ÿ°QJèOõÚ‘ TD«±á,èKHf×®‹#ßOUžùù€"ÀáeAcšTÞ”T¦É$wé%ƒÈMsM: 3òØyE51…H„'˜Kž(Ê=ò¤2›ÙbUŠÈ˜—îÏx“&É)¤ŠdŠñ’ o@ð" ï6óbÊZÛÚÁ‰'À…æëÀΓ̛áì\Ø!Â<ð†"ô²H2¸Éÿ 2’{æñE‘EÝOË·4¡XÉ댄S ÝPU$˜Çù0ý7¯«=[2”Wò·6 å9q‡è6 û‰L±°+ìŠGé‘V.XTmF eã©´h™‘Ueý°ÜØ¡ŒòFªÇÀÙl¾t,„׆‡o÷Aº¹ù28uò¸÷›Òv “›Þ·NVo5Û¶h”hÕðè"bzz,¯Z Srð’FŒ*ŒR^¡)MZåúå G‰x¹«FE´’ôÙ<¼zô×oC$UÌÀѽ ³¹ˆÜØC˜;Ër«JC€6=t›Ü®¨P¢ êÍ]]àËò­Þ”sõe¡A‘ª¯Ò÷Ê™{%„ ynjT44œ§¹I´#‡ö ÖÀ®Ý»ÀÙÆF ÍC‹Ë« L<²™e#X™lD 2æÎ‘r†¥Ñ¨4µ«K…GË;>²Ž¡ùnŠÄ™jÅ—  ßøàjÄ/,Ó‹n³ŒÁÔê1œ‚•#¹Êý9«•éGï¼k½ýö7ÿÞ:øà+ Ö~׫CÅçÜ-úû{?ü±õp*`ë³qõÿ~ó[¬ïýðGÖÃI¿y¬Ïè÷o~ëÿàþrÖgãA\W¬Š`U«"X«‚5¼m[I;Ïû¿°n­ªVE°*‚õç,®ôS¨DuI¸žq ƒ˜e@±=¢F…Å®õÜÛ0ëZÇ 5¶³Æ4!Á7ô´Êñfç°u‘!鵡`­–U1ºõ˪%% —³«#B‰ö#ÁŒü™˜ö' ?''$]¨Â¬,` È׉ðúâ`ÚÖ}ÏÛm*¨ßÁÕ|æ±’0I¦=Š4áE–Þ› ÄçYùÑC‹Ã“úþ4&œÉ‚¥%-@?Sh‘úKQÕ#‰O¸ÀÖÑ&páâ¢^`Ô<8kõû¢F©DLÞÁ¡;†ÃlQ ¤Ùr._*·¸ùj4f¤–&–ʃ°M°Lÿ6)W š©ôà0j8—Nµ›?»6oÏœfl—~N¦×–,{)‡ K¸ŠCƒåËäb9ÂŒiSu¸XD6_(}£r°¸‡UiΕ}ès¹Ð ‡ÊX<($‡°œC¸ ò²%)WÄó‡ÀÕ«W@[{+z?e~ð#W‹ßþ¶8»güì»êEÇþàXí ~ë&À¯¢°¼ ì'.nêhŽr¸g¡üÉÉ.XÎF“æy:N¦ö&ÄòòºðL¹@=G‰•c¥pö«âͽ’[šÇ­­`øúU0~åpK1!¦¾<FΟO¥¬Ü¹rôH"¸Š°]Þ¨wÕÞÖ!Zo´)Þ0çÊüª¡AîõUC„<ô™š²`544€/¾øta„PX’ï‚¥)—Lƒ¹¹y“§DáB‚FàÙX pHßO,X”Ÿø –37Ê)Xa)WDÖ"´Â}ËŃœ «€e0<5 XŽÐ¶³Ñ(]¦K=V•“I¤DÌçeÑ{+‚…á/~#n MÁxî±…7¬KÝ÷ŠV­ì‚µçÈ)±¯ùŒ¬}M§Å§Ÿ}ÁÚKÛŽ¶˜ëÐ~›¶í„ììámúvöÊŸ7m«¯ VU°ª‚U¬ÿ+«½õºˆ}ø¡‘«Õo~St56B®ª‚U¬ª`ýÖ„< MD¬ “Ñr€c©P2qµw°b’ûÕ®Û¬1)WcE‚¥à|©rŒ£kúbI¬Á§£âÕ¤¡µ-Ôëê«Öw·9®¯÷+g,ÇÊ™]°øç)ù¸~úËOm!B%X“Ô¿+V°]wY³Tòš8‹’òG7¬É"‘U½²\!Woš‚„™„Ÿ&Ì+Byà‘âEp5(z‘Iš›O®¡~W'Mái`%µwnçÎ|]*¾Œ…C†ŸŽîåÅ!ºY)ƒ„OKÕLÔníM*¤\yí!B–G}]Uñ¦ðIq!8Ae…·}þ8àÄu’=®È+–=y_”à.á>X¼}6’4f†«è¸czH…2ÀÜŸî?£Ã“öd}Ušû‘rI¨s;AIù~U©ÇCž£$$’qOðÁœëËKWA*·HœJ+ž¥‚¥ª:yuÀOç—'ˆóx¡Š‚¥Cˆá@ÄV•§n7Ë(â9’ÒED“s I}Ì$w{oš>X{¶nGöì³ÁHâñ.•,+á½xT3œY”Ün* \Eèì{eõÎÒr¥†gmbe%»—,§$©Ðߊ®&äþ=œ@νtXNcoW̰ã¸zùðÿö£¢v wåIòœ<ék> hðó…½;À±ºm€Æêæ.++ØC0ÖÉ«xp¯UyUÐX}„œ˜¾œïHzæý Ëk KŠÞž>ÐÞÞL’yW7`¡êïì/n´‚”‘ÂÙ³`õt Xk9 VO+ÖOž«'ŽƒÅcÍ -?[Á#Š ÍàqEï¥Ë CÊÁ¡Ê6ù؈ֶ뢭õà$÷R~‰ª_"|Áry~SÁB7÷}û÷ÃÚ¾};àqk,OÙx ¤ãqÏåM«êX°8!Þ)>Öå°ˆùg –3ùÜ™ìÎ’d›J¢Î_ œ‚µ0_(³¯ó6T,~oòh ´M°¸–öÌ=«œ]Ñ'Ê •^©qæÉŸ)¼÷îû?A{âÝ÷*nô?ã3iÀ‚劭€òr²1œäÎM§äýN霩I§4éËœyÂ&w,x,XS1%W´JõÓ7ÈÁbY,º¯X1%‚_ÖcUò:±U²`é8gsÒñé¸b&¦Âò6B‹`"4&ƒs`*˜®à<8á*Ø¿?8n›5È9WÜ–Á)VŒ}® 5¿°8'«³÷6ðIA$Üò¹,K^ä•éæµº-…{6œ+Xœ{Æ£š¶<¦ ðÊçʳ"ôªX0¶X„¦}á’Õ'ÞÆÌÈ÷ 1-¯OpóR•ÿ¥WÎôýÍ’TŬßg¤\³ÁŒBJ4AÏ—k8¹ÆÜAÀ+XœCñåÅ+€Wè5àŠË€?"ô:Q5¯MÐ*‚ç†Q@Í@yJ–`%€µMå7™íR°,Y )OÏÐ4ãŒRó %ÅŒ™»·m{¿Ø ¸ñå«W“ “_¦E‰ÉÁ*m»`U V*H•­C6ïÀ6§ˆ7ÈÁâÕ¨¹ ØWp ¶ª)urQ‚U:ÃmÅœ.´€Kçω—[·ÉÕp}½¸pñ8IŸu Í›kÞ]ŽÖ}Œ`é•,¬µU¬û^2b•×8+øºÐÂjY²: ±*´bEÄIp³ï¦YâÆŸœ7u§Cá¾~ ÌKé Ö¤hÒ4Äú¹3@ÚXoQˆÓ§§N‚õS'€<‚UyŒ›“RJÄŽÓ‚—ûÁ¦# ãüYÐ΄í¦Ú÷ÆÖ"ÖPÜ΀IDATÞD°X¬œ£qœEßD°*Êq ­pò9‚‹s°¸}ƒßùdd¤\ÙLF,IY"æò l2 œ£røKB<‘0‚å|9+8>(÷‹[GTlÊ9Xºâ149ÑhtIÁïM³b¦p V:šPØK眽uÁRÉጒ•ª`U«*XUÁª Ö` mÛVÔŽÁµy³þ\¬ª`Uë¿T°6«J‚Åi¹ŒPÞû?ýy‘`‘¸üèƒ_`[óé‹*ĦkJ‹— Çi6”+]q7ê ®ÈsJ ßÇ”]~ ZŠ´à)©R‚…ð”·äg¿üTŒËåxÁ*¹­ ËTºÃ€C„V8’)aœŠ.«’S÷(£áÐ÷ÏfÁTxÉcšÀ†òÀ-ÿÆDÇÍ{`ß¾ý@ÍQK¾,X§N¶€¥`Îáýøw{>–i8Út\¸| LúRÀí ƒª†¤Ü¨¨ú›Ö°`¹¥dBd±â|2 ý±Hù©o†ç‹Uœ„A&º9+UàñíÑÑykÓ¡ðNGçy±<Ùó²¬ð¢j4:KûÑ㥜8ÊÓ‚5ÎÏtÔ‡>Ìì•ûÓR. ßt  ªÞ‡C®#ò Á‚ÅÈóR®ÖŽQ@fà´®"Œä€©Ô!DÎo c€ÂyEùI™E«ŠP‡ÆÛ£pICÏx< bñ 0¡Cj"Kè*Ã`()Žîm»åIžàa_o?ÈäW€Ç£Q=±´4i¡ræQ%+†ËTÎs  &ŠËWüÍaK‹ åСS¬ì‚ÅbÅpñĉðr"<òñÓò±„u/ݱÞ%åJ‘\5é“Ûˆ™˜ÂÞQª¿–?ÜI=’*€(ºÚ/Š °%`ÐØîf‰’­é`xèï$±’Üuøo6fÆQ÷|‚{[qR½?”Ü9ž»¨{|“DϲÄÂÅ·ïõÅœì/¥Œ@¿3)~Ëà« ?¨$Xüx€N´Ÿ gÃÃÃ+r2|8š6}°¸S;-âNîV•žº®Ù>2Iæ,AÔW DÓ€»Ã‡¢YÀ2Lb|]ž îƒU'?ŸÄyyR bÉ9ÀRh… K«¹S| ²e®2ãpJ†G;1½ªt'w-XHP7ƒ™¹;9AÖ£bæ—ŠÑ‚5gÃ)V¦'†Gbbý}=¨ù{ßbçN!Ï”BìÝ+Ä»ïªË¿ùM1÷Î;âæÕ+¢£Ãªf»rù 8{ö éîÝ»À®í[AÝæÏÀñƒ@2™ܵ›+¸ŠÓBåL`v†ýæ7G²´´ 2¹9pçÎ=À!µö.ªlQ)ÄÚ…‹`]Ê!4üûÚE…øRrþKÍy°&ßcÄú9‡×NŸë§O„ åñŽ(Èã‘njþÃûÀ0¼o¸ÓÐZ÷îWÏœ&ÉýÆ5ð‡,ªü}C„ÜÍÝÞÑë‹mŸƒ×ÛÀ “$O'¹ëq7V¥a 8ÃÉ,X•3ë%‚e>ª„köås@÷Ë‚UîcÑÒÛ9!ŸE+ÁZÖT®d…`éžp&É]>Ÿt0JËZIY VqÞÔ_þåÿ!°DG owÅU…à¨×,WbT«r’¥gÁšŽ§`É›¬‚~ŒJ°œ2Ä¿#+n]fÏÁŸI‚Jmørº¾…ÞWç±Z‚¥ïË<¾Òço„Kß–K3!e°`Ùöå^d,iœ¿å–F MÁáÃMàˆü0±`™•+-TÇH¢lŒæ² ‹Û6\ïìcS~àK®—<ñ{"`V"©­dJÊÁ WMãUÛè 3kOÊÁyU,^ÓÁ4ó€çC£`dÜ'™/G½`T^FðˆÇÌ|½xø1ÛjjŽÉâÁ²À-X@ÐbA2‹<±¼F 5²GU úä> Rø¢Š°¬pp%åˉYÀ'O>@ž»x pþ‰£ŸšªêF©h–Ê€0XÒ¯W¨V½h$²JÎ~-x5,ŒnÁ3 $J’Ùé i]aµ¡H~y;·¥à|:j>ú oìù|àÜ ƒ u`6¦R‘*M“Ðy…νÊf™|A‘[t`å]Ul j*‹±WòXK¢ÔJ•Y•rn· –•_²¼!«´rEõé§Bär¢èßâ¢[Tr{êÃM£K®4å“÷yò%NÉë~ù%‹¨«Ùj6}¾l9ÌŠ€†Ÿƒ=G¥¤êÊY)è| •gÄÍÍ-‚ÁÁApVy$Ü á¾Æìå«@4‹E¬_RƾŸ–¯µ/ÏqŽÑ+ZgÏ(¤„)YÄú©“&kY~Έ¼,aÎ÷„YYŠÅÍ,Bþü8ßWVV̾z²ÙœyŸòkåJÿ®ßÏü8ã¯ÀÚ4Ap¾¯é3üÑÇŸ˜û°+)R 0‚¥oãk º¥SÛ€ ‚5Êë¹UÁª VU°ª‚U¬¯#X”s…° ­\¥Ó¢ì?’,½’5 O¬UÁª VU°þ«$¼Vær »½÷“DCcsEAj8Ð,~ðÞU–? ¦âËÀ¹¯SN4XŠt_®qo°pLjX.”œ°p-ÁR6/ÆTÒ}Iüé¯(D˜ÜËðáR‰Z‚eݾ,Ýgl\ !áI,b+èÇTÖm)ñ2¿k9ŸƒÉ`à6è6ÖcsJ/ÿ>2“'šjzŒ¦©'O€r¡Àr2USi¨¡~*Ä™s_‚{OG€›ú^%WGÊ1.eq\K#áÆðñìKx¤XxPq8/ò¹n_ O‚ÁÇ/Àó—“àÕ¨ŒOÍ‚épVxCs`Z¾ ë)HçF™g`nqäÙ숈§s ™^Ö@c£”ȃH$ ¸BÎ.[Ü|•›”†4³þ¸Bʘ‰¿-oË_/'€ëøñcàË‹WA9Áòxƒ€‡j[ýÀ%z^OøüòDdƒ›®ÎÌ&W72ÜlÚ0ä9ÔÊ2l óVùd<ˆŸ?…'Ç=àpÝ Aʱóó-àùóa›_Öh«WWþe¤e N‹çh¾¼`*KÃ}:ÞëФWÊÁ}¾ü¶~^è0¯Y{\~0m,G§z«Û½ºœê9Ü盚JO3ÌZ>§Y<· à˃‘à°#u•ÇràÜáFÀÉ×5›6þÞ>0_XöAÌV˜OuG§ ?Â’¢•"ì#kJˈUqO+s[¦2°²@9û^•íhî ŸÙ‚KR†V· Éë,)No²‚5±SžÔ»»Mbu«”+â²”‚N¼Ç¤(\U¼ãó­àÜ©À9Œ·èäcž'wØ.çs³ºÏ['HNæÞË#À$³k±º×ÑH¬HýŸÒQÌeZ¸Š$K… Y°¸Â“ß×I®¸¢°‡ OžëòI,ÊãÁÉîÁÇ€KÊñj¿âáÞ=¢¿a¸QWÎÔÔ€KòvˆëR²ÀõÀ"4ƒ¯(X+ …yLÎW,ÚËÙ«¾®lýâ À]êãápC„fس£íK õÑ2‚%ÅŒpŠ•S°8Q=‹Ð¤ rшó½h’ÜIÀ×U‚µªáûÐÂeœ«>X<(%E’PÞg@.·Q) Á*åð”ËÚH°ˆæsWÅw¿÷®éâþwþ]4;‹mcR®Èc¡©” UN°¬U®À²â”ˆ¢ëÐ*Y|¹D°¬Ç^(Zù±ª-Áš'd0›¼‚UIbH°x·n÷ÀR8AUTIç¬J¿œ`ñuË ¿®üüøþœéßÝÔAÒÛÐA•¥È´]8®ø*‚ålFÊ‚Å߈ÌxÝÆÁ.[W[»À£—nà燞³d:¡˜æÁ­{OÅýG/ËÜŒÔ+ÆãÏ¡Æò,Îã*¬ !öuÍšF¬+Ö×÷‹tzÄÓK€s±X¤¸å¯`qÌp(iÚ"DR‹€gðE“ Œ`$£ Ÿ%³Ôˆe `¼‚uJ~³&.^¾¢‰±åMù¦ý€›®òåa3K1|žYà—÷M-hTŽYDn'‚80£jt?ª"´FÖèö<‹0–&-‘SЊŸ„šrÕ^ßÕË æ³M€Û5œ—'Â)Såfþ9…jCÁ*™¨Åª$«PD9ÁrV–Îß+³jEf—W,Ÿ=/Ö>üyÙV â_ÿµ4ÁÝžƒõÞ{Øïé6q÷Þs¢¾N-%œÓC«§OŸGä •¨•'}âÜé“€¥pQžxWmm$ìßô—*àlžj­pµàÈÈàö »³„¯_âÒ…Œ5ÍúU|.D©p9V´€^ýâjCná …èjÂõ³)#ë¬RÁZ’Ç7"sô9 Ü€)WÄ“}{ÅÀîÝ }g¸°ch©­çå߀¸L+U’kò9ƒk×Õ*–ä’|.àÒe°QƒÑJ‚Åbõ&+X<³ÒÙ®¡®¾ìØQ8W+Ž•ã›ö‰Ñqç>™÷‘~O˜ R°b³³`n®*} áU©àÄÈe笇泥nŸß¿¼äõ|IÒ‚ål·bnKîóÑGÖ,BK°¬F£Ùܰ­`}}Á²’¿­KRU°ª‚U¬ª`UkÁ’ßÒ C/Äò±V †2¬ûÛb[1|ò‰gŸR¹úâ l/lÞ¹ª VU°ª‚õß(X¦×Ri*–’âÜ çåFÂød/e`l& X°Ê…þŠ)“ œ¬0`™)':N‘«$3*„`Ùr(k"QÈÇOl$G&ç,îCõu‹1‚¥«¹‰è”-li=-T‰b\R®ˆ‡ÏÇÁÛè†+Ò~ŸÜ+¦©IÑ|ô8à!U§¨4lRLQ) Á—GBi)" ]‰7̃P0ÂR®@(âéy3Þ%•[îé 0‚uê$¸|õ:à&žTUÇÃý3`nWß/Ý>î#5¾G­Q8iŒ¼)˜ÞQ‰Xd©JOÂB”É̃D(b¤„·%“YMð˜™4õ¢¢¦ŸñÈÒUÜžÜêõ›7º-ŠC»êA$’ ‹kÀ.VNéª4`Ùd2}®}¯Jk”ƒS¦ÿ“¤êѱ²ÿ€Xç{•¥ê»ÿ†¦¢…ž›b>žD“Ñ¢>Xò„‡œ+yä•«õ÷Þs^Ÿxöø)¸„¼‹¦¹åUªV“PØé¼< 'N»wígZÎkx®’[$|’Ô!K¤6î‹UXZ“.ÀbÕÞѮСÁWmí€ÅÈ„ ùþ&Ö¯\|y©h]*‚å pŸ,ÎÅbÁÒbeš‘:Ö h§`e¥Œaù’ðJÙ Æö5‚§{÷ŠÛòïD´7ÔƒKuuàÌöíàXm Ø¿{h¢0„„é*r²®˜\,+ÎÅú:9X\MXN°øXíì‡Õ°«ÔÊÇOì¢H<¼?(|¶´¼Â0}ò$Â!° åpøN ‘•ïñ˜ääç+g«îs~žX–‚îIÍΑ22¿°ªQïM3ì9ÁÉq·å`±`•Ž|R9X,ƒñH¤c ›™¹Ü<0£rª‚U¬ª`U«*XxÁZFÄ’¥Õ­Ÿ ñíoW”*Ú¶"%§pûž˜“ë9G7t’¬Õ ÜÃ?ù䊾ÍW«*XUÁúc¬x9ÁÒT{ÕÛøl L³€“¿_—н¡`yCÀÈŒNà~#Yq†m}°ìÉévÁš”ŸpÊ߯»ŒX• V”܆íz¯,¯Ü‡°k¸ŠnoÙ¾}NЧ$rÉ‹‰Ðrö¼©$²B…J°*ÉÓF‚Åa¿æ# ¾Ü,ë¾x_gﬣGƒ[ƒC £»<|5 |RЦåߎpy"À#Ÿ1“Xî` OÀ³ax:â#®˜ô„Á£'Ã`Ì5 þˆ¦r€CZAy 0!³d8e)Ÿ‘p ðÈžpÐú™äM¢¼–!¾N4–‘p$ñXNäöú‚€“Ü[äÁ’¸v­ds+ CB&ˆ" .Ÿ ‘˯¾Mî^ †A"‘‘²³¬˜×h¡J%ÒÀôƒB'ó³=Ž–ˆL:™TðåêQEHù"TÈMõ[òŽµÛ VÃÖÍ`øÅK°(OÚDQXC„:½TŽ–ŠÛí¡@k$NAÂ¥1 óº {¹¡ÎT9'_Gj¯€$õrùT¼RõÎ÷ÄÊá&QxðPÌË÷\¥Žî–¸È…”à•Î.1ûùfá­ùBô9(®Ÿk1•S/îÝ_ž;n\»8¡úÚÕkâÒÅËà¬<ù|R¥âÂã++Ø+K+!ÁâíKòïC„BQÓµCÓÕÕ ú¤\y)N„‘#-Ok ^S°`]U¼Q¨Ü/^(,“ìî¡Ã!Bêꎎùĉã`ù˜"#e$ÁRp?¬Qùúm‚Õݰ\–‚Lœ­Ù¡Ø¡8X£¨ÙQv6˜¿Éùsç V¨&´‡,g¨°’`q/¬rý°L²;ÎÙ¾ ´œ>òR0 k ˆÎŸãñ¡§`>¿LçuÝ-K‰„߸q£P3ôL,óE`q¥* H]ß ,„"Íuõ{ÖT[Sì‚•ŒÅzwiÁâ¡%X±ò«T®`UÀy]T¼éü%n)Àðlt[Î|¦QWðø—²·Á÷±Áãµß}‹÷Ϥ`M’\þ40+A•$¦œ`é‘5NÁ*¾ÎÆ‚Åû”¬`óÀ~ÜÈ´äñ°`é*Â1Oœ>sÁLy‰±>lÍe…È.[Î}šŽ6)šü!åß±/ÓTÌi:H®µwƒîþûàÎÃWàñËi1ìŠ*Æü`Ô›†&Àã¡ñ"^RcQÉ8ýuŽ—7¤ðEçÀ,Å!tCLΙ ÅÂä6q% ¯J±`qåœ,½‚ &¬9‰F®Ô˜ÎUŠÒø"‘SDóòôx–¡é™ à,*¹'®SóB‰}õ†ÛĤô)y'æVAŽ*íl¹Jq¹‘NçåI}äåÁ‰Èæç0Xù ê¶²ò I$#1Û7Bµ-“ɃlZaîŸ|RP-Xt ËÏ-€d$.žhµŸmVlÚºZÛ@A~[& t\ågäÏÑbÁ)XvxÕ͈•s«X°¬û²Z7ÌÏÅÒªüûÝÆRõÃ÷ÄÊÞ}¢ð乘“'‡¹2‡ÎRqKÞÔß…rPúΟÝ7®Îyu§\l> ®^½hž¸aU«]¸pð —O¦çÏ_¼ÒP'MbÞ^X’WV,XF~å{¸}ûŽy Ý]=€F࣭­ÀH‘F8)RW¯!% `Ë-iŽE¬_UÐõM.Õ19X纊РkÝ^E(åŠà¬ôÑ# tDÁ#sFilÎî= ›*;%—ëjÁ™ÅÙšíà””+b·”/‚Ú!ÔÔ(v64>†òJÕF‚Å+XL¥¬r#sX°œÕ„<^igýNÀmêåó"ffbiepÛ‚t, ¦ÇFÁŒËxì ¿¯°dFåp.cyÁâ÷dÈ= 2Ù9k5ÕŒd*.–##X:‹ÆùXíEU¾<‹pQ –^ÕåJDši—rÿC VSË%ñýüÛO†PEHct>üåoÅ‹©`U°ª‚U¬ª`ýI Ö®¥äó 2R²’~?ÈåçÁ[,ê}ÅRó·÷÷«;Ï]¢UžÿæÿûÛª`U«*XUÁú“¬ùá1ÈÒ뤊V²–.\ÄÊVI’}U°ª‚U¬ª`MÙ¹c¥]Á™¿ûŸ/®ô܃T‘\ýÕ_ÿ9ÙÿùŸÿ¹Ç`â,0Õl%ÕweË!cF°\~À‚µQr¸S4*UýÙ‹w¹aåð^q"¹©È<`Áò&—A‰œÙ__#KÅ÷ç‘×#&)à¢AÛ{wù’ë:NrwÉבõEEoß-påòeÀýOX޾Š`xTÎÁÃÀ r²¹ jÛ‰–/ÁÕÎ{àz÷ÐÑ÷ܼûB <,XƒÏ=àÑ  Ø=¹í¶+1{:2#†'C`*^ù:* ÐI„Ã%)EIW$¹‚$M6¸+¹_ &Ë* u’»U¨Hç–@,–ñdNMJç0‡þ,Áj\õÕÚÖX°pRÖa¯D4 ÒRbšj%q*X°2™œ©îaIâ%ÿL*¬¤g%k,sö!Wò” u2+ËÔí/åŠÈ'ÓÀ3é{¾Ø ¶|öÖ|ü ±pvz.IH72`%¨Ïq²»3Œ˜“Þg/ÄÒ‰ÓbmƒUâ_þE¬~ºIÚ:¥T‹A;W¿*­Šå8Ù~Îú›qq²Tðû %ß/—4€G÷îƒÅ¥u0qôìÝ ÚN60LU{bæxî±tæL 8Š/ZMbÿC`jÊ–W×m'"¬âê«Âò*# ½½CÑÑ*º:{@‡”+b¨½ ñ¡Ñ7—.—Vr8Ð*d±Z'©"¹êìPôtƒõî.E{»%a¸ÚqWr\ïyŒ|Ý î<†\€kpÈc]ö ¾p¡ñY0:¡ð†Ò€géùIºx¥*©%怙ÅÉY)WW†#)S-hÐcv8¯*O+’yE, 2Ù‚©jcY™ñ‡A[k+8wöhkëö’ãpéÛÊȃ$áÌuàÜ©Ãà¹`Z¤²”Edózn˜ZÁâëdå’À¨ ù;‘M$@*'÷‚›?µ›7§:ñ›²n}`¶r…X¬ª {…ž9Su“”ª)ë?üáÆRµe«(t÷Š|,]$J*_KêÑR”Í+xVb†þöDÎ6?QïÃò•Í“¡ëç Ff"þ8¿{xöt°OÜ¿ X°ºÖƒ+R,ªâëìPð¼ÂkWoÎëá3 ¸éðÌl@¬¬¬§Hóýg¤œ·¯¶vuu‰®nM—búF+0cmʵgWŒléºvMA‚Ftwƒõ›½Š^…¼#¬ba%‹î<É9§`Á²7…`0¹W+R®ˆ¼<†qyL$f“RBˆá}ûÁ)Xý»@ûÎzp±®´P³Q4UðŠVKbW-¯bÕ™¶uµ¾\¯ 1- ,>–W¬޲`9£ûås"vïÙ vJa$øþI²n÷õd"¸)'çn²¤„}³ M‚TŒË¬(ZÜçVÞ§:^ù†Ÿƒd2c[¹/ŸÉŸõt2 fG^|É3÷ÃÇCÝhÔˆ× r°øýž”Çf"'Á þòÈ+ÌoE°FgÒâÃ_ü¡BÊÇrGqùÞÿ©h>u®*XUÁª VU°þ¸+7/©ñçÞ}¯•ª•»Äbßm‘Ï,”é›U¬ª`U«*X%‚U^\•dbƒŸÌI$x¬3DXÒ³L¾QI'-ÜhÔžòõU» .YÙ0DXá1—{^SáyÀªMVÙÛ((œ’å¥É@LE€}›–„œe©ŒL®˜mOF= ûf?8þK`–ŠåÁƒà˜A+œÈßý¯Ö[Î{hP…-Á*kYù:Qy #|òØHLhÏ¥„÷¥„ô54€¶úà‚¢EʨÙ8dÈÕ…{ëj,ybÁªïÇ¡Cªe‘röê$X*,'XV.Ö~°W~nÎÅÚ)±}ûvqéâ%àrD( ²Ô[O¹…Iùy'R¨ˆt"-">?àÅy j_1Ç@îÍÇ5óê%ˆKÁâŠhÞ'CŸK[³cZ×}üøºù¼vŸ£¦Ãs¶I-i9Éo?²‹›-“`%üÀ–ùþߊ`ÕmèDÿt*lÚ4üü—¿Ïǧ«‚U¬ª`UëC°rs¢Ð[¬îlÀjTåU?DˆpñÙ°˜C;…Õ"ª‚U¬ª`Uë kcñp½ÁŠÕ÷ø#Ñ;ø?²ÅÙ¦a»˜œ‰™±.n %¿ž`•ö”âÛ*(¾Êª›C°\E‚•VT¬’a™Ç<Î^ÉÛH°J¶9ðJ"&fc`22ʉT¥ÇZö´ ‡/ÇåvâÉð8è»ý\¹Þ .ßhmÝ}¢µ«\ïè­]7A[»Üèè=·@kO¿è”Û‰‹—¯ƒòúDoÿp£½´vÜíÝ·A×Íû¢çÖ è½ýTqç 躭hï{X°®Rh‘ÒuMþŸhí}ºoþ£àî“)pÈ ¸zñþ³))dÅpo÷Ñ+劸Q_ÎKy"LˆP ÿ΢u°Ö¬×‰•ór’,îºÎÉì¯ë‡Å!Bî…e¯"/Iv—GXÉîª/–ºßÝàùÓ!0;1 Ò±ÈÎ/–¿ÛbR¢R´.ì1árý»‘&zŸyâRÜ8ÝÂúb²øË‡ï“ò8K°`årV…6˘³(…n÷7¿ýÄ\‹$@BŸ"ÞiHæ!ú³’}¹ÕÇ>Î<'ʽbÑá6 w_¸EkÿC´i˜œM˜êµJòPV°œp,X&ŸI ‡%KН³‚eËÁb ™ôgÀ o£Ç%ᬠ_ 8s°Üen¯R.–,)¬W(n$X–<©ëºh\­biÑr'WKË“\Ó©Uà“—“þpÇ€/½*fRkšU0Z>y_„7^nyŸ]‡˜N®šëܺ÷ {BÀɃñ`ð V§”.¢KŠU‡-âFG?hï¾:û[A¯”- ¬ûÖc)^ƒàFÏp­ëž¢Sq]Óvó`dA´´tñ*ë_ž»ÀÓ‘iðjÜ'ÜRЉh*’¹À›¤<@éì¡dçç›ÁäÄ”XZb9…î›jDÍ+Uk| –Ïž‹n_Qi¶Õ⡸•ƒ³šs¦ÒYkÊZ©RiJ/ÁfI xæ#ÃbÅ­àö…ßqú€J2´ä-Q‚£ó  øÈ!#X¦Õª‘++¬~zÒ¼–Œ”‚DÊ9Õ,ªf 7ÍÌŠU„Fü–Ìe4:ƒ0!Ïü<¸vê8òÛĽýX¿ó±öÏÿ\QªV?ÛŒQ64'ÐädÍWÆjÊ!ÁbÁâƒ>rŒ‘‹¥O©ì‚f±,i]H8OFÒÒó ¦áûxÐ~Mœ;Ü|R‰dj°`uìÙ nîß¡Ù:š÷¡é'Á}©H~ @€E‹ûeq>ɇYºøwÞ·K^8*»éçžð°£¬ž?¯!t?,K´lÂŒĽ¬J„«8G‹åŒ®³&…0}¯HªÊ„×¥\ ê{µ åjE›@ä°bæÐ!0ÑØ†¥t¤Ð·wï õàŠ”✔âu‚Õô{V9X´xÜ3‹s°ìÕ„\^9«¸ñè.)WDýNjŠº4îÛ^>|Â3~Àá>îóÆM–g§Ü"ì›ñL¤{H×À¡wÃùÆÆA$ž•û­®Ð¶X) ¼Ÿ~9Riú®€ŒÜžÁ~ê~ì_”~%‹SBÁ屨í¡hD’ à­ÖÈlZ|`oÓà‰bjÓpìô…ª`U«*XUÁ*¬¼Ë#–;ºÄŠ<É®\ó.÷ Ö‚< ŽþîS‘ç WªV·×ˆ¥íb>Չߎ9UÁª VU°ª‚õ‡,3tùd`6¡Í7ªÄ«Pihk:*…Ò<¯ ß•!Bî7•Tܧ‚À‚Å‚SIH0¢Æ)X<*‡$S²ÑmT A––5¢œ`½îºdÑR‰õúºô¸ÊÜִ܇pSH‘®¯«íaEg¸Ö܆”+‚ïïƒúor ÿþcà¦ÁÕÁÄ‘¼}22¦Sk&Ôè eÉE0ÃÞ¸?4úï<]7ïŠ} ­óèì½ zúÁMªXDÕâSÀ‰ôýLO®Öî{àZÇpEýº8ɾ÷î0¸ýhÔt•@‰óCÔ“ËLØQÃáÅ‘1˜öGD<•\ÁrEÐÀ^‚»ðs¨ÇJN_1=`’Rztê¬ò³:«êÅÏëyÕ)•L{cÆ;иÊ{’rEP%™(¯e…gm¥åí¤m+XF°¤åt£ÑyÛÊV·¤\­½÷ž¢ï}OÈ#±T¿w¯ᆱ’ÎåöPÌeòbñÉ3QسW¬ýà¥jYJÕØ{?פXíÛô‰8ÐP'"áàxÖèœ I–,Îy+7Φ¸™¨}fàRyð¼A3ÂÇ>Q¯˜ÍÃeßY:)º¡a[s£èi½(…˜[\c÷oƒŽ]ŸƒîýÛAoc  |¬öSM€«Q»º: –S´ì+[,T¼Šjªu®Ë~wwFßž,^ÁzÖÖÖ΂$‹0+YÌÅRaºè€¥L¿1cpø6ÏÙ‰r;†S “s¥à– Ëǹæ£få*|øà•«©À«ýàÉÞ}€ª¹‚ðz}=8_[Î\ÙË)Zf&amå*Açåo"bÎ}X¢œ+Y$Y¼Í¹’Uš‹¥VÅöÈÏ*±kWƒi†º£¦4îÙ ^=¸n7˜ò÷è(˜|öDŠÊ4Çò =ÿ5’Ppev?Ï ïè8“fŸp|˜ëHQRŒˆH4ÜÃ/A8–ÑÔ¼‚÷Õp³h‚‹/ŸõÇ3Aáp_( ò¾‰·"X¿üèw)Jh¯ VU°ª‚U¬r‚…•+£O?"—Eÿ…Ø¢’Ó×ü±þÝïVn§ðÎ;"½k—Ê“|ãö-b¢~Ë&P¬ª`U«*X4‚U)gÇ~¢®Óc^¨jðÈ© ˜ÿñ?þ_yB[Ä ú˽b{Ýn]E¨Nn•ªèHlî¼p« DÛörÂFŒ{‚8éQ»[üúÓ-âm’üN<÷FÍ(ž­´í“Í@µ-öÚ,™l9X|·‹yÁÒá´7,}ëñiÁÒ²â òž7ÊÁÒ¹Qnjö T¸ ”JI˜V¬¤ÂE¦%îhPΠv ?—@bŠ„PÂù\Óé511›÷/¶€GO*.·€{çN€¾›·€/½¦åýòcž’‡pÅ O' À#åŠpIa#|$gé5`*ã0Ê‚g/]àñË)ÀÖÖÙ'Úºo):@çÍ; {à!è½ýH£ÂŠ,`ýÔsëà&¨jäÏÝ’¦¨]Ï@ßý—àÎ#’/0áEÛèâÎÃpûá 0æ€P,-ü”# ñŒO€?¢Ñ  €\:æçÓ¢°´ K /EŒ¨"ä>Xj›Ua¸àó«° ­\¥Ó¢ì?’,½’U®ñ'µSXz!ò™¼HÑ€UÉ™Ã@ýg›@Ý–ÏÄó§OÍÁèçÀW‡X‡ +–éã .¹ª¸Í!V<‚£D¼ìòÅM9T©›-ÎÉD\nÜ)î €x*æ—ÖÊi—rEtI¡û¶þ}ÛMO¬kW®€N)WD”;,GœGE’åÌËâak{èêPtSÞU—•ƒñêQ<’¢F¬=ÖÏi¸?•S–.l R\¨ÃºÝ³çb¥‰ž>¥ F¢<¡X=Ö 8ï*s´IÄŽ)W„GÊ1ÞØx4ÎÃ=ûÀ€ü@tíÜ)®J¹"¬Ü+F •S´˜ƒ¬ðUû`½‰hqu!WÚ« ícs9r8tDaúbí»¥HñêÚ:ç€]Ø_îÝnný‡êÀË›=Âëòé „U̓PlD4î‘q0H˜m,X,gá¤"’ZáH¸_¾!)rÑä<`)‹$vÙúů>6²Å‚˜ ¿ÛfC’÷I¼Á¢Ð ¯Qˆ'ã¬iy‚ý‹¿øË7,»<9Ë)+$WÄþ¦Sâð‰óf¦ñرyÇN<޽GNŠƒÇϙǬ¶5T«*XUÁúo¬¥öN%Jò »á? rªÿDžÜŽ‰Ì‹a1Ÿ_„$™Û¯ VU°ª‚U¬?~Á² Ê‘¦"*lã$tÊ¿º'ö¨¾û_‹½‡Oà$Û}waC— îƒõ:€P9Ä‹+ÔÌØ·üÛ÷ß/á$:qŸ30=´VŠ`Á´KõÇòHÁ™"ñ–<8qÜ9\îÙ ¸Ú©·«Ìd˜¶%Þ³ôq(ÒùüÍóÕ‚e%å[2H_@|A΂@fÌÈÛ%¦<#.ž¾·î>í=À /*¨ë<:Ïß±õäÒt<íR¾ˆÖž€+¹zñJç]yù `ùºõ`Ü{â_xÁ£`ºÍ?w‰ÛFÀ“À3¡XpöòÊC ÏD`}]žxݼ2ò4¶‡ªÜ¢`ýj8„˜ŒWÒêĉN`ý£ª.Ê­j:Šð% %X€2¹9°8·:;@ݦM VÒzù(,¯ge ¡G ÙCY&!=É–JBx¹¹r‰ðŽp¢F,ذ‡—‹Ætó…u@Cž‰³{jÄ‹gÏ@BÊÁ×½Ó:vzöoû½è‹U®;ÚÚ;€3DhËÐ%»åã%8Ë96g#Áâ\,«mÃ^3¢Ç¬déû©—χ¸ÞXø˜ëp-xÞÓ!\࡜m‰OÊ”/b‰Õ¬üÂN„có`üés0-E',¿ ÊÙâÕ+¬`-(Ò‹ IIùÅ„Ʋ%«\öÜ+"š\Ä W( ÁòÌ€`$ "RȈ·"XvFýñk)Y$Dÿò¯ßý·-Á²w02æ‡`ýâW¿`ѾvÁ"¡Q!EëvX°¼æ¶‹‹ÄŠ© VU°ª‚õ‡¬_@å`}ç;¥ îö,]e8?å© VU°ª‚U¬ÿ «$7È9ZEI…[×S(hÒS¡<°BWÅrü»%A¥‚EùF"üK)oÞV¤¦qß+¦¢.óÚV°8ŒGá@Þf‡Å„‹gÁr3ÀœœuœS„,ñZ²~.¬80‚á–“! Ýœ3­˜œ‰W$ÔcÒ¡±7,",¬bù´š‚*ÁâûeÁ²‹ŽS´XÝòáFVpˆFðpeá=)W@~Ðîm(XëÀË÷Iï7)À€ò¯l‚Å÷c¼Z´ì¯}ÉûX~0 K°–€?±¡¤ˆg—€5ÈW4áq’FàHxÒ: ‰6(\.z>zûÐÚÑxìOwÿ€ªE=ê§ç¶¢ë–¢]JÁÆã~8ÌhoŠÚ}ç90òÅ}¹lì‰Aôèr‹'Ãcbhä9‘'p‚¤DÀ§àðÚba ÌI¡ R±˜X’"°!Xk?ý™ þö·BÌÍ•ÊÕ_¨Ü«íÛMþV6™‹K«€GèpU>7N5î46§~ëf0>6–VÖA¥¦¡–`-šñå‡flã9¸O•éW•·7ÕðpZÝDôu!Äbô>Ô—–ÖÁØÃApjw˜ö΀h,Ò™½}tJ¹"8DؽOÁ: 8½»\¸xpE çOõPÿ* ÷Á)ñcK®ÔæþÑl-½ÎüÐS°züX?Ó¢0Òt¬;c~6cnl9Vâ¬%TVž•†¥ª¨ÏÕ1PRU(#VQ_Š„çà`r®ö+K¡ îIA!ú¤Xí;À¥ºZq¶¦8s®LÕàŽ…!žÜQ êåu9‰s±þ‚Åp3Ñ7¬ÊG÷a£žXè‹¥ïo‡¼âØî:Àé ëÀpo‡ðLx‡Y¨üÑ9…2ùeÛÏäcÉØ:»Ï/®§=àtã.3"'Iî‡õjà&hß½°`uîSô5î'5ÅñÚ-àäñfÐÖÞœ¡Bþ=Š×þ3b帠Ìe‹“S`µ¹˜¾T¦Óú@ÒʼnêFÂte …{ã+ÕãJ‰Õ*…%Ô¡ÈKq ¸×UT <¤ðRRûFÀIíO÷îöì\5Èƒí½¯JBÎwdÑ:\S ”ø8FåèSuuo,NT·~vöòª ‹ËTîÛg«R²û®ú:Ð{HqûP=xÕ×-|SÓ ̓<&ÁØÉ/»Wö¹ž¿3òœ‘—G°}pÈÐYMH_–‰‰'O@8–µº¿ë=ܱ=¦‰ë>XÜY>H]Ü%!¿,—„#§Aí™ Ëþ{éŠJqh°tE@ç¥VQAtÅ[%Á*'f´ßñó×Å{?þÕˆÿ¿¾!vî;lVCèDûyêƒõË6¡ßGQmt¬2ÛÞ–`•_ÁÒ+\Ž,,^Yq®MÛ›‚êv ,Z,:|)B¡å­œ`•—n¼É­¬ÊÈRÁbápÊÊF‚e¤0±\„S°fãm£y’’Á“ Öý£  ï@-¸ÙÕf2k HŠ´`y凈°D¯LŽ–”+‚V­JC~¾j_ÎÁòS ^!ùÁ"¡˜™ï̧á*ºL:§ˆ§ V&™–'÷à˸æ’”."O3ú$<&&M€@0*Æ&<àÁç ³»Üè¸ :{@wÿ}Å­A@3¹Í„ÕUÑzó!¸Ñu\íTð*Øõ®¢]î׎¦¨/ÀíGcàÍV²ÚFpSTæÞà°h9û¥xõéïŠ$*𣋖£ÇÄåÆƒâþæ-bè£Åƒ?ví1êƒò„vàà!pP°‰ýŠFÍÆC ©ù88}ìØGß’µ`µ? x¥jŽÚ,PžØ2ØP´tþ”ówK®Ô*Uzâ™eι‚<âÀñ7‹E¤èú6Ò¹e! “ \:.œ8*üÁ˜"œQú6.è,X7¥\šK8p°4ïø س ЬAÂ9"çÁƒ`mmm¡*6«u±ì+X&‹w-,‚åK—Àúñcþ‰ÓŠuü¿E¡W¨¬m'§* eaEÞæ r­fŪIA¹V„ÿbúÐA ÄjpŽÂ¡Yƒ5%Úêw‚KR‚Z‘j¡Õ«-…‹£/ç•+nßÐPW ì+Uµø½ö­Š•sV¡=ëu‚Å+YNÁâJB¢4K­ÂÕÈ×…¸°¿Ü>\ Fnõ ¿{8›…²4q¥ ·Qð @¾÷åqœ§æÉE`Z/è®`8¦ž H<'¥©Œ`¥ŠHH~e¬P(Â4¶Ìíáh$2ðµ«ùìeˆÐ~ö%‚õ“m'N_x#ÁrÞ¯·L"¸[Ê„;j…›XÊ…ÖJCp•$±*XUÁª ÖB°^üî³"¹šùéOÅéã'Åñ§@³"âÈ‘£ *XUÁª VU°þ¤ëu'æøÉ‡ÿ©ëç:ßIIÒX #¶ìhÀ¶ÿã—*ÿ(’%‚óF‚¥$äu‚U.¬Y¹j±|›{–Õ˪@++i&œf{ü¦–Ê“â°Ë ŸÔ§Œ]Nø²Eà+|£Ê¹`¸åe„¬ò"inS¿V®p¸C \î E–Ö à,õ›ŠiÁÒý¨¼Rš¼hعT„—òî)WD@îKøéõ‘¯a –ª(¹wtè;P§èì3éU@òiÆ÷ø“ ýÞডüZ’üÿ³wŸÝq-×¹¨õÓüá\]Ÿûñ[Y²-'Ù×ãZ–¬­à­Ã­½I"ç 4b#çœ `IˆÐ9gäÀ°nÍ·j®µz ·,ÉVhŽñ »ÑáíªY³RàÄÔà™Â!Kþ¾GÓƒ‚ƒ›à¥ú+=jƹ/ÂÔþñå&’zMÐþ$c Ðû7QÀR+á8`ñyOÅùÈ~b¸®è€6GN)€]¼‡Tê–Ÿ>^õ561 ½ƒ#09³´™5O/NÌÞ‡ÉùGÀ«ySë©ÅÇ0>÷F§š.nŠÊu]zSÔùg0÷àÜ{º;e5iáÊño?ÒZ[ZÅçb»f··As³𠾡êê(pÕAMM­D?«ÃH]m-ÔÒi…úÆ&h—S">0 ë&¸]^àé5žºã°dÝ`ùÊ©@˦ËzhR›/SÿÌ¡ˆÃ¯4$”¸E’§á鉘E4v#õ0ÜÓ-ÂU4M(’‚•Ù)à€5/BÕ¼iŠp¦ÂX1¯"T}°B^ XN„SÀ!í7 X´­½oôÚ44û@ûó?ÿŸ™€• X™€õ'°œåµi5Vúg­½Y…«LÀʬLÀʬ?Õ€uU‘ûuÁâüÿK|ÀŸ];½Hü4Š%»ŽËžMü¡ve1},½ùõKõ”R¡áÊËÐ ™ß|p¥žyЧÉ(ýKM/öÃßëïæ"o>Œ§·p‹@D<ÑcàÁ‘<êw­\üqvrè<*ä\ÍàÎõ`Å*»>°øpîµC+)`‰¿AÈá@9—Â÷“·;p¹BÄ EH]h_–Z*à~]‘T_ó"\‘;SàK¾.û ž˜>:Ä}%¼ÔAÛ4Ñm¡û’<—âéâ>§lÄ/ž?Ä9¿×°xëšx;Ú'$÷áøä ’"`YÃØÑ©t".‡¤D¸"Ü ýðàŽÄùOÏÞOîí9al|&§¦¥I)‘8—;¨=}¾ ‹÷ÂØä,ŒNͰɹ{0C[þ,ÈzëæÖã³€–±©µ,œßÊ*H¹ ü3…+;U‹½Uj‘š›ìÐÔØ4}(˜ƒU]]=Ԛ•™ÍV MÍÐ(.·±¾x5á½ùàMyº-¡¯•SyrºîøMÖÜG’oÐ!švÀôƒ|>¢'ú?OKðïÌ/^ï Šuu8ü OX7©õÑt„ÐU–Óããš7—' ¾`–'Ç`D„«LfÃdYÐ*Â…Êlhʽ ´Õi²ÕA_o¬¼XžÚKëeùý£Eïï/¯8Ô{gB¢­NDø!<µGIAš¼mjšú#\¸þ¦A:Aê\ud'‡¶:£ˆ½¶¸ˆÝÚãj£BZaaí¥ÚÃ’bЃUa!Œ‹à0n V"8v­Sñz–1%h-rWÅíܹ½V8RZ"‹p’_ùyOr(Ê12„²/º®;ž§òÌ뺩BëjBs?,X×»KEâ~‘riu^|‘Þq‚?|úkH½6øµÂÓ~;«k@ÛÖpÒ;·«PŒIúë7¼ŽÕUˆŠ ÇSö\ _Š q q9?ùäú{‚σ '¨yvà %ÁXEh­'ú`èI?œúJɺ*kÝ“DÇ}õ«_Õv\aSÀ²Ô-Y¶6IÛâEo‡ :µ«Ú£iå— Xé#bú^x–UvzÀa„F‰~øÉ§¦€%G?\XþoŒ>¹ç F¥…yØnpö\ðÅN¤è!øÅ–øbÇzèòÓ›1½QSÝiUC$.Ñj ‚øbgúê<7ýL÷ƒ†âTöDP$Žà˜o·1²#ñ}à²#>¬É^è0Â&ž3b ‡"\¯¸ýÄG[Î^qß /y‰6¿WdÉ^÷ë k°æ+r`qr‚âà²Å}!Nj}!¸D0"q›ˆ‹UT÷×Eá‹–øŠç '\¢Ç8ÅóØRGgp@M5O€uÀT#ÌT"zÀŠ%ô@ÅçáÆ—'§çÀç988=`™¶P ‡¢àõ€kc¦h¿8Ú7nv¸÷Ô)íK'‰Fàõ`×áƒGË/anáŒOÜZ½86µ“³K0½(q]—¾jqቶQV®Âÿø®ZD¸j‡„,cäŠUkk;4ˆŸTÀâ‘©Z W²lJMŽpÙ¡N)Ò >pIS“]¿üÒÂÒŸ{tÛÈζì_À½ÅšÛŸmÅ%pàZÑâ›0-BÕt™1‚5[y[_EØ”w r~u*E ¯77àâͤ…¥/ûÏÚ­A{ÿÑ…†‡®=¢Ég­¶¯Ðè7倧õ¶4G6i_áúªÕWÕT*}¤Jm{³^Q/Ê¥eÈ’yΰÆ a0/8Xñ¨T[¶ÄõUØ"G„-¸”,É^Q.Ù[@ßFDh$õn0‚TÖÚ§«F80]×–ÁÒ8q¨2ïIø±€en×`Þ6'½+Œ= ³¡¹(Vg&5÷¶<‘}àÏHÆA+LÛ׎Mðú¢úgd˜Âö”ÛÝp{Eæ:EçÚ*Dã‡ZR|Æ&ñ%ë$]JŠ%e héË}9`ñÚo°~ð/ÿªUÖ_°Š«´üÁ?gV&`eÖyÀz]X–6-ùÿEëjh­™€• X™€• Xš˨CIß%=€œ§­6ãÓ´L` ðÿÜÌÑî>ßÒ/ãþËôœ¢ãì²)¦êÙdLòʸ‹kWÿ]°¬5Xú¦ÄæÛÌKqÅÞ€>E;Oƒ€5êŸþ•–7ÛâÍ‹M5UÝ‘ê¥×™6?æËq÷6¢$¡ÄèKŠ7xéT ‰?2‰Ð…e^˜¸=aðŠ@Cü´‹¸]İx Ƕ/z_¨¸1}x)hQýWÌTûå JÁCÓ‚Ç` XAqzâ@€6â© éàB û°d¯ë*B8= ‘ƒ7@¡“§KÞxpÝBì øñØÁ˜8éö ԀɩWžžå°æ •¢Ä¾ÃÁP\ïKt@«Ñ0M(§ýŽ=`%SÀ+“q£‹ƒO+ò”¡>E(B9>8ª×JˆÃ‰G#Fatt ff¦azfôíU ñh’ÉC8>} ¼22–:ÇžÖ6vDðZ…©¹{ÀMQyõ"5D}UTž®âÿöïZ_gø€°KWí"ô´B#¦[Ä›tðTÿ.§UbÖ©Aq˜ Ç5Ÿõ\jº°ºª*Ä á&©<|Á%ˆiºCð žä HüœØõ)^iÇ{»Ê¶Oüî;„mß>ì*{ârö¸Qp*æÍkfŠ*`‰ç²t ñAÖŸ>ƒ¦üÛðâù+Í%^ÏÄ!¾ O§&`¬øL•I㥷aF¬;"\ÝAÀº ·>»C}½`­«zÿáxôÿ³^bdé!¸JË!ZS£7×Ù$þV ‘ºà^V…•³² vD !›åðR„—Øö¦ ž”–7½S\¤MÀ¨,d /:s³AŸîSuVz﫜,#déÓ†J~Ø›%|!ÄëÆŽ×Ž]ÿÙÞ*µÚ%û58œÑkÊZ7uU½áÄ¡©««K¿<X×õ¿ÒWÿªË´nüœ° ò!«!sõÕ„Å´‚Qx>7­nà)uý3RÕSñg'<¸6w!@[™Ñô>Iž+je j?|#%Áýj b‰#-‰^vçX |¦ýôgŸê¥t}à j>§ü¡p}Øo°è°ŸþòW×6ýáO~ftϬLÀʬ?º€µVR™6-˜øáµ;óµîî¾LÀʬLÀʬ?í€eí±t9`_Qüœ®±sPû«ïÿ=6x&õý¿Óª›:äª5U½+ÂÕ.ºŽóÊ¿ô`w5nX*ñ4ã¶#¼•‰°ô€p™µ?“ŽdÏ)ôÁÂÏrz‰¦÷D¸ÚãUjTø¬VõáåDâi2Ó”œ7~žð>x}ˆ 8‘Sà?|tÿÌ8n_Š*Æy$·'°BÉ7Z@±-#`É¢ïm ŒÕê:£ÞÀ«€u»®è›pFÎŒ¢}¬Ñ3&O$ &B€:Þ I®Hâè­Fàº>Xó¹pwjâ‡ï*÷¦r‰ÇøÔdÇ âÅEÜÁ”rظ‹ ćšøp%úB„‰KÜGÂ…Ì<Ìǵ}êê}d¬"<¢Àtl¹køøT"¡ÿÌÅõyÕ‹Öáá H'â¸h4Á@Bá(ð!¬¹;‹ÀEøæp±hxd^!ÇWóê· ø›½ ©é´`ìv©šðøÁ²æª¬I¹Jýã?jSÃÚÈø´ÖÚÖ¡5S!; ÚÛ€§l"}`}#Øl ~–ÅëzЪMWS'Yƒ˜C–Àòù_|‹ó÷ (^çÄESÇ#íÑ‚Šv¼Ò¶[Úr§Òlº¤-×>lŠÓ¼vÀ–GÚö®_â€Å[€¤¬ˆä³àXOfg ¾(6_;Å·¸}qð‰7xòL|X‘±’›0]ž ce·a¦B¬êhÈþ oJ~¯~÷‘êÃÿÞ¾½×ðl‹k¯ø0'>|Oµà¯©ÚÖ†xD¸&ÆŠÀ mKY-+‡gåeÀS\Èν­x…à¤V#ùR_n>tdçµpݘ¼¼±3÷½böºZ©µERKŸ"liáªZìé¬êª e=¬µµ¬‡óksyy€¶µµ+++pÿþ}˜™™¡¡!èììþ¢Dá‹§-¯ë‡•#(¨€•ƒ]š[|¹#ÆvdŠZIËŸz¼[;ޤ´ÄáÄ)L™¶4ã÷¸”W$!q¯¾„dòH¼¯_H–^wæ•Ä?ûù§z/ºp8 A°.¯JÄ“¯p(áqFôZ¢téç¹n…õØÁˆ\ZywÍ–.iሷˆ Àë]xÄåxÌ«úçè§±´ë uJn°œâMK’«ÊxO:óÊ?àÑ:\&<“èCŸ¾!û#§åÛp±}&ž<´Ü›(ê8V¼’É%Â%ñÐsˆ’òf$®rÇN€×híx’W’ÇŸ(éu[Ê<Êžø`%\C"[.Èú¥€øfAxt &B …!ˆB\„’8~«×=´WÁݺx`+‚¹Êl¸7=‰ƒ76-u÷¹À«Fxi-/‘‰ñ‡¤m“ F¤|‘à&¼Ä6à ¿(ùÛ…}dHẪëN#XFí•¢F·8`í'R:8’Tà¢=ùb‘(¬ÏMÀ“‰†ùù9iá.ž¼…ñ8ÓV,$‰C4š¾1¦šãñs5$#}.õE @aYHôhÚ7¾a„«ÿD{¼ðPëéë´aߺ ×ZqcQ[]õU ÀÁ‹j©¸eƒ5hÕÔJ¨¸]ƒ¸púFàóä}qÚÅm"»þcà´á:œûÚºòJÙpºNdC„)²îL)I%=l™׎WÚ ^‹¥È¬#ðŠ× áÚ+X¼úéN_'´T–Ã5/öÄÀL*)x*7/¹ú^„Êl…1‚Usë uwÃuÁꪀõ¥Wþ&Aëâ v'§´UñAMvË+`!ªZÛ!ŠðèÔ–8n #T•°f¡âmn8PÝaŠ,ˆ0E¸iè˜Td /OëÎͽY(“iÄJÕWéË|º¬[R^®T[zÍ•G«T˜úPÀjIw]вžîªã8ñá‡R©¶C"¶ˆ×ë>Íëׯauuž>} ÷îÝÓ¦¦¦ ¿¿ÚÚÚ€GÁxßÂÂÂ"(!ŒÐ6;íà¡ÕyÂáÉ;àÖ( z¼{©þJ ­Ìx»*}ôɲµ‡¨dâ\/W •:Ò¿4sSáËÛeÉ€ÅÇGÄç ûƒZÈã“Äç¡z-’ X™€• X™€õk¬äÈdZ¸:ÿågÚ£{µV2+°2+°2‹Oÿ¸LáÕVήeœF,ñJ8DÑʳ×{>Ø ‚±òîj|Î+ÖŽW;X^°,#D] R<ýå²àÕuXù³O\æ~úK}èÝH€O„à•€êw½OUâLÿ9˜”üá}øÃ oqx´äjÙ‡C†-£AaTœžðʧ¬„WTóN Û>[ª·–%4íxâ°ëM‚+jêY¥;SÔôŸªkÚóDÀI+yß5d¾•a‡uCâC‚ ЖøÀ‹ÀE–š+àAm¡d+†Y®ÈÝ©Ià€ÁP±œ[÷{‰ óæ¹Ñh "±ˆŠÛ¦•c { qÀˆÄ!$þN„_¬zÀáDo0ÊËÒërÀ’µY¨ÁR?!ë¸+).Ÿ¤ö¥DNÎÞjñh4•ÂHy6  ÂÜÜ,ÜY¼ Ço€~^…B ‹ Iô˜êoÆá”{-i¥ õÜO&14ž®.~ôc-,^#<5»¹ã‡©©9­··ÚÄ›%á:)îq¥‡$…ÃM664Ÿ§–V+aÅ’œ"¼T£%ÎGhzQß‚§ªxа¼ žoaÃ} kŽ}XÝ;ÿ[JNé•îôÀ…iÄØT^‹ðFv}GðÁ€¥¦«­Á*H5'$²c UÐ×Ú ¾Ð¾æðFÁ#^_R8`M”d×`Í”çÀ|e–®1ÿ6x=^xE¨zÿ%‹þ6ª°¬×Ç!޶èy=3KâoIVJJaµ¼L‘½«V”g¥¥À«ɽâb¸S$ñTà„¸<2”'ñv799—6hæz*#Pqo«ôÕƒ­Y·5{~>´ÖÕ+½¾êR°RÈü;O Zÿ2ëºÓp}ÕÀÀD"ˆŠ÷š`0¬œN'ìîîO!¾zõ ^¼x4ÅøàÁ˜ŸŸ‡ÉÉIïU¤£³8hqM%½¦ËÊÊ_ûë[;p$ÞIJ¼ÿ’}ŵúèK¡Q7%ج,M…yK3×ê ìïëJ@Le æõó_|*¾¿˜ø’J¢¯7kϬLÀʬLÀúR+5:•>rõÿý«vG2+°2+°2˰œ¦INÓ´¼>°˜ÚÀذ(üpÀÚ µwÔÇ®8`…$§<‰7peÀR÷ãª"î«‚,Y´}&‚Ùö£O>á* nñfE¸°:@›ãF¡u8yAš¤Î$q ºžKSƒ*Xñ}Ñ;çÒÊ §Aãâ>ý!xh/‡{u…À[åð*ÂûÓ3À˼:„¶¯!|=܉›V,¶qLIâè¾’„ íIJ‰& ,Âá¿Q\F°ÔªA^ h,ë*Bùæi@¹[Âuh'TÄNh;²/ÂIÆ÷V2…G-U0^Uð°pæ_àQÚ†%q<õçÄ€ 19hòcèõVÃéC^HŒÏ¦…«ÓŸüDs¬®kñ#z/' ÈÎköäù&Ì¢oÖ’>ioiƒšš:@Q; Ý›#$ñquu +¸¸?O;ÚÄy©wÛVT¬¼ÏoÀôÜ#ØðÁêÞ>¼Ü“! Òšs_IXë.iË#mzE˜R¶¼@+ ·¬Ž°ôbw5ÕŽÕƒªìÀ¯¨Ì@ ˆ—V ÝÅ90>4 ñ·á…¶ˆÓƒe®Èdé-0¦³a^|y™*ýŒôÁ¯3Ý÷»˜4‚Õ;åò øz÷>„»ee°Ÿ K%Åð Xº_\ ÅEÚ\‘4SP0”/õååBgn´çHæbuë¶7í4-HÓƒêx»øÙ®¦ A8{}½¤f…Ó]±NÚ¿ôÔàuÇè4\ÜÎ!iÌËçóËåX›››pUÀâ‚xX048íP[W M"äófÒúÆî•Uððñ2Pè9¾ÐWA;_¾€@ rik+k¡º°Ô–cW,½„£ûù/~©¬¸xo†PD ¹=À‹¿à~…·6±.ë׃ȕ!ÈfÞ@|´_‘ÀÁƒƒ•>¢e XÖ=øwgF¨8íâÑ-\âFŸ|ÿŒQ¨˜äM€9dêx%"ŽqMš7r r?(‰>ÈI"¥¨ûNJ‘èOü£7"ÄDáasð*ÂûõÀkéÎ æ%~—BÔŒ1rdÔ`¥$Á¢Frh&'®—[-$%ž§ˆ ‹*9/2’:8†x4®‡"#hñJ@é€ö¤Ó_ªÁJê#UÜøÓz^n4ºpIñ39>aP„+òÈ^ ãUù0$¾u’……˜»s¨‘+ÁsUÕ§ù}QŠç#‰ˆ×Ñ¿0Ð;…sw|¡#ŒŽÆï?×Þç»ÆÈÕ¤9VÖEˆrémÔD˜¾DQ[áų5mk/\‹tïÁS˜˜š‚þ>h±7ƒ1jÕ¤,þËõYÖš,}ÔKžÎÏ[ñôöCñíÏ!÷³ÐÖÜ\gÅቂÕ+G Öû°)'\sµá’ô:+VÔ¢AµnØF«Z•x®Ü„4@MJ%75. «/CܦA­ æ‘è`ò¶×·¡%÷Ü]xÊœþìú¤mGžLOÁtÙ-˜¥æ¢eYzКxa*äƒkƒÍBÕï6h©­«®C(òÃRk geØF„G¥(HæÂ`~>ô‰ExßÀKm”öìË-ôÕƒ*Hq°jÍÍ“*+ ¹¥Qo± ·_°ŒJñkàRð2öÚQ®„¨«NÃíxd‰j®H2™Å …BÀ#X°vvv€G°Ö××áåË—@uX<‚uG¼ocK®<¤6¤¡±Œ¦¦ ¦†¦Rµ€JJJannÅ—_âÝ\‡P0rž¢2`q݇¦dê\/ŸÃ>j°T:ºô-ãw X‡Go KAB¬°Û ü…ž/+°2+°2ëÚ€…põÍo¥…+Ï–á*°2+°2+°>°xú‡?\õÚ$Ý饰e¬"”!麩CÚPwÇ‚=ñáG¬¡érÝ—QfÝ’†CÚ®'°\ñ³Ë.ÕZK|¿¸?”)`¡A¥Ú"ƒ¦=ÞxýIЛƒªPÁS+Q}oÅL‚ß}«µÊÍhbvª%Åÿ$Î8ŒQ¯"Óð&¯¾KP/BOïø¢GÀÛèèÛr¨ßw]QØsKú6¡Ë[xðö3ÜôÐIõWô¸«©ôPo¨ÆM*õ€¥î×>Ñ ¬òÇÁá½Öé^s5,Öäýº˜®Ì…¥ù9H¿LªÇÑOSYBDÜgÂ+áøñÄR@ʰxçéã"’˜R„7XæPECy[žÖ;9}#]ÀÑá ì‚Ñã*‰Õ€§8­¢.ãôü ì§öû`í‹ó³7â~Sm–Àõjãy062 ‹‹‹0¿p¸®,LµW°Äߎ븡*OK¹]ˆ,>I WÿúošoÍ¡¹üûÚîŽx+~®ð‡ü³• ´Ž`Ñ€§O_ÃËW>˜š¾ íÀõ MÍv}¡NŸ>”oºÆx{Ú"‡466iÕÕµ0½° µ%%'^㤴 ž½rÀ6Mç ›®¤¶NõT¨«’+ ¹žJï{…Z+¬<ÀÍEÉ®b+Å ÆV9Çi(d¹L¯?ùz•á‹ö¾xðêsoÃÓç[à k;ž$p®õí <!–ÌÐÆÎ®ÊoÃ\y6L–|®½šì‡_·ÃÕïzÕ ^÷e VïL{îè·XýðîíØ]yÓâCštd݆öÛ·õ Õ‘ ÖžU×mÊÜj VvÚÚ†P+Z%˜_m••ÐÚÔ—¶½ù@=•«M+õ&½-zèâÓ|(H},XqáÃ÷ööàøø‰Ð!¬ë¦y!,^MøìÙ3mii ø‹¯*ïU¤§§Ì[òXÑk¹+ŠåAÜà”7ƒí—+@½ýø3ÒºðRÀRÍ–Ý«/€~¾6`™¤MªÆÍQ@‹¸Ü‹îŸ>°2+°2ëRÀŠŽkï¿ó´ppÅ®2+°2+°2ëK,mf?×§Œ€ujÂK… ^%¨:©3kXBÀò„`O|ø‘ËëB±,S ãËß J*`ÑÆ½^sÀâÛO—ƒ O/,žU¿§OÊ©4Z ø£Ÿ~ª¹E¸"<ÍÇ+æx³É@L S¡p\ö`²v]ç>X¡@ R‡o•7ŠìQDØzÇmÕC#L—Í-SàõGàÕ«uX{µ Ë+«Ú‹k°µ³›ÛÒÆÆkXÝØ–^mÀš²º¾¥­®mÂÚúkIœŽ¼Ú”Ö7w`ccøð×;Úö¶ööœ°ëpÁžx²§Ó§\â0âtûÄ´žß¿Ë sÒéáÜ,?~ Ôµš¬oìj¯6v`åå*¼·Ÿðc²¶¾#ñ}÷“llíjt_Ìè°-*ÚÜ­­mØ÷‡ìîJ;»;ÚÝ7²ç–Ä}#—"Ž#N‡Sr{¥=‡æöøÀ㠀׿â ñzýàçƒ@Ps‹ë †{`i´øÍíÑ£G°¼üvÝAÀÝ?l­oÃëm—¤Vã®ï(¯=àkíLëÐ~ñ÷ÿ¨½^^Õ^‰Ó¬m¸àåÊ&lì`sWZÝòÁÓg[Ú«í<[݃å•×ð|Í ãSw …:W µ56 žYüÛ87P˜2*ÞGV˜lÒÊË+´ââX|¼ý}uã4”Þ„“£^xÿ~ÂIñ\w¯sWv^¸MS€jkœm¬”¡jÛgl§³ç?…,âSøwUä¾ç?‡i+þrst šŠòaíµÐ9ÞƒMGV_ûáÑä$ÌŠûJf*²a¶\𫼭ÅÝ{ð[iÕþÛh󮇧÷¦µ„ïÓ/ûºë±œôÝÛ·°+^d¬YŒ[·ÀZˆÞj¡O÷q¯+ Ryù`/-…ÖÚha€´Ò66­v£`]=‡Ó¦õ°tE2wg7¶Û[šÀ˜F´§M~ü好½xzƒ·sÀŠÅb—–Ûíe X>þâ7MÑ £££Ð××ÖÕŒÍâ±az¯.ñØ’†Æz¨««…ÒÒ2h«¯‡p8¦Ÿ½‡ë‚ŸóJmïúP'÷¬ýãsíg¿øT?]<ž,ñ¸î1xpt_á¶^5zÃA˼§±ÂÐ|(D™Uâ ôU„I£9å^ä® czHR£bæËå0æáŠìyÂà‡y® Kœ]ªñâÛîJnZ-G«æ"g@‹ê¨ÖˆêQ~ü‰X"ľŠðxé4R£&çú*9Ñâ¥ÓW¡8\~ØÒäåÚ†øÖ¿ OŸKž<^=ñ@|ƒ%wïÞ‡;wann^{úôè!H…°"|çÏW¤•—ðìù éÙŠþóseåÅKxIÁ…¬®¥YUÖÖ^é×Ã×»µõ¶··¿íì9öÀérž;;»Ê¼lnnÁú†Ä!é¹xlÈòÓí±x\Ì=z*=–žˆÇDØ Ùã¼Ož>‡eñgÏ^HÏÓñc¶ò‚‚¬òr ^*«/_Á+ñw$üxð}¡Çäõöl+»»{°'‚q:]Àß9Œ¦=Vâô;ê|äõö6ln¾†-|A½nŠëÝØ”Ö×%~<_ˆ¿!y¶òè1ØmëH W§?øöbîŽþ\ÔŸ“â±NóXzôøPÈãÇmuu^½Ú„õõ Ÿ˜^UÈû R ‡ªJIoJZ[U5uÀ+«è´Be…T\\¬•–WÂòKÐɃ¥'ÒCéî½%XX¸ ssw´Ùù˜¿s7ÍÄÄ4 @ø $í]@›ƒ6aºÄÐÜÜÆp²®¶·Akk;´·uê—Ó®´µv@kk[»Bù’ööNœŸ\:­¾i)_¯Ä[&¤iï€v¥££ :;{ »»zzú¥Þ˸©eoÔ×7 õ¦ë4ŽÓ©ÃÕi†¯1d–ÔïCâï2„¿Ï°48l`Р‘‘QitLâß•a“ѱqŸP&a|L‡~#£Óp™œœ† ¦žWôó²­>-\þõ_k‹Ý½ÚäÔÌ•ÆÅy`BâËâãgfæ´YzN sâ9Mæ™øR@‡Á.ž‡„·Î¡­pŠKÊ ´´êZ¡¶®Ê*ª¡´\*W( ñ´âÀÐ(,/¿‚ºòRȹqJó²`uû"Ðìø`—•×TÆFÍgŠlê‡êýåTܺcp”#‹‡1ý¸ð ìQ½¤ÐYšÃâyJö°ãMi;‰oãæ^Œ,°¸‹j¯ˆëÙÒ婸?à„õenùùÅ9,-=€¦¦F Õk„ë¦Z•_7@]ªþ+‚Uty\ÅSoE˜&<5h XWM^°x5!÷Ã2¬'Ožm›Cfgga||¡££¸÷…,kÀúØý­£~xBQq‰þÉêèä-ðªBžî3Ö+H]¹Šû_ñô¢ X‡"\‘Xâââ1Š‚À½±8ÈeV&`eÖŸxÀ²†«“¿ùíŽx~Ñi2+°2+°2ë?°‚±øE˜"8ô^H¦•VDn >z¡;¦ ÏôÂõ´U„Ñc°N+~8`©øë–ZE¨,u>åÇOo3G}*4r ÜÓ +BEìØ‡ú`mn»àñÓUxðø Ü[z’î/=Ö-Ü}¨,Áü˜›¿ SÓóÐß?mmí@¡¤³³úF``p::ºVW‘fÖ$Q ÒûéÛ¨>"j¯ÈÐXK+´ˆ°eÅ—ßÔÔ|Þq9 >ÀÅq\tŒîÛõ—7çåiŸZÚŢδM áÛjEyý—î7áÛoÕ¬´´ŸŽ§üYÝ? ~mtbVûÖ·¿§ŒOÂðè8 ˆ€Dzû‡€kéÒ>ÇõbŠ0ÄN×A×¥®—k¿ÌAÇ*èè=T8ɰhžþ´N9Zƒ ¾Ž´jº ¸*^š"S–§Lk?6/¯[:Ó”ÆSz¨æM9XëS¶ úýåú„Å–víÜ´·àÉw¾£––_ –éÁ”ûMñv6éx«ªÌS»ºôV4=ÙДvI½­èþ––UHåUÐÒÖíÝ#ÐÑ=¬Œ@—W¤ KåcÏS­Ÿ®ƒC„Ò!>´HîÏ (ë xöb[Ÿ¶Ûv§5NÞ”^#å ÀñÙ=ÐÞ·ÁþѪ\Çix3v_äøËœ‡ ÔÄ—¶Šjþ°•¸ÞÂ@/ÔäçÀ–x"n "`9|qpöaχ§s³0YüLWdAÂïS¾0$p«œ?üiDíKUœ½yó¸ç­„ëîî~¿0¾X¦O+ê sÓšf¦fnfkAú¥€»¼ÍÞÉ=¸ÜKé€IDATÛ"°k'w7móA¢çF» ‡Fà’¸˜ß;,qýkÛml\¬Qšˆ54 Pú­ƒ±‰i˜˜šÕÃÖ°8=ÁŒŒŒN_f·P„§¸è ÒÕÕ<Ò022¼‚ŒWŽMNMÃììP¬gÏWà•x‚“•/`yù),=|÷î=€…Å{€]ú ¯;pçÎ,.Þ…K`yùp/(ÙKZÓ­¹W¬o(ë û/­ÀÓgOáÉòXzøîÝ»wïÞƒE^Â/`î¼ôp ,=€{÷%ý2Ô}2[XX^íF+ை›˜˜‚±± ÓJ?iTFø4Óâ|„C¾÷ï·Å|˜<üðmdæûo½­|ùós¯Ø›žž‰É ¯F¼W]«~÷{F+†ï~W©©ÓCÁW¡¿V«¬ªopREE5”WTIå^Q)ñéù2ôË1]V¹8]y¥q]E Ðå—”VB)­Z;¡³g\UÆ §oÚÅžo¥P.¼Üpƒ7úî.>‚üÏ?.zÓvÝIàþSüûžYdדOБ¤Œ%ô¯yÁožÍåúŠíè YìX G¥ˆø’KBá©-ƒŽ†zз«ò'ÀO!._ø\¾$<¯e2Uô<ì¬îpžž,Þ[ÊÄÿÂÔû÷iÒÃ[ûÿ8œðê:*æ&Ü­œÃ¯àå•rÔï‰w2çé=ž2ã)´çÏŸOÃÑŠ>žÞûu6à¾.`Y‹Ü?°hÃg ẀeíèN‹º¹_ÕÑûañãÀá±³³ÌEëeeeÀ¥ÜŸõwuu ðt!¯ -..^mÍEï"\`Šð9˜wa?BÈ2zh¥Žd'wO¼±3«ˆÇ'EÀó_ᦙ:ñâ&\›L‹G°x Ë+}Õ.í8}`Œ`É€¥7½n»ÓårCQ-gšF°T  KüŒÑ*#PqÀò‰ ê>q¤€J¾QkZ¬ïâ2B5UcSzx‡a™8`OΊÖ´2•fTüqÍx™û¬G„Ò=ˆÞ¡Ð…E=ðð‡ê}À^ è4|ØòÓexüø1pÐX¼»óô¡lB!ë>­„8|Ý»{îÞ“xUÊÊÊsØØxÛ¨W/.ý[ÌÆ†¤^lÜ„nkk3Íææ†~Ü«ui ÛÿP8\§âJž=Wžxçv^¹ÂÛ4¼xùôËP X?~=Ö~üä)<|ô8ðpxá;%Â,™˜0Ú ÐÏdfføñ(.ŸpcWšæ&¬ù˜mlnJl ¸Ñ*sŠÃ ?VÜö™¸o„ƒô#º´ÚUýÝï?x€ð¼ÒÒª½ýºÑŠáâÛßÖÆËÊ1RÇ#Mմ±šFª ¦®Q«o´C]}‹…l ­vÇËÓÖÚšÓÔÔY5¥©µ™.¿±x䪫o\™HÓ;0í]½z`|º² .ÿ!8iŸ°ù:U…yƒ…Ÿi•ÅÅâ}% ÛÞ8¼vKŽÐ¸ƒÇà )¨µ¢=@÷PR¼`H¼ßHÇÊ©¤‡“´=ÖÄÞ‚Ï{þM˜™£‡à¥ "ÎëA€®;~¢o¯ó\<‡ÉDÁ p=FÈxwiç¹Ïèu7þýÇw×ùr÷×»kÏÅAǺrÏŒk¢þ3ï?,¯ X«Áº*`Y÷$üЖ9ùsˆŽr»kÃQsÀ²î›È#ZÜŽk¯8`ñJD]äËáÓp3Ò’’x½ã„ãã p½\DœV¾‘Ï€·x ýjúóOõíí¢Ñ}ˆøZÈå‘xÏ݃sȬLÀʬ?ò€µÜÙ­½1oÜ,ÂÕL±Z0°2+°2+°~+š¤m]δHB S¸"ª6+˜4šsr rÄNàÒtžeE 'u¡íy‚ ‡²äùÕD€z¸º£ýùÿüÒ6n¦-znçi?ÿ寴ŸüìSxùjWsÐ6"ÌÝÎ+Ö~ö©8îÿGûDXs„5…@ñs+§H÷™öÉÏ©ýT·.Ž£>W„š‰’ m}“«éþ®ïù0…÷­oG›„± E®qª·Ÿ2:a+££ÔàqxÊhjz&§$nÌÈx:ЦùøgnÉS;Ü ²»§¸FŠ7É¥º-®õá9|V¯ØÄ“ÔÛ¤†›R§55ÖJ 5R}´6TC·½ºê`¤·FûšµÁn©·½º[mÐ×QýÒpoŒõµÀˆÐßÕ=íÐÝÞ]mi:[¥®ÖèëjÖF:`Lîk…î©Gê·ôu6A¿8ï@¯úº¤ÎÖ&hmnšê¡­Yjm²]ýOÚíÐÝÞ}Ý­0Ø×#ƒÝ0>Ò§êI396ã£Òè°46<#ƒý0Ôߣ Jô3ìë–{¡§§x¸œM‰ un WÕ5˜ŠæÚ7X•ÕµP"©oj×:z†Á¨}’xŠ®«w :™>u7*Î'µw ¥i£U€0¬ AGÏ`E š äËç ÕÝ?=ýSÐÝ? }ƒS@u…W·¼à ÷Žò(ãCcÀý°ò?¿!¾tÜ/õ¢2õ£Òë¦èKZäTŸôGÏ @_ØÂàò%À+¾ ’äaŽO½HÅ!"Þç AKJ½ƒWâC‰Ôd}ÏŸ½žBô…ö!FåjCyÞt>$Þ›Éóù9X¨Í‡‹£#ø` ÒEÂúO$­L»áwËDêÂ),žf¼®ë·°ø óU‹K:¸7å~X–Ì˺4Or%¿Çõöö@__¯¾*ÑØHyy´uôo©ã^{ ñ˜x\(2%ö¥8mƒ'DS§"ü‡ÈKç¦Àçï17ø1àÓüÞ¬?û³?Ó™Vy­øFÛÜ,ZÎ\%¾ñ~~3 «‚º:7wŠË=‡jñ ú‹ì¬òñm¸±]sS÷x¡¦¡M»)ŽË¬LÀúS XS5µÚÙ·V §ßûžv§¾Q¯õˬLÀʬLÀʬßaÀЉPA8h…“§ ¿Ó‹9.ñ÷´â^Sú*BËT! ïyBÀ¡LßÞF+½»)hQÀrªËýëïÿ½¶êâ<ÔëÙ¦Kû‹¿üš¶ç‹ÉãvƒúfÔ+¯}Ú_~í¸ ýý¿Ó^îøõ^k¯½8Î/ %%*(Mðá¹öz/  ‰Póo~K™€qšÒŸÔƒ‡(.pƒ¾!};—NñF:”¶Žà(zïUXL+§¸[wEe…TVåe¥PZ*•—CYI!”—k•¥…P[–/•KuyRYØJs¡®, ª oiµER£ø4”Þ‚ú²ÛÐR™ ­ÕR[MLviw‡Ëaº§šs Ç– }M90ÖQ£í…0ÖY ´æÂ€8žtÚ² ¹B²‰Û`3ÝŽÆŠ[ÐZ“£µ×eCOcŒ´æC¯ø™t‰ãHg]. Øó`˜NÓ ­UÙÐR™|¿ìUéøøæŠÛâ÷lh­Î‚–Šlh,Ï{µÔÓ˜Ãöh·¡9úš²a´­ÆÚ¥î†lh÷‘4”KÍ•·Åá9Ðß"õ5çAw]T–—@aq)´Ýº¥|ó›é#WÍ­Ø!€ß|¬ú¹ Ìmmz â°ÄEå½qeІ6 Y {YqxãóvS²ê—z&¡áJz‡$ZË=»V×]à ÷˜ò†N`}Ëe9Yuã†ÖRß\ˆÎÓnF‘úðjk>×O…ïžûÆäTÝÑÑ.¼{? ‡'ˆˆ÷T6}ÁMìKK#ƒP•} vw=À!ÊŒA$z¤#Ê!DÄí"ÏïÌÁ‹Ñ>x¯½ƒôÐ`Ý*'óïfÅä—XEx]'w XÑh¬‹·Ì±¹›7}¦ír®Ú2ÇÚÑð4¨_׌·ØFyθ^ôÎ_.úô¢y]â}Ž4ÒÊM¡D|~’Ç—Á»¾Ñ衟ÿñ¤‘…¢"DÕ,<ïÿþÉhA‘OˆW„+âvx4×® <Þ0p®øJÉìL¿£Žàü¦– °8Ðèô(U“¥VõQÀÚájë¬몑,·ië«_ý¿õã¡CmO|#üêW¿*.3ŠãÐøT=‚ÓóùbæF£'8Ì'n Æ™¼¿°&ÅÔƒGOµïÿÝ?èõS xÿ9n±ÀÍBy½Z½ –±óRmup‚.+)†òâ"¨(.€ª’<­¦LªAˆ4T䃭<* ³ º8jK²!©Dj©ÎFPH}i6Ô•ÜNÓPž ¶R´ŠoCC¹Ä¡¡C|p“žz©W|ø“¾z‰BÁH›ÄÁiº»†ÛóƒöÚl©&[ëo.€A{ô5åCKåmh °GÔmå ×F«F^‡¥Á–<n-¾=}"ä!{Ìô#’!{.LtÃÂ`9ÌöƒÄT˜ja«§>ú›¥>zH[µÔZ}ÚÄéIMê¶å ’Nq~2Þ]sý%0Ó[c…Ð-®›Ðýía•LvÂâ`)ÌŠó‘úÚR(.ßÒ²²D¸úVZ¸ºÛd×÷\ä¶Ü&[<ðŠÀâ’  Z¨ÖÎAà¶ Ræ-3>ŽƒÔµÁJQSÂôGL~žÁI U„Ü õÅ+¸ü`ì(ñHÖdO´æÝÔ*s³`emÂ⽊øE¨‚è±"Û)¸ ð÷Å›í¡>_D Æö!?GÀA(’<¡é Á8 Õ”B[]5píU@\¡@Gü‘”þÞí·„Å·q²ñð!DÜ{p©!çû+†¬29ëæÕzq»‰Õ`qÀJ¥R@«?°xk{{® XÜZ‚·Ì™ŸŸ^=É#X<òDuSÖ®ÜÂbñÞ}E ¿Ÿu´CÿÀ€684\ÛÕÛÛ\“U]] ÜÞfëé‡Sâ5yÜ"%L«{)+ЗUçø£ŸüBÿRÅ{#\9ÜàöFÀìtüû°ð»)`:ŒýoXn>½þ¿°h˜Ÿ³, V,°2ë=`5äåk‡¦š+êÐNájdt"°2+°2+°þ+OF(d ¼²% Âñ zÀRaEŸÔõp J_6`ñÊÃ+G°T,ZA¸ØÇmö|iKR#XÆÊGÁÒk*TÐb°0äO+ ÅØ~òsíñãg°x÷ÌÍ.×H @W‹Mj®Ò:›Ê¡ÍV õ%ÐÓ\ M•9`+½ zˆ é¹òl¨/»,jŠoAUT'B±‰ ârxŠŠ/«®Xª.¼ UERm‰Äa¥EÜaõâ|õeY—¦»zòÃËPkžd/кEð"íµ·¡C„'Ò'‚SŸ P¤…¦Ú0Ý&ÃSŸ4]G:ê²§ßø´üx4”ç@S…DÓvm5Ù’ 6B‡¾]lÈ0…/a¢S«–|à°4hσ{>LuÂ(5¡·!è¶NŠÃÉLO)Ìö–Ÿ–ï›Ò!B%ÔfiÍâ¾~8É´qäΠ47Pc…Ð%®“´×P̃)®ÈÝáR˜ê*†šÊb­½0GÛ7…«³o~S))Ó·têí„V{;ðÖ5¼ýNõµŠÅy­"lië®›º?¥ùD¸"Áø©DoÎôF?@ô¸ž‹C½y'öÁ»· YŸÃÈÀÄ÷ß‚_7âñÇÁNj¡Ø1ðÊÂPô<;;p.>`ÉÕ)Š—f‚Õã¡5`}¬ÑèU‹û~ý:‹{„]·e·ŽZ·âÕ„CCÀ«ú¸Þ‰ÏÛÕÝ© Áðð0XW)rX+*,„¹ñˆã‹ X‘øpÐâ×½N,¿x| píŠpåôÓa¿÷ëoÿáXÈëÙ†Sû_ÿû/°þîþI{…Uƒòz_¼öiÿë/þÁ çÛ ékU÷¿Åq™€• XŒ áê{F‡ö7_ûºÖóÙ¯42+°2+°2ë¿!`Å©J^à©B.ð2-_Tò`ó猑ë–C¡ ÄEîNqƒœW¹›Û4¸9`ÅŒ­r*lv­ª¡¿Ó6•uÍÚ¯¾ÈBÀªÇU7´éS“Õõ­ÚYù•u-øÝzœÞÁ=z’†–Üü™îû¹öï?ýTóû" % ‘:•ö¥€?‹#p¤F»?jƒ;ƒu°0,Ý©‡Žº<°•Üž–£Ä‡ñ´_uñm©èð4_}i–’­‡nUù· R„+RS”|¼âPО M"T5•«a'[Ÿn›ï/žÂn5Џy °™ŠÁ+³ô)2žf´WgCGm ·êSp–x BÑC£*önT¨ž"Ä7Udƒ½æ¶$nƒ·%xšo )O£"{¡·)øú9 ŠÓ˜ ·À8Ÿö<o/€éî"˜Q¦>´w‰×)‚á°7ÓS³}%0)ÂëBlŒ´å‹ã‹€ÏÃál¼£@[¬½™®.¾ñ màÆ¯´ââR†ú´É™yèéÞî‡oÕS^V ÅÅeP]Û 5Ù»ÁÞÑ<½§¯&TÓ{¦xÊNš¼*I¨ÌäÔ_:cJP^FWÿ8töJ|´…SmË#<[u÷»ŠC„+â Áæ³pßV 5d%ùy°µë‡­JAËOÛÃÇX=HÜÔU=~\àÊŰüfë_‰Û—"‡@+ß¼{ÉP-ÔæÝ€ÇW žº€€WÄCÓ’ÙÍÝL‚7 Ãûp~~—WÑÉýq2Å홀õÛ X¼é³9`Y·ÌùXÀêÀB°«‹ÜÛ:Ú!Š}g /× µÕ.ÂT?ŒŒ Ñ {äŠÄâ¢"°76@$º¯¯ŒÄ!šgât¤»!zó€o¬¸­_¼þ¦|è°å¯.lS8ö‹°Å£^u·`´#æú‹aª»x”ŒÂ™ê.ÑGŸÆÛ•i~ î ”Âh[ ¶æC ]~)pÀ±çÃýª›Úù·¿™6-ØùÅM­¬¼h/ÍIj"ôöïSÉû`ÖÖÕB¹8=¡`Fªk´Æ–.hn룥‚ÁâQ¨ôú©©K¡é*—ë©Tx2zéÁM­&ä@e­ëÆiÆšì°òÊ ®À!8üo{ãŸÂæ³U@Àʽ´¢Œ C0ù8D9!Xôvw½újkåÒWªš,_ K>jPŠv´ò/ ÓíuÐP–w¸öŠÃVÉ`•·? ‘Ø\YseÞ.ÆÜ$3³þ$j°~ËãñÀu‹¶NãFаxËÞ“pzz®Ú2ÇZƒeµ³ë€‹ówÀM>—î?ÐúûúÁ°ht‹ð6Z%ÅÅPS] ‘¨¸ïâ³ðJ\1g¤Ÿüô?ôÕ¿~®ˆ{ONWô,‘?ü¿O‹G°8$u‰`C((QÈ"\{Å=«ø¼|}|m"°º¾qúS½I®Û¢Û)ŒwIƒ-J³ظßÖTw1,—¯äÚ¨±ö| ¯Þ¦BèªÏƒæBàiÅÅ¡rà 5ß/ÍŠ0ËS‘Ó=…°(ó“¿2 Úß~ýëÚøí_iE"‘ššz˜˜žÓ·eêï„®î^à>Xu¶:(¯¨½«¶Qkn醖ö~°®ü³N rxêšÿ_GžÆ˜4«>ž:Lý³Òk½d—~¸eb[MØáåº <¡Cp…$ø=bëùK €ÕTÙŸÝ€ê¢Bp¸cÞ /^lÕ¾‘õµ ýË©Qc¥¦#Çà7œ^iïƒÛkÛPŸý9 tõ¯@<pº£ X™€• X ënsVZ¸z÷õ¯iÓ7oh¶ê’LÀʬLÀÊüˬߗ€ÅEî¼U÷T S?lDzªk?,c N-ËæÏvvv}ào0„¶Ï!žo¯ã¾oö¼ç߇]g8èéΰx*0v úJž„Ä›YSï«wrOÈîõ±ƒ Ôl…QˆÅ ux‡"\‘h$K“vX®Õ–&lðhªî‰Ã`¬º  ¶ø&ð¡MüLa‡ð4_]±¢BRCÙm …߬´£¾YBs•ÄAŽñõö6ÁH[ Lt–i3}i&»*aa° x¥Ü@Kt7d­(Ô{6‰ðÒi½!„pÐlÉîÚN«ù¸§¯N쇓¡ö<ÐWÓ‰ÐC¨“:¡Uˆ\´ÞV“ \ìÞP~ìÕ·€Vïî¢N¡«O#hb…ÐSŸݶ\è¡T ê±åâvî?5ÕU ãE0Ú& ŠÀj…bW}¶Þ#«£. Œîì¹À÷åW¨½½ê6LVÝÒŽ¿g„«÷_ûšv/ëçZuy Iv{;LÍÞÁ&äd``zzûßllõ6¨¨¨‚’’r¨­kÒš[{ÀÞ1í=#`ädX̰¬+Õ†ò2©xž§"[;ÓðáÜ^ŸªT—)ÂÆfXÝp¯¼ÓûSàW«¤·W^À}[¡6ÓÙ …·¿ÞFgzröÞÁêËu(úâW°úò•–ØÜm=;ÞÊ& ¾sù¡/¸KSãPúùg°¼¼ÑÔœœ­A<éo0.‰`åñKnŽNÎ@MzQ»üýêé.Ï$«LÀº:`Åb1øMo™óèÑ#°núÌ[æðTmØLÝÜÍÝ€Õós pvö‚{ˆ…£ÚÒÒ#à~X¼š.—pÁ|YY”*´ñðä-$Åg<á©Âhò""7Ð!¬@h¼œ&®𗪯pJ£=­$ÞKþ")ÉG+ wTâ,ODQµY´Ðhtϰ¼"(‘KJ`]°Ôh¬g¼ÔÌ”¶Õߦ­|Üæ+…–y{HI2`Ñá'œŸüìS=`Eã)à€Å#Y‘pN5ÁÂPµöp¼žL×êȓiLvWBku.tÖ@Ÿ=}ÍÅÐßRSÝå0ÓS ƒÕÒ4Ó_w†lÚü` Ü’ŒÙ¤ñz¸7Z'ÑhšðxºžÌx4%Ý¥UÂdW, I&𠀂ƒ>Bã<*PÌô×vä×]õ7åêçá,|8×muÖæ¯Hn/Ôk¼ø8n`ÊõZ"tºÂõN×\uQˆ²åê×Ãíxe$¶ Ù%ª?›ì’¸‹ë¹øú9PNˆ`H8hQ@äZ.ZQH¸Yjw4@µPÛÿp-Xwc®ÜvGŃ¿ù^ÚÈÕ½¬_`T¯¢¬xô©·w(`ŽOÃàð8ôõBKs+ð>–å"\‘b®H­­IoÓ ,5‚uõªAk Ö5#Vª^ÊÚ¬”W&Ò*ETvÖ>­R{ç0«GB%oýójË áØ!„°äZa‡Diä^p¼\ÅÚíõ“eè¶·@–W¤,?|þ(¼ÞÚ¢[ŸÃÚÚ¦vpøxV –8‚µù)Ø8„ƒ}‘#ÁpRë¯)†ê¢\Øs—ŠB ð25‚åï?´ˆONô}ôŒ„ue+†÷¦±«?”ýõ2ÿ~7‹Ž^°Âá0pÀâ,§Ó ¼e¬õõuìGhޓа¬[æpÃQÚ.‡›‚^ XM000 ‡‡çØÞp ¨ïÙ9 ÂF˺Ü^à˪¨¨€Â¢Bp‰×Úñé[à€Å#W±Ô D9`©Ù¾@èü.Bq¹CÀ[keV&`eÖ@À­º¥%ÿö¯Œš«o~C[*û•>mš X™€• X™™€õ{°.4ß?Sä”a"uô3,^MÈ¡Eo<9“8p©©;Lë©>X.qeÄz+£öê:W­½@Jr…€Ã¯P4V¡ÏG5T “x+ 0¦AOµ5WÕÖ)ú~Q¨¤2C8¾<7›:¾€P0Æ›`‘ÖD=<™‘–§`Èà ðjÃG“õðX‡ÒcrÈó¹&Xžj„'ÓÒ}˜MI’ãŒêaI„9ò`Ô&© õp\âËæÀ÷hʦ-K|ÛOÕÁÂ@<«„‡Õ0ÛWwKµù¾˜ê)n@[Ò^!Ç¡‰ëŸÚk³ô^U<ÝÇÛÝp#SMå·`°¥( qSPž¶Ôƒ QS½%À·c\„;BÓ„¶\à £¹)_O]r¯+g} ùâô¹ o@­êÖ)p |}–ì´êQ €e¬Æ”ÓŒÃâöîÇŵm¬øq™´eiÇó]ÓÈÕ×µå’_jã"è µur'eå•022S³Ø&Ôaƒâ͉45·€ÍÖ•P\\µuÍFÀjïžš³Ö`uöO‚1íGk\‘ÁÊÚx”§÷¸‰©>ˆŸeâ`×Ö©p.Ôa‰ëï…Ξ €ÅÛUmîx ž<~£Œ%ŽW ¹^­Á*lVžI».(¼õܾq榧!ìöÁ@y!lolk‡âÍ™ð{jrÿ–ûÚaóá88~û'oakuM+ÿüŒˆàK¢êË-oälôGÂðöÍ’øØ…pü2ó~™°>¶Ùó—™"´,¯× .— ¬›>_°?~ ÖMŸy˞ʣ€EÍF¯j8ÚÜÔ¼5](ÿÎ6D!|Ñ!°¸ÏoþÌõ]••PPP»»n®ÞCêà øõ?PDN „ „÷ÁçáÊå7\j• X™€• X¿Ç«»ì¦–üû¿IWÅ7®2+°2+ó/°~‡†$]àÁ™´ô¢wÓ!O«DØ!Ö­s8àp±9¶Êq‡@_׆*YÈ®½£PþT‘Eó{â Üaàó¸Ä!úõÇÎ5\ Ъ á¾EBâ©C Xòðô¼ 2äoÒÊÁêàä „C!¸GSpÂÒh­ötºVî4ê‘cµ’ú‹â)l- Ö¦‡#ÀÈcq¹d~°æªá1: MSF€{(n¨ó>š¬Sd »?b“FÍämâiÄGS5ðxª^Î×ÁòDÍÿÏÞ}¸Çu]g£÷Ÿ–Äß—Ä÷¹¶ã›|¶ã*K–cËŽ7¹È²$’`A™ÁôÞÐ1˜Þ+z/ìEì`Ø»(JÜw¯wŸuf0)É–óÙÖ™çù=ÂôÁP¼8{íµà`Ý+`¡è„ɬ Æeø"S…ÇÀ$Bí0@AB/TW;¡NàÍ:ªÀ›—؆d8ãÝ‹V¸ûyqÀ¥h4ÂQ; ÈhKƒ¼¬Çø<ü¹ñüjI’ûrÐàgψ8ö*F;¡DˈèÖ ‹_ÓdÆ ³',V\°\V8hžðëŽM*G'‚°\u.>’U‹™H;ÚÅ;üýû€[+p-T\†"ÂA©Š›“r-V³;ô–µ˜ 8€q[QÃ!¯_†7‹[<´ŽÎòïQ|š«‘P'èaH iñp'pë‡D¤Jh±Ð ú2ùºíâ÷¡0Ð)ª‘½âîG®žÉpµ°û×(vi \sfëÞA¨ÔÆj¯© dryHg ééÞuטM:ª ö@ †P¨‚=½é‚ž¾AÍ0ôõ ɰ3 ½ýC _§]ÞC—Iy?ÒÓ;øZ»®õ¾|¾ }2ôÁÀ0ôôQí†rN~ð‘ÁÝ•»à PdõøawÊ€µ8 OÞòC›„ä_»düœ =æ˜ šáò¹sòÃùxøHóðìÏôÃiù¸ä)0‘Î? yÛ^±‰Âõë·áêÕpíšróÖ¸.?‡àú]¸‚°u¿Y?TŒ“°þ7}QÀzÞLÂíŽòX›ç5 †‚°²rÖÏŸƒëâìùKÀGÄZ×wyiÂFï*°ø±8`ñ.FÔˆq¬GÛZkQ97T_œ ­‹Öú»p‹zcÐaÃGOa}c¦ !O?£Iðˆú\{aRÞ¦Y%á•< ŠN+ÓÙ€’÷À|1¼”¸HË’Õ0è»5¼$ÈAŽÏs½)…zpËú`6ç‚…’¦óNØ_÷ÀšG.'ðá‚ `dQ³TRædP#£±nˆiø±Â»éx‡áHh/pM7 åž[$Õ£p]dMtƒ^ îi¥a‹>’‡×<ñ`h$ÍáeÀÓtÕæ¾[ü¹¾.à@5ìÝl@«Ã"‰ˆÂ÷å&©Ô@5nÛµ©Ù¿ë×ú{Á;½ ×oy]ˆ%RP®A®P‰d 2™de¸"½}À+ jT˜¢ÞX>@î™ÅËŠ¿hd¬ç5å€U«Õaíô) ?„V/o+Æ£xxùÑëõ@{{;,,­è?¿÷e¸RÞרóä׿ûÝ›úå·n?„ë«—Åk°±q îÊ뉰Œ€e¬¿€µ·‰¯¼´)\-íù-:ÈËXFÀ2NFÀú+ Xw)\ñ‰8\G5ü{ÀKƒ¼ÜÆÃŸWµe¹K-h‡+Õ{TqÒGäð®B­ÈývCkèâ€uV†+²zû)ècv8è5í"\½þP£v>®m®šÖŒ®à%Âk°~ãp •síÚ:ÌäƒJ–æP.a|r¦fæ`bJ™U¦gºÍÄôLM)ÓÓšÉq˜)…a.ïî±5GÁ.^"ä¢w.nŸ)(S9/p¯«iÑû` éIÙãi'Lç]P‰Y¡·ÃXÒ®w¯DmP•דѤ ø¾Ë/Ìdè¥íík‡òJƒ]¡^X´ó‹Û} Fwcç¥B@ ÚÀx°2ý& çÑÇèh…âî}Ð#CáñEaçnè—×Z’ëqîQ{A_^ô)¼ÌÉ; УK¾^yŸˆK¡byBË•„ºÊg¼m›–ÉÁ¶×ñ:iðs^†GÂ!”wBÆiÄt› ÚßéØ Œ „EØ}aô†½ö:!è¶AØç¿Ë A¯UÜ _·—]ñØ èUâ±ÄB>1$›Ä‡F —+¼¼ $W(C>_‚B¡¤Ë \©CutÆGG!Û€Lo–—š ÅÕ!üû÷Á½‡JkÀZ;~ÆœûÄéÅ9xøè}¸ÿ1<|ðFkUðì~ƽ]°0V?^^|ðè1HÁ¹¥9X¨–Á´ãmˆY÷ŠwÇ«pW¾Nrñìy˜Ãõk×a}ãŽB_“õ›âÉ“÷;¶'ãô§¬Û·oëK„ðQK„LÀÚndËyÑÈœp$ <çâ»'Ëù7îB¡PX<Šgppü~?pÀ¢ß³?ÞW*<¹åDëÁ£§pGþ!Dn¬]·ÖoÀë·7Â}îŽüË´5C7èŽwiÇM†k¨Yz¬Ïðb—n¼¯è£jTСÐÅK?Ï™A¨»½ÕóŽ`µ¬M¯«¥ÑèÚ' }MG™V‹Å»)PÞ¼£^]]‡÷á6P ¤Úö굫0[ÁLÖ-¦K;“+aéè,S–4ËGWaéØeý6KGVº —¯ÂT= õ¸&Ò ˆõ¤2‘ñåjÔm©úª¹¢Âífóf¨|t‹&lP±B=aSbv%nµ˜8`•GìP—¡«¾)xY &ƒ)Û$;dúº,q­RL#ÒëÙÑBA Gu"z£ÓtOpÀéuînDÚhøÙ8b”ê3Á€oô8”ˆ U„ÃÑ 5.u«£lñpð³bÔ ÜŒ”ÇßðE>:‡š)­¦+-Ÿ“Ìpï7¿¿ùÈÕþw~©?…,WÄï#?‰à^àÉH;ð®Jj²Ê¯¥×­ ù; Ñ£däk #ÁNРbNã^HÐüÈžN}N#×¼%iü¡ÖÒñÃ˰8죎}ðîÂl\¹\§ÀGÍoÝy7®ßÚ­Ãã©®È/Âuwî=†òçŽ,öyaªÇwoÞ÷) I¨î?x €õ!¬; °N.ÎÀ£÷žÂ½{á¾ W„ÐxrDq·ƒgïNQödýúMà_bG 1(øm`–¡ŠD\.XÎÅÅ‘zîÝ{gO‡Ÿ®^Y‡+«kpúРQ]~ø Œ“qúSÛ4ܽ{¶ XW¯^… X'NœÀ¸Âë£fòÈœæ€õ¼‘9<î†owêÈ! Ÿ…[wAµZ®ÅâQ ×B–ÊàÆ¢\·ÕèÃEÁÊz/-­‡Vª·’aLe0-™ %Ä”j2p‘ʈ ªQ'äû-Ê ëb\‰: :b‡©œÆ3vÈË Ú}’=û`$°W†ª.e¨x§_¯[és)¼ìÇ»ÿh/lß H2²¢=ÀK’<ö‡GùPÏ.îÉ5ìß ñP;p=U,´W î½^'¬Éï•\8~–3#pïvscQ{cT`§?_ÀZ[[ƒÖ¡Ï'`ñÈXÛ }æÁÌÏXaèë탃 óp]þQõO“À‹Gñ G! ï",Ëâ½÷?„{ßÓ7Lõ8áúú-=¤Ü¾ÿ>ð!O«¸!¿&w>…‹Gè XÇe¸"÷}wî=Tî<î¸vú4Ì„¬P‹ˆ€Ë;Þ¯cÝù6 úÜpâÄ)àeÏãSc2\€_ÛÅsaz( k«Wàüñc°’Š-Ü„eœZŠÜŸ¢²XqÃLAK¹`"£ð¶ey²¿F¡Íãi‡¢ÝfRÞž¨ùa"ã€Âfd`Y©û`¿f:ã†RÔlø9xWçXÒ%Šƒò½ë7‹[?xyS¸ºÐþ[´°˜Î¹a¡äùZ 5yå9‹e/ŒÉ÷”‡­Àï÷þQ/› À¡q¿Téˆ#áטî5Î"Fütés+#v<ûp$´’ávHG>:VNEa^†_'Œ'cJ} ¦&§a~éð‡áÂü<|B:¢9xN[…“çÖ”wOÃB¯¦#¸xyCþQø6äá¹~7ä_ dãþ{ Ôáì±c@G°Ž.Îà ¼ÈÆ'š‡pýî8{â$Ì,pþä)±zý.,< S3óà2›¡V… úÈeùÇ™—ÁÏ2@³PÉÙ3ça!Ö.^…GÃJ!¼ÿtK 2ò•qú¤ëþýûð§¬Óòòî»ïn X5“°X,m¢q9/™ÓÛÛ‘päâ1¸rõ†¸óäCX9t8`e2 ‡· “ºM&è×ï?U(×äÏ9Y×\»ûžø%лû>¬Ýx«×Ä¥Õu¸¨Y¿÷>ËXFÀú Xå~“¸þ“l Ww½®÷3–°Œ€eœŒ€õ7°ÖnSˆ¢PòžÂ!…Qºý2ˆ=Pñy }}æüe¸DMF‰¾‹P=ŸxxÉpÓó«Ûrø:wù¶ri®Þy |_þ^Vº”-¯YtO4ê±°èõÜù@¬Ý}*~þ›7Å…Ë×áÝÕÛ°pìdëû!^^†ÒÔ)(Lž™Ñ£¨€Lý0Ô¦•âä ÈO½ …©P¤¯'•Òô)˜=tR•yˆf'”Ì8 &kÐ+‹H4n¯|–6(Ë_úeüâÀlÞ¼‹j®–kaàË8”Í•媲_(r@ õÉ˽0Wp‹¹¼8´ðÎÇZÜ•˜]ßEÈˈœ’SfäãY¨ÈLΡ‡8Mx©r¥æ±¤&4¨1j–1i§¥æKnX©ù€—õ&dè"|=/!ÒNG›G&°"ßÂõi£ +ð²jIH2“²‰›?ûѦpub篱Œ9*Ÿ“p­Æ” -­J…A«¾Ón8yâ]qE~n(ï½TÄ£Õ:p°º|óPóׄü…BÖn=„S§.ÀR´Î\¸G‡Ã• P=û+…Ï´J,ãd¬?=`]¿~®]»—/_†¬còrøðah X­CŸK¥PÀâ±6ܳjKÀb% ™YqúÔ˜›[„L6éL8¼õô„Ájµ€_>ÎúíGp]þñs]û#ˆ\¿÷rÿCñ«ßþA\“ÿ%t@†\<¿*ÃÕ5¸°¶×î=#`ËXæ€Uí3ËpõÃMáêÌï¡×‰ËXFÀ2NFÀú Xz8/¶Ž¬¹³Õªî)l½\Y“Î]Z‡Õ[áò÷B áe>þpº|ÿ¸r·aýžriý.\”!‹\ àªfýþ3o€²þà™¢_÷\‘ßá9¤ñcðr¢Þ}^ xÅa*·Â’|d^¾y„>¬T}À]ð3=]b£e·àÅ¿T®è^äÇæ¥K£©¾n=`U›za‘ñ´ ä{Aø±8´Íä’xs‚ìäýH®ß üü\(?_’!VÞŸL¤º¡4h‚T_'ðpëAêb/9öígÛ;"ní„^ó^0íx ºv½­yK£Î›d¸2!`½%:w*zèÂ×ò²J׎·!ر2ÎN°Êûî}KÙ'¦•·4ê|»üÞyœ{vÀ˜ß,¢xä‡.é ø!†r¹Å<̆mpôÀ~qqãžBEêÒ•[Êb¼Ž-ÌÕ{ïÃê­‡PÈD|$«7ÁÉÓçaq¤N_¼Gƒ û犖±Hhœ>aÀzøð!|ëÌ™3°]Àâ¡Ï‹‹‹À‹‡>—Ëeh̼5`õBoOz¨Ë»4 @ʺO¤»Û ãµAN¶\ÓÐgX½}½àp:ÁÖm‰(ÌåS0_.Áòø(˜ÿñÊ+zyÂé3—”§Å™Óàœü%×hƒ ô9Nc@®iô`²ÜþCj>€+òkå©¢ýwùöqþÒ\ºþxà…À­Î\¹§V¯Ã‰ ×ı³—áè©K°tà8,Ð_Ò²üšÌ-†é¹˜˜Zõ±i¨ÔF¡Xª@>W„T* ‰X(` ‹Á¾| ~åK_™|l@¢4 lå%à€•;"ÒõÃ=¦9ÙÊ<ð­ìèeì0¨¶‡ù8dV†+Rž<™Ê$ SËŽA¯4Çø-mPqÂTÖƒZ›Ì+ ÂxÆ µ¤K‰; ©žvЧjGß8÷‚ZÁ%ný|ó²àÕ=¿–ÿ.h ü=MRÈÄÑ0í}LÚõÀÊß'….’íW*ÃvÈôš­y+튜–ïáðu}â<•±ÂrÕdèd|“Ž’ò°rýíÀN]» sçÛÂÌ´£QtdJQªSc’×Á®w «­A¿N·SÙ½ø±­»óî]òü;ÐxŒ›èo§†žSrí•ápïèxç-à£kû(°I{ÞV,2ô‘´­zm&Ô$:<åB &û}pxj®È¿z µ„!û'ÇÄd.|ëäé‹°í…³òóœ8~®¯‡Í9JkÓ`LÌ1NŸBÀ¢y„äyëÒ¥Kpþüyø´Ï Ü:“P X‘´žOÛ;D†Â…,GdS)E XñXúûúÁévCwg»ˆu퀤éhœßܾþÏÿ 2¶}wvAÅceŸê=>î#`ËX†€Eáêö/^Û®Vj^#`ËXÆÉXŸ…€µrä¤ ‹ÃìÒ!˜’AeJ +£2¨ju 8¬ä²yH§2Àƒ^G†‡a¨¿_ôú½ñû!èõ€Ïé¯Í ®n38L`ëÜ'º÷íËÞ60ÓêîÆ‡-`ïÛñ´ïx[y§yY@óÖÛ°GØìm,¨ëL»w‹úüçE}æ8Œ.ž‡DY‰W š›xa bÅe‘ª=`Õ)•Yˆ—W Aˇ•ýúùxù€´¸~köÐe¨Ïîu•¯-A¶: éâ¤Hjzzú!dÛ Åa´ŽÌOy`2+¿Î(£ò<™_þEÏ»ü¸Þ‡CSaɦт“&-¹“L¯’í³@FC;îxék„x‰N_:£ÑCYªãrÀXÒ¦PcÓ„ÒØE§v+Nd”±” xw]qÈ®ÈÛfúLP¥z§¸]ï?UŽ*Yz½½Ýz+^ª£þV¼L9§KYa*gƒ£^¸ù›Ÿl W«»~¥ï°œ¤•¶ë¯CZµ[ôÛ€—ôh™o¡è —„Ç%{Ìîë^JœÌ:`"mÓƒlJ1’é3C%j~Ϩgáš´Y,ͪp6¥á^aY wR2Ò °|Ö½`“X¶Î°w)¶®v°ËŸobëRøz«ü™WÚ_·ïÑìÛ¾=`Ù»Ì{wEcÚ½S˜÷ìúçµÆ®õ¼©ímèÂù·C—™BÛ®­á¯s§bÖ½…Ïþ ÁgŒöÙìhƒ~K'ô…C§×ÒD|P¬—AßExþ,Å"Àµ¬çϯÂ{ÂæÁ5ÆágùôìÙ3ø$Þ?Í€uòäÉ笥¥%˜™™ú\©T —Ë=7`éKƒZ°êéíW´¾Xi—ü\·ìV´”‰Ç”\b‰$ ÈlB<˜äçOܲ²¶½±´AÖºrÖÝâ_ø¼ÈÉËH^>Q¡n·f¤Ì;ÀXFÀ2Ö§°UÝâÖïþss¸zçhñ`,#`Ë8ë3°ô°¢.ç.ÄL…•ÍZ‹H÷¾¥è·Ñ }Ý!?`€wiø|Ç…‹V7±î|­8–? õ?¶óí-×=W›B««^Û[b8–ÿöÕoˆùcë/ÎÂ`¢ š!®†°”â’œ’Tè.ejËP.Œ!^R¥e‘(.@²² K§ 6yJ£‹«ÎA¦2£”§D¦8<> ÿ‡ °¦2~ ÁЄ—GS>1–öÃhÚ£$|ÀKi¼üŻ܊C6ÈË`Õº$•ìµ)^BÔ—‡ºvR±:¡ðA¸xž‹¼§óÊŒ| d‚ ÔѯJ†³‚8ÌÜ0[tBy¸’%7¨Lei¢¸Ø}*çîZž°@U†9ÂK‡T<l" 2(½Ë{Æ"¦’æ-­®üî¿ÄdÒ÷7”dØ)a‰ÕÙ~ $zM•‰pÏ0µÌg.îçZ±¿Ï\ ~`ÔÔ]&çÞ IïáǘÊÙ—F§ä¿5¡—‹eLe\ o@Ðv|ò"ÿ{„Ý&Pk/X;4ò%+þhjË…w 2sS(jàe?õsÚú3Ïò;›B¶©¶i¸p¾sçèjþìÐt¶í~žNù‡ðåü¹ÉK‘»é¿;@_ŽÜ¥èÏ£=ï^ùÚ®J«ü~#v $F09: 3C=püäyX½vš»·oiànä+#`}Ì€õçX"¤€uüøq8rä´¬ÙÙYà€ÅCŸóù<>o?ô¹·… V}½=sË?òeÈH:…¼ W„{p`|^˜ä‚1y?’³ï” T$kkƒ<,Ûn¡‹ìÕ¿ÎÒí­»õÛ|®±“góŽ}gÏ®OV´ºˆÍFûëP¯‡Pç?ò±¶±¥¦BûPm½ž>ˆ·|ó_¯òû0oó½PÀê”zvS·˜^¹(^þÁ‹ÚÜIè)¹ÒƒVR鋿 ܃ò<á]„™ò<”ª“.ŽA"[Ý,S’j”aŽ %Êêh‚áþMáú!â1C@þ£“Â8`M¤½JÖ“™>‚5šTø—)ÁᦞL¨(ݧdd -Xåú•Òˆ*1eL†B¿¼ù( 7}¼KÑ3y…Ã7Þ¤ÐÇa¬0d~M¸~*)ÃIõ)4®g<å„ÉŒx'd"Ô+2ŒQ‡F=â 'Ýâ\÷ïÅÚž×Å…Î߉Cƒxnþfó‘«soþLLS€Ëª£sÜ•ˆé;µ¨²| ¤Fc‰ª´ j×`¾½„›¯ò{IMIA«_[(¹ô÷5ßo†d¤KÑÞ›¬ ž¤NA½jG.§eèä#uÜ"ƒÛc,V=À;-Œ!âØû¨í×\µÔ:éG‰ZþpjDšù³Ü³L{w+ûö‚y_»Ò®PÓ@S§Ì&‹b¶iœ`2; Ûæ‹Õ¯8‚¬1ÙCÐmk‚`¶G‡Òíè³CÞV³3¤8øö!0ÑãK]òqÁ&¿¶ø¡Ý¬tÑõ’Ýg(s‹G¡yò 1!Ç8ý9`ݸq8`]¹rVWWáã,žI¸¼¼ °x&asÀ¢q9ÛÍ$lÁ↣|dKµmò»ô#Xéîˆ@®‡d* CCƒàÁd2‹aóÈ[Û Mc¡ÀÔú, PQ{k-Øñy#`ËX3`ËÙÄ_ÿD<ûÚW7)òä{ßÞt~í×ÿ)*CÝzA»°Œ€e,ãd¬ÏXÀjÝÁ³u m‡øÈÛ´ m¯ßÑâ£ë°¦0Õ¼iócm~~–¨ÁÚñ–ˆ§kbæàšxù‡?#™)è—¡J)Ã@Ó2!é)?Á¾$«û![ž\±éBR¹d 5È+ò¿cжÓp83 ½ÃEð÷DÁîôC·Í«8<Âmë‚üì{¡špÃL>“YÆÔtt®à‡±´j LçÞ96-éÉ0Dr½Öª0l…â ¢×dõ+•a ð²#õ™Z‘A€LaYÊÞèí$Ãáº*^Ò;:ÿÂ;ì8”,”¼ÀÍPyyq®è‚©´¨^+ÕcÀL¸Ï÷òM:Cí¨;”¶ˆ'/«õì¥ïŠgBþ” aµ ñýÍ}®.½ù31³ª¥= *YÀìÞ=ÈÍI'ÓJYÞ§Œ ¥Œrˆ:X§:5p}7¥+Âˬüýs=½\?ŽÁ88òûM#q7IåeÈæÇáÚ3~O8(s3ã~ùÿÙG%;wBgÛèhkS{tv˜ÀÔÕ „,VúÿÚjˆÕÛߨQp‡à(®ˆJ‚»'žÞ,¸"p÷¤•¾,¸"iCÞ8u)àËø:%ìòk⌤ÀLÉ×S|ŠÕ¯X|lørfÇãšØü#`ñÅ¡ÓчŸÆrà³-»“°þØ€ÅCŸ?NÀj]"<{ö,PÀ¢Ïäyknn8`Õj5àÏÍCŸ9`Ez{ §7¢èGû`0èkÔ`i» 3=~Èç Ng`hx‚Á˜ÍfÑoÚ y›’²´A–B–E-;~ã ÿ K…y„¬6Ŷ§iqðuñëïþîï¶Å·{ùÛßßüú×Ä7¾öUØý‡7ôûï;›¯ÛƒëŒ€e¬O°nýúÇOþî7B~mþD{üXˆ;pýã—¾-FãV½ÜXFÀ2–q2Ög5`}¬#CÚr¡VþI¯ÿX!¬5ÄiÕ¦Zýè•—ÅO~ð}}ÙñµW_ßûÖ7ñ8?”×ýx›ëËŒJçÛo,ŸGã×Diâ˜øÖK¯éý®8Hõ'ÊÀK„C©:èEï©Q1¯ï0Ld+Ê!‘)@J“Éu©Â(¤ÇŽB4; üøƒ‰ ôF‹àï‰Ó>» üòžô¸Úa2燙\¦ó^˜-úa®ä‹¥ LТT‹{ žp=æ®á£I… ¶ó2dÞÇý˜8`ez-T¸‹8íšãÄ»Ø8xpQy¶ßÅ! ð/{·™’aŒÉ°5†žQVàÞVS9'p—rz­\Tžîï†zÒ Nú`¥ê†ÅþN, ~ðÝïùɳý§…¬W_EÈ:üØÑáa…Â@+bÚ y˜òMKzÀ’é_ø‡F°Ò—÷l¿\ˆ%ÂO² ÷QJI£D­5PŸ¬ÖÇà£N_þâEÛ›¿Ó/§£WÿÏþuYÿ"¯Ûóæz½ÝŽ®ãmÕ|9×}PÀ*ÕWÄÌákb8=*þíëßmÔ\iuUý e0VQ´‘5\‹¥‚P“£‹@À!—ü¯£8<î§;¯Ž^u´¿ø“– åíNîþ­Þ’aŠŠÜÓÔê¢1N¨*ßËjS{Eü%{MÀ¬x¼z Ñƒ›V{¦Fâ8ôz+>*G—-×Ü0_pÀ‚ F„Çà,”]ÀG¸¸&ŒÚ\pÅä#ü|s%pà ÷FÁ.ƒ„[†èQ8”85|¹ƒÂ…#’¤é2%"¡´†KZ F*Ô4®oÜÆTl¡ÍøùšoßxœÍø1ØÖçMmy¾Æã&·ÅaÎ q°Ë¯‰Í¯tËEzGJðôC/ Xü Ö8«5`=–’?eakÀºpá|œ€µ²²óóóаŠÅâs±êÓôöömˆ”Ó ËH{mÉe”ŒÂ-¥ú! ˆ>¿¢®nà£aº*>‚¥ŽRq›†ÖP•Ãõê(×_EÀú‡øøÂ?ý“øþKß;ÞDÀúüç?¿ù±åsÓe°è¿ªM£¯ .3–°>AÀ:,ÃÃÆëZÓÐpøÅŸlòC€nwö_ËXFÀ2NFÀúÌ,}¹ìcÔYµm¶¥¼5€}Œ0ÖµÍã<¯Žk÷~‡€EuU¾Zë¹xIQ_ZlÚÅØ¼ÜØŽ1ji-¸%²cE_¬$þ¿¯~K0­8èè5YMÁ§?© % PÏ„ ß³|–]tìƒ~_ôÉëÂHVW`83ý#ÐjŽŸ‹^“ßíGÇNHõYa¦Vs4à¹DK… 5Ê*ã?p-ÖxÚ \+ćû(a¸ó  rýVàå7å’ÆR\cY®Û­Ê8=/`qíUkÀâþW°¸öêE}°Ö××ÖÚÚl°žWƒõ¼€U¯×¡9`ñ`æàÝ‚¼T‘_Gø²mÙ0€áþ>¨—*0:Z‡±±q¨×kÀ#zÒ©´èï»Ý½¦½P‰äd˜úÆ?7í"Ü´{°•Öë¯)`ñãÓÑ,“vDª5`©ËÿmXúeM+è#`}¶Ö©¨I\ÞûºxðÚ+[Z0è¾ûÝ­îÍ5X?ü!n·é0–°Œ€eœŒ€e¬–à´]¨ùc—ø>Á}Í-ý«¶+Œ'ûvüAüãÿþßXòûÊ—¿¤vj!Šê¬¾ðÏÿ„ÛÑu»ßzCÿ¾vÿþwXbÔûîh}u*“ÖäþKb0QGXúׯ~[_îJŽÂpJá"wX\ ßYuȤbP‹»!äØ>K Í0ž õ¤ªç Îî‹ò¹Æa!N>~¬¥I|^=äìØ¹!ÌW‚° CÕ‚®÷ÃKÉÿf”±´&©ð(^†â¢k>O]ÆõÈ20.zçÎé\ÔÎaJ7bÓïËÁ£¤á_æ<f’º·§cqòØ¥¨– S=&(',0_q/óÑŽC²XvÃ|É‹]xdð8íÛ)®îü…xüÊwžª¤÷¾ýMñð%­ÏÕ›o ù‰´5\µµáú{¯ÿD!§8$C9<æ I¼;°JË„NµáË\ΊÃh%j†1*À¯{`FDÂ]Þy7ï¸ä.ñš®Ô(‡Ìýu?ð‘»9ù^~ã‘.Èô›ds¶ª Q„ÇùðxÞtîk–Ï4ÚR\ci.­É‚M~mkZn³7-ÉÙäý‰3”Ö¥ºí–îôçm):·”Æò߯±Ì§ß&˜ÚD-ã%ôÛÚÊ–çÀu‰M6/ns¾™v¿³g–„í~™ÕíÆ©ùD»ɧ°¸È½5`½h‰ð X¥RI_Æk XÜÁ=éÆ.ÂFðâáÍ™lÆÇÇ R)C&“…èp¡¸\.a6wƒÉÔ}önH:Úë^ñµ/ü¯M£sˆ*€ßŒ—?ºëc´gض¥üéu]ÿö•¿ýùkGþ€¶ ¯¾ô]ÔWýèû¯`w ?ßk¯~_¼ô­o©†¯j×ióš¼íKßú¦èÀøž·å?Xf_ X‰ÂŒv$ª*þõk€Õ¯Â`²¢©ƒÞªk°ä}ã‰"ŒåBPqϲ¼]» 6ÁTÖ3ô ¬…L}? g¦ ±‹°¦pí•Ö>bX~ô¸ÀcÚ ¥Ì—°\ ÉÒç Ò.Cn6šõ¿¦’üe[Â/]TG6£:®#âš>ºÅM5¹å×@ñ/b X0x"×(5j€ã^Š2XÑÑÂ;òø¨×TÖÜb©ìzMKC]â°éM±þË‹þýë/ UwÿãeqêÍŸ‹)ûNÌÜ«ÚÅ£ï~K]ÿòËBtw«š+‡C?rõôûßFÌhtÊßiÒ¿'|_ªY(ú¬ÇaðQ"še:>„c“˜Î»Ã·màz®ZÌ“ò½¨tCyØ UD 5]%zÎ8¡ž´…BDøµð¿ ‡ì9j>š§Fª^èí‹?®…i \od *Ö€bók[éáE¿: ¤?¦v_{Ó}ô6Ú¤ÖÓú›Z'´Ÿ­¯©5,ÅŸûxzhÒC‡(®·ŠmýÞµóVj÷ »tžsÌBˆ¦†£ÆÉ8rÅõWÏÛ=Ø:"çϰŽ= °öïß 055£££@+›Í¬ÁÁAÐk°´¶ ƒ2t}W¡ ]~Ÿ¾ôõ÷A$@ N¨ˆÍf‹ü<7uv‚ÝaƒžP"Á ôüâ+_ú’x`Äç€Õk9ºm"Éú‹Xÿõ£ÿ@Èúû¿ÿ{ñOÿøâß{IÀJG±¾÷íÖ^W¿~¬7ß0Ög,`ïm—÷üZÜûÑ+/ T¸Öö#q¬ã÷b2܉דë76®†:ÄÚOÿcÛNîtäêhª[ï$o,#`Ë8ËXÙýï2lªçÚò8-ãm¶ôÒúˆË0xµMÑëµ´=úò_Û[òú·5ªÉa'†»¾#ìf̼Ëk@K5-#¼üë×¾Ó´¨‚Ì@LÃgËR]QTrC0[ @-æ¿e/¸:wB²·¦ò~˜/xÅDq2ãG!*Ã駇°§^/Mê¯K zœà·´AaÐóå ,UîƒÅuW´49žòÀ´|dŽjµ¤ª U+ðŽ²‰´ÔÀ`õ‹wäõÃa,yÅ!ê¥DõCZMÒXÒ •¨CߥÈK„Ü<”ÃláÆ¦¥! `ל6Ô˜‰ò‘ªeyÙç;âæïÿKï¼þ<^þŽXûý‹Û;¢JKkÚR]c'¤ÚÕÇApR«}Zì—¬W÷ýF\é~CœL˜ôš¯5,½À5Re£íÂä%BZÓY;p=ÙÁ1™PŽOÅþŠx 7!寣sò¹ÈXÊ O4bG¿2^.,È`JxD¿\OU´.Bo,ùœËZ ™+9-ïå]„Ѹ¨Ág_^¡¾VM¸Šë™¸&Šwz©n+’·¦qŸð}ô:/­oñöj´ÞY~ùœ~Ôeå_¨µÎÊÓÛx,¾·?­ý¹P#Æ·Ñ.ãZ¬ç=_óëäZ2®Åâç÷àÖíûð¢z«gúàãd¬íVëá‹ï"ü4Ö¡C‡€—Ÿ°¨ŠV<X\{•Ëgë¨2é ôëA*B­V‡ÉÉ)˜š˜€æ¾[$™JÉÐ6.§¡ôöö…»¯|å+%Éž^è‰P/®0ô‡JÐ FÀ2Ö_}ÀÚ?b,oŠ{?ýÁæ]-žÉëî½öŠ8¿óu±iã røu|Ü€EøÈÎá1b,#`Ë8ËX/ Xs,΋:¾w5+øÚX6Ô5óyÍ—ïÞ¥ìÙ-ºö¶ÚûöB—¦sß>°tÛ›]X­°ÙœÐm±A,S‡Ù£× Q˜,êÚW=¦(`éÚµ%ÀÁĨÂ; Sc0…‘tIL{`¢z:!§ <¶}vÁBµf‹!1VÂÄqˆåf@>-ðñyê×Eè2ŸË nÓàBé*p—ËÊ\Qá>XÍ£qÆ2.X(ùA•£õ<Ú?êƒCã ]|<€y¥î^"ãâè³B;ÙämïœOºôeCËUe® po+îD>†¢n»˜”¿ÐùÚÄÅ]¿¿ÿÝ¥¢!Ì7Þø©¸àyGªÈçª4ó2¦¾4·íp$8xÔ „Öpo.:ü}ó@lî)Å]ÚU‡zBªqe:§¬T=p˜‚é(Ÿ{à€|¿ÈŠ|ü¹¼ –Ê.8>€ý57ðROüši¾ó(œÙ¼ô.ìÚ¿¯–´ÛÑNKýû¢]l|¿+ðÒa¸oìØU—T´q3úî:IC»µ13­]Ò´;PRLíêsõæÀM»±#P]¿©»ÖÕ]ïyT¡Œ&ý\.êìŽïÜm}³ÖËÅõ©çÞ§õ¾ÎÐóñós‘{p°½/Ú9ØX"4 ?ëÅí\àþ§,¶öÁú4Vëáôô4|œ€ÅÆÆÆ€ïËç‹¥Rã>ƒ@KŽdbr8ÐON@¥R…l./’É$ðó„ÂAàÐF˰ô%I*¸oÁ˘|ŸÏ}œñ6­áˬáx]»ÛÀ´gpêÚ'CѾ=Jû^¥ctt´C§¦õ|gg§®««kš|­t‚Ýí‡lý ¦ÏˆòôY¨ÎƒÊÂy˜:t Ê3'¡¤°Su„•á̘ø·ÿ®(N„òì9(ÍžÖœ‚òܨ̟…Úì16ê ʘ¦:ó.Ô¦NÀØœ2± ,½(æžñ…ÓP—דÚôaeêT'B²8}1ùW®Û .Ó.(È€@Vê!X®\qÝÕhÒ#FS~Ïø€ÃØrÅzCÊ¢Vª>Ø_kA™/9Ûð/âÙ¼¦2à€EGYh\Fæh3—«>¨Ç0‰ñ9v±D#lrvq†zSýî§âƒo}ã…¡êþk¯ˆµ=¿ÇCm2˜ÙÄTÞ®ûÁ®E­¦ŒPñîÈ•šø{áp4žr |T¯8¬ðѵÒxG`%n…™¼S1Ä·åù|$‹C™>B(¥PƒÏ…²ô179'pïÞä]šž–+žFÓÓœ å÷FJ.˜¦—L£‰(Á¡ÐËG®ÊN¥ÄãÔ}ù>¬¿7 n_¯ðX¿ì_Hñ‡ú7ñ•ùÐâ‘KP™>Õ©ÃPžTJ7¡úI‚º'­nië,@×hmšoïÛlë}˜ºœg6_¶å6Ïañ*VßÖ×hrG!8T„÷ß /<‚a,#`}ŠëÓÜEذ¡5$5¬D"x†‡‡`||¦¦§`bbhb¹T=¤Å⊼ҩ,¤RIH&S‹'D,…‘¨Â»N'DÂañ/ÿò/¢¯·èÈQGÖT³SXÌXFÀú‹ X‹ƒâüî׎ÿzñÒ߇_ûš¸ñÓˆcoˆýQ‚Å|É®Œ€e,#`'#`ë<`™wïÄ´· ôe·v¥«½]"Ö¹Y§ÒÙ¡è盂‘ÉdÚ¤»»,Åjµ€ÝnþfÜ2,x½^‚À“¯C!%,¿aÒ;ƒÜÄIÈOùÉSÊÌi(MŸWæ•tý0p=VRíÒÉN‹ÿóï/‹¬|,’›Tø|A>>L+Å™³P"³êHy^©Èç$Õ… PB`“×Í(+gýg¡:sL™:¥±ý­/B¾6}Ñô fEÀãg×N(ÛáàhfÑl4 j2PÕª¼ŸþõTVY(úaÝ +X ôèõVK%…~ÁÎäÜÀõSÜ»Šƒ×2-'ªåáf¡Fýúðc]³ò—ü‘Àn±öÛŸŠÇ/}ë…G©¿ômqåõÿìïˆÑ‹¾¬Ç¯c,áÞ ÈãghÙ4ßonpÊ»ù&©VJâ`3M¯-ß¼ó‘nçnªïø“÷#%ªåÕÓ‹wQª ¤Œrpã€É5h嘸6Š{¼itºÇ Ò¸ñ(¿F}¶¶›“Bëá>WÜ3k2킉´ 8Lñ¿3-Ïê=»d %ü~‡-Àïß'ö‚ÅáV—bs*vW@qúÁæb>趺 –®‹åãW $C)O€âØ2d«ó.Í@¬fÏ -Qŧñr˜Ñðõ#[4Âv[¾Žï£ÙÌ7¢µáûðùnyûîæçÛæ±ôÛh×ånbüÁÏÀ8§çÕ^qÀâñ8Ûõ¿ú¨€uçÎøsÕ`ý)‹ûañ²-^^¤š*¾.ŸÏ7-倕Á‰ mÒüøŽ<p¬`0(¾üå/ëK„\Ö×¼4H P›—°Œ€õ5`-GÍâT÷Äõ×òâõ¯}UÜùψ‹»^sá}zЩÅm`,#`Ë8ËXQ«sïíû”ÎèЗí:^°T·}x²Ymà°;„Óá·Û^ˆÏëÓxAï_áhèkŸF] (~y™—ûaH~‘‰åU•&^>©ú!à@•›x’å%À² ¤¬dUDósâÿ|ãQ_ºµ¥ ª…̨’2šìø ùا /)ÈGòSJº~ê ça\¾^2üº(ÈE’…H•‘Ì8D3u¢îò’?…È`FÜNà>X5„§X®… žtA5®ð!-R7w2™óÁ”†CÃJEYD±³K/|¦À‰Çå/pBÅÜ„ ·yI«±óÐ-†ÚÅ¥¶_ËÀôê¶=¥š—þ¨)èñÎ7Å´ G¼tHË{¼K’—ùR}&à€€ŠÃÝÀKiÔõœ‹ê›¿ÂËj\°]•·¯6¶™Dhq/™ñ{Äß¿^˜¯°sx©&¨‹»¸˜~"í=j}²ø18”Ò8ÞQÉË›¼Ó’w/.×Ý0_ò/ªp©Ä#Ýî³À”üžÈbÕ ¼{w"RãPÆÃ¼yóDF†=ÂK¤Ü¥Þï÷@·Í#¬2TAKÐâ`eqxÁ,Cé²8aXþ N.Ÿ…xqRåyÉM‚¾EÛYë·/+ÂÃàxÁ¡DFj.m©@÷)nÒ¸®¬ÈÇ>¯¡Bt¾.­?¯¯7¡Á„åíI¯|-$"Ÿ—…‡ÊÀ¯½8ºz¿ö–âv>¿ÝeÆéo;Pµ«æÝƒ¼ƒðYäåÁ?G‘;ï"üc‡I¼CÙá`ÅÝà¹p{jñ}ù1›IS€"tp‡pžéêê¯×#¾øÅÿWþ‚p(¬„# ZgøpXùœÍbÄa³€“Æ­Hz òºå¦‚ŸôC(À² „B’<!už¦V›Î³°& èøy‚ôÜRÀçÞ1çrØ š‡ÙÃ× 6V dfa¤°Ãùyˆ•–ŽR‘¡ÔPÀÀÎÀš QÅ׿ýCùX0¾t¢…%ˆ—W Y9©ÚAHJzø;¢ÈÐ¥‚×1àë§\™CÊÔUQ]ù½aùº≠ç!4wpz¢ò2ùïD<¦]PK¸`&„JÌ |‹wÎäFçŠ~à£]x)dÊ_¾„®›ÊÚƒÕþå=•s‰9.NØßWßø¯\ú£néçßü¹Xrì‹TKUvë5J|Ô$7Э·¹H÷)<:§U¸)‡¨eöÈt–‹øÈõá#GÅA;”cà#]“òûä#sF~O¸Ži*cª}"39Ð{Åí&ø¨ âz­dDÉö+híu ]Âj¦œâý££€5e®à=ƬP´…¡„|L22CDy¥èÇû:ÄjÛëâ½ï½¸ƒú{ßý–¸òúOÄ»¦7ÅŒü?[pÁxÒ ÜÄ’—òôZ%©Cá$a!­½³FN f¢„ëÆÔ’ jÿ@Á…pã1;VŠòµ‘Ü€ (üñÒ×§5úby€ƒærÍ­hË|ó{£^Jë3ÅËoIªOáz'®•š¡±8YèuRÚNÇѤŠCJ¶Ï ZÆ£pÕc‘aJ‰ÊpEÈ2AªÇ0—k^àe^z<~rÙ„ ¨„—5ù5»ègX겸…Ù¦˜lNE†)—w[\Ðe²CG— úä S+g!YœƒXnøŽxU‰• 2˜[ ¦÷â‘4ú(žiNlÏŸhºïæ¡Î[îëßL¿݆/ÓlûÓ:à¹iŸ7¹‡`já4ÂÓæ‚Û-§¿zªæÕŠÕvÁŠÃÕÇ]Ü.`qí¬k×®Á_JÀâlI~¿_ø|¾M¸Ž›†97³Ûí`±X`»@ÕŠÊ“¾ü¥/é9‡ë¬6ç#m7!½NÉXFÀú£Ö\´Kœ·¼)n½þcñôÛÑ›êG¯ˆÕ¿=mb"aG`âi#`ËXFÀ2NFÀú› Xܾš4½ ­ß~8L I> ¼N;¸mpš;…­sè!i÷Nh„$­ûûÎÍ:›ÏoÛó6p—ø¡ä(ÌÉ0DjóçD¬° Éò!E[ÂKÖC¬0›v"lÕõ%©#0±²±Ò~HVÝŽ¤k‡ E´ËRµ#š£®+9Z.”fmÀô¡kÊÁ+blî](Œ.C¦6¯”g Q˜€d^éåÁáò µÖÝ0èë‚‘ˆR}6óuˆ#moˆSø¥8¾ó×bÔÓ.‹f A@o,"ƒÑûFõ˜Ä¤}§8½óuqëǯ¾¸7•¼»þÚ~#Glú²ÜxÊ 4.g&ïU àNâ<|˜{;¥{Í—¿è õ¤Z¬xa2ë.îžË+µ¸CIÚ`2ëï", ™;°O`ˆµ*®çbrî¡Å¡Œ‹Ïùû®h=Ý"6+ð!/¯rpåv<¸y,éå;Tb༻°µBZ>7qÚ­@K~¦n¥³Û|¾Kc¶8¡Ël‡öN+ôçÄü¡K]\m2•H§!U˜„h²vc'^ëî=s‹Ö„Í;·îܼ#ðùç·jݵغc°»iG!_×á臅•ãÀ¹êÙ³ÁX*üÛ.T^ˆjR­¶‹ÃÁêÁƒÀ«5Xµ¬æÝƒði¬ÖQ9/*roíäÞÚcª5pQgîBÀaÉáplB›ö¶Û¸·]°ÒoÓi¿ X_’KSÚÈì*ÔzfµPúœ’Ü.ð8ìà²vƒ!i/Xöµy÷ÍNØ.)om¹®u¾à–ðô¢Æ§úuZÀÒ“GØ,œ¸ µÅ3"­Ã@j†éh–+.ÀHfx ¬!°(¨¥ë‡Ä׿óC1wdFe`#\×+.BB†-Rž> Óû/ˆŒ o„k½•›ä&ß…éÃWa|é"L­\“ Ç¡<¾ű%à_.Ùê,dJ0”*Óí6›ìòߌ8ìNB&«8óÚŇ_ݺkïÒ_#v‹ÈÄz 1†¡ˆbÚºO\xýgâÉ7_|”êÑ+/‰µ?üJq·‹‰„_ÔsQLjR!MÒÑ ÈÆBPH)Ù‘¤†•Ø _P†û0ÒëùxÒÑäãAº<.ïG’Q…Ÿ+'eFüV’ÃAˆöù >€|" ÅDè5&†Ž)ÅtRòû!ñ% Cl(Ѧë†|0qÁPرIÀ ñ~;d£n‘ù '¿W’ôBbÀùÜ ¿2ÒëŸM>‡zƒ.yí0Ö„lïwBN~_$Þç”á€áˆ†Ân”_“ìHïÛ&°¸ÆÊá ‚Ë€n:zEG±¬NÅæ®ÁÉ‹¥#— $`lòòç€ä*³)MC<[›oX˜\`vk\ý›˜œŠ™nÓÄäì&‡b¦ëévüX|žnãl\ßzù¯“}–0Ì,ñ왲]ˆj¹Î8ýe…§¨:õ¢ Õj»yƒ­Áêã¹âú«?gÀâ#X333ðqVk jE«f‚H{{;ð‘,Þ-È‹Cß®ù2³Ù z-…-‰êпô¥/ê¡G°zô@Å#søµë3°B&»¸÷mUõáK/ ù‘ÿ7aµ ñ꫸üÁw¿#jVIþ>ñÖbýG?|ík/ U÷~ôª¸°ã7âPÈ$& a1ž…+#`ËXFÀ2–°>³K_ªÛùöGã0´SÓö6´†¦ííhÑš4ús´íxîàé®]omÂ;‹æŽÞ„ÓW‹éå“0œ,ëpù{!_D¡€å¦Û„†ž¾úÍWÅüÑpüÒ=àæ†¡Áph+L……#«ú2_¢4 ñì$‹Ó; \ßµ|B©NÉË+3.N@¦0©B’¹$2ˆ¦Áî¦æ>àCvwD DÅ™ÿü™ Woü^ÈŸ®ÍŸòQìØëï¾ô²¸þÊ«âï>?TQà:ÿãÿÕ6“ð{„#[0¦½î%¨á®M¡Zl-¬¸¬©nE=fcQ/ØnàÝvÝ2t®n»Þ„Óè·/6§\¾¸½A°Ë C|4žÆ‡+V‡GãS·,6¥§†F’b(š€Áhp™48‡L®…RbÉôÆDPþ\w <ÒÊ"_/±k,M; Id !¦ Aÿ¿Kñt¢‰¢¦ $‹ÐÍ€ÝOÿNQE[vûÿÙ{î8Žkßןྯpß{ë®õî·8Çö±-ûØÇ–%+GK¶’eËr’³M$&˜`rÎ93L ˜³˜)Q9X’úÕþWý«{zf’lKwÈõ[˜î®®îééîúWí]{ÛßÏšähÔ)mñ.Cga€Q›¾¦sÖ/ã7¿Nj'?ÍÎöh+سï(X3ûM93οŒ*%¦nÖÌWL8­G±ƒV§Àºrå ¸xñ"ØHªœ••p3q°ÖXþe¯À¢bL+úu1¶ÍÜ^¬>úk16hcmØù¯¯Ýæ´5$@®©Qqeܨ29@“aY`}ÆV6ÜÁô¾Œ\©¨è?Yf$«o<ôˆ³úü/¡Š“ˆµ:‘ÆWeUXeUXeUXeåX¥ÍpA³I³¡„ÐbȈ/Ÿº•ºlFðQ¬µöÌ ¢Ëo:ÎÕw>û_-Ýã ,):µu "bD!«&XqÒaRå,{\}Ç—_},î= :1+qÉé™<v­\pö½v®œ³;WÁàøèßv½.)Í#9ÿª32µ¤T£$P4EU£$$S9Áy7Ú:¬M+¬ˆIG2ûë?èT3êfZóŸ˜ =¢êúw¿ïìùÅœT8c_þl ¬“0%:æêFª&¡ (ÁHx{ 5q^îÈÃ&É-hä˜bĤ<ñ¤ I²Üx—ç8]€*SŸ’¯@ ¨†-tø† ¶±“mHæÛ‚دW“êAõ9ˆåÀÙgõÝ ÖØ ê’%f làï4Øß“Û#X52ûNÁʵ±F Ç@L (¡¡)b ©o´‚PØ„›*æÎæ3ñ¨²­`h\þ›ƒ£“``D3dè-} Ù˜SÏWP@- VçöœWµB¥:^‹dCDU'B Ù‘æFš«Ãqmìuj›@´±Ô6dèÓ˜õQõ9Še—:µ¤5,ËeK^¹Po((ƒzûÔ¶þ" äÁÎ…Xkiª²Àú‡ˆ©Rñ§6"¢>ªhZ *ÿŒA1 RPÝŒ°¢¸âìÁ„ÀbºXŒÂNU*†U:ÎC¶ùg NLLŠ5Âõ žiu8k‘æÅêªJШpnÿÊápè‹l± Ó’ŠLSÈfšÀçnIà”\5›4ù‚­ø(ÔF„]Éú9rf„V{ÿØsìUpùÍœ«o}VN½úÇwƒ†\/ˆ7fó£A`…ë!dįê®û%LÃËàò‚+¯~ ÌÍc÷ê%çàÉ—ÁÊÉëàÐé`ùÈE08sìX}P`]{óCçøÅ·ÁàäÑ4b©>Ù¤1ênŒÔ©F¦.( £†åŸ½ ESSÓÚoæf”;ÿìOœÆT¯S—qj3C j¨Mk겚¨jjÕ_¡>7jɃu¸ëF]$°£ÂÖ›õR_Ö³lˆ©ýI]fÔ«ó,÷%qµPŸqë©sˆyÎ#–Ñ4kTÒ­wØ·.§á¹ÖgúCgÄYÀß!X+©cR†8 ²‚LjŠHTÃßWÒÇT‡ë@"•1%Ø@J“ljµñÀQª`Ô=ŽŸ€Œ’)6©j3m`dbÎÔ Ïh¬ÐÒ´÷ €T¦`4U”s7‚’‹ßÛŽ ™ïVŸÃ<ªGé*kjm‡!©D¢OfA‘‚„&êXB ’q܋à ž5ä4þ@ X4–·}Øÿô‹¤Ñ”ÑlD,ÅHÆ$eàQC}ÖóÙ@µcy”ÖRþCg~Ò3ÿÑ3 ×…Zk4j½Ñ§›JO^U ú]ÝŒ°âh•?4ÃGXEH%B‡‹Dý9Igg'`:)ð ~591&&'4FXMOOvýý ­½ øVkM…óí/ÑlC!Í@p“3¤—Ð_[ zê# ,°>ãkî׿»©¬]¿ßqUXeUXeUXeUXA`Õüé¿O”OFU3ëÙu>ñ´óßzeŠ2Æ»ªÞôGP©¶ƒÍ›Acs?˜Øy ¬œzÉÙü*˜Û}´õM&H¦ …æ7X¨žñÔ=¢}°˜zçŠ@ÂÕ·4OÞýsG4³ÇÁâþóÎÒá‹`ÙrìYÑŒì8 V¯‹¯®½í8Ç.¼ÆVA®s$›ÚA¢! ü&‰ƒEeM„F8æªCÍðþC:¸{}°žxk(Ýëd{ç“Õ6´&ÑÍvOƒú¦~'Ó5ZTy`fm6÷jXG¶{x·s_&¶ÍtNiTÝBªm¤»&Asï,ÈõÍ:M“ ×3 ¸Ç±ušcd»¦AªuT-Ož#—3ÝS YÎOÁD¾iµ¿ÐÔ5eÏѦliõªñxÝýæ@øJE5ÌÄ/|h¦‰Pf܉`è×”J·€úx#°>x¬'bˆz[m>ôy¢ÙMLäE[WÿÓÕ7 º{5íÝ ÝÜjÕó"X*š#qo™P®ï·å 0Î.Ü^± ¢ÙJ5µLÛH)(DcI®oMê÷êœLœÌ¤Ê\ÎtL&]Ψ²Bcû˜Iø>åÄšºA4Ñj“ëSW@;ˆ¤5ÑL'¨Íj¸•`ÁY¢·ZÀžð¯,Nþ‚çf (ZO4­eæ[K<­çû´Q!TLùgýƒ¢É¿¾ØŒÁ›1 –VÿlÅ€D’: €ûÊ>ŒÕÉ€Œ555ÆÇÇÖÌô ˜œ˜t‡@»WÅZUU5èU8wÞöEg(²YcDÕ`h³3l oƒ¡ PXŸq%ÛÎ<ýŒ™Eø+G=…âjÓ&l¿òÜÏ ~Ê«,°Ê«,°Ê«,°Êë# , ª‚™€›Ö˜á÷œÝ]gê$OK±}ó&PYQálÛº lÝZ*¶À–íA°ykTE@«j,…\Lj5Æ’95í®Æ¨Ö5©‰ÀBã¨>g:FoÝyŸÓ3¾ŒÌìc‹ kpôOÖÌcs‡œÉÃâŠaLÍL™3¹tœ¼ügpüò›Îc×Ààô2ˆÖV,•^ó &ëänmΓu©`Ôyóµ©ð‘G'Ð>WêÆãÈÕ[>èäT£X—Ê9‰Üˆ4ô:t‡ú } ÒØï„ ©¾þãû—Åa:oµñŒF]c"šûŒ@°÷Y]!e`kB³ÎîIPK9©†f@gvþήˆÑX3#EšÇäè?®JS­Ä•@‡òLK‡“kíMJ̱xà¹ò ­+,Í9Y“aþ,J&‡Gw^“:u,Ïq*Ý b©fÀE°¶ èIúw‡÷µà„:¹ þ{‘ñ¥ëéó¿Í¿¯u¢/¨³¯N™^Ü>ëi`ŠE,_/ÔFãC­Åz"É+–ÖA~‡òb&:?þHê¥"« þmþºŠ™×T~aå]þ(ëØ±c ”“ûFM€L½GSàÜܘžžbÞ†‡‡m -Š$Š2 ­©©i07;ff¦Aµ¿ÐÞÞêjëAMåvÐÚâÜ}Û¬° iD`ñ³+´4Ÿ«Ùô{GøH³ú F½þhPBisØV±lÝZ *¶VkŒhÚ¼-(ž¶lWBj›†Û6o'°I­Bñè[ì…j&@s×(Hçz@¢± Ô'3@Vm¼4w;·ç~§}`Ȫä.àês_IDAT^±ÐØ<2í# z (q%ôŽîq:æs§‘î¡EÐÑ7Zz¦@›:'¡1×é$’Í ªàI:ô‚öo‰ÖÅAØ­O8µâ—" o]ƒÆ6Þ1ÐPSëœyêE#¹Ÿò)§©2`g¨µôL€Œôî®_ó¶¹yÙBª 5xó»ißΪãv»\ _™’¹âÌqCÉÂmœÝçÏ çÏWŒ‚²ö8¦NOþ¹P}Ð'Š£0+jòý©FAL™+ *ŽàPhøE™DAÔ¡;œùçŽXùÄ‹-†:W¸¹ç¢á DÎÞc®@ŽhÅê•ÊÞƒvæ_Á(”ï»x…”ÝžŽþ¬ðøg ºþ[†X3ßËΣ`6hBòu(×mg˜rö©;ô4Ü—ÌN5³`Cžuþmܧ²¶ MîŸf!åQ·"pneTh½Q¢bbi½Yw~Ñã…"f-(püøñ–ñ×å?~±QªõŽã±ZkôêãXûöíX‹‹‹€bIDgóuttÎä2C.Pˆ±¼ä-ä¨gr†aƒä?V´µµƒÑ±qÀãÏ+Á62< Zš›A uUÛ€ø[ݫְú;r}°´ÀÒŸí¶°¦,°þX‘h d*ƒÎì¯~ç,ýìgá…_;YuC‘zPXeUXeUXeUX“Àrý›Œ8*H]ã ¦J1ÝÁ„·lÛ²lᤨØV¶l«6ÔxD’#š¸¼5(°¶V…­ÕQ°-•‘&P]—Lq‘h3‡A[ï¬ÓÖ7 :æ@§¡c`VÓ?Úû§“=7w:]£»œïÜ÷]§oêhí›í’r§Oê˜×Œì£{Aߨ.g`b·Æ$h¶Ë“dèÙ(fš»Ç\Ç(hjë©t¨K6H<¥¡ÿŽM0ç´hÚ†@*Û©1u$ÓíšLg ŠT¦4¶öƒ–î  “_‹Ê8° –mÀÜF+?øgaPPTÑÄ¥ÂzïgÏrѲÉüÀŒ ”¥ë–±Gí2ëïœ4X—%H×$çó«¢ 2DW¡DP…g¤©ôˆ“ø}º¤À¢ÖîÝ»Öää$ðÎäè’«¡¡PDq¦ Å’P[[ ¦!`0jjj@uµ¦ªªÊ†c¨ÆAM´ÔG’  9·ßv›Óª"¬ ´*\¬à&ÀÑ­²À* ¬²À* ¬²À* ¬²À* ¬²Àú¸Öæm!'Ê0¨¨‰‚­Á˜ëûT›VðMwœ•4&ŽL¸Ñ 3Í0Û¬‘<·ÇIæ†ÀÀèNÐ50 z‡fœÞáÙ<úFæ@Ïð è]Ýj›Ð?<zGgÕöyÐ7¢éšÖŒÌ‚ž!Mïè<Õ?"ÇPõ¨ýï¾ï!µÿ4èWÇìDZg ºÎžñ O %axr‡3:9F&4òY16µ¸~ht ŒÍ‚AuÌõÀÈ,`™¡ÑY0()J4?Í¡q%¼ú&÷·ÐDìïÑ¢JÔ áX““ë­bžìÓfCÁú`™Ù„ÖDKß$ñ½²Ÿ»}>YÝ%ðn/^–3¸J×Õ³Æñzòpgnësef–ùÖËwˆyP0 ’­à1Ø€¯J Áx+×§œ\K轃€A<»ñyÐéêÓtvkBÑzk>£yÏ Fªg€f›ÛAïÀèèê=}CNwÏ ¦wô¨º…æ–PªœMÈtÁ°óÛßþÞ9¤> «GŽƒÃGNX‡Váó¡Õ£ÎO>å¬>=¸ÏÊá`tþè[}ƒêQ ‰Ð§0¤a#3¨!02TC$xË ŒL–å2랪±dŸlǰBƒÍ=1œ„€zi|ç¥ð:ž þXe4¥1eŽã¤î¿TªLLN:vryÒÇØèFc6f•?½Mx­í] ¥¥Љ³»§×CCÀËL ÁØVt:Oç:@}2gÍÕ4-[StÏèÐ ¨ûWA'Èý::>†G'ÁÈØT>£šÑ1ÍÀˆ³`sƒšfMCó0àr“ˆ¼6w2K“Å]—nɃ“5²c€ñìr]ã ž´1³‚ 76Zñ˜l|¿ÃÍÜ7%|aì¶ž tôóœnj™L6¬/®‡_…ß”B§aF–F’©Ô?¿;ÍÞdÂ4ýùÍ~þÙ+¢ü3Ð(8(D( ¥±ö"ÔEf³ ÅÖ•âĉëBñ@8cŽø·Ë>ÅÎEà÷â÷%^AÆkã_~sb1Çø›µèÅo^,õ{ò¼(°ŠÅÁ:xð Ø»w/صk Àbê™ÍçO‘“)¸ÇÓ@Ó…`0 MU`ûöJ`•|ªª;“œx*âÖâÎngǻ˹íëßq;CÔJª Mªw½ÐQ\Ÿ‹6õ9‚+ž ƒ ÜØïa f{“Æ­cÀc¦ güÍ,.ƒK—_7^}œ;w ?~Ú ›c'N£Ç5G ÇT¢‰ìÒµWœ‡~ÄYQBé¸^Âî¥}ÎwÜá?qP”qY—8í<ýÌ”X;Ž=Ž8 ®Ë5K4;÷ìu–öî{–€»—ÁÒÞƒ`÷Ò~°´÷س¼ìÚ³Oý=Xf·Ú_XZÖìܽ,©2K(·ìÚ³¬Øf–À®}`~×^0·CÓ7¶‰Kw9íý €þ`VôýÊ´þ–îh˜Wlk‘íþîýÿòú¸)yXgáöÒûl˜hC>nú;Ijœ¨§î¨Ú'$£¢FRÌ( Â3Ø”E&Œ@¸„Tï*‰€þ!095 ÆÆ'€_hŒŒÌÞ33íJ/žl‘h-—ÐÔ”¶õ±þ‰‰I0==(Î3Í ÛÜ[œVÌpV{ ä ôéëè‰TÈæÚ€Üë«GŽƒ‡ŽäÁŽËÊÊQ°ª:6¡CGÀÁCÇì3}ðÐa°ª:4Š*'Rû ìôHçÈí é϶uì$`‡Õ³,¬9 Ž?ö¯srà *£¿)Oz?ò.òÖ÷ŠïSgé7ô­K­ø~*šÒ­ ›ÍLnx\_’ß0±çÏ™U2ëŠAWWWG¡üùü¼ÂJðŽTùG¨JÍ6ó6Ä~Áä/Gôá)oÂïP É…çý¼8ò²¸Áóòž3¿)%Ê(ÈäQlù…¯+¯óFüµ6"¬ÖX¬›Ëïƒå ÓÀkqàÀàXûLwS,E޽¿39Ï'« #¹i'‘H:õ2SPÁ©u‰,¨O´€xC;Hfz4Ù>Èô{è+É׿u·úÛ ’¹~ÈöÛzÒ= ÕÐ>³ëK_ú’óâÅ«V`‰h’ueUXeUXeUXeUXŸ¼ÀJ9B¤ip €¿¬»¬·GÒƒš"û2)p³jÀ…e%.„cÇO‚÷þúxù•×ÁÉ3ç•à9N;d8«9}Fs`e¬¨³píÆοýÛ¿©zÞrNˆ QqT½`eÝá£ÇÁq%¢Vä¨m«ê%}ÿýÂL(<óƒgÁôcðô3Ï‚§ž~Fó”A}a&p]ö•-(÷´§ÌÓ¾ºìº€§ Þºüû>£Ö=ãYï_¾û¾‡Á·¿ssÏýç!þgÂ=j»p×½oßõ@·û–5ø„ùG'ÿ˜·ßqøÖíw€Û¿}g îÐÜþmÃíη¾õ-pÏ=÷eÕ<ìC¯衇€®GómU÷·×<Þíù|ûÛÎ<üõòx‘‡4÷Ýÿ¸çGÕýð¸[ýþÀoÜsøÖ·nwÝ}xüñï}>òð= OªÏOúË=õ4(vÏ{®ŠÏÖkêâsd— ò||÷±ï;ïºÜ¡~sp÷E¹Ó —wzÖçó ¡Ôri¾s×½àî»ïwÝåånÃ]7Íw¾ó<î¹çÀ{ã?øxþùçÁO~ò ŸŸ{î9ð£ýüð‡?Ï>ûl¬ã™gžO?-×û)ðä“O‚'žx"ïÿû%ùÞ÷¾wS<þøãŸ(¥Ž»Öw þïÍëÁë#Èõxýx= ¯3¯1ø­‡÷7åçõ~gÿï[ìwå÷å5{ì±ÇÀ#<øNºÿþû{ï½Ü}÷Ý ôý«ï{–»[­“ý…Gy¸ïXò(xHÞƒ·€¼óÇ'fÀXÓ†üõŸYõ¥/}9O`9z#X~uäèIÀÑ1 ¬UøfgÏ_W®¿NŸ½NÂïK»æÄé³Î©³/‚'΂SêüNá»è³'5§Ï¼ŽŸ<N‰O™1cY–9©®€ß™ªïô‹€ûž>{ÇNΨs8ªç.뺦!šjsÃBôN Ý€ð 6'¡ÁŠÊo@Ä.{2 ~ U¯~ltßÚÖpŸº´†ëëÔñ´Ô–K „ÂQ ˜`´¶u€©©`G˜ÌÈG¶FGÇAÈ3‚…#}áV8ZBád4‹¾VvÄÌø`ñ¸ã†ñIÐÝ;Úl]Ž`IžN¡=Ò-Ý ƒL¶Ȩ,ïEÞóÖgÒÜ«ÇOž|FŽ©{Y`9áøÉÓÀÏs>3\}̨4ë`½'T=}5ùàú3êY[ܹ$ÒÝ >3bÙÁ¢$ q0žõù |˹Áui”L ™;‚E?Ý‹/>bµèŸE-N§ GM8²ÅP âsµžoÎZ¹ê8båéðqäà ƒU:NŽ’åååIœ”ZÏ€˜‚¿,áqx|žÏ—þG^ü£^þÑ.ïHGûüò¥F²¼a(Ö \ºVÓø`ùܽ‘Ü9BçÁâ5Û¹s'˜™™’ƒPðFp÷`ñžçD뇘ÍiÔºÞÞ>0==&&g5S†É90®Öi¦]&fó™œcD­ö[X¥,B~ÛØùɸŸÃ ƒ`TbA)<VÅd ø@b¬(Þy÷=p¢H‹¾dOˆiOqøØ pJ•èȾ¸s \ºò²RÏO©›áUçÊÕ—˜í¤GÏ—þÙsEÒi%>€zɃ™ðŒ5ë>{ÜxýpùÚ pT‰1pôsÄ1užÂu~À˜3)àh¶3ä1À¸Ž&uX—k2Õå¸ýL¦~qàWð;p K6HcS»€ÄXªO5ƒæÎQÀÙƒlDÙ¸ºâiXãµéaàAy']Hém¼§J•Ú@™C!å‡ÅŽg–ºnBб‚Ǥ1&B;Á`¤ÕKA ‰nzfL©ÏÂÌì à å™yiwÜ$Ìõ¸B¡°ÓÙÕ X¿ßTH¡5¥Ä—08<z&8m}ó€¦AW`M‚DS+¨ „@6× vì±&AŠÞ‹ì9v ;n0®ò,P”ù;DöÞ7÷;; «2 E!DZÏž“¶ÌÕiè6pôøIyŽN(±wôèqÐ30âM}€ Ïc™AÄ-ƒ†>(|(t(¼ìV‘Y„¥F°ìÈU$ JÐñ…D!gTÓ3žÇøÔÐ;}Hýþs€b»¹GC?½TºøÖüâgïC€#¾¼Ïž¿xïó~–‘XAFªÎœ»l3ËbŽfÛçÈ,Ë=nËp_u,³“O˱§ÎhÜóPÏŒêH |—¤²Ý .ÝêÕ½îÅ]/¢ˆä ¨z³½$fý ÞbZ} ÉbKzðÀŽr­-´Ö[^ÁÅ‘."9à(BèGņÞÛÐz[¯¨ò * Н(fggsÖ œÚOØX{ËøñïC8‹¬U'G]xNô#"\OD°Q”ùÅG¿(@8²Ea"׋£|¨~¡Eã^êÉúg , Gx^+ïìAÎ ôÏtïÑüDÖ`ïUyϵôiÚ†@¦mÐ0`,IÖ2`è7ø×ëeÖ™-Z¯Î ü™X|ø¡jÌBίóç…_þÒùñó?XÚ¨Àòúf•)S¦L™2eÊÜ”À*åw•'”Jl‹XUj»»®.; ö<è7uðæý÷ÿøïú˯:ç/\®\Ó\¾ .^ºd– @Ÿ©;vƒKW®XÁöúëoúeÐdÇekz03Å|vôØ)Àz–Þ²ó—¿½øïÅ‹WàÑ£'l¯˜&ŒãÇÏöÞwË?‹QL',S`5~T‡Õ:=nš(ÅÌB_3kR1ûÚïíÂÊêPor‚‘zÀØMÄqJ¨ž{È"vШé±³ðÒƒ%M‚¥F°ÖÚ¶QŠícG›Œ?U©åb#T.%pËH̉‘XÔï‘ÄÌ0FF¯Lº†hm ŒŽùG²ÆÆ&€N¦¼ö,ˆ*#øM„AuÜD2ü³''§5ª‡>9åšG0w‡Ó3yÀ& §i)˜h"lH·ÎZÌæZÀÂŽ%g×î}À5}~¿G¿)Q«МH3ÞŠÚ¾â)ÃÙ…â[ Œ9påÐQO]/·s¤âÚO`‡mE}Ä Ïsݱkhëèñ쀆f¿¿«Ò$sƒ€þTI?ÍC`¤³-ÀíͧKŽ>­7:u3ÎÜrêÒo‹#<µX+á¯ÄŠ#<ãèGŠ6š—8 "3ÑŠ!A,YV>{á¾Ä¿]ö‘DÄÅð—¥?Ï]¾¿GæhV䈖ߋ#|^³!G²ü&Cš ý#XI"½Ñ„7ãƒ%£lüÍù=Jù^ñ7älV-ô½ò›Â 2J›•QÖ&ßÁéYÜfFCŸ¬ééY¬SÌÌÌ.ëušYµ~Û¹N—¥ ×£þY-c¶•VY`•VY`•VY`•VY`•ÖÇ/°/ùq¯úK®Ëß'?–Wl1Âv¼e¬= Žª—8ªùàƒ÷Á‡{EñÚo;ç_¼è8~ã•7E ý/ø2Þµ{\T7ã·üýïïƒËW®жËW^”@.^¾¦Qe8ëˆk÷ž½à½¿¼Ô ‚ËW¯ŸKœ×îu 5ß‚Çm4l ÄÄI1dëcã”ËežŸ×™—‚×õø‰SÀ:øšFÊqˆ¿SêípjÂu KtòÚd«FýÖµ1%"Ë®CÜ(‰©Ö¯ú Å–ýÛüeüècDòbSåo+M±²ëìkR>±œÄ8 Ær€³­À2IŸyí¬V(¬È ¹ñ°ºººAÿÀ ðÑÝÕ "€–[¿Yu냉j<«®®ŒŽ 9¾l |¿ŒÀêÜïI—”õœ>XÉÆV@•kn"°8é„ñãècuöÜEpæ¬ãw%þSô›²>Œ†3~L9úNÉ:úxѧñì‹—g#Óñìù €>`çÎ_²õ±ÃÒ×? #ÇúVY§vãìžô8µk8Ó°ÐÙÝW–âÍÅYL}ŽaYÏ4ÿåeÊ2{–öŽäì^Þè{E‘ÂQ±ó.º©$ó;Û[€/] ­—_y pdëú˯ÙQ¨å}Áž¥}àõ×ßü÷æ;ï¾°_|ñ²sñÒUpN½Ð… —®h.^çUA„ÜEà;/BOíwÁ»ïåk€#u,K1Ⱥ^”úMÙs/^í;á ¸pñ²FSxQGÌ«g…U}p$+K7mˆIWãÍqhÒç0UNÄÀ²káÖ¡Ó¸ûæi(¤`#è#Œ°þþ‚uQõYˆÈ9¤ ÷)h”©j”è©Á¨Ÿ8uqà.3à¨× C(h‡w ­žž^À° @ <:00d!g%Þl˜† Õ tw÷ ºÁÁaÍÐ èïë#Ó ktÙ¦Èa¾Êæ® À‘­Æ\'°+× w.;s »Ÿ§Kêþ^T÷¥pùÚK€÷ûÕë7€÷~¾|íeÀ2×^zpô˜Ïg Ÿ;YÕñ `·.}¼³ç.ƒë/¿ΩgJ¸$0s¼³ªbM½ ¶ÁÐØ¬øÆr¯A!µÁB?ê¤>E"ÝòzñA`}£\þ:èOáÅ ÉñT1à$^Š'ŠŠ&§”8¢ˆâ´i¼9C"çÆïÀåbÐùš#v<>Åw¤Ì;Z&߇£o~Gy .^ -Žøˆ8ñÏ0äHßÙÝ?›ð£ -YŠÿ¤ïh%Gæø½8’çÍ=(xÓã¬oæßçé¬&•îtâé>Ëhâ™Þ5ñå¾[Fc÷ómϯ/Ÿ²À* ¬²À* ¬²À* ¬²À* ¬²Àú¸V©ô$ ,®cãjÄ•$6ÉJ3]SàÔ™‹€þ«‡4ïþå/àCã=%Cý»wï‡KËûãa1`áêêpáü%[M„'OŸôÝ`š†:ع{ ¬J¬¨Óý”öí?n¼ò* ×Ûïþ¼òú[@^¹ö ¸þòë€þRxÖ¯ìâ5ðÚëïŽ7^y°±8uæxU‰:Ë4M^W ³!Yæ¼j„ÀÍ+¯½CŒ\¯_ t÷;ÁP= Àª5Dê Ë+fRý&‘·$·í~Ae“M—H¼V"èR‰¢×Jm…X©}6$ôŒP³ßÏ’$PŸ5ÁZàÆ¾J›äY Ó°'é3B9„¢€fÃæ–V@Sa{{èîéÙæVŠÔ9A$cŽ»~rMu|,k*´aÂVlIâgatt ÐlD}˜ zdbôNtZº§@®kÔ M„ B›kï6LC¶,îÜãÌÌï6d‰18¸ ®^0 Ÿ 1iPϽpE‰aIu¨ -šÇ÷«z„ËW_’RêEÕ¡üþ[—®\»Õ»C °²©{Ôûˆ.t©t!©s‘äÏÞdÎ’$Z((gß$Œöv—C =€ëkU½B:b2IgÓ€a2ãûX|¯>ü‚KQšñüá(¬(V(^Ø··)¡ÔÚ DR.«É‡>;oª¡›˜Ú$Å0¹\0Á:Å…ϹW=sý}š®®NÀïE¡Eááç3áÁƒ`õð*8zì؈©ÐëåBºVø/þÔGôõò&wö&xöšiò¤€,XÔ\tíxm¾0 6‰\užäì’ҸϞ!ið.ûÊ”Ú7bqë– ,°Ê«,°Ê«,°Ê«,°Ê«,°>nU]ßêäÓTÕ·¸Ô•¢ër º. ¶«uBMm³Sn­ƒà셫ฤ·P¬œ¸Þxïo€ÿÄ©|v~èëssó`lb \9 ދӶbßC@^Ð45~ðþàÄñÓ€ _÷ï?hÜ»ÀÜ-\=vRc^À¯¼úà?ÎP<ñ2šïw6.ŒÃÅ$Ðl vîZ¶ÎútˆŸ_Ü èŒ+¹É^%øøù _ø‚óõ¯Ýùõo~ëìP ›W²ë¥ÁsjëcÎ!u|šU[:ºUÑXÊÙ²½ÚùÿøU.å16€ °©¨®osýÞœûz̹÷Á@Ê©©×Û„CõÍPתñ¯åSk/B›¡}M¼×PΟ?ÿùÏ;_úòWœ‡¾û¤³5ФDT»:–.ï=—ÍÕõÎò+§.‘ÖŒj £›+ªœ/ªk®ÏŸ]È…ŒèÞ¨I#)œÚÚÜÙ3íbÖP°!GåOå<ÕÂ.ZÞ§pýý §§p¹«³ ŒÍ‘§ŽÐÒ= š»&ó®a®c(ï~å¶Ûœ'žú?ç÷Ù&Š šGÆÆ!Æ8icï¾ÀØxò¬õ ;_þò—m”õå}Àþƒ‡€t´ÂÑZçÑG¿ë|á‹_Ä5üÉOŠ™Bpn‡L‹3ÿñ¬2óPqF—y¾FÓOdÚ@u¢Ô$Ûòñ®KµjìöÖ|Ô¶¼ûгŒûð+ê>|üI§"ÚàÕþÊ%Ü:ª•xþé ¿2 ŒT®ÀÒ Ä”gy-Ñð‹_üyÝä¸_T×ñp"‘È'2óÐ/¸Š$šý¬°R®ÐÕÝÚÚÛ@sK³6Þk(õæÝ‡êJî»x,n£YŽÔÖÖ:¿úå/M°ÊlQ6r 3iƒúüÛßþÇIËz%z…l.g³7°^ ÌÉÉ ÀÀ› ‹`çÎ]`#•ô?˜Y¸Ë,ÛÕ£H9‹Ï+´(†h6¤à"Þk(¦C~–{áßø†óßÿýß8…•l£S»ˆ:±X¬ ±3…•ˆEPò,û“:{;{ÏÃÿ»–šEÈû+™nõ|’šx;&:4òÙ¬ó®÷.ÛÏ>jÔ¶”ïx‡Æ«&áÖWoDø\0./$°jbš‰wo™ªºvÀ|v'Î^«Þ;Ïý \{çà|ø!^éìÜ"˜›]ó ;Áœê ôí8}úsUUñøÛßþ æÕM+ÌÍï}}ý`qÇ.042æv[ Žví?p¼ôÒË€£co¿ý.àÌAi8êÆ°\¦°âl>6&œ1(½gÂ}Ù˜°'Î:䯓õòWBLHc² DœŒŠüç~#qÜΑ«uÝb±„9älÂщiÕP%€÷ÆaUgˆÔ§EÌ“ÏþÜyæÇ¿t<íÎS?zÁyäñg J(dì=ÓØõ±Žuñ‹¢Âí.Ûä%V -ï_aSMB}¯ÔËà6%² æ¼Û·T78Ïýü÷ª~ÕØf{49ÿU<ïb&G• Џ »3ü¼e%:w6§aO?“iõ±ÆÂ° †‚Y…õ3‹Ñ+°›@k[;`zö9ݹµk ÌqÚú¦g6dôJÎ[rVÊß–îq ùû^øÍŸœ¯~íkêyš@DwÙ¾gé€3G¹…]J\†ñÌTâF #G•½×…~•œÉ»{÷2{þ±ÇWß'çyßgÛöJçþû°£Ñ²N˜_Lϵ¸c7عk ìS-aF½kø¬KGEèë õ;5‰G,ïºd‹Áˆ!õÙ‹Wláþòü6Õ}øü ü[B)+}쯶‡SÎó/üNõº[&;Z•Ë›UUº‘ÊG;WTTä ®ÿú¯ÿú§vqˆ÷d‡B )R|>8ò³ž¿²®®®ÎùùÏ¡U__oËqKÖýú׿.2:â’ ÓEFÅL<³¼mÛvçÁ4å³V`¥=¢·µµ 0gÞðÈ`¨”ÙÙÀ-™iéÎ,Ü VV ø¯Ð¢_Gµü¡¼BKD”üå:ñ¡J$è¼ËqDTÉvŽ˜‰¸J¥RÖ7Œ!ÄçŠ#qÞk(#”Å|®„õîSoäv·#¡‰¤šAU"§žC" ªâ9P“hÕê3H6ky^Y–ëlYbëÌ.WÆTÛ×TÕ7‚jõY( ¬ÏˆÀ:|ä8¨ª®q~¥^^u@{*Õ¤˜#k ¬h}XO`}óö;-J€P`mVŸ¿úµo|êGoŸzögÎÃß}*O`m gœçñGˆ«µV´®lD`%“)ðYX,÷‡?nržÿÉÏòÖŽ]{d*mŸ¹R‹†õ–ÀwËÉhî§]`‘'ôSç¡ÇžÌX[k›œÿúWk ,MfC —ú´ ,òüóÏc$Ë+°É„ó»ßý®¨ù¯”ÐZO`E£QˆÕTªá3#°8ê%#šÁÜ+°D|É÷‘8gk ,ŽÌý+° l“EÑöÿPRã÷-5tk’]Àú$»ÕA;@S×4X|PÂD¸òÒ `M„o¼––ö r:>9 –÷SÓ€/y6#ˆ£1á,íÝfçlYú0Ý‚jŒ„Ż܌Ãj½üÓ‡˜Km@µ™yç¶Ûns‡F±]F®šš2Îôô<`ðJšMºzú<1œ4²_m½ˆƒˆ*q%ð÷“Œ¾Ôr¢èu]Ÿ(Áé¶¾}ë•uËyPëå;zÿ³ïöhÎùÒ—¾ŒÏÚDØîüòÕf»®ƒõ3W]¼! x-e¿H}*ÏD ×jŒà‘—1_Èò¢Î5·¾¸s¹fÐÚÖ©ÒV`}¼Äߪ6Q °\Ó¡ñÉ2~_BƒWë¥ïH6Û šs­ ½g ÌvZûf5F8µöNÉQ©Ö¸ù;khl÷áÂŽeýrœQâªb¨¯oŒ«ûV`PÀîÞ~ ez•¨‘ýè095 z{…U¯ª§uM;ñDÊyèᇵ] ‰½§oLNÎhX—:†0·°H=“ Ì€t: ¢ê¥)¸ÇÎ<‚ÞÏ4%ˆÙÁcZpM .ò½½e¶Eô}(æ Ù^Yßâ¼ð‡J]§éÇ[ rzMs7#°DÔÿò—¿„ÉðŸç³Å¼Ššfõ9™áç77£…S“ù›Îóµ1%æ)^ ‰ÓµyóæuÍš6¡°®²_S:ç$ÓÍ ¦®¹PÛ`PÏÛw~ljŒèQ`Ùºð]´8hiÎÎÎ@óû,~«|•Ÿk3¥:¶B ¾ x]RjÖ¥½wßâë×¢¦Î‡9NY`}Æ–Z•^<ÖÏ~ösÕ»ªûXD‰O`éuŸ % œ<èü®?ö´óü ›Êë&Öôü.܇X’ TLØŸ”À Gj»ï¾ÛŠ«Ï‚Àä>¤Àzè±§œ¿ðÇOD`ÑŒ#&.1 }V–À9Ù.£Yâ'õq ¬{î¹Ï©®‰ØçÏšÀ’ýåY¦ÀÓªˆñS`yó­¬G}ôÓ/°n¥+ئ^!Ï Ä]ßåTÇ:@¶s =s,Ÿ¼ úvž箿 þöÁ‡`÷žeÕë““`jj0âô¾Õó`×™7Á♿‚ÝÞwÞùëàÍ·ß|‰æ12>hß¾›Šçøé `ùÄupîÕwm„7^~LN̓Yõ§øS£0®^袽NÂÙçCc`zvÌÌ.Øs¢›S3€BKnÖ¡mšSŠ4cê;òÅKÄ…س?ü¢k‹¹uD‰+aÇî%ÐÛ?dEkˆqÚ®ƒ°Wß/|á‹¶ð¯û´ ßÑþµ¦qÝPVÕ¶8_¹í«hÈd»Œh=ðÈ÷ßm‰X1Æ0œì©l'H$3&ÂÚxÞ,?ëä šè¤l{G§M3ÓÑÑ èô®Mz] ¡©øgZ¡e× ,¦Ìáq3Jd ê³`73{±{pÚ霌‡ÕÜ3¡éž0¦Ac*4©tZÕ6at|÷¡Dt×BiÚùážsZZÛ™¹@aÃÎ׋(’gBöó—™]XRî7¿ýóý'žt†GÆuãZÀ}øŒ±.yÖÏø Èè/ËÌ:;ºAU P`­¨r—t­ða3}Û*k›¯|å«¶œŒh=ðð÷ßoŽØ2‘D»ÇT•?«êVF°èôþOX"j²¹ÒóóÈç0NAS컊Œ¤ÒÉ=™L8=ö˜S]]7³P”RÏ–SÂIˆóm(Þæcí@\!„ úÝ‚æ·+F<Õ’M9˜rµ9WŸs.×ü1µ÷‹³*%F£Ÿ[¡µ´8So-¡E)ƪ*&Dyþz‘:Å/Oé1aE‰¨ŸýìgpÒ÷ +žŸWX1˜ì'³&iÞÈìÍBSµ1 š{$’hÕuíš+ºžø–×Ú·"«È>ÞIW¢Ló¹`R½(Š‘× uÅÛãòËÆs_­«R7 nW¯ÝT)箾 ^¼þxûÝ¿yñÏ€>Õû(8†•@v8v»†÷^S«/9o½÷xãí÷G¬ÆÆ¦ÀèøŸQÚ½w¿süÅ—ÀòùwÁâ¹À±WþèƒuãÕ×@Þ‹|F3?¿L©ÏS3óö¥Îí||ÉÓ¼LƒÀÞ4÷eÏûßÿýß±^nZ)/â‹ûþñO›œŸþìç0É`»ÚOÞÃ<ŠÞ:똞]=½ƒÖŸGF\-°œH¬ ÈË/˜x7¸ýŽ»œ­á´ñÕër*‚i綯þ§*Óež‚wTàæ÷eƒ·6r ƒñü†u<óÜ/ï~ÿY·T÷rU}«sçÝ÷;ÛBú{óåjºd;hÌtÙOò<ºÈ;P´4·ýòéwzû4 §ÀåžÞ>§[½œ„ή>lÈŽdE¼âÊ'°¬–Qð¹Ò³ Ù0·ÉÔwG¶(°Ú:{œÞ¡ÐÞ7 Z:Ç,¹†2ªE¡ÕÒ=aG¸þ´¹ÂùÅ ¿²kF ˜%æ|èaˆ,¹e”Uà,a™9(HE–e?[f` Žä‰Ö9Áp$¯·õ«“ÀúÔöïñÌv×{Nƒƒ# ¥¥„Ôµ\_¾|À@¿Àõýõ}h–ÏŸðé™ûÐlÇK]‰ÿï¨û°"ØdüÛœdc3 ÀòÎ&üWòÁ*Ö¨®7?í#o”ÉŒrÉ5ÌZ+¯TF Ÿ:O?ý´½)uâªt÷=÷9HÊ 'Z@P=ßBÀÂQã§éi<)°8Zá΂ÖÀ­@>×¶‚©;Ö"ñfPß I5f@c: ((D$0ðˆ„RQLÏLƒ…Åy@?'z .]òÏ4,…üæP\Ç\’2“òø„–l§ŸÕã?Ž <aÅp à û‰Üè½"ÂX&(x×±ÃÙØ¤ ÄZAžhòÏ&·¿[)ñĨV-€ÑªUGG“Ë'ª©Šf-•‘Leõ)Xµu1çŽ;î,XÒ`ÔÔo~󛪱ÊXB[G—óÝÇ¿e¥Gz´Àzú¹¿´ëéç~å<úø>5ë…ßms¾þ_ß*X[U£õü t¾úŸßPB*“'°„MÕ çî{ºe…Q­ÏˆÀÚRYë|ëö; V*×ãüas•óÍo}K=_Óyk`xÌÉäZ­Ø¿%ë( ‚¡H¡Xû ¬_üV߇~Uht~ü‹?bâÈVÕyñ ,AÙ\wîR÷á­ ,ïºûî»ÏÙ¶m>‹Ï‹ø`‰™ðÓ"°6mÚ„wž_`Iè1Ê ¸x<ž'°„ÊšzçÞû¼e…uŸ%aÄAß/°d– ÜwÜqê÷ ,AÔO<øV·"°¼÷¡w6«ˆ«§žzÊùáøéX4ïù±æ¾"ë\ Í&¦˜d‡*KwŒƒWÞx|h%–þÿΟßW®½ æw*‘0F•˜¦¦¦ÁèÄ8tøxûÏ'.Ý£K'×ßþ xíÍwÀÄĤfzÐ$8¡DŽ@5»s¿³ãøË`òÈk`êØ›`ÿ‹ï¿ýÝ—¯ßô ëGj“a04< øý#yˆ©O24/ÒTèî;†8?Ÿÿœ;î¼ÓI$Ñ€p(Zzp2 þì³?DÂËA4$zûÈÈ8åʪjç¹çžs†‡G`Nººz¨ÄkªO橘BÄ;ÃTzÖ÷?ô¸sßCßÛBM&nˆ:F”¬I ³ÉÍÌd½™}¾ø_BOíßü¶óûЍP¼†2-þáÇž‚Ð’0§ ÑýN?ýÕf„¤ð ºbq¶H,Þ±ÅÊvvuÛ”64âsgèRÂKhm,ø[a$ÎX"B$á³@ó„[` ÓÎÎ.NOß›âtýùÏÁ¹ýŽï8Áº Ì‚Þk(¦Õ'ŸybJÒå²MâٱÆçŸÿ :À˜ÀÇ'§A±ë2¥ö›2 )ÃF1X;1\žœš1èN×Kì,î쀠Ã3çô÷ ‚”˜$&YÁ ×â#SÅfÃ’¼ûpKûøïǾûF©Xã¶1VÏOÌ}(õ…ã­ ÙÔ D4¯SÈûYDˆ4nr>â þä“O¢û$ã`­=SŒ~6¥þêí›JÎùöÛowªkjœt6›w ¿¬®á£=áÔD’êÖ |Œè~òŸœG{Ú4¨ôŸqÚb×P7ÌfÖg½OÕÒ+(Ê\ñ¥j‰ÈÏVÀÉgÏr(®©M¶€„_BCS¶Àu€fDÆ”¢°¡)Ñ›n‡fDŠ"/2B$¾U÷Þ{/êQ潆âlþ /`6#G­dM²^b‰ýô§?ÅHÏGf z…ÕFîÃ_ýêW¸Ù–ýèG?*ri˜6lku=¨¨Ñl­®s*ªk Q°µRSQe¨Ìg˶°!äl6l1lÞnØ,ʦ­ð§ŠgÙª‘uBY`}J–×¹Ÿ=tï¾Ö¯ÌôÀYÖ+°ˆ«Ë:H›° "®(°Š…ðÀçD;p³ýë ¬ cʂѱ„;:QJ`ESm 1ÝD\Q`Ñ‹«¥¥ 0'¡ˆ«uÕÞ7 èÔn}¯z' ÆÉ}n‡X L%3þ„RK>Ó!~˜XV,±.îcêò §UUl_ÿ:ˆö¸)°¼õXÙì??’û')°0Bª,Ä•ÐØ¨I4ä@]²P`êZóF—(‚Ь_Ðcwä£5¿Àrëú×XG»ÖÂ~Að ,ÑÙ‹ ,†Ûøxî£O£À*˜¡EŠ™K™ Íú”&DµŽ/†öQðÆ[ï*, ­×^ œ;w ̪4Õ°¤õPØ™pJ §Î\|àÖõú;ï:¯¾ö¸tí )pl|0‘îÄÄ àÚ±sÉ9wùØwú:è]8 vœ¸L¨.祗^“òb–úì<œy…õr&§4lhd#ÃF@L}œ1E¡¿âz:´Ó¤( ÷eý£ê; “ªîÉi×üÈijó;@ŸúÎa8²‹C{Çšÿ÷µQr­@rÅ4g‹2Z¡ùn-³ÝG1®]×ÚÇ/QÖ˜¸Ý¸nš(dt_WàuuÝ„pm=œBH1‡B@|†‚H]Ófp0ŸA084lï×!%Ž…Y§hiï±dØDݵœ¹èÆÁjnmŒÁÓ×Û d&iÒóÓdO?èî–m=@&C}£  ­wH,!' ¡%Vßµ°¸ffwNN±b߈|> 4Ї‚©j8ãP:1…;!2™cØ”c'‡ÏMÿà`]œIÛß?(¼¤^Na§ŽNîéæv°v 7#Žê;€ýzbª¸k47VÇ4ÖéiÐi报²à_%UέÌ"ôǺÊeIÖiÊ4™A)„ãš`¬ P´#ƒºÅþú:[ÿªµ~ +¦ #´HM­f¦f¥ˆ¦2œÛÃMyT…šp0ãF,žœ‰Ç´;4Ëyg"RtÑœH(’(Ƙš§þDÍ~AEGvŠ* á@ç}šõnEtûˆÂuàOU`ÓæjÀå?mQ0pfSEõÚ¨2›Õ_Á¿ÏŸ kí_jŸÏygüåQj}‘2d7ðO$E`iÍ#àåWßüG±rñòK€ioå¥<Ø{]XØ8…úâåk Ø¿7Þg”Xør·½e“cFü£ÌǶgï~%ß<·•@–]þ뇀ih8#P^îlœØ;f~76SêØBOo?UBQ3n§¯óÙàptŠ"ŠûR ؆‡¡8å}tt °qa€UÎÈ” 6ʱrG$7(¬‹nâ¾(,c뎻!6:ó/Pd*¼ÅŠ£ü²ÅêpGX‹ùnì ¯¿“\¡( ¸ "ÿ XáHPDøsÚe3Ú8:6ž÷˦l:›ñd#``Óp4BžÀ¦ôµâˆG®ÄÇKèèìÜ.£\]"²ºExõƒþ¡QÐ72¾!×5Úz'ÁôÜ.ø_ ý‘”QÚ¡+bG}¥³ÂÏCþ2Ù’{_àó,eFFÆ€-cö¡Hc]Ã#c€ç…zUÇEàq)F›²í€ 2U¯Hâ,3ŠW ­5ÚU\|Ù«uFÅÖ ÐËœ¦l `І ÐÁ0M8æÙ³ÛþÁ£]„!JŒ ÊJª™lÚÎt­KHZ¶fPÕTA°ä\±B˜pÚ Óµm5*À¶`JH`kMÜÓp”¤ªÖ©¨ÌgËö¨!œG@¶=ŠÂ‹›¬H l°õ¨È¦­Õ ²: ¢u1@QÚÙÑ $?ü™žžK+˸ù"µOCLp$G«$pèF…”¿œŒLò·çlÑ@¸X¡ã¿fy Å·»×èk™/˜ôhÔfƒ]¿µ4~¶YÕ-”VY`•VY`•VY`•VY`•ÖÇ/°@”{6 ‘“.Û†S¶™†§¶©œ¿ô2xï¯ïƒ·Þyœ<}0áñÒÒ^gÇÎÝš]{Ód0ò•«¯ ! ÿþþûV|1)ÅÍy42®_Âqþò׿ÖÇ ¥Vç+¼úÚ[à䩳€/÷ñ‰I+d&” h’Ÿ4GPÐqy||Âãk¦E˜­Ëˆ& *®Ÿžž²~?6Ĭk3&'ì¹ÒǤ]5¢ÁXØO>Á\8kt#tß]·@ á¶®(ô|…‚jãšûÚý¾}Íp>;Ël>~®««ÖÎúÅæAs Ü'4R|ñþáK5„œ‹äý‹DjE>ïEÖÅ{…uz·ÛûךÐF¦@ÏÐàìTâJس¼ìÞ½HáÂÅëà=%†„÷•ÞzûÏV°:| ,,îs¹Ñ›ÁÂâÀöu{ÿà™êÞûË_g:®9hÒ“F‚ S×0Ý6g÷QèŒy^ø\Ç2ÜÇ6DªhÞ£­ß4HV„Yçv½Š)8[;û¿ Øèü¥Äv÷¬S&±qJ§¹)–a x™µRåÜœø»91hgÝFSc" +jAÐÐÐ&&'ÀäÔ4 ©j’p=²è œ ;3;d¦Ÿ@ñ§vq¢gz˜$ÃNTœí4I‰xµ¢¯`pYL îË—ë´ãý´z†„Ù…= ­k4fÚÌœ_\2#Xà½IÅgÐ+¬¦Í½ÎçÆ–¡°2Ï;Ü—Ïõ¨§ ;/¶Œ‰ÒÎgaVÝÿ³„quÍçÕ± :~ßúxØ´= ¶Tj*¶G@¾™(bÇ3SÉ5'•"¸J˜ŸÔþÀ-Û4P=ˆ'lbqK:Òét7cîc̪&µ0™X7ã± |êêã ²:6‹pÚêšÉ\±°æ]Öߘ–º.ÅÑ×Òþ&æ·ª 2ã¬2*¼ëq/D|ðÞð”ñQ!õTF<÷Šf³Ú¦ñÌbS²([C3«Í /-ÞDÃu •j-Í- ££ôôtƒ¾¾Þúûû—»º:Ak[ È5gSÝœ)Â^‹p ì†Æ´zGÖÿ̼‹ 5ldßRâ·”€v©.)È?Ǽ¥é[g[ŸNWŒéõAˆ²^€J—³{ÿ ðê/_+«GŦL›†Æ0º’‘"aõÈIðÆ;o½ópéÊËÎᣧÀ¡#ÇÀÞ}À®]{óýñx;•`NyÑæCtÿi…õúoƒ³ç/íØ@™žôüü`Ã0·°Ø0p¤I’1 ò=g¥ÁTÃ)P4‰?šÀ—>g(ºÇ´©=lýîËÑ/Éy(ÌÏ-‚–Ž!%z5F€„“}€¿ÝÚan>Ê›i@=`cˆâ¦ÇPJY÷±VYC Þ üç0p9TŸ ‘ ¾WÕJ\ 9ñ‡QP@ñ~æ(#GÆ=ø×QhõôúuɈ•@¡x" læx¼7ý£CvDÖw^fgw€…{׎OÌùÅÝŠ=“Ö†÷(ÅТä×Tð¾çöO™j»`;Df»¿Ãä-g˰.îkRMÍÏï;wŠ*õp™+ƒ?ª—æó^ú¦áßV³FÃ^“G±—»¿ŒËÆÄTFpXñV @Ì1Õç Õ€ [+ UšmUa°]‰/•5QPlá¾ÄÖm[¶kؘÚÑŒRñ{Ûk Œ ØFX¶Ô5ÜÀ53b†Ó÷eÊ¿°-×Ðg«ÆëfØLj8[ÐøÑ¯Ì ëP$8&S´øÂEÔÔ5ÎTĬDø5ªº›@PRž)’9Дi¹–ÐÖÖtÂë[%0øûú® Ù€ù>v[«4âƒç ±­&诧}øR`{°ÁÐÊ«,°Ê«,°Ê«,°Ê«,°ÊëãX!%„n•pƒ¦p[¿Á]WëC“ËàêK¯ƒcÇOƒ½ûWÀž¥}`~q‡5cñEÉ!}΀ ({ùµ·Á+¯½ξxI­? öî[ôçâ‹yÇ®Ý@ŽÌËý L¯+‘&üõüí}pùêKàØÉÓ`ïþƒ`aa óò¶‡I£Ã—;ï¾Ðwƒù…E+†¸/ËÐTÊëÀsåú9OÃÀ}Y–ß›‚oaç.@3kKרÊ¢)ѧ)a†+&t M%f¥úfžz¡/_AÝkaê C3Uú—×7/–Ü^ôè¿å;WÜ0®bª zºû;œ•SjÙë e—Y¦ßT4Z \A§ (AÇ—ÝäÔ H³#è]>· ;7dZuš gŠÀšƒypIudVÀ®]Ë€ÏøòÞ`ç®%ÀçI>³´´¬Ù¹{ ìSïˬƒåQæÀ  ïæ²êX ¬‹¾œûU=û=uíÝwÐv¸v©sz{ÀöšZà "Ý0»&¾b¾A~Q´_ñ2ôAÚPTÒ(å ŒÂ} ÌjÛ5›¶~tìyXS{]Ür¦±dGulæ?š5½ç^ú{Ýúõ-©E‚K–4Åæ³–+,›ÿûºÀ4ü‘·=˜Uˆ¥ˆf@Mm3`,/¦ñ‚o*߉|Ü÷¦&bˆ¦4µ ]N]c>µ  šÒp¹6Ušh²Dm€UÃñ6ÀØmUêüDÕ1(k$ lmk  àMÄMòSš•Ž“èv´K½ã×lƒÌç°¸YyÊ«,°Ê«,°Ê«,°Ê«,°ÊëãXI~±Tl]±m€æ )Ûà ,o™€:˜ÐÐ:= ö©—š°wù°N¬³Ê6:ú²qÙ©^žÂõ—ß/ÝÐ?qÚ9°² •¸&&'5’¢cB™Ià:ëcˆèzé•·ñiÎó¡°zô8 C>¿ƒ¼Ì÷8ö,ï»UC"8xìܹì÷Þ«Ù¥^ð,Ã`Yí/؆Ç4û®‚=KûÁ’ª¿jP6 l’r—݉4;æã½)-ÆÙÞŸ²¦ 6ç¡B ¤¢0ÃãµÙ¢ðeTI[ª¢»Ì—ñs*C yÁ%À¶`l Æ4’ªA†«c`ku-¨© q@O$gíQ,Ùå ÎDÕ¢É.+±Eóžéç›J'Ö` lü-uܶö`SO™Ùƒ~±æ7ŽyΉûØÙ°&5Â$(˜=@ÖÍAdÉlÂ`qQãï0°SÀN:4ՙ΄í8˜Ší@øLˆ‹žmÖäHs¢obËÜü˜÷”å:væZZ;MZþÙm®ó1˘å-> ÕÒÜZ¡|¶óa9˜èÌg»= ÙÀqJmó_žGIgmï÷ô9ýÖíŠ@Ö_±U³Þuͬ|Ì÷ö ¢¢ß«àw6Îèkˆ¼õ®Qiáì%Iáš/ø Ä™™x±µ²V½‹ê54MÊ;Lá¾uDy¾? ?$n_çØ}×kÂkáoaOgÕ®1"“ßT‡^Ó P¿|f·Í(Æš3ÁÍ,sd)¥@‹3+Ö| ;1å6çQcPK„P¡h*¹¾´À*¾Í@±f”]Së ˜˜^B#Ò+÷S´ÁÍÌ¢Áá0¦‘pæÅ+àÜ‹WÁ¾«vDŒ‚†/WÎTâ¨GÉææ4ò"¾|õ¸ñê[àú+o€cJ¸ Ûµg/˜›[Ô,,8‹ ;}CæwÖÏóàˆýOæ<ŸéƒÅ,.ûg@Z_¬¹y;«lV}žÅ±tÙ™é9Àp¬‹Wãén›wËÞ06m„ïFªÓ0D~ê‡|³=Ü  ‘”!QH0¶bع=ÐÂl—^gê¸ éùú_F¾e`{Ƈ£Äôn7ç”Ïæïµû[Û>_âz=±U­ª”èI$›@¶¹4e›A6× Òê³Ð”ÉåÑØ$~ Í@> ™l hLgA], $‘²@‰F¡á!ÀËðð( àâsÆí6|ƒGä–ÑÏ"ó Îï0¡5¿°d?OÏLƒÉ úkŽ/©)³Žë§gfG×X†p=˱ó5í©‡efff»¬'Úó2#{ò\¸8íÆÐô¡ñûéûO®P¿w…§1-,ûqàSù7ýªüçã?/-°ÌÌ6ù\*"ènþÜ*JàyÅÖJ E”+‚ŠË/’òë^Î5ÅjþõµøßmÒ¬{œ"Ûì÷òýŽvÆ£+ò½ä 7+Lk4~hêâ¬Gäð3Äíê,T©w4PïuA:œ‚Y¯kÔi~T¨N®¨×p„ßbF¶0êÏÑ-Ƀ‹\¸†X °eýxêc~ÎBÚòàÈ–Å %Úó“dgÏh PXeUXeUXeUXeUX·ÀŠ4ô:E—93Ð].$l`Œ«PC/°³ ‹,n«Ij’¹ÐÖ5 2ÍÝ ¹­Ïɶtƒ\k/È´v¦\7hÌv€Ñéàà¡c@›åöš?ø¥9Æú–ˆ/Ê”ï2óî̹ àÆ+oKW^+‡ƒ6snðRifÔ‹}æM=korzØÆƒqŒÌKßUèÆ¿r- ÜCÇM$­g N›ºò]fjzJcŽCá52<¶ël f—¡e™É·ÖÔiõ€æ1=FûPPUFMtÌoOpP¿ýšæFÞWQu¿x)ÒÜÐ|èx<>ì¤Z‹>ôÕµ9Oø2hu9¬oöQä…`iô)¨®Ëš·Éu«ŠƒPêåº1_-è*¶@W}±$íã@1…ŒDP‹$›†Ç—¢gdț“±AOS@ÍÌíó;öh(°Åçp7`gÂïSÉ{ôsŸûðvh²s;Å;¬‹uH‡„ŸßÊv®Ìó%ùžÿóþßÎÿ÷¿ÿ·õ'C‡ÄÔËÔ@U(`åÆ>2±ŽTÃÊã_û¡Å2.AùÇãç¼²¾†Ø–)*:tÎð+}ÜBþŸÿ÷9ÿãü_Îo~¿9o[WÞ±ƒkâŸiènã,ǰž}X¬¬Â:6~¼›9÷­žßÂ{M gM®Whïï=^8R¿7ëØ&Mo›oyýëîr~áêìùfV}ï‡Á¶êZP©Ú¦ÿ¿½3ŽâÈòøÄ >ÇØlŒ¹…¸„Ä >ð…m0s߇Ì0$„º¥ÖÙêVêK÷Ô’À·=»±±±±?lìþ²ûóFì/û7äæ{™/+«ºº%aÏìÄŽ~øFWeee]]•Ÿ|ïefY{}ÖlÆÙŒªãßH”üÆ6r iôu \þ.”Óßzê©§Q h2C•˜øÛ)Õài“jµH¤Ó3€ýiâÍhÙ~ȼ‘Á1µdé 6mÚt¬ƒôôIÀš,X§Ï]âÎ öÌ3ϲ?üáø›”¼”}°óÓÿ7€E/Ž®ÿ+À²žÇoXŽš:þÜžÁòà‚Ûä›>ý †âä™`Ñõü5ÖÎ;ÅÔ߬Ëî¿A"ÀRÏêWV¢cÀ4qÀ²ƒ‚?`ݼ}ýþ÷¿gËW¬ü‹–~ŸÄwê6ÁBöžU€•ðùþ VÆúMx½Ù9ù °jƒl×î}lö›sÙ”)O¡fÏ™Ç>ûâ’X¯Î˜É^žþ*•õ~Âÿà¹çžg[·ï`e5Þ¿8`Wyð]X“¶Á XñÁjl5Qƺ9O<™÷Uù©…že<Ö®*@«)R¹Ž¤+‰àL¸%ÄÇ”Ü!4²¹µµNSäP¾(¯4¨GÒý÷ÿ þíßÿEnGê•ô'˘=?ÿô'Õ*róQð,¹4(ÐVwéQ^UyH÷"¹U™Tùh¹@iý§’®Ar=úÕècXö:¨MÛv ÐÅÓ.^Ïçê×0Ý`®ÐC”;<¤iPJ¬7E„¬ëvŠW†U4Í’¡‡±â r6÷¢¤êý=¨_7 ͼŠè%"óµ²h‘œ!™»Åÿ¯UUïâ )äª5«’ƒ)>2å'ÊŹÁ¦B§”%E¸vèc{ðÈq,綾^ÂßsYXs¨åk¡šƒ”¯9¬ŽUå€@|Þó…P×ëPêñPn«É‡êèîCýÝßÿ ,WT&MxN r3R#€‚é)?5‚ô<Ô@zôø1ŠòX{ZRðNÓ2Ü~jàv5áöåË—›zmÒ~0±z£Û*ƒ \½–JS¯”ìÒÆÒXûд'V« ŒQEûVP^m¼)S¹rL«Dç¡*Þ1dÃPÿ/S¥ùÕÅk6yµsµ+¶ü íœ*Ƹw´Oq™ƒíúl¦­\µÆö<â_—X¯„±¿0­^ˆÎ£ZßG¨Ò‘øúôg¡ÊÓ¤¶ÃzuƒIÆxd±¢s¬”ç@çH뺬÷ÓH¯“²æéåUú½ª3Ë!Dçc{®–ë¹~ã^+€peS¥¶()Áä‹Ì<Íŵn\†´¤ÅKXM½—Õ»¨©«Q îºwΦ0W]—ïìøP¼×+Vá¤àBíB¡¦`'Êì’êFùÂTZÕr°mH)ÔM¨ío¿ûäT¨´ß9ƒCÌN¡ñ(*e]O¤±ö–ŠràD)+XL¯6’è}ÐèEµt²pçC“"R¡¶~T¸cêxˆ ’ÚâРù×ÿDpÁô= Ô˜SM~ÀanAêòN3K˘ždí"?ªUF¥"¤* ‡Æ€«B8gÛw?¢hÞµo¿ù^H‡*5ÒÞB¿¸»•=ÿül¸[XXš¸îT²×^CƒVvþR[¸(Y™kðå3np£èé€*ÅÁ¤Ò9¤Ðòåì\6oþBl}8Ào_ïgGOœ—­™)øû9oÉPŒÖÑ“çÙ³ßÄüO?ý Û°i++*¯c•µM(*âpŒ¸FÕðë2ÎU¬8rš¿ÔKE«øÙgÙª5鬠¸†·Œc¾5kÓµé.êØê5i˜þéç{Ñš¹ÿ0¿7IªUºr5»s÷ŒÃ©³µ€e€¥]ºz“-]¶œßç§PðáÈ/(5·bùÇ %Ë[²4÷ÍÜa#ÖÄ2=Ke æygÇû ²rïk±¢EŠÖ’ £ÅeAz.¨X(}xô{ÛÖ:XÂzûúÙç»w³iÓ¦á³]–’²¾úŠï3j´"ëëYFF¶OãóÝ´i3koï ¤ÑÇ,7÷.[À?аÿ³üù|øáG¬···«c€EV6²¾E‡£¨wÞ}·Ÿ:u§ ¢í´ŸŸƒgye-ª’?P<‹„ž~ùÚM¶L{^+RW±{…¥¶•ºœšÌƒt~ué:ºâàš¡Ì5k×™öµ[6_̱Øé³xE·Hç¾|úL–‹Ëõ¬°\Ož'Uòç³.ËwcmÅOe8y–¿Ïsð:à>täÞ‹‹W²YÚº öÑÇŸJÀÚñÞ˜s¥¥ÂVï¹eÔuéë1}ÿ—‡Yu­ uààQ–Ä+q²ÈÃ;{¿¸‚eî;€y×òóvÔ8Õ=t?ß“i\m`ù³XçYTñ펡*c ûm rÝxÏ6`ý˃G8p,VçºjõZVp¿„_G£Ê{=;‡¥,7žIêÊUì~Q9æ©©s±ƒ‡¬×»–o/SSæ\º|M}£à:fÍzaÊnt73o<=~Š%/ßAxÏÒÒ3ø7µù-ê<îÞ+á0»V¾«O³´´ Öàjf¡–níUõ yŸ¶n{÷;|ä$kïdíÝCìÀ¡c˜¶û‹/YgÏ0ª«Who¦x>Ÿ`ÝýP›·lGÁ2GOÿ¨Rwÿ¨´fMa=P½\_çä±ùXÏLÁïú{ïïÄsê0¾ÏX6Òa˜‘ío¿‹çÝ+ËÁùAºƒCaïÃǬ«ðA9î× i IÀš,¬é¯Ì•Îùkìⵯeø¼ž °.]½çòê«3øÿêî Ï2`Y×Ë¥ô´3ç.ŠwŒWÒE%U(X†4€%½ q=Â=g\Ï `DÑ»‘°Ò36°¼ü"VXT ¡G8ìTâþ |ö 7XSÆ X×®ç`úܹó`läÜÎÇå={÷Ëw:ÿ»tÞzœx¾ôN—›ë²ö,²9ôÁ>ùÅ¿)`\Ýä0ç²[^OÓëøÉ3˜ÒvïÏ$%e…,ýzwïÙ§®àêòÕl\‡ï­;ù¬¾ÑËJ˪UÙXÉÉKù7­„5ùBü‰Æå’¥ËL€K_ ùCA@°ž•ïÜ]þÿ%Àš7o¸~þí¶V}£·Í›¿@–.;Àï¤È`Ý•ïKzÆFÌsêÌ\ÿàÃOÆXÎ f ÖŽ~Ìæ×Ûà;e`Á6ÈÀ©ËšñÈ Af¹ÂBã)Ë%EeºÂfa:”¥É‘B5B>Mjß îN2IOn‰5ƒ±›äØž`jøÑ÷(ê‘H`C°DcQÜŠX~d¥«XZ×*riPÍlNëÆä¼äÎE¡›~¹†øö!­ÒPüJWhÿÊQëBÁd°àbÚÏ[zÖÖï¬7f³½û)«Í‚…I¢u#W¹¨²¿Î“¦ßEØóD}Ìa€úÛEÊ-Œ®aj å¶œüRçU/ ,k¥[µ3«F@–eä÷£LVïG8j Ï›Ê[kØ?ý‚ÛIù¢®X¹Fº½(:ÈCûðAìUxŸ²siFÜM‰¨øMqÊeXÏ>ùT@Ýæ­oaÚŽ÷?²k·ˆª2«LÆðPŒìë`aÈ¿_¦â-(NÄ€ mª“ #.©¶ÁÃZÚ{ KP Œò6ñ>@Záý"æó‡Qà~$$¨É@yüAþ¡÷«ëmò6³ÅÉâùC‹^¸)›U%ÜäÕÊðLË Ý­Y\jTœuõnæt«³UÛФÜ2ÐÒ¶˜DitßàüVЇ4°d™ÆQ§TViüÿ¬.+øNà»É+m°ô8ê<(pë¨ÊÛájAUaÚ›sæ¢g]úFt'α&;;qJÀó¦-o1OsÊè u«2›#½¨@KŸPë€ü&<ÍѼáÜ>ˆŠt ¡Â\✂Ëiò¸—³ï°æp»|M4 פ¥3OÀˆÉñ:ز婸|#'ŸùÃ]B¡.ã\øþˆ,ƒ=ßpkŸ©'µ´ "\ôýhå ÊÛÚ9ÈÚ¤ ?¤¶w qh‰¢:8¼€bèz»zGøÿY\X! Jt°è“ êü† }‹¿TÆÀÐ7*ïÃè7lPj`ð‘:¡áoMŠŽ|Ç‚!ã^>„zè;ü—D|çwJ#£\„¨ÌÑÇߣkRa¿Ã)êÚÚ:ÙæÍ[Ø /LeÕŽšºó…‚aöýw?p‰}Á2÷Ã÷?¨r nZÅ‚þ¨-ÿô#{çwpùö­[ì—Ÿb¹wnã:ÄqþòËϨ?I%%-Âmím¸> X“€¥*Ü£'ÎpˆZd², X&U’ÖÙäéE˜(`Uqˆ`Ñ @e,Úf',”3ðD€U¡aMZÿ®d/L}ÝŸ*F‹ç=uV´ü×¥o@x‚_´ø»„€UTVÏV¯MÇñ S§âýÐ-j¿°bÒm‹*:pAZöÍ\\‡ž;ã,xÆ6 +À¼ùì~QåoX‹’’T:|·lÙÆ**«ãü ÔœðùO°.^ºbÄop¸ŠX/ù%˜o*ÿ?L°ôô±-XV+–°dÑ5›b”~%`Ñ;LÛÈêDïp<Àª0Å™kYÊrL?sî¸ËåmQ–B¬+×o©Ø¬´uÂÕw7À~ `¹ù~ë7ne«×¬ÃÞŽ®‡ÖÕy²Aò6ѦÍÛ¥k4'° ø–}ÁÎ V§…ZzM²–ÇßÊ6lÜ‚.Lë¹Æ,¥`5ósÚ¸yº._|É\}“`9`µ¶÷³-[·£{ö¥—¦a–¶Ç,)J·,h)«²Sã,8~"ÀÒá]=Òs#¤‡­/`9¢A°~ýz¬·Þ ÑÚÚšÀÚ¸QÔ ŽªJXV°±“ÕÍ7ÞüÖe³ƬۇbdÍã–Š|z¹1å|q5p€Q1GcU-­#z½¤ ³ÎÞAT¨µiïC…Û„ g#µ@B­½B-=Âl nIpOÊÔb±¦‹Þ‘½(µNâù‚ÒÅ òó (Èä|d@´ÞÜ*j õ¢ê=](¼r¼'öØc|€éüeAêSù Ó'Ðǹ1دÁïøqæ/³;h|š À] r×ÓiÙÓ5 ZIzº7"åª8œÆ¡Oçc€~¯IÔóÐ:Ú0¿;ù>$õñçËië·àò®=Ø­‚*Vï7>äÍ=Ø‹îóÏ>Çá»=Ã_ä —e­l üY&»™[ÂjCÀ1žÆ•¡h¥10e©råæiD©ôêFÕ;‡‚è¯KKb<ݸ]Èj]a”êíMC=lÖeˆÀe@%€®ª3\°Uµnæ¨÷ ñë2Ò=&W­,zÒ²wïA[¿a Æ…Ðvˆ±«®÷›@–¤§;ê|êùWÕ6aå ¿º ˆ0îSÀ¬&€Á§Îi®N£×LËR\V3é1€ÕÝf<s°²®¸ìŽñÆÓûVn ζ‹ÿ² q©€E“4C ˆ«ºÎ£=/«åP}£áR‡ c&—¯ •ºR„ \½‘Ë|€è[é6 ¢ Â6F”„ ôÊ«3ðC÷|ø¼4íeÖÒýH)¢d¸rõí â §tg-g­<߯M˜2¿<ŠÛÂQµoßÞÚ5‚ï0¸¯"QüÆÛÚ=ÂÚzG xéUß&éè•2à–;ú›¤¶ñåN)øé{Ä6mçúåÁcØkï6ޝ»Äºûs0‚e=}³,,®•ü=èÔ®¡wàu °L.,€*°®0ö‰—ÿî>£Œ¾ÁÇ&ØÒeM#»§¹Ruø"kª?ÐÂAì[¥áÑoq¼@´ª.\( kÂ\¾E=’ð%:µÀ˜|ß²´4wòÔiì1MFún|ƒÐeŒÅGoTcl™Á ÆÌ›9S„[ô÷÷#NŸ>ýôÓOš~DmØ «²²’ýÌaìw -HcèIKíÇúd0¡xÇ3Ÿ;2ŒRÇ'‹˜%Ì 1_hÙŠ½×à ¢hðU²†‘õË°Ž *9¥ÈR¦¶¡åMï!'äÖÀ‘ÔGnÈÒÖ Éy.%¨ÀÊÍ¡dÀÑJlïS uû^•1‚[tc…tMJ^&be Î:pè‚ÜBA÷àÂP¥–^µ+ÄjÜ0Àœúh7š—QÎjî¼…ÂÕp·HÅE ùÙ¼bÛ…«9¸îhª"i²êá§ bZ‰•¿G-—I` ,Lªâáð@²Zã¶n¦â­ÛEôö·ßcÐCKí/§Òч„ت…! ÜB4ìƒÞ“QØNYXì¸]ºøós‡¤¢ÞýàSq®ßE õHe]V,Øîi¢¼-B¾Qú[‡Q>)/WúÆm"¾#U¸F¡Lz¶ðÿ¢ò€MÀÌÓè^(x–Çõðÿ;(ï¾Cº›ž2u®qQÕðÌ|Z|žc10öãR¶‚Jê 2ž•Ë[Vå–™ºÇWÊý᣹?×*^&ÒoÞ.P=ŸäèÚhÝ¡¥´ÜTKVŸIÕÚµÁr BL³P£P+€.4Œ[),çïbUßѺ–ËN/Œ!Ô®ÎÏíïPsÆ©ÆM°—-^":A–5ðg7À|‘AVT!þë‹“S˜¿Åh5· )Ú X ¶°P‡¡9KÓÄuØ ´ûÀ2¥Cçk¹Z¥íα>«×fàï{}Ê|©óë!©-ÛwˆX²K_ã:…~®´/íóÁÎÏELŽüýh×"o§žw„ÿÿ„릛 ,J`T1 uˆ´n# ´n§smáÛH $»FL˺ôt*à’¤,_|™B0à/Qƈ’‚ÒžQÓ2©Ý6}ÕÎåöÃ!€«ò·s@=|ì ¦íÉ<¨ Ò;ø>_ìARC8uòm$*ÒÃm,yÉ2„ â²Z•oqòRñPÁºøzWéJjßh 1_/(<ººó“ÏUœ—.¦Ç÷Ï¡¬IÀš¬>R§²²YŒçÄ•¾a+¦ï;tërö]ÑzM:«¨ °Òj/[¹Z´.\»ƒ€µ|¥¨?ç/ @Vna…úðXÇN~%Ê_ÆË÷°‚Ò¶4%êtÖeõ¸Ë_€«K×oc/ĉÉ!$½õÖÌ×^øål„«ÃÇNÅ´ÐÉ¢±h_ßU€5óµYrÿ›XGOfýÅ ¬ˆÇp^°¬VcHÄrL}qkâ•çx ´fÝF£ræe­\#*¿ÌC§ùÿn=¨ô°·w|Xs$(ß¹_£kñÒœ+°.]Ö¶ÔUiã,rÑΞ=‡åÞ+A¸ºxåk´p"€"×É€5 в.^¾€•s§-xpO³.^W@´h­É¾óg¬#ÇϪ÷¡ÔábÅ•Nty%¬9sç‹x‘»%¶€uífŒ3ZÏ÷oÃFŒÝ#¬œE¬`û° ì5Òb ÎÒŠ+ÝüÎÃ}ò‹kU^˜íA·¸æ=pŒ°àº2ˆáIà[CðôÆlñ¬îÜ«ÀóʺœXʪçüU¸lëÜŸ q[0Á±Ó×c.ýV€EçšÇ¿—W®Üš0`QwyK¯Þ2ÁÓÙ¬k26áÐ.á[dc‘ò˜ËáeäüjÀ‘åºàA•,ؾbUÎtÛªûìïFÑÀ‚ªræ÷téú],Ž å@ƒ óàI^áeèËn¥¨ ”„Ò ›EÉ/öç•óCÔ¹‹9üœ>äe¾ŒeC…°vÝ&–w¿–yxÀt#·ÔtmºûÛ£­GK'W·’öŒã àtàÈY6íåéÊÚz§Ðaäi5Êñ·Ž0_Û»™_ÁfÈóƒwÙoÉʾSÂ’–¤¨ÞQIÉ)ìú­"<žžß«Á¶×šŽ>Ä®Ýÿ=hŒù8H£ä¹ÀÃý„á\ôãݺW‰yýÜP|yiŠH~eÆkª,‚]æ&§à}YÂÿã'Ï_Se Jø{ºPi´k¯v/¢Jsæ ܼIZºšpܶ¨Í³¨2å±Ê¼-#d‡Ya‰¿)vçÚÜ5-ëRB(£Ô|½;µ2`;èðñ, oâöÔUëXyM³º¢ óyXËPçÑå-Õ¡[*EZ³­oÚ*¬ÿûøû àжíç x°‚Òÿ–÷î?ÆE€'ÛuØ7§W;ÃøŒ¶lÛ¡¶}}§êSžß p?:v·1yúòˆi™”²\À Œ áë‘N³rreOçMÛTÚ$`M–DÁÎ<ö2f¿ìˉ¬RmM%mØ)â’®°PrÞ,;À¢€z5²»PTÍ“E#¸«9¯Äèì:`Qü–²Ú¾::v€eN‹/–†Mp剘·PY+þ`¯"o<À)7_Ä*ËyHkªqÞ±e(«­´È©¸8 TVÀ\‘T:Xö¸¨PÍR|YUÀÊ{  pÁ¢% °PðƒBUº°Ô½³¬{ȰPr]ÀHÔ¢a <¢Â÷Jù4K#‰À Õ:Š2 dÄ$ÝR©Ã–ßV¶’€µYZqnÞ-·,»²qJ¬Ì'Ud"À²ª™_H/kâ’2¯,`éË(KžÄŠÚ‹Ÿ/ŠòµÙ)šPOjfX¥Û‰Ê&ÀÈL”ß´¯ž°PZš5¸¤E\çR`)ˤ)3TŬñ¤ëÛÆRؤ[}yHx5`lF;ÀÚ&½[X96`ýš`w»í1iÊg–}™ÖÀwû2ɘðÜ”KÒ IFOEí\$PÅscº9@é‚r¨¡‘Ä«›Ú„Ô¨àr4ú:?ŠbvªÈ•e£r©Êz¿IUJÆ„›Žú IUΊÜqiÕÐ/­750M Ê<1'¹´ª]B0)¨¶©Uª-F'µž6T·eR¦ÁÛeH޲Žñ_Zp:¨«)s¤lÈJ€£\¤Ö‘àéyÆŽ,o…+¬xì€&žëVîãÇ“æúµ@Û9ð¿²Û'æzâ¸cewÎVÑû6ûÆÜX÷Òʨó‚ c­Q“ôŠœòÄ«è­ešÒ ÐñŒ¼ÛßSåä‚…Š`ÈÒéb"RÇ·ÕÊz^Æþ±P{ÎB:ÀÆl‹îøÏÕ’ £öÒ€»¸Ê'§Ù8f^]ÞHÔö8¯Í½` Ë]ñÏ«%\ã¿%QC!A^­£ÍDþ±é‰ö±W¢<ñÓ‡P^[‰<³Þ˜ƒqš^~ÿ`J¯µé¢CËé¬ë|‡ H¶‘¿EÈ×2(54æ>ºÞÚ±Sºk´ô!)êXQŒdë¶ñm"PŠǯËÿ'¸‘MçÁïAiµ–gíº ïDdoáy²}íʰ¦™+ÂÄßq{gLGd¤Å9WÙãSMY$çu‹ŽÅ~A˜Þ›TZ¥ŒÞœ`êFQ,ꥪàšfµ7OÚZï…µV“¨§cmSUÇ—AØ‘ÂV=$í1pV•×x1Þf%€ùÐ@¦á<Ôr‚pš;M)`‘]š•Q‡³$´°F´Ì÷ŬÕkÕi“ÇzO]Ú2Y쬕*YÅb+òPh²Ti¢ôˆpéÁÅÕ®V,ßV*›}­V9;e]¹ƒ®_pÓ™óš-{:\Y©Û–a± Åˆâ­4c_¯fQô>‘µ, ãÉ+G?'»íf¨V7™çбó6ªØÁµ îq{ ±·Æ‘o¬|&ñk+2XÊ £E|Ð+Ùj¼–;«OHÆrÈù[J J‰u_DÈØëR´mÒÿÏEZFYËä»ïw@;î$`MÖ$`MÖß`™5 XOX†‹3ú«+†&ëÏXsaþ¶€5>·ç_'`éûØ'€BŒãþ/Øáe3LÚ6dIEND®B`‚MinMaxCategoryRendererSample.png000066400000000000000000000266311463604235500372130ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/category/doc-files‰PNG  IHDRXr5˜-`IDATxÚí݈Ug~Æñ@)¥”R ¥”Z†Z,RŠ”R(µ”:ȈÖœÎ21™nÖi\ƒë¢uV›hšj6lŠnlw3»¬&‚tI\c‘’Ô`vÅŽä!˜ÎÖE´q3ÄEh}›ßKÎåÎõÞ™;ãŒfÎý<ÃÃÜsÎ;ç¹ïûžó½ïyÏ{ïJDDDD4¡ºË[@DDD°ˆˆˆˆÀ"""""€E4ÕõÞ{糧îº+ûþè¼!tçOŸ×ÇÂD°ˆž$~÷w·îóþê¯þjÄÊdŸl6nÜXyíõë×OJ†/D~ýë_O¿ÿû¿Ÿ~é—~)ýâ/þbúÕ_ýÕô¥/})õ÷÷§«W¯ª°·©Ž2øµ_ûµ\/¼ðÀ"XDã?±9rdØsþçþ§îónçÉæOþäO*¯ý£ýhR2ÜI}ó›ßL¿ð ¿ÐðDþ­ßú-=.·°j=þütíÚµ ?• ,¢8±Ü}÷ÝÞ=FwN~úÓŸVþg[[Û”Ì0’¾õ­o5}‚w¿³€~üñÇÀ"û‰%zRóöÿû¿ÿK¿ù›¿yGádÛ¶m•ÿùw÷wS2C#EÏZ\¬Þ§öööÜKûýóŸÿ<X±ÿNâ·§TÃý—¿üåaÛâ.À"XDãúäþØcåí{öìiª7¥Ñ¶Úõ.\H_ûÚ×Ò¯üʯd÷õõº.ýMd†‡z(ýå_þe÷co|~çw~'Ýwß}7]–ŒuÕ¯óÛ¿ýÛˆÂñ7ÕÛzzzf«íY‹üÅ%¨j;w._&ϾŽÖCSè­·ÞJ_ýêWs/a¼æ/ÿò/§?üÃ?Ìû W­ØÇèɉÿY<7Ê) /¿ürºçž{*û¿c9Ö¶ÏñÞ>ùä“yŒ]ôŸþéŸÛ¾eË–Êß~ç;ß¶-ž;^À‰±oÕÛâ½íon%'Ð"€ETBÀš;wî0`(–‹ß· Xqr¬]Øé“O>©ŒMŠÁƱO·+C­ìùƒ·Æõöµ™“øsÏ=7â°­?þ¸òz1i¬—4×®];âscûHûüñÖcàõòŸýÙŸUþ¶ö††®ñVÔÁêmK#ýÍ­æX°ˆJXÕ€ðÄOTÇTXNÞÐR<ï+_ùʤf˜5kV“€²èµX³fÍM½KÕÚ»wï°íüÇz1ãÄïêõ‡jºî\ºté¦÷ïƒ>HW®\‹ÑƒTÛ×ì{¯÷×ý×öÅr£¿OÎÑ`•°âÒϯÿú¯[Ëq’˜À:uêTúÏÿüÏ›n…o¤ê“Û~ðƒIÍP«Úç×ÛÏîîëFÓh—ÔšU3ûÚ({í£òj_ó÷~ï÷êö^U_¶mô?j!%êAèwÞ¶>.£ε —4k{CwïÞ=l]ä»ÕÁj?~¼aÖñäX°ˆZ°B1¼zÝÊ•+G= 4 XõNÜN(J€Äïzc“&2CŒsŠ1>q’ŒK™µ½#õö3zm~ã7~ã¦çviðV{°Æ³¯¶×ÂèHsBÕÛçê˶þG\V«÷7µu¡¸üÖ,tÔÂzôìÕŽ;zôè„V䯞 «Þþ''À"€EÔ"€Ÿº«×Å%¤‰¬fO(ÑcUl‰'3C 1^ã™&!{e\Yµj/)Ö]«ñîk£í£Í¿Uû7ã©#õÔ5êyk:þàþ`Øóª°Ñ¤³ÍVìÏÌ™3ó°ï¿ÿþ¨YÇ“`À"jÀ Åx¢X®¾sívVooï°ä“™¡öŽ´¸1ÆøÜJVlMqeí8®z=uÑâoe_›í]jÔSب×m<ÿc¢z°Bÿò/ÿÒšÝñŽ,"€E4!'–Û XqB*zi¢‡¥ú¶ÉÈШça´×ŽžµF'÷Ÿ5šª¿c±z¬bœO ØŽ»åâŽÈÑzIFÛ×ÚžªFêÁl\ìW=À«îÙiv VL Q/ÿHc°)@¶Q/\1Úí¬ñä©lˆÀšPÀŠq3ÕÀ1ÙjÇ!4ÔŽï©}íÚùµ¢§l¬w†ª¿g±™KtãÙ×Pi…ßxã¼>.9Öö¾Å@ñèÉ Ð²ˆ;=‹×¬½$Íãyqg\£}¨ÝwÕ…ÆráHªº1-Çd´ƒ‘þf<9G*"€E°&°ª©ÿÛ¿ýÛ¤g¨æ`´qMæÁªžVb,ƒÝ}•O½/±ë¾j4?S(o×Î(ßè¹Ñ›“‘ŽöÜè•iæÿ7;?ÔHŠ;kŸ_ûåÌ·°Æ“s´²!XDk«ú_'3Ù¢·&ÆC_¬ú(mذ!ÍŸ??{Íš5éìÙ³‹y ˆgÍš•fÏžúûûóòŽ;Òœ9sÒŒ3@³6 °n‡|ðÁtàÀtãÆì½{÷¦%K–,æ)êø”âzÛ‟z½OÌÚ4ÀšdÍ›7/ƒUµ:::óu\BˆO¹õ¶íܹ3_Zð>1kÓk’µoß¾ôüóϧW^y%?>=z4mÞ¼¹.X®ÖúõëÓàà`ÈoXü¶lÙò[ŽñC©»;eÇrñ¸«ëJ^ö~Y¶=7ÒiÓ¦¥žžž¼Þûã`ÌÚ´ÌÚ4Àš$]¸p!W\€U΃ҬY³ÒìÙ³S^Þ±cGš3gNš1cÈr0fmZfm`M–¶lÙ’Ž9°Jèç8(ÕÛ§øè}r0fmZfm`M°Îž=›–.]j VIÝéñ‰¯Þ¶;wænv1kÓ2kÓk‚õÔSO¥×_½!X®ÖúõëÓàà`ÈoXü¶üÅ\ŽFÙÙ9”º»Sv,»º®äeïWù–‹rõ~”o9Êv¨³3 9–‹ÇWººòrëóh™Ë^þ`Mõ<-Xzùòåî"l¡O»ÕŸ‚ô`ù´ËÚ´ÌÚ´¬IІ ÒÉ“'V רn¤Æ`9³6-³6 °&X§OŸN«V­2V Üq4sæÌÊÝ7ÑHã_”ÚÚÚÜEè`ÌÚ´ÌÚ4ÀšHõõõ¥wß}`µÐœ9q *æY¼x1¸r0fmZfm`™É5RVά¬eX‹5RVά¬e–`,”•3+k™eX‹˜X9+k™eX‹5RVά¬eX `i¤¬œYYË,3ÀXìÀ¤œ•³²–Yf€°X#eåÌÊZf€°–FÊÊ™•µÌ2,€ÅN¼Ê™•µÌ2,€Å)+gVÖ2,€Å)+gVÖ2Ë °–ƒ1+gVÖ2Ë °k¤¬œlÈ °k¤¬œYYË,3ÀX)+gVÖ2Ë °;0±rVÖ2Ë °k¤¬œYYË °ÀÒHY9³²–Yf€°ØI9³²–Yf€°X#eåÌÊZf€°X#eåÌÊZf™Àb'^åÌÊZf™Àb”•3+k™Àb”•3+k™enYÀúðÃÓÚµkÓüùó³–FZ&:t(õöö¦éÓ§çÌÓ¦MK===y½rVÖÊZf™Ö¤èܹsù€têÔ)=Xi)O¸³fÍJ³gÏNýýýyyÇŽiΜ9iÆŒ-qâm%VÖ`Cf€õ…ÑÖ­[Ó±cÇ\"X¥t|xˆn½mqâÞ å¬¬•µÌ2¬ ×ý÷ߟ!kÁ‚ùò`ÓÐÐÀÒHKá¸T½õ¶íܹ3_BRÎÊZYË,3ÀšpÍ;78q"ݸq#]¿~==ÿüóiݺuuÁªpµÖ¯_ŸÓÀÀ@~Ãâ·å©±´ìy#cgçPêîNÙ±\<î꺒—Ë^ÞE9·B}îêLÝŸÿä²þü§ëJWËÔ÷V;žµJý®^.kªçi ÀªV€Ö¼yóô`ùTÊ^êÌz5”µ²–Yf=X“¦%K–¤ . [w÷Ýw,´”ãrª3—£¬•µÌ2¬IÓîݻӶmÛ*ã®Nž<™¾ýío,´4w–Íœ9³rgYdŽÞŒ8á¶µµ¹³LY+k™eX“§gŸ}6rKƒ[×®]XiéæFŠ“l17Òâŋͤ¬•µÌ2,3¹³F*³ÌrË,3ÀX¬‘Ê,³Ü2Ë,3ÀX©Ì2Ë-³Ì2,€Å©Ì2Ë-³Ì `±F*³ÌrË,³Ì `i¤2Ë,·Ì2Ë °;0É °”µÌ2,€Å©Ì2Ë-³Ì `,Tf™å–Yf™Àb&™å–Yf™ÀbTf™å–Yf€°X#•Yf¹e–Yf€°–“ÌrË,³Ì `±F*³ÌrË,3ÀX¬‘Ê,³Ü2Ë,3ÀX©Ì2Ë-³Ì2,€ÅL2Ë,·Ì2,€Å©Ì2Ë-³Ì `,Tf™å–Yf™Àb&™–Ì2Ë °k¤2Ë,·Ì2,€°4R™e–[f™eX‹˜d–[f™eX‹5R™e–[f™ÀbTf™å–Yf™Vjoo¯ëV¬C‡¥ÞÞÞ4}úô\a§M›–zzzòzTf™å–Yf™Ö-V+ö`DÍš5+Íž=;õ÷÷çå;v¤9sæ¤3f´d90É,·Ì2Ë °Ö„:z®®êm ÈŠž,Tf™å–Yf™Ö¸«££#»»»;÷æ\½zµô€—#k½m;wîÌ— 5R™e–[f™eX·¬ .dèØ¸qc]°*\­õë×§ÁÁÁ400ß°ø=–£‚vv}•);–‹Ç]]W*xªäÏrd,s¾zË‘¹ìy+ùsGæÚueÌßlnõ»\ËÅq¬Uòç¯2äi¹»oܸ‘æÍ›×r=XÕŸô`É,³Ü2Ë,³¬ Õ¥K—Ò¢E‹Zn Vu…5Kf™å–Yf™Ö-©¯¯/8q"÷\\=óÌ3é{ßû^KÜE8sæÌÊ]„Qa£ç*ભ­Í]„2Ë,·Ì2Ë °Æ¯ƒ¦Õ«W§¹s禮®®´}ûö–™h´˜+€ª˜kñâÅæÁ’Yf¹e–Yf€e&wVf™e–[f™ÀRae–Yf¹e–`,€¥‘Ê,³Ü2Ë,3ÀX©Ì2,™e–`,Vf™e–[f™Ö°._¾œ¶lÙ’,X0ì{W¬X‘öîÝ °4R™eX2Ë,3À«ž~úé V… ½ýöÛyÚ€¥‘Ê,3À’Yf™Ö=WûöíË…VV(æ¶X©Ì2,™e–`Q®BÕ€uþüù¿S`i¤2Ë,·Ì2Ë °héÒ¥rΜ9“+`ëÔ©SiÙ²ey†v€¥‘Ê,3À’Yf™ÖõÚk¯ ƒUíøŽA€¥‘Ê,3À’Yf™Ö8ôãÿ8­Zµ*_ŒqWqáñãÇMÓ ‘Ê,3À’Yf™–y°TX™e–Yn™eX_Àjty°vÚ€¥‘Ê,³Ü2Ë,3ÀX*¬Ì2Ë,·Ì2¬/Ö%­[·¦÷Þ{`i¤2Ë °d–Yf€5QúéOšúúú–F*³ÌKf™eX¥˜ËD£©Ì2,™e–`M \½ôÒKK#•Yf€%³Ì2¬‰ä^ CK#•Yf¹e–Yf€5NÀŠž«Gy$ ,Tf™–Ì2Ë °L4ªÂÊ,³ÌrË,3Àº€5ÚÜWæÁÒHe–`)k™eXK…•Yf™å–Yf€å!À’Yf™å–Yf™K X.\H«W¯Nz°TX™ené̇J½½½iúôé9÷´iÓROOO^¯¬e–`x¤æÎ{Ë€uøðáQÿ`i¤2ËüE…«Y³f¥Ù³g§þþþœ{ÇŽiΜ9iÆŒ-Yê·Ìkµpá´wïÞ<¹èüùóÓõë×óú£GæƒL³ÈS;,Tf™§¢£ç*àª^èÉRÖ2Ë °šV\ ¸ uvv¦>ø`ضftæÌ™|™ñòåËK#•Yæ)é¸,*ëåÞ¹sg¾\¨¬e–`5­£ÁÁÁüxݺui×®]ùñ©S§òeÃÑ“‘®X±"}üñÇ•;Uáj­_¿>ÿÿè‹7,~O…å¨°µë¦Ržf–Sw÷0GæÚueÊ[o92—9_½åÈÜ y#ggçP¥:ç6ýùã®®+•÷¡Lùkšï°Ì…ËVÞÍ»Ë^ß Àšêy¾ð€õÃþ0CVhãÆÃÆ_-[¶lÔ¿øá‡Ó‡~8l =X>É,óTïÁª¶,™eÖƒuKºtéR†¬¸4¸dÉ’túôéqÏ©°4R™ežÊc°ªm –Ì2¬¦Û?ùä“I™ÄT–F*³ÌSñ.™3gVî"|õÕWsÏUÀU[[›»e–`5BÑSµaÆtüøñÊ@w€¥‘Ê,s«Ïƒ@Ũµxñbó`É,3Àj^VÕ“‹.Z´(mÛ¶mØ]„K#•YæVɽ­~Ë °&HŸ~úizùå—S__ß°‰F|ðÁ´gÏžÊfr×He–™•µÌ2¬q(`꥗^JË—/¯€V3Ó4,Tf™YYË,3ÀjBo½õVzà|¡F*³ÌKYË,3Àº]¼x1õ:0)gVÖÚ´Ìk‚uýúõ´ÿþ|É`•Ïq !>åÖ˼sçÎ|iÁI9³²Ö¦eX¨öööì¨O>ù¤.X®ÖúõëÓàà`ÈoXüžŠËE…ªûßÌrdìêLÝŸÿÄrñ¸ëJW^.[þÔÝ=Ì‘±v]ÙÊ»Ùr.{}o…åfʺìïG+Ôçêr.~¦b¾–êÁºvíZô¾råJ=X-ði·Ú>í*gVÖÚ´Ìz°&YqG!À*ÿxj¯¡œYYkÓ2¬ Ôš5kÒ©S§ò„Ÿ~úiƒÐ°ÊyÇÑÌ™3+w½úê«ùSnˆÛÚÚÜq¤œYYkÓ2¬‰ÒáÇóT qçà=÷Ü“¶nÝš.]º°J>gN|‹9s/^lÎåÌÊZ›–`™É]…eåÌÌ `,–•33kÓ2,€ÅL̬MË °– ËÊ™Y›–`,–•33kÓ `,VÎ̬MË °–FÊÊ™Y›–`,–•33kÓ `,VÎ̬MË °–Fê`ÌÌÚ´Ì `©°¬œ™Y›XK…eåÌÌÚ´Ì `9ñ:01³6-3ÀX*,+gfmZf€°TXVÎ̬MË °;01³6-3ÀX)+gfmZf€°TXVÎ̬M,€°X93³6-3ÀX)+gfmZf€°TXVÎ̬M,€¥Â²rffmZf€°4RcfÖ¦eXK…eåÌÌ `,–•33kÓ2·$`íÛ·/­^½:Í›7/-X° mÚ´)}ôÑG‹KãC‡¥ÞÞÞ4}úô\ÎÓ¦MK===y½÷‡Y›v¾X“¢¾¾¾tâĉtãÆtýúõ´k×®´|ùr€Å¥9Ïš5+Íž=;õ÷÷çrÞ±cGš3gNš1cÈbÖ¦¯ÖíSGGÀâR8>寏^9Ç9>õzŸ˜µiç+€5é:yòdîÕX\Ç%„ø”[¯œwîÜ™/-xŸ˜µiç+€5©zýõ×Ó²eËêŽÁ °*\­õë×§ÁÁÁ400ß°ø=—‹ ;U÷ßrýå(×ΡÎÔýùO,»®tåeï—eËSg9·éΡÔݲs›þüqWו¼\ö÷£,ç«–¬íÛ·çÞ¨¡¡!wri?íV[³6í|¥kR4ÝMÓààUöñÕ6‹Y›v¾X“¦ãÇÚk°x*ßq4sæÌÊG¯¾újþ”â¶¶6w2kÓÎWkrÔÞÞ^׋Ë6gN|‹9s/^ ®˜µiç+€e&w–™™`,–™™Ùù `, —™™¯ÀRa™™`,€¥Â233;_,€ÅÌÌì|°Àbff€°– ËÌÌ °Àbffv¾X `13³óÀX*,333ÀX*,33³óÀX‹™™¯ÀRa™™`,€¥Â233;_,€ÅÌÌì|°– ËÌÌ `,–™™`,€ÅÌÌì|°Àbff€°– ËÌÌ °Àbffv¾X `13³óÀX*,333ÀX*,33³óÀjiÀ:tèPêííMÓ§OÏvÚ´i©§§'¯×ˆ™™Ùù `ݲÓÂ… [°¢RΚ5+Íž=;õ÷÷ç »cÇŽ4gΜ4cÆ ÅÌÌÎWëÖÔÞÞ^q«V|ˆÊZ¯Ë5*m|2а™™Ùù `Mhµ `E7k|¨WawîÜ™»_5lffv¾X“ XV…«µ~ýú|yq`` ¿añ{*,GêìL©»;;–‹ÇWºº*xªä±lÙ²eËå\ŽóQgçPqŠÊËÅã®®+Sö|°Z¤«Úz°˜™ÙùJÀš€kÚÕ6‹™™¯Àç]3gάܕñꫯæOQYÛÚÚÜEÈÌÌÎW `Ýʼ"QA‹yE/^ ®˜™Ùù `Mì4 £M×P֙ܙ™™`™É™™™ÀbfffX‹™™™Àbfff€°˜™™`,€ÅÌÌÌ `1333ÀXÌÌÌ °–ŠÂÌÌÌ `1333ÀXÌÌÌ °3333ÀXÌÌÌ °333,€ÅÌÌÌ °333,€ÅÌÌÌ `1333ÀX‹™™™Àbfff€°˜™™`,ffff€°˜™™`,fffX‹™™™¹õëÆé™gžI›6mÊðXÌÌÌ °Æ«^x!íÙ³§²¼{÷î´uëV€ÅÌÌÌk¼z衇҅ *Ë}ôQºÿþû333¬ñjþüù7]2¬]°˜™™`AíííM­+ÆgÕÖ×¾öµô÷ÿ÷ÙkÖ¬©<¶lÙ²eË–-[n´¼zõj=XDDDDwRS°V¬X‘>þøãac°î½÷^%IDDDk¼zñÅóƒ…âñ–-[”$¬ñj¬ó`Õªzl333s³.5`éÁ""""XDDDD°ˆˆˆˆÀ"""""€U½ùæ›éÁL©»»;õ÷÷Û>88˜.\Ø™÷íÛ—¿ª`Þ¼yiÁ‚yªŽ˜h¶ì¹÷îÝ›V­Z•sÇ·¬[·.9s¦%êwèðáÃu¿«l™#c=—½œ?üðôvíÚ\·Ëô­­XÎ#åŽcõ† *å_=söìY€EwF|ðAêêêJy9Nªßþö·+ÛËØ8GÊÜ××—Nœ8‘çB»~ýzÚµkWZ¾|yKåh>ðÀ¥¯ß¡ØöÈ#”¦Ž”¹Lí¸ÙÌçÎK½½½éÔ©S-uì®Ö… ò{PöÜ]¨Çâƒã’%KÝí¿òÊ+£>¯Læf3ŠOIr—3sœ£ÇòòåË¥©ã#e.+`”yë֭騱c-{ìÅ7‘9r¤ô¹£>ÀªŒÇo€5—þ>ýôÓ–¬f3‡Nž<™{wZ)wôÜíß¿?_2,sæøv†êï-K)sdŒNõ¥•«W¯–:óý÷ߟ!+.ùÇe£G}tLßÌ1ÕÛs\"[ºtiK¿£çýùçŸÏvþüùtôèÑ´yóf€EwFÍžTÊXÍfyýõ×Ó²eËJ3«™ÜÅåàøº†O>ù¤Ô™~øá<6§lu¼ÙqÙ(kãÆ¥ÎÆ7Ĭ8µJý.S)sõÝ¢WÏ<óLúÞ÷¾WêÌ»wïNÛ¶m«´ëŽFwÛ•©nGONÆQŽõ8— CQÇ‹+‹î˜Þxã<2ºÒï½÷Þ\)«O:eœK¥Qæ²ÏÓ(wÌ'ßø´wÏ=÷äAÁq.{ý.뇈F™<˜/™Äú¸Õ½Ñ{Q¶r~öÙgó ÷è• ØŠËàeÏíùÝwßm©sV@tŒrÇX,c°ˆˆˆˆ`,""""€EDDDD‹ˆˆˆ`,""""XDDDD‹ˆˆˆ`,""""XDDDD‹ˆˆˆ`À"¢)«#Gޤ¾¾¾4þü4oÞ¼´pá´fÍštàÀ¦_£½½=›ˆ`QËkÓ¦MŒž{î¹tíÚµtãÆôÃþpÌÀ°ˆ`}¦ƒf(úæ7¿yÓ¶‡~8»P@×êÕ«SGGGîázüñÇÓ… †ÁUµ ½öÚkiéÒ¥ùï,Xž~úétùòåÊö“'O¦•+WæíÑ{ÖÛÛ›æÎ;ì5~üãççD[8¿ùæ›7ÁݻロV¬X‘_+?ùä“éÅ_–ëûßÿ~^úôi€`M¼zè¡ &õ`ãã?¶p588˜{¸öîÝ›ÿ.ÖÕBNµŒbÝ£>šÿnß¾}yyË–-yû©S§òò—¿üåÊ>Äÿ­~­'NäÇ<òH³p<ŽugÕÿ»³ð¥K—2h…  »xñbÞ€ûCD‹ˆhÂð1žËz'3ñ÷#ÖªU«òº3gÎTþ.–£'«€¶jPª÷ZÅkÜúÉO~’×EOVõóß{ï½aà´qãÆ¼>zßB‡ÎË›7oVøD‹ˆèÎVô¬¬[·.uvvæËtµ@U°â’_½Ë‡ÅóŠíµ½IÕÏi´±.ö£Ñÿ½_k×®ÍËqY³ÐÀ""š0-[¶ì¦Þ¡j¨*TôíÚµ+}ðÁéúõëMVqÉ®Ñ帑ài"+tï½÷æýʯÕÕÕ¥à‰ÑäiÏž=ÃÆDUkëÖ­yª†FS 5õžÎc]Œµª§åË—çíçÏŸoøÚÅ%¸,X(.9Ö»DXOqwd1†+~oÛ¶MÁ,"¢ÉU"W¯^Íëâ½jhY²dÉ0P:zôèMPSô†½ÿþû•uÇÏëb[Qo¿ýv¯Ð+¯¼’·oذ!@^¦¸Ã¯úµãr^ñœèU‹çÅãzƒÜëéܹsÃ.MÆÝ…D°ˆˆ&]/¿ürp^Œ­Š»ìâο PÜáT lïïï¿ j¾î¿ÿþa—îB1*^;Æ[ÅßÇ%º¸ ±ÐþýûÓ<_7ÆJ={6¿Fõúê©Âñ8^·ÐhspÅ$ª±ý¾ûîSØD‹ˆ¨õTVÌ5QÚ½{w~ÍíÛ·{ƒ‰Qù—;–ÂÇå¿Ç{,ÃP|}ÏD)zÈjÇqÀ""*­bòÑSUÌÒ—cŒ×D)^+.OƤªD°ˆˆˆˆ`,""""€EDDDD‹ˆˆˆ`,""""€EDDDD‹¨ì 󮻘Çd"XDÔ`©/D‹ˆœ0I}!"€Eä„Iê ,"'L"õ…`‘&©/D‹ˆœ0[LíííM=ï'?ùÉmù?ãyõ…`ÑëñÇoèz'ßZçäÞìóßÿýôï|7=ñÄ“iÛ¶o§wÞy§îó.^¼˜ž~úé´`Á‚4oÞ¼´råÊôâ‹/NÀúÙÏ~–ßïø=Òë4òhÿg,å°ˆM`¥îî›Ü°nt\ýó?oM=ôßy—|ðLzòÉ­u!kíÚµé…^Hׯ_O7nÜHo½õV^7Uëûßïø=Þ×ÔƒE°ˆ¨d€UÝ‹rùòåtß}÷ååcÇŽ}öRݹWiãÆ•çÔö¾ÔÓ¶mßý ªþ{Øn}å+gÒ–-ÿzÓs;::2X5ÒóÏ?Ÿÿÿ7¾ñœkhh(¯ Û¶m[îùš?~Ú°aÃMëÃÏ<óL^WìÿáÇӒ%Kòÿ]¶lY:}útåèuuuåÌ«W¯|¢×jãÓß~ð·ù÷H½X£V£ýjT>Íî/À"XDt‡k`` ²þž{îIgΜÉð³wïÞ1õ¶lÚôT½ÝJÿôOOÜôÜU«V¥ïÿûéÔ©S7VÄsÏ=WY~íµ×ÒæÍ›óãíÛ·§þþþ <Šu/½ôR~­ðþýûÓ³Ï>[Ù÷ø_.\ÈËGŽIK—.Í÷ìÙ“vïÞ]y½W5ZÖèµú‡ýÿº?û‰ßÍôb5¬FûÕ¨|šÝ_€E°ˆh’k¤ñ?µ'ç/}éKéÍ7ßlÔªõôÓßM½½7÷`mÞ|sVôH4Ý}÷ݹ÷fݺuéìÙ³yÛ½÷Þ›.]ºTynÓÂ… +ûwíÚµ›^/ÖWCW<'zyêí{¼Þܹsóã誅µ‘²½Wó³¿É€¿›éÅjæaõ~5*Ÿf÷`,"šdÀé„_»ýí·ßNkÖ¬Ié•W^`½óÎûé±Ç¶V +à*–Þñï¢Çl×®]ùY#(m`x½õn£ÁÍXÇFU÷^?Íôb5ûFàf_`,"`å.±V¡óçÏç1Nc¬Ð©S1Ðý»ŸíËéßø×ô£½ÓtÆSŠÞ¨Fã³¢WªÞ¶X_Ûƒ8”,Z´è¦×k”õç?ÿùˆï}lŸlÀjvÀ"¢1ÖX4VÀбO1¨zpp0v/TŒÍš(ÅƒÅø«pŒ»zôÑGó¶à^=Î(ö¥ƒãªþýßÿý¦×‹1Xñ7ÅëÅx¬ê1XòÇ`ø×sîܹôÔSOMØÝ}“XÍî/À"XDô¬'Ÿ|2÷$E/Òo¼QY— ‹;ô&BÈÝã2^ü¿M›6Uî pÅX¬Øþõ¯½wW¯^ͽkñwÕwÆúXŽÇÅX­‘@&`2îVŒ±O‘ùõ×_ÿBV³û °ˆM"`‘úBD‹ˆœ0I}!XDä„Iê ,"'LR_ˆ`‘&©/D‹ˆ&å„É<À""""*µþ®”zÝ-RÈCIEND®B`‚ScatterRendererSample.png000066400000000000000000000266231463604235500357320ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/category/doc-files‰PNG  IHDRXr5˜-ZIDATxÚíÝhÜ÷}?ðÂcŒ1cŒ1ÆŒý1ƃ1ÆFHÈxe*R¢϶„ªÖB&¸1‰Ü¹Iœ"Õa#š5«ÍUkÍAIÏÔk¢&UZ}Uyqv©™Zw^Skòœ¡ÄÂjÞß¼>ë©§ÓÝéGdÇÒ=žðÀ~ßNçO¾§ßŸ÷çóùP‘ ͇lKDDDDÁQ°DDDDDÁ‘ ùGëCZBDDÁ¹9þ|Ú·o_úíßþíôs?÷ségögÓ/þâ/¦mÛ¶¥ýû÷§sçÎ)*EüÌÏüLúùŸÿùôû¿ÿûéàÁƒéG?ú‘‚u¶}¼Gé—~)ÕÖÖ¦®®®ôÃþp˽çþò/ÿR9Kd3瓟üdÉñAý¿Ò÷¾Ý¯m5Û'üê¯þjúÎw¾£`ÝæmÿÞ?Q»»»Í>Š‚%²™ÓÛÛ»êò `­Nuuµ‚õmûÍ^²î”Ÿ?Kä}æw÷w—ü#þ™Ï|&ÍÍÍe÷ÍÏϧ¯~õ«éÈ‘#é7ó7¬"ß/¶Ñg?ûÙe»¯¬[ÿgˆ™Â;w.¹ï—ù—ÓÌÌŒ‚%¢`‰|°‰5Dùÿˆ¯e-ËôôtºÿþûÓüÁdëbÝÖ¯ýÚ¯¥{î¹'ýó?ÿóâãZZZÒŸÿùŸg»qâ1Q@~ã7~#{Ü׿þõ5ȬæCèµ×^ËÖ”E1Œï¯ï÷~ï÷Rgggúßÿýß²ßïÇ?þqzä‘GÒoýÖoeÛg¥¢Ï¿/¾WaÞÏë‰ÂðÑ~4ýÂ/üBæÀEÿ>â5Ǻxþø>±6i¥éÞN·{ÛGþìÏþlÉýñç^ÏŸ±ðûDyŽçÊmÓßùßIÿ÷¿øw~üøñì?(q_<&Švaž{æÞûñkŒãvKDÁ’-œ(Dùÿˆÿú¯ÿzzàÒèèè²Ãüœ\gggK~¿Âìròׯ_ÏJfþ}UUUKþ<ï÷õDÙ(¼­°D444¬y7ï­ØN·sÛç—˜ÂçXÏŸ±Ø}Å¾æØ±céþèŠÞ»Üs‰Ÿ£rqÿVŸ}Q°¤bG¾•;BîOÿôOÓã?žnÞ¼¹ø5qÄaþ‡Ö¯üʯ,exéÒ¥¬`ä ÄvQ¢âý1+Pø=cl­.¥îÿæ7¿¹ìC,¾oüJ}¸­¦”¬v PÌŽLLLÜÒדûðÏåÔ©SË FÌzÅläíÜN·kÛ&ÞSÅf×úzÖ²Ök¥¿—ÂÒ3ñ½ã×üÛógz,Q°D¶Pb–*WˆÊ‰’”;ÁG>ò‘%÷åv›äe,¡0…»Ôb×ÉF¬{ï½wÙnžbß3vé”z®¡¡¡weÏùÒK/-ùºx=Qhc-\©mVWW·ä¾³gÏ~ Ûévmû•ÊHnÛ¬õõ~Ÿgžy¦h-w_ný]á)âï0ò­o}kÉí±»PÁQ°d 'vùÅ?öñáTª@ìÞ½;{lœ(ÿöØMV.1“ëjâC'vwÅ Ãû]Ä^êþX会2”_PÖú^J±Ó3lÔë),ù¯3Öõ®cú ¶ÓíÚö+Í`ÅöXÏë)õ}ÊmûR÷•ú;)||îµ*X¢`‰lñÄ@,<óðÄÿìóÿÖ‘ÂV.ñ\……l5 x×{¹õ6«]#¶š»ØW¸~-fùÞyç%_·‘¯gµæj;Ý®m_˜ØÍVl ÜZ_O¹ï³ÖûÊýŒ¬4{«`‰‚%²¥àõ×_/z_ì,öaPø¿óÜ®—bùã?þã%“šÆi nUÁ*|mùkÇÞÏY±Ç|å+_Yv{­–Ÿ|=¥n/<ØàƒÚN·sÛç§pa|œjd=¯g# –,K*ýMý“Åì{öìÉfre)ÖÁÄUþ?ðQ–"þð‡—ÜþùÏ~ÙóæfJýO~­³!«½5¯- ^\h#>äãPÿbëˆnÅë)uûŸüÉŸ”ÜUYêknÅvºÝÛ>þœ…ß3fsk¸Öúz6²`®ÁŠSEDâ?3Ö`‰(XR!kµbAo$vû^&·°ú?øÁâ)"…ë`âC±pÁv±8]Dþý¯¼òʪî/öÚb}YÌ^ÄìAí˜;ü~#>äã9 NÌJ¼ñÆþzJÝ^x¾«8ïX&ÖsÅÂy3X@ÅÎ`å§\1Š>|8MNN–}ÌÜÜ\ª««S°€Ê+XLçÏŸÏfœnܸ‘­Áв“¿ë0?—.]*ºF+ÿhÄ(W'NœH}}} PyëÌ™3Y9ŠÅé;wîL===YA*U°â±/^\ö<£££Ù*ÿªªªT__Ÿúûû×t¡‚lé]„·#  `)X€‚¥` –‚åMâÙg¿–Μùª‚¥`)Xð~ýÓ?}=}ìco¥††”î¹çÇéÓŸþž‚¥`ùÁ€õ:wîÅÔÒ2—•«|O<ñmKÁÖãÿñ›ËÊU8p`VÁR°€õøÒ—&ЬO|⪂¥`ëuÿýÿ½¬`=ýô”‚¥`ëuöìXzøáï§}ûÞNÿøõôùÏÿ›EîDN:•][0.ð\[[›:”._¾\ô±qÙœb"q ¸þàÑ£G³ò®]»¦`•W°ò/ÒFFFÒ®]»J¬RLÃÃËã“'Of×5T°»ßK\øy­«¥¥%ÍÌÌ,ޝ^½šššš, ² ÖÂÂB:}út¶Ë°TÁŠòRooošŸŸÏî‹Ý‹ù‰Ù°ÂÛ, ¢ Vn=UŸëׯ¯øø˜­Š‚ÕÕÕUrv«Øm¹õY…«³³3MOO§©©ÿ;Ò!~56666666Þô3X7oÞ̽ïß¿UYªXo 0ƒµBr¥i¥ÌÍÍ¥ºººì÷mmmivvvÉ¬ÆÆF ¨¼‚uðàÁtþüùlÆéÆÙ¬(;Åvóåqå*NËÐ××—Ý744”9˜aww·‚T^Á:sæLVœbáúÎ;³S+Dy*V°FGGSGGGªªªJõõõ©¿¿É.AçÁ,gr`“øÚ³Ï¦wöìYæÚÛmKÁR°XñÑÑTìJÆQ²lKÁR°P°P°,,,  KÁR°P°P°,n«ÏËJV¡8ºÐöQ°, P°,@ÁR°ëƒÌ©S§R{{{vçÚÚÚtèСtùòå¢É.•ݾ}{vYœ¸¨sî’:Å(X@Ŭü 8‡(Q»víZñ± i`` µ¶¶.»f¡,À.‚ąŸ×úX P°Š$f¥NŸ>í2\M&''³Y­\ÁвRooošŸŸW°€Ê-X¹5SQ|®_¿¾âãÇÆÆRssóâ¬üÌÌÌd««««h±ÊÉOgggšžžNSSSÙÆŒ_7ý ÖÍ›7³Eïû÷ï/û¸þþþlÆéÚµk%ë´b1¼,¸3]øÜçÒ>¼LÜnûüÔÙ³cé±Çþ#>ü_é‰'¾Î{Ñv3XëK¹bM2º¯”¹¹¹TWW§`Ájúøñ¢—‰ÛmŸÿó /§}ûÞ^²‰î¿ÿ¿•,P°VÎÁƒÓùóç³§7ndk°¢ìäï:Ìe||¼ä¬Uþ†Q®Nœ8‘úúú,P°6­ãǧ‹m¢48xÞö«|Μ9“•£Xœ¾sçÎÔÓÓ“¤b«Ü¹®FGG³sdUUU¥úúúl7¢£AÁÚÌb·`±‚» mP°œÉP°Ö!ŠTá&ºûîwÓ3ϼjû€‚¥` Öz=øà›KÊÕg?û]Û, (nâK_Jß~â‰eâvÛg©üÇof³Yf®@ÁR°KÁR°KÁ, P°,@ÁR°,@ÁR°kcsêÔ©ÔÞÞž]๶¶6:t(]¾|¹ècã:ƒqÁ£Gf)ä®MXî> ¨¨‚•‘æ022’víÚUô±ƒƒƒixxxq|òäÉìÚ…+ÝW ë[O>Yô¤cgÏúáÈóå/¿”ž|ò[©¯ïb:{vÌ6 rvÆ…Ÿ‹¥¥¥%ÍÌÌ,ޝ^½ššššV¼¯ Ö;{ö½ìÈøè¨ŽŸùFÚ³çÅÍóWu#}á “¶ [»`-,,¤Ó§Og» ‹%v!î2ÌÝVî>ËGøøÇ¯/ÛDûØ[¶ [·`mÛ¶-Åçúõë%Sê¶r÷«œütvv¦ééé455•mÌøu3ç÷í+Z°.¾øâ¦üóÜŠq‘Í“±}ŒË7ý ÖÍ›7³Eïû÷ï7ƒekÃíÛ÷ö²M·Ù6TĬ8¢°XÚÚÚÒììì’uV+Þ§`ùáO=õZºçž/nž»ï~7}îsl¶^Á:xð`:þ|6ãtãÆl V”b»ù†††²£óìîî^ñ¾J(Xßøáô_‡/óò /øá(XèÞÕõƒ÷þþÿ3 ÿ?Û€­Y°Îœ9“ª!Žܹsgvj…¹¹¹¢Ëy°Ë™ÜKÁP°,@ÁR°KÁ,KÁ, P°,@ÁR°,@ÁR°kIÙž·oßž]ê&.Ô\,qÙœbVºOÁ*ª`Åu'&&²k .,,¤ÔÚÚºª¯™™I»wï^vÍB3X€]„‰ ?¯&ÝÝÝéìÙ³  `•Ëääd6«µR®\¹’öîÝ»d÷a³ÐÐÐz{{Óüü¼‚TvÁKÍÍÍ%×`åçØ±cÙãKí:Œ‚ÕÕÕU´Xåä§³³3MOO§©©©lcÆ¯ÆÆÆÆÆÆÆÆ›º`õ÷÷g³H×®][ñ±ñ_iV¬éŠ…óf°€ŠœÁŠv ÝW›Ã‡g»Ëenn.ÕÕÕ)X@å¬ñññ²³V…‹×/]º”ÚÛÛËåêĉ©¯¯OÁ*¯`­tþªÂ‚Eêâŋ˞gtt4;ŸVUUUª¯¯Ïv9:Šp¡3¹ –‚(X –‚(X  `)X€‚¥`)X€‚¥` –‚(X €‚¥` Ö{É.qSSS“¶oßžŽ=š®^½ºæËêÄ5ãúƒñõQžB¹k*XÀ–-Xùi^XXH©µµuU~ÎÏàà`^Ÿ»¯¶¶vÉccF¬ð6 ¨¸‚566–š››K®ÁÊOÌVEÁêêê*9»Uì¶Üú¬Â‚ÕÙÙ™¦§§ÓÔÔT¶1ãWcccccccãM]°úûû³Y¤r Ó ³T±8Þ `« Ñc¡ûZ377—êêê²ß·µµ¥ÙÙÙ%k°, ò ÖøøxÙY«üÝ|ùGF¹ŠÓ2ôõõe÷ eGæEØÝÝ­`•W°ÊÛª°`ŽŽfç̪ªªJõõõÙnÅü]‚΃(XÎäÀ&ñÊóϧkïý'¿Ð›>hû(X –‚ÀzŒŽ¦ÔаÌ;{öØ> –‚¥` `¡`)X(X(X   –‚¥` `¡`)XÜ^/}ùËiêé§—™üÂlKÁR°KÁ, P°>ÐŒŒŒd—¿©©©IÛ·oÏ.uj^ëcWºäŽ‚TLÁÊ¿€óÂÂBH­­­k~l¹2¥`¿‹°ººzÍU°«D&''³™ªµ>6 V”­ÐÐÐz{{Óüü¼‚TvÁKÍÍÍ%×`­ö±333YÁêêê*Z¬ròÓÙÙ™¦§§ÓÔÔT¶1ãWcccccccãM]°úûû³Y¤k×®mÈccV,†7ƒw¦8AãôñãËÄí¶`kí0¯oäcçææR]]‚w¨(SÅ.;·Û>€‚õ>3>>^v&*ñz¹Çæaåêĉ©¯¯OÁ  ò ÖJç¯*ü}©ÇŽŽŽfçȪªªJõõõÙnDG‚‚à(BgrKÁ, P°KÁR°ÀQ„ –‚(X  `½Ÿ¼ýöÛ©»»;mß¾}ÉmmméÔ©S  `­5=öXÑÓ+\¸p!;]‚‚(XkLÌ\ŒŒd'øÌ/X‘8'•‚(XkLuuuV® O úæ›o–½ ‚(X%²wïÞ¬œ\¾|9+XQ¶ÎŸ?Ÿš››³3«+X€‚µÆ¼ôÒK%/a³Ú 8+X€‚Uo|㩽½=Û%ë®â¸0ójë·b¦+¾6Ös=z4]½zµèccv,.≂r.w_%¬É/|!;Ac¡—¾üe?° Öûɲ™®(H i`` µ¶¶}ìàà`^ŸËTtÁšœœÌfµŠ¥¶¶vÙ.ÃÜmåî+,V9ùéììLÓÓÓijj*Û˜ñëfÏïÛW´`]|ñÅMùç166666¾SÆwÔ.ÂØ=÷ú믯ékÆÆÆ²£K­Á*VØr·•»Ï –ÿ}À–Xƒõƒü äLT±ô÷÷g%§ÜÂô˜ÁR°€M[°¢à¬öD£1ý¶šS:Äщ³³³KÖY566®x_%¬7|0]ëèXæ•çŸ÷Ã[¡`E¹ú⿸ª‚§s(7k•¿›ohh(;:0ÿHÁ¸ÐôJ÷9°e¹–˜µ|}±‚å¯ÖZ`ܽ{÷²kšÁ6eÁŠ…íׯ_ßЂ¶Ö’ÔÝÝΞ=«`[£`E¡‰™ªÃ‡§ñññÅ…î·«`]¹r%íÝ»wÙë ÙŒóóóE‹UN~:;;³Ý”SSSÙÆŒ_okÁŠb•rѺººôøã/9ŠðV¬cÇŽ¥±±±’»£`ÅîJ3XÀ¦ZƒuãÆôÜsÏ¥,9Ñèž={Òððpš½%+šekkkÙÇÄŒZMM‚lÞEîQ¦¾øÅ/fÅ'W´Özš†Õ¬˜=›œœ\qÁ}̪)XÀ¦>Š0—×^{-íÚµkÍ Ï‹=¾ð¶82±½½}Ùãbmbb"›¹ŠruâĉÔ××§`›·`½õÖ[itttÉîÂÕÎ`•;UaÁŠç¿xñâ²çˆïçãŠïY__ŸúûûEl¾‚uóæÍì4 >øà’ï±+v^»vÍ™ÜkµÉP´ð(ÂÕž\TÁ¬çÁŠbòòË/oÈy°, ¢ Ö3Ï<“-&߬Q°€Mu¡‚(X –‚(X  `)X€‚¥`(X  `ý4ÓÓÓiÇŽk¾¤NîR:q®¸þàÑ£G³òÊI^Á¶tÁ*v ÂÕ^ :—ÁÁÁ4<<¼8>yòdêééQ°€ÊÞEø~ VKKKš™™Y_½z5555)X€‚µšË󄆆†ÔÛÛ›æçç³ûjkk—<6vÞ–+V9ùéììÌvSNMMe3~56666666Þò+?1[+.:]êkË=Ÿ,À V‘Ä,UMMÍšf°,@Á*“¸Øt]]]öû¶¶¶4;;»d Vcc£‚(Xån;pà@š˜˜Èf§¢\Åiúúú²û†††²#ó"ìîîV°§i(<]CþïGGGSGGGªªªJõõõ©¿¿É.AçÁàVùê™3iúøñe¾ûÙÏÚ>¾ô¥‰ôØcÿ‘þæoþ==;¦`9“» Šï?ù©¡a™wöì±}òôô\Jwßýîâ&Ú·ïíôì³_S°,P°Ö#ŠT~¹Ê9rä?, ¬õxúé©b›(ut\S°,P°Öãùç_IùȲÍô©O]V°,P°ÖëñÇß°KÁR°p¡£, P°,@ÁR°, P°Šdzz:íØ±£ìcFFF²KåÔÔÔ¤íÛ·g—ʼn‹:—ºÜN¹‹G+XÀ–.X«)D…{^XXH©µµµä…¢Í`¿‹p­%)R]]­` ÖF¬ÉÉÉlV+÷µQ¶BCCCêííMóóóE‹UN~:;;³Ý”SSSÙÆŒ_+ª`¥æææÅ5Xù™™™É VWW—,¸CÅY±§ž~z™¸ÝöYjtt<»ÆÛ™3_µ=À Ö­+XýýýY!ºvíZÉÇÄ:­X ¯`Á)΀]ì²#q»íóS?üýÅËŽÜ{ïÍôw÷ºí ÖÆ¬˜ª‹…î+enn.ÕÕÕ)X `mZQ¦ 7Q”­gžyÕöëý¬üÛÆÇÇKÎZåaåêĉ©¯¯OÁkÿ£~¥Ø&JO<ñmÛ¬µ¦¡ðt …¿/õØÑÑÑìYUUU©¾¾>Ûè(BP°6³G.Z°>ÿù³}@Ár&w@ÁZ^x9Ýwßü’MÔÞþ?éܹmP°,`¹o?ñDºÖѱLÜnûüÔ³Ï~-ÛUØÑq-?>Ξ³]@ÁR°KÁP°,@ÁR°KÁ,KÁ, P°,@Áº³ ÖôôtÚ±cGÙÇÄuãƒGÍ RÈ]›°Ü} Pq«Ø5‹epp0 /ŽOž<™zzzV¼¯ Ö;{ö½ìÈøè¨ŽŸˆË‹|úÓßKùÈBºçž§O}ê²³b°õw®T°ZZZÒÌÌÌâøêÕ«©©©iÅû,?áÈ‘ÿ\¶‰â$¶ ]°jkk—í2ÌÝVî¾Âb•“ŸÎÎÎl7åÔÔT¶1ã×Í4žß·¯hÁºøâ‹›òÏs+ÆMMï.ÛDwßý®ícllll\v¼å V±ûs·•»Ï –ÿ}„Ø5X¸‰bW¡m€¬÷9ƒ¥`U®XU¸‰b–m@E¬¶¶¶4;;»dUccãŠ÷)X~8,r@Á*qÛÐÐPvt`þ‘‚ÝÝÝ+Þç4 @EŸ¦¡ðt ù¿w,@Ár&w@ÁR°, P°,@ÁR°KÁR°KÁ, P°, P°,@ÁZGÖr‰›b—ÔÉ]J§Ü} PQkpp0 /¹HsOOϪ¾vff&íÞ½»ä…¢Í`Y°ZZZ²¢”ËÕ«WSSSÓª¾¶»»;={VÁ¬üÔÖÖ.ÛeXx[±\¹r%íÝ»wÉîÃêêêLCCCêííMóóóE‹UN~:;;ÓôôtšššÊ6füjlllllll¼) V±™§ÕÌF;v,•Üu«««Ë `k53XÑ,[[[W\<_SS£`•W°ÚÚÚÒììì’5Xe¿æðáÃirr²ìcæææR]]‚T^ÁÊŽÌ?Š0¯—Ú]xéÒ¥ÔÞÞ¾ìy8&&&²™«(Wqꇾ¾> p¬Âó`¬(R/^\ö<£££©££#UUU¥úúúÔßßï(B 2 –3¹ –‚(X –‚(X  `)X€‚¥`)X€‚¥` –‚(X –7 `)X€‚µÖKåä'.›SÌZŸGÁ¶tÁLÃÃÃK.öÜÓÓS²`mÄó(XÀ–.X---ifffq|õêÕÔÔԴ悵–çQ°€-]°jkk—í2,¼-¿`UWWgRooošŸŸ_óó(XÀ–.XÅf¥ÊÍTå³UQ°ºººÖô<¹õY…«³³3MOO§©©©lcÆ¯ÆÆÆÆÆÆÆÆ[~«Øùšš3X€¬ü´µµ¥ÙÙÙ%k§Wõµsss©®®n]Ï£`[¶` eGüåý×ÝÝ]t7ßÒÄÄD6;å*NËÐ××·ªçQ°çÁ*R°FGGSGGGªªªJõõõ©¿¿]çÓR°€-]°œÉP°,@ÁR°,@ÁR°KÁ,KÁ, P°,@ÁR°¼‰KÁ¬µ^*'?###Ù¥rjjjÒöíÛ³¯‰‹:ç.©SŒ‚T\ÁLÃÃÃK.ÒÜÓÓSô±ù{^XXH©µµuÙ5 Í`]°ZZZÒÌÌÌâ8f¤šššVýõÕÕÕ  `å§¶¶vÙ.ÃÂÛJerr2›Õʬ([¡¡¡!õöö¦ùùy ¨¼‚Ulæi5³Qccc©¹¹yq V~bF, VWWWÑb•“ŸÎÎÎ4==¦¦¦²¿WÌ V6ãTj1|îyb1¼, âf°ÚÚÚÒììì’5X%M2º¯”¹¹¹TWW§`•W°†††²#ó"ìîî.º»p||¼ä¬Uþ†Q®âÔ}}} àv-Ï£`[º` ¦ááá%{îééYóc×ò< °¥ VKKKš™™Y_½z5555­ù±ky ØÒ«¶¶vÙnÀÂÛVóص<‚l邵mÛ¶UݶÒcWû<¹õY…ë£ýhúÄ'>‘9xðàâï+{ÜÑÑakµ3X""""·"wDÁjkkK³³³KÖN566®ù±ky‘-]°†††²#þr‰ßwww/Žówó•{ìJÏ#"""R1k¥óWå¬<Vaò×f”Z·½) –ˆˆˆˆ,Q°DDDD,KDDDD,K6C^}õÕ´gÏžT]]Rooï’û§§§ÓŽ;l(¹ãß³###Ùå1jjjÒöíÛ³SÀÄ ŒEîÔ÷ì©S§R{{{öž+™:t(]¾|ÙS°d³ç7ÞHõõõijj*Çößþíß.Þç4˹Óß³HÙ9öÒÀÀ@jmmµÑdS¼gCü'a×®]6š‚%›=‡NÏ?ÿüŠS°d³½gs‰ïYQ°ä¶&výݸqCÁ’-÷žLNNf3"›á=³®§OŸÎvŠ‚%›<«-N –l¶÷ìØØXjnn¶K6Å{6·#.ïrýúuMÁ’­ð?«ùùyK¶Ô{¶¿¿?=ôÐCkº¶©ÈýïìÍ›7³Eïû÷ï·Ñ,Ùì‰!k°d+½gc!q,ÙlÿÎæGŠ‚%›<.\HwÝuW:þ|6Ž£[,Ù”ïÙññq³V²©Þ³Ìn#cV¬ÁŠB& –l¼òÊ+iïÞ½©ªª*566f»Wò‹U!‘;õ=[ìýê=+wò{öÌ™3ÙqäàÎ;SOOOš››³Á,KDDDD,KDDDDÁQ°DDDDDÁQ°DDDD,Q°DDDD,KDDDD,KDDDDÁ¹•9{öl:pà@ª­­M555iÇŽéàÁƒé™gžYõslÛ¶-#"¢`‰HÅçèÑ£Y1zê©§ÒÍ›7ӻロ^xá…5&KD,‘÷2::š•¢ãÇ/»ïþûïÏ䥫££#UWWg3\GŽI333KÊU¾\^z饴wïÞìë¶oßž{ì±ôöÛo/Þ?99™öïߟݳg»wïNUUUKžãßøFö˜˜a ñûW_}uY¹»xñbjkkËž+~ÿÈ#¤¡¡¡%®'Ÿ|2»ýÒ¥KÞ" –ˆÈƧ¥¥%+&ÅÊÆììì’q”«ééél†ëÔ©SÙ×Åm…%'?QŒâ¶‡z(ûº‘‘‘lÜÝÝÝþüùl|ï½÷.¾†ø¾ùÏ511‘ýþÁÌŠYˆßÇmQÎò¿w®˜…¹¹¹¬h…\¡{ë­·²û¢ ÆëKDdÃåc=»õ¢œD™‰¯/W°ÚÛÛ³Û._¾¼øu1Ž™¬\iË/JÅž+÷QîrùÞ÷¾—Ý3Yùýõ×—§®®®ìö˜}‹œ9s&?úè£þòE,‘¶`ÅlP”•C‡¥»îº+ÛMWX¨Š¬ØåWl÷aîq¹û g“òSê5Æmñ:J}ïHnöëÈÆ±[³X¡KDdÃÒÜܼlv(¿Tå’› Ho¼ñFZXXXUÁÊí²+µ;®\yÚˆ‚illÌ^ǵkײ窯¯÷/¢`‰ˆÜº /Y•ŸžžžìT ¥JNa©)ö˜Xp·ÅZ«bimmÍîóÍ7K>wnaìÌ%v9ÛEX,qtdn Wüúøãû‹Q°DDnmrÅ#ŠÈüü|v[¡—_Zî»ï¾%EéܹsËJMn6ì;ßùÎâmãããÙmq_®D]¸p!+^‘çŸ>»ÿðáÃÙô˜eŠ#üòŸ;vçå³jñ¸ø}±EîÅòÃþpÉ®É8ºPD,‘[žçž{.[pž[[GÙÅ‘Q€"q„_”¤ÜÂöÞÞÞe¥&ÊWSSÓ’]w‘XÏë­âëc]…˜ËéÓ§Ó®]»²çµRW®\Éž#}þ©Bü>ž7—•ÎÁ'Qûï¹çÙ" –ˆHå%W°âÜY•“'OfÏÙßßo‹(X""[?±ûñå—_ÎÂÇî¿O~ò“YŠË÷lTb†¬p—ˆ(X""[6qòÑXS•;K{ìNŒ5^•x®Ø='UKDDDD,KDDDDÁKDDDDÁQ°DDDD,Q°D¶êè‡>%‰ˆ‚%"ë,X"Þ" –ˆøï ?£6ˆQñÞKć¨xoxoˆ(X"âCT¼7D,ñ!*Þ"¢`‰Tä‡è… Å­·Þz+=öØciûöí©¦¦&íß¿? ­ùõmÛ¶mCÿ¼ÓÓÓiÇŽïë9¾ûÝ”þýßSZXXÝãï´m122’:::²×¯éèÑ£éêÕ« –ˆ‚%"·­`54·Bxà488ø^ YHï¾ûnzíµ×²Û>ÈDAÉYO®]KéÁº JéG?J›n[8p MLLd¯%^ÓÀÀ@jmmU°D,¹Ó Vuuuö^*ÿðÿºººÒÃ?œŽ9ò^y¹¶X‚Þ~ûítÏ=÷,ŽWúš—_~ù½—ÔÍÈÄý«)ZëÉg>³|3;–6õ¶È –ˆ‚%"wxÁjooOO>ùd:þü²r³9O=õÔâø¥—^J>úèb‰˜ššZV†Ê}ÍÎ;ÓåË—³ïsêÔ©[V°¢ç¬cSÜÑÛ"299™Íj)X" –ˆÜá+fTâCÿ/þâ/²Ù‘C‡¥+W®d÷566¦¹¹¹ÅÇFÈ­‹*,?¹q¹¯ùð‡?œ^}õÕUÿ™×[°b/ZáfhnN›z[Œ½÷gh¶KDÁ‘ÍP°ò3*±Î§ù'm$-TẨR¥¢Ü×\¸p!yòdv[$N¿î¨X©(÷5±)ƒÇm «˜]ÛèS?l¶m1>>^rÖJÁQ°Dä.XÏ<óL¶¸;ÖÅlMœk)ÿC=JF¬%Šû?ö±e»ÎÊ•Šr_óÈ#dß#fv^y啲ŪØnµ[;m[”ÛŨ`‰(X"r,ñÞKć¨ˆ÷†ˆ‚%">DÅ{CDÁ¢â½!" –ˆQñÞKDÖõ! ¥ˆˆ‚%"""R1ùÿܤV0飸ÓIEND®B`‚StackedAreaRendererSample.png000066400000000000000000000423021463604235500364640ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/category/doc-files‰PNG  IHDRXr5˜D‰IDATxÚí lSçzǯ4MÓ4MÓ¤iš¦iš&]MÓ4MÓ¤iš¦)B UÕ®S«Ët+ª–$vB;‰?òUP> -”´J¡ä†Òš@J¡|ÜB¦ % É…òQ ¤|&MøH¸ÏxÞ^gŽqÛ±}Ž}~ô—9Žãc?>Ï/ïûœçý™ „B¡¤êg„B! !„BÀB!„°B!„€…ŠòÿÙÏF8Û÷Ëg†°ÊPuuuɬY³äoÿöoåþàä÷ÿ÷åÿø%''GŠŠŠdß¾}Ö÷{òäÉGžCïËTék/,,”Ÿÿüç#bæ¿ÿû¿eÍš5òã?XX9W/¾øâ# 0š³t¬ÚïܹsyŽyóæedÌ,]ºT~ï÷~oÌxù‹¿ø [°‡€…PÊ¥# ±À€5ñýþýßÿý#Ïñÿø3¯½öZÜ1`!`!ä(ýÝßý݈dóÊ+¯ÈÐÐù™Nñ|öÙgfäå¯ÿú¯¬ ì7|zPG~ÂG2išðÂ… f:0ü8è4ò—_~)ÿû¿ÿ+·nÝ2öçþçBBÎUä4Ï•+WJR‘ +//Oþó?ÿÓÔähmŽ&å¿ú«¿’_üâ&GSww·”••É?ýÓ?ÉþášßÓi&ý?þxÜùÿñ#î××Ò±cÇL™‚¢>¯>ÿ?üÃ?Huuµ‚H-\¸ÐÔ£écõwt*o"‰9|zPŸW÷=Ö4aä¾^ô5ýÍßüùÌy_‰|&‘Òç ]úYÝ»wï‘ÇiýË¿ü˨璘¯OÜn·üÑý‘±×ë}ä9â}½±³xFhBB K&<Éüå_þ¥Ù¾}{Tðˆ°Æ›:ÒçWccã###ñL7Eпþë¿'þ 6ŒY/¤@Òßß?üüO?ýtBS¦cI§C¿§€ kš0r_‘ð˜ÈûŠ÷3‰¦ÿ÷ñ;›7oNÈzÆ«G‹÷õÆrÌ,„,„Ò"ŸÏ7jÂÑä­ µ®®.ê(ÅX J¡A Ž"èTcä~tä#$½‚1þìÏþløªÅ³gÏʤI“Ƭ¶¶¶Û:=õý÷ß›Ç=ztÄÏõ5é{м_ÕÔÔôH’ÖÑ‘I4!G^=¨ ¡S±c]M8\Äû¾âýLF“Ž…ÿNø•‚‰Žx†a¢1Ë>b‰[„€…PR¤£T!€Ëšì®]»3`EJ“døcuÊ'¤ÿùŸÿñ³·ß~{Äï*Ü)茶_ ßÞ»wïðcùË_F‚È×£Óvª'žxbÔçJ41G^=¸uëVñŒÞlÚ´iĈb¼ï+ÞÏd4éc9‘ïG¡ZëûâÙÿx¯w¼c`!`!”véÝã?þH ÷sÏ=s¢Ò­ù¯ÿú/39òþø?ù“?qÿ7 ©¬¬ñØ?ýÓ?iô$”¬µÞ'²Žg¢‰9|z0Tç¦Swc]M8Þ¾â}_ñ~&©ÁŠL‰¡x> ! ¡´K“ž/_¾ÜŒ~„'"-FŽ%QéïGBÓXS6ñŽŠŒõœZ®ñz5Eî7òñMÌÑš‹ŽæðiÂñöïûŠ÷3MZ¸þø;v$ XÉŒ! ! ![IGNFk S‚ñLÉ„¤æá÷k#Smý0Úã#GƉV+6ZáuäsG«% Wd¡ýDs´æ¢£9|šp¼}Åû¾âýLF“^íþøþ玺o­[Óú½D+‘× `!`!dŸ/ËïåùçŸ7­Bp£µ+‘…ØšôÆEoDj´Ä¦K«„ßÿ«_ýê‘שub£=Odñ³Ödýö·¿ù¹5qk/'Õ¿ýÛ¿xüéÓ§'”˜#§Ç«s‹u_ñ¾¯x?“xFätz¡J»6¯Õ«QcÙO¼£šÉ¬±F)BBI¬X­WØ…+”DCþüóÏÍý‘õA )‘Í‘ÓA‘W†ŠËõjÀPÛ„Ñ^³Ž–DîSGŽF{n­7ÓQÕ«C=©T‘í´“>®¥¥%nÑ÷9Å^Ó¥ÿ‰ &Ž·¯xßW¼ŸÉXŠÖ,Ñ+÷F»?‘×ë{-nBBISd‚­]ƒ^þ)míñcµ~-®_¿~B}°/^<â>}.mñ Ú¸qã¸Ïz¹‹¼*1Ñz¥ Œx¼ö¾ŠTx?¬ðiÂXöÏûJä3K‘ G£Y Ó¬D^o¬ïe´¸EX%M:%¨#Ú*A‹ÚCWjé­N jgõ¨DJGK4†š•ê´ÎÏþss¿Öê„’¿þ\§ÇKhgΜ1¿§u=¡×¡…Î:‚^LíytŸ‘Í+õªÈ´K¼¾V}OúÜ úútZNG©Â;ƒë¨™¶k½~}ÞO?ý4}š"ÛDJ}´ÞN±î+Ö÷•èg2–´Õ‚îC—[ÒçÕ}ëÈ“N_êûŠå ÌÑîOäõÆú^F‹[„€…B!`!„BX!„BÀB!„°B!„,„B! !„BX!„BB!„€…B!„,„B! !„BÀB!„Bά¹sçŽØ.**’Y³faŒ1Æ'lÇ`…«ªªJ>ýôSŒ1Æã„=gÎ ÀÂcŒ1€`aŒ1ÆÀ°0Æc `XcŒ1Æ€…1Æc ÀÂcŒ1€`aŒ1ÆXÆcŒ¬xÔÝÝ-Ó§Oq_oo¯ÔÔÔÈÔ©S}>Ÿ\ºt ÀÂcŒ1€5žrrr†®çŸ^ZZZäÁƒÆMMM2sæL cŒ1ÆV< ®)S¦° ×äÉ“,Œ1ÆX‰Vss³Ô××ËÎ;åêÕ«²oß>Y²dIT° 9\ÕÕÕfê±³³Ó ½e›m¶Ù¶ÛöÞƒ{åÐׇ¤«§ËÜîøb‡c·v%>ضÝvÖ–BUQQ‘ª§Ÿ~ÚÔcݼy“,ŒqÖXáâÅË/Jîí\K]t£Èò× ®û¦Ž¸ÀŒ`¥° äìÙ³ÃÛ›7oÇ`aŒ³Â[¿ÜjÀ&а~g× KZÚZˆ `¥°¢Õ[Qƒ…1Îo:ºIÜ·Ü*¬‘ö xd÷g»‰ `¥ °´-ƒNª´Ø}ýúõš,Œq&»þX½© €õ¨k/Ö+ÀJf›†ðv šôjBµÖbQƒ…1Îd¯>¹Zò†òFÀ€Ý ¿i f0€E'wŒ1Ûox븰F·N¡noÛNì` ÀÂãèÖ«ãF kt—÷•›6İ,Œ1öþûei÷Ò1!ÀÛ¯ž}•X€…1Æ?ù“ƒŸHí¥ÚqÀß;7SÀ°0ÆN÷žC{¤êJULð`ï‚›²«u±…, cìT+ÄMVlž÷ý<3åJŒa ÀÂ;ÌÚ…¼´¿4.p°b÷Ê3+‰3 `Xc'Y×Ô.äñB€Ÿ·|µ…xÀ…1v‚u]ÁÂ… €ŸË~(£u°,Œq¶û½Ž÷†++1/:¿ˆØÃ€…1ÎV¿sìë XéóºãëˆA `Xãls´u¬ô.¥£‹À°0ÆYb½š-Y `±”°,Œ±£­}˜–œ_’TH°hÝ€, cìh¿tᥤ€51ë4-­0€`aŒ3täªöbmJÀJÎR:Ú‡ŒXÅ€…1Î뺂s.ÏIXɱ.¬M¼b ÀÂg€w~¾S|½¾”‚€•<¯:µŠ¸Å€…1¶³··m{]AËúz¬æ#ÍÄ/°,Œ±=‘¥o,ë[7è´.qŒ, cl#ëÒ7Z4. °’ïyßÏ#–1€¥êîî–éÓ§?rÿ¹sç$ÈÔ©S,Œq*½±kã„—¾°ìá·N¼ELcgVNNΰÃuåÊyî¹ç¤««‹,ŒqÊ]¬>íp`¥¶uƒ^¤@lcÇOFÖŠ+¤µµ•)BŒqÊ­£ÉXWÀ²—+®UÈ'?!Æ1€®3fÈš6mš™T`°0ÆIõòo—[ Vj½´{)qެpMš4IÚÛÛåÁƒrÿþ}©¯¯—`0¬BWuuµ©íêìì4HoÙf›m¶CÛçºÏɪK«Lο“/î»ns›îm,+÷¯ÛÞA¯¥ûOõöGg>"þÙNx;++\ ZS¦La cœ”îì•W+%x-hù #X©·¶ÜоfÄ>fë¡fΜ)}}}#î{ì±Ç,Œñ„¬kÖi¯$M¼–3‹¥t0€¦ÆÆF©««®»êèèU«VXã„ÝÒÖ"žÏpÒ°œXê7N¿Á÷;³MCd»†Õ«W›"wTغw1NÈïõþ#ÝÙ,g–^)ª]úù>`:¹ÓÉcœo:º)j+ËY€ZJgïÁ½|/0€`aŒ'âuÇ×Úã Àr`©ô,à», cœ¨µæf¬¢–3Kýö×oóÁ€…1Ž×ußÔ›d,ç–û–Û\ôÀwXÆ8‰p`9°¨Ç€…1ŽÑûì7K£Äš`,g–zÙ¹e|w0€`aŒG³.ê«Í$ãI®€¥Ö«Lùa ÀÂGX—¾©ºRwb°¬P=Ö®Ö]|—0€`aŒCÖĘ(¤XÖðR:kÍ3ß) `X;Þ;?ß)¥ý¥ 'U Àb) `Xã0ë’' ÀŠ\J§ùH3ß/ `X;ÓÑÖ°¬døÅË/šš>¾gÀ°0v”ß9öŽ)JNF2°¬H—ýPfZ}ð]À…±cüÖ‰·Æ\úÀ°’Xz»±k#ß9 `XÓÀ°’ X®AKé` ÀÂ8»Owv ÀJ`©+®UкXÆÙÙ}AÏ‚”%S À °Ô+Ϭä»`XÆtg°¬d–ú½Ž÷øNX€…qæ{ïÁ½)‡+ ÀаôøÐºÀ°,Œ3~ä*]Ð`X±–zÑùE|?, ÀÂ83½ã‹ZúÀ°RX,¥`XÆém‡·‰gÀ“Öd `Xñ–ö`Ó%šø¾XXÝÝÝ2}úôQ¾{÷nÉÉɰ0ÎRÐþAR–¾°¬T–º¼¯ÜÔò½°lX N!GSgg§TVVXg©µc¶6u´"™XV¼€¥^r~ ß]+s¦£TOOx<¹}û6€…qzÝñuI]úÀ°ÒX,¥`e4` HAAô÷÷ `!° 9\ÕÕÕfêQGÀôé-Ûl³mí£]G¥ñ‡Fqýö§‘«ü;ùâ¾ë6·éÜ®¹^céþõ¶ââ‹RrËkÙþuÛ;híþí´­€ËãËï”ËÇ¿àûì€í¬¬²²29wîܘ#\Œ`aœ9Wn:ºÉtÅÖþVî[î˜G ²}ëŵ_J^Þ”.ýZÊwnßñW¤¨¯”,›`©5–uµ¾ãŒ`e`…×fW§`al? Òé?í4Ú•V®Tt­”@ Wrso?â’Ê‹ܼK|GWJñ¥‡¯s0À²`©—[ÆwÀʼ¬x~`alÝzïõþðèT¬ëN,÷B)(»:*`EÚUÒ'e>•Àgë¥üÛ’wÓ `Ù°´†P¯‚å\`Xã¤Ni2Jô @§V`ûVN±V¤g¹oHÑËÇÅ¿£I|ß,–¢~€e`©K®—ÈîÏvs~°ìÛ¦a¬i@ ckG§ô,Hj#P'ViOä¹nM°¢9¿ú;)oÜ#þ¶5R|Å/³†¬tÅeíÅZÎÜ1Æ£NÕ«{ºÀŠÃƒyR¼èä0%°"=ÛwUÊÞ<,åÞ‘òo_’‚볬ÆeÃo8XÆŒN}bjGÆ+F°’\Ø~hÃJ%`=â‚ëâ}í¨™ž,ûz©ÌþÁ`%1.õêX–Ò°,Œ)FÏØD–©€å½WQ¿u€ÅEs¾“à;%xpƒ”]˜ciã×L,3ýÛ_*{íáœ`Xg«wµî2Sz¹€ ÀzøžßþìÀ±° dÝÙ"bñ ìhÿ©—GV°F÷Òƒ, ãlñ¶ÃÛdÍÉ5fºO¤µûH„Ó+xrYT¸±#`…[‹ñ=K‰¿q·¬Ï•€ËR:,¥`Xgœ÷Ü+ïu¼'¯yÍ\½Tx£Ð±‰,Ëô¼ªîÎHÀmZÑ×z)ëž+ù·\V„ nÈÎÏwr¾°,Œ3¡÷Ôòo—KåÕJÛM÷X±õ¼ÊÀz¤=Äì)_Ûjê¸tZ1T<ïô¸œsyç/ ÀÂØ.Þ`¿4i–Õ'W[ru€•ä÷©…ã¿ëy•­€u©Ÿ°:®âkåS<Ÿì¸Ôï1ç5 ÀÂØëGZ¯¡W÷Õ^ª5S g`i£Ï²]c‚H¶V¤ |—Åÿî'Ãu\v®TÄ¥Nçs®°,ŒSìímÛåí¯ß6W÷z޽,Þ €¥ÕǧV´âym‚ªã*(ÎÚ¸ÔQh­›äü`X§hí>¯ŸfŽ,‹ìy`s,ömŒyyŸL‹Kýþs>°,Œ'X?µêÔ*3ÝYŒNÇlçV´žWÖøö}Ôdé¨n*ãRÛ¨pž°,Œc­ŸêÚ(˾[Sï)Ë€Uqâ•ØGm¬G!kýg–µ~He\ê\Ú¯Žs'€`a犿󵬉KZf) ÀÂÙ{ À°R X¾Ó‹&Ö½ÀJ¬WÖ—ofM\–\/1(r~°’®îîn™>}úˆûš››ÅãñÈ”)SdÚ´i2þ|éíí°è=•øåó€•dÀÒ>M…U,‹ìßÿ«¤/­cU\êùó=€•Tåää ;\^¯WÚÛÛåÁƒrÿþ}ihh—Ë`9¨÷TÍ嚤ôž°¬TVÕž÷'¾þ€5!{÷¼›TȲ2.uxò€•ÐO“'O°²ÌÛÛ¶›“Ê«g_5Ͱ¬þ~,î],‹Im­i„¬·éÞ~ýÆë–î_·_¾ö²¥ûmg-`­_¿ÞŒF pa†^ñ§S©Xó,F°¬Á*þÁkš^&kä…¬äºäå¯'´´ŽâÒç*;¼;|¦!l6–Ž~h¡;m2wm?m£ªEu,Ë À ÖH*X)x?s¾“âkå€`E¬¶¶¶qG­,ûZ 5CÔ,+[+xr©äå X`wù•„Ö/°¬¬¬ð6 áí¢Ý?V€ek!{d{ ÀÊtÀÒ«ÔJjÏ%¬.­Süƒ”»À°œ=‚E'÷ì𦣛¢¶Z°¬L¬àžÆ”@€•bÈš= ¥ß,°, ÀÊ\¯;¾nÔek,+“Ë{9˜”žW–uë–w-°, ÀÊ<ëŠðc­ `X™ X×eoIYò°ÒØ+ë³u€`X™ãºoêÒ²¨.€`YXU_®IiÒ°ÒëŠ[ÆücÀ°,+cà À°2°Šú=Ràí°²m$냒;˜`X€e?ï?°_–v/MÛ¢º€e`ùÞÛ“òd`Ùki À°,K׬½T›¶Eu,Ë ÀòŸZ’–D`YgÏÒcR8P `X€e¥otm¦t-ª `XV–û–[Jœ°œYóÎJQ_)€`X–uÞÕº+á/€`e`[×¥-ÁX6XZ§æüðÒ:€`XiõÎÏwJiiZ×|°,+KG3\Þ^Ëa-­`X€•6oýrë„ÁÀ°2°¼ë¤5±Xö±«¤O*N/á\e“×Pu|…Ô[n ÀJÛº‚€•­€å;þJÚ“:€e³« ×Ä÷›å–E¯A×ü t½&%‹OHYÙ¶ˆ ÀJºß9öŽ)öM÷šo€e`é%ûZ‹`9Û¡¤îûêu+ûÓ¹K¯=òYXVVù­oÙíÀ°² °ü;š,9yXö¬ÐÒ:³†¬TZÛdèBêá``XŽïÎ`XÙXÞ 5’çº`áG’ºï£¦´C–Ës©Bü»MÝ[¬Ÿ€`e´ãéÎ`XYXƒy¦á¤U'oËÞ€¥ö6}8êÒ:V|.ÿvé¢Ë4Ž¬Û·oËòåËeÚ´i’““3|AA455XÖ}AÏKÕ°,+Ëh½¥'oËþ€e «á×’Ÿ¤šT§––›ø½*eoNÊgáÀZ¶l™«C:~ü¸<õÔSV–wg°¬Œ¬ÓK$ö€`Ŕԋ^?,î$\UíÀÒc8¼ZŠçŸIúg‘õ€¥#WÍÍÍòàÁƒ€¥š4i€•Þ{poÊá À°ì XU«:,?yX™X¡õ ®Ï°Æ+\o]µpÀŠQ“'O6p¥ ¬«W¯Ê”)S¬ ¹J×À°ìXþ¯_‘`ð€`ÅÔg¿Ø-ýÅV”ÂõàÇ[Æ,\°bÔ /¼`@¦§§Ç–ÂVWW×Ãôð@{<–½ã‹ZúÀ°2°tê¢ÀwÀ°Nê…Á‹Rr9`éw¹{®øßý$éWâ:°<8¢+Üííí–M½íð6ñ xl±¨.€`Y²ï–fsâ°¬‰$u·ÿ’”ôT9°´p]¯ô¾Úe‹Ï"+Û49rDŠ‹‹Í” Ö]é„mmmq=Gww·LŸ>}Ä}:¶råJ™?¾'õÀÀ€5AÐþAR–¾°¬L¬òós‡ÿÒ°¬‰&õ|OßCÐxÉ1€ZÊ&ÑÂu+Šv¢jãÆ²yóæáíÆÆFY±b€5oìÚ(®A—mÕ°¬tïSÿê.^trøÄ `XIIê×¥ìë¥Y X:­¹” €•BÀmz00Åò\áÊËË“¾¾¾áíÞÞ^™1c€• ×_—Ô¥o,+«âІ'N ÀJfR÷}ùfÖ–¹"ðã-–´3°RXS§N}dÊ0ò¾X…®êêj3õØÙÙiCoºýÆ7oˆë·.qßuKþüŸ†yÞ¦s»æz¥û×ÛÚ¡ZK÷¯ÛÞA¯¥û·Óvõíê´î¯d¨L ý?]Ý”ŸGÜî»RSs}Ķަ{[ËÊý›&šÞAK÷o§mMêú<÷mœp¼V_¯¶üûùRÏ*©jÚg®´êóoÚ">l5E¨Sy'Ožœ`E´± ¬è^þírÛ-ªË£WVŒ`•4ì}ä/SF°ÁJŨ‰ë‡š1°ò\¥Wúê?•@ÅÕ¬ø,²®ëûï¿ø‘7-#XÖèKß¼tá%Û-ª `XVVÙ™…QOœ€•ª¤^޸DŽg¹JaÐwü)[“Óq `E‘ÂP¼F#K¯FìïïQƒõÌ3ÏX1ÂÕœËsì·¨.€`YXƒyR8ï €`¥=©—®m×ÍÛž«ýmk¢^`Ù°®¶lÙ2aÀÚ´i“¹r0ü*B]XÀŠ¿;;€`9°|oõÄ `X©Nê‰,­“ês•¾žªý¥¨æ¼­ã’"÷Q <ñ<}°ïÎ^ÞWnÏEu,Ë‚¸,ºX1fwi ÀJGR×Ô¢¾RËÏUÅ×ʥ꣭âòöfD\XÖ‘«ÊÊÊ1aˆNîÉwK[˨ÝÙ,Ë©€åyýȘ'N ÀJWR/¬º`Öê³â\¥ûÕ¥l´_W&Å%S„6‘“kë—[ÇìÎ`XN,Ó“hœ'€`¥3©ëÒ:žï«Òr®úi)›—¤ôõvÉËÊȸt`×û*Ñ>XVbÞtt“¸o¹m»æ€`Y—ýÅ1MƒXVº“ºvB×V©:Wéjå]Ë¥dÁ7—€e™ëÕÇ´ô €`9 °Ê7íéÄ `XV$uíŠî;½(©ç*]ÊÆßú¶”ÌéΚ¸dŠ)BKüÖ‰·bnd`XN,³ðnŒ'N À²2©ŽÖMø\¥£µ•¿~/ékX–®èñxdòäÉŒ`¥Ñ+Ϭ´ýšo€eE\j_ŸÑz^X–“zÅÁ «J.$Ðü‘äÎîÏÚ¸t4`)Ô(HMš4 ÀJ“—v/µ]11€`Ù°|»ã:qX–’zåîÍ2k(¶s•ÖoiÓÜüÁ¬KGÖôéÓ¥©©ix›û÷ï›û÷íÛ'kÖ¬°R´ô €`XÑþ¢ŽÙó À°ìœÔuýÂÐK‘ç*³”Í7‹¥è•.GÅ¥£K§®TO>ù¤œ9sfÄϬÔ,}`XVü=¯,ËîI½¼a¿YZ't®rß*ßÑ•RºàŒ#ãÒÑ€¥õWÝÝÝæÿÁ`PÌÿ»ººÌ´!€•š¥o, ÀŠølÛW%tâ°,»%uï«]RùÝñZ/Å+ŽŽKGÖ®]» d©æÍ›7¢þ*77Àš ÷Ü+UWªlYë`Xv‰K½Š*Ñ+¨,ËŽIÝ1`Ù¨MÃÐÐ,œ9s¦œ={À²xä À°œX¾÷ö$|â°, À²`iaû7胕"ïü|gR¡À°²°ÊÎ,œÐ‰À°,ËV€¥S€:RUSS#mmmÃ…îÖĽíð6)í/µu11€`Ù!.µçUñK§, À°²°¬Â›‹>ñÄRWW7â*B+ù‹6X€öyî}oÂ'N À°,ÛÕ`ݹsGvìØ!^¯wD£ÑçŸ^6oÞ,ýýýV~¯ã½qm°,ëÿ{^¹ nX€`ew‘»ÂÔ–-[Äår ƒmb÷;ÇÞI\XV¶–v¼N¤ç€`XVF/ö|ìØ1yöÙgY*'‹6X€•xÏ+ À°¬Œ¬›7oÊöíÛGL2‚5¾ë¾©ËØ5ß,ËŠ¸ÔžWeW, À°²°îÝ»'{÷î•ÊÊÊïZƒ¥Ó…V’m°,§ÖDz^X€`Ù°B E#¯"LwsÑL,]WpAÏ‚Œ^ó À°¬ˆËòo_Jú‰À°,Ë–}°bZ[[SÒ«··×´ƒ˜:uª±Ïç“K—.e4`éÒ7µ—j3~Q] ÀJw\º'Þó À°,Ëö€ÕÒÒb–ÆI¥tªQ÷£ð¦ÖîñºO¦V2—¾°,§V2z^X€`eôU„ÉÒ”)SÓQ³L¬d/}`XN,ïÕ€ä¹nX€`XÉPss³Ô××ËÎ;åêÕ«²oß>Y²dIÆV*–¾°,GÖ_¦ìÄ `X€å8ÀR¨***2PõôÓO›z,m ¬B—¿Ú/‹Im­T]©2·Vlûnù~Z7íN¾¸ïºÍmº·5‘Y¹Ý®¹^céþõ¶v¨ÖÒýë¶wÐky<Øe»úvõ¸Ÿsü-s‚ËÏ¿#n÷]s›Ìíššë)}þX¶5‘Y¹Ýöz-Ý¿¶5©[ýzª«¯[~xÀ\Yw±0˜r°°, À°’—yCyRþz{Z“€`XÑ=·¢gøê>ªð«ûÒe À°,+ qüruÚ“€`X?NùJ®Êk.ù(Ð,Ç À°’xâ*]Ù!þS/§¤ ÀJ`éçUqj©øÞn5ËOÈ,+ûãR¯î«òèt_›|ê«—W0ëàÀ°²°†‡•—“àW«’Z`%°\ƒ.©èZ)¾]$2 ÀÊò¸ÔfžKü§Lï©ÃE¯ËõÜ‚¬‡ ÀÊ:ÀQÿÅZS( `Ù°ôó|¹ZJæt“È,+KãR{O½îïfÿGrªlqÊšyX€eá‰K ¥Í•h-kâ€U8P,7D-\'‘XVæÚ•Kæ–uK}ð€™î»Pþ"p`XN¬W6$Å×ʬ4VÉå€T¶|8fá:‰ À°2ÇÚÌóµ@—|h6 !G6ó´CR°,Ë‚Wžë–¹R­¬{.€•BÀ*é©þ˜ ×Id€eïÑ©UÁ¯ P}W4'#’:€`¥\çÎ{xò ÈÔ©S¬ˆõæa)ÿvÁ¸WX±–Gÿ7‹Äûj‰ À°2|ºïpÙ› -5`XYXW®\‘çž{NºººÁÇÅóϘ¥XF-kÀÌ“`çÊ„ ×, À²çt_¦&u ÀJ©V¬X!­­­LƳ/]ó0JA<€°Ì—«ò<‰ À°2dtj]°5æé> À°¢hÆŒ²¦M›f¦«ªªd``ÀŠõÊÃ_o.ˆ°F–^üx‹ä—^#‘X–Mã2Ó}€`EѤI“¤½½]aF§.xçË]·[îäç›§·éÚÖ¤žÎýEÛ¾^]méþõv¨¶ÖÒýëöÍ`ÐÒý‡¶³°Â¥ 5eÊF°°Ï×'þwŠ÷B#G°ôŠK]ó1Pq•x`‹,Äeqá€%£SŒ`1‚ÅÖCÍœ9SúúúFÜ÷ØcXLdzå¡ÿÔ’”¬yh'ÀÒ÷§…ÿ% O1`XÇ¥.5³ØwZš;äDIú;£X€¦ÆÆF©««®»êèèU«VXIJd%µç¤æ‹u’Ë•U€¥ïG×r,Y|‚‘ À²(.Ë‹¯™ÞSŸøß•Ë>Û€`9¶ÖêÕ«M‘»N *lÝ»wÀJr"s{¤âÐ)¸>;£Ë®·®3WR2`Xé‹ËP1zCp¿){#〠À°èäN›†”&2]Š'øQ³”ô–g`y®$¸§Ñ®3`X©K­ŸÒÞS[ÛmW?`X€eÛDær JðÝO$p¾ÖÖ€¥`åø:ué j],+uqYá¹d€*ê§, À°2"‘Ö|)eg&¥ >Y€¥W–¯m ¬, ÀŠ-. \7¥¦´GVddý€`XVF%2Ï’¯¥¢ýÍ ÄO°BWz–£˜À°’¼ÔÌ+þ¯åýª=¦~*Û¦û, À°2"‘Íö]•ÊÏޑ¢´–.e<¸Á¬µH11€`MÌyyCRé½(kƒ_Èn㈥fn9¬, À°,OdZØý~\ññVèŠ@w’—²°,'Öì‚ë¦÷”®Ý÷©¯^®¹KGM €`X–™ÖA•oÚk ΓXºvb¬WX€ñ<ž+¨ƒ{¤Ëûj\Åè€`X– ™N=øßj“Òós,Ï¥Š¸¯°,§–6ó¬ •ý›'\Œ`X€`Ù<‘ùVÿׯõÕË5w)‰ À°, À°,+—_“žÓÒÜc–šIæBÈ€`X€`XYXZŒî+¹*u£ò±³œ/ª!‘X€`X€`ų ®[ru€`XÄ€`XVÖ–^Ý÷²ÿ”4vȉ’Å5Ý`XÄ%€`X€ey<¸nš«ûVÈ'þwårD`XÄ%€`X€•H3Ïw«öË‘²7²nºÀ°ˆK À°,+å..eÒØ9¨Hd€E\X€`X±xÖø‹¯H]y‡|èß*¿)Y&×s Hd€E\X€`XñL÷Í/;+ëËÊ~ƒôxId€E\X€`Xñ®Ý·¤ô¤l©øØ4óìÏM"°,âÀ°, Àаf Ieñ÷òvàsÓÌóû¢J€`—€`XV<€U\ÐoF§êýŸšbôò‹Id€E\X€`X1–ÿªøŠ®Êª@»l¯Ø&_Ï^"ƒ¹ù$2 À"., À°¬x{OéR3ZŒ~¡¢–DF"°ˆK À°,+ÞÞS¡¥fN•,$‘‘È,âÀ°, ÀŠÇyyCRé½(kƒ_˜Ñ)ªñÖî#‘‘È,âÀ°XÍÍÍâñxdÊ”)2mÚ4™?¾ôööX¬B÷ YäûFƒ{ P%²v‰ŒD`—€åxÀòz½ÒÞÞ.<û÷ïKCCƒ¸\.Ë!€U^|M^õ3@¥½§Æ"‘‘È,âÀ°¬5yòd+ «ÀuSž+R8jzO/ª!‘‘È,âÀ°¬t¨££ÃŒjX™X¡«û‚ûG,„L"#‘XÄ%q `XiÔ¡C‡&æÜ¨5X V!‡«¼¼FÜî»’ŸÇ$u½µbÛë´tÿz«€eåþg\—µó¾–}µ»ä‚w¾ Ø;ùùr×í6·éÚª­Mëþ¢mz½–îßNÛ·««-=×kj,?šÈ¬þ<4.¡mMêV¿žë¿V;œ/oþî «?¬¬õë×KUU• paŒ`ååJ°ø’¬ ~%;ƒÈ·³k)`¤€,â’¸d‹,;©³³ÓºÓ¦Á¾€¥W÷Íó}'õòyééË÷ÈHdqI\X–]ÕÖÖ6•~ÀÒfž+*ËûÝr¢dqÌW÷‘ÈHdqI\X– ”““ÕVúK›yÎñ]Õþò£t‹ô¸‚$2€E\—€E'w+ÀÒbt]jfcù'æê¾>·‡DF"°ˆKâÀ°,+Àò—\•å]ÒRña\Ó}$2€E\—€`XK›yÖzÏÉúªVÙïÿ•\pWÈHdqI\X€`ÅãP3ÏuÁVé ®”ë¹$2‰Œ¸$., À°âqiQŸ¼è’ÍÒQºrDwt‰ŒDF\—€`XqŒN)P}W4‡DF"#‘—Ä%€`XV¼£S¯ø¿–úà9\öfÜk÷‘ÈHd$2â’¸°,ËÑ€¥½§¨´]BS`‡œ*YH"#‘‘ȈKâÀ°,+ÀR ªô^”µÁ/äS_½\s—’ÈHd$2â’¸°, Àаt©™ÐèÔ1ÏRés•ÈHd$2â’¸$.,+3«¼¬ÊLµYíyïÌèÔ~ƒ™îKe3O‰ŒDF\—€`¥°ªmòÁØáÄE"#‘‘ȈKâÀ°,‹DF"#‘—Ä%€`X€E"#‘—Ä%q `X€E"#‘—Ä%q `X€E"#‘‘ȈKâÀ°, À"‘‘ȈKâ’¸°, À"‘‘ȈKâ’¸$&, À"‘‘ÈHdÄ%q `X€E"#‘‘ĈKâ’¸°, À"‘‘ȈKâ’¸°, À"‘‘ÈHdÄ%q `XVT=xð@V®\)óçÏ7ð¤°Hd$2qI\X€•¨6nÜ(›7oÞnll”+VX$2‰Œ¸$., ÀJTyyyÒ××7¼ÝÛÛ+3f̰Hd$2qI\X€•¨¦NúÈ”aä}‰ŒDF"#.‰K À°âPNNNL÷…ê³"ËírI ¸ØØÿðäú?Ûl³Í6Ûl³mÿm¿M^ÇãqöB!„Õ²=`Hÿˆ¬gžy†O!„BV¢Ú´i“¹r0$ýÿòåËùäB!`%ªxû`E*¼6 cŒ1Æ8Qg`!„B1‚…B!`!„B! !„BÀB!„°B!„€e>|Xžþy™û쳎ün¨ôg•••Žþ~8.c9W®\‘çž{NºººŸ7ÂÕ××gŽ‹…BWKKËðùRÿ@9s&€å)]ïܹsÜÇeûI4Öã’þ¥Â±Èîc1ÞqЩŽlÞ¾}ÛÑß'ÖXÇaÅŠÒÚÚJÞˆ®b²wï^G éW°Ê¤ó%€•DéÔß;wX±UGG‡Éqú±ÐѼmÛ¶™)C§]™!|ÍQ'?ô½kÒŸùñÇwf̘a KËt:¨ªª*®<²ñ¡Óa/¼ð‚c¿:Â___oìêÕ«²oß>Y²d €åÅš²=Äúþ:$¹¹¹Y]ƒ˱Më2 7nÜpÜq(++3õ6|?FJ§ƒ°æÍ›ç¸ã0iÒ¤¥šXµFÑÉñ°xñbsÎtêwC¡ª¨¨È@•þñ¡£]7oÞ°œ"¥ïXþÚtÂ_èã‡õë×gõ_¥ñÆÄ½{÷LMž@œvœVÈkL¨0tjÄiÇA‹ãðÿÒ‹£²¹V5–c¡£ÜgÏžÞÞ¼y³)+°"j°Æ?ZÀ¨*[“H<ÇÁéßp ÉO<á¸ã ÅË:‚®Ç{̱ñ £5ZNáäóe´z+j°¤ãÇË“O>9|å‹înܸÑq d¬ãÐÖÖ–õ£V± ½ÌXï׿εî@k°ôãäï†Ó¿áW–*\­\¹RÖ®]ë¸ãÐØØ(uuuÃç …‹Ñ®¬ËöÚdkmf¼çK&Té÷#4 `9HŸþ¹)DÔ!îgžyÆAxâpÊ4ÈhÇÁ‰}]F;ÚóIªþöøã›¢^MªNün8 °Æ:Û·o7Óz¿^®>Ú1rBL¬^½Ú¹ë¨®Â–N£;ñ8è9âĉŽÏ¡ ÛZ§ªñ ÖZ,j°B!„& !„BÀB!„°B!„,„B!`!„BX!„BB!„°B!„,„B! !„BX!„BB!„€…B!`!„´wï^ñz½2uêT™2eŠLŸ>]|>Ÿ´´´Äü999Æ!`!„¯ùóç0Ú°aƒÜ»wO™ÖS8Q˜Ñß °Š‹‹Í}===ÿ§Û:’‚¶pPŠö\¡çP¸ éüùóæ>É üÉ“'G€Ó¼yóÌý:ú¦Ú½{·Ù^²d >BBY X:¤° åÉ'Ÿ4Ót‘@ °tÊ/Úôaèq¡ŸGŽ&…?f´×¨÷éëmߪÐèW 0Û:­ èBB%M¹¹¹Œ…CUH¡‘ ††9sæŒÜ¿?&À MÙ67<%°TÏ<óŒy湞zê)>x„,„J6oÞ<¢&*\+V¬0­FƒœH¨‰ö-8×û´Ö*š\.—ùùÕ«WG}îСN †¤SŽÑ¦£I¯Ž Õpém]]}zø¾¶¶6sŸþ,QÇ7à¥Ú¹s§ùyMM)@×Q&½Â/ü¹u:/ôUÓÇéÿ£¹GÓ•+WFLMêÕ…! !„R®;v˜‚óPm•^e§Wþ)©ô ?…¤Paûš5k…¯3fŒ˜ºSi”>·Ö[éïë^…Ò¶mÛäÙgŸ5Ï«µR—.]2Ï^@ÞÊA­ÿ×ç i¼\ÚDUþ‹_ü‚! !„œ§`iï¬d©±±Ñ<çúõë9ÀX!”ýÒéÇÖÖVS¯Ó/¾ø¢!]¾'YÒ²È:.„€…BY+m>ª5U¡.í:¨5^É’>—NOjSU„€…B!„,„B! !„BÀB!„BB!„€…B!`!„B! !„BÀB%þeÿÙϰ Œ°BYXˆÏ!`!„Hî|! !DrG|! !’;â3@X!’;ŸBÀBÙ2¹Ï;w\§RNÜ?€…€…r`ÉÓOêHÀ¸yó¦,[¶L¦M›&S¦L‘¢¢"Ù´iÓˆÇtwwËôéÓcœ1v×þ›››Åãñ˜ûõçóçÏ—ÞÞÞñ÷?Æ¿xößÔÔ$ÅÅÅæþ©S§J0”žž ! !` X@@6nÜ(÷ïß—ȱcÇÌ}!åää ;€5Öþ½^¯´··›ûõç âr¹’ X±î_­À÷ì³ÏXX!klÀšùDå'?ù‰×Ý%¤»¯æÍ›']ºtXuБkÊËË%##ÃëvÝŠ|ñÅfטó³èná¿ýíoæsþ÷¿ÿ5ûÑ~Ô¬egß¾}rï½÷zÝ×¾}{9~ü8À"`Rýøîªî¸“ªVÎñññÒ§O³›Eñ xçÎeøðá¦óö—ââb™6mšÜ|óÍrî¹çšçén)}Î[o½UeçØ«W/¯Ûõ3ØÙ»w¯c¦PÔ×Õ׿þúëeöìÙ¾yúé§Íx4}¬>GÇäÔeGî{Ÿï¬š~¾ï£8ÒièÚµ«iã@Ñéw>O¿û²²²JÓùâ§?ýiÀ÷S€Lž¦©¶H¨ê±¾ó’¶¿3ÕŸ|ßGÿhÑײç-ïöòË/{à­»ju^Ðûô1Š[ßlÞ¼Ù Ðþõ§^×ÛX„4’(`œ+Öððp™>}ºlÚ´É/<ª»r®j·‹¾¾3:ÉwKJMvOù¨G(¬X±"èø"íKJJ<¯¯Ïk³Ë´&¹vªÎÛ—/_^íï×ß÷ç{¿/åöÛo¯Õî0ß÷SôøÞæ‹’ú˜¦ú–bÅ÷½íÔd~òwŸ¿ç<ûì³°þîÓÝøvtÙ 6ÍzSÚ*LÀ"Í6)))WÖÚ‰hœ––æw«F°•ò7Þh:Lý«\ÿj÷}ÝRbG`tvX—\r‰ç¨Åýû÷KdddP`åååy]×ÝY‡2ýûßÿ^©ÒϤÓä¯cÊÊʪԱêÖÝ‚S_c}ž|òÉJÏ«É÷W÷ Ýòâ»…¥®¦K1ÑÓT×[5úùœ÷ë÷T›ù©&c½ªú}ѧ[ õ½õ§óvçÖ^€E!!Šn¥²¬´sÆþl5Ÿ|ßgÆ ~!ì>ݲ«ñ=•‚~çš?þØëvÝ]°À"¤‘DwÑéŠÙ·ÃuÖØ±c«½RÖ->:^F;Ý}以Äùx=÷óöÒÒÒZ£eæÌ™^ÕÊÕŽÝêxßq?õÑ‘+õübÎût‹Nm¾¿³é ëj –?dœÍÁ¾«@÷š7}oV€E!(º²ÖÇzný+ܹÒÕÁËÕY)ëó}ÑlOM·¢{MDíLUçvò}_ßÇ×gG®ÈrÞ§»FkóýMé;¥kº»®>§©>€¥»Ùœ÷ëVÞÚÌOµ>÷[NÎöˆT€E!uÝõ§gðöÝ%X“]8vt€¹óv=‘©žú!Ðã}ÿ2¯j+Š¿±bjû¾¶¿±dÎø´¯ÏŽÜ÷;±¿ßš~gÓAêÑ~ÎçuïÞÝïw¤ãÐt<^mUŸÓTÀòP¯§/©ÍüT—Àb !‹4¥™³(qqqæ¯v7:ÖE;ß#ó‚mªj‹T •¸þ+çí«V­ªô9í-þ^Çw°´ŽÉ²Ç3Uçµµ£×s?izöìéõx=/R}täŠ×Ñ£G{ݧ‡ì׿û;›Rqíû\ý.ôÀ~z›]ZÏRÓ­”u1MÁ¶:Ö´]´½}ç=ÒÖûUÓù©.å;KOᯠƒE!XÕ-|ëŒÝéÚµk×.s»ï8í´|@ûî>ò= Ð\®GÚ§Mô™uëŠï{êÉQ½¶Ž7Ó-ú—¿­h:¯ñ=݃ž»I§ëûŒáz¤Ú|gÛAú;ÇWmÜ t{}NS ù°®ÚeëÖ­AçÕ`óS]Ëw·¥=¨©íQ„µý„,BªßÝN× ‡¡û&Ð9y‚ú!Њ[Ïu6çÁÒóùy¥§xФ§§WùÚöëè– ß£ëò°.ŸZ—ÿXÍqë²Ïºì².d…uù­u™i]&Y—!—¡—aÖexÅ%ÖºŒ¨¸Œ´.V\FU\ô3Œ©¸Œµ.ã*.qÖe|Åe‚u™Xq‰¯¸EQ°ÀXEQÀX `U«¶ìÚ"ËŠ–ÉŠ¢òjÑ«’]”-›‹6Ë;EïÈŽ¢ò·¢¿IAQ™Ÿz]oÏ)Ê‘7‹Þ”¬¢,É(Ê?ýÉL—³ôuíúCѼêE¬T/½ì©WŠ^©TË‹–W*}_»tœµ²he¥ZU´ÊS«‹VWªô¢t¯ZS´ÆS:úSo·¯¯£¯k¿§~ý\ögÖi±§O§[¿‹U­bÅOQÀX«®•¶7MÞ~ûmO½ûî»![øÖä¯1í£ßÙë²ÈºXü7­Ë_­‹¶·¶;í_óö×ϮӢӦӪӮßE’+‰?EQ `¬ºVÒú$¯ÿC©'¢X‹¢( `,:X€Eû,Š¢ÀX `5`%|™`ÚÜ®ììl:Š¢À¢ƒX´ÿÙkìçc½ÚñâÅtE,€E °h€EQÀX `,€EQÀX `,€EQ°,À¢ýEQ `,€°EQ `,€°EQÀX `,€EQÀX `,€EQÀX `,€EQ°ÀX `Q°ÀX `Q°ÀX `QE,€°À¢(ŠX‹`Ñþ‹¢(€°ÀX‹¢(€Õèá·4gΜ‘¥K—Jjjªg%êr¹ÀX `Q°j’ãÇËØ±cÍïééé’™™é¹O;P]‰,€°À¢( `Õ ‹-’œœó{||¼—cÇŽIll,ÀX `,Š¢Vusøða?~¼çzTT”×ýºËÐ÷6€°ÀXE¬ yöÙgeÇŽ^ã³üÙò+»œ™={¶K~~¾ù‚ôg}]X¡ÖƬ½}¯¬Ðë…^Yûsë\oÞ×›4°tB&Mšäu[°è`Ù‚Eû³‹¢(¶`EæÌ™#{öìñº-!!AJJJ¼Æ` :`,€°EQ«ªìß¿_¦NZéöŒŒ Ói:"ÔAð `,€°(ŠXU$99YþñTºó`ÑÁ¬Ú·ÿ IÛ Úzµì¡Xé:¸«´9·©ÎQeøþá~Û¿×’^Þ'\Z·kmÛu@W½o4À¢( `q&w€°Z&°Zµjå)gû·¿±½Üñû;dBùS·/¹].ºö"¿íß)²“ Ì(‰å‰¦îL»SÚ_Ó`Q°ÀX-{¡/°tkÔøòñ^íNÛsªÝþúX€EQÀX `,Gûÿ<íçòÓù?•;—ß)#¾!w­½K®‰»¦ÊöO(KˆßEH§>EQ `,€°œí?ü‹áÖ+Ì ê¼Îç™ñXcJÆm{WãUC®’øãñ‹¢(€°ÀXÎöÿQÏÉ}÷yÚ¿çó=%<"¼ÊöŸ|j²ôYÒGÂ{…,Š¢ÀX `9Û_ÇPù¶MÆ`é.€EQÀX `,Gûëib¿ˆ5ß­ExËÜ[¤kLWOûëãíöïÕEolŽ œtb’ƒÕ-¦À¢( `,€°8Mƒ]ú]Ž<:R®r…Ù¥¥c±œc°œÀºkÕ]Ò9²³ÙÂÕ®};¹)ñ&™T: `Q°ÀXœÉ3¹SE,€E °h€EQÀX `,€EQÀX `,€EQ°Võ;Øi=$,"ÌýzÏk#ÝFt“a‡†™NÊßÀg-l Ç,€°(ŠX«Å+,2Lîι[F—[Ÿ¦l´Üòä-riKýv°#”ö×·¯V;îð8¹øú‹ÀXE,€Å.Bí\õ0{ìu“¯“ÈôÈju°7L¾AîN¿`,€EQÀX+:7Z:Fv¬ÔÁß?\:ÜÜ¡Zìèý£å’›/a ÀXE,€°"߈”Ý;xÆ`9;Ø«Ç^-ýÞèW­öÚ±×Jÿ7ú,€°(ŠX«eëæ¹7K—˜.{4¶RûÀǘqYÕé`c?Ž•Ëz\ÆQ„ `Q°VËVô¶h3Ð=PÛupW; Zl·ÁÝdPî €°EQ `µ\`ݵù.vtXÀvpÁ` ëæ·ƒÕÓ08;ØaÃ$¼O8çÁX‹¢(€°Z6°¿Êî`Ã#Ãe`ÞÀj«Sd'y EQ `±‹3¹,€EQ°,À¢ýEQ `,€°EQ `,€°EQÀXt°‹öXE,€°ÀXE¬Æ¬Ï>ûL¦OŸ.QQQ¦ìœ9sF–.]*©©©ž•¨ËåX `,€EQÀ –#GŽÈرc¥   Ò}ééé’™™é¹®¨®DÀX `Q°‚DWŠ;wîô{_||¼?~Üsýرc °ÀX‹¢(€, &]1FGG›ÝƒŠ"{7 sw¡½ËÐ÷6Vv93{öl)..–üü|óéÏúº°B¬76X{û^X¡Ö /¼²öç:×¹Þ¼¯7I`EFFÊîÝ» žNŸ>-+W®”3f˜û"""*=ÞßmlÁXlÁX¡Þ‚õÊǯÈò¢å²¦h¼^ôºl*Ú$9E9²½h»|PôäåKQQ‘|Tô‘|Xô¡ì,Ú)¹E¹òvÑÛ²±h£¬+Zgž«¯ñRÑK^µ¬h™§þPô¯úcÑ+ÕËE/{ê•¢W*•¾‡oý©èOžZQ´Â«V­¬T«ŠVyjuÑêJ•^”îU:mvee˜Ÿz»ýx}}]û=õsèç²?³N‹N›=Ýú]äìÈaë Ŭ`ÀòÝJÕ¯_¿mÁX `¬P+î›8óÝê4.´.[dƒuÙi]ЬË1ë¢)±.û­Ënë’c]2­‹E'yÖºè÷¢íÁü°û± `,€°š8°âãã­Îè¸çú±cǬ&`±p¬:ÖØ±¥òƒ\âXíÛ_k=÷°×¬ïÿB€°ÀXMX}ûö55ÄêU–-[&§N2÷EEEy=VwúÞÖX€µdÃ;’²q“¤ný³,~ï/ò{«^Î}O2wî’·wÿ¯ìÜ»WöþóŸràÀóóýöÊ–wË«ï¿//½ó®üfó™•õ†LÉX+cþ´ÒÔØî·b•§âV¬–ñ+¿« +ÓMM\宸ÕéòÐê5žš´:ÃÔäô IXã®Ä5¯zêá wéûÚ55ã5yäUw%­}M’×fzêÑ×Ü5íµuòX¦»R2_÷ÔãëÜ5}]–§f¼ž%O¼žmjfV¶™Ný]ïÓÇêóôõôõõýôýõsèçÓÏ«Ÿ_§E§O§Y¿ý.’¼°j¬ððH¹çž YåÖcJ冦H÷î3=ÀÒÇÛÀºýö…róÍXÏ;j€5xp®ÜrË4€°ÀXMm»n­R`ÍŸ??àÖ-·9¿`gfÏž-ÅÅÅ’ŸŸo¾ ýY_×.,6€v8úñ_xA¬NWä­·D>ø@äÓOEþóN4Y›V?¿NNŸN³~ú$%­÷jû76X{û^o̧i°K¿Ë;îX&;F˜Ý?üa˜¦¹^íï–V÷î)f»îTl%&žj´ÀzÁZèBÕþz}© +ÄÀ*,, Yû§IX!Öúõëë­}›ÅQ„º•ª_¿~Mn À =°Ø‚Å™ÜÙ‚Å,¶`±‹-XrâÄ ‰‰‰1¿'Xkø’’¯1XCµÇX `,€°ÀX“l­]wïÞm¶N)®ô´ üãÍ}¦ÓtE¸H{.€°†éël5óƒ]zÄ)ÀX `,€°D;˜MÖJ:I"##å¾ûî“åË—{íl2çÁXlÁX `,€°8“;ÀX `,€°ÀX `,€UEÛ#­‡„E„Iëv­¥Íym¤Ûˆn2ìÐ0ÓÁú;ºTË_è± `,€°ÀX-Xa‘arwÎÝ2ºÜêjËFË-OÞ"—ö¸Ô¬êv°úX¶`,€°ÀX `è`Ïi{ÀX `,:X€Eû¬ºê`£s£¥cdG°[Zçu>Onž~³Œÿf|@`Ù=¿óùrËô[$ᛀ°ÀX `¬– ¬È7"¥C÷ž1XÎväá‘XW¿²ÊvÜáqXW¿`,€°ÀX«åëæ¹7K—˜.{46`;¡|‚ _6±<Ñ<`,€°ÀX«E+z[´è^U;¦tŒüà’T«ƒ/—s/9`,€°ÀX«åë®Íwɰ£Ãüv°á‘árOÎ=2¾|¼ÁÕõS®—î3»{:Xwew°";ÉÀœ’Pž`puÓ”›äÖ™·,€° X'Ož4ÿ¦$::Z"""<·ëÿÌÊÊX `,€Õ€À tþ*í`ïXv‡tŒè(ßký=ùaØå–¹·xu°N`E,‹N<½mîmE°VCë¹çž3°²ËNaa¡ù×7 `,€°8“;ÀX«†Ñ-WÙÙÙæÿ:¥Ñÿ/°ÀX `,€°j˜¾}û\iœÀúòË/¥_¿~ `,€°ÀX«¦o­24ÀRlX+ljÖÊ7 `,€°ÀX `Õ4Û·o÷ƒå¬Ý»w,€°ÀX `¬ÚäÃ?´VºSÍ.Aw¥Gæååqš€°ÀX `,΃°ÀX `,€Õ€h÷ ïiÀX´?ÀX `,€°ÀX `,€Õ¸vêÊî“O>X«E+_a5¨°>·nX `¬¬C‡YëÍd€°ÀX `,€UWÑóaq¢ÑÐ+&æciÛö¯þ5Ðÿ(ó׿ŽsHºu,mÚœkªK—(ëµ÷,€°ÀX+TÀR\­[·`…XN<ë_GŽ<,íÛ_ï·íÐáF¹óÎß[}l¹Õç•KïÞKä⋯X `,€°V¨¹û‚`5lÿZ°®»n²DF¦ûí_[·ngý,÷ê_Ï9§-ÀX `,€°B,Ýr5Óê,\.Àj¤À>|¿tèpsÀþµwï4éÑc¾°åVßö…DE­µ@°ÀX `,N4 °õ¯W_=Ö‚ðû×Ñ£¿Ž{ɵׯÉùçw6ã±âãKÀX `,€U_ÀªêÜWµ=ÖÖ­[½ž£c¹–.]*©©©ž/0ØV1€U=`=ðÀÇré¥=‚ö¯—]ÖÓzßOÿÚ«×óÒ©SÀX `,€°š°òóóÍnEçsÒÓÓ%33Ós];P]‰¬³V×®ƒeÀ€Ü ý«Ž·òí_ƒ°ÀX `5¡]„´ÖÓIròäI/`Å[+þãv§d娱cVÿ °ÎXƒHXX¿ý«>Þî_ݧeøÂôwzám·Í•nÝbÀX `5b`õHë!aaÒº]kis^é6¢› ;4Ì´ÿ¸²qrý#×›ÛÛ^ÐVnšvSÀötZ€Õ€ÀR)Žúöí[«-XºË/ÁZ‘—””x¶ŽÙ‰ŠŠªtúßÛVðÓ4Øe÷¯áá‘2p`^•À7î¨\yås4¡–pg ÀX `5n`…E†ÉÝ9wËèrkN(-·ûÌk÷£¿ßƒÝæü‚™={¶›Ý¦´~Ö×uÎäz`mܸ±ÁÚÛ÷:À =°^°ºPµ¿^ŸpjÀ 1° CÖþiGÒí.ÂsÚžcÚ_·Z9ÛÔÑQrÑ55`­_¿¾ÞÚ7$Àêß¿¿deey¶.>}ÚÜž››+Ë–-«õx.¶`Ñ¿²‹€-X‹-Xg×þѹÑÒ1²£iÿïµþžÄ•Çyµ¿îJ ,…™ÖùÏ—[¦ß" ß$°«!¥»>šAƒYøÔë¾Ú  ·ãÜuhÁªÀX `,€°‚¶ä‘Ò¡{Ϭø±üìùŸI\™5¯–O{¶ÞXÎöwxœÖÕïX ,¥›Ñ43¬•çêÕ«Íïf·áÙ+##ÃtšÎ£iǰÀX `¬€íóÜ›¥KL‰=ëiÿ‘GGÊåƒ/÷ €¿nòuráO.¬Vû'–'šç¬–ÎЊ,Í|KÎÝ|uyÀâ °ÀX `¬f½+îóÕê$.\(²|¹ $²s§HQ‘ȱcö¿ѳÙìÞ-’“#’™)²l™È³Ïº¿mfê­fÍZ °ÀX `,€°ÀX `,€°ÀX `,€°Àj9ë€gŸ}ÅZÔy `,€°è_3ÀXlÁX `,€°ÀX `¬V±®QVƒ®]×[ï °ÀX `,€E °h€°ÀX `,€°ÀX `,€°ÀX `µt`eeeY ëTéׯŸDEEY ß 9xð ¹ïÌ™3²téRIMMõ|.— `±vX `± X `K²µdí¶Ö4Š)­ììlk!eîKOO·V>™žÇjª+Q€ÅÚ`,€°ÀX5Lß¾}ÍÏxk©?nwJ¢+¨cÖ‚ °X»,€°X,€°ª›Ó§OËúõëÍ.Cî2tF·pùÞ°ÀX `,€°$""”~I¥¥¥žÛü=άìrföìÙR\\,ùùùîNÐúY_×VèµqãÆkoßë+ôÀzÁZæBÕþz}„S+ÄÀ*,, Yû§IX!–n ©¯ömò[°ÊÊÊÌ ÷)º”±‹µ+[°h¶`Õzˆ‰ùXÚ¶½Àï,ðÀîû‚ͽz-‘ðð>Òºu;iÓæ\éÚu€õúûØ‚Å,¶`5å1XzD¡&ÁZÁ—èÚÈ1k¨®mÀX `œZµjå)ßYÀy_°Y S§H80Çš Ê­f)—>}Ò¤}ûkÀXMX)ÖVPP`¶N}ûí·fŸÂH“‘‘a:MçQ„úÓÀX `U= ø–= T,³À9ç´X `5`mݺ՜ªA¼÷Þ{ÍJòĉœ‹`Ñþ«‘+!¡Lî¼ówÒ©S€°grX `,€u¶À²w'^uÕëñÇÀX `,€°V]lÁš<ù”ôî­ƒÞ{,€°ÀX `¬ºƒ¥G,€°ÀX `¬Ko³g.]¢dðàíæ(ÂI“N˜1XݺÅ,€°ÀX `qš»ü¦Á.Àºë®UæT zä`»víåÆ­Ç”,€°ÀX `ñ¯røW9 `,€E °h€°ÀX `,€°ÀX `,€°ÀX `,€ÅÚ`,€Å*`,€°ÀX `,€°ÀX `,€°ÀX¬]ÀX `,€°ÀX `5_`=_”&“N&ÈŒ“3åÙ“¿‘ßý÷÷²ò«U²á«ò—¯¶KÁWÉÒRZZ*Å¥Åò÷¯ò%÷«÷$û«7䕯–Ës®ßÊ,×ItM‘Ø’M(ejdÉhy°dŒ©Q%cetÉ8ScJâd¬Ë]ã\ãMŹ&Èx×DS\ñ2Ñõ©x×$yÈ5ÙS“\ ¦&»M%¸6ï­õ°kªLq=bjª+ÉS¸’%Éõ¨©d×4yÔõ˜©i®S¹—×tO=îšajºë ™ášiê ×,™éšm~×ûôqú\}=}}}}ý<úùô³êç×éÑéÓiÖï@¿“Ä-É `,€°ÀjÖ[°›Ï§Óñë_‹¤¥ÑÔwû'&nX‹¥ `,€°«€°ÀX `,€°ÀX `,€°ZNûOºCžyæO­\¹`,–.€Eû,€°X° `,€°ÀX `,€°ÀjÈúµ¢ùµ|ž4ß]É©òù£OºkÚSßÕcO›*N±êñgÜ5ýY3m¦žøÏ\èþݺÏ<^Ÿ§¯g½¾¾Ÿyý<Öç3ŸY?¿N‹5}:Íæ;°¾“çgÎX `,–.€ÕðÀZÕ§´jÕÊÓþKzö”>aaÒ®uk9·MÐ¥‹ìÓ×÷Óþi½{KD§Næ±çµm+#®¾Zéë,¿õ~N6a5è:`±µŽX `,–.€Õ í¿-:Zîýñ½€.9÷Ü#åÖ< •vÇrÍEùmÿH W9J¹Õ&eÖõ'-œõ¸ì2€°ÀX `¬– ¬}ƒKDX˜ü×ºÏ ,íßöœsªÝþæ± `,€°ÀX- XG­Ç÷¼ôR9¢Ï³¾»@À*³~ÿ]¯^Ò§cÇjµ®…¶ÈÎÀj4ë€-É8ÓX `,–.€Uoíß×ÓÞ˜Oûû–Þ¦5¤[79®óLíÿFÿþÒÝBc°[°Àj"ÀÊÎζ(Iúõë'ÑÑÑ’ššj­‡Ü+¢3gÎÈÒ¥KÍmöèr¹KÀ Òþ6ž|Ë·ýOé ÷Ûo—^aaAÛîm·IÌWÈQ}G,€°VÓV²õÍî¶V4ЩӧOËêÕ«­ždîKOO·Ö=™žÇjª+Q€ÅÒ°ªßþUÁÒ£µÿ¶Áƒ%G·†qš€°Àjú»ûöík~Æ[­~Üî”DÌ–­XýfKÀª°¢:w–í¿ü¥9‚ð„uŸŽÁŠéÚÕÓþæ±í¿yÀ9ªÏã3Ýqîïâ¥)Ï,€°ÀX¡VZÏžѱ£ùÏçµi##®ºJékXï·äöÛ¥OÅ}çZ÷ ¸ürÙ§÷¬šõlÂjÐößf=`,.€°VH¥' Ö“Un}Î2«ž¼õVéq饿ý"ÃÃ%ç—¿”rë{×JëÝ[®¹è"€°ÀX `,€UÓy@ÿãC yÀܰÀX `,€°ª?ä`¶\ùÎeÖï¿ëÝ[úè} `5âöÿ«õ~‹¬u­]ééé `±p,Ú`…Xoôë'Ý;tðŒÁrþ?R­!W^)Çõñ `± `,€°Àªz˜Û½»Ä\~¹ÕÇú™Né ÷^½¤WÇŽ `¬æ¬ââbéß¿¿×mgΜ‘¥K—Jjjªç t¹\‹… `,€U‹y`Û€f {uæ=¢`,€ÕÄá)gtj¦®|*¢¨®D ÀX«fóÀæ»ï–£úx?ó@Tçβ}à@sá ë>ƒsÅ `¬æ²‹ÐXñV£·;%ÑõÓ1ë{X,\ `¬Îöø*ßÒ÷[õ‹_Hd§NæÈÁöíÚIâ 7H©N ÀX«y+**ªÒ.CßÛlXÙåÌìٳͮÇüü|w'hý¬¯ë+ôÀÚ¸qcƒµ·ïu€z`½`-s¡j½~J¿WÎäR`†¬ý(¨VHµ~ýúzkßf,ßënc Àb Àb Àb [°Ø‚UÇ[°ÀX `,€°V5•`5n‰®Œc°†ḛ̂X¸ÀX `,€U;`edd˜NÓy¡ž­`±p,€°X,€°jpšçé8 À¢ýóÀ¢ýgrX `,€°ÀX `,€°ÀX `±p,€°X,€° ÀX `¬ÆÐ||ÿýrAÛ¶^í¿äöÛ¥OÇŽæÿOžÛ¦ ¸ürÙ§¯°ÀX `,€°‚·¿×¿Hr´dx¸äüò—æQj¥õî-×\tÀX `,€°ÀªnûûË_ûëÿ¥X `,€°ÀXu¬2ë÷ßõî-}ÂÃÀX `,€°ÖÙËÞ}8äÊ+å¸>`,€°ÀX `ý¬S:è½W/éÕ±#ÀX `,€°ÀªË1XzD!ÀX `,€°Àª%°¢:w–íš#OXó†ŽÁŠ¹â €°ÀX `,€U“Ó4xN×`µýª_üB";u2G¶o×No¸AJuX `,€°ÀXœÉ`,.€Eû,€°X,€°ÀX `,€°ÀX `,€°ÀbáX´?ÀX‹öX `,€°ÀX `,€°ÀX `,€Å°Àb°h€°ÀX `,€°ÀX `,€°ÀX,\ `,Ö `,€Å°ÀX‹uÀX `,€°ÀX `,€°ÀX `U3gÎÈÒ¥K%55Õóº\.€Å°Àb°ÀªmÒÓÓ­uO¦çºv ºX,\ `,Ö `¬Z&Þjôãv§$º~:f}¯±‹… `,€Å:`,€UÛDEEUÚeè{ÀX `,€°ÀªA"""ªu›ó vf²ÕH[ «•b5¤ý{½\·f²éÖÌ1Ýšñ¦[3Ê4ë÷d«ñ“õ§UIÖíÉÖc’­IšëzŸ5Óê㒬ϚlÍØ¦¬™=É*ý™lÍdU^·*Éñ{µ®Û¿ëûUuݪ$ÇïÕºnÿ^1]¯û{¾óýý}þŠéO²¾»G¬ïZkªõ}N³ÔkoßëÖ{O·>Ýþ)Ú¾´ý¶¿õXgûëw²ö·®{–«·>Ó£Ú¶Zvû[·yµ¿=hû[™ùn´ìém,ío_¯IûÛ׫jûz ö·¯ûióÓúíöןv»„¢ý·ÚÖž·ê1{ù¶*Iw¬’t^°ç½_Ûßžhÿê·¿µì8Ûßîê£}“’-y !„BH¨Óè•`‰¾D7§;Æ` ÕÍ¥„B!«vÉÈÈ0ãjìèï‹tl !„BÀª]jz,ß8ÇfQEQEÕ¶š°!„BØ‚E!„°!„BÀ"„BX„B!‹B!„¬æ“>ø@âââ¤oß¾2dÈY¦ÿ£¬Šøû÷A-ÅÅÅÒ¿¸…µvv¶ùýúõ“èèhsÊ•cöÿä#-bÈÊÊ2ÿÊDçýO3f̃ÒÐ-¬ÐlݺµÉ|V€ÕÌòé§ŸÊ}÷Ý'ùùù溮„^|ñÅ&¿pé糋´¬ö×ÿ»¶{÷nsN»Ó§OËêÕ«e’þ3YÒ"ç-E÷(ý§½¤Åô¦™3g,šÌ™3G¶lÙð~ýkFÿª4h¼÷Þ{•ðâœqW®\)óçÏ—§žzªÒ ZÓÓÓÍkè_”ºò‹ŒŒ4·k˜––f¶4héI^õ6û}Nž<)Ãõ¿¹‹˜¿HíèJ³:ÿ`µìö·£Ó@˜HËi…¢Þ¯ÏX$$Ñ]hß~ûm•Ûµk—ù+'\táY±b…çúöíÛeÁ‚æwÝ\¿aó@höïßïy¾nŠ^·nç/Íõë×ËK/½äyû¯*¾þž={<™è °hÿª¢ÏÑ:i™ó€vÖúšÎΙ4ïöWØ9ÿ¯0À"!IMf<û/ÏÓˆ}âÄ ¯¿.ìñO>ø gÁò}þý÷ßïùkESVVæYˆ}ßCJýëF£?«3¦`µìöß±c‡Lœ8‘1X-t°·°èÖ”ÒÒR»…´ÿ´iÓä³Ï>kqýÀj„½œ:uÊï}:óêaÝD¬E3©ï ë»ÉØ¹éØ¹Pú>ßߌooÊ÷wß”)S< P]¯ùļ÷X°h¿ÉËËc«U ŸRRRÌôèëéø"×£ -¯X$¤ÑÁ‹ãÇ7›qu?ºîZÑäääÈ€LéçœIõ/û¨;ú}¾nÞMLLô,|z‡ŽÐ××s­ìÝ»×lnÖè¨ûÒõ¯K-ý]ÿ‚ ¶Pèg­ÎÕÒ6ÓþÛžöoYó€žûHlÐÏ¡±vðÎñA¤ù÷‹´¸è_(¹¹¹fa«MtÁ¬é®BûæBû,Ò,£ÕØgUÖÍõ݅£ÝÔdÓ0¡ý ó¡ý!„BX„B!‹B!`B!„€E!„°!„B!„BX„B!‹B!`B!„,B!„°!„B!„HNNŽ$''›@«ÿˆ¶ÿþ’’’"6l¨ökDDD˜"„€EiñIMM50Z±b…”••É™3gäí·ß®1˜!`Bˆ•M›6-\¸°Ò}Ó¦M3eGÑ•””$}ûö5[¸æÍ›'Ç÷•³ìlß¾]Æož-Ï=÷œœj³[Oq¢˜ÑçÖÔ©SÍmôÝ\×Ýšþ@GX„Rg™8qb¥­CNTÙ±·­^½Z>ýôS9}útµ€eï² ´;.žêXš¡C‡šÏár¹ÌkÝwß}4_wÑéQˆvÖ¯_/£F2¯«c¥>l^Ã9€Þy*-ý]_×NUçàÒ“¨êýǧ± X„ÒòbKÏUWY»v­yÍåË—ó°!¤ùGw?îÜ¹Ó „×ÝsçÎ5ÒßSWÑ-d¾ã¸!‹Bšmôä£:¦Ê>K»îNÔ1^u}-Ý=©'U%„,B!„°!„B!„BÀ"„B!‹B!`B!„,B!„°!„B!mjÕŠ¢šuB!!!Ìß„€E!Ìß„,Bè€aþ&`BDó7!‹BDó7!‹Æ×=*RX¸ôþf–8ÉrÔº¹èý‹ÂÒCH]u@™™"C†.½ß'_ýµ<÷Üs-ýúõ“)S¦HFFF?GDDDNWqq±ôïß¿ÊÇÕb’Ý4gggKRR’ù,ú™RSS娱c§Ùº rÑûû4geeÉÔ©SÍg‰ŠŠ’3fÈÁƒ!‹æ¬éÓ§Kzzºœ>}ZΜ9#{÷î5·…2Ú‰ÛUÀjlÓœœœ,»wï6ŸE?ÓêÕ«eÒ¤Iu ¬Æ<ÍZŠÌQ£F,B!ÍX}ûö5\ ¬\¹RæÏŸ/O=õ”Ì›7O\.—A'Ož”áÇWÚ²è9;wî´>ƳÕB﯋­%µVcžfçg¬K`5ÕiX„,Bš$°t7Í+¯¼"•:`Ýâ±bÅ ÏõíÛ·Ë‚ <m~~~% {ν÷Þkvéûè.¢P«1O³fÏž=f O]«1O³nU[¿~½ùŒ‹€EH³–nuÐŽqÀ€f ‚Ž…9|ø°¹oèСrâÄ ÏcµÃ´ÇEùâǾì9÷ß¿|ðÁÕž¶úVcžæ;vÈĉë| Vcf{W°n+--X„,Bš°œÑ­:þG;xgçç[Á:Þ`Ï),,”””4hlÙ²%dÀj¬Ó¼|ùr™5k–gW[]«1·sYY™ÙÒ¥ï!‹f,;:vÆÞhÜN Ž7Øsì|ùå—æÈ±Æ¬Æ2ͺN}W'g ¬ÆÖξŸ`°i\Àzï=‘yó—Þï=’Ì—£¥ckt+ŠF1¯]»ÖŒ‘Ñ詜csüu¼Áž£cvtÀ´Þ¦ƒ ëXµ˜äF7ÍyyyUnµòšfë2/ÈEïoìÓ¬[¸ìÏóí·ßš1XöçX„,B°j‘ 6˜ÁÅ:.G· è9˜œ½vÄ:ÞFïOLLôœ«(PÇì9O?ý´yÝú±k×® °ò·ë©®Òئ9Øî¶æ:Í[·n5ùõ¹:(~ñâÅ^cº!‹& ,B˜¿ X„: ÂüMX„ÐÂüMÀ"„ˆæoB!t@„0B!uÚQTs.BÀ"„Biùÿ¹î¢lq·àIEND®B`‚StatisticalBarRendererSample.png000066400000000000000000000477131463604235500372410ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/category/doc-files‰PNG  IHDRXr5˜O’IDATxÚí½tUÕ÷ý{œvæ™™53kžŽ3ÛygužÎ¬eW;gÞauúÎ3Ï<)O˜œÂø§:Pi¥¤( ¦€‰ˆœBh1ñŠA"Ä$‚ @Œ ¥Aƒ¤Ð‚ iö»‡Þxs½¹¹÷îdÿvîý~]ŸϽه{²Ï>çs÷ÙgR‚ ‚ H¯†ð'@A`!‚ ‚@°AA X‚ ‚  A$=QðwÈî¿‚ ,$ÓÜܬn»í6õµ¯}MýÁüú⿨þäOþDåää¨ &¨7fôIXâ$Ÿê¿ûû¸¾þìÏþL 6LÕÕÕ9±?íß¿_ýô§?U_ÿú×»ìO7Ýt“Z°`ºxñbÆV¢:ÉËËS%%%êĉYs,ùþ÷¿aF XÂùùÏÞíÉ;Ѳ·OŒ‰Ö×ÿV_¯_J°b©¬¬ÝŸ~øaõ{¿÷{ ?ãUW]å¤`¥óY’©–˪ªªŒ>ްDΞ==’ A8Ü›ì‰[R°¤NžýQ°þþïÿ^l›{ì±”÷§l¬™*Y©7‚…dEød}0|ä‘GÔ¹sçÂ÷ø2_rš1c†úë¿þk–c‚Åùío«–-[Öåu¾'‘÷ß?¼4ýYøóŽ;ÂÏùë_ÿ:°¿üË¿ÌhÁŠäÀêÚk¯íòÞŸÿùŸ«S§NA°‚…dzb/å$;V¤§êرcÕ¿ýÛ¿…—Fø„Ï'Þ¯|å+êæ›oO¸©®¯»ƒöñãÇÕÔ©SÕ7¿ùMõ‡ø‡á¿Åc_øß‰î-Hwýœ––5iÒ$õÿðÿ_ââãÕW_íü=Óm69ù'êÁ2ý\,G÷ß¿ú›¿ù›pé.ÅÅÅ]ÊñßëÒ¥KŸû=ÞÇþéŸþ©ÛäöÛoWüÇRTTô¹uôÅ6¥+ =ýî¿þë¿vyŸÇdEg÷îÝáøGþÃÛÂûØ7¾ñðïÉRÚÝ¿Ã_€x]0@M™2E­^½ºËÁÝTˆbáõ› VCCCØ#Îe¨d‹Ç3Åöʤóo$³Íé ŸT£_///Oúo›Ì犄îò/ÿò/i]‹ý÷Xzb_‹•’¾Ø¦¾,–•Ø;’Å‹'¯Æ¢uúôé¸ÿ¿¯Ìƒ> l¼÷xH@$ÜÎm3¿Ÿ =Å‚½ÉäÉ“»=ÀòŸOšsçÎÛ‘è@Ê=J|’ãoÒüM;ößáÞt{h"ïÅžDV­Z¾Î—f†šÒ@öxïñ•Ñ'¿¿ø‹¿è¼›òСCjРA]Ö“ê6÷Ŭ{ï½÷såL?W²Ÿ“{^b{XL{?¢eÂÆ6õæ%ÂHøóE¿Ï'ÎÛo¿ý9¡áíáöOtRëÕÓß1Vú¸Çÿmþýzt- `!H á^ªˆ($‚Oh}ôQÚR>x÷4N(UâuD¿¶wïÞ.exÚ“õÿð‡?ìòZäòK$,ѽ©ns_ rçÏýÉ'ŸôÚçâ1^‰z3#‰­tOÈ,¶<ö/•qe½µM}!X±¿ùl·ÜrK\!ݾ¼ï߉|¡X±bEÒïqo,'v*þ›s¸ E¿Î— !X A Â—Âø`{’Œ&???é)³á1.| çK>±½©Þ•ï½Øú‘oè<>'rÂ0Y?åŠ~íÌ™3 ÿ†©nso]"dÑã¹Ê¢ßã[Ÿ«·{°âI†éþ•ì6ÙèÁâqNœž.oÇ YwÿN¢¿Uwïñgˆ“ï÷#Ÿ‚…@°Ä0|€åAÂ<Ÿ sŽ>Pò€ãd¤\>VNL§}ˆ÷KO4~eéÒ¥½ÚC–(élsorgÉŠ~/gÚú\‘Ä^²Mv tª—ëúr›úB°ø2[ôûÜcÌéi®°TnÄèÍ}ÛôŽTÁBuy, Ϻ/|I0K\ßþö·»¼Î™òÔ½-XœÈ ôñz³"—VÒ]ì·üD=2élso Vì¿©+Ÿ+¾Û/ºÜ?þã?Æ»Çw òؾt«/·©/+v@=O…oÿŠ÷·2mÉîÛèÁB XÒÛ;Þï³=:ü¦‘ŸÂ'‚è%ŸØ¢û <’î¾÷tàín}Ý•ýñÜåwbÇÄ aªëçǺD¿öì³Ï~î3Gz#ÒÙæÞ,aþ[D¿Ç·ìÛú\‘°¨Ç–åy°êëëÃ÷y\ßÅÆwª&óYº{½/·)Ñ>’j½ðͱûßµû•ÌþÅâÈÃÞ¬Ø1XÙE&+Ì&ͯóxœÈ‰ƒßýÆï`ÛÝúº;IpoîÈ Ø,‚üûwÜqGØÃdºþH<n)Šü}x5÷`Er§³Í½%X‘¸ù²Ol/£Ï/<6Žïæä±qüosݰd°üñþó“Ž`õå6%ÚGR©.ÇÛË=¬Ü›˜èñ8ü¤þ7¹½ñþÅ+Þ6ÉÃÈ…Þ¬HOÖõ×_ße&w^îÍ™Ü!X AA‚… ‚ ‚@°AA X‚ ‚ ,AA‚… ‚ ÁBA`!‚ ‚@°AA‚ ‚  AA‚… ‚ ‚@°AA X½—3ftYž0a‚ºí¶ÛÒ¦°°‚iÓ¦©7ß| m~ñ‹_@° X€`A°Á‚`‚Á€`A°Á‚`‚Á€`A°ÁJ&+V¬P'NTƒVyyy*uôèÑ𽎎5gÎUZZÊÓÖÖÁ+QŠŠŠTCCC(SLuuµ9rdøÞÒ¥KUUUUçïVVVª²²2 X©&777ü9vìXuêÔ©Î×[[[Õˆ# X€`%›öövµråÊð’!‡/F‡{¸b_ƒ`€ÛQÒàï X½œœœœ–¤3gÎt¾ï÷â‰U„è«––ÕÔÔþø'–±Œe,cY~™e ,÷—å~߃uéÒ¥pÐû„ ЃÞ«…¿@–åð…œ‚‚uúôé.c°†Á¬D™°le¹_ Vyyùçz¨X°¢Ã¢ÅƒáуÂ=X§)y(RX/z°z°z"÷TÅfÔ¨QêÔ©S]^:t( ¤ëc‚ X R__ßí¸ªÊÊJ5wîÜÎ÷y|Ö¼yó XÁ‚`V¢äääÄ%’ùó燃ÜùÒ ËÖ¥K— X -X§H€`a&wÈÁÚ¢«•ÄáÏú,dŽ`}Dâ@° ‚,€`A°$¬“$ @° XY‚u‚Ä` 2K°>$q X‚Á @° XÖq‚ X,È,Áú€Ä` 2K°Ž‘8,Á‚` ‚ X,ÝV¬£$Ô€`u“êêjUXX>Ì™ê\ZZªZ[[?÷{555] Áê?QÒàï@?¬÷I€`%HQQ‘jhhPª½½]UTT¨qãÆuù¦¦&5uêTV†IþôcÁ:Bâ@°+ÅäæævþÿÑ£GîóçÏC° X @°ÒIcccØ«ÅikkSêôéÓá2 ‚pD°ZH€`%™ÚÚZ5f̘Î1X“&MR‡î|¿;Áb±ŠââbÕÒÒ^bä?ÿIJË,Xø{`Ëýs9¬÷Hœº–:Ô–­,÷kÁ*//{œ¸×*Z¨â,ô`„{°“8èÁèÁê!lˆ<н§à! Á‚`V©¯¯ïÒkÁ‚`ú`"q X‚Õƒ4%{‚Á8"Xï’8,ÁÂLî‚@æV­¬ƒ$Ô€`A°  @° X‚èF°~Iâ@° ‚ Xd–` q X‚Á,2K°Þ!q X‚Á, X,Á‚` `í#q X‚Á,2K°ö’8,Á‚`,€`A° @°ö8,Á‚`™#X›µ`í&qøs > ‚ XdŽ`í"q X‚Á¬¬¯ïdÁß ‚Á¬^Huuµ*,,TƒV¾ï«ÒÒRÕÚÚÚã{,À>«™Ä`V‚©††ÕÑÑ¡ÚÛÛUEE…7n\ïA°prØ€ `5‘8,ÁJ1¹¹¹i½ÁÂÉ`–ëm‚ X)¤±±1ì¹Jõ=N®û€`ÁŠ“ÚÚZ5f̘¸ã¬½Çb!:ÅÅŪ¥¥E555… þ‰åÞYtrpq€úÊ¥¯„?¯ú䪔–ùäjRž—~ïaÔG?^æ}þ¹ V#‰Sw¸õe+ËýZ°ÊËËç¶¶¶”ÞC– ÞiOQ¥‘YyMðN€º@êÁz‹ÄA@VaCäÁ쩾Á¬ßRú™•×û!X,ÁB}VÜÔ××wÛ3•è=–°`}¬«Ò‡È¬|; ‚D«Ä`V‚äääÄ¥§÷ X‚uJ Ö%J"³òš` ‚Dk“¬$Ô€`a&÷̬V-XŸRú™•ÿ‚Á¢‚µƒÄ`+3ë"¥‘Yù‹,€`A° ‚•i‚õ‘¬ ”>Dfå5Á^ ˆ Öv‚ X¬Ì¬“Z°>¡ô!2+ÿ  ‚D«žÄ`+ó넬ó”>DfåÏC° X@T°¶‘8,Á‚`e¦`£ô!2+¯ ö@° X‚…ú,V& Ö‡Z°~CéCdVþ7,¬­$ @° X™'Xǵ`ýšÒ‡È¬¼&Ø Á‚`1ÁÚBâ@° ‚•y‚õ¬³”>DfåÏB° X@T°êH€`A°2S°ÎPú™•?Á‚` @° X™&XÇ´`ýŠÒ‡È¬¼&ØÁ‚`ÁzS V-‰ÃŸõ X¬Ì¬£Z°Ú(}ˆÌÊ·A° X@T°6“8,ÁJêêjUXX¨¬|ßW¥¥¥ªµµ5|¯££CÍ™3'|å‰ikkƒ`¹ XïkÁ:MéCdVþ4 ‚Dk‰Á¬)**R ¡Lµ··«ŠŠ 5nܸð½¥K—ªªªªÎß­¬¬Teee,WëcJ"³òš ‚Á,Ô€`%ÜÜÜðçØ±cÕ©S§:_çž­#F@°\¬#Z°NQú™•?Á‚`QÁz“Ä`V ill {µ8yyy]Þã^®Ø× XB‚õž¬(}ˆÌÊk‚& ˆ Ö$ @°’Lmm­3fL笜œœÏýN¼×"ã³b«¸¸Xµ´´¨¦¦¦ðÄ?±Ü;Ë.VÉ/KPýx™÷ü=úçr(XIœºwëPX¶²Ü¯«¼¼<ìqŠÄŽ,Ç{°NRú™•?‰,ô`Ñ, =X=X=„ ‘ºÇ¦  @>}ºË¬áÇC°\¬CZ°>¤ô!2+¯ Þ†`A°€˜`m q X‚• õõõÝN½°lÙ²ðÎÁè»gÏž Á‚`A° X@Z°^'q X‚• <¦*˜ËqÁzW ÖqJJãÉÁ‚`!ÁzC Öz‡?êd¬`?>ìUâIB£ óå½+V`&÷L¬ƒZ°Ž‘(A# ‚ X¨¡‚5kÖ¬Ïõ]ååå…LžKÕ„ B©6lX8ëìÙ³qÅ*BtŠ‹‹Ã±]MMMáˆb¹w–½F-X»I”’æ’Œúûò§¿|ÞñïŽW.P_¹ô•ðçUŸ\•ò2o¯Iy¿ÍG{Z«šÄ©ûeêÇ;+Ë'XüLÃC‡u.WUU©ÂÂBô`¹Ðƒõ–¬]$J° =XRä¿—¯¨ƒÌ ³òÞimQ²k‰ƒ,ïp‰0MÁŠ7Þ c°¬f‚%(X‡µ`µ“dVÞû‚%*X/’8,ï2Z°N:ö*±øôvOËÀ— #S?”——‡ÒÁr@°´`5‘(ÁV–¨`]"3Ȭ¼w ‚Á‚`áx—Á‚ÅRÃb4hР´«»r|"¯Ÿï&dx,V¼1X,ÁÚ©ëm‚%(X‡´`}JfYy¯‚%&X´`-'qøs >p¼ËXÁâÉAy~*îaâÉ@ÛÛÛÃ×ùŽ¿ `&÷L¬Z°I–°`]$3Ȭ<KX°ªHŽw-X|i02WÕ 7Ü <˜Ôx)VÖ[$J°‚%&XïjÁú„Ì ³òÞI–¨`½@â@°p¼ËhÁâñW|+#'UQQþsssxÙ‚Á‚`e `Ô‚užÌ ³òÞ  ‚…ã] Öºuë:§N())é2Žj̘1¬L¬z-X;I” ‚%&X¿Ô‚õ2ƒÌÊ{B°D«’Ä`áx—5Ó4œ;w.”,¾4ÈÏ ŒžÃ ‚•a‚µM Ö‚%,X¿&3Ȭ¼w‚%*XÏ“8,ï2R°x`û™3gœšG ‚ÁÂÇ’`Ђu–Ì ³òÞ,QÁZFâ@°äXذP•5•¥ ·“òLe}ef _äž*~|M}}ýçÊ ÁÊpÁÚª«žD j!Xb‚õެ_‘dVÞ;Á‚`A°ÄÎmú`ò‘2ý/¿%?3‹Å*zrÑ믿^Í;·Ë]„¬ n\[tãÚF¢@°„«Ì ³òÞQ–¨`=Gâ@°Ï§õ9à·”>Dfå5üÈ®ŒƒuáµfÍUTTÔe¢ÑÑ£G‡Ï <}ú4+SWn\[I– `íׂušÌ ³òÞû,QÁZJâ@°Ï{¢Êbø‰Y1Èejùòåjܸq¢…i2\°¶(Áf–¨`}LfYy– `½®«‚ÄáÏú:´z¢Or`xÂ㬹‹0’Ý»w«‘#G¦ü,BV?j\µºqÕ‘(,AÁÚ§«•Ì ³ò^  ‚Á,Á'90Y#XüŒÀÕ«Ww¹\ˆ¬ n\›uãª%Q‚M,1ÁÚ«ë#2ƒÌÊ{ïA°Dk ‰Á<|¤Ï(}ˆÌÊkø‰+X—.]RëׯWS§Ní2àÇ`ñåB~Xs²áÙàù¹†±9|ø°š2eJøœC‚åHãÚ¤×f‚%,X'É 2+Á¬Å$KðpÂ}’ÃO”ÈHÁŠL({a:“‹FÏþ'N¨üüüð±;èÁrP°6‘(,AÁÚ£ëC2ƒÌÊ{‡ X,–¨`£ô!2+.ƒ+2KÌ–-[ze¬XÁ*++ ×K„6®7uãz“D Þ„`‰ Ön-XÇÉ 2+ï½ Á¬gH–à9à¸'ú$†Ÿ(‘‘‚µjÕªðÑ8½™XÁ1bD(Y¾ï‡—Y˜]v„`Yl\oèÆõ‰Á¬]Z°Ž‘dVÞ;Á¬r‚%xøÀ}’ÃO”Ⱥ»{K°x|CCCØ;ÖÞÞ®–,Y¢‚ ˆ+V¢S\\Žíjjj ÿ@ü˽³ìmÔk#‰R²£$£þ¾|Àé/Ÿ×ÁøÞ@´G¡eW«î:Ô‡ÐòÀEŸäÀž(´¶½)XÑaщÁ<òÌŸæ` ‚•™kn\kI– `5hÁz‡Ì ³òÞ. ‚%vxWŸ> Qø‰,Vf Ö%x‚ÁB[¬'I–à9à g>Ù°!<á1 ‚•yëUݸ^!Q X‚‚µS Ö>2ƒÌÊ{M^¿®ëdqV°æ‘8,ÁsÀÏ|²aCx>>+ó×ZݸV“(ÁzwkáÂåymFðÉÔtVk/™Afåû³`õwÁë ‚%,X¦“ Á‚`efãZ£×Ë$ŠK‚UVÖ¤O†ñ:¬ Ö-X»É 2+ï5B° X,±sÀ~Ï|²aCxÂc+3k‰â–`½­Oí†ñ:¬ Öv-X»È 2+ï½Á¬ÇI–à9`Ÿg>Ù°!< ‚•ykµn\/‘(Ák® Ö§†ñ:¬ V3™Afå!X‚‚õš¬¹$…Î{õ9à]…çãƒ`A° X/XúyÑ2^‡5Áª×‚õ6™Afå½,QÁšCâ@°Ï{<óɆ `A°2³q½¬W5‰âž`}b¯Ãš`mÓ‚ÕHfYyo ‚Á;ìöÌ'6„'<†`A°2¯q­ÒëE%¨qI°ÞÒ'Ès†ñ:¬ Ö[d™•‡` Öc$Kð°Ë3Ÿ Ïž‚•DZZZÔ!Cº}¿¦¦Fåää@° X Ö¯ !ãuX¬­Z°È 2+ïm‡`‰ V‰Á<4ësÀ~‚•DXœ"ÄKSS“š:u*˥ƵR7®å$J°Î5Á:k¯Ãš`mÑ‚µƒÌ ³òÞ6–¨`Í&q X‚ç€&Ï|.>V ¢›£GªÂÂBuþüy–Kë%ݸ^ Qܬ}‚ü•!d¼«‚µÌ ³ò,Kðð¶>ì!Qx>>Vš‚ÕÖÖ¦ ÔéÓ§»°ˆXEˆNqqqxé‘{ÀøÄ?±Ü;Ë.VÉægþ—ë´!d¼[Û›_§k™Afån˜Qí‹ÿýåó†‚õ(‰S·¯Çc¡åÍÍçÂ3¤ð@¡µíÍ8Áš4i’:|øpÂ.ô` }{©Ö‚UI¢¯ºÔƒµSŸ ?6„Œ×a­ËÁò¶ K´ë=X‚瀷<ó¹ð áùøÐƒ•¦`EÍêiœËrãzQ7®e$J°Ö5Áj5„Œ×aM°jµ`m!3Ȭ¼WÁ¬Y$Kð°Ó3Ÿ Ïž‚e0+•÷!X,YÁúÈ2^‡5ÁÚ¬«ŽÌ ³ò^- ‚Á;ìðÌçÂ3„§‹`A°2¯q-×k)‰â–`íÐ'È !ãuX¬MZ°6“dVÞÛÁ¬-X“8ü9p<,Ó¹ð `¥8MC¢Ë€,‡W•n\$J°Æ5Á:n¯Ãª`m¢ä¡Hr,aÁzˆÄ` ž¶{æsáÂóñA°0“;+ãk»>A3„Œ×aM°ÞÔ‚õ&‰â½ Á¬™$Kð°Í3Ÿ Ïž.‚ÁʼÆõ‚n\KH÷ë¨!d¼k‚õ†¬$Š·‚Á‚`‰ –é\x†@° X™Ù¸*uãZL¢¯¸$XõúyÄ2^‡UÁÚ@¢@°„ëA‚%xØâ™OÕbOÁ‚`A°²B°Þ3„Œ×aM°6jÁzDñ6@°Dë‚%x¨Ó瀭$ OÁ‚`e^ãZ¦W9‰¬vM°BÆë°*XëIïu–¨`ÝOâ@°ÏµžùT-†ðt1,+ãk›>A4„Œ×aM°6hÁª!Q¼õn Ö¼yz?hJ®“òLuõV°sجϵ$  ‚•¹‚µˆDqO°~i¯Ãš`½®k‰â½æ–`]uÕ'º”dX^© xÇž`ÝGâ@°Ï›¼Ô¦jéxº+ó×Rݸž&Q‚—]¬­ú÷Ž!d¼«‚õ*‰âÕ¸&X¿ÑuÐn–oׂµßž`ÝKâ@°ÏozâSµðt1,++k¿!d¼k‚µ^ ÖÅ[çš`ýZ×Á§aùOµ`í³#Xë´`•’8ü9p<:lôħjá»™!X¬Ìk\ºq=E¢¸%X[ô n¯!d¼k‚õš¬WHïU×댮ƒ aù Z°öB°€=Ážª‚ÁÊÌÆõ¬n\óI”`•k‚µÛ2^‡5ÁªÑ‚õ2‰â­qM°Útœ7€ ËŸ·+X%$Kð°ÁŸª…ïf†`A° XY!X» !ãuX¬U$Š{‚õ±®ƒß@†å£k=Áº‡Ä` ž^÷ħj`%™––5dÈ.¯UWW«ÂÂB5xð`åû¾*--U­­­!X©<üÖÉÆµX7®y$J°Ò%ÁªÓuÕd¯Ãš`­Ó‚µ’Dñ^qM°Né:8k–?«k·=ÁšAâ@°Ïë=ñ©ZxºVÉÉÉé$:EEEª¡¡Autt¨öövUQQ¡Æ—±=Xýi¢ÁP°ž QܬFCÈxVë%Å[íš`}¤ëàWaù_iÁÚÁvίyâSµðÝ̬D«§äææB° X V­®¿· !ãuX¬Wµ`­ Q¼—]¬u|l–ÿX V³=Áú‰Á<¬Ó瀵$ ßÍ Áê%Ájll {µ X4®rݸæ’(ÁK® ÖNCÈxÖk­¬Io•k‚u\×A«dX¾U V“=Áú9‰Á,á©Z X½$Xµµµj̘1qÇ`±XEˆNqqq8¶«©éòA‡º¼ÌØþòy½EºqÍ!QJjJœù{\¬†ñ:lmo(XËI”¯ tª} À=X@†å?R³f³²½¡`M'qêöÔõ›ã{¦-|m ¢Õ$JáŽBkÛ›±‚U^^öFµµµeô]„ýªËÁr«k³®¿m†ñ:¬õ`­Ñ‚õ‰â½äZÖûº>4€ ˨‚àm{=Xz°Ïk<ñ©Zønfô`Û#tφiú•`-Ô«ŒD ª]¬­†ñ:¬ Ö+Z°*I¯Ú5ÁjÑuðdXþ-Xö«˜Ä` ž^ñÄï$æ›m Xi V}}}½V,¡Æõ´n\³I·k“®¿:CÈxÖkµ¬e$Š÷¢k‚õž®ƒ£aù£Z°ìÜèPùª¬i$ÈŽÐ9`µ'~'1+Åi¢§kˆ÷z¢qZ,–¬`m6„Œ×Á’¬CºŽ@†åج©$Kðð²'~'1ßÍ ÁÂLî™'XOéÆõ‰¬pI°¸þ6BÆë°&X/kÁZJ¢xË]¬ƒºÞ3€ Ë¿§«‚ìœVyâwóÍ6,Væ Öݸf‘(î Ö†ñ:¬ V‰âU¹&Xt2€ ËÒ‚µÓž`ýŒÄ` žVzâwC° X™)Xóuãz˜D ^tM°6BÆë°&X«´`-!Q¼\¬wt4€ ËÔ‚µÃž`$Kðð’'~'1ßÍ Á‚`ež`=©×C$Š[‚Å=Hë !ãuX¬•Z°“(^¥k‚µW×Á;aùw´`m·'XSH–à9 Ú¿“˜ïf†`A°2S°f’(î Ök†ñ:¬ Ö3$Š÷¼k‚µ[×Á>Ȱü>°wxѿхo¶`A°2O°æéÆõ ‰,wI°øÝ:CÈxÖë%-Xå$Š·Ì5ÁÚ¥ë`¯dX~¯¬z{‚u7‰Á¬çH+3ë ݸ QܬW !ãuX¬j-X Iï9׫I×Á.Ȱü.-XÛì Öd‚%x¨òÄotá›m X¬Ì¬Çu㺟D ª\¬ ºþÖBÆë°*XO“(ÞR׫Q×AS’P 4%Mlµ'Xw‘8,ÁsÀ úð,‰Â7Û@° X™)X÷‘(î ÖjCÈxÖk…¬§H¯Â5ÁjÐuÐ(  X;Tzâ7ºðXP+ók®n\÷’(î Öˆñ:¬ Ö‹Z°æ“(Þ³® Ö] ¢Á;‚µV Ö$‡?dGèð¼'~£  ‚•™‚5G7®R%xÁ%Áz]×ßK)Ê%¢ä×kU°ž$Q¼%® Öv];D ‚:{‚u'‰Á<,óÄotá›m X¬Ì¬Çtã*!Qܬjq¬ Ör-XO(Þb×k›®ƒzQ‚ Öž`‘8,aÁZD¢@° X™+X÷(n Oôù¢8,IÁÚ¢ë`«(,`í°Ô¿Ñ…o¶`%‘––5dÈ.¯utt¨9sæ¨ÒÒÒPž˜¶¶6– «L7®$JPéš`-Çš`UiÁšK¢xå® V­®ƒ:Q‚`³=Á*$q X‚ç€ OüF¾Ù‚ÕCrrr:‰ÎÒ¥KUUUUçree¥>‘•A°\h\³uãú‰â–`ñLê/ˆcM°^Ђ5‡Dñ¹&X›tÈ›ì Ö$Kðð¬'~£ …`¥ ZÑ;v¬:uêTçrkk«1bË…Æõ¨n\?'Q‚ç]¬çű&X•Z°ÊHo¡k‚ÅmX– x‚윖xâ7º@° +//ïs— c_‹ˆU„臗›ššÂ?ÿì«åW^©Sk×îSuuÇŸ••õ)/³`™”óÍ=Ö¶7¬é$JɪkÛÛÓòeÁZ&Ž­í k6‰2ð™ÎÔ?/À’³Q”Y³öXÙÞP°&8u»êœ©ÿl[¸t ø8ÌÂõ…Ö¶7ã+v¹»×\èÁÊÏoÑ8e•÷ýí}{yD V1‰âVV®ƒ¥âXÛߟׂõ(‰â=åZßIº^” Øh¯k<‰ƒ,Á¬gô9àq…Ç‚¢«{°Ü¬Ãú×n•÷ýì5®YºqM#Q‚e® Ö³âXÛß—iÁšE¢x \,îŬ‚¬yâã0y,(+MÁ*((P§OŸî2køðᎠֻúwÁ2*ïûGí5®‡uãšJ¢¸%Xët,Ǫ`=L¢xó]¬u½ðÀo3‚`ƒÁZ£ë§$ÈŽ `=F¢@° kÙ²eáƒÑwΞ=ÛQÁ:¨pç !£ò¾Ä®`ýŒDqO°žÇÚþþœ¬‡HïI×km/<ðÛŒ xÝž`8,AÁzÚ‡ÉcA!X)LÓ=]Cš+?ÿ€>À5„ŒÊûþ{ö×Cºq$Jðœk‚U.ŽUÁšI¢¸'XkzáßfÁz{‚u;‰Á‚`A°²`&÷üüýúwÚ2*ïûïÚk\3uãšB¢¸%X|‰æiq¬íïKµ`=@¢xO¸&X,9«D`k瀧ô9à…o¶`e…`íÓ¸VCȨ¼ï´×¸Ôën%Xêš`=%޵ý½B Öý$Š÷¸k‚µ*Å~÷>Aðš=ÁGâ@°k'~£ …`e…`íѸ†Qyß?`¯q= ×]$Š[‚Åãoæ‹cmV Ö½$Š7×5ÁªeÔØ¬ÿ"q X‚‚õ¤'>“‡*@°²B°vë܆QyßßÁ¬yâX¬RÅ›ã¢`É>‹2ÖÙ¬±$KX°„ÇaB°²F°šõîˆ!dTÞ÷÷Øk\÷ëÆ5‰D *\,àü„8Öö÷%Z°JHï1×ëEñgQB°€µsÀžø8L ÁÊ Áz[àBFå}—½ÆuŸn\E$Š{‚5Wkûûb-X3H¯Ì5ÁzAüQIA°Öž`!q X‚‚õ¸'>“Ç‚B°²B°õî !dTÞ÷›!X¢‚5G«‚õ Å›íš`=/þ¨$«‚u‰Á¬¹žø8LªÁÊ Ájиw !£ò¾ÿ¶½Æu¯n\…$Jð¬K‚õŠ®ƒÙâXÛߟтõs…8î–`-TR¬±#X¯hÁº•ÄáÏÙ¬9žø8LVÖÖN}€Ûk•÷ý·ì5®Rݸî QܬGű*XÓI÷k©ø£’‚à°sxLŸî!Qx,(++k»>Àí2„ŒÊûþN{«D7®‰$J°Ä%ÁâI&g‰cm/ÏØ7?pÜ-ÁzV|&ÿ XmO°F“8,AÁ*óÄÇaòPVVÖ6}€k4„ŒÊû~½]Áš@¢¸'X‹cm_”/þ°o~¦[‚ŽH E±*X?!q X‚‚5Û‡ ÁÊÁÚªp; !£ò¾¿Í^ãºG7®ñ$Š[‚õ²®ƒ™âX,á‡}»'XψÏä/Û¬|‚%(XzâÃx¨+Í´¶¶ªéÓ§«¼¼¼É“'«ãÇ;*XuúWo•÷ý-ö× Ý¸~J¢‹]¬ı¶¿/Ì%?Ó-ÁZ$>“¬‚`;ç€Gô9 ˜Dá¡ ¬43zôhµjÕ*ÕÑѲbÅ 5jÔ(Gk³>ÀÕBFå}³½Æõ ݸ H÷ë~q¬íïOç‹?‹’Ÿ‡é–`=->“¬´'X£Hœþ,X—÷Éá¤`=ì‰àžtVšZ¼hO°~Hâ@°ë>O¼›{Ò!Xi¦­­-”&¾›á±XñÆ`¹!X¯êÆQ”É­Ó÷_`‰ ÖTq¬ –ðLþî Ö£âó Ár ‚ÁÊ´™Ü×êÆ±\ß_m¯qýÌ™[‚Å’ˆcmŸ—/>“??MÀ-ÁâËt÷‰Uök‰Á¬RO|˜U€`e…`½¢Çó¢øþJ{+ðÄgqº&Xw‹cm"_|¢Yž‹Í-ÁâçA–Š/ج›I– `ñóh…‡ pO:++‹gò^*ŠïWC°Äk…®ƒ»Ä±*XÂͺ'X<êQ¬ ÖH– `9ðð‰,X&å :™UOóà/z¬¬¬ª”žzßøþR{k’'>Ž[‚ÅSeŒ‚%)Xü<È)¢@°äÈÏ?¤ëà¢!dTÞóZ³êi¬¬¬ôþ˜(¾ÿ,KT° ı¶¿?¦ë'$ ÏÅæ–`M¿‹4ì|ɪ|Y ÖM$w뀮ƒ3†QyÏû «&›æ/z¬¬,ž‹êQQ|‰½Æu§'>N°À%ÁâÌÛű¶¿—å‹OÓÁS…¸%Xźî%*ì Ö$Ž[‚µW×ÁG†QyÏ{/«&›æ/z¬¬¬çôþ(¾_n¯qyâ·h»'Xÿ%޵ý}¶¬“(|£…[‚ÅÍ E ‚gí Ö÷I·k—®ƒc†QyÏ;hW°„{±!XY#XKÅŸCæû !X¢‚5V«‚%|©{‚ˆßäK Xb‚Õ¨ëà]CȨ¼çíͪɦ¹'‚•‚õ¬ÞÁïÅ÷Ÿ²×¸ =ñ[´ƒù. Á»Ukûû£Z°n!Q¼É® ÖÝâ79X¬H·k§®ƒ½†QyÏk²;Up/6у`äðáÃjÊ”)*///Ä]ÁZ"þ˜ ߟo¯qÝá‰ßAäž`ÇÚþþH¾øM|£…[‚5Yü&‡ XlO°®'qܬmº !£òž·#«æB„`äĉz§ÍWÍÍÍý ëñYœ}KL°ô ‡òű*X#H÷‹W4N” xÆž`]Gâ¸%Xuº¶BFå=oKVÍ…È=é¬4SVV¦¶lÙÒO..ŸÅÙ÷·×¸&xâwOº&X£Ä±¶¿ÏʃÇãݬ"ñ1xAPÁ,ž‰Ý2*ïygrw`ªþ¢ÁJ3#FŒ%Ë÷ýðò S[[[\±ŠââbÕÒÒ¢šš._—æŸ}µœŸ¿P|’Áë®›om{CÁàZ²¸ÄÚöö´\VÆÓtŒÇÚþî€` œ<ЙúçåŠÄÇàÍšµÊÊö†‚u-‰S×XçLý·E×A!dTþ;ß©µ¶½ï(>UOáÂBkÛ›q‚5hÐ ÕÐР:::T{{»Z²d‰þ†8Úƒõ”ø$ƒ¾_fïÛËxO|€k0Ï¥,¬‰cm8_ü&¾Ñ­¬‰âcð‚`‘½¬ÿ qÜêÁbÉyÙ2*ïyk²êNrþ¢‡,ÁŠ‹ÖàÁƒ¬z/Å÷µ×¸~ê‰puO°~(޵ýý¡|ñ1x<Ð-Á/>/ìLÕR¹J ÖP‡?‡;‚µV×ÁrCȨ¼ç­Ìª;Éù‹+ÍŒ5J:uªËkC‡uT°žŸÇ÷±×¸ <ññÁ. Ö2]7‹cmŸ™/>/S»%X·‹_"‚§!Xb‚ŽH†Qyϫʪø‹+ÍTVVª¹sçvŽ»jllTóæÍsT°žHévêk®¡Îžýb—×®¸âv5gÎ7ÕsÏ}]UUýmÈ•Wþ$éuúþCv+‰1ãç“zc© Ÿ’úÍ'¤ž{“ÔÕù=¿— î Öð¤¹æ_×ÿº¼v¹I~žTÖkU°R/wÍDRgÏw}íê[IUד:ñ2ëÞ&õµqɯÓ=Á—ÒåÜk®ùß>{mâÄjÓ¦¿T.\¡ÎŸÿ=õÊ+W«¿û»k“^gMjßÑäÇàñ8@·‹ïä‘]÷Ï^ß°á¯Tnîwõ—­›CÆÿ'õÎ;šôz­ Ö÷¨G64é6>Uׯ–¡/è6;m‰nãï\~o×{ºþç^~™¨¿pí{Ÿ’Zo·‹/Ó=aÌ—¿|¿Ú³çËi•õ¼ÅöÎ)ÜèÔeOð{_Ö‚½çýä¿dñ=VVÌä>Gïà·¥ÄåƒëgË ¡×;—¯¾z„:tèO’^Ÿïßo¯qóÒîÖÿôRzïÅ<îš`Ý”—ë¿ûåt°¶¿?Ÿò%Ýpû¢–¹WãŠë»¾öi{òëã^T·‹ÛáRâr'þO?½"éõÁ{‚åSZ„mÜÿ]ý/þ{Éâ–`ñT-³™7ï©ýè–´ÊzÞÓöÎiÜèû%+–y¯’úѣɯ¿èA°²B°ÊRzùàúÙ2_ˆ^¾âŠQŸ{-¾¯½Æõ_^J]ù¾\þV›ê{ñpK°øY”ßO‰Ëõßu™O¦ÌÑ£¨|ðïÔÿï×¥´N«‚•â-õáöÆ\>ž¾”ÔO#õÕŸúÁC¤­O~}î ßÉ7,%.ïñßûÂþSü¿jÓ¦+“^_ÌwZ°¾;åwm\ÿÿøyºþŸÕõ¯O¨_©ëÿ~]ÿ5ýY°ÌŸGûµ¯MVMM_N»¼ç=áô8ÜØ/YÑ|m,©¦Ã©­C¬¬¬Ù)H½|pí~¹»×ºÃ÷Kì Ö÷Rëο¡„T㻤®¾%µ÷º#˜ëš`]Ÿ—ë6þ{_þrž¬¯«çŸ¿:¥uZÛßïÏOù–úp{£–¿šOªv¯>©¾Fêhëåñ8~sòëãËÔn ÖOŒ{1£_gªª¾¢¾ô¥k“^_¾Lí–`2îÅŒæ÷ÿz5qâ·Tmí—’^_̳'XÿNIsÏÒßµñaŸ½¶mŸ®ÿÿúlùκþ›)¥õº%X<ÐügisÍ5cÔöíŒÖáy;=L$öášÛ7þ6Åõñq‚•‚5ËxüŶm_RõW×w._}õuêÈ‘?Jz}¾ÿs{kŒ—ô·Ìÿs©Ü õ÷zÂ-ÁâÛ¤ÿ#%.×÷ïÿéŸúªµõ÷SZ§µýý¾ü”{0ÃíZÇÛ|¯ç׺ƒ{Qݬ[z]²™T$;ž°'Xƒ))þÏ$ÝÆ§|þõ°®“x-n Ï…xgÚTWÿOõÝï3Z‡çÝçô0‘pó:÷^ó‘T×ÇÇ!VVÖC)ßñ{ר”)ßTwÝõ­Îeþÿyóþ6éõù~±½Æu›—Ô7Ì!Å]¿µ&û^2s\¬!)q¹þ?[Þ°áK*7÷ÛêŠ+¾§åêßÕœ9£î»ïoSZ§µýýÞü”Ç߄۵¼®áòØþìéÉLv}Ü‹ê–`ñT צÄå}à³åuë®Tÿûÿ/½ü‡ú£?¢ ¾©V®¼*éõ¹&XC¦é6þŸñß[·S×ÿ/ÿÿÜËU¡ëK¬yºƧŷ¾5BmÚ4 íò<ïg¯bÄ;0ߺԦÝéÝ0ÁÇ!VVÖÌ”. |~Γïëƒê ú¤ú·ê¹ç¾ªªª®¹òÊ!I¯×÷§Ù¬$€ÝÎsÒÃ{Éà–`=«ëÀOŠøõ﫱c¿¡Þxã¨ööÿ¦>üðÔ=÷üϤ×Áª`%ÙÓw{õëW'UµùòÝd ÅIe ÷¢º%X|©~hRÄ߆ª‘#½P´?ýô¿©Ó§¿¨üÿѲ—ôzƒàq{‚•K=Òm×ï]©ëºjSTý¿ªëÿJj½Ü¬Ôï$°aÃõÏÿ|]Úå#xÞ4'¯btw `6¼MêŸïHï*+k딿½ö6¾oO8¼[½”„}{‚•'޵ý½4?¥ž†¾€%ß-ÁúAÊBÜÛÁ\;‚µR Öÿ%qøs¸#X©ßèÔÛxÞÝÎ}ÉîKø8ÁÊ Áº/åKD½ï[l\£=ñƒkð˜K‚µD×Á`q¬íï%ùâ‚Í’ï–`ñø™%æØ¬A$Ž[‚õpJO]è <¯0«¾dóq‚•‚u¯xï…ïße¯qýÄ?¸º'XÿWkûû=ùâõÏû [‚ÅS%äŠÁ’¬Sžª¥·ñ¼ŸfÕ—lVÖV‰øÉÕ÷ï´+Xß%Q‚2×k8VK¸þݬÅë?³'XÂõϸ%X÷Š_"ö¼±Yõ%›C,!Á*,, %ËwÝu·?¾P”;ï¼ËÚöNÒÿfáxQ&O™lm{{bÊ”@¼þkûûÝw‰×?Ô?3qb‘xýOž<ÅʶþlêÏÄëŸáÏáJý»p(,œ”Uç>YÛ^íY-X‚ ‚ Ò`!‚ ‚@°AA X¢á1Y¦@°AAЃ… ‚ ÁBAA X‚ ‚ ,AAâNrrrðGÀ>€?êAý#,¤»lß¾]=ZåææªaÆ© 8߸ZZZÔ!CPyY¸TWW‡”}ºZ»vmÜ÷øä5wîܰ—€™3gNøZt㊷sG¿Çåù[&7à­[·ªeË–©¡C‡ª›nº)üÖù=þ&2jÔ¨ðÔ˜1cÔ¡C‡úÅ7(ì²û@$\Aý#ÙQÿ,Ü‹}þüyânø2Û… â¾ÇÝÄË—/ïü¦¸råJ5þü”7&nÍÍÍaÉ^1bDçï-Z´H:u*\^¿~½ºõÖ[!XØ’úüa¯’}õÏëáÏÄ— ‘ì¨ÿ¶¶6UPP NŸ>õç–ãI´sò7ŒÈ·Î¥K—Âo!©4®è 4(îrìïqCŽý]öx©­­ ¿íb VöÕd˜?^äÌ™3¨È,©ÿI“&©Ã‡ã<Áêß^.^¼˜tËtŧӸº[N´öîR^^®¦M›~£E²óÀ'|ô>aÂTd–ÔôÜl Ár<|‚êîú;S‰ýörà 7tÙùã}Ë€`aèë}€¸ò@g$»‘ð…HvÖ?z°g³gÏž°Áðõp\ºtiøÿ|ý½²²²óú;_‹½þ>vìØðz9‡/Óðutö¾ÜêëëÑk•Åõ?yòäð³òçá±C<.ˆ%ɾs q>|g(äo"Ç/½pø› ß5Âßþþ½Só‘²|«ôš5kú¼q¡{8»÷\"Èîúç»Íø¦¾Tuíµ×ª²²2uîÜ9Tb X,AA‚ ‚  AA‚… ‚ ‚@°AA X‚ ‚ ,AA‚… ‚ ÁBA`!‚ ‚ ,AA‚ ‚  AA`!‚ ‚@°A¢’“““A‚ H/È‚  A¤kß¾}êþûïWË–-ëòú¢E‹Â×:ÔYîøñ㪤¤Dååå©ÂÂBÕÜÜÜ¥ÌæÍ›Õ­·Þªrss•ïûjÖ¬YêüùóøÃ#ÁB$»ëܹs¡1:{ölø{C† QåæÎ«ÚÚÚÂ2Ó§O_c©âìܹ3\ž6mZX¦ºº:\ž={6þð‚@°É.Áâp¯¿¾nݺp¹¦¦&\ž9sf·åŽ;¾6f̘pyâĉáòÑ£GÃ刘qO‚  A¬¬†††ðõ)S¦„Ë3fÌ—–ã׸ç‹3xð` ¤G‚… +:ÇWƒ /²4Ýxã ËEz¨x<‡Ëò2¿Ž ÁB‚¥³xñâð½©S§vŽ·JTnÏž=ák“&M — ÂåØï‚ ,A²V°Nœ8Ñå²ß][îÂ… árKKKx· _IEND®B`‚StatisticalLineRendererSample.png000066400000000000000000000315031463604235500374120ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/category/doc-files‰PNG  IHDRXr5˜3 IDATxÚíhUçýÇ cŒ1ÆŒ1Æc0öÇc Æc„±ˆ]J¢©:ç„4Mðk5mÕ`‰Ì‘˪_m¾í !$h]m‹”Ú•´f6©N׺t±±?“oRmc“&æùæýè¹ß›ã½7÷ǹ¹÷žózà =÷&7¹ç>9çužçó<ç.C!„B<Í]ìB!„‹B!Á"„BA°!„B‚E‰òG~×]s ì#B‚E|–þþ~³~ýzóÃþÐ|å+_1_þò—Í׿þu“——gª««ÍéÓ§}}¢Íĉ<ÑŸ™È×g³˜\ºtÉlذÁüèG?šÓÖî½÷^sðàA399éÁrÿþBïùßø†)**2»ví2~øa`Ž3¿ûÝïf‚`‘àäᇎx"˜ïäæõÉ/Öëyñ³ÒýúÖüùãÿh¾ô¥/ÅlgßùÎw²ò}$ó»Äów%¹ìììôõ1FÙÔÔD$A°Hp¢ƒxN™¬L sU°²1ûöíK¸­A°ü*Y‰SA°ˆoòãÿxÎïOú“·Ïi¨æ•W^1;vì0ßÿþ÷,+é¼ûî»vh,ü÷×ðó믿nnÞ¼i>ýôS+`ßþö·}-XNÞzë-³dÉ’9Ï}ó›ß4###!ñCÜÃ5ñÖƒÌwЬ¨¨0¿ýíoíð‡jltrýÞ÷¾g–/_nOª‰¾^´óûï¿o¶mÛf~úÓŸš¯~õ«ög©¾E?'¼G Ù×WÍæÍ›ÍÏ~ö³ÐÏÐ0–~Æßþö·Ð×¥úž½<ÁÇÛ#¤úý÷ßo¾öµ¯Yjkk#¾ÞùóçmžD[ïMûá'?ù‰©¯¯·r4_ôuá?WûrjjꎯSûûÅ/~‘Òï›êç á{ôÑGÍ~ðû÷‘Š$Ì÷µ¿ùÍoæ<¯š¬dö»ûçèâH¯¥šJ}ŸêÝ>l¿VïOCµº¸ÒsúÉ­;'Ož´èìGý«m=Ž`‹y"Q?à}÷»ß5<ð€9qâDÌg¢ÂâF¯Ÿª`õööÚ«þd†šâ¬ŽŽŽ;z^’ùñ¼çL–$Âý˜û$ÿä“OƬ›Ò tt4æïóë_ÿ:©á°d~ßT?·ô¤S°$+îŸÌ~ô\¤ïill´é9• 8Ñ1 Ö{Öó~èE&!iK]]]Ôƒ¨î:1îß¿?boC¬ƒ¥z”t"ÓÕ²®¦Ý?G=‰¼^¤çÜ'ŠãÇÛÇ5ür÷Ýw'TÈé9ͬ ?Á}ë[ß Í¦0ùùùs^'Ñ÷œ ‚íÄíä7Þ¸ãĪ÷§6‘È W=/î–T{?"ý¾^|^ÖƒÍ÷=úýŸ×~Jf¿'Rë5ß~tKŸz õ³õoøãá½·A°qE½TŽ(ÄB'­?þ8郥Ðá_«!‡TK¯þØ?ÿùÏ9ßSRR’Òëßwß}ss†XœH:Ã{}ÏÙ X’HÕÙEû=W®\QŒÜïMÃL±âþ¬’=!Ï÷ûzñ9´··Gì½M‡`¹¿ÆùÝÝïîŸã\ltuuÅýœzj÷R ÚçŠþ¾Â×p!‚E,B扆ÂtÀtŸÃùÃþ÷ÁRµ4ªcÑÁZÃ:îŒDg%FzÎ] ï\…«Ç9)¤òúªå ìÚµk1÷a¢ï9+ÒI;üëç‚Wr¼êÁšï÷Mçç°=XªsJf¿Gû9±öU´çô;¸kÒ"}½ó»"XÁ"$Žè ªB`­Y£«ãðƒ¡ŠŠã9XêûÝr’겑žÓ•x¬•¶¶6O{Èb%™÷œ-‚ëñùÖ¬Š÷÷qçÆ[(èï›ÎÏ!‚¥a¶ðçÕ›œÌ~Oöý%Úî‘j‹ X$ÐÑПVÖŽ &3ÄõË_þrÎãZÈTK?x-Xг}¤Þ,÷°U¢¯ï¾’Õë’Ì{ÎÁrïƒHµxñD³ýÂ_çç?ÿyÄ×Ò,AÕý%ûû¦ósH‡`¹ êµLJ2ûÝKÁ¢‹ X„xÑÐn³¯]»Ö^M;¡ìÆ:y…Ç}•í$Úð|×h¯í{ÿûßÏùwˆ[}}ݺ%ü±¿üå/wüÎNC2ï9+ž} ÑšV±"‰wÿ }OOO}þóÏ?·³Ø4‹5•ß7ŸC¬ö“èç ‰î}«½NíW¢ûÝKÁr×`i©ˆHŸ!5XÁ"$^,TçdèðꫯÚÇÝõ#:™¸ “#\£½^´³swŠÛݳŸ~õ«_¥ôún L‹`¾ð ö¹«W¯Ú"zçk“yÏ© V2CFÉ ¹¹÷êõÔ£¢ ͪt–˜/ZNÁ«•Üã­óòsˆÕ~¼øû:uêTÒûÝKÁr[jö ’ì,B/†˜ A°HÎÅ=m¹Mw'ÚZ9±–~ˆu@öz±+:Ѻ•Lôõ•ÖÖÖ¸ÖÁJæ=ç‚`)ªe›oÄ{‚t/8 ¦'ûû¦ósˆÕ~RùÌÔVÝks%ºß½¬Xï5™u°,‚`‘@FC‚º2Ö’ªYrf\é_ js­ù)ºšÖ ÍY¬ÔY1Z«æÆ99èy÷Uq¤j´×‹v"Po’~og•k‰ ¾~Ó¦M¶‡)Õ×wrùòeû~T7äìR«Ë)ÖNæ=çŠ`)ZÍ^ûNmBû@ûZïU5|šµi…ôhQÝœ¾Gusz ½–$CÃbj‹NÍO2¿o:?‡Xí'‘ÏLß§÷«ÞW­¨ëö8ñîw¯ËéÉZºt霕ܵíåJîA°!„B,B!„‚`B!„ X„B!!„B‚E!„B,B!„‹B!Á"„B!!„B‚E!„‚`B!„‹B!ÁJ-;v옳]]]mÖ¯_4555Vx¶oßn^zé%€¤yä‘G, , , , ÁB°ÁB°ÁB°, , ¬ ¤««ËlܸÑš¢¢"³uëV344dŸ›™™1---¦¡¡ÁÊ“C°ÁŠ•ÚÚZÓÛÛkeJtww›U«VÙçÚÚÚLgggèk;::Lss3‚V¢)((°ÿVTT˜‘‘‘ÐãÃÃæ¬¬ ÁÈQJJLܰ¿Áò(ÓÓÓæØ±cvÈPÑaxÔÃå~Ì+‡ðÔ××›ÁÁAÓ××gwþe›m¶Ùf;»¶%Sì¶³y;§+//Ï"IºvíZè±H_G€¿zµØ@V355e‹Þ«««êÁB°,kžhF¡RUUeFGGçÔ`•––"X‚+uuu¦¿¿ßöNMLLØ,‰‘ÒÞÞng†Ï"ljjB°,+VN:e—jÐÌÁ%K–ØeÆÇÇY Á@°XÉ,@°,@°, Á@°,@°ÁB°Á@°, ÁB°Á Á , Á@°, Á@°,@°ÁB°Á@°,³}ûGV°|ð#ö X¤ÂÓOÿì\ù…Yµê +X«WOšõëo˜®®³ì@°,H„_|ÙÔ×h–-›1;w¾g·!ššOÌòå7ÍÿüÏö XÄÃSO½iî»oÚöV…÷T…×`I®$Y’-Éû , "ðü󯘺ºSZ:cöîýϼEîÏ>ûª.”Œut¼Á> Á€p¸dÊÊnšµk?7Ç¿žÐ,††«VÊ4¤È¾ Á ×êùW̦M·jªšš’^¦AC‰*„×°b4A@°,ß#¡Òð^eågqIQ<ë`iˆQ…ñûöý›} ‚žy¦ÇJU<½V‰ –hkë·¯½aÃu àÁB°üŠ×%?ÕÕ×ìð`"ß›ÈJî*€¯ªúÔ¬X1År€`!XþD5R*`—\=þø[I½F2·ÊÙ³gÐj%xz³ÁB°|ƒ3ËoóæÑ”$'Ù{JîV®œ²EðÀ‚…`ä|¯•¤FË/¨.*Õ×KõfÏZÆA=hWø|ÁB°rñDóíµÒJ몇òâ5S,¡Uâà½ú½, ­hEuõZéÍ’/_Û Á*®W‘½zÖ(€ ÁÈjTH®^+õ^¥£ Ü+Árؽ{ˆx@°,€ìD=UZA=WO?ý´ý¯KhM.­þ®·tþî€`!XêõÑÌ@ç>€éîJ‡`¹kÆ(€ ÁȪ]R±¸zTwµ?3‚%4ÓѹuO¢‹  X@J½Vš—‰Þžt –óþT/yôºH, î@+°K<´"»j—Ò-Sñ’ŽŸ¯[ú¨^C |ö€`!Xžã,k áЪìA™q§u²4šîâ}@°,€€±oß¿­XUT|–ö^«l^~Bû@Ë:Ð&ÁB°RZ¾@R¥!A —}¨_÷3\¿þF`E|*XÝÝݦ¦¦ÆšE‹™††3<»ƒô/Ûl³Í¶ß·oÝFÇÀÏMoï›ìŸ$·_zé³|ùŒÕçŸÿû'ÀÛ9-X­­­¶Çill,êרNKÅðô`Dž§µÔkuàÀ%ö‰Gkfé;êÍ¢~¬œ,¢ ÝçËøø¸Yºt)‚pÇ àÖM·lÌ‚¡ Éž=ƒvuù ®³¬Ü¬žžž¨½Vá3 %W---æÐ¡CÀmt#cõZ‰…º9sëÚT¯Ù˜Úïì+«+ÖZW'Nœ°kdåçç›ââb;ŒÈ,B€[8+‘«÷Šý±p¨—P½Yú—ý`±’;Ä$Ó7`€øÑ¬À+¦lñµzUØ'™©wS­Ûš5ÜÏÁB° qéb?dªý©«aù€,A7ËVM–Ž•ÜvÁB°ÁÈA4+P=&ôZeWìñR³ %]ì Á z­´ »z­è%É^œàUÏ®‚@£u—tÂ^¿þ†yæ™öIP_ÿ¡=v2ñÁB°ÁÈÂÚžŠŠqz­r”Ç/š²²›¶G 1F°,@°² • ­¨øŒ›3û ^ŸeSÓûÁB°ÁÈêéP»z­8!ûk˜W’¥::„ÁB°ÁXà^+‰'a¢ÏTÃ…6|ê©7Ù'‚…`!XéDË-¨×J…ìÜH8ð¥¥Æ®ÀÏþ@°, ÒÀîÝCöoŒ^«àÀkþ•+§ìÒì ÁB°Àt[ÖK§žY¢‚…`@ŠhhHCD55ŸØDÙ'À«öŽà, Á€$hkë7+W~aouC¯¸ àÕ6Ô£I<‚…`!X'µµc¶×jóæQz­`žø³eË0ûÁB°,ˆ†zªÔcE¯ÄKGǶ½¨FOµzì ÁB°à6ÎÍ™éøôZA2½ž:þ66^a X‚?þ–­¥Ñ|Õ]±O Yöíû·2T<Ëx X‚Ø^+M»×ß ½Vàeüš5v¦!ÃÌ‚…`AÀÚH¼ø½×jÕª/ìÊì´ ðçà:d X‚´_£u‹œ^«Gù€ÏÒŠŠÞ5ôŒÈ#X'J Ýøº>F½Vº ';ÈÄІ†«ì ÁÊÕ«s(5eX«Në„ÂJÃtÁRMŒŠŽU³gÏ Ÿ7d„.Ù!C-çðÌ3=ì ÁÊÅeSÓ€‘héªI+«@àòBK :©©×Š“dµÁŠŠÏl›Ô-wØ'‚•Ã'JÝù}÷î!{¯á —N6>ø1·xß –TONdôZA¶¡µ²t,ÖÐ!½‚å“¥N<*î•diÈD' õëo˜;ßcbÚ/ÐŃN^›6}Â9d-ªTñ»F¸ØE°,ž(µ°¢NHZ·Eß«“®üµ.ÖpáêŠv“+„ßœYÃá|® ¨^ÖY‹ý`!X>>Qj} ºº{e¥×R/—z¸ÔëEí&[gi©Íê=Ðk¹ÈáÙáŠ`!XA:QêD¥™ˆ:i­X1.Õs©®Kõ]ìsÚM&ÑЊz¬Ôs¥“Ÿ%äò,pMN¢ÁB°x¢Ôtw ;3%!´Í’´›LôZéd´eË0CÙà´¼þu\¥]#XV@O”êÊvf(êD§Ÿ­U‹Yƒ‹v³P½Vƒ_ àU[Vv“žY ÁâDùÿ3U³å—j ô˜žãó¡Ý¤Úk%y×D µ)®îÁï¨ðx ÁâDS¸Â—„@¸h7ÉÌT¯•jõ>3 šÍ­x;Y,ÁB°8QFì}а¡z 4ŒxkIf(Ònâëµ’ ××H¯µ{çvOšéÍ>A°,N”óÎP .ê·h7áh&•3u«· àuaªã$‚ʼn2)áºuE„+ˆíƹ93µ'‘ àu5|È>A°,Ë3ᢋܿíF½VQ¯ÃÆÑÙ¼yÔþݪĂýá#Áêîîž=ÑÕ˜ÂÂB³hÑ"ÓÐÐ`†‡‡ís333¦¥¥Å>&yccc‚•*î Ÿ¡HÁ¼¿Úz­´î䊛3Ä¿d +ÀûL°jkkMoo¯•©ééisäÈSYYiŸkkk3¡¯íèè0ÍÍÍ‚•Ö%!®Üm7ª+qz­˜%xoEÅ­ø¦¦ö‰‡ ì¿fdd$ô¸z¶ÊÊÊ,+#Â¥“7õ[ÙÙntÅ-©Òg¥Ï‰Ï yTRA¼ëܹs¶WK)**šóœz¹Ü9båžúúz388húúúìÒ¿lÇ¿­%ûcÐüõ¯—­p­^}«~kÕªi{àùïÿ~Ïüë_ïÒ^\ÛŽ`-ÔÏknþèöÉ`Üœ?ÏçÁ6Û^l?ÿü¿fy7Íòå3¶>èû#çëÌ™3¦¼¼'«§§'êÒ ííívæ`ø,¦¦&+M'Åxa1C1‚å Uh͆b2[¯Ù†Aê=ÎIÁRMU$X ‚ \êv÷[ý–ׂ¥[õX±Ú4@vàÌÚÕñ+(“¬äŽ`A©;ðjIé,é4;J.˜?”|fÿÕ¶V˜ÏÕéÑ^ –Þ¿z«TPW7Âtq€¬“ŽB·¡òûß'‚…`AJW¤‚y“Þºiõvûðá‹9sóB°$˜º:Ö,Ansýðšäãçx Á‚,7ªoÐýôTXºbÅ­ŠŽüØ´µõûR°$‘JÒr_4€Ü üïÖ¯ ý"XøH°ÜèÖ/:xé>{ee7m ך5VD²éÊ1YÁÒ¬@ÕY©÷îøñ×i9Z¯S¿À#XøX°"šJ®4›Ç)˜×MkDer†b¢‚å\ýê=ìÞ=D­@£ A‡üV`!X Ár£aC Þº'Ÿ±½\••Ÿ™ÆÆ+ zããDKC :K¹93€ÿ à5QÅM‚,7*Œ×ì;ÕmI¸4ü¦z®={Óº$D<‚å,Z¨^+ý>´À«~Ôð‚VÔa8ÕGHj$Z·f(N™ÚÚ1Û‹äåæ|‚¥:2Ãêwá67þÇ)€oh¸Š`!Xþ¬Hu:Ô½ü4D>CQWé,ýL jè’Ûܯ^’•«ð‚VŠóãwÜÒ'Ñu¨" –®\éµ6* p àÕsŽ`!X¾,7Î-}T0/1’/ÍPœO¸ÂKË-è54,©š0>sбE½æ:Œ·‚€`$Œ–…Q¼f0R° ¬\)á‚õÑG™ÂÂB ÁH ÍDÖÝ4+9Ú’0¾¬uëÖY‘²‚%Ùêïï7åå妦¦Á@°’F3’7l¸n—ƒÑ1¬—_~yN V8½½½‚2ªÇ’dé&õ’®@,ÓpöìY³qãF;$¨º+Í ìééa™ ÀÓÞ,çö[¬ƒ…` Xž ÏöX“Ȥ9'XцÝË6 X@.k, , ,?677›K—.!X‚åU®^½jjkk, Áò*Z‹…F,ËC¹:zô(‚€` X^¹»…Á@°¬$K=WÛ¶m3ccc‚€`±Ð(‚…`Çš Ö|k_±‚€`!X‚k"D°Áâ Á‘©xñ`ŒŒ˜ššSPP@‚…`€ïȈ`Ij$Rùùù) Öàà Y¼xq\C‘ X€¯KRÔÕÕe-**2ÓÓÓöñÓ§O›ƒ&\Óå~œ,@°,€À –†%WÊ=÷Üc._¾<ç¹D‹ç,@°,€À –ê¯4¼§lݺÕ9rÄþ¿¿¿ß¦*X’4Q2{ÔVØää$‚‚˜ö/ì/Ÿ ÖsÏ=g%KÙµkלá¾òòò”Ë]L/ÁÒψ$Vᩯ¯·ò×××gwþe›íLo_œýÿ³¬_¬Y:9NßwŸ™\¿ÞLü×™Ñ;Íöî5ÇŽ™ÁË—sîý9‚ÅçžmG¦Øl³½pÛ_¦a||Ü zœV¯^m<,ç&Ò±n Md+g»ºÌ{³â4µr¥=ANÌÊÕG³íSÿ×so?ö˜}þ“Ù‹•³¢¥¯›Y¶Ì>¯µ=^QaF7o6WÌ¥ÌëÇÓƒE¯'ø­K…í×®]óTÐæ, ÜÒ¥K,È $@?ø ùbÕ*{Rœœ½èÚ½{ŽÍw²|åùçÍ›O=e÷챯u}Ãû:7ËÊŒ)- س&1«­µöÖãgLÀ, ÁJQ†ÔSõÐC™žžžP¡»—‚U;{²èííµ¯-¹jii1‡B° kéok³=Sò“Iz$U/¿øbZN–zÝ7::Ì@S“ý¹×ª«mï˜ýù·‡%c’²O«ª¬¤IÖ$m’7 Á€,,‰Uøâ¢êYÚ¿ÿœY„©ÜzG9qâ„­ïR±|qq±imme!d ÝM­Xan._n‡òþ½o_\“î“eÏ3Ϙ‹‡›+f¤®Î|VYy«lö÷´½_³(û|íZ+g’4Õ¥"`‚€`¥˜‰‰ sòäIÛÓ¾ÐèÚÙƒugg§e%wð%‚S½”DE’"9‘TEë©ÊÖ“¥ê¿ô^4¬(IülV5¤9Ÿ€I*, ÁZ€"wÉÔÑ£GMåì²#Z‰.Ó€`A¶K•$Ñ –zzü|²Œ&`3·ë¿$bÚþ,¬[ÉÛöûh3‚åqΟ?oVÍt¹!ä2êR¯”äÁ‘*ID¬Þ› ,|õh9õ_êéz»d[L“°!`‚g®_¿nk¦Â‡ éÁ‚\”†p©Òò’/¥Êï'Kgˆ0š€ITµoÓQÿ…`€/kjjʼð fÛ¶ms ÞUƒ¥á±±1 rBªtb׌¿p©úÇÓOs²Lc‘{²õ_A0 Àç‚å,(êžE˜èâ¢dJªtbw¤J'v­®®Ywœ,³c™ Áô:X’˜¿ÿýƒ…`Aº—,D鄬5ªôo&¤ ÁJ_ý—3üègC°|.XÇ·‹fS,ˆ&UÎÌ7õX©G$[N°F›@°²>ÕNédiïçw[ªtâÌÆ“f®Ÿ,o/Ù6Dkø1› Á@°,X04Ë/\ª>K`5u ­ÿÊä´ Á‚´K•Nlΰ#U‰®¦Ž`A. mÁB°Às4<ã¾ENZ,C. ˜sîá-[â¾ 7mÁB°ÀÂïû'¼¸E ‚™0µaI•äJ7áv/Aq³¬ÌL¬Y3§þ«¿­6€`!XšTy}ß? ri¢Æ¥B=`š¨áÔªÍHÆ$\¹4€`!Xâ¾ø¥Í|X_o{¸f–-³~ÿ;@°,HpªüBÞ÷Á¿µ™‹‡ÛáE-œûÅìßÏ•ÆFzµ,+¨R•©ûþ!Xà×6ãÜúIO’-I—†Ù‘- Áò¹TéàïÜ¢&S÷ýC° mFµŠáõ‹º€áo ÁB°|B¤ûþeÓ-j,ð{›Ñßš† µ„¾G½Æ¹¶N‚0ÁJäþ A”ªl½ï‚Am3ªk«­µEñúÛüdÓ¦´¯8€`!Xœ(SœNž+÷ýC°€6ó’yû±ÇìÄg¹ ã,+¥jöŠ8Wîû‡`mfn³þŽí­¦fÿŽU·År‚µÀhi­·š¥4+UºæÊ—v¹ßf´ÜƒnÙ㬠¯u¶(Œ@°¬4K•³ ¡¤Šá?Ú ø·Íèoû½;íšZºŸý›g¹ Áò€ÐÍ”U¢7mâK»¶ ^ß°ÁtD–{ ÁB°’(zÕð€½ÉììÁTÿ§§Šv´¡‹+T¯Ÿ­%W¸" X'Ê(ÌÁ={BR¥«S]©êæ²ü!!X@›‰5ÁE÷Õ1Ã)ŒW)Ÿ XV`O”êhj ²jæÖÅÑ <ÐfE¥áË=°æ XV`N”¯>û¬•*÷}ÿ¸âäd ´¯_îÁ™a,ùâ³ ÁòÕAOWá·¨‘T oÙ‚Tq²ÚÌ‚L’±÷A\¶Œû ‚…`åþAÏ}ß?Ý{Œul8Ym&“už2 /Œ×bÄ|~€`!XYÐÓ}Ä´^s‹À†vï¶Ã‚ü1p²ÚL¶àÆ;Ë=èÿzŒÏ,+kz¯?n>~ðÁ[7Sž=X9W…–r²ÿ¶¿ÜXÞYîÁö´‡ƳÜ XVFN”ªÒpŸ½_˜ H++í ”,€\%Ò}™Ñ ‚•öåœÕÔoß®‚ž* À„–{¸½| …ñ€`!Xžž(µÈ§{?¦:3Üt^Ïr€`!XI –ZA]=U+-ª»Ú3üAF¥ÎrÆ‚•bÍâÅ‹ç<633cZZZLÃìUäIŒå¬`IœB«©ß>ph5uÝ ©ˆ]?±f-Íñ¬8“——"è>ˆñÆ#X‚ƒWŸ}Ö.\:µb…™Y¶Ì|RScÞèè@°, À .>l>­ª²5¹zµ¹ÒرW ÁB°,€$ ãßÛ¹3´Üƒ¤Kò…`!X€h¸Ð.bºl™FTa<‚…`!Xõj îÙc‡%XªÝb¿ X€‡Ë ±, ÁB°, ÁB°,@°, ÁB°, Á@°ÁB°, Á@°, ÁB°, ÁB°,e*^Ø_‚–ÉËË‹‚V ‚E X XÙ-X–’’sðàA399‰`‚åEFFF¬`íÚµ+¢X9„§¾¾Þ š¾¾>»ƒô/Ûl³Í6Ûl³Ív"Û¾ŸE833c éÁz°¼Êøø¸Yºt)‚V²©­­5½½½¶çJrÕÒÒb:„`‚•lNœ8ajjjL~~¾)..6­­­Ì"‹•ÜÁB°, , , , Á Á Á Á@°,@°,@°, Á Á Á@°,@°,@°,@°, , , , ÁB°ÁB°ÁB°, , , , Á Á Á Á@°,@°,@°, Á Á+ë233cZZZLCCƒ•'166†`‚•lÚÚÚLgggh»££Ã477#X€`%›ŠŠ 322Ú6eee Xɦ¨¨èŽ!C÷c X $///®Çœú,·`ÝÿýfË–-–ºººÐÿÙf›m¶Ùf›m¶ãÝ®©© v!„BH¦“õ‚UUUeFGGçÔ`•––òÉB!ÁJ6ííívæ ý¿©©‰OŽB!V²It,wÂk³’ÅW‚E!„B!„B‚E!„B,B!„‹B!Á"H¤ê ¡ÍÚ ¡Í Xäv^{í5³víZSPP`JJJÌÁƒ³¾šÅ‹óáÑfæMww·½mDaa¡Y´h‘]FE‹ÚM¬tuu™7Úv£;rlÝºÕ ñ!ÒfâÊ©S§rJô¬4äòå˦¸¸ØôõõÙm@þüç?guÖÏv ´™ùR[[kz{{í:uÓÓÓæÈ‘#¦²²’’vw»õU«VñAÒfæ~ßmÛ¶!XAÏC=dž}öÙˆÏéd´ÿ~{Õ/´ˆª oÀ±np­õýºúÓÉ«¯¾jW»¿ûî»Í½÷Þk¯Lœ¯“í¯^½Ú^¥”——›œ¸J¡ÍäV›q¢ï!´Ú mÆë6# Tù7¬ GÃlŸSWìÑ£GCWpÇŽ3H¨«ÁªÑ÷÷÷ÛÆ¾]VVúº'žxÂŒŒŒØí^xÁ¬[·Á¢ÍxÞf”sçÎÙÞ B»‰·Ýèuô;iÈÐf¢µݹ%üžÄVÀ«Èâ+ejjÊš~" 8<ùùù·Ý_§?÷×"X´/ÚÌ™3gì(5X´›xÛSŽ [\»v’6µÍlÞ¼Ù¼óÎ;9yŽB°Òt…099wãvºÈ“iÀѶc½‚E›ñªÍ´¶¶šíÛ·'tPB»qNÞ*z¯®®æƒ¤ÍDm3áõÁ¹V+Œ`¥!:áDãÖÕ€û áž{î™ÓÀ"™<‚E›É¶6£¢S,ÚM*ÇÍ($´™xÛ =XÏ… l£Ô˜³¢½¶¶6ûqwtt„Ƹ5Þí㮨¨°cÒŠ†]4V`Ñf²©ÍôôôÐkE»I¸ÝÔÕÕÙßU¿ê€Tã£>¡Í X$îhö„Šödû¥¥¥v(EÑÕffèªMèÿºJo8šMá|¯¦0ŸjÚÛÛç<þÄOØÇBß÷þûï›]»v™¢¢"SSScúûûç|ÏË/¿lÖ­[g Ì¢E‹ÌÞ½{Í7Øñ„‹,Á·B$º~ýºýºÅ‹›™™™Ð÷í߿ߌÙïy衇ìc’*åìÙ³v{ûöíö{º»»ívSS;ž‚`B‚%XŠz¥ôøsÏ=g·O:e·÷ìÙõû®^½j+//·Û7n´ÛCCCvÛ3õdB‚E œ`õööÚÇx໽cÇ»}îܹ˜ß§ÇÔó¥RHOA°!VxJKKM~~¾”4Çü>§‡JõXоWÛzœB,B‚5›'Ÿ|Ò>·mÛ¶P½U¬ï»pá‚}lóæÍv»ªªÊn» ß !Á"„V°>üðÃ9Ãzš]èþ¾‰‰ »=88hg jXð­·Þ²õôô„j²>ú裄I¼!Á"„R°”ÚÚZûüòåË#~Ÿ–mPѺ†«««Í¥K—æ|j¹´|ƒÄKC†fìêêbÇB,BHpÓÑÑaEªµµ5!1#„‹B¢dÕªUV¤®\¹‚`B,BI5§OŸ¶C{w<§Ç!„ X„B!!„B‚E!„B,B!„‹B!Á"„B!!„B‚E!„‚`B2ó|×]àc!!$C‚Eøl !!„“0á³%Á"„p&|¶„‹N„ϖ‚`B8 >[B,B'aÂgKA°á$òòò,fݺuæÂ… ò~ÍâÅ‹t.ô{íîî6555¦°°Ð,Z´È444˜ááa‹‹ÁRfffÌéÓ§MYYÙ‚‰Žó³R°ò½ÖÖÖšÞÞ^û󦧧͑#GLee%‚E‚E Š`9âQTTÚ–ìß¿ßö¾ˆ––û˜²víZ344dÿýúuû:W®\±Ûï¿ÿ¾í!Jäg/¤`eâ½:Qï‚E‚EÉVÁ*)IžÒ!áèìì4»ví =wðàAsôèQûœ8vì˜9pà€}îÉ'Ÿ4'Ož´ÿ?uꔹ÷Þ{MGG‡Ý~ñÅMkk«g‚åÑ[Íè{UÎ;g{µ,B,BH¶ –G ®SÐÄÄDè9‰„Ó‹£LMM™ââbûÕP9‚²cÇ[o´qãF»ÝØØhÞyçÏËïõÌ™3¦¼¼œ,B,BHPKyíµ×LUU•í½‰%@áC\úzIIÉí®"ý{íÚ5óÀ$ô³R°2ñ^Õõ}ûv366¶ Ÿ-!Á"„dX° {:t(´­w¯Î=÷ÜÚÖ×¶··›¦¦&»ÝÜÜl‡Ù4Ü–Í‚µïµ¯¯Ïºgâ³%„ X„,,e÷îÝ!!þÚµk¥}ûö!·i§Nâö5DZ/vî܉`B,Bš»#UV¬X`E^x!âmÚÖ«G!Á"$ÒȱcÇdòäÉ>ËôP‚:Ÿþ¹}Íû>IIIòßÿýßrîÜ9ùî»ïl»öÚkÛ”`iôùïØ±£É¡d‹‚`ÅNDÇù¿ tÝñ®ZµJºtébs²ÿ~ÉÊʲÇ9ãtZ犮£k×®öm;wî,¥¥¥!Ÿ_°eávŒ–eÉ‚ ¤oß¾rÙe—Ù§‡ëòóóå?ÿó?#êÅ –’’ŸÛéc:´úå—_Jÿþý}æE³½Ž?.K–,‘>}ú4¾¥¯a×®]ôÂmÃP=X‘Ö#GŽÈÌ™3í÷YŸ»¾†Þ½{ÛÛO%Ô;³fÍ’¡C‡6n­‡7Þx£ýZU\#}îC† ñ™¯ëmnÝ‹æýêÙ³§Ïz¼ëØ#<Ò8РA>ëö¾®#Ðk«©©‘‡zH:tè`S\\L£F,Bâ]°N:å³Lw îã¿ãÒ,Z´(äÎ]—{GMG{H­9‚¥c†ü{˜"]O$rrï½÷úÜÎ[vB%šíUYY)W^ye«½†PÛð÷¿ÿ½ÏüŠŠŠ÷ T7¶nÝr|šŠÖÉ“'#z ʾ}ûÂ>wa8p`£ø6§îEó~©Ì{/Ó×ïD{…ùºMœ×ýÒK/\Ÿÿ㨸úÏÓ×J‚EHœ Ö'Ÿ|"999>Ët:’žÿ·oý†­=ú7Ð7ùÝ»w7Ù)ë7síåiIÁ:|ø°ÏŽýꫯ–×_Ý^öé§ŸJrrrÄ,Úã}ûHÎŒv{iÏ—÷ü½{÷Úó=*™™™ü"y•§Ÿ~:êû½ÿþûMÄA_ëÆƒ ŠöÒ©Déít{.\¸°I/a¨çpèÐ!Ÿi=<«‡À›[÷¢}¿Þxã ŸùÓ§O·çëY¦ÁNð_—sÆf$ï‹ *!!q&X¡ÐU ûè¸ïÃ:þb¦b£ùðÃ}æëáMvv¶ÏüD$Ñ Öĉ}ækoŒw´GCw°"'z¨(Z™‰v{ù?†Þο70‚¥è6ýûßÿô~þuÃ\Ÿ# *(Þóõp]°øßÖ˜ÿsÐC~ÁêWsê^´ï—>_íýõ>#W³~ýú&1uêÔ&¡½•Áž“>¶ÊW¼‹#Á"Vkby_ +ÜÛ{gâŒÃ ´ct9»}K –ŽQòž¯‡@›ÛÃ×’=XÑn/÷¨×BÇ9;û–,o ;w®Ï2íQŠô±ÂÚ $ Ú›¤c¤T:ô˜ÿ6W/¼Ñqk‘lûP¯%Ú÷K£ãÅœù:VJ£'?è´öH:ã´´wMã-…*¥¡ž“ÿã2 Ÿ X„ı`éNwäz¡L=ôÍ;T/N ¨ÿxœH+ZÁjNïR´;.ÿÃwÁô_ÈöÒC‚¡Æ0ù_¶%Ë‘,ïez¨5ÒÇ wm0ÿûê v1Žvlž7:X>Ôó‰dDû~iôП÷2=ÑÂylدƒÓ½{¥¼Ÿ—Æln'Á"$N«%îí7|ÿAç­%XþÏ+’Þ¥h·÷ŽRéׯ_À³uœˆonˆî„õ,¼@½Yþ‡×Zú=>}útP‘ˆ¶n„»x­F÷¾ýòåË›<~¸zá/QÞ'4§î5çýÒÞRï硇ÿuŒ–Ž×òî±òÞ¶¡Á"XÁ"$ËŒŠž’¯ñÔëŒQÑÓÓ½ç{÷˜…z,ÿž‹¢gØ»þD÷|=SË?:Ð=ÒŽ@ 4pY9ã×tg©?“sýõ×7®/ÚíåŒÓqâ?öÇ N´¯!Ôvÿúë¯}äÀÿúháêF$ï ”n³P½EÑ–ÿ x=üæHKsê^´ï—ç âæÔ¿*f*šÎkõ~ÍÎx-‹ X„ X>߯³¬4Áβò?…^ÇéNG……z,ÿë …;|¤‡›üÏ&s5ëYeÎéúÞqDÈáí·ß»}]C)Ôs‹v{9;ogp»ÿYmÞ×SjîkˆæäýY HëF ÷@Ÿ©`è{®guêx2ç¾þc¶T€üu‡,í-ô_ó«Í©{Ѿ_Nt;ù¯SÇf9Ñ÷Ôù¦M›,‚`‚`ýO¢¹Nžeæ¦W$cm‰Œ u½nS¤×Á õ:ÂÅÿ‚£ÐÛÍÙ^¡n§"áñÍæ¼†HåJ{¤¢­:F,Ü{àÜ׿÷©¹×G[½zu“‹æê¥9š[÷¢½Î[°ÞMíÍtè'–ô9"XÁ"ÁjÒ“¥§Á{_éZ§ üÖ$]æìxU>ü¯è±tÌ“fÑûéõ…ôBáî£×øÒûéø(çP¤Ö,ÿç¦=+º“×+½;‡o"ýÉ '¥=":NJŸŸªSR)Ñ^ï3Ö¢Ù^Ú릗Gp®‚®ëÕç¤Ww®ït¡¯!ÔÉú¼ô0™÷àëhë†òÖç¤c¬ô=Ð× ÛH¯y¥ÛÌ‘D}îú^9õB_ƒR$‚¥ëñ¿(§s¯¹u/šúíÄÿ €þ´R0 t=+‹ X„B!Á"„BA°!„B,B!„‚`B!„ X„B!!„BA°!„B,B!„‹B!Á"„B!!„B‚E!„‚`µ½¬X±Âgzîܹ2sæL€f3þ|Ë;K—.•7Þx Ù<ùä“‚‚‚‚€`!X€`!X€`!X‚‚‚€`!X€`!X€`!X€`!X$#kGJWŠ‘’]׿aOÓ aYÅ‚Ð:Œª%Ý(FJNÝ]ˆ‚…`¸‘ìÚléE1RòêîAt, ÀäÖæÊí#%¿nHÞæf0„e=Š`!X­C^mžÜI1R&Õ Ct, Àä׿Ë@Š‘2µîþ†=Í-` Áh5&×N–Á#ez]¢ƒ`!XndZí4F1RŠê2ö4ÝÀ–µÁB°Z‡éµÓå~Š‘òPÝhDÁB°ÜHQm‘¤QŒ”9uc Áp#Õ>$£(FÊ#uãö4ÝÁ‚Ðj<\û°Œ‰“Ò®]»¸y.±(ÖM@t, À‹`™ä›oVÅìóÖæ{°Îœ9czŸ;w.=XPX[(I#eVݨ†=M0„e-¢+Úè…šÙ³gËÉ“'}Æ`?Áø“k'Ë`Š‘2½.ÑA°âW°.\(‡¶{§êêêì1X*Fš;vØgzŸEXVV†`üƒ‚Ú¹›b¤L«Ó΀ÛÁV˜¼öÚkö¥ôÌÁ¬¬,û2 §OŸæ:X0¾v¼ô§)“ë’‹+¹¸‘±µc¥/ÅH)¨Ö°§é †°¬Å‚Ð:d×fK/Š‘’W7ÑA°,72ªv”t§)¹uƒö4w€!, ÀÕ'Wõ½*à²H׫‚`!X‚…`¸Šê ™i¸ ­*Wô¼Â–Ÿ1•cçßrƒŒ=2¶qZ—G²¾î…Ý%õÏ©—EºŽX•?Uý‰zhT°îC X‚ƒ’ó~Ž-?†t’äÉ2ˆPë÷á8¹fà5A—#X€`!X‚‡½ßM«Ÿf2lîá½›rn’ô×ÓC>‡ÁB°,×r¿¹ðfÉ;žgËv¿²ï•oç?@Ý:ëp–übè/B>ƒÜÁW°ƒ!,k9‚…`¸û2 “ —/ ;ØòsUÿ«$óHfÀÛéòPÓ’;IÚ¡´ ÷õgR.Ó`R°^nØÓ C Xƒ ºµp¡Q ÁB°,~*§UËE—^ÄOå‚…`!X@Û‡{æÇž[°îCXÖ“‚à^6Uo’ÑcÁB°, Áp!¿®þµdRŒ•mUÛ¨‡Fk(ÁB°\ÍÆê’F1V^ªz‰zˆ`!X‚à66ToбòǪ?R Ö00„e­@°BeÏž=2þ|IMM•ôôtY¹r¥œ8qÂ^–”” ~(¯.—ácekÕVê!‚…`!XMS\\,•••rþüy9{ö¬lÛ¶M|ðÁFÁ¢ ¾)«.“¡cÁ2-XI`«IIIA°ÚÏW?/ƒ)ÆÊªþ@=D°,+|Þ{ï=»WË,•-%//O6oÞ,õõõ@±¾z½ ¢+UÔC ÁB°BçÍ7ß”¢¢¢Æ1XÞ©©©±«´´4 X9x§¤¤¤á °¤ªªÊÞ@ú—i¦™nÙéß|þ¹‹b¬üéÿþ‰úhhúرʆ=Íp0Ä7ß¼³÷»M VEE…ÝãT[[ô6:NKÃÓƒ?ce¦g&uX°ÒÀ–õ4‚…`AkÓ—b¬yЍƒ€`‚…`éG1V~åùuX°ÒÁ‚1 ?ÅXyÐó u,@°,p#(Æ ‚‰-X#Á–õ ‚…`Ak3b¬Ìö̦‚‚ndÅX™ã™C„¬ 0‚…`A ¸‡b¬<ìy˜: X¸‘Áce®g.u,@°,p#÷QŒ•yžyÔAH`ÁÊCXÖ* Á‚ÖfÅXyÄóu,@°,p#Ice¾g>uT°þ£aO3 `!X’)ÆJ±§˜: X¸‘бò¨çQê $°`e!,ëY Á‚ÖfÅXyÌóu,@°ZZ°’’’‚.KKKC° &¤QŒ [°Fƒ!V°Ž=R¾,hIFRŒ•Ç=SÁ«¥Kå)Ä„Lб²È³ˆ: ,XÙ`ËZx‚•’’"EEErøða bBÅXYìYL ¬XŽÁb;ÄŠÑcå ÏÔA@°Áâ,Bp#c(ÆÊÏê $°`C¸R°œC‘ŒÅB° äRŒ•¥>gÈ‚•†°¬5‚­ÍXбRâ)¡‚‡ÁäQŒ•ežeÔA@°ÁjiÁª­­•Å‹Kjj*=X`Œ ce¹g9uX°rÁ®¬eË–qˆŒ“O1Vžô2ZnyCãí#]fª¬ô¬¤B‚ Ö¾†=Í80„ëKÔùàÁƒÍ¾ÿž={dþüùö ùôôtY¹r¥œ8qÂ^¦=c6l°ç©<):¨Á¦ÅAÉx;C:öèh ÖO»üTî*»ËžeŸ+eÐÿ$SÏMµøÂ@騳cÀuŒµÆÊ½¯ôƒéQ-3Y,@°Áj¥ŸÊ¹AîÅÅÅRYYiË”ö„mÛ¶M|ðA{ÙöíÛe×®]·Ý¹s§”——#XЄâ ¨\:4ʬ Ç&H׉]íùí/m/…ç }n{Ñ%\GÏ9=åþ½÷G½ÌdyÚó4uX°òÀ–µÁŠ6úcÑšY³fIMMMã|íÙ*((@°  3â ¨HMýî‡C„ÞóïÙxÜYz§ÜWqŸäž/Ãw—îÓ»\Çå]/—^szÉÅ.¶Þ”}“LúzRØe&Ë*Ï*ê X€`Åû…Fß{ï=»WË@ïíåòŸ‡`ReXÅ0¹¢ç¶`åTæ4ΟøùDé4¤“ô˜ÞC:ÜØAºät‘©'§\ÇÚÿH2dÈÌs3eƙҿ´¿tÎìv™Éò¬çYê $°`C¸V°‚õXiïÓŒ3ä­·ÞŠzo¾ù¦5ŽÁ Ôhž3>Ë_°JJJÞKªªªì ¤™vçtQœ”Ü÷smÁR¡ºçýö¼k]+cm¼Í õƒäº¤ë‚ –÷´Ê”öŒ…[f²¬ÿë©L'äô±cï#:ùæ›-1{¿ãB°¼9|øpÄ뫨¨°{œ¼±Óƒmé¡ST°¨ÿaì•Nëx+ÿÛš§E{À ŽøÌûqLJ]Æ!Bz°èÁJ€C„§OŸ–ÒÒRû‘D QºûgöìÙròäIŸ1XãÇG° .¹w+ì&ŽO°K»_Õ÷*{þ i7ÈøÏÇÛÿë`÷;–ß!³;7Þϲ”»ÖÞ%½é%_ØÓ鯧Ëm n »ŒAî¦k²Ö%æ,œž‘‘öv‡ zé…;vØgzŸEXVV†`A\^¦A/¿Ð¡s[˜~Þÿç’}$Ûž?áë Ò%¯‹Ý£¥t›ÞM N4ÞOo~ÛÞöÃ@ö†Ûö|¤§L©ŸÑ2.Ó€`!X 2È=’³C}Èu°À u{A° ±+ ‘°‚¥‡ö"éÁâJîà¶ŸÊÑÞ%~*Á«Åóí·ßÚ²£‚ƒ`A,à'—ù±g3‚U†°¬õ‰u™†æžEˆ`Á…P@1VžôúH’““åã?F°À#)ÆÊãžÇ©ƒ€`‚ÕÒ‚îjîÄ‚4бò˜ç1ê $°`MC XÄ€cÁ ,Î"—’B1Võ=V:ø}÷îÝÄ„dбRì)¦‚VK Öºuëüàƒ$77Á‚˜0œb¬ X€`‚Õ ‚¥=W{öì±/Óà?æJÏ.D°  £+x¡B V!Âõ‚¥u®å-X_}õ•¤¦¦"Xî£+ó<󨃀`‚ÕÒ‚5cÆ [d¾øâ‹Æ Ž>|XŠŠŠdþüùÄ„Áce®g.uX°CXÖ î¬ƒ½DCee%‚1á^бò°çaê X€`µÆeÞ}÷]™7ož}HPÇ]鄇â2 3QŒ•9ž9ÔAH`Áš†HÁâ:X`šce¶g6u,@°ZR°öïßo÷\åååÉ3Ïv¬ªaOS†øæ›­1{¿ÛüY„¡d­¦¦Æ¬ÒÒÒ¸ìÁRÁºõVCðmÌô`!:¦°¬rw÷`½üòË2gÎûÿ'N؇SSSm¹9}út‹ö†?Þ^w< ÖÂ…–ôì)`{@°,W –ÊÕG}dÿ¿víZŸ±Xååå-*X*lÙÙÙq+X½z ‚ÆÌÖ40„ëKÇG9QùQIÒã–•••’••uA‚U\\l¯G{®T®6lØ [¶l‰KÁzüqKz÷0= X–«k̘1 •ì-U*HÞײJNNnö‰š}ûöÉüùóíõäææJEEEÜžE¨‚uÛm† ±3‚5 aYeî¬M›6ùˆÑš5kìù|ðÌ›7/a®ä¾h‘%}ú‚Æ,ËU‚¥‡î–,Yb*ÔñX'Ož´ç«\½òÊ+ %X·ß.`{0#XSÀ®¬xŠIÁZ¼Ø’¾} Ac‚å:Ázï½÷dîܹö;+ú¿ÎK4ÁºãCÐØ€Áš †°¬çÝ-Xï¼óNПÌÑ3E°žxÂ’~ý Ac‚å*ÁÒÞ*ƒõÙgŸ5Îûâ‹/dÙ²eö2 ,p§`íkØÓLC¸^°ôà÷ßßdþ·ß~ës,· Ö’%–ôï/`{@°¬„,=»Á ,@°.à¡^hÔ‰.L´C„*X‚ÆÌÖD0„e­w·`½ûî»A¹ÇúLB“‚µt©%wÝ%`{@°,×]¦AÏÔ ‹ê!AEÿO´Ë4¨` (`{0#X`ˆ„,.4ú†””Xr÷݆ ± Ár…`íÝ»×> ¸`Á‚&Ë.\h/K¤ŸÊQÁ4HÀ4ö`F°òÁ–µÎ‚åH” –óûƒÎÙƒåååö2œD¬eË,¹çCÐØ‚…`¹B°222äÕW_ y…w½M" Ö½÷ ‚ÆÌÖ0„k+55UΟ?ò6Ú‹•(‚µ|¹%ƒ ‚Æ,Ë‚¥=sæLÐåõõõömI°† 0=˜¬ñ`ËZëNÁ*..–­[·]®ËæÏŸŸ0‚õä“–ÜwŸ€!hìÁB°\!X°®]»V>ýôÓÆùzE÷²²2{ÙþýûJ°†0= X–k®ƒµråÊ WqןÏI¤ë`­Xaɰa† ±3‚•†pµ`iöíÛ'³fÍj¼Š{QQ‘ìÚµ+á.4ª‚¥cúÁ 4ö€`!X\ÉÝ…‚õÔS– .`{0#XãÀ–õ‚•(‚•œ,`{@°, Á Ú¼`ýGÞf,ÁJÁ*-µ$%EÀ4ö€`!X– kåJKRS Acf+ aYk, ,@°ÁB°š%X#F‚ÆÌVÁJÁzúiKô§Á 4ö€`!X–K+=]À4ö€`!X– ë™g,9RÀ4ö`F°Æ€!,k5‚•(‚•‘!`{@°,Ë…‚µj•%™™† ±3‚• †@°H°F0= X‚åBÁzöYK²² Acfk4²žE°E°F7¼ç`{@°,+@,K‰gøÌ;þ¼lذAV®\iË“R[[—‚µzµ%ÙÙ† ±3‚•†@°"HRRR#ÞÙ¾}»ìÚµ«qzçÎR^^·‚5fŒ€!hìÁB°¬¢åY³fIMMMãô‰'¤   .kÍKrr Ac‚…`E(Xiú(~‡ ýçÅ“`åæ ‚ÆÌÖ(0„e­B°š+XþÓÁæ9ã³ü«¤¤ÄÛUUUeo ýÛZÓÏ=gÉØ±†hí÷—i¦™fÚúرÿÓ°§ÉC|óÍobö~Óƒe°KkÜ8Cðmb߃µÑ1=X X³gÏ–“'OúŒÁ?~|\ ÖÚµ–äå ‚Æ, ÁŠP°vìØaŸ9è}aYYYÜ –º˜ÆÌV²žA°¢¹Lƒ÷åÚÒu°, ,@°¸’{ ³n%&‚ÆÌÖH0‚•@‚•Ÿ/`{@°,Ë…‚µ~½%z T0=˜¬t0„e=`%Š`Mœ(`{@°,Ë…‚õüó–Lš$`{0#Xi`+kòdCÐØ‚…`!X.¬²2K¦L0=˜¬`ËZ‰`%Š`M*`{@°,Ë…‚U^nÉ´i† ± ÁB°\*X……† ±3‚• †@°D°^xÁ’c´k×Îè㛆Æ, Ár©`MŸ.ÆPÁ 4?7÷C¹ä’Ë›Ü6ÁÖê¶99GäÆGJûö—Ù˜zý4ö`F°RÀ–UŠ`%‚`mØ`ÉŒs²²Þ–Ž{ØÒóÓŸv‘AƒÊ—y ‘÷}ü§£¡ à¸\yeoûÿ ,ûÿÌ̃F^»74ö€`!X–KkæL‰9*W£G²¥© à˜Ü|óÄ&·Ñe¡¦£¡gχ$)i»ý¯^s$5u¯‘×í=Ä^°^nØÓ܆@°D°6n´¤¨HbNûö—Jaáw¶4»ÿ2¾è¢Kl:t¸Qúö]$Ó§ÿ=ìcåç*?ÿy߯éË/ïjKÖÅwhà2¹é¦l™2åk#ÛÆ, Ár©`Íš%1gذ ¹âŠž¶4åæV¼. vÿÉ“Ëw,’›oÎûXÝ»ʈnœþÑÚKfæÁ9'3gž‘J¥sçL#ÛÆÌV2²žB°8Dغää¼oKT§NC$9ygØC„þ̘qÎî u›qã>”k®è3O+Úõpˆ,@°¬6!XŽDMŸ^Pp ִi§ä'?¹:ämºtÉ‘ÌÌ×}æiÏÙĉÇ}æýøÇ,H Á†@°8‹°UéÖ­Ð>³O%J»_uUß&·ñ?kðúë“eäÈ BvN¦N=%½zÍ•¾}—½}NÎaéÔih“õ¸Vz÷~D&MúÚžÎÈx]úôYÀY„€`‚…`µíë` ô‚üô§m)ºúêþöu©B]ÃJç¼Y®».É>ÄwÙeäŽ;–‡¼¦Öu×%KVÖ¡€ß§ÏB{»öœ©lÖs,@°ÁB°¸’;Wrh®`%!,k‚•‚eú·¸\¿E€`!X–ËkÚ4CÐØ€Á†@°D°ÊÊ,™:UÀ4ö€`!X–KkÊCÐØ€Á †°¬'¬D¬çŸ·dòdCÐØ‚…`!X.¬I“ Acfë>0‚• ‚µ~½%' ‚Æ, Ár©`‚ÆÌÖ0„e-G°A°Ö­³$?_À4ö€`!X–KkÂCÐØ‚…`!X.¬µk-?^À4ö{ÁúsÞf0ÁJ ÁÊË0=˜¬{Á–µ ÁB°Á ,+jž{Î’±c Ac‚…`¹P°Ö¬±$7WÀ4ö`F°îC X X€`‚?IJJ H¼ VNŽ€!hìÀŒ` CXV ‚u!‚ÕVz°V¯¶dÌCÐØ‚…`!X.¬ìlCÐØ€Áº `] `¥¤¤ØäååÉæÍ›¥¾¾>.ëÙg-=ZÀ4ö€`!XV3RSSc Viii@±rðNIIIÃ`IUU•½ôokM«`ee ¢µß_¦™fšiÿécÇÞmØÓ C|óÍÚ˜½ß®?‹ðüùó’šš—=X«VY2j”€!ø6 fz°SXÖRz°Z*§OŸ–lp§‚•™)`{@°,+ÂKee¥Ýs¥rµaÃÙ²eK\ Ö3ÏX’‘!`{0#Xw!¬ Ⱦ}ûdþüù’œœ,¹¹¹RQQ·gª`)`{@°,Ë…WrúiKÒÓ Acfk²– X‰"Xii† ± ÁB°\(X+WZ2b„€!hì ö‚µ§aOÓ `%`é$À 4ö€`!X– «´Ô’”CÐØ€Áº aYO X‰"X÷ß/`{@°,Ë…‚õÔS–$' ‚ÆÌV?0‚•@‚5|¸€!hìÁB°, ,@°ÁB°Â±b…%II† ±3‚u²#X‰"XÆ ‚Æ, Ár¡`=ù¤%C‡ ‚ÆÌV_0‚•@‚uß}† ± ÁB°\(XË—[2dˆ€!hìÀŒ`݆°¬EV¢ÖàÁ† ± ÁB°\(XË–Yrf ±3‚Õ `%`Ýs€!hìÁB°, VI‰%ƒ ‚ÆÌÖm`ËzÁJÁºûnCÐØ‚…`!X.¬¥K-8PÀ4ö€`!X–Kë®» Ac±¬Ý {šÞ`+AkÉK 0= X‚åRÁêß_À4ö`F°z!,k!‚•‚õÄ–Üy§€!hìÁB°,— V¿~† ±3‚Õ `%ˆ`-^lÉw‚Æ, ÁB°ÁWÖ­`Ëz ÁJÁêÛWÀ4ö€`!X– kÑ"Kúô0=˜¬` Á ,@°¬æðøã–Üv›€!hìÁB°,— VïÞ† ±3‚Õ aY ¬D¬… -éÕKÀ4ö`F°º!¬¬ž= Ac‚…`¹P°{Ì’[o0= X‚åRÁêÑCÀ4ö`F°nCXÖ£VssþüyÙ°aƒ¬\¹Ò–'¥¶¶6.kÁKºw0= X‚a¶oß.»víjœÞ¹s§”——Ç­`uë&`{ˆ½`ý{Þæf0‚u™5k–ÔÔÔ4NŸ8qB âR°}Ô’[¤Ì@c‚…`E˜´´´&‡ ýçÅ“`ÝÜðžƒhìÀŒ`uCXV1‚ÕÜ$%%E4ÏŸå/XYYY¶¥¤¤ÈðáÃí¿­5Ý·oŠté2R~ùË ûoçÎéLÇpºµß_¦™fšé¦ÓÉ2rdIOïlÿÍÈø%Ó1œNO³÷{þüù‰ÝƒE!„b:q/X³gÏ–“'OúŒÁ?~<ï!„B¬æfÇŽö™ƒNôÿ²²2Þ9B!„ XÍM´×Áò÷Ø,€æâ*Á"„B¡‹B!Á"„B!!„B‚E!„‚`B!„‹Dš@?5DØ^¼„÷‚mK,r(>€"ÿõ_ÿeÿ¾ÔÁƒlßp·¹÷Èÿ¾mùýÖç>wîÜ&ó-Z³zï¦íÉkUô³1fÌY·nÏ/i\h½u–'býv¶m Z²þ»Ý…<6A°,+âè…kõôoúÞKrrr›lK¶mÛÖ¸ý/´ÞzÏÖ…kgÜP¿½ÆpÏ?P¯S°zí~bÉ’%RUUeÿÿÞ{ïÙÓÎ}öìÙc¿þïS¸ÏM°í¨î„Z‚EâþÛÊ€Js{m4OŸ>m˅ßwܼy³Ýðé|åå—_–ßþö·5|Î4Þ£ ˆóÁÖ£²²²q™~c×ß»t¶Ï>ûÌç5ê¶Òe‡¶ïé‚‚‚&ÛDeÀùöàÀ™1c†Ïm²²²ä‹/¾°·õîÝ»ƒ¾§¡¶¯÷{쾑¼·Áž«éº}êÔ)»qwê«Ê¬þ¨>ú×sïmæÜ^wååårìØ±FÚºukãíô°ñš5kÂnÏPÛLß˽{÷6Ê»î\ÚBB ç¨ÛKëiKÔ[ïm¬ Õθ¥~z¡ž¸vÞ¿ÞG»ŸÐÏ‚³=ô¯óÙpÚ<ç}RÙrÞ§PŸ›PÛ;PÝ ÷D°HÜw7;ß ýoïôähtçåÈ…³,Ô·%ÆTÄ:ºsÕ¼æ•W^±§è75ï×ê5ú÷Høo_ÿÛkƒâPïi¸íëý\‚Ý7š÷Öû¹ÆCÝÖF]ÅW¿¥‡Ú‡ªç:}âÄ {'¦¯Ý‰nÔˆ÷kÏÈȈH°‚m³É“'7ém«‚¥Ñ/-Qoƒm;ï6¨¹‚Õ–êw ×êùGÓ4w –Êœö؇’:}~Îg#Ôç&ÔöTwÂ},w¢~KÐ1FN7|(òo@#]ÖK·‡ÓÍ­ÝÐ:j[{áv‘¬ëƒ>… Ú]ü*{‘½·ÑìÔ"}oã¡nkʯj³,ËgY¤õ\§UÒêëë£ÚñD³=yvÞmU°tÇç/*Í­·-QÝP¿#}>Îó¦ hî,s§õV¿„DòœB}nB½¾@uÇ­ð, –î´KW±è7‚PßÉІÔÿ[”~Âí4ÚʇáèÑ£?Èü±½\Çfëyh ÁònÜ´‘ŒtGì½ ußPïm¼ –FÏ&œ7o^“e‘ÖsgZßëãÇûôø¿çÍ•ïChnéÁR)Õ±A-Qo#iƒÂÉ©êw Çõü£iš+XzˆR¿À衽P=XÎs õ¹‰d{{×pŸA‹Ä`i«s¬[?ˆÞ@ç¬,ýÀèàÂ-[¶4~È´AuÆèá§ËxÖ¬Y‡Öô[Ž~Ûš`UTTØc ¼£cAœFE–ê8,}í_~ù¥¬^½ºUKÇè `í•ñîIsÆ){¬`ïm¨û†zoÛ‚`éx¸@¯+T=¶.}u,žFë¿÷¸;}/¼ÇÛž¡¶™vÖç¢ÏI×·lÙ²6'X:~æÕW_µÇz^¾zIªqKý6+Øóôº#­÷‘¼^ýÂQXXhÿ¯Ò/ zŸ¿ýíoöÿú8û÷ï—M›6…ýÜ„ÚÞêN¸Ï ‚EŒ~Xu­j#¥ãDè}øbñâÅöYú!Õ±S±õ¯J†.Sôg¼Š¦ÑŠzÿ)S¦Ø¶¶&XÚˆ8‡˜œèëš>}ºý¿~ðu¼¾FýVõæ›o¶ª`­ZµÊÞÎúX:PÕ‰v;gZW°÷6Ô}C½·mA°‚- ¶-½_z¦”ÖaÞOÇèá9sæøìÈ‚mÏPÛL둞 ¥õHëÖ‘#G|zzâ½-Ñç­½¹Á®ƒÕÜzIªqKýva°çèuGZïCí'œÛêgÁù¡'ìxŸE¨½Zú|ôqµ'Óû{°ÏM¨í¬î„ú "X„BšD¿ù¿þúë¶pÒÖ¾¼‹Bâ*úm\¿©kÏ•"tÃu}HbEë/A°!„B,B!„‹B!„ X„B!!„B‚E!„B,B!„‹B!Á"„BA°!„B‚E!„‚`Bâ;úî¡p{8 ÅÅÅöoêo°eddÈÂ… eïÞ½QoCBA°! /+W®´_ûÖ­[åÌ™3rþüyyõÕW£Þ&!Á"„D%o¾ù¦½\eÄ;«V­²çëÐÛŸ={Ö¾ÍîÝ»íy*\š%K–ØÓ‡²§U$T²"Y¿fÍše/$'Ožô™V¹²,Ëç9é¼PÛ0ÜóQùÔéÉ“'7>}\ïuUVVÚÿëëV1Sœm ræýØŽ˜)ºýU´Gè¾ýö[{™ ¢>B‚E‰`…ƒ¥’£½0*7º“v¤'˜`;vÌžWTTdOÏ›7Ïžþâ‹/ìiÝÉë´®3Ø:T´tžöîhTtZeÇ?áÖïg]ÑF׫2£÷õúÃ=4oQ ´.gÞ¯÷³Ï>³çiO–÷í?þøcqÒžDo)~íµ×ìé5kÖPé A°!±¬pÑ^ímqz]t:Ü:tž#"*I¡$.Ô:œùÚ[åLgggÛ‡÷Nœ8Ñú›+Xú:UVT(õФ¦ó_o Ç ÷|œåþ½IÞ· öuž>PÛÍéýZ´h‘=­‡5 !Á"„Ä‘`é!§Pëpzlpa;<‰`é!4íÑ4g¾ÓCnýþÑûë ó–G§'hÛ¶mòÉ'Ÿ4öª…¬pÏ'”<µ„`iÆo?=l«ëÊÍͥ‚`BâI°´GoWQQaÿÕK„ZÇ|`Ïs‹ëlöø`yGÅÈûða¸õûg×®]AÇh•——7¾¾@’ãÿœÝ&ÜóyðÁíå_}õUÐu;‡õ° =äèa èÙ‘Î.gŒ!Á"„ĉ`9ƒÜwîÜiOë_Þ²e‹Ï:êêêåGÏžSùѳ5:vËéqr¤B%LE$RÁRáÐûhœC`:ˆ<’õŠ#*"õõõö<=CÏû1§M›æ#J¯¿þz“çêô†9¯5’çóÊ+¯ØËõdí Ô^&çÌLgÝz8Ϲöªé휓ü¹Ê—_~éshRO „ X„ V0‚]¦Á¹†”÷eTt·öèh‹¼öŽJ‘îVñÒCWzÈJÏÊ‹T°TJT²ô¾YYYvO“÷á¼Pë–ýû÷Û÷qÆVéáG•6},žá§’ä lß¼ys“çªòUPPàsè.’磗˜˜2eн^+¥—¹ð·æH–s)g»êz#d½ˆª.ÏÏϧ²‚`BÚ¢¤‘ ‹#XÚû×RqzõÐ.!Á"„ X®~|ë­·ìðzøoùòåövÔŸïi©h™ÿ8.B‚EiÑC`Î`syôâ£:¦Ê¹J»NÔ1^-]—¾/zQUB‚E!„‚`B!„‹B!Á"„BA°!„B,B!„‚`B!„ÄQþ?àédf·; IEND®B`‚jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/xy/000077500000000000000000000000001463604235500260125ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/xy/doc-files/000077500000000000000000000000001463604235500276575ustar00rootroot00000000000000CandleStickRendererSample.png000066400000000000000000000435601463604235500353330ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/xy/doc-files‰PNG  IHDRXr5˜G7IDATxÚíhg~þ¥”£”£PJ)¥”B)\)¥Ž£”„„DŒQª`ç³9Ÿ„ª`Uª1Q¥wHÜáãrH'CˆPOÄg!„‚Å%ê]”èjŸ#;ÖY+]”¤>ÜÈ&T‡Qâ¹RõÖß÷÷{·£ÑÌì;»³»óçó˜—õìŒÞùîüùì3Ͼû„B!„B‘ê J€B!`!„BX!„BB!„°BŽû‰'4ÛyiÜ^„°й~ùË_ªçž{NýÉŸü‰úßø õë¿þëê·~ë·Ô3Ï<£†‡‡ÕÎ΀U8Šj›ÜýH“÷ùóŸÿ¼ª¯¯W}}}jmm-3ûûßýÝß®X•WßýîwÕ¯ýÚ¯y^„Mû½ßû=« ëSNÀr7êÉÉÉTïë‘8ƒX•Wßÿþ÷­.¾q¹% °âÔ¿í{,-­÷}! ¡”è?ÿó?õm"ç…æÉ'ŸT·nÝRÿó?ÿ£þë¿þKØïþîïX),£;wG˜÷Û¿ýÛjccÀBX£ÞÞÞ™¿ø‹¿P»»»‡–“Û*õW•ŸnkkSû·«o)IVK íþàÔ³Ï>«á¬Ð…M.Þÿ÷¯~ó7S·îînÏõûö·¿­þøÿX¿ÆþáêœP±€õÁ¨¯ýëºéïsŸûœú³?û3]I§îß¿¯Î;§þüÏÿ\/'ËKVI¶Ïéîº`­ÏǬΜ9£kn^CnÃÊk¼ùæ›·éoþæo°N2?ŽÎ#BB–¸n·ÁFâìD‰!söìÙCNX 3b£Ë—/ºØ‹ë%NZXÀúÅ/~qè‚,ë}áÂÏ µû¢ÿúë¯ëçåVÚ‘#GB¹f^ó–——ÀÊïüÎï¨+W®èywïÞU555€5??`ZnßþêW¿*(ä}tΗ}£˜ú…ÉzÚÜÐ'®§¼¶<:Ÿw:~BBU—Ün‰â"#=g?ÒoÐÅL ãúõë¾ÓØØx`ÞììlÑ@ó•¯|Å"Ýë,·§¼jòïÿþµRëË_þòçÌí2#q¢œî‘»¹EçW›RÂëý[?÷ë@uCsÐ&¨.ß4ÃSøõ!n¢û=øæ7¿(‚Ú[o½•ÿ»bê`¹o[Ê·EÅ~‹0I¿X€€…P äpÔ«I˜]ävPÊ‘Á÷Äým¹R^GrS…Æò÷çîTýÆi ZŸÑÑÑ’ÆÁ’1¤Üߺ“!J… Ù>÷f¥Ö¯À ªo1ã`XXU\’;§HrGr1•[_rÁ•[DâX˜ü‹8’Ý2\¹ävJ,ãæÈp æuð~ö³Ÿ}—‘Ó%C&ßz”퓾eL/ÙnNâ&ÉP fÄrYNFÿ‡øÏñ¦¤Ò¯ÔÁÜB”å ­ÏG}¤ë(¹7ó-Lùò€8XÎ/xõ!¯é¬µÐm2¿oþÉ{,+#ªý<Žmý¢,ãdɾàÉ]¦£ÉÀBÀB!„°B!„,„B!`!„BX!„BB!„°B!„,„B! !„BÀª†îÝ»§Ρ¾¾^7#ùqT¯æ¥ýý}544¤úûûõïŽIÛÚÚâ]F!„PökmmMYRjC£.8X,2X4F£XŸ ¨ u¡6Ô…ÚXÅëÞ½{êùçŸWõõõºMMMé{––ƒ–r^__÷ìC~øÙ«‘Á°ÈGPêBm¨ µÉ`­­­©“'OªåååC󺻻U.—SûûûjooO]ºtIµ··ûŸ.© u¡6Ô…ÚX588¨nܸa½|mm-€E‹F£Ñh4+H---²ä ÜÐÙÚÚò\viiI»Z~€%ð%íØã‹ûðð°ÚÙÙñ+ÓœêííÕ6¦!myLÚ´¸ò(€533£æææô£€•™^XX¨Èú`Å©>+++‰Ë1-u¡ì/a¦M£Þç_ê‘Îã)‘€USSsà6àÅ‹UOOÏ¡åZ[[}3XNmllhÀêëë#ƒ…ƒE>‚ºPjC]¨Mö,,§´$ÔîÔèèh ³å%¯~È`‘Á¢QêBm¨ µÉ`8qB;NN9r$ÿycÄá «íímÕØØH À¢Ñh4-{€511¡.\¸w§$gõòË/ëÿÏÏϺVÎ`»ó‡WCCCjdd ÀâÓ%u¡.Ô†ºP›lŽƒõÊ+¯è»ÜÒØÚÝݵÛÊùÿééi=f–ÜrljjÒ·ù-Âè+œd^P#@6‚ºPêBm,FrÇÁ*°žx»U°øtI]¨ µ¡.ÔÀ°b›ÁJ*`Ñh4`X8X|‚¢.Ô…ÚPj`Xd°â Xä#¨ u¡6Ô…ÚXŸ ¨ u¡6Ô…ÚX,2X4F#ƒ`á`á`Ѩ u¡6Ô…ÚXVµ2X¶ã[‘Á"A]¨ u¡‘Á°p°BÖ>ÿ’X|º¤.Ô…ÚPj`X±Ê`¥°h4F°,,>AQêBm¨ µ°¬´g°’Xä#¨ u¡6Ô…Úd°îÝ»§žþyU__¯›Ñþþ¾Rýýýˆ¤mmmyöfY,,u¡.Ô†ºP›TÖÚÚš:yò¤Z^^>4ollLMNNæ§'&&Ôàà g?a–%ƒE‹F£Ñh´T–@Ð7<çµµµ©üôúúºjii)yY,,u¡.Ô†ºP›T–@@VCCƒ¾=( cní9ošÛ€îçŒl—5·Ý€ÕÛÛ«ï›A“6ýé§ŸêG¬™™577§¬Ìô‚çß ùõoXf¹ Àªf}Ä)Ëû%µˆËþ#uIêþž•ý%nÓîGêsðüëœïyhy¯çÂ.‹ƒåíRá`U¦Åév)Ÿº© µ)_]ˆFà`U°ÜÎS]]]Ù,2XÙ¬85jA£q¬Ó¬²ëĉ²S¢#GŽèÇŽŽµ¹¹y WÕÜÜìÙO˜eq°p°8éâFPjƒƒÅ>“jÀ’oû]¸p!Ÿ»ZZZR/¿ü²þÿøø¸žï\v``Àó`¡e+Û€§qXâtÒeìêBmÊW‹q°ª®W^yE‡ÜåÖ ÀÖîî®ÕØVNÀb,,,ÜêBmp°Øg,Fr'ƒE.ƒF£q¬Ó,   ‹OÜÔ†Úà`á`Xü!€E‹< u¡6d°Øg,+ñV@㟸©MZ9,,‹ VÙ‹ ¹ cŽc €…ƒ•!ÀÂÁ©¡.8X8Xì3€•È VœO,d°ÈÓP—ìÖ¦Ç,2X€…SC]p°p°Øg,‹ yF‹`X8X8X8X|â¦68X€E‹ ,u!ƒE‹}&E€%¿'èÕ Í Ó€•D«ZŸº³v±ôê‹} +€e« uòäÉ’û!ƒE À"OCÙgÈ`Ѭÿ¯5;; `á`á`áFà`q,á`±ÏX…«¶¶V·cwÆááaµ³³sh¹û÷ï«S§N•Ü,2XyšBý‘ÁbŸ!ƒE+U!w¹(`Ô××whÞùóçÕÜÜ\ÉýX™æToo¯Þ iËcÒ¦×ÖÖô£ÖÌÌŒ®—< X™é……Ï¿—_ÿ6€¦¿jL¯¬¬Äf}¤66Ë›åʹ>R—¤íï¶õ+¥¿8í/q›6-Ië_‰ó‘9ÿ:çû³6†ã)ß"ÜßßWuuuž“ moo/¹2Xd°p°ÈÓPgö2X´LÓ°½½­<÷â‹/ª¥¥¥’û!ƒE À"OC‹}† ûL&«»»[år9í8  ©‘‘‘üü»wïªÎÎ΂ÁöBýÁ"ƒ`‘§!ƒÅ>C‹}&3€5==­ºººTMMjjjR£££‡ìöíÛ«P?8X¥VPÃÁÂÁÂÁÊέµ¤Ô¦˜óç™Tß"d$÷øe°È‘Á"ƒE­“¸îê‰'¼[…¶‹}‹ €…ƒ•zÀÂÁÂÁÂÁÊ ƒUAÀÂÁÂÁ°È`e°È`‘§!ƒ•­ÚT°È`‘Á°p°p°,,,,,, À"ƒE‹F‹ ,€`á`á`á`á`á`á`X8XVuK€È¯]¹r… ¬ÄÕå7ÞðݧýöA2Xd°È`‘Á°¬H› Iû·û7,¬ÄÕ¥˜}    À°b Xd°’}‹«Ò#Æ-˜¦ VR“4íƒÕÊ`¥a`f€`ÅØÁâTq€ö'‰p°âéÒ$° íƒ8XÁÇR1¯›vÀÂÁ°XqÊ`‘H`•«.I¬Rë’fÀ"ƒ|,Xd°,,,     +­€%?ØìÕ Ísk_ ©þþ~ OÒ¶¶¶È`X‰Ê`U°È`‘ÁJë>XÕ €E+.€UÌ<·ÆÆÆÔääd~zbbB â`X8X8X8X8X8Xœ¬b«­­Mmllä§×××UKK ,‹ ,2Xd°È`qþÍ&`ÕÖÖêvìñN6<<¬vvv Îs«¾¾þÐ-C÷s8X8XVæBîâ@ Dõõõ…šççvy=gòYnÀêííÕ”mvy¬Ô´\tfffÔÜÜœ~tN/..†îO.n~ý-,,xþ½à6ý;O²QlÔý%aZ¶Ùk~¡‹[ܶÇl‡×üÙÙYÏýO°ÊQ¿¸ôWÍã)Šã1h¬ÔúD±¿r­Ÿ `•c{ËÙÓï¥ã[„â<ÕÕÕ…ž‡ƒU«Za`¬ø9XA넃…ƒUí÷¸œVÐ>‹_ÒÀÁòÔöö¶jll =¯££CmnnÈ`577“Á°È`•©.i,2XÕÍ`%°â”Áâ·`,­îîn•Ëå´ã$%C-ŒŒŒœç¾8>>®¿9èüáÀÀ€…ƒ…ƒ…ƒ…ƒ…ƒ…ƒ•=ÀšžžV]]]ª¦¦F555©ÑÑQ«ynÀb¬ÊŒƒ•TÀb¬Ê¾wŒƒÅ8X媟ì?~íêÕ«U¬8ƒÅ¸Z#¹ã`á`á`á`á`…ª‹íù  À°È`‘Á"ƒE‹ V‹ ,   ÀÂÁÂÁÂÁÂÁÂÁ°,2Xé: dƒZR¶Ã½Êrd°È`¥°Â¾n±Ç{àëFØ À°2ä`•²Ž^Ÿ â¾Í•¸HWÂÁJ"`á`á`•ÛÁŠfãü%,‹ VŠË+`U&ƒ•DÀ"ƒE«Ü¬,,    ÀÂÁÂÁÂÁÂÁ°,2XiÊ`%í"M‹ €UZ++€E ÀÂÁÂÁÂÁ°p°p°p°p°,‹ ,2Xd°È`‘Á"ƒ`X8X8X8X8X8X8X8X€E‹ ,2X,2Xd°XòƒÍ^M455¥ì¹®®N544èr^__Ý–÷Å-Óá`Uw@R¬d8X•<«á`Ú§ÛŸ&+êFã Ç8XU,?uww«\.§ö÷÷ÕÞÞžºté’jooÝ,ÀªöI«8À*æ}‹:kD+›€UŽ/×ñ‡éT|‹PòVjwjttT;L[[[%õC‹ ,2Xd°È`‘ÁÊä0 ÛÛÛª±±1?-ä(A÷Rû!ƒE À"ƒE‹ ,2X™,ç7І††ÔÈȈž7??èZ9ƒíAýÁ°È`‘Á"ƒE‹ ¬LÖôô´몦¦F555éÛ¶c[9ÿÔ€•ÏFTyÜ/,¬rŽu†ƒ…ƒ…ƒ`1’;¬Šg°’ö Vv3Xå„2Xd°È`XV䀅ƒ•+¦€…ƒ…ƒ…ƒ`ñ[„V¢3 d°²›ÁŠ3`‘Á"ƒE ÀÂÁ°p°p°p°p°p°p°,‹ ,2Xd°È`‘Á"ƒ`X8X8X8X8X8X€E‹ €E‹ ,2Xd°,«$+èâwÀŠr, ,¬rÖ¥Ø}5jÀªÄ‡,,,‹ V +ªõ#ƒÏ –­Û÷ VÔ®bœ‹ ,2X€…ƒs+®€UŒƒ•ÀÂÁÂÁÂÁ°È`Xd°bžÁŠ+`UjßJ"`‘Á"ƒE ÀÂÁ°p°p°p°p°p°p°² XA?輿¿¯†††T¿"i[[[žý„Y– ,‹ ,2Xd°È`¥°ü466¦&''óÓjpp°äeq°p°,,,,,¬ÌV[[›ÚØØÈO¯¯¯«–––’—%ƒE À"ƒE‹ ,2X©¬ÚÚZÝŽ=~c†‡‡ÕÎÎŽžW__è6 û9£0Ëâ`á`X8X8X8X8X8X™ ¹‹%€Õ××çënù9^¶Ëš|–°z{{õ}b³#Èc¥¦åÄ033£æææô£szqqѺ¿O?ýT?ÊEËü½¹¸™é……kà¨Äö;OÚîùQ®ßÚÚZ,¶×l—Íòf9¯¿·¹üRšÔ¥Øíñ[?›7Íò³³³¾ûÿ;wJª_)ï‡Ô%L…Þ“°û¡‹t1Ç“íñh»Q6€åu¾tŸßœçËB5,u}ýο6€eÓ_1Çc\Ïoa§8N§â[„â<ÕÕÕá`á`á`•è`žèêjÞ&ÁÁÂÁ*ÅÁrŸßp°p°p°´½½­õÿ;::Ôæææ\Uss³çß…Y– ,+92Xd°ÒXd°È`EX>Tª¡¡áÀ-9}Pww·ÊårÚq¸’¡FFFô¼ññqým@ç7åµ¼nZ  ÀÂÁÂÁÂÁÂÁÂÁJ `½ôÒK‡Æ¯}øá‡ª©©©àßOOO«®®.USS£—µÛÊùzŒƒÅ8XŒƒ°Ê5VÒ‹q°’1Vœ‹q°+rÀçjjjJŽ;T.ÐÄHî8X8X8X8X8X8X8XVHÉð WnGé“O>ɇÕ¬dg°Þzë-õæ›oz6¿‹ ,2Xd°È`‘Á°È`• S§Ni8Y]]Õ€%°µ¼¼¬Z[[õ­?+ùÖ믿î»~_8X8X8X8X8X8X8XÖ;ï¼ãû{‚^°ÊXA÷¹,2X6cQe9ƒu]¢zO’šÁ*çë’Á*ïù2Ž™FË!¼²³³Sߔܕ|ƒp~~^ÅYi,,¬¸ŒäžÀŠÐÙ X%»1t°¬Ê8XI,¬Œ)€U® V‹ß"¬lv)é€U®}+ €Ué×%ƒ•|À"ƒUÂo 5        À°È`‘ÁÊ `e:ƒáø`Y,2Xd°È`Uùáàà úå/ `á`á`á`á`á`á`á`XQéW¿ú•þ‹ ,2Xd°È`‘Á"ƒ`E$‹Fq°p°p°p°p°p°p°¬áêµ×^°",Û±{¬h3XÕú„—µ VоJ‹ V%«”c3«,›±Ý*‘Á*÷‡äØ…ÜÝSHr±vãÃçÃ†ì“ XAVy¬4Vœ¬8V6¬RŽÍ¬:XÕý?³€%ÎÕ¹sçÔÖÖ–u?òÈßAÑÆÆ†:yò¤ïzdÁÁ²9€¬td²–ÁŠ`‘ÁÊf+*аÒ÷ˆ‰hT~ÇP~·ðáÇ 400 fgg,,,,,,,¬ôV¡±¯ÂŒƒ%.—ü´Îææf (Ý¿_ÿ°tÐ:ÕÖÖêvìq¡‡‡‡ÕÎÎN¦‹ ,2Xd°È`‘ÁŠ`‘Áª`9sFÝ»w¯ uþüy577gµ~r+Q«¯¯Ï¬Lsª··Wå±Ôé›7ojÇM."òèœ^\\Ì//'†™™½}ò蜖ål˜µµ5½œôoþÞœ€Ì´ün¤mfý°üÖO¶©”z9?á¸çۮߛo¾y¨¾fZÖQú[YY±îϽ>ÆÑˆjÿ°íÏ餸ÿÞæ£MR—b·Çoýl"Y^Þ'³?ÉóÎýëÎ;e{? ­ŸÔ%l…ÜeÈ[?ÛãIŽ{¿óÑo¼jýLso©ïG!Àò:_ºÏoîóe!ˆs>*´½æükXÅÖ/ŠóeÔÇ“Íù(ìñd3ýÿñögy-3}åÊ•È_/‘·mÀL6´½½=Òa"*á`Ù:Sd°JûD&€å·~ÿú¯ÿJ‹ Væ3XRK¿cDÞ2Xd°’–ÁrïÓÎ}Á¹O':ƒ%n‘ä§äÖ\?•ãõ7/¾ø¢ZZZ ÕÏöö¶jllÌ`e5ƒeXd°È`e9ƒ%`‘Á"ƒ‡ V&K@E ¨¦¦¦,€u÷î]ÕÙÙYpY5>—ËiçJàjhhHŒŒd °²šÁ²,2Xd°²œÁаÈ`‘ÁŠC+€õÔSOé °©¯¯W{{{úy¹*9¨RKÀéöíÛ—žžÖNš€^SS“­ú·mOaNÖù¿U°¢^?7`9kˆƒÞÁªämÛ}¡ÒëWNËvßr9Û‹‘­QÌûWÀŠjŒƒƒUÌ{Åù¨Ðë–ÃõÌ`É­A+ÑÓO?­>úè£ó²<’{¥«\Ë•°¢\?À"ƒÍI¼€v[ª•“©æøQ¥î QV¥ëW ÀŠr¬`{ΪÖkX×Hß~y¬žžuéÒ%ýÿååeí&XÕq°,¬¤8Xq¬r9Xi,ÛoÊe °ªí`Űp°Š”|=V K$Ã"8óW­­­VË™°È`›Á°Ê—ÁJ`™c ÀŠW+΀UŽÜ^&ËýÍ=,¹5xâÄ P°p°p°p°p°p°p°p°,KÉF|X4`µµµ©üôúúºjii!ƒ`‘Á"ƒE‹ ,2XéøaÉm¼ŽŽŽü˜YnÀ—LÚ±ÇV;;;žýÔ×׺eè~΀•iNõööêÀ| ‘ÇR§çææôa›Ã9½¸¸˜_^¦åy™o 3-Ëyõo.Î×[[[³vjd¹[·nù®ßÍ›7óý `™õ1óÍ´,¶>6ã—Øl‡éON2Îõ“yfúÚµkº¿••ëþ¼Ö·”ýAËoý$Ãè÷÷N'Ž>6ï¯MR—0Ûf,¶B‘ ô–ò~øý½ÍúI]ÂöWh;üêi³?ùíŸQæý°q#Ân¯ `…=_Ú@t”ï‡óü[èÂoûþ†9_ÚöWŽã©PæüöýZ¿bû+v:‘€uæÌuïÞ½‚®•¸SX’ýò»eió¬ê;XQo¬Êe°¢ÎèÁ*m[äøóÛ·ä¸%ƒ•ü V9·ƒ Vʬ°Aöººº’,2XÙ,2XÑd°²XIÉ`U°È`U6ƒ•À"ƒ¹N’ù’o+zÉy›Ñd°š››É`Xd°"Ì`e°’’Áª`‘Áªl+ €E+†€%ƒ–ær9íF \É0 ###žËŽëo:¿E800€ƒ`á`á`á`á`á`á`XNh’‘áåÛ…2"|SS“õ]–q°È`‘Á"ƒE‹ ,2X#¹ã`á`á`á`á`á`á`X,2Xd°È`‘Á"ƒE‹ €…ƒ`á`á`á`á`á`á`X¬êg°lö »ÅôGëà{,ï¥4yÞü_š¬5ôEËâ= ³\Z+ʺÁ°ÊyÌXV¬¨Üb/¾•pK’â`Ùî«Q9a¥´V9ö,ËkŸ©Ô9À°È`Å(ƒ•%ÀJJ«Ò€öXÊ`…ÝgÈ`X^û €`á`á`á`á`X8X8X8X,+’ VF+)¬JV¹ÆJ`•+ŸC+Ý, ÀÂÁÂÁÂÁ°p°p°p°p°,‹ ,2Xd°È`‘Á"ƒ`X8X8X8X8X8X8X8X€E+=¬jŒ¯’¶ V”5LB+Kã`Uzì#ówe¬cu‘Á*ßø}å8欒“…󜧦¦ô=×ÕÕ©††ýCÎëëë¾?íÕÒ XQ|êŽ`E¶VµFN“ƒµ{w+k#¹G½ÏîŽ}¡€U-—&U€ÑÈëa,Fr/ƒä 8wîÜ(êîîV¹\Níï﫽½=uéÒ%ÕÞÞî XYr°¢Èd °ÂäFÈ`XYý-¨÷™,VÚ2XQV˜ €±VWWµSõðá T[[ `á`á`á`á`á`á`á`XAÚÚÚRjss³ (---iW˰¾¤{\Üááaµ³³ã V¦9ÕÛÛ«)ÛìòXêôÜÜœš™™ÉŸœÓ‹‹‹ùåeZž—ùæ„a¦e¹°¯oûƒ­·nÝò]¿›7oæû“µY3ßLËrQÕË=]h;ÌòXÎõ“yfúÚµkýûÕ¯Ð[Ž÷£Üýùm¯Wÿ~óï¿©³™^YY±¾È˜þ m‹éïÊ•+žï¯< d”²½aöG¿ù6ÛáÕ¿íûïÜßóåøó;Èñ]íõ‹°J9_ú­_ÔÛ[ìù£’ýÙï…^·˜óG1ÓQ¯_±Ó‰¬3gΨ{÷ît¢äàimmõÍ`9µ±±¡«¯¯       +{–M8}ttT¸]¶’Ü–„ãÓXò)^¦½ÚÕ«W«žÁ Z?™G‹ VÖ3XîcDæÙ#d°Ž•t¾tÖÙœ/Ã.÷ VÐv³o‘ÁJá0 n¸ò• {Xmoo«ÆÆÆTÖ~ô#_ÐùéOZuK`Æo9 ,¬¬;X¶CŠà`•XÎó¥{99_†].î–sßroGÐù+£€5??èZù}ãPàjhhHŒŒd °ª=Vœ«šnšÆÁÊ `U{̶Tƒ•Àªö8Xq¬rwV‰€Uèö¡óÿÓÓÓú›ˆ555ª©©IßVLÛ·q°p°¢°¬V„tâ`á`eÚÁ²8–ÊXe<ÏXŒäž(Àªv+΀•¦ –ìAûL2XÕr[ãžÁJ`%%ƒUiÀ*%ƒÔlÏ«î}«TÀ*÷ñ`X8X8X©¬r9XI,,¬J:Xi,,‹ ¬ÌV¹2XI,2Xd°*™ÁJ;`‘Á°p°p°p°,,,,, À"ƒE‹ ,2Xd°È`X€`‘Á"ƒE‹ €E‹ ,+€e;ÞV¬"ÆI²ƒe»ÍÕ¬0ãjEñÞá`á`á`%ÀÁŠð< `¥°,H2X•É`¹/ZÎæ¼h¥)ƒe»V°¢\.êÌ,‹ Ve3XòÞøm¯¼§€)`á`Eã`e °Â~êÎ `…ýă`á`UÖÁ°¬Š¬ò\´Ò Xa÷Á¬VR÷U2Xd°²’Á°BJNÎß”n–mîïï×@$ÍïÇŸÃ,‹ƒ…ƒ•À’Ú˜&Ë9§q° â~ûí·ÔÌÙŠÙ[À’¾ýÞ;ç1‡ƒ…ƒ…ƒ`”¼çÎ;Xcccjrr2?=11¡=ÿ>̲d°È`e° -G+83b»OG X¶àD‹ ,+P«««ª««K=|øð`µµµ©üôúúºjiiñì#̲8X8XÖe,‹OÜVbKnãutt¨ÍÍM=í¬úúúC·Ýϳ,,2XÖe2X^ܲXd°È`‘ÁJ`9sFÝ»w/?í,çÿƒž ³¬Ég¹«··WÛ˜†´å±Ôé¹¹9533“?pÓ‹‹‹ùåmHYîæÍ›ù¿—Gg jmmíÿú+0>ˆ,wëÖ-ßõ“×2ë''j÷ë™iYÎô·´´ä»~ׯ_]¿BÛa–—“‡óõdž™¾víšîoeeų¯×—‹–ßö^¹r%ôþ`sq ÓŸ\Dü¶WË;×ß,g¦e¾ÔÅfL›þ¤/³¾³³³žõ“Gã¶J…jcëʆí¯P½Íþ"û×þ,ï¿ÿ~èõ“ãÀÙŸ¹ Ë´?¦?9®üÎr<šþd9¿þäø»~îzxퟦ9—wî¿·9Ù–éßo{eZΫ^çK÷ùMΗ¦?¹ûõ'Ëyí^Û{àükq>·=Þ°üÎG²?™åe,æüôþÛœ/ÃOÅü½ßúݹsÇw{åüõõ<‘€%äÕp°ŠÏ`Ù~"³u¦ªå`Ù~’!ƒUz+î#¹“ÁŠw«Ô}• ¬° «Hà2rÞ:4¹ªææfÏ¿ ³lÚ3X¬°,‹ V),‹ V¹2XV™k||\ÐùÍÀ¢–ÍR À"ƒ6ƒ`‘Á*Gî&K€E«² À*° mUì˜Y8X8X#¹3’;,2Xd°È`‘Á"ƒE À°p°p°p°p°p°p°,,‹ ,2X,2Xd°È`X€`‘Á"ƒE«hÀ*4Xeµ3X6ëG˰lkH‹ €`ÅÂÁ º¸á`e °Ü-I–íÏ:UËÁ²mAÁ ÀòÛÓâ`…Ù·Ê XAû*€E ÀòqÞ~ûm]Ód9óÿb.ni¬¤d°* XåÚ§ÓXÒä'£œÇ’»e°È`EN~ûÕo¼`X8Xqt°Š½í‚ƒ…ƒ`¬{ôƒw,+¬ƒe N€Uõ €õ,ërb3X•¬°¬,–ÔÀ"ƒU® €`á`á`á`á`á`X8X8X,2X,2XÑd°,2XÕß À°p°p°p°p°p°p°p°¬ÊKŠÑÙÙ©êêêT}}½êééQ«««ùsöj~?m»,,2X,2Xd°²˜Á²oŒ V «»»[år9µ¿¿¯ÛÔÔ”:~ü¸ç²êäÉ“¾€…ƒ…ƒÆÁ²½há`á`XÑ8XA·©ƒ–ÃÁа"\?¬„Ý"¬­­õ|~``@ÍÎÎXd°"É`Xd°È`U6ƒ•fÀJJ«€E+ÚÛÛÓ…‘[†nÝ¿_:uÊ÷o°̤{¼“ «,      +»€e2S><84ÿüùójnnΪ/¹•(€Õ××ç V¦9ÕÛÛ«ï›AK–õ™™ÉàÎéÅÅÅüò6;¼,wóæÍüßË£³¿……õé§ŸêådçrÏ7Ó²œéïÖ­[¾ýÉk™õ“µ_²œéoiiÉ·¿ëׯ—\O©…×|9y8_O–3Ó×®]Ó˯­­©Û·oû®ŸŒLmú“‹–ßöÊra×ßæâ¦?¹ˆøm¯<:—÷Û^y”ùR¯õõ{}çß›×5Ó+++ùåÅiöªŸ<šÒ_¡Ú˜þ¤î~Û+ïWØþ Õ[ê"Ó²ÿøÕïý÷ß½?Ëqàן?fy9®üê'Ç£éO–sÎ7 !Ór|;/ú…êâµ¾^û§<ÊùËoýdÚ¹œßöÊ´ßùÒ½¼ó|é·½2-Ëy/Ýç_9šþlÏ—ÎúHîz™ó¯íùÜöxÀò[?ÙŸÌò²?Úœ£^¿0Ç“Íòr~ðÛ^9¯˜åïܹ㻽²\Ô×óÄ;X»»»º8§OŸ>ð¼lh{{{¨¾$Ï%Áy,,,,,,,,2Xå£_|Qº £íímÕØØH À"ƒE‹ ,2Xd°²XgÏžUËËËÚqzôè‘.ŒÀŽÑÝ»w=3Yî`»óÛˆWCCCjdd ÀÂÁÂÁÂÁÂÁÂÁÂÁÊ`É BàHÂéGUƒƒƒœà$Ù™B€5==­ºººTMMjjjR£££Éú¡Å¸$Œƒí8XV<ÆÁ²Ù÷ã>–l»l—WsÖ%ëã`EXÅœ/£¬B¯v¬ }æêÕ«©,ÆÁb$÷ªVT-,¬J:XAÁrÞÎŽ£ƒe NI,¿ÚdÑÁ*&a¬ }«˜ó*€`•°È`‘Áªd+Í€åÅ-m€•Õ –-˜Xd°,,,      À°È`‘ÁŠS+Í€6ƒ•%Àªv+®€6ƒ•%À"ƒ`á`á`á`X8X8X8X8X€E‹ ,2Xd°È`‘Á°,,,,,,,, À"ƒE‹ ,‹ ,2X€…ƒ•iËfJ,¬²:Xû €`‘Á"ƒE+1€e»,2XåÊ`Ùîƒd°,   +s€…ƒ…ƒU¬ƒ`á`Xd°È`X©Ë`EXd°È`›Á°È`ۤª®®NÕ×׫žžµººšÿ1g¯æ¥ýý}544¤úûû5<84_Ü)¬¾¾>ß¿·yÎä³Ü€ÕÛÛ«wó)DK™.4Š9 eyÙ!Ä‘™››Ëfzqq1ßÿÍ›7õóÎùfzaaA­­­éådçrÏ7Ó²œéïÖ­[¾ýÉk™õ“߯?YÎô·´´äÛßõë×C×óý÷ß?ÐŸÔÆL_»v-¿¼Í¸3+++êöíÛ¾ë'.éON~ÛkÜœ0ûƒÍÅ-Lrq®Ÿ©‹™v.ï·½ò(ó¥.²|Pòz6ýI_f}åÓªWýäѸ­ÒŸ,ç×ß;wòýIÝýÖOÞ/ÓŸ,絿ȣ¼ÿ¶ûŸl‹LË~æ·~²šåeÿöÛ^¹@šþe9¿þäø1ýÉqåן¦?YÎïü!Ç·ó|´†Ùÿ¤ÉyÉoýdÚ,/Ëùm¯L;ûêOú)å|)˹ϗ¦?Ûóe!01ç_çùÒ½~¶çKçù×8ö^ë'û“ßùÒïüks¾,õúçu<Ùžåüà·½r¾0ýËùÁ¯?Y.Êõ—ÇÄ;X»»»º8§OŸö ²K> VàÁèú¤@«4Ëö–{€ƒE‹ Vz3XÕj8X1‘Dmoo«ÆÆFÏyjssó@«¹¹9S€•¶ V”€E+8ƒ`‘Á"ƒ• V5Z˜ó/€¡Îž=«–——µãôèÑ#]÷7 ®d†‘‘Ï[€ãããú›ƒÎo d °²šÁ²,2Xd°È`‘Á"ƒUF«J’¢ HIýèÑ£zh)Ñôô´êêêR555ª©©IŽŽúf¬â6€…ƒÅHîd°È`‘Á°È`‘Á"ƒE À°p°p°p°p°p°p°p°,‹ ,2Xd°È`‘Á"ƒ`X8X8XÑV¡qgp°p°Ê XQíƒ8X8X8X,+,÷‰À¹ÅœÈ`‘Á XåÞãXQìƒd°È`X8XV̬8V6¬(÷Á$:X•,,,‹ ,‹ VÆ2XQîƒIÌ`U°È`‘Á°p°ªXò»S²]^ÍüÖ_V,›ºà`•X…öÁ¬:XW¯^-¸â`%ÛÁ²=ÿÚ¶ }Fæá`Xd°*XÅž0ÒžÁ º-Gë­Hó/Qì«I,Û‹€•Ü –í>u¶ À°p°R XIp°ªXYu° íƒYu°l.Z8XÉv°ªX8X¬V2XÕ¬¬f° íƒYÍ`Ù\´È`%;ƒU À"ƒ`á`á`á`X8X8X8X8Xé,)Fgg§ª««Sõõõª§§G­®®êySSSúÇže^CCƒþ!çõõuÏ~䇟½ZUËrp?2X–E,2Xd°È`‘Á"ƒ•ÀêîîV¹\Níïïë&PuüøñCóöööÔ¥K—T{{»/`ÅÉÁªÆ #íV)-,{Ëv_­`Ùî«8Xéw°*¹â`á`¥âammmèyVú3X¥\´È`Ùg°,2XIÉ`Å °È`‘ÁŠ5`‰K%…‘[†^ZZZÒ®–` |I;öx§V;;;8XVvËd¦|}úÀó£££ÚaÚÚÚ²îKr[ŽÇÁÂÁÂÁÂÁÂÁÂÁÂÁÂÁ"ƒõXN0’7F‚îaµ½½­É`Xd°È`‘Á"ƒE‹ VöëìÙ³jyyY;N=Ò…ØÍÏϺVÎ`»ó‡WCCCjdd ÀÂÁÂÁÂÁÂÁÂÁÂÁÊ`IÑŽ$œ~ôèQ588¨Éfl+çÿ§§§õ˜Y555ª©©IßVÌÚ·Ó>Vµ.ZY Àb¬¤Œƒ'Àb,ÆÁb$÷˜V)' ¬l:XÅ^´p°p°p°Êë`{¾ÄÁ°¬˜¬lf°J,2Xd°È`•/ƒ•dÀ’m—e½ÚÛo¿M ÀÂÁ°p°p°p°p°p°ª¹¯â`X‰,2XÙÌ`•Xd°È`‘Á*_ À"ƒ`á`á`á`X8X8X8X8X€E‹ ,2Xd°È`%°È`X8X8X8X€`‘Á"ƒE‹ ,2Xd°,   +p°p°p°p°,‹ ,2Xï«d°È`‘Á"ƒ`X8X8X8X8X8X8X8X€E‹ ,öU2Xd°È`Xe•£³³SÕÕÕ©úúzÕÓÓ£VWWõ¼ýý}544¤úûû5IÛÚÚòì'̲8X8XVª«»»[år9 HÒ¦¦¦ÔñãÇõ¼±±1599™_vbbB zöfY2Xd°,2Xd°È`‘Á"ƒ•¹[„µµµú±­­MmlläŸ___W---žfY,,     +3€µ··§ #· ErËÐ}ÐýœQ˜eÉ`‘k°È`‘Á"ƒE‹ V&ëÉ'ŸÔMÀçÁƒùç¼–óû{›çL>Ë X]]]êÅ_T½½½¶ä±Ôiyý™æµ¼4ÓŸ<çןÙÒÜŸì[^ý¹Ï²\ÜÎGqïOjÔ8_FÓ_9ÎQ\Ï…í`íîîjú<}útÙ,„B¡r(¶,ùF¡¨££CmnnÈU577{þM˜eB!„R XgÏžUËËËÚqzô葾w*–œh||\ÐHþ?00ŸvÞ,´,B!„PfK‚o2Tƒ|sðèÑ£zh…íím=¯ÐØVNÀ ;–[ÎlF£Ñh4š_n;€…¢SØ€Ú êB]¨ u¡6)u°;1µ¡.Ô…ÚPj`!„B! !„BÀB!„°B¡ÿ'¿_Ü@Ô)+u°b¼sy5›åÓ*ù5ö§žzÊw¾ ÷QhûÓV§©©)ýó 20oCCƒ¢DØ S·4Ö&¨.ò(?!¿ò MÆá»ÿ~¦­BÇN˜sQ’/AÇŠÍq”Ö:ym{˜ã(+û€•Pmll¨“'OföÓP¡S~©]~{ÊvûÓR'?.—Ëé1àä‡Ò/]º¤ÚÛÛK‚ƒ4Ô&¨._ûÚ×ôËÊ­Ê óZ[[ÕÝ»wutkuuU»>, °ÒV'YßRNVi­©‹¸ZrA(T³,ÔÅöØ šwñâEÕ××§¾õ­oÜYþæµ×^Óµ‡C~ã³Ï>Kä9Æö8JsÜÛæ8ÊÒþ`%Lb»ž:uªè“ûᆱšššòóðƒhGL$ÐfÛwr9¿AYŠƒ•†:---i÷&*ÀJKmœu‘Û‡rRÿÉO~¢>ùäuåÊõï|'su sìøÍS¯¾új~úwÞÉ×RþFœCùU¹ËO™%á§ËÊXi¨“{ÛÂGYÚ¬„éüùójnn®¤“CMMç|Ùqͼ$Ö™3gÔ½{÷BƒDë$ûˆ¸&î V€•äÚ¸ë"ƒÓ§Oë¹|:–‰Í'ã´Õ%̱㗡innÎÿ¬™Ùf“Õq÷·»»«Ž9’zÀJkÜëæ8ÊÒþ`%H.tfjÜ;«ßA Ö¿{½jžú›$VЗ²T§ÑÑQm¡ûýöfPÝÒ\¯ºˆkã¼u799©o“emŸ sìømG¡>ܲ¹›$ÀÊRÜëæ8ÊÒþ`%Hò©@noØÊ|b~öÙgµ…ûàÁý© ­€U춤©NR–@wuISmüêâu’¶9qgùØò›÷Ì3ÏÊáøý8¥iw°ÒZ'÷z†9޲´ÿX ‘|:èìì´^^ò æ[b³Ê'mÙyåb`¥³Nóóó¾®U1ëž–ÚÕE¾N.·7D²mÆåâØ ’Á™˜˜ÐßÔ‰ãîÌÐ|ôÑGúÿ2_–É$`¥¡Nîm seiÿ°" åÞ¾}»àrr—OòµÙ;wîèç$`+÷«¥I0éÛqT‚¶%u*4^šmÝÒV› ºxÉ·•ä[PÒä„î—ɱU*XH $K#uzî¹ç4|ŠÄñëééÑ5–:¾òÊ+ù iÒÎ1Qœ’Z§(Ž£,ì?B!„€…B!`!„BX”!„BÀB!„°B!„,„B!`!„BX!„BB¥KA£r#„€…B>ðô³9BÀB¡a !„,„*3`y¹YKKKªµµUÿøl{{»úä“OÔ{ï½§ÚÚÚôs2Ïü ­èwÞQ§NÒóÔK/½¤>|HѰBÀr–èç?ÿ¹žnnn>ôÜéÓ§õô‚ž~á…Ôþþ¾šššÒÓ! !„,¯eüž·JÔÙÙ©§£%%Óâd!„,„°B–y®®®.0D°BÀ X555úÿâ\!„€…°"¬ŽŽýÿååeŠŒ°BV€5??¯ÿ/ß,”oŠ>üðC ^! !„¬"K”ËåTWW—ÎcÉ-æ¦&uùòeŠŽ€…B!„,„B! !„BÀB!„BB!„€…B!`!„B! !„BÀB!„°B!„€…B!`!„âuâxâ‰Ì5„°Be,¶!„,„ÀÁö"„,„ÀÁö"„,„ÀÁö"„€…8Ø^„€…8Ø^„€…J;p|ó›ßômn}öÙg꥗^R ª®®N>}Z‡~ý'Ÿ|2ÒíùøãÕSO=`!„,„P|K;v¨yÖóÏ?¯ÆÆÆÔÞÞžÚßßW|ð~®šX3 ÀBX¡ÄVmm­+?]¼xQõõõ©o}ë[úï·¶¶òôðáCõì³Ïæ§ ýÍ7¯Æ1í”É|аBB(q€ÕÙÙ©~ðƒ¨åååC %ÎÖ«¯¾šŸ~çwÔw¾ó<ü¼÷Þ{‡`(èoŽ=ªVWWõë\¾|ÀBX¡t–¸K@GŽÑnVOOºÿ¾ž×Üܬ¶··óË ™\”~ÌtÐß<óÌ3êç?ÿ¹õ6X! !”HÀrJÜ¥K—.©ÖÖÖ<àxµ À ú›?üP={V=ýôÓê'?ù €…°BÉ,ÛozI2R"qœüòY~€ô7FŸ|ò‰ª¯¯°BB(9€FòA“¿’&ª^xAÏ“°úÄÄ„þ†¡H†Npf°¼`(èo$›%ÁxyNÂîBÀB¥°^ýut—ü•8Wýýýùoý‰¸$W%óŸ{î9}1°‚þæÛßþ¶~ q¹Þ}÷Ý@°òºÅ`!„,„P"‹íEX!pX! !p°½! !p°½! !„,„°Beެ5„°B!„ª¨ÿHeé}ÈñIEND®B`‚ClusteredXYBarRendererSample.png000066400000000000000000000133171463604235500360040ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/xy/doc-files‰PNG  IHDR,ÈRßÜU–IDATxÚí{pTešÆ»Ê²,ÿpœŽî¬eY‹ã¬³®eÔÒµX/ÙlX2"¤¸H aÀ¤Aˆ `@]z‚(d"—L $„@b¹ ‹Î %˜¬R€“L.:qÉóî÷ì®îÐétH§où=ÔSô×çtç¼}Î÷ôsžóõùlQÁ €`‚@p›Í‹ÁaÂ=÷ÜãÕ7lØpÙ:^ëÜ}÷ÝýJ<, D.\èÕSSS/['%%Åk}M¸ÁV?Æ—_~éÕ¯¾újioow/×Çúœç:úšþ$‚"÷ÝwŸW‡Ü²e‹{Yii©×2=…ìo`!X ‚ðÆoxuÈI“&¹—écÏe¹¹¹^¯=qâ„Lž¬@·¯'íS§NõýÂݦžÈmŸ?>àõcò*¡º¨Áƒ‡Üa…*à …Ã"ó¡2¬¡µµURRR¬ÇéééÒØØè•a¥¥¥‘a‘ùPVx™™)‡¶Ü“Š•cX½zµµ¬°°Ðº2èy•pÙ²edX’a…eeeÖ}reäÈ‘’ŸŸïu kã°pXÔƒÃb¤;™õañ[BŽ„zpXýö·„dX ÁÂaáH¨‡E†E†EæC=dX8,V¯ê9¼fÔ=ÿ¼Ô™ý¢¦® @ê̾ªK7œc¸Àp©á Ãß–î1ÿÚ(Ž=âØ¿ZWˆã˜CŸü^[7‹cýDz­¼:f2¬®‹ ÁÂa…Áa}<(Qn¿]ŒPuêä7þEl¿þBlîÛè"±M_!¶ù ÄövºØ>HÛîGÅöù?‰íÛ¿»ÔÁ÷ º$Á{û ‡…`‘aE¯`‘a‘a!X8, ‡E†Õ2¬¾¬…¹»­»èºë'‚…Ê8Áš4yõewLÀaá°,2¬¨,2,2, ‡…ÃÂaá°È°È°‚-XdXdX ‡…ÃÂa‘a‘a‘a‘a!X8, ‡…`‘a‘a‘a‘aá°| Ö€µ2~­ÔÖÖšX+ññMÿðÿHüãÿ-ñã«%>½\â_,”øÜw%þ\‰ßø¢ÄoK—ø½OJüña_÷ˆÄ7ÅK|Õ3ÿú»25«‡…ÃÂa‘aõ`éË_y%â£$±=¿T’§$Ã"Ã"ÃÂaE¯`á°pX8,2¬¨,2,2,2,VD Ö£*¬‰?]\·n ‡…Ã"ÊLÁж5àÛ“aõŸ Ë鬑ŸýLdñâ^î›ÿú±ýg†ØR?IÏ'ÃÂa_°pX8¬P ‹ «×‚E†E†*Á"ÃÂaá°pX8¬h¬ŽŽÓò$''ǽӛššÈ°È°È°Â X )¥^õèLIdXذaƒÑ‰"wÛétZWÂpX8,VdËS§N•††w»¾¾^ÆŽK†E†E†!‚E†å!C†\vŠØù9—P¹è‰¬¬,ëu} èÿ]µ]ô\þ—‰¥=#CÚÚÚä‹‹åo¦Ý'ÍíÒ|¿a²]ZÆ›öLóøUÃ<Ó.4í óø áiÓn1íJóø5ÃÇì×"óç7Kss³ÙÞf«mÿ‡Z±ß{TìI;%.µXìSßûÜ%bÏ'qïMû‡©bß9XìGºÛÄÞlû¶ÇÅþÂRI[*K—.µ:ƒþ¿iÓ&9uê”ÏzÛn¾YÚßxãR=¯¿.Ív³}ÍöÝk¶-Ép´i?kÚsÍã\ÃU¦½É´wšÇG kª§Ì<~Á.åÿþ„Ü{o‹üîw—êÉÎþîR=¿ûý‡Äž\!qãÿ ö™ËÅþêkbÏ›%q…Oн"YìûéÛ$μŸ}g’U«}T±ÌÌ|ß«žC‡ù¬ç‹×^“¶‡–öµk­zþ¶pá¥zn‹sï›æq¦=+νoš7šveœ{ß4›Ï±åcÓ~Ý<e— w8%#£Å½on¸ÁÔ3°Î½oì£7Iܳ«ÝûƾêY‰Û”æÞ7öÚ—êÙñ±¿’#†•Ë›o¾é®g¹WÝ~_õؼYþ¸oŸœ9sFþxà€T¯_/G¶ËÉ]•rd[±|òáûr¤Ò´kL{—i—›vµi7íÓÞeÚG~jï,–Í+?4_&ÌÉÿÉ… dÑ¢V±ÛÍñv[­{ߨÇJܬ·Ýûƾq¼ÄUþƽoô8‹Û“(ö·f‹}üF6®Ä«žbÓ'Ο?Pÿê‚•Ðs}•aA*6ÅNj̚Õ{÷û±áË6iJüyÄýއÕÇV}L=‘QO´ –ÒÓÓ¥±±Ñ+ÃJKK Y†Õ~ßE=‘QO´ – ­+ƒžW —-[†Ã‘à°pXŒÃ‚ ‹ +jGºãH¨‡…Êšß’ùP GB=8,‹ ’a‘aá°p$ÔƒÃÂa‘a‘ùP‚…ÃÂaá°pXdX’a‘aá°p$8,‚E†E=dXdX8, õà°pXdX ‹ ÁÂaQ ‡E†EæC=Á¨ç›Ñ£¥);ÛºIÓ²eòÍ/)µw Ú‡ ‡Ž3œn8Ïp±á à †[ ?1,rñâEY¿~½L›6-*ké\Ry„ Q[ úà—^z)àíŒTÁ åë¬^~˜žë¦¦¦JRR’Œ1BvïÞí^®ß¢'N´–M™2ENŸ>òšôoÇJ-±P:DuÁ?üðà V¤Ô Á v-V×=pà€Œ9Ò½|íÚµÒÐÐ`µ«ªªä™gž i=Gµ\J,Ô¢Ž±¤¤Ä:EŒÖzôþëéééÒØØØ£ã-ëÑ¿©‚¢TáYµj•´µµ…µë ÖMLLô¹\Oi\ËBêêjëÛÉ_†-µ¸N×uÐaKKKÔÖ3{ölùꫯz|¼EúþQqQÁÊÎÎk-ý^°üe[®çÔâk¸­ß2 »ž÷÷š¾F~~¾u3BýFöZ\hoo·Bø™3gFm=þ‚êhß?*,zq$œµà°üÀ¥úcÆŒ±Â`ýæ×îƒH] ª{‚H­Å\"ê ôoFC=­­­’’’ÖZ¬. çÜ *t(„~[¸®b…ó ª©©ñrUÑ\‹bΜ9rüøqk{.\¸`eXꣵž+ù›‘Zç\«¼¼éµ:$‚…`ž`=hóÍN˜;w®uó3QFïÜxâÄ ë¹p¢§s<Ú<à“‘^k s@"XVì ÖH›ov‚ÞÅQ;LWxÿý÷­™L-ZdÍ8ãº}²Š‰Î‹§÷Õîì:ºzÍþýû­ ÔQôfv”Ëj¹É'£©VÏmD°¬þ'X3l¾Ù :GŸÎáæºï¹'Ô¸Û{÷î•%K–¸;­NVÑY\ü½FoQ캷Î^4Áš±Ü'£©VEWs@"XVì Ö"›ov‚:ídz_lývŸ7ožœ={ÖZ–––æuÿsí|®\É×tfݽfÔ¨Qòé§Ÿ=+²-zÑ'£©Vs@"XVì V¾Í7ý@æ(Úq:gI¾æÀóÕ‰ý½æóÏ?·f±ÑiÅ·oß<ÁÊŸä“ÑR«¯9 ,« V…Í7€kÞ>u ]e>]ub¯qáÛo¿µ&¾ š`Uü‡OFC­Ì‰`!X±/XÇm¾Ù z•Ì•é(5—qÍÛ§²Óé´®`)t¨g®ã«û{æ=^ësHM°Žßå“‘^k s@"XVì Vs‚ovBii©Fk¦£nCÇyv"íÔšÕèò3fX§Rþ:±¿×,^¼ØúêLtBLBȼˆîõ›‡ûd¤×è‚û‚Ø·Áâ ì[ pPö-‚ÅA Ø·Áâ ì[ \ùA ™„ X€~‚ÿGã;€áûÓIEND®B`‚DeviationRendererSample.png000066400000000000000000000613661463604235500350750ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/xy/doc-files‰PNG  IHDRXr5˜b½IDATxÚí½Œ÷}ÿ_©ªªªªªþSUUUU•ªJí·ê•ªªª*„@ ¬(m#8ðÁņ೉!Ž'NbHœ8‰}ø('1†àû\ qŠãØ&ìîíqÜwÜîøq?øaóëÎw>l0˜ó½¿ózïÍ2;;3;³;³»³÷xI/ÝÍþœçÌìÌsž¯çûõþEAA„¿Ã& ‚ ‚€`AA@°‚ ‚ XAA‹ ˆ?YýÎïä$A‹ <.”’¿ÿû¿¯þøÿX-X°@mÞ¼Y]¹r¥¦/â•$ NÛ_òw÷wÕüÁ¨ø‡P=ö˜ºví‹ßSìâ?ÿó?9NÁÁ-ÿèþH555Åú"îõYÕH°ìù§ú§êôéÓ,~O±!‘õõõ'‹à‚à'ã|Q¨Ö}í?oÞ<¶'¿§Xo‚€`³ö‚`†¨%ŸùÌgržû“?ù566Á*Ãzݾ}[=÷Üsy¥&¶'¿'A@°ˆ ÿöoÿ–ó¼xH¬ÑÛÛ«xàõ—ù—ê÷~ï÷´oèïÿþïÕ׿þuõá‡f_÷ÏÿüÏ9ŸóüóÏgŸ“ÿ­ÏýÓ?ý“纭Y³Fýû¿ÿ».µÈw ñø‹¿ø µtéRÕÖÖø„ïµ þ÷ÿW_Íï’¿²,ú¹x>øàƒêÿðunذ!ÐöÿôÓOsž“mk¿Û¿Øõ“xê©§Ô_ÿõ_ëÏ—ï‘c ÐqSÊz nùοú«¿Ò^´(?3®¿'û÷!—Ï2÷Óßþíߪ]»ve£üàú1yN^#äÝAŽuÁ"ˆ/rrµ>/3~úÓŸê‹•ÛÉT. ãããúµv5F ¿fÜsÏ=9Ï™'·u+Tzyýõ×C!X7nô|¯<ïõ=r1·?f¿ º}÷ää¤zôÑGsž›;wnÎ{ƒlÿb×oñâžÊ]a®—„Dõ™qþ=9=çôžgžyFýã?þ£ãs/¼ðBÑÇ:J'Á"ˆO€rg줢tuuå€åNyÛ¶mŽ'fg½xÈÿr±Ï—»eóqùß,›¸­›Œ¬%ß'ï—QvÖ×É%N§çìBQzäûä¯õñ7ÞxÃ7ñ3/„~ÉŸ}»wttdßtû³~û÷ïÏ#²oÄÀì¶=£X¯(>3î¿§ ^¯Bûº˜c‚E@°"„ õy!@Ë—/Ï+QHØËZRŽ0ÃîA‘;v1úZ“×=9Û¿Ó\ÇR–}xyww·~üäÉ“¾×WÞÓÜÜì¹n~.„² [ZZrÞWÌöº~Ÿýìgsž{ë­· nÏ0Ökß¾}9å°(>3Èo"9(×ïÉþ=t$Å^Ï™ž¾bŽuÁ"ˆï¸Å—!!]?äÀzÁ¶Ÿàån¿°Èk ­›((â§‘ ƒ”¹D(„#(ÁœvÓÏÜnŸc}¡ïvê‡500‡§˜ítýܶ×ö k½¢ÀZìo"L‚ÖïÉí{¼ö§ÛsÅë,‚E%ž¥,àäòòЏ}æ;wtÃEëź,™Bq1±[ßã÷b”`YË–^¯÷sQòÝR6ƒ³½”óñÇ缯˜íôqûwøÙža®W”ŸY)‚Öï©Ø¾naë,‚E%žíæ`1Í:Ýõ yò=ôë…cݺu×Í>ñßø†ºqãFÕ*XÅ|w2™Ì{\F–Y£˜ítý¤„ô"æzEù™qÿ=…I°P°A”ñ‚ }{>÷¹Ïå<÷gögY‹ý¹—_~9ï³…øÌ™3'ç1»çÇšVwлí *H¡ï±ûRdø¼„”ë‚zÆŠ¹ qtò™QÌöº~ÿò/ÿ’󸵓¼Û{Â\¯(?3î¿§0 V1Ç:‹€`DÀ W¾ùæ›Ù÷I©Î>•Kcc£¾ó–;ßÇg‡ŽÛCŒºöÏ–‘~NÎv¯Š\¸œH›=þüÏÿ<çy«iÜé½öRŽŒ¨’2аTµá_ÿõ_ó“ä³ýƒ®Ÿ½ß•ô“ÏÓ´Û{Â\¯(?3î¿§0 V1ÇzÛ€ XĬ'XBjì½¥$öîÝ›WFòs"ujTi–J ] ì-ü~§[Ÿ¯ R©}°J%X.\È#”V?VÐíôqQWì~0?Û;¬õŠú3ãü{ “`s¬C°Aq2”2œ\¤$  ½¦ó8wîœ&=â’Ñ|RŠ“‹„(R¢x8uV—÷ØGÊÉÈ@?Kîè¥ã¸y!’2‹ýÜéä-ï“õ”×›ÿæoþ¦àI>[F;Z»[˲ŸNî¥, { »+Èö/fýÞ}÷]×ÜÞ2jóÈ‘#×;Œõ*ÇgÆõ÷6Á z¬C°AA‹ ‚ ‚€`AA@°‚ ‚ XAA‹ ‚ ‚EAÁ"‚ ‚ XAA,‚ ‚ AAÁ"‚ ‚€`AA@°ª3¾õ­oå,éK_R<ð@ìó¡‡8Àp€æúõë!X~ ÖO<¡Ž9ûÄÃt4’U,1¨X?(ë5¹B18P°ð`‘$™×PóT‘ÝÅuñË…~˘'šÇGðÀ$, 8ÀQNý%NÝ"*R)M8ƒàh‹ÐC…Òp `áÁÂóp„‚£/¤yñ†|– “3- Ä3Õiü•þS£§N©ž•øºšÍ63„êøÌëNWù=nËã==±^sYpÄyýÙì(—Ëqý˜U –,ûsóçÏǃpèÕëC•?y 8Àõ”·Šd!)“ßý®:W…ÞI)J9ÐJ¨n×Õ©›_ýjÕ,þò—}«^Qï1±ßظ1gý>]¾\½¿}»:ŸH8®¥H ,¼Kà=û""'QzL䎸\ÿù²@!tNJSP‚ïfú¾õÈ#êêž=9ÛØN>Ü âZ5r(/:eª¯VÐŒmïõ=²]kUÐL!jå<®¦Œó  å%÷ºŠ™WÆ~‘1ïä.*…LÁ(XþÊ‚VÂND)׺_ýÙÏ ^ȳþžçŸŒŠç*Om²¬—_‚ïfúöÚ¦n$ËîYß“Ý<îE~ŠU}MdA„ôûMŽ-ëöŒ²Ôi?®Þ3®ñ,SÙçaWôÁBÁBù™8º»º´Òöç”I¹ Kù)¶?ŽßC,¹Ü~øa׋—bç‡\t­¥@ù® $ ÌžFy¤æ¥—|áp#€Å– ó0(:m¿J\EŽØZ=8ù²¤é©Ý{öÑOh²åïÃs`ƒ… I©¶X¾y\Ñ Þ¥Å‘˜i™pÒȉ¯ÌˆAˆd^À³FöË{‰¡\Ópè2•4ÅtQ¡œ.èN¯û=ÛÙ>EK)ŠÓ÷Ú/>a,_1›?FÈbPx²¡[sO{y²Øã¦’Í3ÃòeY·KµÍýiÿ}Ëù¨XbŒË'Ѳƚ5kÔØØXvyttT-[¶,»|ñâEµ~ýzuóæM,p”-¥Ü7äB~üÞŠa½ÝÈ„ÃçwDD®äâ.'[§îÓVC±àðE"îoï×ãtÂ-†¹*&.¹½.è¢Þí|íÇlU ¯º”†JýÞ0Gº^ KQ!&~iñ "?Çfæx9ªÎ§{Œ¼Ab(¾õÜsyo!_y8ãóì½zÂîÈíuW[ !ršä×Íó4ƒt¾.ÆÅ÷†1ª³ÐèŨ–V ÒcÉ Òòü¥¶ ɹÔvJ]ëÔ­ËïЄ(è÷ ûóã=ýÆçö»|߀ñüɪ8ÞG#û¾Ëí]ZÙ“ít­ó¬Þ>c»›òÈUÐÏ/ÇõcV)XVß–›‡ V6—Á™ùÕVJ‘‹¤.™ÙJN†âÀÓe”±¯–Ÿaü^ ‡ët->;sK L”;m±a£c)Ì…tMFÐ(ÒWãÈÆFÇ ÅŽ^ ÛC—Ù&íŽÊU¸jV‹.;º©d¹Ùk¼¾µº}\éãFÊz†Y:,¼n<‘ñÍM~ïÙ¢ö¬"–µhz°–,Yâë½x°À©:n¤”ðüÚ«itQ1«¡Ø>â¨:HÖV!Z¡ÆwìR’âwióÜVBäj@/”í·Síéϧ¹ú…ÂjY¾ßG«& ~ –I€Î§Û}†Ëí½>I•ÈõjâWs\æn³RJ¨¥ì‡bJªx°Š XûöíÓ#­£ëëëC!Xx°Àá–©:S¤ZU-ýqìMú‚zt&[[«Ž\IéB.‚“ß.Óºá[ßQï½Õšsr>×Ü«¹«å¸åÂ$RÂqRZ yÄüŒ^,ÏþHë‹tPòS˜hµÌ×{uù¯øÏï›1¿·”ý÷ã¾?œ·YæxhËÛ¾þÖýhŽ-è>pZ_\!eªÞ¥r(WÑâh«¨W&SF9ZU%ÀBäÈG5¨&iM—ù¸j©¹*‡ Á  V™p T°ìµò£ˆ–¡,XŽ£3æéÞ¼¡ýá4 |=ˆ¼·WoM( w=Bmžd+œßGyËË(X(X,ÿyu}ófO³ùõo» —ÊK¹òò˜È¨Ã›_ýjÞçÉÜ<󌺺gºµn]ÞóÚÐ^æÖh×™Hú,N¨´Aª„\Ys }ªê<&ÿZ¯ëDÒQšÚgƒçÇ[)ô7Õ Þ%pàÁBÁBÁЇ_ßUžúd§×IÙ®råw””Œô3ñvÝÃêÊ_×ÌmoóéYÒ3Ëþ=Ÿ._¡ÆwìRSµÑïç\ËIÕÑ<èI®Ì_V|°‰Âu,rrE’$¬’B$¾E‹å<6==­Ô–-[4A’œ˜˜ÐÏ8p@­_¿^ÍŸ?_-\¸P¿fttk–àðÓ’Á«‚˜Î/íߟõGYGû‰¹ü¼AàÊÕ]FÊHD!x}s‹zï­Öš¹³jéW­3~+¿Ùb¹°w虑‡­MU;PÀ¬ÊÆœ9s²i½{÷ª¦¦¦ìrcc£ÚjÜõKlذAu3!aSSSjÏž=jíÚµx°fŽ®"È•”öddžU1#ù{æ˜ÏÅx~ñ7¿©È~’Q—œLæax3ÄÌÞâà·*”âÑ ktay=&æt1ÇCW®ðü€x°J&ZÖX³fË.‹BµlÙ2×÷Ï›7«ÆqM$ÔHQƒöùú¬¾)!TŸ®Xá®j½öZI¥¿Ëí'fÚtÎøINøö_ÎRÒHºO›Ê»ÒƒYÃy{ói9ézg{ÊxýQãµé!u2}Z+S^~)Q¡Ò‰•5ÛÒÃêp\–Ñ~W:Ψ‹'.¨ÎŽË_åòPÛPIø½öGœ–Gœ×ŸýÁþˆrÙÏõ£ÔåY©`íÚµK«Q¦ùVmâè.ä»2ž·v>ŸZu¿6‹{Mâ+=±äµ…zce:pwy4G<–V^ÈÓ`Ÿ²&ê9úÄËTŒ¢Ôo±0T¨RSÊŒÃ%ø±ðÊ€x°P°V]]Ïñ`-Y²$»,¬RŒîôÁªmÇüø®žy&·Ü×øKKgò£%”þŽæLÕ‘éäÝ–C®¬å=?ž†»$«=ºNºOõ¤Ï¸– ¥©$UCŠK|cxeÀpàÁ ‰`íÛ·O´Ž"¬¯¯×ÿ·¶¶T­ð`ÍŽ~W2¹²µq§½óyñÓ›´¹6<Ì4Hl/º!bf¤Y4¾«SéÕš©‚FŠ ÿ<^’$ñ`ߦÁÚ®Á«–Ó{œüY(XñÅ‘0òtåJ¦Ÿ‘©f²ÝÏ~ıAg†¥#ŸÚ¤’w„Å–«]ÁÊ®S:8ÉBiè˸€b,:¹3á,ö`ɈÁS>&q¶ú®¦ïý‚ºt(é¡ðA²Òúuqó4ô•PtJ1˜W£’%ÒIù;?3Ï¡ «g«Z=&Òl y@·¼ˆò¸’ã‘y.]yÜx—À‹¹Q°*ˆCz] ú Wö¹ßáe¥¹OVKŽß*.w„…æø«ËÚ¾Á$YBª¤UÄQ‡nñÒF¢·ã]Ýó«ÜåÅÁt¿îfõŽÉ:›ëk%ç|(LÅW‚Ûº=¤£~X \Q~À‚ÁƒU…Ùf(ɤÃsbh÷Q³Ïxã‰ÍêBsO õ–N®Le¢ÜÊU-y­|ß›‡I•[JGy™LÚÁrt¢ù¬~¤øÙ„œˆLÈÝH}Üã°OÌÏr)6ñ‘.únЦ(gøvH£Ës¢Q›z bägÞD!J§T'Q«ŠõÀ+й>ˆÒ ÇÝÑåb9FÍ&±' â)ØO—Á«…ò,åÿcI–|G¿ÏÒ_¡üðÉ'sLíc»›‚£ô°l)þ]&<.E¥‹ßH #¹„ †y1'«/Ó3åÇóÍ@ž´BåÊ0G /â'ŠÛPK?^" Vå¼KvÂ1dž¡ÎNG³x!“yŸñyÒÀSþ&]|:ä B²„\ ø$W…,ûˆÁžÛéŸ\µÜ-ßÈE¨É"%žI1FKéÆmÂã Š‰t%/F!uB¼Cµ¢ü€£°’d*<²ïÝ”!ÝrŒFÑ#,ru<=äkÈ€r]åà·E  «†=X­SňwIˆÒqƒÈú ñ@9‘¦!#ZH“£3md”Ÿ’u2€rååÁ*eÄ +»'å¸K/¥0¼M~ˆ‘¾$«'`YðrûqÇ& öƒ·×mð=bð¬‡ñ¸ÏáîU+—b"¥ ièUŠåâDú,Ê8*†c „Nål?8DeŒ°dhUÔ²7\!(?¢LK !¦2¸%=3bµšI kV{°úgÈÑõÍ›5Á’Œ¤ó"+¢öIªœÊŽA^/¤®ÙÃïjšî™Ö ]ÙUy#ïûoõÞ›-¾§)ÔÙÜ:,½·Bý¤„JÈzb%eŸ4ž$²âž°sEù£Â¸Y‘°É‰[9µ½„ ÅEM´“6¿£CÍ÷¤Oá¥Âƒ…‚U ßÕø¶m9þ#QtÞþy=ªÎ×軈SÔ¬®™åñD¢rÕ‘w'u>uBÝzdÃ݃K—©+~íÛÐf6x ÓÃR¬Ò Æb!Z]éA”ùÆQ°Ê”Ò,ˆéý”G™=(!.aõ“;Y€ôÉ(G¿ß%ç+9wtÙºòõ—É y®ê¯ÉBÁ‚`ÍJVÛLGóËMM¹XšC°²å²º:õá¯)yºþíoëïÍš5âC»øÆ–åcú.Õî¸õåÇr°Žî=à»GÕ±£©Ò1ôüüö·çgµwé»ßÔ‡Å7¿ù¡zûí xÉÊ4=‘ß>\^77ÅàÏ;UbTQ«ýÞèR²¤œ8Øsµär«”Ó>ÛtàÁƒ…‚Vï©Sû»¿ú•šZ¹2‡hܹÿþ<¢õ¡±ãß=t(tre¹7±uk÷´ê)g¼úXM~÷»ú³>2ÞÈvP7襤ièÍMO䎬ßáÛPê%ÕׂÒðôÓèͲzõõøã7ÔÖ­ê•W®¨wÞ¹PЉy”ç6n¼‘sø/_þ©zþù÷U2‰‚uºøjÇP*1À Q ÚBBÖ;ÈDè‚á|Q‘Åâ0¹z•P{Ë8š²KT>)ß–£ó? ¬P³w¦,(†öÿ‘A´.þæ7š¤L<ûlŽñ;SB[ª Ñ9ÿÓ…wÞQ—öï×åFñq}²v­~ÏäSO9¾çÊ+¯äŒÜ3óÚË/»¨£3ªG’%ßkݺ¼užØöãìˆÀ˯ÖÙ­¯¹þÝgJ2ÕÖR>ûì„“™Í¨IW”)ëúío_ׇœ¨Ÿý,W!øå/ßSk×~⊽®î¶zõÕËø¦"îÑ5èÑé½³LåmQ}ä·®û…yx´„ˆ»Néü‘Æç òŨq~ÎS&É2#eD!‰2॒^-ÙÞ2 À$«é€£¡ñ`¡`U´Kû)³¼fž›_ùJ‘RaN™Í [¢\ÙI²+êò«¯ª÷·oׯ1É”ÛÕéã/Y ó³E ³+gÙ4¾O”µ\•ΙËONLÖÉ”åõwV¯výþO¾ø úðÙíÚÄníÒ>¶ë•¢º›ÿàèÍòõ¯T3JC!rå–²-ª]1ùÑÆÔÊ•Syë~ÿýwÔ3Ï| ¶o_+Uæã÷Þ;­Ë„ëÖÝÊ{4¬h;×›m„ÀˆIT¿>ưqÈúÈäÚö)~ ͸à'…Y[;XûЕc¤‹TÃV°dj#·9:uçÿ* Y(Xx°\³ÅÖÔóã |ªFÍê÷ßÎS†ŠI)=¾wð &ZV24µj•~Üú˜¼V«iY“z—KÉ®Mþä'Yº7‡ }¸ù)uë‘G=Öeu )p¬#íDdçαØ{e옞zjR55]VÛ¶ÇñGZÙqK"Yåðü¼öÚ%G’ä•Bºä}ægÈþ]±âӜ׈‚‡+Ú‘¯é*Ä!„JÈ–”¯ÂòVŠ’dí›U-û#,’ååÁòÛÄVú ŽTxº#Àü ÛBáœk7ö5ùýgó=S¯v$W‚/ì~,BZMùZ.ânu1H[Ÿ…¥”ÑmvÄ„.ï Z2›±¬•Lýä¥g’A'‰!g¢6I*¯Q}NûCšƒZU9) ÚU«RSF i3¿ãÑGï„þÌ©ŽjÃá·µƒ L0ÉUT8*Ñ0µæ«±ì4“[—ßy§ß`þ侀g/«£GÕÒŒÓÃeü~ÝãêJ²K“ˆ‘¶AM¶D±¹½ñk¹Cû¿êJâ¸ny ÊÔÄÉ“ª¯«+ïû­Ëƒƒƒ*H¨w×˄ɦ¢u¥ÈåkÆg7Òéù÷»Ô¥¶Lï“ñ}¯©é/|á®êfü§îá<7¶>Ÿ¥"¿ÛòÕãg=Ÿ·.Ë]’Üy½òJnO¤mÛneïÈäoOÏê‘G>±¤?Í*ò¼œ\¬¯·/ïÝ{SÝw_®ú+_¹­Iˆõõ~ ÌÎ;~Ÿûkx`Êxü–ñ]£¾××k¹¡áC_ë(ß+¥µžžñìû…˜~ã{é3Êݧê¹ç>RO>ù¡ɺªÖ­›ÊQ—’ÉëEãñZ–Öïß°ácÕÖÞçWbÙº?â¸þæ²àˆóúWóþ槦§Êéüy¹sXuw¼W–ýq®Íÿù<Œe¹º]¯ÃZ.+Á2³víZ½±T°þî<¯}/½äÈbŸ|òš~‰\DÎìx5§µ€WŽ-yPíÿv·êyó´ºúóCyï»þ½g³%²L›ƒ£37§;ðcFv¤>ƒ œšÉÓFê”祄x:$%˹ÇUþË—uD·XpÂëW¹ ,ÏŒj9tèÝœ¡ûâ¿qº›²¿NüX¥Žn“‘l¦¿§Ð?û{ò“Ñ<ß’¸ˆÑ; ϘY”õ¯”¬»ñDYUù_HŸ[éϞҷʾÎ^¤³þ(Yw;É*TŠ$ÉØ—.ÓCyX¥W•(\G‹ðU•2Ò´œ=²j΃µbÅ MˆæÎ«~üã«Û·o‡N°êêêÔøøxŽÏjÉ’%ŸóC°¾ùwwwƒü]ÿ‚úø¾UwÛÜ{¯j?p goÝ:¢Ÿ~lñˆ:³øñüQz˾ º¾ðÕ¸øUõÅ]jçâ·ÕÇ‹ïz«äÿæÅ[Õ'‹ïŽÆ“ÿ·/þ­zvËû–¹¼´ñ0ã×òãÉ’¬-KIQJg|²vc}ú]Gˆ\4ˆßM›W ¹òÓÅ$WvC³¨!^O!5v’qÝ&ÛIˆ½D&JÝÓ`_Wùl³Í€ k‹y]Ôå,7?“”Öü)!gâ/3É™×T5n$Ëáuš„ÒzœÄµ)Þ%pøîDŸÖ×9_2«G‰C¾;¬y$ vª9–(F‡Ò¤Fˆ‘ÌÛÛÛC%XûöíÓ£­#ëëë >ç‡`}ãïþ?Õ¶øûê{‹;ÔÒÅ™ÒÅŸŸTãËï*2·ÿû¿Uú׿Ö÷å—{Ô“KzU×âïä]1Þ]ö¨:ðÍ6õ–ß-,¿÷Sµ÷.«×¾Û­./}Äñju}ñ*MIJêÆÖ«†ÆæÖÐ3$KÖUŒöb€ï3þÊÿ2³Ìm˜žñ‹9M(Ÿi­Z Ì4ð,d^œüþsreübN¬îæ3®Þ*1’½ØŠºd%YnÓ°XG·y‘Ókääi°›¯Å‡dïL^ˆ$–käW¿zӵ᧨¥ú¿Jõ“ÍW_½‘CŒE-ÅÏ8À=!{ÒòÁ«ã~©äJOF^«}°¦¦¦ÔÏþsõÙÏ~6§Ý‚SÛ?m¬ï‹²ÖƇ¿lœü‡Õä^PÖ/~WÝZrwš›kÖ¨þ-Ï©KKÖç]U?Z|ŸzqñêÞÅŸ¸6C”„¢òŒ~é9ïlÝâË%ßÝ[ï@R©»$+H7H–u’æóéczä)Û©…¦ixïÍ–@#sF6Ÿ)8œØiZ ¶ýb/æu!YNÓ°|å+7  ±¹Ýîíæk{ÉÌÚ<³Ò)ÛÔœ²&Œr¥9AF-–«]†u8ãBmPLÀŽð-i«#=³Â˜ÇÐ$WZ¨ÕQ„Rܽ{·nZ,Áªô\„{÷v«‡žÌž|EUòª“Èhþ¹EÜ6 þÏŠ©‚ÍeÊað¦Êóá–ï©‹©Œ§©?uJ­äãý)Å‘¬ö„(YDz^+é‡bŸ 5ª¹§¤<*ØŸÙ<®Þp%WBn„¼ZýTAKjraµ—ì¥?»OÊK Bðìæk3_z©úZ „ÕÏ«šð؉±uÔ$I’å  ”îïÖˆ’5çÁéõ×_WŸûÜç²%Ba‘qž‹ð¥—úŒ l†0½¼ø—yWÂÑÅuêâ–ê¡Cª;™R-©>}‚–;s§aüÉäI•J d'ó¡Vyfz‘˜ž"§þ=^Þ– w ¦^2™¤ËŠ95sã±tc¤ôQ±ð/oË%??úá5ÇÌÉtîa?“f Îe3sÚ·‘l¥Î1ho%à6 Kž!ä•*™á1ÉL.m%Yò»{ñÅÑØá;å†ã eý~—&©öùe¤\wä¦Ûiꢚó` ¹’‘|a¶i¨´‚eæ[o¥Ôce™ûg†Ú××_³MusÄB Žg‰Š®#G’.¯k7”³F*!åöÊí.ý…*óÃÈx¦~³ïœZê@’¬$k0ÝŸgÞ¥ÈÚ>aù>Uëê Oü+ưˆ‰Y. ÓÃma¡f°µ¼?Ìß‚4 ãFƒß8ü¦TLÜféðÝA¾Ö¬ƒª8E1s~ÿûôIçë_ŸT‡§<_›H´ùô>%´¢•ÔsžÔ¥;¯’ŸŒzŽ9·L¼>’3rÒ‰dÉìövELÔ>kGoë:Q¦¬­ ÂÉæ•aŸ”ò¾½ßY5”l:æËÀ¯™JÙ2 Ã>zVFZÆißÁáÜŒKF%W5éÁŠ[Q°¬yèÐ1ã$ÔñÎKDëL^ ©šI–ÜåZKw«W}¢N¾uJ½XÕs½¤ì)Mí#è¬wR¦¯Í­‰%w„à ‡Wv’U®>]^dÊÏtF¥¦üî¤ïö]ò\Ø}Ãø}€ÃÍv"¨ZŠl†Ê\„1ñ`U.›e¸àÉØ«Kx¹H–½yæîýTýÅÝ>Y?©¿ê›š#èÜæ¾ÃÓŽ¨qØçrtš7Ër^dÊÏLŦ(Åöѳ²>N3H;Ž+pTsÖl¬ZW°Ê™Rf >%ȇe%YöÑ|&I2G ÍŒðð3?ŸµÃ!8*‰£”‘¼Å¤Ýh_¨cþ‹/Næ¢bŒùòûµ«VöѳNeú°J§ü>ÀE¢`ÅЃU‰L&»Kî–6É’ Ûh>ë‰WÈY%^³Á¤SVó:rvf±#yƒ¦#;¹ò3ýßéŒìa¤‰Ûï×mô¬”!íƒÊY: Ý/š8Ï1^É +É:øÄI1²žd‹ñmÈÝ®L ìTÆpêûÅ8âŽÃÏHÞR¦û±Ï?é·Y®‰Ãm:£ éç÷Eé´Ç•93l7Ù¿aœ³ø£`A°båÁ²fJ÷¨ ZC/T–ós‚ñR«¢͇§Õ‚ÃÏHÞbTb¹É±NÉ$£õüöž²ã0ICPb%ß)ê±ß&½a—NË}\9e³œÃJéûÅï+¦ VÆuÔ Bï…:U;¥y‚‘’ƒ}“ÛÝnÔ£ù¸#G5áð3’7(ɲ«Aû÷_ ‡} Œ9*QZ1Hi²˜RY˜¥ÓrW…n8KQáù£`A°bèÁra(xñgIGøbN²Å”¢R«Ê‘©Ôi¼ däm¿$K¼NÖ÷ù™ï²šÒ^:%,ÊAaï'¹”s™ÝWÆœ”x° X³PÁòV·ZfÈÖï;‘ÃÝN0ö¬Dï©0ï¤2ÝôÌ̱È!8*G²äfG~OÖ×K;„8î{é´˜†¤åÀáµd]…,Úo:ƒ¶¾ð‹£Ú‰ kz°üçÐÐÅ‚eD»qUN0R2‰Ü>¼Rw¥ayd2ëÜ®gc‰oFuã°_Ä…lÈïÇ^Z³_È¥§T17.Õ²?„Ú-A’FCn"ý’_Q‹m}Q‡9@H¶U)ƒ"øãÁBÁª` s—)zPFô\N^6îÁQ.YV_£u®MIQ{jaˆý@F?:5$-¤ØD‰CFL-ßJÿ/«‰ßTð‹Áá5@¨XbÍï VU´v81këï©Ô°ç<•(’³#ý*E£ýhVÈTò¤Qi¹É„}PBÕÈiNÊB8Ää/ƒä5^„ì>0zÿáÁªš˜ššRÛ¶mS .T‹-R;vìÈ>7::ªË| ,ÐùØc©K—.ÍJËü?ÎÆîb滑\9ÒR`;%}·½àŽASüB´œ&)—ò™9‘y-îÁ&êŒdŠÂå4J2 vrU ‘‘×;•?‡ã óEJʱ ŸgÜPm$ k¬;wªÎÎNýÿää¤q€oW»wïÖË«V­RTÓÓÓ:÷ïßoÜ)®œu¬ÜÚs³.•Uê O&û4á)— •ñT®rK…å)£â]šÝ8äBlzo¤@XóiVûþR›]2ýGVB6§Ñ‚¥'ARÊ‹âų¶±ƒò;ǃzˆje‰‰ ãÎ`…þþüùšXYcÞ¼y³ZÁÊ”Ã:*D®º²ó) ñ‰þNÊ?¹*g©åæèÁÙ¸?œæ1´¶A‡}Â{Y‹¸8á(4_¤×!!ÛÖ÷ ¯’…‚5‹ ÖܹsóH”+‰h5ëСCêêÕ«êðáÃÆÝÒÓ³Öƒ•K$zËìj³}W­ÓÈþ£ÛŠØ6‰šè%-:‚L§D’åL!Svò´ B!òjo}æ´]VO>ùa`2å5“þ[x°ª&6mÚ¤ššš´KˆV{{{–` ©Z·n&U‹£UJ~ׯ_w$VfÚK„"+šÌWþÆqyppÐñù®®uêÔ¨±Í.gïTD6ïXÂY7¾ç¤ã÷?>âûó‰CÞßÙyF+`…^_ìöêê’þaƒð¾§ cWW¯êï+øúžžñȶ÷É“ãz¿šx††Þ5¿É÷ Žð—ò/G¹?ʹÇýñê«7Ô¾p׫$ÿ¿òÊG%}þ›oŽåÊ…¸´µÅcÔ×”ãózà)M<ù}”¶l^?¢¼ÞÖÁ’’ !!Ubt¯¯¯WË—/×ÏÕÕÕ˜¡ìk…ˆ­_¿~–{°œU)†©Üˆj%j“x¾J™´:ã¡êÈÑi­0d{í öx%­¡4j-Ô;L¶WfÝ’6Lýï9£†‡/¯i/ÉæüÙ§ô•ΘZŒçû‹*ÍâÁG”)½Àìm ì¾,¿ùòË×rZ_ÈÆJuÄ/eüèGc9$+Hÿ-Ž+‘èÔäGH›ßI«Ý¾OÈ€wIãù“3¤ª-ÀwÙ#.ÄêhଔídsqdÈmfNÉb‰ÕˆnÁáJ“Â0ʇx°À¦M&š/Åèmo‡!%;1¤Çu¼òÊ•¼Y5¤ùs¹=ãÁ‚`éèëëÓ#¥=ƒ„´e2¡„”wíÚ¥}Ux°üx´: ª+™ ûIMì*NÐy¥Ä&s!™ÎêC êÑL9Ô™£ñu™}Ç2ïkõáý:S„J(Sþ¤‹ÄÖ<«{£‘Õ—bìJ²œüVâ]ªæyKí¿tú¡pý™¶#x°f Á’ò ä† ÔððpNùPH“ù¼x±œ ‘8^”B–!dI_82eÉ_¤6C>“!áëFùG5f½á»Ã¹ÓTCâ·ª¥Ñwný·‚L?TŠQÞŽÃ4õ[ÆEÁ¢“û,ó`£Zp‘s?Iøi˜Z,ÉêûŽªÁa^ĽFV“ߪûíÿÖ#Üʛ㲘ùe!Ùžò× ‡}2ï8răÅ\„5¥ü€£8Î&ùèÈÕÝï@ùGÕà°“¬Bý¦*é·*çþpë¿eÎq)ÛÁm¾C·¹íÍX­* ‰CHœÓ”Oa¶×@Áb.B’ŒØ÷Õló¿EO®ÌAÿ^ ²:ÒÞ|Ó)kÅoULÿ­BÓñ8¥d9MHnoÊ*¥XûÄäÕ2Ê Ê8áïVyÉ••Ü ÚF‡žÑþ;”pT‡7²P-~«Jí!–RÒ[½úŽ«²'ª–] 4I–¹²ú߉k9j˜Œl”ù%ÚkHJ³UY'é·|(šš.ëö²n6|¬ œà*æóP°ð`á]GAs}9ÉUn+‡£y=µì­'ªÝóã·ç,pÔ! BFDÕ’Q•{ö\Í!Ÿ…È”IºìÝä­)J•µëÔ^ÃoYÒJ ƒ(q…> Ê8b™Ò¬´’Љ&'5-ÿuýš ʈK,p€Ã›dÙI‹[iö¥—®¹NñãäÏ êýŠ’d¡`áÁ"É*ÏàýºÂ!VCºOZ"‘Îvòwžy`d¦/[î¼—av°'É8§Ô¸‘{YÑ>ºÐ­Ì·mÛxÞ<“~½_2âQJš2úQÊA> Š 8bC|ZÒÖ~‡žñkõë–™ìÖL3Ù©‰O¦W‹n¼Z¨ç×Ýéº\{~%“dzSeºuí1æ6cŠ 8f·– n“OoÞ|½(¯›™óKòŠ%‡(Xx°ð.#Æ$«EõõMhrSìTE…H–(Tþš©&uÇýÂߟt,/âù8¢Ãá×û¶‡  űͮ®žˆÚ1r5úÜ’wKœƒ(&àGqøõ~ûy~6,q¤bK²PLÀ¬Pcîܹjzz:ç±ùóçë¿Ë–-Ó$KʇREJ,p€î8âI²ðü€x°BM›6©¦¦&íÅ¢ÕÞÞž%XB¾:::ôãò¼”åõNÄÊL;Á’b2_ùÇåÁÁÁX¯¿¹,8Øìrì)vt¼§/.æ¼ü­æåžžñX­¯Û²àˆóú³?ªs¹竚#X¢H R%JU}}½Z¾|y–`YCˆ–I¾ð`‘$YØ“5„…$ñ`ѦA¢µµU544èÿW®\©ÆÆÆrž¿çž{ð`àð…Cº½§RÃxeÀ‚%e?É 6è¾Wö6R:”ç…lݹs àG ‰D«ž·¥à@Á¢“;s’$ê¼…xYH‹¹QLÀްq$“Ý( à ‹¹ñü€aãH¥ðÊ€x° X(X(&àG¸8’UÙˆÅà@ÁƒE’dÌ{dŧ}I’x°P°PÀŽØàH$ÚPÀ,,p€aãH$ÚU2Ùg䩊— ñü€x°P°PÀŽšÅQ©v(&à ,’$k¼gVOH†a]Š”F§‰Ä1MÞÌ”eyüÈ‘´6Þg|aÌH’x°P°PÀŽÆ‘J.™\9ÒG-,”p `áÁÂ+p€Ã#SEÏa(~®D"]ŽÌ܉#x~Àª†‡‡³¯u#XB¬Ì´—EV4™¯üãòàà`¬×ß\ìöû£º÷GW×é¬ò!å%Sñ»ÜÓ3^Òû«eYpÄyýkm”ã÷QsKT*!BBªÄè^__¯–/_ž%TN‰ à8ªÓ…w x°ª4Z[[UCCƒãsx°Àp€£\åÂŽ¢Fâ]¬*Œ¾¾>µjÕ*5:: ÁƒE’$Y ÉjdÒi’ăU¦ò ä† r*x°P°¸“8Àp̉D‹öfUÙBÁƒE’$I’5Ò{«  à8Àްqø1Á‹Ú•ɳÆëOO…®€¡`áÁ p€à¨)Bšœ‰ÕˆžÆÇ»ÜØ×ü wRà8ÀpIêV¹¤gH÷ÛòÿͺÑ) ,’$I’$³™Ê–ý’É“[?¸5k—yÃÚe+âØ¹s§êììÔÿONNªíÛ·«Ý»wëå½{÷ª¦¦¦ìkÕÖ­[ñ`à8ÀSÉd¬r„¨VÖ…jÅŠúÿ5kÖ¨±±±ìs£££jÙ²e(Xà8Àpḉˆ‚qÌ;W—­1þ|ýwÁ‚9ËëìáÁ"I’$ɸe2ÐèBµjÕ*M¤$öíÛ§GZGŠÂ…‹$I’$k'‰´§' V‘!åAÉ 6¨ááaú`q'p€à˜e8‰fW’…‚Å\„xÀp€à(d%“½yßñ`1!wRà8Àp„B¶Žd«S¥R§P°˜‹$I’$É8&  à8Àp@°ð`á8Àp€£ºq@°P°Àp€à I’$I’x°P°¸8Àp€ ‚… à8Àp@°P°¸8Àp€ I’$I’x° X(Xà8Àp€‚…‹:8Àp€x°P°¸8Àp€ I’$I’$«‚µ~ýzM²H’$I’$½R8‹ ‚ ¢‚Á"‚ ‚€`AA@°ª6Ä“E’$I’$é'!X8Àp€à 8Àp€à€`AAÌ®€`AA@°‚ ‚ XAA¬ZŽééiÕÐР¶lÙ’v911+ 2kø¢E‹b‹ëÀzÊùóç«… êuŽýû÷«/}éKÇ‚ Ô¦M›ÔÅ‹c}œ½ùæ›jΜ9±;®d2ŽûbxxXmܸQS’qü×ÊþóÒO<‘Ý=ö˜ºtéRìpLMM©mÛ¶éó­\;vìØ»ã*èu¯pÍ:‚µwï^ÕÔÔ”]nllT[·nÍúÛOVqĵaÃÕÑÑ¡òÃß³gZ»vm¬qH q\±bEl3™þñÇÏ9¶â‚Ãþ{ˆëoãÊ•+ê¾ûîSÝÝÝ5uîÓ¸â†cÕªUêàÁƒÙ߸ÜT­\¹2v8vîÜ©:;;õÿ“““jûöíj÷îݱÁQÌu¯pÍ:‚µfÍýc·Þ¡,[¶,v8ìZÜqÍ›7 QÞDU¼yófα^+NûB.étºæÎ]¢ž¼õÖ[±Ã!ê´«¸ÿÆíÊ(9æÍ`œp¹îU®YG°¬’»„üxìÅ‘`Å—ÜY‰g¢Äýâ¿Ð%øá“m]]Ï;¶â‚CÖY.|’‹/V/¼ð‚º}ûvìö…\„dI)GÖñk_ûZ¶¬×߆¨r÷ß,ÏU¢J‹ÒsèÐ!uõêUuøðaõôÓOÇÇܹsóˆ¢Ç¸árÝ«\³Ž`9ÝézÝýÆG\q¥R)µzõê¬+Ž8LéZ.†"¿Ç Ç£>ª}?NëÇý!w­B°6oÞ; r!´–Ïåâ.Þ¾8ÿÆ¿÷½ïéßy)!UëÖ­Ó¤Jˆ»ø±®_¿;r I¹LŽ)9¶ÚÛÛ³+N8‚\÷ª  VÅb×®]9wèqÞ?wîÜÑþ 9Çñ®ÐÍ×ý!ëÇ;t!Xµ€Ã )=›þÊ8þÆEÙÊ. I‘RzÜpÈ9VÈ¡9°¨¾¾^-_¾<–ç*¿Ç V…~0f)DB”“%K–Äž`Å —ªåN½Ööy1Œ3ë±W7nÜPŸýìgc‡A ÔV߈Ä=÷ÜÛ}!uÓ\ÇcÊô[9=çßxkk«a7A®{Õ€kÖ¬}ûöéÑfÈÿÂæãN°â„K~ÜnÃeã„C†lËh/¹3ºuë–ö`‰"÷ãÌzlŇuD§+¹xüøÇ?ŽÝ¾uC¸ùûrb©Û1%ÊéIŒóo\Ê„¦bªîqþ÷õõéÑ‘¦-#N8‚\÷ª}°bÖ«zËÔJé%v¹£ýÌg>£ÍÉrqûqÇ>X¯¿þº.ÝH‰í¿þë¿ô…0®¿yR/eQC…lIù9Ž8ÄÛ×ßßës°¬—¬Ÿì Iñb™¬¸íƒœ³¬žË8à(æzA,‚ ‚ ˆ AA‹ ‚ ‚EAÁ"‚ ‚ XAA,‚ ‚ AAÁ"‚ ‚€`AA@°‚ âo¿ý¶žZ¦¤q ™þHž—×A@°‚ |†Ìý's›ýüç?Ïy\&‹•Ç_xá6A,‚ ˆ !Á ™jiiÑˇÖËò8A‹ ¢ˆ¸sçŽZ·nZ°`úÅ/~¡ÿ>ôÐCêöíÛl‚ XAÅÆää¤Z¶l™V®–.]ª&&&Ø(A@°‚ J !T,‚ XA!…Y"œ?¾6»S"$‚EQb|ãßÐÊÕoû[½œJ¥ô²¹›DÁ)½H…×{º»»µiû?þã?Ô¡C‡B!X³a[ˆª&dÏ,/B°‚EDŒ–â ’•ÅÍ“äF*¼ÞcÆÕ«WÕ‚ ªš`U˶ңÝ«åØ ‚E,!£äDM" )¾!QK$Ä ÝØØ¨G°IHë«ïȉTx½GüHb?þ¼6xWÁª¶mÑÚÚêªZA°‚ED¬ƒjs·xŽD­‘^KÖ‹º ñÉó=ô.y‘ ¯÷<õÔSú;DÙiiiñ$VNeµ¨£Ú¶…W‰‚E,‚ ª˜`A@°‚‹(Apl‹ .¢ÇA@°‚à"JplÁ".¢ÇA,‚ Šºˆ’¤[Á"‚ ‚˜5ñÿ“$®¼ó¥IEND®B`‚HighLowRendererSample.png000066400000000000000000000354521463604235500345110ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/xy/doc-files‰PNG  IHDRXr5˜:ñIDATxÚíhçýÇ ãËcŒý3Æc ÆcŒ1cŒQŒLBqçb!yu#2e6!$¤^Wœu¨¬C^ÊJW“,Â…SPðJº†t5YC5¡Ô´Ë(Îl”¦ÃZ¼<ß~8q>ŸNwòÉÒ^oø`ßtoÝé^÷y>Ïs÷)„B!ä«îÄB! !„BÀB!„°B!„€…PÓ'Á}÷í „8–B íÅÈi¹Ÿ²V]Ãv±ýûßÿ®~øaõå/Y}ò“ŸÔ!ÿËÿßÿýŸúÌg>£zè!5;;«¶··ÿ[?·„ìëg?ûY‰DÔôô´Z__çG ! X–Ÿ:qâ„íEØ'Ož Ý1úÛßþV}âŸpÜï/|á ¡,kTf³Y~¸°P˜ë ?G·–d/Ü\€%dݰè÷¿ÿ½ëýîÀ2ÈBÀB€µO‹Å=œ_ÿúתZ­êÿÍËd]yMÐõÏþS7‘™÷íþûïWûÛßÔÿþ÷?õŸÿüGØç?ÿùP–ùûàv-ûÜç>§677ùCÀBa¬zËä¢ÿÄO¨/}éKºVæSŸú”¾0Êÿn·%ŸÿüçêÓŸþ´ŽÉÉɬ—^zI_̤)ƨó‘i™oÖ7¾ñ]ÛýóŸÿ\[ö‹_ü¢6ÿûßÿþ®m›_#Ûp#kÓà‘#Gö¬óÈ#ìZG^SσÿûߺžIöM¦§§G×8YõÖ[oéíJ—ñ=~ó›ßTSSSnZýýÉû˜·ùío[[VI]Òw¿û]Wß¹ÛïWôþûï«3gΨo}ë[zße}©‡ŠÅb{2H^¼jöXýá蘩tû¬ï#õk²­¯~õ«úuRçöÇ?þQ¯+ +M´_ÿú×õ2YG ¶Ùó! u-`¹m†qº HAî~·õ•¯|Å—¦//€uêÔ)ÇÏ+ËëAÏùóçkËäeÎ&•Ëe=ÿOúSÝí9é{ßûÞ®×]¹reÏ:×®]ÛµŽp|ñ‹_Ü3O²@挈ìSÝ“\¸ýjÕ÷÷ƒü ©f±zß¹—ïweeEg‰Ü¿^½jöXµºW3ŸÁn™Ýk’ɤ>Žì–IÇ‚f|EÀBV“€•N§÷\äÂ-Y/Ûªw‘h`Y/^’}‘»wùk—©zå•WvÍ—^|"ÉYß3“ÉèeÖmýõ¯uµ’‰0¿N>—U’Ù1¯#¯©çÁåË—m÷M2"éh½@ÊúÏ<óLÝ g+¾?ë~›{ zýν~¿V°xñÅõ|i®;|øpm»ÍxÕì±*ûo÷{ý ^Ïy§ïÒ«¯XÀj°¬Ù«—_~ÙÕĺìÆ@Ìó¤éÁÍgo°~üãïyÑ?þñ]ó¥ÙC$i1æË~‹~÷»ßíyÏŸýìg{ÞC²#n÷éiµÞ¾š½ª·ÝR©´kþw¾ó=ÿ§?ý©-ØÈ>›çKsQ+¿?·ûíT¼~¿Ö÷–õÌjÚ«ýÜ Øùéõ3XßÇ€Ç\.çz™4-7ã+BêZÀò²Ün™S¶Å붬·Y¯fË KæÏmý ²ž!©Å1æK‘HjÍŒ&:£NKšßDRc¬/E·ûá&ƒeýœN¬F¯iÔ4æâöûýù™ÁòúýJÍ‘]Ö&× ¢Y¯üÊ`ŸÕëgp{<¸YÖÌyƒ€…¬& ¨YØi¦Ë½Ÿ€å¶ß|¡”¦?ó²wß}·V#ÍmRØm¾³7×ÈHFÀí~Xk°®^½ºçóË<»l”}k4æ”—ïi?ߟµ™ÎmÁ´Ýö¼~¿’¹qòAšÂ›õªÙsQšÙÌË¥sB3ŸÁÏs´™ó! X>d°Z X~ìß~2XwïÞÝuq“¦@ã©Ñ2_ÍÍ8r±ùïÿëzdðP?{6ʈX½°ë¹çÖçý|f@5 Ñî³HŸÄû™Á ‹¯vÙ,£¹­¯š=V­½¥9º™Ïà'`‘ÁBBXÖl‹y<¦N,k-‰tyY‹Ö­µ$F“ ÑÄfü• Œ\èŒ;|ó¾Q¯åVvã`I<$ »q°ÌÃ.ÔóÀZ¨oì›<~Æ<_z?Zµµµ¥÷½•ߟ]‡yOé1)H•ÞlF¯H§÷ôúýus†¬5EFF¦¯¼«òý[ßGF®7†^ðúü¬fÏ„,`yüÁ•ñ¯Ìó$ °amÞh`Õ ëç“^P¢F½¡dŒ 붤6Ëu€H‰?üáž÷Ãê«S<þøãŽÈÅV²of8”XZZÒëË@žÖ!¤9T€Q¾ËåååZ×þV^F°wzO¯ß¯Fq»µ·œ1¾Y3^í÷X5z‚òúü¬fÏ„,`yüÁ•±vÌÅÜõB²,X¢fÆó±Ë²˜Ç²{ÔË;ï¼ÓÔwem*tûÝìÿO~ò“]¯‘#ë(ê~×`¹•uÀQ»q·½§—ï×i=)*¨iÖ«ý«òÞ7û}ù]'É8XÀBèKô¯ýK7±HÏ:iJùÑ~¤Ǭ×Ë­S˸#Ø5"µL;XK=Žy[òˆ—zÖÌX^fɘG2æ–…|> ù_2…æÞmNH6Ìx½Œ¶/MŒv’¢}:iö•ïK X.â2²¹ô¦3CF«Ÿ (û&ï)õPòä³lHó˜dkÜöVuûýÊð"ƨèò~2€¬ŒÒ/Ç÷~¼òr¬Ê{Ë~J6M²¥NÇqûü¬fÏ„,„|µ†ÅMM :ˆF! ¡H²"Rg$£·YÉbÙusGBX5qA·†Ý0ÀB! !IíŠ )uR"!]é¥w].j­ŒïÀ„°B!„€…B!`!„BX!„BÀB!„°B!„,„B!`!„BX!„BV˜õÄOìšN$ê‘G U<ú裡Û'üÀüÀüÀ“fcbbÀ:hÀzì±ÇÔ+¯¼ªx÷ÝwC·Oø'ø'ø'ÍÆ¯~õ+ ÀÚ¼ù曜üø'ø'ø'€EA€`qWx‚x‚x`!j°¨ ð?ð?¨Á°,î´ðOðOðO,‹ ‚  À"ƒÅ]žø'øA«³ëþûï°hÇ<Á<Á<°Z XkkkêÔ©S*‰è0¯kvºwïž:wîœ:{ö¬$‰;wî4\F‹À<Á<Á< ,` 522¢)‹©ÅÅE=}}]9rDݸqc_Ù®t:­²Ùlm:“ɨ™™™†Ë¨Á"‚ "°€%pµººª¡iccCg“D:¯½öÚ¾›ãñ¸ÚÜܬMË{ 7\F‹À<Á<Á< ,`õõõ©J¥²št²úûûuó ÀŒÑ|'ëöööêR³³³j{{Ûvûæ¦E£YИ紌,?ð?ð?ð$°€•ÏçÕè訆¦b±X›ßÓÓ£VVV4ôììì¨ .¨Ó§Oïy½d °¦§§]g»ŒyNˬ`e„YSSSú 1H\þ}Zšfúý?¦düÐôÍ›7ñ?§Àü¨7-çMP>o ‹ÜK¥’›D"¡–——k€eÍ.I¶«^!{½ed°:? À"‚ 2X-èEX­Vk $Y-s}”èðáö¯ÝÚÚR¶ËÆÇÇU¹\ÞUgF.£ À¢vOðOðO XÉdRƒ”–»Õzô=óÌ3µº«ëׯ«gŸ}Vÿ?99Yk>¸’¡æææl›ùô¶Ì=S©TÃeÔ`XÔNà ~à ~àI`+—Ë鬑@‘ôꓱ¯ =÷ÜsºÈ]²Z[’á---©‰‰ ÝŒ888¨æççëÖQ1,î<ñ?ð?𤫛É,‚ ‚°|®À"ƒ…#ø'ø'{¦]œ,j'ðð?¨Á°,2XÜyâ ~à ~à €`QƒEA€Å],î<ñOð?È`!‹,j'ð?ð?ðÀ°‚pWÑœd¹Üeqç‰x‚x`XÔ`ù˜™¢‹ ‚°,2X]XÜyâ ~à ~Á°¬Žj`Q;'ø'øA ʰÈ`qç‰'ø'ø'–3`ɃŸO:¥"‘ˆC^ÒÌÞÛ[ƒå¦ˆ,‚ ÀòY…BAŒŒhÀŠÅbjqqQÏ___WGŽQ7nÜØóšt:­²Ùlm:“ɨ™™Ûí;­ëe;d°È`x‚x‚d°XW«««°666t6I$ óÚk¯Ù¾&«ÍÍÍÚ´¼nxxØóº^¶C 5Xžàžà5X¬¾¾>U©Tö4 èdõ÷÷ëæA£ùÎÜ\h4õYçrZ×ËvÈ`‘Á"ð?ð?È`°òù¼Õ€U,kó{zzÔÊÊŠ†žuáÂuúôéºõZõŠäÖu»£>Ë XSSSšÂEþvË´@Q£õe7Û3«›üóÃ_¦™fšó™éÖOºÈ½T*i°I$jyy¹XÖì’d»È`uÆ]…›¬¬Ögÿ¸óÄ< F‡| ƒÕÖ^„ÕjµQ’Õ2×G‰>¬ÿŽ«r¹¼«v*Ún×i]/Û¡«;«]uxB- žX#Ö¾”L&5H `I±ûØØX­Gß3Ï¿·ç§~O·Ã_·Ó7oÞìØïËðí ·ç·ÆE¶Üúk„ïï·íØž›ó¹]Ço»¦å¼ Êõ1°€•ÏçÕè訆¦b±h»Žd™¢¦§§=-«—í2×ryÉŽQƒÕ}=q¨Áêîc„¬ö×`uzÏÐNîðù«R©¤Á&‘H¨åååºÅê’íòºÌ 5XÝ XÔ`u÷1B Vûk°¬p5XÜ‹°Z­Ö¥­­-500àyÙøø¸*—˻ꬢÑhÃeÔ`XÔ`qŒPƒÅw `¬d2©›ù°¤Ø}llLÏŸœœT+++:«$%Ã)ÌÍÍ5\fmæ[XXнÍ=S©TÃed°,2X#d°È`Xd° X¹\NgФWßÚÚšž¿´´¤&&&TOOTóóóµ×8-³ã`u_}M;²¹¯í¼°“ÇJ£‹¬ –Ÿƒ·ó×Ô`1’;#¹‡0;vÀj÷»“ÇJ#ƒE‹ ,Ëf¸‹,«ó´;¹™“,j°,j°,öL À"ƒE‹ €E ÀBÔ`µ·mÀjm]‡ßõ5Ô`µöøí䛿k°ü®Kê´sµU¿§ÝrŒX,«£ïŠýÎNÁê^À"ƒÕ½¬0<¯À°¨Å°:ú‚M V÷5X5XÀ"ƒ`‘Á"ƒE À"ƒ`XŒƒuЀՎqd:°¨Ájo V;Ç6b,‹, À"ƒE‹ ,2X, À¢ À¢‹,j°,¾/ À"ƒ`‘Á°È`‘Á°È`ñ¨«;~Üý¬Q°¨Á:è±ã:ĨÁ°¨Á `É´]ˆ¼<¤™‡=w'`‘Á"ƒE«}ç,‹ V¨P(¨‘‘ O±XL-..6Ìh¥Ói•ÍfkÓ™LFÍÌÌx^×Ëv, À¢+ˆ5XA?,j°¬&%pµººªjccCg“V<W›››µiyÝðð°çu½lÀ°,2Xd°È`Xd°X}}}ªR©Ø6öööêúø ›UÛÛÛzY$ÙÓ hgÈi]/Û°,‹¬N‹,‹,Ë•òù¼Õ@U,mב,“ÖôôtÝìV½Œ—Óºn·cÔgYkjjJ$‰Ëß OËÉáf}c½ƒü|òžnÖ7NðƒôÃïi7þú凹øÚÍönÞ¼¹ïïËüží8~ýÜž~4süvòù`„çC'ûáv{n/;ý|ðÓ_9o‚r} t‘{©TÒ`“H$ÔòòrÝbuÉv‘Á"ƒE‹,j°¨Á ãqN ¬–õ"¬V«5ˆ²jkkK èÿÇÇÇU¹\ÞU;Fm_ç´®—íX€E 5X[ƒ`Qƒ`Y”L&u –»éù“““jeeEg•®d8…¹¹9½laaA÷ø3÷þK¥R¶Í|Në6Ú€`XÔ`QƒŒ,‹,Ë¢\.§³FEÒ«ommMÏ_ZZRª§§G ªùùyWc[Y‹q°ºoi«µ´vCËË`µ­È`µc@Ò ŸƒÝXd9,Fr°,j°¨Áê‹'€`QƒÕAªW{`X5XÔ`X,‹‡=X5XÔ`X€E êÀêÔºŽnúq'ƒ¾‹'¬înZ°Z[ËÙ ×# À"ƒ`±Ô`Xc¥X€`‘Á"ƒ`XÔ`X€`Xm©Áêäï, À ÆXi€`Xd°È`q>Xd°,+L@äw1|Ð õ¬Îñ·4òÂãepS¯ž4ZÏ/?¼îC·üô>4s,uâ{XVè«[2Xwöd°º/kÆq޽ǀ`X?ÚZ/ 5XÇ9€bÀâQ9€Å6,.žçì€u€€uùòå]Ëå»°SPöÜ©mñ­¨±°øÑfÚ¿ZãÖ õKÝtœûéïA] X…BAŒŒh@ŠÅbjqqq×r¹;„ç³u` \­®®jhÚØØÐÙ$C·nÝRªR©4 Xñx\mnnÖ¦å=†‡‡.°,‹ ûÀgc¬ÀV__߀ISÝøø¸*—Ë{ JþïííÕ1ô±±³³³j{{Ûvû‘HdO“¡1Ïi€`X\xØ>û`°òù¼ÕÐT,kóOœ8¡ÖÖÖf­$%€5==íº¶Ë˜ç´Ì VF˜555¥{E®òׯi9`ܬo¬×©ÛócÚÜÎîv}7Ûw³½vùáf{íðÃëöÌõA<~Ýl¯þî×ü=˜íùå‡ù;=èï+ þ6;è"÷R©¤Á&‘H¨ååå¦ Ù%F‹ ,îŠÙ2XìÇ,Kæ¨Z­Ö%§º«­­-500`»ÌÜÌhÔYE£Ñ†Ë, ÀâÂÃ>ðÙØ+°€•L&u3Ÿ”»5¬ÉÉIµ²²¢3NW2ÔÂÜÜœíº ºw ¹§`*•j¸ À°,.<ìŸ}° X¹\NgФWŸ¹îª`---éÞ…===jppPÍÏÏ×]·ÛÇÁ°Z{â¶j–ƒ|~?Úûÿ:ù9‰ízÎ\PŸã`uιå÷o!2’;€E«kFÂÃñËÅ8a¼¯çç,Ã5X€`X|øþ}ðóž{°, Àâ{NØ2XÀ°,‹‹'Ÿ}G‡ À°:`ZñÀ×n,/ÞùømÕ1•;§¸ÀjÝqÞ(ƒÕŠcÀ°‘9i`µc,2Xd'ølì5X€`X\xðÀb¨Á°, À°,¾ü¥ À°,+ €„¡N¤Àâ³uê>´£ŽÌÏ÷$ƒ…,‹ VC €Åg 1KD °, Àøl–Ïû@ X€`|6«Mû`ù žEغqd¬ƒùq÷óûèDhçØ`íx€,€Åg°‚“ÁjÅõ°+ëòåË»–ß»wO;wN={VCÄ;wl_ë´®—íÁ°:qÈ`µÿ³ùYKÄXÔ`užw¬B¡ FFF4@Åb1µ¸¸¸k¹Pî™3gvV:VÙl¶6ÉdÔÌÌŒíöÖõ² À°¬V߉1,Ë7 \­®®j€ÚØØÐÙ$C·nÝRªR©ì¬x<®677kÓòºááaÛí;­ëe;€`XŒGÆg°‚û[Øu€Õ××· DÒT7>>®Êåòž&ÄH$²§Ð:Ïͺ^¶`X€E‹Ï`‘Á `åóy5::ªªX,ÖæŸ8qB­­­ÙÖhÙÕkÕ«árZ×ívŒú,+`MMMévdã@‘¿~MËAåf}c=?ÞÏ??ßAN[ ݬPþ¶êûw3í§ÆöŒðc{í8üôw}}Ýwz{íò×ú»Ó)~´ËßvM»ù|~žÏ2-çŸßW+§]ä^*•4Ø$ µ¼¼\» ƒœáÈ`‘Á"ƒÕY¢vŒqD+œ¬NˆPô"¬V«ºÉ°Q&ÊÜthÔNE£QÛ×9­ëe;€`XÔ`uîsæ¬pÖ`XûP2™Ô…æPRì>66Ö°t?sï¿T*åyÝFÛ°, À"ƒE À"ƒHÀÊår:k$P$½úÌuWõ«ÑøUn×e, À `µsÔFãùå¡Û~¾§cÁ7«õã`XŒäÀ Ò]€õJGC8¬ð]<»)ƒÕMÍkd°BXõj¯,ÚÅ,+ 5XÝXa¨Á°¸Ö„°xØ3, À"ƒE À"ƒ`X¡¬ µ‹XV'|¶N?g‚Zƒ`̱á¶Î, À"ƒå«wíòÀ"ƒÕ ¬né† –ßA«ä72¬Aÿžqª¤w €E VÐ+ ?x5Xœ3çCÀëé§ŸÞ3ʺ¨P(¨ÁÁA‹ ,‹ , À"ƒåU’¹ºté’OÊ:ÌBOO€u@?^ÆŠ¡ËÛzíòÀꌱ¨Ájm –ßuDnŽ¿¾ûvÖ/…°¨Áj ÞÞ^ WÖq¬nß¾ˆaÈ`‘Á"ƒ®æ2XÝÛCŒ ¬P–<ÖF ãÖ­[°¶nܸ¡Ž=ª&&&,j°¨Á°øîãÀ°¨Áòª+W®ìªÁ2ÇÊÊ €E‹ €E‹ €`‘ÁjFo¼ñ†:~ü¸n”º+éAxíÚ5•Ã8XÔ`X]q‘í¦¬NöÀ¢‹q°\– ó`€[$Q§OŸÖM‘ƺva'öL‹ €ÕªN!d°È`¹õ×ï㎠VÈK È®ÖêÔ©Sêüùó®¶!C:ŒŒŒh@ŠÅbjqqQÏŸœœÔÍŒAÒ[QÖóšíJ§Ó*›ÍÖ¦3™Œš™™i¸Œ,‹,2XÔ`qÞ“Á"ÚXR̾¾¾¾g~¹\vÝ‹P iuuUCÓÆÆ†Î&9õZô Xñx\mnnÖ¦å=†‡‡.#ƒÅ-,‹ ,‹ V[K ªZ­î™/óÜŽƒ%Ûá iggG½ð ºÉÐ,-‰¡ŒÙÙYµ½½mûZi^´6óœ–Qƒ`Qƒ`QƒE €E VÛ2XgΜQ¥R©6Ö{ï½§üq½Ìòù¼ÕÐT,mk³$nîÞ½»g¹d °¦§§]Ïóœ–YÁʳ¦¦¦ôAb¸üÝï´¹­Þíú~¾¿d$ýÜ^;¦ §åFtªn¿?üh÷öü>Úß›7oú|ðÛùkÄAó¹]çƒßþ¶â÷Æ|Ý9ˆ÷—ó&(×—¶Ö_þò—ºçËËË®·#€&¯I$¶¯“Œ˜Ô{Éòz…ìõš$É`QƒE ,j°¨Á"ƒE®áÕ«Wk½ý¤ÉîØ±c¼šéE( åT»UoÙÖÖ–°]&ÃFHM˜¹Î*6\F ?´Ô`XÔ`Qƒ`QƒØa’ɤnæÀ’bw^tòäI=*¼d•>úè#]ƒ%@cía(p%C-ÌÍÍÙ6ó-,,èÞæž‚©Tªá2j°,j°,j°¨Á°¨Á:0À29U¯yÐi\*»¡$k$ëK¯¾µµ5=ÿòåˤ$+öÀèá¦DKKKzx)¤Tóóóu먋 V2X­/À"ƒuÐã3‘Á°È`u`1’;µ$xG‹ïž¬ —5X?’»Ûq³,2XÔ`Xd°¨Á°È`…°¤ù.™'‹,j°,j°¨Á°¨Á `I/<,c ,ëàkqÈ`íï"ÛÈc2X,2X¬,éõ'…æo¿ý6€ÕeuD÷þRƒEà/5X-,?‹Ü¬Î¸P„!ƒ?:°º)ËI‹ß‹ €`Qƒ¢:N¬nªÓ£‹ß‹¬À÷"°ÈNp—E‹ ,‹ßÖ®¬—^zI?"gèãçÉ'ŸT·nݰ¨%!¨Á¢ àXÂ_j°šU>ŸßÓ(£­ß¾}Àâ®?Bd°È`ñ;Œñôøm `ÉóOœ8¡Ž,zï½÷ô O=õ€E»8~„°¨Á¢‹ßoÞñ»J –§F­M‚ò Á‹» ü ƒE‹ €Åï*¬f{ÚIÆÄò{›A]¿Ôq,] Xû$ëks¹œ.¢—gF"uúôéZÖLFŽ?wîœ:{ö¬†‰;wîØn×i]/Û!ƒÅ]?Ú#d°8FÈ`‘Áj9`¹ 7* jddD¯‹ÅÔâ⢞?99©VVV4I\ºtI¯'J§Ó*›ÍÖ¶‘ÉdÔÌÌŒíöÖõ²j°¨°8F¨Áâ¡‹¬À–@“úH×` Јt?sï¿T*eÛÌç´n£í0AA0V Kšþ$k$P$½údDxÑåË—õP ÒsPžs(Ã'lmm¹¿Ê XŒƒÅ~à ~à ~àIW72’;íâø'ø'ø'–ÏÃ5XÜUàžàžàžX]&j°‚ ‚, ÀâN ?ð?ð?ðÀ°hÇ<Á<Á<°,î*¸ËÂüÀ<Á2X€EA€`qWx‚x‚x`X´‹ãžàžøA °¸ÓÂ<Á<Á<°,‚ ‚ ,•Ã]~à ~à ~d°Ú X—.]Rú:ýýýú̵uíÂN<ì™ZüÀüÀüÀ“®¬B¡ FFF4 Åb1µ¸¸¨çONNª•• A;;;êâÅ‹êØ±cž³]étZe³ÙÚt&“Q333 —‘Á"ðOðOðO XW«««š$C%Ù¤zêííõ Xñx\mnnÖ¦å=†‡‡.£‹ ‚ ˆÀ–4V*•†ÐtýúuÕ2K`KbhhHÍÎΪíímÛ×E"‘=M†Æ<§ed°üÀüÀüÀ“ÀV>ŸW£££šŠÅ¢í:¯¾úª:zôh­Ë,É@ `MOO».ž7æ9-³‚•fMMMévdã@‘¿AŸþàƒBµ?øáÿôúú:~à‡ã´õ/~à‡uZΛ |Þ@¹—J% 6‰DB-//ïZ6??¯3ENÅç’y’L,î´ðOðOðOÈ`Y2GÕju(É …î´µµ¥l—«r¹¼«Î*6\F AA¬d2©›ù°¤Ø}llLÏ¿víZݬ•¹‡¡À• µ077gÛÌ·°° {š{ ¦R©†ËÈ`ø'ø'ø'¬\.§³FEÒ«omm­áXWKKKzŒ¬žž588¨›ëÕQ1ãµàžàžàžtu!#¹sWx‚x‚x`ù<\€EA€ÅÞ¹«à. Oðð?È`Xµø'ø'ø'€Å]~à ~à ~à €`AA °¸ÓÂ<Á<Á<°,ÚÅñOðOðO,‹» üÀüÀü È`!j°‚ ‚, ÀâN ?ð?ð?ðÀâQ9´‹ãžàžàžXX—.]Òt–Gèô÷÷ë2ollèe^ÒÌÞ¹ÓÂ<Á<Á<é:À* jddDV,S‹‹‹zþää¤ZYYÑ´³³£.^¼¨Ž;¦—¥Ói•ÍfkÛÈd2jffÆvûNëzÙ5XAA V`KàjuuU–d¨$›TO½½½úo<W›››µùòºááaÛ×8­ëe;d°¸Ë"ð?ð?È`°¤ °R©4¬Áº~ýºÎj‰"‘Èžf@ëË XSSSú 1H\þ}z}}=TûƒþOß¼y?ðÃqÚüÀzÓrÞåóºÈ½T*i°I$jyyyײùùy)2Ÿ“Á"‚ ‚ –Ë^„ÕjU7r”Bw«ÆÇÇU¹\ÞU;Fm·ë´®—íPƒE'ø'øA V`+™LêBs,)vÓó¯]»VwÈ„……ÝãÏÜû/•JÙ6ó9­Ûh;Ô`ø'ø'øA V +—Ë鬑@‘ôê[[[«A’]¸¿Ê XŒƒÅ~à ~à ~àIW72’;AA–ÏÃ5XÜUàžàžàžX<ì™vqêð?ðOðƒ, ÀâN ?ð?ð?ðÀ°‚ ‚°,î*ðOðOð?È`!‹ZüÀüÀüÀ Àâ®?ð?ð?ðÀ°‚ ‚  ‘Á"ðOðOðƒ €`Q+€x‚x‚x`XÜUàžàžàžX-Ï"$‚ À: À’4â¡C‡ö¬kvºwïž:wîœ:{ö¬$‰;wî4\F‹À<Á<Á< ,` 522¢)‹©ÅÅE[j6Û•N§U6›­Mg2533Óp5X~à ~à ~àI`KàjuuUCÓÆÆ†Î&5Êny¬x<®677kÓòÃÃà —‘Á"ðOðOðO X}}}ªR©8B“`õööêR³³³j{{Ûöµ‘HdO“¡1Ïi™¬Œ0kjjJS¸q È_¦™fši¦™f:<Ó¬|>¯FGG54‹EÏð’ÀšžžvýZcžÓ22X~à ~à ~àI ‹ÜK¥’›D"¡–——=÷0”Ì“dÂZ•Á¢‹:OðOðƒ¬Àö"¬V«¶ Ô°¶¶¶ÔÀÀ€í²ññqU.—wÕYE£Ñ†ËÈ`ø'ø'ø'¬d2©›ù¢¤Ø}ll¬!`MNNª••q¸’¡æææl×_XXнÍ=S©TÃeŒƒEAD`+—Ë鬑@‘ôê[[[sïJ´´´¤&&&TOOTóóóuŒq°¸ÓÂ<Á<Á<éê&BFr§]?ð?ð?ðÀòy¸‹» üÀüÀüÀ‹‡=s¢A5X€Å~à ~à ~à €`Ñ.Žx‚x‚x`XÜUàžàžà~ÁBAA€Å]~à ~à ~à €`Ñ.Žx‚x‚5Xˆ x‚x‚d°,‹ ‚ ‹GåpWx‚x‚x`µ°¤öСC»æyyH3{¦V?ð?ð?ð¤ë«P(¨‘‘ X±XL-..î‚.#ÌJ§Ó*›ÍÖ¦3™Œš™™±Ý¾Óº^¶C‹»,OðOðƒ V`KàjuuUCÔÆÆ†Î&5ÊnÅãqµ¹¹Y›–× Ûnßi]/Û¡‹ ‚ ¨Á `õõõ©J¥âXƒe]‰Dö4Zç¹Y×ËvÈ`q—Eà ~à ~Á `åóy5::ª!ªX,º,;«hNëºÝŽQŸe¬©©)ÝŽl(ò7èÓ|ðA¨ö?üŸ^__Çüpœ¶þÅü°NËy”Ïè"÷R©¤Á&‘H¨ååe2XÜUàžàžàžÁò«aµZÕM†k||\•Ëå]µSÑhÔv»NëzÙ5XAA V`+™LêBs()vkX ºÇŸ¹÷_*•²]ßiÝFÛ!ƒEàžàžà¬@V.—ÓY#"éÕ·¶¶f;Lƒy¸†FãW™‹q°¯?ð?ð?𤫛É» üÀüÀüÀËçá,‚ ‚ ,öÌ]wYx‚ø€'øA À°¨À<Á<Á<°,î*ðOðOðO,‹ ‚ j°€Å~à ~à ~à €`Ñ.Žx‚x‚x`XÜUàžàžàA QƒEAÔ`XwZø'ø'ø'€E»8~à ~à ~à €Õ&uÊs É`q—Eà ~à ~Á -`É´]ØéÞ½{êܹsêìÙ³$îܹÓp5XAA„° …‚Ñð‹ÅÔââ¢çŒV:VÙl¶6ÉdÔÌÌLÃed°üÀüÀüÀ“P–ÀÕêꪪ iò Xñx\mnnÖ¦e;ÃÃà —QƒEàžàžàž„°úúúT¥R±m"ìííÕ144¤fggÕöö¶í6"‘Èž&CcžÓ2+XaÖÔÔ”>H —¿AŸ^__Õþà‡ÿÓ7oÞÄüpœ6?ð£Þ´œ7Aù¼¡¬|>¯FGG5P‹EÛu$%€5==íº@ޘ紌,‚ ‚ B[ä^*•4ô$ µ¼¼\·]²]­Ê`QƒE'ø'øA V({V«Õºµµµ¥l—«r¹¼«Î*6\F x‚x‚xJÀJ&“º PKŠÝÇÆÆôüÉÉIµ²²¢3NW2ÔÂÜÜœm3ß‚îhî)˜J¥.#ƒEàžàžàž„°r¹œÎ( 0I¿µµ5=iiIMLL¨žž588¨æççëÖQ1AA#¹sWx‚x‚x`Ìp íâø'ø'ø'{æ®?ð?ð„À2XÀ"‚ À°¸ÓÂ<Á<Á<°,ÚÅñOðOðO¨ÁBd°¸ËÂ<Á<Á2X€EA€`qWx‚x‚x`XÔ`Q'€'~à ~Pƒ`XÜiážàžàžX<*‡ ‚ ëËËCšyØ3wZø'ø'ø'–I…BAŒŒhÀŠÅbjqqQÏO§Ó*›ÍÖÖËd2jffÆvNëzÙ5XÔ x‚x‚Ô`…°®VWW5`mllèL“(«ÍÍÍÚz²lxxØvNëzÙ,î²<Á<Á2X¡¬¾¾>U©Tö4F"‘=Í€ÖynÖõ²j°‚ ‚¬PV>ŸW£££°ŠÅ¢cÑ{½Bx§uÝnǨϲÖÄÄ„úå/©¦¦¦4lÉ_¦™fši¦™f:<Ór­e‘{©TÒГH$Ôòòr[3X!„B¡êEX­Vu“¡h||\•Ëå]µSÑhÔöµNëzÙB!„P(+™Lê"t,)vÓót?Cò*•Úd†œÖm´„B¡ÐV.—Ó%&éñ·¶¶¦ç7¿Ê X~Žƒe•¹6‹ ‚ ˆpFè˘Pkåõ@Â<ÁüÀüÀ“€–Q{…8èñOðOðO,„B¡.€…B!`!„BX!„­è¼„?aõÀꂃÑ.ܬ6ÉSÛ:TwùåË—îwü¹té’~äƒtéïï×ÓÈàº^¼ Ûñâä‰ü•GeÈÓ$Nž<©Þÿý®<‡#^~k‚xÁt:/Üœ3aóÇnŸ½œ/a?^¬.“ ØzäÈ‘®»kjtËÝÏœ9ãz¿ƒìÏää¤ZYYÑã¿íì쨋/ªcÇŽí ‚~¼8yòðë_|Q/“qùäÙ¨Ýv¹9GšÙç øät^¸=gÂäO½}ör¾„ùx°ºP2BýË/¿ìé R½½½êÁTùË_jËånVNYvôèQõÎ;ïâGÒª[·néìE¥Ri °Âà|Æýü…ñx1<‘¬–\(ùfOÜž#NË.\¸ ¦§§Õ“O>¹k0gyÍââ¢öH2ò€Ý?ü0P¿!nÏ™0úcÝ'/çKØ«‹$iZã1CÍüˆ\½zU Ö–?ÿüó:#&hs»íNú1“Öü,Êýd°‚êÏõë×uÇ/À ÃñböDšåÇ>ŸÏ«Û·oë‡Í?õÔS]㉗s¤Þ²t:­ÎŸ?_›¾råJÍCyd ·¶¶ô…Y]ÖÉ*k`Ùë>y9_Â~¼X]$y¦ã«¯¾º¯‘žžÛår Ë‚X'Nœ¨=zi¿€Däx̉µËÀ êñbõD.‰DBÿÀË]³Ô—¸¹c‹'^Αz55ò¨3¹ š÷ըݱn¯Z­ªÃ‡‡°Âæõóy9_Â~¼X]")F4×ÙXîz'4 HÁ¯‘Ž5æ;½&H€åÔ ìþÌÏÏëôz½çn:yÖãÅÎÉÞ˜›î²Ù¬n.ëO¼œ#õ>£mXå¦ 6€Õ þX?Ÿ—ó%ìÇ €Õ%’»iöp+ãN:‹é”ïÝ»wõ]DØ«Ù}º?R°,EÝ~x–㥞'v?Þn~лñª·ì¡‡ÚS—Sï5’‘ k+lþX?Ÿ—ó%ìÇ €Õ’»‰ãÇ»^_êDŒ^’–•;p9Øå"`ߟk×®ÕÍZ5óyÃp¼8y"ÝÌ¥ÙC$ûed¹8‡Ü/“šœL&£{hŠ$£n®©)•JúY.ëÎÍÍu`Ùë>y9_Â~¼X] )Ö]]]m¸ž\äNCºÙ‹E=O o¥}[B ƒzqp;ÞŠÓ>„ÅŸFc£¹õ*LÇ‹“'^Ò‹IzGIÈ}½š’0ŸCû Ùw©­}ôQ "Éô>}Z{+þ=÷Üsµ kP~Cüø} š?~œ/a>^,„B! !„BÀB!„°B!„€…B!`!„BX!„BÀB!„°B!„,„ ¯œFäF! !„êÀ“Ó#r,„€…Bû„-„°B¨Å€e—ͺ~ýº:zô¨~í±cÇÔíÛ·Õ›o¾©âñ¸ž'ËŒ‡ÒŠ®\¹¢ÆÆÆô²þþ~õôÓO«J¥‚éX!`™Kôúë¯ëéh4ºg^"‘ÐÓo¼ñ†ž~ì±ÇÔ½{÷Ô¥K—ôt*•Ât„,„°ìÖ©7O²U¢ãÇëi#£%%Ó’ÉBX!`y,c^__Ÿc=BÀB!Ë#`õôôèÿ%s…°BÀò°ÆÇÇõÿ7nÜÀd„,„°ü¬k×®éÿ¥g¡ô6  ^! !„¬&K´²²¢&&&t=–4ª\.‡éX!„BÀB!„°B!„,„B!`!„BX!„BB!„°B!„,„B! !„BÀB!„BBèà8ë!„,„PË‹ýE! !p°¿! !p°¿! !p°¿!`!„ö!`!„ö!`!„ÂO<ñDݰêÃ?TO?ý´êïïW}}}*‘H¨……Ïïÿý÷ûº?ï¾û®:t者°BXjhhOØÖ©S§T:V;;;êÞ½{ê­·ÞÒóÚ)5#,„€… `õööj°ª§ .¨ééiõä“Oê×ß¹s§A•JEÅb±Út£×¼öÚkŒ!)“ån@ ÀBX¡ÀÖñãÇÕóÏ?¯nܸ±´$³uþüùÚô•+WÔSO=UƒŸ7ß|s 9½æP·nÝÒï“Ëå,„€… '`IvIèðáÃ:›uúôiõþûïëeÑhTmmmÕÖ02ꢬðcL;½æ¡‡R¯¿þºë}°BB(€e–d—.^¼¨Ž=Z»p,§× uòäIõàƒª|>`!„,„P°Ëm/B;I”H2Nõê³ê–Ók ݾ}[E" !`!„‚X^$=ú+ ©¡zì±Çô2)VÏd2º‡¡H†N0×`ÙÁÓk¤6K ãež»X! !JÀzñÅu¡»Ô_IæêìÙ³µ^".©«’å>ú¨nFt,§×üæ7¿Ñï!Y®«W¯:‚•]#€…°B,ö!`!„À`!„,„ÀÁþ"„,„ÀÁþ"„,„°BÀBµ8º-BÀB!„j£þ¢J¦oŒL‰IEND®B`‚StackedXYAreaRenderer2Sample.png000066400000000000000000000405401463604235500356540ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/xy/doc-files‰PNG  IHDRXr5˜A'IDATxÚí l×zþ¯TUUUUU¥ªªªªª*]UUUUU¥ªªªÊB¶Ì¥B¤\­±]|  Ø`ìKœ¸&&® !üáÆŽ) ÄBÁワ Á%˜¾âp ! LÀÄ›pìøüyÏí8ëñîzgwvwfö÷HìÙïgÏÌÙgžóÎ9?RÀVüˆ¯ƒ€ÁÀ` –vüýh½þ¾€ö`°€KÑÝÝ­.\¨þò/ÿRýÎïüŽúíßþmõû¿ÿû*--M-_¾\utt`°,¾ovvö„Çÿõ_ÿµúþûïÇïVõW5á1ú§:aûoþæo&½®¼NàcfΜ±†K—.MÒ!·¹òÙ‹ŠŠÔüã ûíOúSµuëVý{Å`™?¿P4ÿÁüÊÌÌTÕÕÕª¯¯Ï³}Ô‘#GTYY™úÉO~¢5í-ÿÿû¿ÿ»nïÀã H:V¯^´óžê)Õ’$«ï{óæMÝù>G~ TUUM¸OûÞ{ï©ßú­ßšpûÎ;ÇŸ³gÏž ÷Éc/_¾±ó{š?“›°aÆIß•™ò'âHƒÍg‰äsÙÜÜìͼôÿÓ?ý“ºÿ>:À`äCÎú"é¸0Xѽï+¯¼2)qC$É‹üxßæÍ›õsV¬X1ávIgŒ3ssâ%gôV`N¿„û·ëºýö¥—^²¼ß¦‚Á2èE“©ö•+WÒ± H>Ì?Ø¿øÅ/ÔÐоO†WNž<©S?ÿó?Ç`Eù¾ÿøÿ8áyÿú¯ÿª‡^o“Çðûý:‰¼_Œ°9½’tæîÝ»ŽÀáAI~Ó7 ^¿~}’9•ïóƒ>ÐFT 1`üÇìiƒeà³Ï>ÓÃÄ÷ýáþ¡Þ¼郤/’>idd$¤viw0X é0±DZÃ1UbPPP „©“Ä?û³?ÓuIòC ===úìóïþîïÔïþîïê牉ç¼ýöÛSþÐüË¿üË„Ûå3¸pá‚®1“NZ^W^_ꛞ}öÙ C Ï?ÿ¼®G“ÇÊsd-Úè?þ8ìP–Ü' Ä /¼0á1òݙͰ‘xEŠÀáAÑ&úà š?§˜ù^þâ/þBæh¾Ûhö 3äu?—ì/Æn d_þ‡ø‡zÄ€,]ºTýÞïýžfiié¤×°úy#ùά¤ÄVL™yÿ7·i¤íd~9Ñ’×2ŽITùË_êÇŠ>ª•}Sî“Lj¹5ã­·ÞÒFÈøå¯lËí±BN2?¯¼>,tˆ1Y?óÌ3ª­­-l-ÃT?SÅøòúhjjš”JXê1 I„ŒÝ;v„58ò#3000þú>Ÿ/ª!S+¦ “/Ÿ]~¬B='ša=yŽñ|1EøÁ^Ïüžæðh¾[«ûE0üó?ÿsTÃaæ÷Ó3U=šÕÏÉw/ƒ%fÅüÞ¬´S°û‚=GNÄÀ»OWÒŸ„Ó,÷Ç9V_OÌ,tH O¸dE~Ì6mÚ4!×á˶üøÈ®œ›ßGRrc`çÿGôGãW-^½zUM›6-¬Áêììœ4DpãÆ ýØsçÎMêÌå3‰¦`ü¾}û&ýHIÒ!iH,ëáÇA “üÈË}Á ¦3TÛX½ªÓ|õ  w5áTæÂêwku¿I^Ì K¬©k¨«6­~ÞxÖƒMõù|÷Ë÷Í1`¥ÖkªïÑlú$1”÷–¿·&ÔV!iš.Ú,à1HJe˜p”š¯¿þÚR‡éTCÅø¹¹¹î3†ÏPÏÆÍï+CÛr9·¹sçý161?‚Y³f…|­Xkx‚fK|8ÈUQæçÈg´ óÕƒû÷ïׯÁJz#5`©¦ÕïÖê~ ò˜hÚÁ¬GŒ½ÔóXyÿ©>ïTßY< –ù1Æg³ÚNæ÷imm zòî>I£2}‚ù;Èx´SBj°¤Þ,PW p$-‘NÎüãÈ'Ÿ|2â_©=‘VRsêøxóTSn‡3‚æá¶ÀÎ7#sqyà¼:ñH°¤ž%\ÓÕÕ5é9V¦eLb̵v2$nØq*½V¿[«ûE¼¬`&#–ýØÊ>’ˆKöåhÚ)Ôû„û®BÝêx2?Þø¬V ÇAà¼q¢Ó\Ë 8ÒñIñnmmí$C …À‘tøò|³i 7\b5‘÷šRˆ©æI2¿¯ùñ±þ(W‡"éR¬ÉE8›\4‡ §z_«ß­Õý"Ìu?‘J[®‹æó&Ó`É0[àý’LGÓNÑê v_¸c;–ÂtI«D² ƒI-B]¢/C‚V†C ˜§%‰Leê‡P7ŸåN•H« Uôl~í`µd0ÚÛat¢¹ŠÐNƒlrÑP &œê}­~·V÷‹P«ýÿ÷ÿ÷Aß[j礆0ZƒÍçM¦Á2ÔK]4íd§ÁŠG‚%ÃêÇ“ÔÁÉÔ`°€³v¢ÿûÏÏÏ×gÀ†¹‘:stà\MáÒžPg­¡:gYÖ$ÔÌ匳ñ`¯c.<–š,£p<’×–M™GI`®y’XŽ9q‘÷2σhì6XæáÁ©jí"}_«ß­ÕýÂJ"'ï!;¤íå*6cø(ZƒÍçTK¸¤Ôê~ û¨¹-äê`£Éj;Ùi°Ì5X2UD°6Œ¤KJÌËOI±|¤CÄ`°@ V¤”BÖ@˜×Í;}ú´¾Ý\ó!?æbbóPŒù*@£¸\®4¦Mõ™%©0¿§1ììµ¥ÞLÎâå,Z®Æ3.C˜§{yäqRÌÑ‘¹ªÌ) ÔŽÍiY¨‚÷X –|÷æaÞÀº2ùßœ2‰æTïkõ»µº_„C°yÉ¢½r/ÔíÑ|ÞHµ„:vì:VÛÛÛ£n'; –yØR ‘ š«ƒM«a×4*`°€í0ÿ¸†Â’KºÍUWnê‡PàöíÛcšË<1§¼–Lñ hllœòµ×‘³~óU‰Ñvಡùûj-Â`½ÆòñfÍš Ï•À s*`|ÆHÞ×ÊwÍ~áæ œ#ZƒÍçTK¨c'Vƒ%¦0Ø\bVÚÉNƒN«Õy°¬N $¯ËÙ¬L• EíÆURòW†efuè˜!gÀò#dLVjÌò,·KŒÑ¡Ëýæ3Ù` ¤:ò<©©1>‡˜I° ™ƒ½Ž¼§ù 7pØAf‰—Ï*šäµÅ4Êç“!1I©gå–ÔL¦B0>¿¼î¯ýk˸yÂRY0°þE¾{óÚ€Á P,?RŸbž6À ó|[ÆÜN‘¾o¤ßm´ûE8Èeÿò2¹¤¼®¼·˜ ]‘\êöh>o¤ZB;V †U\\¬222Tff¦*//W½½½ú¾´´´ $ÁBšÐ“ šäj»T7WæÙ⫪¾ruªÅ±„ÁJJKKUWW—ÓliiQyyyã‹,ÆïÑ„žTÔ$iÍüù0VIX‘c Mž"LOO·Å`‘`¡ Mèq«¦M›.c¤"Lµ$ésKªÅ±„ÁJ FGGÕô¡a°Äl }¤­[·ªááá ÆÊ Ù`‰³6_þ²Í6Ûl;}[æ…Z°à; ”E–—?T»w÷èT‹ý‰íX¶=e°Œú*1IwïÞt¿ßï׫ººš MhB§5¹mÍA§¦Z­­ï³ß¡‰K022¢‹Þ—/_ô~©Ñ’bxj°Ð„&ôxUS{ûIW¯9è$Ι3¦V®üF×jut¼Ë~‡&j°B™¨¡¡!5kÖ,,4¡ =žÕDzÊNHµ8–0X CYY™êîîÖéÔ£Gt –Ì_e¾ÂPÌU}}ýã3‘æÁ‚z’¤W©™jA V\ÐÞÞ®”²Ïœ9SÕÕÕi3%hkkS%%%jÚ´ijöìÙjûöí\EÈÙšÐãYM¤WÞOµ8–0X¬EÈX7šÐD‘^¥TªµsçGqOµ8–0X¬EÈ™šÐD%uuW1;Iµ$I턞dj"½‚v±¢â6ý‹,«ª¾Šk‡TVæg /Híôåd”ã ƒE‚ř”ܹó#ۇݫ“`y[é´“rýËv´´´¨’’•‘‘¡¦OŸ®jjjT¿¾/---(©ÁröÐ`~þÔ/¥Ë»šH¯`<èæõW©Ár¨Á*--U]]]jllLŽŽª]»v©%K–Œ,,w)<÷ÜÍ„vJ’”mÛö1턞„i"½‚ñ $ÿô¬¸#==݃E –7‡ƒÅë{÷ž¥ µWеܼùsŽ1 V|qöìYjKÌ–Ð÷xܺu«&ÁrèÏüù’Ö9É{;)b'Áò¦&Ò+/Ê…Aô¬¸áĉjÑ¢Eã5XðûýÚ`UWW5V±zõj=6l4¾üuûö;wùùä2ãdwP……ÕÇ_wÄ÷aþëöýÏkzd»¯¯ÏÒã;;/¨Ÿýì{Gþ8×-éR7VT©‘‚%ª¯x•:UÒ ^YvF•Î÷c^\§žºOÿà mO¬íÛ·ë!½ÁÁÁ‘:-)†'ÁrÖgjhøÄ1”S¦o Áòž&'¦Wë >R×–U†}ÐГ Ô•åÕÚtý÷ÒNUü³¯14,úLÿ* –4ŽºO…¡¡!5kÖ,j°64˜È«#aeå-æÈ‚ž®½cõåòçÔØœ9Q½€˜®O‹ŸWÇV¼¦MW^ö#LŽxôèqŽ7 –}èìì ™Z^a(檾¾^544`9èó”—;ólxíÚë´zlÓä”ôJŒ•£h¦ËÙtëÅ:$X5XáæºjkkÓsdM›6MÍž=[#²aCƒrË–Oi'ôÄ¬É éÕÚEÇÍX…â÷ÙÙêÞ‚%ú}®xÓ•JŸJÿ€Áb&÷?S—“yÕ`¤sd%ëŒË;š’™^­^p%áÆ*RÓÕR|@m\|Óe#e_£À`±aŠsåÊo\ÑaIòàæ’ajÖ^‰±’âu14N?Èä3ú¯Ð¦ëí¯ë+ós¿Å0¥à¢Ï,,’„)ËÓ¸©Ó’¤M7ÎèÐcUS¢Ó+1VrÅŸŒÕT”¤K ñ¥¦«¡è8W/FÀeËîÒ?`°Üa°¨Á²Ÿ¾çÊ™¬—.½¯×I¤&=‘jJdzU‘ÿ¥º\¼ÆÆ*>™¯z—­Ò5]RWÆð¢7}¦‹‹$ÁÃW FBÖLÔô $Xî×”ˆôêçón¦„± E©-\T¤¿ƒÿ]Ѩ‡ þã‹>Ó?`°¨ÁJ-¾øâ®ï¼’=}¤öJøÌ¼k)m¬¦â·ù‹ôðâñ¿L¹ÉQݼè3‹‹$! îßÿg¹Ý¸ñKÎè8C «)^é• ~¶Â9Wº‰©2O—}&Á¢‹Z˜XR2è©NlÛ¶©I +aéÕ³ ¿ÀXaº<»è35X$X$ )<4hfnî¨zóÍ.ÎèH°âš^‰±údÙ:Œ“£Zª¥À`Qƒ•ôÒÐ`°éZ[ß§¡íéÕê‚«Žš ÓåžÉQemWŽE  –ÇÏ䊻ŋ½=a`AÁþQ匎ËŽôªjÉŒ•KL—19ªM—Û}&Á¢‹Z‹tÊ·ñ¦Ô—ÙÝ¡Qƒå>M±¤W+o˜®¾¥O©‹+6hÓ%“¾&ËtÉÈý‹Ë£g --gôZ~©Ò¿>ýt¿­sd‘`¹OS]ÝU†á$Ó5°¸X}²b½^H’®D,ä¶EŸI°¨Á‚ †åºu×hÿ¥$˜yyßa¬`ÄËÉ’Frõâ+…§Uaž½k³Ê…E—,,ž)¤ÊÐ`<ç !Ár—¦—^ºñU+ŒRÓu®ä%ÕZ¼O/”;'ú‹%*+oÑ?`°¨ÁòÚXwª ÆkŽ,j°Ü£)’ô c£]H†e€"^tÛ¢ÏÔ`‘`‘$04ñY±Nß@‚åMáÒ+求vóÁÂÅ꩹½žZô™Ë¡«¥¥E•””¨ŒŒ 5}útUSS£úûûõ}cccª¾¾^ß&æI888H Cƒ ™¾áðáSì)Z{ÅÌë0žüàÉÚ)vèÐiŽQ Vl(--U]]]ÚLŽŽª]»v©%K–èûUssóøc›ššT]] V^wïÞ³)?4hfQÑ=uäÈ Îè<œ`™Ó+Ö „‰àwÙÿ¡žô…-ؽû<ýË~¤§§ë¿Êï÷ß.ÉVNN5Xq8‹gh08ËË¿¦&Á£5W®\Ó³fc¬`2ØìköÌ¢ÏÔ`¹Ä`={V§Z‚ÌÌÌ ÷IÊe¾+vÊôôwöNß@‚å|¾úêWê™y×0V09 Ug=6ÅrÓ´1$X.0X'NœP‹-¯ÁJKK›ô˜`·õYÁ –8k£ñå/Û·¿•Ô¡ÁÙ÷Õ+ËÎ8~AV™„’ýÅ;Ûï<¨®—Kñz6?ö0iÜâ{'Lz~ã5‰Ûž2XÛ·o×EéEì$Xñ=SHöРœ½Ýœ÷ó ËTœ*iPÿ½´Ó‘†ëµ×.’`¹œb¬z׬Q£¹¹üÀäó–o¹Êö†¼Ð†þ+fHãH¡»………j```B VVV5Xsu5«|ÊuÁœd¸dú†ææ©Ár!O:¤nTWc¬ ã¸Ùw,äT nYô™,› ÖƒTmm­žR!p¸NÌо}û,¿^gggÈ©öìÙ£¯ ¼ŠPÞ›ËÝW æùª+¾ò¨bM¶á’Å€#Yˆ•ËY‰•ì?ü˜C'ò†¯$dŠå–EŸI°l2X7nÔÆÊ ‹/ªÙ³g[~½À×2¿.ó`ÅoîŸd æú¾SùªmY„5Y†k᪽ý$û’Õ$V#sçò#Ï_øÞ z×Îq<§’Á’äJ&óc.8Ÿ6m3¹»àL¡ªê«¤™«.ß q[õ>‘†«¤d0l|O‚•ž:|X][·ŽÄ ºŠ_úÊ‚¦XnYô™Ë&ƒ%sT‰¹2_Ñwûöm=;k:{¬[&¯KÆÐ`<ÍU² WY™Ÿ,‡ðd{»êY¿žÄ º–U¾‹®]ô™,› Ö‚ ôÐ[oo¯6Xb¶º»»õô ²ä –sÏ$q‘á-¯›«D.Y^ˆ+ÉÆjÃŒt=¥të¢Ï$X6¬ãLJ¬› v%`2 5XÉ”Øûœo­ã:³@ÃÕPt<&õcÇö/+cf¹oâÒMóæ s¼§Ú4 gΜQÅÅÅzHPê®ä B¹0Ù ÁrÖР$WN4W¡ —ñ ulÅkÚpåΉÜDf¯»I°TcEb½Êc¾Í®\ô™ËEkRƒeïXw²†ßóýµÌ—$†ëƒ’Íê•ÂÓªà?îL9}CkëûÔ`Å9±ú./bèÝE }¹j‰Ï?áf™R‡þ!E V¨áAó´ $XÎ9SHÆÐà;¾-žêø$áúzÉÏõ¢®üÜoƒÎ¼|øð),j¬ Œšoû¶M¸iË–Oé0XÎ3XÔ`%ghð}¯z¾”„Kj¸Ž¯ø¥ª[Ò5žp-]z_9r‚Œ„Q§X‹|ƒ®\ôƒ'ÔÕÕ©K—.‘`9èLA&Ô"I’«ÄP ׇ%›Ô¾êÿUÝ]]$XQðÝŽõùæÍ+˜ÒÜéûÕøfEÅmú‡T7X7nÜP¥¥¥Ô`9h¬[æPÁ\%‡c99j°¤D§0çwïVÇ¥k cõé–-êÁÂ…ì?0å9è[¤—sË¢ÏÔ`ÅÙ`É|XN›h4•,Ybså¬.7®x¡b¬ Yô™+ŽKÌÕo¼á8ƒ•ª5X248þ#̆˱ÄXAùò9nYôƒÇ"w³Á!ÁJΙB"¯ü•o'a .»ÎP1VZ_Úé‹>“`ÅÑ`IrUQQ¡©ÁJòX·ˆ‰ºj°Ù×L'˜"†ËŽ‹O0VF‘b9}Ñgj°Rp¢ÑTK°d¦D]5Øä{“0… W,g¨òyå³ÓŽZçZß9=*A‚åQƒ5ÕÜW̃å Ê弘+ —c:]Œ„1ó¢¯J•” R…Áb&÷d)Èl¿‰8Þ·ùÞ¦Ósá:qäHÒÎP1VÚËusÏ“`1DÈZ„Éë–¡AY /Þǹ\6<ê˦ÃsÃ<\sæh“smÝ:uvïÞ˜ W$5+ãÓ¾:GOÕ@ –MËï÷«’Çhzzº­ –4ÐŒ3"JÎH°&ÞV^þuÜñ;c˜+—®oV®Ô†ë£;-)†;CÅXA祹÷½om;J‚åuƒ%&FLδiÓl3X¡žoõõR±kóæÏ1W0*Ãu§¸Xõ¬_u ×û­­Ú´ñ}B~¶âyj£¼n°$eÚ·oŸž\433SŽŽêÛ;::ÔÖ­[cžÂNƒåõ«­­SåæŽb® -†k ´T®s¯¿®ç« µß‰±ºUY©ŸÃwab8’•£A'Áò°Á’¡A1W‚'žxB]¾|yÂ}v,yM¡ïñ&nxx˜¬ÿûåÊoâzL×ùNb®R¸hþþÒ¥ºhþÂŽêÚ•++“L9©Áò°Á’ú+ù2åååj×®]úÿîîn=lh§Á2×~‰Áª®®j¬ š –|VÃ]Ë_·o÷õõé¿6ôÄõX–„¿óåÒ©Áß$\99+“}â󳟩 Žû}2è¥ßÛ¤¬Ã‡k“%³X?µhÑ¢¸¬H”N•,Y—*ž³µËÄv˜+!tàìî7R#• Ó4 i“%CxóæÍSW¯^«Á’÷›5kVJ×`=Û­ŠŠîa® „0ù0?R$5X.7XRØ~÷îݸÎce6X¥¥¥ª««K'Wb®êëëUCCCJ×`54܉۱[íûs!„§,˜N –‡ –Qp.¦¥³³s¼Ð=^ H ÚÚÚôp¤ÔvÍž=[mß¾=¥¯"lnþ0nCƒ˜+!teÑt'¥X$X1,1V“‹ÊPݦM›&\E贙ܽTƒ%³ø a® „:.ÅÂ`ňG©·ÞzKÝN4šŸŸ¯š››ÕÀÀkƉëÖ]Ã\A!Ô¼WTD‚åÕ"w1So¼ñ†Z²dɸъušÖ" Î;?ŠË1Z¾õ=Ig!„.¤¬Â@ –Ç{¾pá‚ÊËË‹i-B¬ÐCƒ >ˆKr…¹‚B÷RÖ%Áò Áºwïž.B.tZ‚å…¬U«úH® „::ÅÂ`ň‘‘uäÈUQQ1¡à]j°d¸pppËFnÛö±íW –ù¾T÷|ùtLBèÞ~ü{L‚årƒeL(j¾Š0ÖÉE©Á ΃ßSóç?Â\A! IYÂJÖ ¥Ëó`ɰ۩S§l‹k2Ÿ~ºs!„pJÞJr˜@‚£Áj}ìe6u'Ã+5X¯¾zÉÖãïç¾ë˜+!ôpŠÕÙÖF픯"$Á²w!ç¼¼ïl;ö–ûn)¿o „z˜=ëד`a°¨Á Ç’’AÛŽ¹Bßט+!LŽÌ«N¶·Sƒ…Á"Á ÆÍ›?·5¹úÚWHÇ!„©’bmØ@‚åFƒuàÀ×,7Õ`µ´œ±mJ’+!$Å‚.ºŠP–Å‘(Ë^vt¼«/þÖ6sEr!„©ÉË›6‘`¹Í`KáÈlí jxx˜,›¸~}-ÇÖŸs!„)Ì ªw;:¨Ár“Á’y¯:¤²²²´ÑÊÉÉQgΜ!ÁŠ‘{÷žUÙÙßÇ|\-ò b® „ªO·l!Árc‘ûèè¨jnnVO<ñÄø¬î¤ËÚÐ`QÑ=[ÌÕ—¾2:!„II±0X6@Ö#ljjR3gδÕ`IÄ8cÆŒI©Y}}½ª©©ÑæIn½C·%XÏ=w3æcéIß·êºïçt*BÇùÑÎ$Xn2XmmmÛÍ7>DØÕÕeKòÊ 566ê´Ì€»ºº:OÔ`½þú¹˜¯$¹‚BŒƒ%%Ô`¹Å`Í›7o¼È}Û¶m:ɲfƒUPP ü~ÿøv¿6vnO°>¥òó’\A!ŒÏïÞM‚Å4 Á Vffæ¤!Cómn¬Áª¬¼³¹ºâ+§B’߬\I=•[{Ž7Ì+XMW°ÛŒú¬`C„b w-“½ý«_}¹‚Bfg«KÇŽÅý÷Í Ó~ocÙöÜR9v'XN«Á:tè´š;w$êc%×÷úÜWA§!„0"ÞJÀï 5X.4X………j```B –ÌÃåÖ¬•+¿‰)¹ºä«¤Ã€B1ÇæÌQï·¶Rƒ…Ášh°öìÙ£¯ ¼Š°¶¶Ö•5X/¿üÂBÎkëÖQW•ª+Ô|Z^™«µõ}•›;õ°àG¾j: !„Q1Þ‹@“`¹ ÁòêZ„Ñ JrEÍ„ÂXÙ³a5X,o­E¸qã—$WB“žb?z” ƒåµß|³+ª…œó|1WBme¢Æ`‘`Åm!çÅ‹¿Ê\QÐ!„ÐnÆkh,j°Ê z¬Ï çUç|ké „º&Å¢‹+aÜ»÷¬å…œ%¹bž+!„ñäÝeËH°0Xî¬Á:zô¸å¡A’+!„‰b¢Æ`‘`Ùʪª¯,'W]¾8è!„&„ƒ%%$X,wÕ`íÞ}ÞÒР$W}UðB]›bQƒE‚W¶·ŸTóæ [šçŠaA!„ÉàíŠ , –;j°*+oYÄ\A!L±4‹+f64|b)¹bQ!„Éæ-›ÊhH°¨ÁŠ :ñР˜+ Ú!„:%ÅzïàAj°0XÎL°"]È™š+!„N£‹@“`Qƒe;·lù”ä B¡k)‹@Ÿlo§î ƒåœ«­­SÍ;‚¹‚B˜Ò) 5X Ä\A!t:ÍŸÓ"ÐÔ`‘`ÙÆ_üs!„Ð3Œeh,¬´´´ Lf ÖþýL94(3´c® „º….Œ)ÅÂ`¹Ð`9)ÁêèxwÊ…œÅ\÷ÕsÀB!t?ih ÁÂ`%§kƆ!„z’Ñ.M –K Vzzº¦ïqãoݺU '%ÁÚ»÷lØ…œ%¹bž+!„nf4‹@“`¹¼ÈÝï÷kƒU]]ÔX4,qÖFãËßh¶»ººÃ 2‰(„B/ðþSOÅô{é•í”»ŠpllLedd$<ÁªªúŠ‚v!„)Áöï'ÁJ5ƒ544¤fÍš•Ьݻχ¤ B¡×huhj°\h°JKKUWW—N®Ä\Õ×׫†††„%Xíí'C.äLA;„B/R~¿µ•ËË«­­M•””¨iÓ¦©Ù³g«íÛ·'t-ÂÊÊ[˜+!„)Ç› ó`1“{\¬††O0WBS’£¹¹/M‚ÅZ„óСÓA‡1WBS…‘.M  VL 93„ÂTâÈܹêÄ‘#$X,{j°¶lù”ä B!|ÌÏ^~™, Vì V[[礅œ™ç BaªòÑüùS¦X$XÔ`Ydž+!„©Î«uuÔ`a°¢O°^|ñ †!„Bæç«w;:H°0XÖk°öïÿ`ÂÐ íBáüxÛ6j°0XÖ¬ŽŽwÕÒ¥÷©¹‚BCðþÒ¥!S,,j°¦$¹‚B­¥XÔ`‘`MbKË•›;:ž\a® „Âà(-%ÁÂ`M]ƒuäÈ µxñ·$WBa„ü°¹™, VøËXÈ™ä B!ŒŒþ²2, Vè¬ÆÆn5gÎS1@!„ÙÙÖF  Ö䫽ý¤š?ÿÂBa¼Q]M‚E Ö䬧Ÿîg*!„0J~Ÿ­Þ;x,¬¾°;.°ü „B#o>6 $XÔ`i>|J-œ÷-É„B#GssÕÉövj°Üj°ÆÆÆT}}½ª©©ÑæI888U‚õŸOõa® „B›Ø³a –[ Vcc£jnnßnjjRuuu–k°6½øíB¡™;W8r„,7¬‚‚å÷ûÇ·ûûûUNNŽ¥«­å´:;g„Bh3?{ùe,7¬ÌÌÌIC†æÛ ce0«W¯V§Ê›Õ•¼Juyî³úïÕŸ­võöóžSƒ%O?f‰ºÿÔSêay¹þëæíአOé‘¿wJ+Tÿ²gÔÀŠßüí[ú”«·ÍÝ®çváSz¿óÊþfl<>©Dý±=Xÿã©ç?ÿŸ:z´Oµ¶^ÿ»{÷ù¸l;vkÜÌÉ_©ýŠ×¶ç VZZZD·E»¡éÅ3/jzçKª¸ø'¸äÒ¥÷Usó‡Kèñ¼&cůPô`%8ÁŠf-BÁ zôªgpíÚëzRöM˜ lm}ß3ýè=,›PXX¨&Ô`eee‘`¡ÉUš$Þ^¸ð'‰ÌËûN½öÚEŽ%ô¤œ¦U«úQóæ c|4¤ðâ‹_¨ŽŽw9–ГҚÊË¿võ±\]}ƒ«ݰ!„ɦ¤)¥¥˜ 8² `HŸ¹³¿Aèî+é‹ M.×´yóç*7wCd3Ÿ{e¿CšÜŸb%#½Â`Qƒ…&hÚ¿ÿµxñ·#(fU†`ÙïЃ¦à)–éd¤W,,4yD“¤-ëÖ]c:‡(C®mmìw$Xh Ã’’AW×2Í 3¹SƒaÌ”é(€·^ÈþÒKWB²C'ö1n9¶çÎQíí'1X$XœÑ¡ÉMÒ¡0Cä…ì{÷že¿#ÁB“S¬d¦W,j°ÐäaM[¶|ªÏà0R¡'Œõì–ý=©¨É )V²Ó+  š<®‰é‚w¼b>ÙïH°ÐäÝkãÆ/“þa°¨Á‚)@Ö3ü åG!‘k‘AèUîØqÁ±Ç¹Ô¡:a½P  šRDSKË™”ÎA& 7#;û 𬳍èž#÷ºº«Žø~0XÔ`¡)…4ÉtR{”JiV~þøÍÈÎ~‡žTÖäÄKN¦‚­ŠÁ"ÁâŒM Ñ”*Ó9ˆ™ŒçPûzR]“ÓR¬dÍÚŽÁ¢  ð·=;#ûÎÑΦPŠ•¬51X$XœÑ¡)ät^ZÏPΨ¥ÞŒýM´Qb¸páGûë×;k8ƒE šÐ¤×3,.¾ãúÙãQÈÎ~‡4M}’ƼW,,ÎèÐäÁé¤]êÊØïÐD%žrR“ì+Ù³¶c°¨Á‚pJÊÒ1nšÎ!Þ…ìBg§XNL¯RÂ`¥¥¥% šÐ~:¹Çé3²74|Â~‡&Ú(ÅS,'¦W)c°¨Á¢&MÑQ®Ä›?ÿ‘ãÌÕÊ•ß8âj!ö;ô é¾üòg ï òò¾sdz…Á"ÁBš"šÎÁ)ëŽÉeØÒ‰'²ý=hŠ<ùNô ™SfmOYƒ•žž®é{Ü[·nUÃÃÃÔ`Ah‘¯¾z)©Ó9,]z?n3²Cíá¦M—SnÍAŠÜÃï÷kƒU]]ÔX4Jkœ1È_·o÷õõyJl_¹rÅSzä¯A'}¾cÇ.©•+%¡Æâk}vì´ïCö;úôÐßý°}åÊ5µ`Áw é¶lùÊÑíå9ƒ5U!ûØØ˜ÊÈÈ  MhŠ2ßT"¦spúŒììwèASrR,§§W)9MÃÐК5k5XhBSŒ”á:™Êë…ììwèA“õZ,¹Ê7žkóæÏßNž7X¥¥¥ª««K'Wb®êëëUCC5XÚ@¹zG¦s°3Í’×’ÎÓ)…ìÂè&-Ž—¹’é ÜÐ?xÞ`µµµ©’’5mÚ45{ölµ}ûv®"äŒMq˜ÎA.—NµBvö;ô )ôÉW¼R,™ÔÔ íÄLîÔ`¡ M¶MçP^þuÔ¦$a2´@¡ =ÞÐ$S(Øm®d• ·ô,,4¡ÉöÉ­Lç É×¶mÓFhBÇ4IŠe÷Ô.É^½ƒÅZ„&•mmjÙ²»²KòÅw!µX^©½Â`‘`¡ Mq¥t„’f«Ã³Z©£p{!;ûzО‡Ÿ²-År[ÒÁ¢ MhŠ+[[ߟfÉÿû÷@¡ =)¢iýúØS¬‚‚!×a°H°Ð„¦„pÓ¦>=\à¥éØïЃ¦Ä\Qè–+1XÔ`A!„ ãÚµ×cš¾Å'f,,4¡ =hb¿C“c¯(ܱã‚+Û ƒE šÐ„4±ß¡)î\³¦7%j¯0X$XhBzÐÄ~‡¦„Q¦dÉÎþÞÓWb°¨Á‚B“`:nz¾ö ƒE‚…&4¡MìwhJ(|/âËM³¶c°¨Á¢&MèAûšŸbßqý”.,,4¡ =hb¿C“£R¬×_?çáP 5XBa¹jU_HsUTtÏ#õf,,4¡ =hb¿CS)Kh…J±»1XÔ`1~&4ÑFhBš¢auõI檬Ìï¡+&1X$XhBzÐÄ~‡¦S}Ÿ3gÌsµWž4Xâêg̘1á¶±±1U__¯jjj´yRƒ!„&™••·ÆÍUIÉ §´yÆ`¥¥¥3ª¹¹y|»©©IÕÕÕ‘`¡ MèAûš’Ì7ßìO±š›?Ä`9fƒUPP ü~ÿøv¿ÊÉÉ¡ MhBšØïÐä+ ËËz®Ï¬Ç­mîs[m£”0Xª¹¹y|»©©IÕÕÕ¹Þ`y¡<7·Y(Mnm³ÒÒRÕÕÕ¥;—ÑÑQµk×.µdÉ×¶S8=^h#¡˜È¼¼·Õ6J ƒÌ‘zéLUµìÕÕÕži#/´Y¸ÏëÖ6;qâ„Z´hÑ„š%7·“YÛÛÈš–Ú»wïz¢‚irk;­\¹R}ñÅAÛÁíNú;óç¶ÚF$Xh’bP¯,/&Xno³íÛ·«U«VM(êts;Óã…ãjddDˆ/_¾Ü3Ç’Y“[Û)\µÛÉJ!»[£?7 VF˜9[ÍÊÊò”Æ¡¡!5kÖ,Ï,/´ÙTËMm&E¬Rpì•c+”/WÆ‚—ú¿P?Ðnm§À>Âë}ž[Û(ðs[m£”0X{öìÑÕþäÿÚÚZWk ¼ÂFv¹t´¡¡Á3Ë mfÖäÖ6ëìì ™ò¸±Âéqk•••©îîný¹=z¤ë•$só±N“Wú¿À>Âk}ž[Û(Üç¶ÚF̃åR´µµé+7¦M›¦fÏž­‡;Üj¬ì˜oÄ šÜÚfvÎ ãt=nm#™ƒH~¤0wæÌ™úÒqùqpó±N“—ú?/ýNêqk…ûÜ̃d`°0X, À``°0Xƒ€Á"Ü"¶€Á¸ï¼óŽ^luÓ¦MAï—%Tä~y\ Z[[µ)ª®®ú¼ÊÊJ}ÿ¾}û0X  õ°eËmpÞxã ·Ëâ«rûÖ­['=GÖ­“uì„###î“ŃûîÞ½‹Á`°© YXULÎéÓ§õvGG‡Þ–ÛCaÕªUú1GŽ™pû[o½¥o—ËÀáÇõ"¯bºf̘¡_×ï÷O2X¡Ì–ùöãÇ« è×›>}ºÚ¸q£zðà  œI¡–/_®233Õôߢ¢"5<<ò9§NÒ¦§¢¢bÂí¥¥¥úöÎÎÎñÛÄ\õôô¨±±1=l(÷ËmѬ3gÎèÿÅàÉëµ´´èíÚÚZ 8 2œ—““£ÍJvv¶ ûx17RŸ%)Ò½{÷ômjÚ´iúv¹?Ôóä1ò¼h Vqq±þ¿··wüõd[’, 1TV –@Šãåñmmmz[ê¸d;°h^굤¾¼¼\=ñÄ: eª"1XãÛf0XàC„b^Ä$E2D(¸zõª662,(X²d‰Þ¾|ùòøcÄ\Ém»víÒ·ŽŽÆd°$ý’ÿC%d 8«W¯Ö¦åرczûĉz[nŸ ùùùÚô\¸pA?G¶!CfÓÊTé”Ù<>¦°°PÿßÝÝMÀÁg˜¦A¦e„1Ü'÷‡ƒñ¸¼¼<ý·¹¹yÂýóæÍ›`ˆŒ+ƒ,)˜7¦Œ“uãÆ =Wàc¤x^þ_´h‘º}û¶¾íâÅ‹Úx0XtЉ ©¥ 6Ñh ¤V˶“¿RèF3d¶Ë¼Z¡ –¼–Lá I–P†/]º4iè°««K_…(‘×={vD“š0Xƒ€ÁÀ``° ƒ€Á,  `°ðèû£Á"ƒHÁ´5ƒàGÐÖ`°üèÚ€Á€]@[0X~tm  àü݇—%c°À`’ý£{ñ¢RË—+UXø›ÿCàÞ½{jãÆjúôé*##ãñS–«={öXþiii¶êêééQ3f̰ôœ%;NsKK‹*))ÑŸE>SMMêïïÇ`€Á8Ê`mÛ¦Tv¶R>ß”ÛFG'=ô™gžQïUcccêÂ… ú¶dBŒ‹ÁHaA²ã4—––ª®®.ýYä3íÚµK-Y²ƒ àƒå÷Ot¼ukÒóÓÓÓõ{(üÏÿüª®®Vk×®UUUUjpppÜ=xð౩Éßžê9§Nzü1|:©‘û#1Z‘À¢dGküŒ,0X§¬sçB»®®IÏ/..VÛ¶mSÝÝÝ“L‡¤<;vìß>~ü¸Z¿~ý¸¹8þü$3î93gÎT½½½ú}öíÛg›Á²(ÙÑšgÏžÕ© À)ëW¿ í6öïŸô|IZÄ üÛ¿ý›NMÊËËÕÍ›7õ}YYYjhhhü±bŒº(³ù1¶Ã=ç§?ý©zÿý÷#Ö©Á²(ÙÑšOœ8¡-ZD ,€£ Ö–-¡ÝF}}Ø×’¤EêäÞ0ÁÎl„{ÎÅ‹UYY™zâ‰'Ô¡C‡l3X1Hv”æíÛ·«U«V/b°À`œb°**B»òòˆ^Sê…’¾„ªU e6Â=ÇÀíÛ·Uff¦mËÉI×,C]ÁÆ31X`°0X/¼ TUUpÊ}&ÈÕsF-’Pê‰$EHávSS“¾²M S'Ö#3áž#uJR$.·Iá·]Ë¢dÇiîìì ™Za°À`œ`°,¢µµU}K-’¤82Sམ©1’û‹ŠŠôZ8³î9Ï?ÿ¼~I|NŸ>ÖXn³ NÓnˆƒ àBƒhk À.m À`øÑ´5,?º€¶`°àGÐÖ ÀÖ]˜:`°RÿÑK*)*¤GùIEND®B`‚StackedXYAreaRendererSample.png000066400000000000000000000415401463604235500355730ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/xy/doc-files‰PNG  IHDRXr5˜C'IDATxÚíl×ïûTõí[u«¾J«UßjߪZiµ»í®V«]­vWUeY¶ìG…œu4,ŽâB v\;ȉeJ@vhœµkDÇÅI±q'¨ uCº8€ ®ƒ…&†%€]{¡1ààø<ÿN:îx¥B!„PBõ)º!„Bƒ…B!„ÁB!„Â`!„B! B®?òO}jéþ¹(ss!  ¹†‡‡Õƒ>¨þâ/þBýÁüúÌg>£>÷¹Ï©¬¬,µzõjuèÐ! –Ëç.]ºtÎýó7£>þøãÙÇ'''Õ_ýÕ_ÍyΟþéŸÎ¹ý•¯|eÞçÉûXŸ³xñâyÏ9{öì¼vÉ}a1*&Ÿþô§Õþᪿû»¿SÕÕÕêúõë¬ô½ìëŸÿüçU^^žª««SW¯^M»±íßÿýß1ǃ…üÑO<ázà‹40eZ$Éís¯\¹¢RÖÇä`ejÆ s“ç;vL› ëý;wîœ}Mggç<òÞ{ïÍk“ý½íŸƒeçOþäOÔ;#ÁJqßËIUwwwZŒkb‰>" òG---1ô0XîŸûŸÿùŸó"bˆ$š$ÿ[Û¶m›~Í7¿ùÍ9÷ÿå_þåläËñ’¨Ž“ìQ.A¢@a7XBNN˧¾»Éò2Ž!„ÁBI‘ý@¾eË511¡“©­7ß|SGIþüÏÿƒåsÿùŸÿyÎã_ûÚ×ô«õ>yŽ©ÑÑQ1°>.†×½ú⿨nܸ1ïó¬Óƒá²FÄ‚:MèÖ‡ò[ûÞ÷¾7Ϥb°’ÿ$R(ÓÏÖǾð…/èß' ! ŠSöiªXs0¢ `¥¥¥Ú`ˆ|.9XþÙŸý™ÎW:~ü¸ã{^¸pA­Y³Fýýßÿ½ÎǑ׉¹×¼úê«Q_ýêWçÜ/m0uêÔ)c&FQÞWÞ_òž¾õ­o©ßþö·óÚòío[ç£Éså52ím þùÏ>¯?íÓ|ò«æ”˜këcÙÙÙs^»Ð¶‰Ùˆ–¯϶_h»’±œÞ3ÚïWÌŠý=âùŽN9½FN.þñÿÑñ1‰äš’ñ(Ò¶ÇÓ=úˆ0X(„’ÜžH—û·S[·n5+±\r€—ƒ«œ¡Ê¬ýs$JeJ®`´ÞüÇ<{Õâùóçõ6’Á˜— }ùòeýÜ·ÞzkÞ`,m’ïä4HïÞ½{ÞAF"Ù‹e ¾}û¶£a’ƒ»<æ$1—nÛÀíêMûÕƒbdz7ÒÕ„ÑLƒ×¾òºciƒ5z5888ûºxÚËçX¯ÞŒgÛ'£]ÉxÏXŒ†l?§¢×öxÉõж]ì¦O"òÙò×z¿5ÂÁB,I”Ê40‘©ýÒy/—}úGÂü¦î»ï¾9™ÓÖ(õlÚþ¹2ea½Ý××7ûÜûï¿Þt…S{ĉ \ß+ÖïkÏ%$ >’þå_þeÞk¤-n²_=¸gÏmt"Egìï/¹^Ö(¥×¾òºc=øÊû>|xÎëâi›ý}ÅÈKN¡[ûâÙö‰hW"¶C´÷Œõ÷ëÔ7^Ûcÿœ}ûö9ØH™ùwö ² E2Õ­„  a°P $Q¤dPu;ð-_¾<æKÎú%DH‰ÞÈÙ°Ûóí%œºc=HÛ§á$Y7–ƒºy0±'[ëY-$‚%y'æÉI­±¿Æ©,ƒ5rdÏ“)šHÓ‹ÑÚﵯ¼nçX –Sy†xÚæÔ»)°¶/žmŸ¨v%ã»z5ö–ôG<íqûœH}ïö˜Û6±?ßl+ a°P`%—$'Ký»QÄàX.y½Ý4EÊc±›º¨?òï) ×VEÊqjýù^êHù"uòò½ÜäT\Ô ë4a´÷÷ÚW^·³Sd Ξ(ÆÐ>êµm‘¾¯ÛýñlûD¶+™ïËsdšÍ)Îk{¼lÿhE"EJ1Xƒ…|—ÌÜ.é—)A/Ó=¦ìå ¤©”~ˆ5r)Òãô¹ö€µ†ý½rɬ²'Ú{¨ã¹Š0žSqQ7¬Ó„ÑÞßk_yÝÎnmøÉO~2ï~¹Zm!m‹Ç`ųíÙ®d¾g,ϱ'ÆKN_<íI¤Á"‚…0X(¼?˜ßøW¬X¡Ï`Ms#ùö¤ik §Hgüngnݽ÷ÞëZÑÜ”õŠ2ûûØ«%'ËŒ€ÄòÞb ¤^•Èž e®Š6PÛ¯ˆ’÷´×Á’‹z °OFË‹õý½ö•×íé1§«/%(Þ¶Åc°âÙö‰lW2ß3Òsä{Ú?S¢Šf—×ö$Ò`Ùs°¤T„S—,„ÁB4X±"‰¨VÙ×Ó;zô¨¾ßž³!¸=¹Ø>¥h¿ ÐL0–«ÍKçÝÚ,ÓLöÏ4§ãœÞ[òÍä,\΂å*=ó2r‘½ÜƒÔy’çI2n¤ZjUÙ£}’C%Ø##n ﱤ/íÓ¶Ö\!ùß~ÖoF(£½¿×¾òº#µAÞßnpä{˜yh^ÛÁŠgÛ'²]É|O¯ûúÁƒãnO" –}ÚR®E»Š0ÞÕ)Â`¡„É~0v›Ú’K²írË7ŠTúÁmpkkk[P,{ÁNy/)ñ êèèˆúÞæûÈY»ýªÄh푵íým-B§‚®± þO>ùäœçHí+»ì‹O›m‰åý½ôU<Û9Òc/^œgÚäÀmF#½´-ƒ϶Od»’ýž±|/é{í2¯íI¤ÁŠ4ÎDªƒ…ÁB,ä»dJPÎF¥T‚$µ›WÉ_™”â¦Q±KÎ`å k&)›Ušå~©’mÈò¸ýLÔip“h…¼îþáfÛ!fD"XÖŠÍNï#Ÿi/$i6*ñÒVùNòÞb¥}2…&‘ kÕq‰šÉ%ûfûå}ò„LÙ‹SÊú€Ö<écûšNÆ(–Á_êJ¹M£™²×Õ2kQÅzp‰µ¯âÙÎÑÚ`_&Èžåe;ÆcF¼nûD¶+Ùïév埘*ÙW¤¢z¤åqbmO¢ –É’íb­ä.·ã©äŽÁB,„B! B!„ !„Ba°B!„0X!„B,„B!„ÁB!„Â`!„Ba°B!„0XþJÖ¬«¨¨P¹¹¹*//OÕÔÔ¨K—.éÇzzzTee¥~,??_Õ×׫‘‘Ç÷1˵ƒB!”qK–ÏTÓÓÓ1UÅÅÅ󛚚Ríííª¬¬ÌÕ`!„Ba°\”““ãù1 B!„0X’(ÕÞ½{õ”¡“†††tTËÍ`‰ùdAÝ––½p.B!„PÆ,3gjÆ êÆóïïïW%%%®9XVÉ*ïb°êêêæ=&ïob•˜ºG}T­ZµJ=øàƒú¯_·M‚ÐÉó»?ä¶´ƒíÃöaû°}Ø>l?nK[BÁº{÷®Nz_½zõœûÛÚÚÔÚµkÕøøxÌï%y[’ï&»ÁZ·núÉO~.\¸@[è¶m¡OØ>´% mY¿~}¸ –)«1:yò¤Nt÷ª‰‰ UPP³ÁúÖ·¾˜ )ß™¶Ð'lÚBŸ°}h +nUWW«ááaqºsçŽÎÁ’h•h`` bÔÊšØn½âPÌUss³jmmÙ`ÉgeCÀO0X ÑÁƒµ9’äôÅ‹«¦¦&m¬yYnµ­¬ÿ÷ööê9ÒììlUXX¨§#‰gx´…>aûÐÚA[2jŠ0"‹ÚBŸ°}h í -¬$,"XœáÑú„íC[èÚ‚ÁJ°Á" 0XD°8ã-lÚBŸ°}h ‹,æÀé¶m¡Oh }B,\;gx´…>aûÐú„‹,H>Ùu¡0XD°8ƒ Oâ4XNmñÓ`±}Øh }B ƒE9 ¡n‹ÕHYÛâ§Ábû°ÿÐú„, ,ÎðÒ"‚µmÛ»ª¶ö†êí ‚E[è¶mÁ`‘ƒ°Pƒµ}û/ÔüÇ´2 ¥ÿnÚt, ,Î è“…¬¥K?ÖæÊŠÜ¿sçÛlÚBŸ°}h ‹,æÀé/tt G¼ŠPŒÖúõWÔÁƒo²}h }Âö¡-,"XœAÐ'Ñèé9¡Š‹?š¹râþûïê-¶m¡OØ>´%C ÖîÝ»UEE…ÊÍÍUyyyª¦¦F]ºtI?6==­š››U}}½6DÂøø¸ãûxy.9X6öì9®V¬¸“¹²RU5¦}a«ªªJ jƒ$ôôôÌœ¥ëÇ:::Tww÷ìs»ººTSS“ãûxy.,ÎðÂÔ–Žª¼åÙ\™HüÆÕë¯fû°ÿжmÉä)œœý·´´TŽŽÎÞ?22¢ŠŠŠ_ãå¹ä`‘£–¶H.ÕªU7ã6WVxàŽÚ±ãçlöÚÂö¡-™f°¦¦¦ÔÞ½{õ”¡H¦ íÓ€öûLyy.,ÎðÂЉ8%Ê\Y©­½¦£blöÚÂö¡-`°²²²4b|nܸ1{ŸÓóÜ^Ë}f~–“Á·lnPùËmnûu{hhxæ7y+áæÊ¤¨hZµ·éÏ¡¿¹Ímns;òíÐG°îÞ½«“ÞW¯^M‹¶dtŸ<öØHÒÌ••‡úPíÚu’íÃþC[hmÉ„,¹¢PT^^®ÆÆÆæäU-Y²Äñ5^žK9 AnË“O^J‰¹²²aïT;‹ß,ûm¡OÈÁ ˜Áª®®VÃÃÃ:âtç΃%%Dúj@땎S€ÑžK‹3¼0´E®öKµ¹2Y¶lRµ¶žaû°ÿÐú„¶¤ƒÁ:xð .Õ W.^¼X—V˜˜˜ˆ©¶•Õ`Q ÂÎ÷¾wÎ7see͚ߨ}û~Ê6H·)B*¹s‘i}"U×ÍÅ›ƒ€¬uøÝïþR:ô_löÚBŸÁÂ`±!9 ákËöí¿”¹²'Á¿ðÂ[üfÙh }B‹gxáiËsϬ¹²²nÝkgñ›eÿ¡-ô , 9X::†õT\ÐÍ•}éX§ ÈÁÂ`Áâ /¥méíPÅÅ…Æ\Yyä‘óæ7ËþC[è"X,r°ÈQðµ-û÷[ÐâÍA@¦5ŸzêWª¯¯Ÿß,ûm¡OÈÁÂ`Áâ Ïß¶¤ƒ¹²²bÅmµsçÛüfÙh }B ƒEøƒTJøáߦ¹¢vƒ…Á"‚Åž¯msµjÕÍ´4WñÖÎâ·BŸÐú„9XÌÓŽµ%U‹7‡©v¿ú„¶Ð'ä`Áâ ‚vÄÝ–õë¯dŒ¹JäÒì?ô Û‡¶`°ÈÁpDLF&š«D, €Á"‚Å}2M›.d´¹²RY9>¯v¿öÚBŸÁJ¢zzzfßJ•››«òóóU}}½Ñeee9â$/Ï%‹…d·s½v¿öÚBŸƒ•DUUU©ÁÁA5==­¦¦¦T{{»*++s|îèè¨Z¾|¹«Á"‚Å^Úò½ïÃPÅP;‹ß ûm¡Oˆ`¥X999Ž÷766Μýö%Å`‘ƒ‰ lë úImíµˆ H`°¨¡¡!Õ²ëÊ•+jåÊ•®¯ƒ%ÆL0fFï––599I‹3¼”µEJ`®ü_@šý‡>aûÐ –Mýýýª¤¤d6˪††ýx,’©D1XuuuŽÆÊĪ'žxBÏ÷šTþúuÛþ×Ïö\½zÕ÷þÛÒŽ oŸƒßQË—c®âåÑG?TÇÿšý'C÷¶Û'ÈÛGþ†Ú`µµµéiºñññyÉuËËr“ätIâ<,Îð’Ý–={Ž«ââ0J H‚òÉKêõ׳ÿ0¦°}h ¬DH:QÝÝ$WøÉÔ¡MLL¨‚‚r° ©HQ:-ÞxàŽzî¹Óü¾ƒµ 8F­L?^UTTDMl·^(檹¹Yµ¶¶Áâ /imÉ„õýN‚giöú„íC[0X1Ô¯ãtæÌ™¨«··W×ÓÊÎÎV………zÊ‘µ©“¬¶`®R·€tSÓyOÓ†ì?ô Û‡¶`°¨äÎDûdppxÆøa€R¼€tw÷ÏØSØ>´ƒÅZ„®TWbz|L‚÷ci  ƒE‹3¼$’é‹7eé;~îZ;‹ý‡>aûÐ V€ 9Xä(Dcóæ÷18bÍšß8&Á³ÿ0¦°}h ‹g!iÇöí¿ÐST›à%Á‹ñµ&Á³ÿ0¦°}h ‹,²x3æ*øIðÉ\@ÈÁÂ`Áâ /´¶žÁ\…ˆuë>PÇŽfÿaLaûÐ 9X̵»vT÷Ý7…q ßøÆÇzJ7‘ H³ÿÐú„, ,Îð@OÏ Ö 9RV¶#ûc Û‡¶`°ÈÁ‚°ÿ1µbÅmLJšÔÎzê©_©¾¾~~Û€Á"‚Å„_íèí`ñæ4D¶©IðDHh }B‹,r°2>GáÀ£D®Òœššëêµ×ްÿжmÁ`Á¢-©h‡L!±xsfpÿýwué öÚÂö¡-,r° ‰ÈÕfRó‘yIð±. ä`…Î`õôô¨ÊÊJ•››«òóóU}}½Ñeee9â¤ééiÕÜܬ_/æI'‚Å^TsU[{ ÑÁH|²&BB[è"X¾©ªªJ jƒ455¥ÚÛÛUYYÙ¬ÁŠU3g£Ý³·»ººTSS9Xä(D„Å›Á\@ú¹çN³ÿÐú„¶¤÷aNNŽgƒUZZªFGGgoK¬¨¨ˆgx®<ùä%ÌÌ¡²r\_IÊþC[èÚ’vkhhHGµLƒ%fK0fF¿––599éøº¼¼¼yS†öûLceb7Xâ–Í *¹¾·¿ÿýËjéR ÌGª÷oÝzU;÷+öns›Ûúoè V¿*))™ÍÁ²J¢Sb°êêê_ëíŠ#‚•¹gxÏ>{–õ!*?ü[½\ûm¡OhK¨ V[[›¾’/RbºD¥$~!,r°2;GÅ›!žÚYR#ý‡¶Ð'ä`…Î`‰K•D÷hš˜˜PŽ•——«±±±99XK–,!‚ÅÞ,’ļtéǘˆsÚð="$´…>!‚ƒ500൲^a(æJÊ0´¶¶:Nvvvê+­W666R 4/¼ð–.0‰Y€…ðÈ#7ÔË/²OP+ø+R­«ÞÞ^]#+;;[êiD·+ê`q†ç†‹‹? @ÂÞ¸ñbLµ³ˆÐú„k’ƒ•–9 {öÇ\AÒjg=ÿü)r|h }B‹VfáIbòC}ˆ€¤"Ë,íߌ m¡Oˆ`a°ÈÁJdúF.³Ç@ª’à7mº ^ý0û9X,"Xéy†××ׯòåÀ©æÁo©ŽŽa"$´…>!‚…Á"+½r$‚ Ëp°?Y·îõÚkGÈñ¡-ô 9X,"Xá?Ã;tè¿Tuõ(xRdç΋ìÇ´…>!‚EóÌaŸ#¿ÂGEÅ«žžì£ä`Áµ‡ï oÆ_s0‡@×Îzúiÿ’àSˆ`Ñ 9XÌ{nÇæÍïs‡P°bÅí¨µ³Sßh 9XD°8ƒð½²>nãÒnµ³Sßh ,r°ÀWvî|[׆a^@Z.Î` ‹gh‡ÔZºôcÔz¤ ®,FÎ˜ÂøF[ˆ`‘ƒÅ¸¯íÅ›‰\Aº!jIJ€4c 9X´ƒ“zzzTee¥ÊÍÍUùùùª¾¾^ŒŒD}Ì®¬¬,Gˆ`¥×ž˜+o†t^@ºµõ c ,ÚBk᪪ªRƒƒƒjzzZMMM©öövUVVõ1'ƒEVzÓÛ; xàbȈ¤÷íû)û=+±ÊÉÉñüØB ¬`·åðáŸëuÞ8øB¦ 9†ßýî/’ϘB‹¶`°ÔÐÐŽ\y}L –˜/Á˜ZZZÔää$9XiÐÉKùæ7'9èBFòÐCª]»N2¦ƒE[0Xñ«¿¿_•””8æYEzÌ®ÑÑQm°êêꕉÝ`ÉÆ4³üõë¶IÚsîÜ9_?ÿøñ·ÕªU79ÐBƳqã‡êÍ7Oǵ?É~Ìø¼ñíŽí#Cm°ÚÚÚtÔøø¸§ÇÜ$y[’OVx‘©ÉEáà ðû¤·m{—ÚYD°b“¸CIf÷úX$MLL¨‚‚r°BÜ–êêQª<òÈ O H3¦ƒE[2Ð` ¸F¦"=fOl·^q(檹¹Yµ¶¶’ƒÒ¶°¾ @ô¤ŸzêWª¯¯Ÿ1…,Ú‚ÁòV¿*Zm+ëÿ½½½ºfVvv¶*,,ÔÓŠ\EζlÚt(€‡¤£ÕÎ"*A‹¶p!kf8,Þ @í,r°X‹3ˆ²}û/ô´K€ÄÖÎ"*A‹¶`°X‹0CçÀwî|sÀÚY֤ɫ!‹¶`°ˆ`eà„äÌ›#@r&*A‹¶`°ÈÁÊ0äRsoHîÒ2ýNí, ¬ 9ƒ`ñf€àÖÎb|£Oh ‹¬¶eÿþcúòr|Á¬ÅøFŸÐ ¬µåÀ£˜+ŸkgÉ…%ŒoD°h ‹¬4áõ×ë© rþS[{MŸð06`°ˆ`…¸-’dËú‚Á[@º©é|Ê’à‰Ð'D°ÈÁbÞ9ÁmY»ö*4€'ÁwwÿŒñ1Ÿ¶`°ˆ`…©-O>y‰ƒ@X¿þŠ®ÅøÆ˜O[0Xä`œ/ràR›î¹çN3~„Ù`õôô¨ÊÊJ•››«òóóU}}½ÑMOO«ææf}Ÿ"a||Üñ}¼<—VêÚòôÓ8`„”ÇIøÒDHè"X)RUU•ÔijjJµ··«²²2ýXGG‡êîîž}nWW—jjjr|/Ï%+5m‘ÄYR᯵yóû K‚'LJ>!ËGåääè¿¥¥¥jtttö~‰l9¾ÆËs‰`%¿-Ï?ŠÅ›ÒˆU«n&$ ž }BË' 騖(//oÞ4 ý>S^žKVrÙ±ãç,Þ @<9XAQ¿*))™ÍÁÊÊÊš÷§û¼<×ÌÏrš"”p¤é˜å¯_·M‚ОsçÎyzþ«¯¾«î»oŠ@óoÜU»výfæ„x8£Æ·dÝ–vpü îö‘¿¡6Xmmm:ŠdMLOe‹¬…·…Å›2‹5k~ãyir|èr°R(q‡’ènWyy¹›“WµdÉÇ÷ðò\r°ß¹ÒhÙ²I:š/Ë`‘ãCÞ9X2X®å:;;õÕ€Ö+§£=—¬ä!ù=ô!€ F¢×»vdLr°‚b°Ä$9Km+«Á¢–?m‘…bKK'8À€fݺÔþýLj5"‚E%wr°âm‹ÔÅ©¬ç ó~æ™wkg‘ãCÞ9X¬EˆkÐ8åL•ƒ ¸ñðÿU{öÿÝáÆÆZ¢FD°X‹,ÔÖ^ã1%ÁË᦯¯Þ8â·ÁÀ`Á T[žzêW8 fäpóàƒ·tbë˜B‹¨,r°˜ÿÛ¶½Ë8àÙ`™ÿó›“z‘²~,r°hK  Ö­[·t9„üüü9WõI]ªÝ»wÁJ£3ˆ­[ßÃ\À‚ –µ¬ƒÜéŠC"X´%£ ÖæÍ›ç•W>}Z’ƒ•&Èâͬ/‰2XæýrÒ&9ÔÐr°l’ÈUOO®Ce_û/;;›VœA°x3,Ô`¹a}žÔÔûîw™²Å¤‰`Ñ–@¬œœm®ì…?¯]»¦rssÉÁ ù¼óÑ£WtM*ä„NÊÀ¼øâ9X´%s ÖÊ•+õôÚ¥K—´Á³5<<¬JJJTee%¬»vY_påÊðÀ7yä†Ú¾ý1¯uH‹¶¤Á:|ø°ër7N 8“ƒz{t*<¥:ü† ¿Ö…K£!cÊ4œ8qBUTTè)AÉ»’+egê`…Óµ¿öÚo€À"Ktµ¶žq\Їm¡9XœwÆ\@X(û¦Mâ.õ@m ´Ár›´—m ‚|×.gƒÿÍÀ ¡[Žg͚ߨ;ßöÕ"‚E[2Â`‰S]´hQLïí¥ä`Ån®Ö®½Ê` ¡F–äIe© +åS„MMMêìÙ³žMZ$ŽŽªåË—»¾¬ø©©¹Îà iUê!ZS"X´%”ëòå˪ªªÊs4,’dIž¾¾¾¤¬LÎÁzúé ȶHSYÿÐÕ"‹¶„Ò`I=,¯…F#™¤+W®èš[‘^+EOcfjiiQ“““D°¢°yóû À‘¥ˆ`Ñ–Ð,1W/½ôRB VCCƒêïïé}d*Q V]]£±2±,qËæ•¿é~{×®ß0è@F²f͵sçE]À4Æ{n/ìvà’Üí&&^ƒ%_´¬¬,¡´L`=ûìY}å -dzTkãÆ‹º¸2‘Úø«ÅØÔÖÖªñññ„,Éòô^ª  €,äRfÌÀ\ª«GUGÇð‚ ˜’÷DVà :¬óçÏë*ñÑž/ õ²4D®Ä\577«ÖÖV"X6º»¦î»oŠÁ BS)õ …—‰Ñ–”¬hµ¯¼ÖÁŠô:1NgΜ‰j°z{{õâÒ²TOaa¡jkkc-BÌÀ‚J=¬[÷;©E¬P,*¹'ßµK~AqñG š Xÿ°¯¯Ÿ¨,Ö"¤Ö'È%ÉË–M2H0)ž¼'r°\K"ÈԜԟ"‚<×.‹ ®Xq› Á<ö؈zá…·ˆÁJŽÁ£"FJrŸÂl°Ò1Kª?ôЇ „INŠß´é‚:pà(ùJä`%Î`ÉÍ»wïÖWïååå©©©)}ÿ¡C‡t±O"Xþ¸v)žWQñß ~)LŠ_¿þŠzùåAO¥ˆÁr”L йÝsÏ=ê½÷Þ›ó9XþÌ;³x3€ÿëÆ’OÞ9XŽ’ü+éQMMjoo×ÿëiC"X©wírÅœ¤x‰j5"‚åI¯½öš6Y"YûÏšURRBVŠ‘…LÔ‚Ç#ÜÐË”I yMä`y^žFL–L .[¶LWa'‚•:×.I– bÁFÊæÈx-%tˆÁš'Il¿qãu°2ï¼yóû \!c͚ߨƒ¯¥|ýCr°l°d P"UbLfÝÃj°ÂÁzæ™wX¼9ÃhhèUçÎý“º}ûÔÅ‹«¶ly‰~1>xK¯(åuˆexKŒ•µ¸hAAÚºu뜫Ãd°ÂšƒµsçÛúÒ`¨Ì2Wׯiæ7ü†¾][{L߯d¤G©YÿðŇÈ}Êä¬;wî¨W^yE/Èl-4ºbÅ ÕÝÝ­ÆÆÆˆ`%ѵKaÌUæ!‘+Ó\™ˆÉº|ù¯é€4KŠß¾ý)MŠ'‚À$w1S/½ô’*++›5Z^Ê4È\«-eQi'Éess³ª¯¯×æIOÛ,¹ä—Å›3“?üߪҸ¬5j”©ýÆóªÖxW}ôÑÿ¢Ò´Ôƒ\!n&Å“ƒ•Á‹=Ÿ:ujæà_óR9næÉËR;:jfª««K555¥eK•¥x2rãºúí¹ÿ«>Þ=÷ÚlõñåÏ«ÃF³j2ÞTË –HHG*+ÇUk뙤%ÅÁ  ÁºyóæÌ¿wÎt¡×B£ 1X¥¥¥záiS###ª¨¨(ír°Ä\±xsæQlÜV{Œ]ê¶Q¬TÃW•ºþÙ™qÖ¬¹Ò··üëì äyoÕvãÇjµñ}f˜ëîߌ©t4XwïÞU}}}ª¶¶vN»ä`Éta¤)ºX –¼¯`Ìü¢dmÃÉÉIÇ×Ê:ˆö)Cû}a`½öÚ}¥ ƒK%¼SêYãGjÜ(±eºÏ˜¬s_Pê£O+uùssÌ•ïÕj—±GÕçèW€4B® ¯­½¦ví:I+] –YPÔ~áBŠ‹FŠXItJ –|n¬¯uºÏÌϲ¬'žxBÏ÷šTþúuÛþWV55D®2‰mÆêºQžð7–¼­Ϫ:ãmmàèk€ôàá‡ïèõN%ôøã×í«W¯ú~<6oûRK¦ÖŽ9’:XѦå3rss3.‚%sí=6Â’!¬3ΪsFMJ>L¦%oKÌy[$ÅÁ @kß¾}ziœD*šÁ’Ï“H™“ÊËËç”…¬%K–¤E–ÔDaÐHdúEMK}iÀGÆ}ê´±AOIJ2=Û }’âYÿ0M®"L”Á’„ùÁÁAs%eZ[[ŸßÙÙ©¯´^EØØØúÖúõW$ÒIBÓhòÍX¹ñ®Q«ºn]‚íþ¨ÖSOýJíÛ÷S"X™d°Üj]ÉU‰•••újÄÂÂBÕÖÖæjÈÒ±ÖÆÒ™’“\¨ +'$LÚºÁ8­î3¨¿æ¤xYÿPVq*õ@¬4`QÉý÷®Å›ÓÛXIdèCcy(¿€´û c›®·…Ù/æú‡r…:¬¬½{÷¦•Á RÖÖ­ï±ó§iÉI&ŸWr!ÄHÞ–ÔÛ’ïEÞ@HǦ߭˜¨Rä`%à*BYGBxD°»xsQÑ4;}š!ÑžŒÕiÿEÉÛ7åå·Õ3ϼˆ¤øŒ`™KáH~”$ž» ‹Á BVGÇ0‹7§’³$Wæeâ——¼-©>OÞ@øX¶lRWŠïé9AVª –$•8p@—B£%ËÒœ8q‚VœÈâÍr•;vzð¨q1cy[éWêá¹çN'mýC"X.šššÒ‹,ßsÏ=®W’ƒ)W\ÌA']J.ˆ‘ÕAÈÛ*3Fé€PZ:¡/À’5qÉÁJÁ’õ¥æÔâÅ‹Cm°üŠ`±xsú\(Óa+oHI¤¼-€p!«‹¼øâ¬d,©S%‹0›S„R”¬Ø9xðM}FÀÎJÉ o Œ¬ZuS—zã9X 2XË–-›Mrß±c‡ŽdqaìÈ2¯ÍJÉ o ìÜwß”ª«»µR<¬ ,Óʬ¾¾~Ì%ÀcÞë$„ƒªª1õì³gÓjýÔ/öL%÷ø"W²L;!% ~.•zj¶Öx—îx©‡ŽÁÊä¥rR•ƒUSÃÙw¯ ”«ÞèŒàæmÉTâFã-¦ˆ¬X]=ª i“ƒÅZ„IÅ›)¹ÉŸJ|Óh¢@@‘õ›šÎÏYÿVš¬dç`±x3%ÀŸ»Œ=ämíB¡ß­ØÝý3r°’- .Z´hÎ}===ª²²Råææªüü|U__¯FFF\“î½ÔáJeKÖu’);UÀ¯‚1>¢ä% Å<òÈ µ}û/æ%ÅÁJ€Ü QUU•®­%ËòHÅøööv}å¢Û{1«µõ æ*È”WRüC–‹{ê©_Í–z +ÁF+šrrr’b°’ÁbñæàÓ` ª÷j:"Ãó¶ä"†Æ™JHR¼\m¿{÷ù”¯˜±khhHGµÜ^+æKêò---jrrÒ·¬^x s`äò~J.€ïµêe£KUï낲t €¿IñR)~ÿþc¬d¬þþ~URR⚃eÕèè¨6XuuuŽÆÊÄ>E(áHsÎWþÆ{ûå—Õ7¾q—#€ÈAsÐh b⦱BýÈxV—€(6X3À¿¤x9~«]»N.èøïí´5Xmmm:Â4>>ó{IÞ–$ǧ:Ko–âjìÁB.Ùßo<¯§ƒèˆw*ñ°Ñ¬ó¶V7éŸ5|·m{7)ëfTKœc<‹HOLL¨‚‚‚”æ`IsÅ Îrƒ„åÊÀÛF1 /Ñe¼¬ë¥Ñ%þ$ÅoØðkµgÏq –Wƒ5001je}¾õŠC1WÍÍͪµµ5e9Xâ¤zèC~ôA¦s$i™Å˜!È…?4vêÜ>®JH=²¾¯\µŸ¬¤ø´(Ó`-×­¶•õÿÞÞ^]3+;;[êiÅT]E(‹7K~䘧7¦ô‚À£F¾ ÑRÉÛúŽqœ¼-€óÀwôú‡‰NЧ’»u°X¼98H"2% hS‰ouÚôS µ¥jk¯é¤x VH×"|ì±~Ì>ScœSgut„¢„,Ý#S‰t@ê’â¥ÔÃB’â1X)^‹ðÉ'/ñãõI.>fl¡ä„É”©Ä:ãmò¶RRêá“õ_|qƒä‹7û‡LµÈåò+H§oMzÉ&Ylœ.H.nëb°|ÎÁ’ú¬/èÏ•¯;XŒÒÞlI ò¶RSêaãÆ‹º†%ËçֳϞÅ\qe @J󶤖y[ÉMŠ— Ö¬¥>±IŸ€ÁJrÖÎo³¾`Š‘ªÙ×r:`Ù$okƒqš¼-€$!Ã%)^l¬D°X¼9µHâïEãQ:À™*ÃØ¦OB0[‰GlRMÍu}üÇ`%)Ko..fK댳:ÿ„Î o ÀoƒeþÁJBkß¾Ÿ²xs xÔ¸¨ŽßáÊ@€æmU—é Vðr°X¼95%dšc@Þ+"X,Þœ\¤Öœa˺mty[A4X\E˜à,Y¼yÕª›üÀ’TrA– ¡–€ÿy[ouqÓ2c”.ˆ@¨ Ö… Ô¢E‹æÜ7==­š››U}}½6DÂøø¸ãë½<7RKªºVUñƒJ‚±’üc5@äª]³Þ–ì¯t @¬¬¬¬Y¬êèèPÝÝݳ·»ººTSS“ã{xy®“ÁZ¿þSsÂü ‡ä~Pr <Ü4Vè©DÙwé€4ÈÁ²¬ÒÒR5:::{{ddD9¾ÖËsÝ"Xë×_™—Ø 3V”\¿Ù’µ?¿c×ËUÑ%€ÁJƒ•——7oÐ~_<Ïu2XO8^9ÞYm| ®¥3Ò ¹Ú÷m£Ní0^Õû9]¬,ûm·û¼<×Ìϲ¬/y=k”ã”\È .•j±K&o 0XD°#X¬…—\àÊ@€ÌEJ®3¶¨-Æ1G³ÕÐЫÎû'uûö©‹ÿVmÙòÝ,¿ Vyy¹›“WµdÉÇ×zy.‹’ ü¼-©·%'`b®®_ÿÒ̸û†~Jmí1}“,Ÿ Vgg§¾Ðze`cc£ãó£=—«)¹þämÝ>÷ÔÁ çäm‰Éº|ù¯é"À`¥ªLƒµ\C´ÚVVƒµÐ:XÖPrȇŸ™Ý’1D’å?þè3êe£KGÂ%Ú%5¸dù,ªÌ+*¹c°"#% .Î}afÐͲ *ÙJ]þœëkÆD/Õæe-EÉó|Öø‘Úh¼¥±–‹jèZÀ`a°B]r+`A4|U©ëŸý½És%··üë‚ÞW–û&'RF®h$, % ³L–D²>úô'‘«š+/H4ì]£–h`°0X”\H% »n”k#&Ñ01bf4LrOň Ã`a°0Xq](gt+€ø¦%1b, k20Pr ñFL¦%%ÝBŒ˜œÄ’†ÁÂ`e²¼…„ÃÙ{ü!ÒÕ’bÄÈÃ`a°Bveà Ñ@;@óøZƒ…Á eƨ^¾cYÑ0®–Ä`a°’te œñÈb¬ì)DìѰÆ«³Iú2ÃQlܦ«0X¬HÈNBÉðŠÌtˆ{ߨÖFìÇÆv}<±^-)WŸc°0Xg°ŒA® €¤1Y[Ò^¶b‹q,­Œ ƒ¥Õ9£†cÂF2mÄŽßÑe+$¶Ýø±6ba¸Zƒ•Á«Úx_ÿpÙ™ òÂTM?í VVV–# }n:,IH”"W@¦_-™èhXCC¯:wîŸÒ?‚5::ª–/_îj°2)‚%WþÐØ‰±ˆájI¯Ñ01WׯiÆ/¼‘þ«±±Qõõõe´Áb1f€ÄEÃä‚°³Æ:]'Ò\Òh›ñ†ºrî+ªyÃnmÄÒÚ`]¹rE­\¹2âtbNNŽÆ˜é´––599éh¬LÂf°šÃꦱ‚ Ù|ø™ÙÿÓÚ`544¨þþþ˜§Å`ÕÕÕ¥E‹+R̹/̘…¬ô6X.\Peeež^3==­rssCm°äÊ@ [òCHutã«J]ÿ¬6Yik°Ö­[§†††<½fbbB„Ò`=j\TÇŒ-$°øm²Î}!= ÖùóçUEEEÔÄöªª*588¨#Wb®š››Ukkk¨ Ö ã¦^ŽcÒÒ`‰q:sæLTƒÕÛÛ«*++Uvv¶*,,Tmmm¡¹Š+0XTrO²>“ÔåÀX`°0X 0VRcƒÅ˜0X¬ deÁK~°, Ö‘õ‘Nø¡`°0X Ec~ÃØÆ•, ÖB‘Õ¼1V, %ƒƒ%WÊ Ü+ k‹’ , V %0X¬,J.`°0X 2X”\À`a°d°äÊÀÏb¬0Xá7XYYYŽ8izzZ577«úúzmž„ñññ,J.@Z¬XÕÑÑ¡º»»gowuu©¦¦¦˜ ÖÚµÿS]¼ø·jË–—(¹,Qii©½=22¢ŠŠŠ’Šq×­-©{ÝÚ‘êq7Ò¸áu¬Å`È`Ɇ:räH ûI*â÷õõ¥üså,B~èVåääøÒö3+9sIö2–ßlii©ˆ­gžEEE¾ï?~@£)¿ ´Ã­-=“3ô[·n¥tùm°Ü>×Ïq7Z¤rܵ·Å¯±×ÞŽ Œ»æ÷ö:Öb°f6¦tž`†jiiQ“““¾´E6”ìì–”¬¬àŠ©žh’°ðÊ•+}ùl9Û—³•¨k×®©C‡©§Ÿ~Ú—¶dggÏpdò{Pös‘ó0,9 –³S?Û!gÄ{÷îÕS†~ô‰Œ'Ö5[ý6X~Œ½övø9îFêÿT»ö¶ø5öÚÛá÷¸k7¼Žµ$¹[$ÎTvòºº:ßàÖФü¸%_Ão544¨þþ~_>[vìÕ«Wë[aÉ ¸yó¦/m‘m!áaÙ6²Nœ8ƒå6M‡Áú½ä÷[RR’ô¬Hí0§?$oC¦:üè“5kÖè|#?¶Q¤ÏJåØët÷kÜÔ'©wímñkìµ·ÃÏq×>nxk1X6Éôã iîèAi‹)I:LfÞJ4ÉÙöùóçgoËŽ&Ó~HÎje1“%|ÿý÷ûn°ˆ`Eþ¼¶¶¶ÀD%îÞ½«sYäÀå—÷ëâ•hŸ“ªñÎÉ`ù5îºõ‰㮽-~½NQW?Æ]§qƒÖ511¡ |ùlI ´Îþõ¯ûÚòÃ6 ýÓœ¿_9Xv è+Jü6XÖ)‘œm-Y²ƒ¥>Iæ–èDŒžU~ÀýØFÑ>+Uc¯½~Ž»n}âǸko‹_co´ßI*Æ]·qÃëX›ñËzÅ€ìà²áZ[[}i‹\‘ —£šŽYv0?J˜’³—T劸I. –Pµy¶`žUø­Ó§Oë«lR1åmêììÔ¿ëïHÎò2Ý`É@ìG£½ò–+Ôä÷{ç΃•ªßp –_c¯½~Ž»Nýï׸ëô»õcìô›LŸiÜð:Öf¼ÁêííÕaO ꑟ’KR% *g¸²ÓË”‚ŸæóÌ™3¾ö‡üÐ%gEúC|¿r°Ìȃ }cÍeIÕè4µãG¬HÓL©ž‚rû¼TO‡¹}–Ô›’ß‹œý/^¼X'T‹¡ðkû¤Ú`¹µ%Õco¤>Iõ¸©-©wÝÚ’ê±7RŸ¤rÜ4nP !„BÈga°B!„0X!„B,„B! B!„Â`!„Ba°B!„0X!„Bƒ…B!„ÁB¡(•‹$#„0X!”týøÇ?Ö‹Ë’&N’¥häqyžUûöíÓ¦¨®®Îñu²À®<¾{÷n Bƒ…Ê<ÉÚqbp^zé¥9÷Ëb¬rKK˼×Èú² `_kNa6»qã !„ÁBe¦d¡U19GÕ·:¤oËýnZ»v­~N__ßœû_yå}¿D±L½öÚkz1b1]‹-Òï;:::Ï`¹™-ûý‡V+W®Ôï' oÞ¼Yݺu‹ ‰ !„‚#‰B­^½Zåå婽{÷ê¿«V­R“““®¯9räˆ6=µµµsªÒ÷ ÌÞ'æêÂ… jzzZOÊãr_<ëĉú1xò~===úvcc#! BK2WTT¤ÍÊÒ¥KÕøøxÄ狹‘ü,‰"ݼySß766¦²³³õýò¸Ûëä9òºx VEE…þÿÒ¥K³ï'·%’…Â`!„P $†Ê‹ÁIr¼<¿··Wß–<.¹mMš—|-I†¯©©Q÷ÜsŽŽ¹™ªX Vnnîìm;! BF桘1I±LŠÎŸ?¯L ŠÊÊÊôí÷Þ{oö9b®ä¾ööv}ÿÔÔÔ‚ –D¿ä·Bƒ…BÐO<¡MËo¼¡o÷÷÷ëÛr4­X±B›žS§Né×Èm«d*ÐnšÜL•²›'ësÊËËõÿÃÃÃl8„0X!L™e¤,ƒUætŸ<IæóŠ‹‹õßîîî9/[¶lŽ!2¯Pt2X’0o–Œ“uùòe]‹ËúIž—ÿKJJÔµk×ô}§OŸÖÆ !„ÁB!ßeã$É¥r*4j•äj™ÓvòWÝ­’iD1Cfb»ÔÕr3Xò^RÂA"Y‚L=ž={vÞÔáàà ¾ Qž#ï[XXSQS„ !„Ba°B!„0X!„B,„B! B!„Â`!„Ba°B!„0X!„Bƒ…B!„ÁB!„Â`!„B! Bi²ã~êSA „0X¡,ĶFa°BtÛ! Bˆƒ.b[#„0XqÐElk„ !ÄA±­Â`!„‚н}û2Héü•1Xa°B~tOŸVjõj¥ÊË?ùßE7oÞT›7oVùùù*77wæ%«Ugg§çvdee%ô{]¸pA-Z´ÈÓkbüÊûÎ===ª²²R·EÚT__¯FFF0Xa°B2X;v(µt©R†ñ{侩©yO}üñÇUGGÇÌCSjzzZ:uJßç§Ä¸˜Ä*_9pß¹ªªJ ê¶H›ÚÛÛUYY ! B(0ktt®Ë°òÁó^Ÿ““£ìnúÁ~ êêêÔÆÕ† Ôøøø¬ ºuëÖŒ©Y:{;ÚkŽ92Ó CGjäñXŒV,òø•ý­mÄ`!„ÁBÅ`½õ–»Ûœ÷úŠŠ µcÇ5<<<ÏtH”çù矟½}øðaõôÓOÏš‹“'OÎ3C‘^³xñbuéÒ%ý9»wïN˜Áòø•ýECCC:ª…ÁBƒ… ŠÁúáÝÝÆž=ó^/‘1_ÿú×uÔ¤¦¦F]¹rE?¶dÉ5111û\1 f^”Ýü˜·#½æÞ{ïU?ýéOcþn±,_9Ðß¹¿¿_•””ƒ… !(ƒµ}»»ÛhnŽø^i‘ü9À›‰Hf#ÒkNŸ>­ª««Õ=÷Ü£80ƒµ€¯¨ïÜÖÖ¦Ö®];;½ˆÁBƒ… ŠÁª­uw551½§ä ‰$úâ–«äf6"½ÆÔµk×T^^^ V¾²ïßY¦æ31Xa°B0X JmØàŒu¾ý8ûWßþÎÞG„B!©Ž B!„,„B! !„BÀB!„BªâJ|Ä]ª,½ù曉²=û쳫¶ÎVc=§m# ¡,ŒQ>8Ëñ!¼§RÝ㫯¾j† bN;í4sôÑGÛ¤ÏÚ§cqÐäÉ“ùÖÐÐsþËöN8ÁôéÓÇ^s×®]Vu¶í ÀBBVÙÖØ±c3ÞǸqãʾ.þù‰û}饗"ƒ…ã?Þ,^¼À°°€`eïõÉ”tn¹êwÞIܧ•SÛFÀBeXÙ ;ÔÕÕ¥=WÇÃ<„,Xp˜'ÇŸô0Þ³g=·¥¥Åz2¢É°N?ýô¼‡ð.¸à‚.ÿÿüóÏvÎæÍ›»œ£8§|î)›¼«½{÷&®©Îð¯ýkdù.PòpE]ÿ²,ABp߉'žhÁ ÊzE› {þSO=Õåúz¹ ÛgA9·m„,+À v|òœ¨cÕ_ÿþ5kÖ¤}ë ¹`§¨ëø‡œü¥?ZiåÊ•v¿†”úõëW4ÀJÕYd#ýB÷ÿ20‘7ÁŽþ'×{Ê6¯Ãê‘GIüïw¾óHó½££#©ýQÕ¿lkíÚµI¿G^—(ëy”€%o Jß§üÔ þóüSj,Z´è0 •×îý÷ßϰʥm#`¡XÖ•W^Ùe¿&ùÍo~Óe¿†kÒ=„¯½öÚ.ûÔHzû÷ŸqÆvp˜Bßç×À3Ú`ÉÞ_|±Ë>Ý[˜¼uJ7ä’ê:þïÈöž²Íë°ò×…+VD X©ìªþe XNêôSAJõEUÿr¬à±LåeÞg;l¦yÄîºë. ¥FÖ»°u2Û|*dÛFÀB Xa^Óy\¤tñÉÎ×°AºÿÑG±+ÛN'xN0kÓ¦M‡}¯öùÏ9÷Üss¾§ló:Œä±rÿsÕUWE^ÿ4Äç?^SSiý‹ °¢®çQ–‚Ø—k¼V.yQŒ¶€…ð`eð ¯Æ¢_³z+)Ù/Þl‡· X™Œ…É÷-ÂlË,[oS2éžÝõ‹u¾ß"œ1cFYx°RyÖ¢¬çQVâ'NœhöïßÚƒ•`•KÛFÀB±¬` Œ^Å–üëÑ…‰‘×ÿ/Y'­A¯‰KÁ ê`ÌM6Ã[¥¬dó`é­?uÞJÉæÁRþæzOÙæu& fœwD÷–íÛ‡éîUy¼ß“O>9ñZ.õ/èõR¼šÞ\˰~ö³Ÿ´žGX©<}a½ªþyײͧriÛX(V€ºÑ[UR¶oqiÿï3ŸùŒinn¶¿vÕoÜ¸ÑÆa¸ó]gæ`ƒo“]xá…yÙŸ-´äû¹æ ;Œ"ïC>Зm^g’Îw× eÙæð ¾|ê_÷îÝóž~@©i)üó~ùßx‹ªžG‘gR0JÀ|ÂÿÁú(o’î/˜ßa«Ôm! ۤ¨æ!Rl…&? Óa¤;®Î$ìxåXRp¨0L^æzOÙäu&iÆvwþ}÷ÝW0ÀR¹çjÊ¥þ%[–HÃN¹ÄÑùÓ5×\y= °ÂÔ-ÿw “ÍõLòX–{ÛFÀB±,çIèß¿—™´µíLî;vì°†(¢¸ÌšÃGo¹‡«^W‡æf…Öyݺu³½Ö˺ƒ/`Iš3hÈ!ö-/Ù¥¤Ïò ¤zƒ*×{ ›×™äïˆÿøÇ?FX²]ª¼Ó§OO»Ú/HuÒ•Ú'h’© Æ¿p²¼vy©E°ÃÀŸÿ;2å[PºG _‚A]óŒ3ΰÛL€‘Ó‹/¾h÷ibΰçäb+BÀB(–€åökm6I³[k[k³e’†ÀÔA»ŽXÃLÚvë»åz?VÒ~y‡öíÛg“>k߯³,ÿ]TyæîÇ¯ŽŽŽÄP©ÿ 9O Š!C†$ý> Kª Ž;Ëó$»¯0ù”bÂt^:tä²/hë‰'žúœ\mEXŰRÚwíµ×Úmyy‚ˆî’7GÒ‚ÈÚž1cF^ðâÝõwºî>å‘ɰ^z饌±aÙÞ£¼2Ú„I7ä&íØ±Ã‚«ô”dCð~üÞ(ÅBi!âlï+L¾…‘óÄi¸OJæ‰ Úæœ\mEXÅ °äAÐЙ¼n¨Ë–R G)@ÞïùÝï~g·{ô葼¤‚¼àÐZXÀŠJý¢¡1YfÜ=Ž92ƒ%Ï•þgÚ´ivß]wݕң¤a4ËÊ•+³º¯0ùšå…s 5`ek+BÀB(€å¼)ºÑP•¶|­ã‚¯túk¯½–öm@Ø\©€%éJ7,ª ÿqãÆÙsžÒÉ'Ÿl·ýS3¸á7Am*‡èŠX.®LõbÓ¦M‰ýŸùÌgRÿÉÆ°çäj+BÀB(€•NÞKäîÿ×Ë+ã—^ûOõÿaïÇ uùãÀäaóuih)èi˰\lRÏž=óÎ3'½‰§sÇŽ›ò“¹×ÕÕu¹ŽÞ®óÇÅ%S2˜ “o©ûØÃœ“«­! ¡Ø–@±7:OÀìÿuŽ<êäõÆ™_òZ¨£Öñ°sb¥!G÷6ž^ç¸×ùÝp¤$ ié{4´òÖÕòWš¡_K+i½M-‡„,T…€Õ¯_¿. Ïf{¼ÊÌ-“RÎAœ«ÒëT©í+e](„íåZ·ƒÏ}Ö2YZžJë~j-íSž ða§E^ÕÐäFVÒâ²jˆþs‚k›eÚ¯eG´¾›[«L¿³y0½õÖ[æì³Ï}¼[·nöz¿ùÍoìöÔ©Sí¶[Gíõ×_·ÛZû­ç'“9ýŠÔläÁ¡®§s\Yè³~}&Ë·Te åc”çú]ãšk®IëÁ s_ZƒNù­sŽ;î8»ò¾}ûì1ý v¿Žu\ë×éžî“I¿¶5„£{ÐßY³fe/²Eÿ«û¸à‚ ÌÿøGÛ©žþùvŸŽ©î„©S¹^S !+ÿuìøã·ë¾ûî»Ió#[û´°³ÖCÔµ3å})ì˵.¤«_…´=ìs0L»Jg‡ÖjÔyú¿¾óï$]”=“}Ùæ³’ÖÔâáþ~ýõ×Ûë=ðÀv[CÚÖCHúÉO~b·õ@+ÆùA 0u\ öÚk¯Ù}»víê’ZÐWŸ;¡‡²’>»N0Y¾·µX²šþïQç— °ÂÜ—[ºÿþ¶ŽÌ™3Çn>Üw÷è†VG¨~6¿òµ& ³½££ÃìØ±#±u6ùâ¶ÝbР©S¹^S÷-÷·§à¢Ú¹–»aç0y_lûr© ™êW!mû Ó®2ÙÑÐÐ`·¨RSS“ÝÖþdJg_¾mN­ãgžy&!€…Šå®×Fûô+-ÀòË•ê—p˜@[[›9ýôÓíC*ìñæææÄ/DíwÞ˜=zØãú•­ã:¯ç¥Èß™%ˇË.»¬‹—LzóÍ7»tp™KëóeúÿçlîËy1d¿¶õ ]R^ï;Ûa÷¯¾újÊcÙäKº}É~µ'«Sù^Óå•À x<×rWGï¿ÇLy_lûr© ™êW¡mó Ó®2Ù! Ø´-0’÷/Ý]:ûòisIy¾ÂÄn" EXa:ñlÐÙº£5ü¦”Íq÷kòÔSOMxeäEÓ_;ñÄŸ‹q~PzPëxýùåšÉòν”©lÂ|ÿs˜óÝ9©†xôËÙm+4¡·˜²¬T÷‘k¾d[?“Õ©\®)­†ÆÕqªÓýeúŸ\í “÷Ŷ/—º©~Úö0Ï·(Ú‰Ë?n ¿é‚ÌÓÙ—O›sǪ-~ÀB Xî—\˜W…u®~mêWg¶Ç݃käÈ‘ö»ô+T½qãÆ%u‡úü°%`…ùž°ßéö»¡™TE )2Äþ"wÿÖ[™é¾‹X©êT.×|è³:tyãœ'¤P€•)ï‹m_.u!Sý*´íažoQ´“d€å‰Êƾ|ÚÓ´X¨Œ+ÕÃ%Ùþd×Tp¨öÉÍžé»yä‘DÌB2¥;îâ¤t_ê\‡à~YêTÌóý6*xXŸ@œê7Ä á!' 7d3D¨‡¬¶ƒó¥êPÂÜ—‹õ9ø¢B2iÈ"8Ħsr÷á·Ý)—|Éf_ª:•Ë5ö‰|Ë=lޗʾlêB¦úUhÛÃ<ô«0íDž?3qâDû·OŸ>9çeºc@€…bXîᢷlüJ¶ßýïþýû_oÕ¨ñ';/(]3Y'渋“RR©´hѢľ`¼A¡Ï÷Û¨[}VÀ¿~µê÷‘;GñîýÒÕyî°Aîî~üßãÀ0Y‡æ¾/â~!;4«Cq€ mÉl+Ð7Sýñ‡òνJ® wýBwså’/ÙìKU§r¹f÷îÝ»t²þ:“êrµ/LÞÛ¾\êB¦úUhÛÃ<ô«Lv¸ ÷éÓ§ÛmýÕv}}}JðNe_&Ûñ`X(&€¥‡©¦ó%ÛïþW´‚9õ‹U¿Â šé»u½à/ºlŽ»8)¹ê]lƒ†Îu&(ôùAõµ†•'‚MeŒMóO™àòNÐT×L–zê”SNIØê £tJ˜ûÒ=¸WÔeïÉ'Ÿœ˜FA¦¾öò“Ÿ´olùcKRÕŸà}¨³Vç¤ëè{&Mš”s¾„Ý—®NårM½aælÐ}ÖÕÕ…ºN.ö…ÍûbÚ—k]HW¿ m{Øç`¦v•ÎŽTÓ4¸©ÜÖaó2“퀅ªÚ¨Ú&u “n¾/î«°*EJö¦n%ÙWÍe‹€…ʰs¢‰ s=ixeåÊ•6Vž/ ‡)¯4üÀ}_¥ªSnÎ4ås%ÚWÍe‹€… &ý*/Æ/ó8J*†ÃÍ­¡„Tófq_•' å¸YÀõr„–p„°B!„,„B! !„BX¨ÒëàÁ‰ý"¯°%|e¹öUsÙ¢üÛD¥¿Ò_lû˜"X¨äÒ6n¢ÉJ|à˾r|ÐÚvÍ9T[[kçörAå555fÍš5]ÎÓœEZÓÍ÷ë³î©°çøµcÇ;÷Q0o‹u°¶°Ê,ÂÞW¥ç°P©Òç°)µ}¥|ÐÚv7Iª&oÔÔš€QË¥h¿{;Rëè¹¥Jt\IK9iâF {Ž_z+P„ó¶˜× c;€`! U©4‡’K÷PÒl×ZR¢µë´…:jÍw£}:¦ÿuZ°`r@Ç´(êÀílìþkxk×®µ›óè³ÿ׿;_³8k 7Û¹:2y>ó™ÏØ}ò,h†ôRÙ—î~‚¶ûíWǬ{Óÿh¶ê¡C‡š}ûöÌvÉ?‹¹¼._úÒ—³ãKnq\-»$i¡am«¬¤×_Ýnk†pIÿë_~Dr“{ºs”WÁuã^|ñE»Ï­3æ'呼rn Ažó:alÛ6üŸŸzê)[U>ª‡Áe¡²ikÁkëS˜v˜®ÎeSÝ=hé¿û>M[â¦ÑÈtÿÙ´[ X¨¤Ò²Z "€HzÐjû´ÓN;lŸ[WÒ_²~Ék¹ ×¾T¿(Ýš^W\q…íè”ô9ÙšpÜÿºó´™¤NCöRÙ—é~’=èÕ©¹5ÍtMÍ¥m·Xn!l,èQZ†EÒ:„þë»5ßxà»-Ѷ:QÉMÜ©å™$M–ê ÐIöø—gÑœTÚÖº‡Nú¬}'žxbèsÀ¨C–ÝÉò¶˜× c{¦º“ "´¹€Iðâ&¢ÕBÊaÛZº%x‚õ)L;LW粩î{õÃBùï¿W˜ûϦÝXÀB%“Öó;ýôÓíƒ.ÌÃ-¸Ïÿ+8ØÉ¸õÓR]Gk{ù=%’ŠMö€ÔÚŠþ{Ôuƒÿ[Jû2ÝO²ë;ûݯm×1Ë“U(ÛÕ û;©d÷çÖºmú_çéêÑ£GÏNº!0y`Üðšóô¤ZÎåa˜sÜ÷;oZ²¼-öu2Ùž©îd"ÔË é–RJÖÖÒÕñ`} ÓÓÕ¹°õ1Õ}%[Ê(ŸÅÄýå`! •LþQÊõAæöé—¶†Mô+TCúõžé܃9ÙC8¸˜uPú…ìŽÉ›pÓM7™wÞy§döeºŸd×W‡’l8$S¶»ïôw°Áïr‹lŸzê© ×èÑ£í_ÓwºÏÉÔØØhkèÊuºQ–¼{MMMiË®˜× c{¦ºv!dÿq¦¶¶Ž‡m‡éê\Øú˜É¾\î?Ó> X¨$R'+‡<ù>ÈôÀ×guè v¿J XÖÒ0Š:2wŽb/Je_¦ûIv}×a'Sg”íéòÜ¿ßÅa9Ò–…ìœ7Îî×[sɤaD×ÑnÚ´)±_ j¸Moï…='úï¿˜× c{¦º“ ‚CŽaÚZÔ€•®Î…©°PUI1.Þ'ßY²‡t¦ÿqCŽprÁÆÙÄPÈSb(•}©î'Ùÿ+Ð8DæúùØ® `¯Ààtßåâ°tß. [Lª_ŠER¬– QpÝ?þ·õÙ0æœ0ùTìëd²=SÝÉ.°^o0†­‹ÙÔ×0í0LKu,¬‡.—û°€…ÊRú•鍿ó ëÞ½{Xpq<éþGq@ú¬@c {¨£ÒçdAîÉ:ç%pAº /•}™îG÷âÞžrRP°ûµï€G×xÊv¼»û;ªTH‚ìú>unz[ÎuJ¥°/ÌýèµýàÛe²UçúůÿÕГު*”í’^¡×ŸìçeÛ¶m‡Å¹8,}‡»¶:^7¬ézô¿•–nJ ”+^G¶*é³?È<ì9aʳX× c{¦ºü¬óäiô· ÿc˜¶–¬¾¥ƒLí0]ËTs¬°÷`! •J1ñf²·…*ɾ¸–­¬To©!„€…P)¾B“ò[nþ$ÍëS‰öÅ¥l5Ä´råJ<-¯”›gIC€!„,iØÀ­Ó¦àà`0*®4¡©blÜ,Ú¢ »¬ BX!„BÀB!„°B!„,„B!`!„BX!„BB!„°B!„¬Ðš4iR—í1cƘ;î¸#©¡¡!6÷ZÉ6`6`eØLâ Ë'eŠÖU‹CÚ±cGlîµ’mÀlÀÊ;°!˜¾ÿýïXq¬-[¶Ä¾±T‚ Ø ØAY`6XX$‰D"‘¬È¤Åb“%éСCföìÙfÊ”)ž”ÚÛÛñ`avP§°°¬L€•J‹-2‹/Nl777›™3gƒ… ØAÂlÀl°r¬¡C‡š¶¶¶ÄöîÝ»ÍàÁƒñ`avP§°°¬L€Õ«W/›hæÍ›g:::ì±>}út9WC†Á}Ä`‘H$‰D°ÒHÞ*ÖäÉ“Sz·’ísñYAÀÒrA:JÖßrÝv).÷›lûí·ßŽõý»mÙçû¯¤òصkWìëí›öMyÄ¿}WÄ[„òRõîÝ»êøàƒ.€¥IíÚÚÚÛ»wï6ƒ& °ƒ:`X´o ÀJ' ù1ÂìÙ³Çnû«OŸ>]ÎÕapŸ+—üjhh°•Б¾þ–ë¶Kq¹ßdÛo¿ýv¬ïßmËŽ8ß%•Ç®]»ÊöþV®|»à€5wîÜ‚ÛsÛmû"¬I“ž3óçÏ·£ú»råJÚwµïbÖ&¯²Ó¾XÖX/7·oßžØö–ÿsº}Ä`‘ª5ôžJgŸ}¶9ꨣ좻^x¡Y¸p¡=6ÚëµÏ9ç{ì˜cޱ³okí8ò-¾¬tåj‘æRx°jkŸèb‡Â;¨3Ä`áÁ*²Ür Á”‹,l¨V;Î;ï<3cÆ »„‰’ êsŸûœ=vÁ$ŽmذÁë o4Ý»w§,b XéÊ; LX<§¬*ž¦Áï¡òº¬Aƒƒ… Ø‘&yä‘)ípÇ(‹Ê‰Áre `ñœ°¬Ð€ÕÔÔd¦ÿ-ÂÆÆF `ñœ°¬Ã‹y°H¤pÉÅÛ¨ý<ù䓇Ÿ:uªéÖ­1XX™Ê{Ù²e°jjj,€`1“;¿¨°#Ÿ´nÝ:Ý£G.v\ç=.¾øbóÄOPäÁ –w0)FKÁðÏ) Àb-Bb°#‚ä:UÙ1kÖ,MYTn V*ˆZµj•9á„,žS€…‹_TØ‘múÊW¾b‡Ñå­ÐG19òVéØ~ô£Xz­ð`¥¬tåíÃPp5`Àsíµ×X<§,«º‹DÊ%iy)u¬ l>þøãÍ•W^i;×|æE"•/`¥+oÍ)¨yÏ>ö±™O}êSvh˜·I€… v`¬ Y*, Àb<°,b°Ê2¥[AKå|ë[ß²û=öX¯¼þ À°,¼ Øx°,êT¦ä« ®Æ !`Ím§Ïšªc WhC¼Ê`X‰D" À"e™ÜÌýòZù÷ë…·l€`Xx°#Ë´ví‹fÍšÍæ¹ç6›Í›7{¿j7{üGéé§?J/¼ðïfÕªÍfõê’þWIŸuLç=óÌ/(‹2¬{ïýIèòVyæRÞx°â“ü«1èey¶ÂLÙ`XÄ`/ƒÒôé;Ìm·´’?¼Âv†µµÇn¸ÁxŽÖœã@4lQÈ8jÁ;v³=çÙg WÞ¥,Mª%~T§Ž>úhsá…š… f¬oåT§ÒÙ ðÑ4ºwͶ¯”ï”)ÁÕ4K¿ÖëUÔ÷MŸ>½j«õ Àƒ… nG)+]ÈÕW_]Ð8j÷`U*`ùë”’:A7¼•®¾•SJgÃÍ^ ~ܹýLÓ”¹~W²Õž{î9s饗&À¡ÿþæÔSO­JÀ*F}°ˆÁ"Ux*`Å5À*_ÀJW§²=VŽñQ_üâízn¿<&§œrJN× »ôiӬ׌!ÂÂÔ' 6àÁʺÃmhxÇ>¬\Òwå‚+zÀ –÷èÑ{J Xò*hFz ñdªoåZ§‚6h˜Ê\m$¸/L4¥ZôÛqß}÷™3Î8£ls/%`ª>XÄ`a1XYw¸·Üòj—×ܹsCÇ(Ö¡q Ä`EXÁò:ô?JXnuÅ(iˆ9S}+Ç:•̆d+&䲊BºÕd‡Úš’€á¡‡ªúy° YŸb Xr£Ž5ÊôîÝÛôéÓÇŒ?ÞìܹÓsÁÁ„+·ÀËBâÁªlÀJ¢ÏQĤª·þ²X°`ÁaC’V¼KIS•=2Ö·(žƒêc¢~mˆÊƒUŒçTº~AÕ¾uïJZ ³©©©¬=X…¨O±¬Z¯å·´´˜C‡Ù´|ùr¯¾,b°¢ ¼,tð(©r«Ðq éêmð—<€UY€•lh9l}+·ç ³á¬³Î2+V¬èƒuÒI'Ån2S ;~Ï+d——Ïþó±ˆÁв>UÔ¡ºÚ«^“bâÁªLÀJâOQǨnËÀª À’'döìÙ¶ÓVG©˜y²©o¹>“µï\žƒélêe”Þô¿E(ïnÜú AJ0Æ2L^°ŠQŸbX~ø¡Í :Àl)éõïyóæ™ŽŽb°r ¼,tð(1X• Xéâ@ÜC8Ê8½ –€U€U__oë‹:ëã?ÞNa°jÕªPõ-ßç`°NåúLgC!æÁ*ÖsÊŸòüiÚ•ºº:¯î-6'N4}ûö-;À*F}Š5`¹J¨ŒÛ»wïaÇÛÚÚ,`Mž<9)X¹äWCCƒ­„Žôõ·\·]Šêz®õë×Ïüüç??츂ýþîïþÎΕ=Z|4.ùn[v”ëýÝÿŸ XóçÏ/™}þ UÕÛ]»vv¼\ÊcåÊ· Xuu-¬aÃZ‹XåPŸ‚íû§?ý©}ÊëZí{áÂ=fÊ”³~ýëLX¹ò ¹é¦/ðò½ÃŒuÐK^}9àåûA3~¼R‡<øéðúǃ޳½Ãƒ‘^½éðÊá ™9óýÐßÌ_þò—æÜsϵP¥!NÅI?ÿüó¯W ÀÚäUÖb–Oì=X´AïçM&Åh)ž¬Ü/£ ö#åh›kàv©ß",e½ÅƒUÙ1X<;Û÷øñÆ»çâÏs—,?Köàƒ&¶õưâY*'Æ1X© jÿþýv›¬Ü/£ ö#«tÛÕXþzK €õsPuªÏÁ8VªüHoUŽ1XV ójÄk¯½f½SùË_,= Œ‚o ®Äv¿{ƒ•uàe¡‚G‰ÁÊï…+u½% ÀŠú9¨8ÁrõÞ—°Òõ ÊGÅ^¹¸2çå°bXk½žB ¥`À+®¸ÂÌœ9Ó”ô”÷äRÃТ– Tloæxuð(¬h^8(7Àš1£Ù¬^Ýøüó[ÌÓOo±0ÑÔ´Å<öد³ìL?¾Åûþδdɳté³lYgZ¾¼s[ÇtîêÕ›rª·®, Yo¬ÂּΫ³ujݺ®õÉÕ)Õ'W§TŸüuJõ)X§Ö®}±¨ÏA}ŸîiÆhìÐ9Ë–µ°FŒø7 Q.ù‡ü2å‡ÀKñkîEÅb©_.5`}ÿûSÍ#¼dÖ¯®N-^ü 3¹3)n)ÓìÂåX£Fm5sçFÛáΛ·•µ«°®»îÿ™… £ípÕ™»¼u_§ERÞ:gܸö‚V)<Ô…,}V^,_]Û>ü€… âhGTÛÕX¬E`E X¹Ö) À°˜ ÊØŽdëôX¬E`°r­S€`áÁ†2±#] mµVcã+6žÃ¥g=bÀƒ`åÓáþô§/v©Sëׯ«¶vwî¼ó·V‰ë–[öw)‹`8€U…1X=öª÷@lõÀ­¦µµÕ«­f„V3qb«—­fÊ”V3uj«¹ë®V3mZ«¹ûîV¯“nµÿÓØØjfÍj5sætnë˜Î;÷-b§ üÂA®Ûq¬`‡[_ÿ¦÷pL^g'OwÞÙj~øÃêì½÷vÖÓ™3[=pý¨Þª^ëü+^°ª°n¿}Aâú&Üã=ïÿ`ž~:º:¥ÿ)4`%+ïBÖ„ ê7:óé…ZÍÃçßg47¿{Àºá†ö.ù¤¬*÷`Í»Õ6¦5k¢kjd¥ö`b…wÖ",ÀR‡»hQüƒž¬ò,u¸ÿüÏÆ,[Ýwè*°Ô¾çÍ+l€`UDÜO9V”ñ2…Xáµ, À°, VU{° ±Â;, À°, À"«d€U[»ÕBºôØc•ÜÎ(Vx¯„`¥~O›öd—z»zõj ÀÊ °üõé‡?|ÀJQÞ“&=×%¯®¿~?€`áÁJÖ ‡ Ëí­“By~¦Njºuë–ˆÁTi>)AÕ§?ýifvábÌ“£<~òÉèý°a¬:\%ÿòébúrÙÀª|ÀòÛ0nÜ+‹ò°,b°"¬BÄ.E¹Â{1æÉ°Ê°ÒÅôåºì€`X€…+–¬¨WxǃU½€•.¦À°, À"«¬+ÊTˆÞ‹ô `Űü1},Á–’†œ¯ñn"ÌD¨€`X¬Øy° ±Â{=X×_ßµÑý"€•'`cúüi™×C °jjj, À°â Xz 5ÊôîÝÛôéÓÇ«\ãÍÎ;í±C‡Ù¥F¦L™’0º½½¬*ŠÁŠSYXåXÉbú‚I^ÑdkFX€`Å °j½šÐÒÒbaJi¹—cßVîxZä=5äÔÜÜlfΜ‰«ÊÞ"ŒKYXå X©bú‚IËp €`X•7D¨W¨¥¡žÕmmm‰ý»wï6ƒ&« b°â˜¬ò¬t1}þ7 W 0×^{-€`XViK±0©¤!¿lôá‡Ú‡ † “ý¿<\鮉 ,+`¥‹éë]PÓ||ìc3ŸúÔ§ì0"oX€U¶€õÖ[o¥…¯d×Q’a{÷îMyídûü™âWCCƒ§q¦þ–ëvðoØÿ_º´­à€5þüP÷³k×®œí¯¯Ï{8î3ð:¬}Þýµ{÷¶ÏkH¼cûL]]»™0aŸ™:õ€™J«W”Ö¬éLú¬c:À°,«h€%àÑä ÖK6\ÈLî¥Ñ^Iêm%MbxÌ1ÇX¨t³Iû½&õõõ¡×GýGVeÖÕW³dItß!», À°ŠX&LH`%ŸogÆ ^¾ÑtïÞý°É/ºè¢XV¹Å`•bÈÀ°, À°",½Õ§7ÿ4‡kfŸ´À¬óš,ôž@òp­ñh , À°,«ÊK±R,öœ}RÀ¾¼ZúüÊ+¯˜³Î:ˬX±"11"1X¥,b°¬T€õÌ3Ï$ÒC½`X€U8ÀRüU9x¯âXS§N5ݺuKÄ`ýÃ?üƒ÷À~¨ËÌÓx°ð`XåXÁÀ°,«`€¥ÛŸþy+‹¤¥8.¾øâÐKyƒE €`X€U…ó`ä>)ˆ]&x°ð`X€`X€"©ÀRuòÁ¸b°ˆÁ°, À°˜h”!Â)Ý0 ,D¨µßÞ|óM‹µ‰Á°, À°¬¨ôÎ;ïØEœ,6é³öe«^xÁËŒ›1X~µµµYÀšøà°ýþóŸÁêé´yóæ”^+ÿ†‚«Ù³g{‚û¬2,b°, À°,«À€% XéæºzÊ{riެšš3`À;ŒXíoâÁƒ`X€`UÑ¡âVœ4\˜Ía9Ìä>ÚËåsÎ9ÇuÔQæ˜cޱp¸téÒ.ç,X°À{ì±UX©òJu`ãÆ†µÏÁr°! À°, À°|zùå—Sz¡ry“°T€uÞyç™3fX@ذaƒW¹n4Ý»wO?âˆ#©Ú+U^Éó£·@GŒ‘8w¸wÑ+¯¼€`X€`e+ÅI5*1g•>® 1Dxä‘G¶ÀJŸW_ü⽇ܲÄ~y¶N9åb°, À°,+®Š°­§ÀÊ X.¯äù9ú裻“—+¸€`X€`¥ÐÊ•+í0àXÝi@ã¼бիWǰ¦NjºuëvX €•>¯»”,Âä1X€`X€åƒ(Öž={º¼=8sæL{L€7ÀºÎ+•‹/¾8eg `¥Î+È¥‡z ÀJÑ ô£×»äU¹y°|ðAsöÙg3€`X€Àzá…ì[ˆ.+ÙR;Éöù3ů††ëpC>úëß.`d¡«®nMÁk̘ßFXÉd¡kýúõ]Ê?Õlô:~ÞyçÙáêtõ'¸½råÛ¬ÚÚM,•7€U€UW×RpÀ6¬À*ÀRy°Ô¾¬Ì}ë=÷ÜÓåùkÀš?¾õ8ùƒØãèÁ*6`ÅÕƒU Àb&wÚ½ÿo÷Îm7uuíÞ5”¿ûÌĉúÛn&On7S§¶›»îj÷\»™>½s{„Îÿ°, À°, ÀªZÀRƒÔµ–.-ì¼g€`X€`X€`X€`X€`X€`X€`U4`imL—~ðƒ¥€`X€`XVÔå `X€`X€`X€`X€`X€`X€`X€`X€`X€`X€`X€`X€`X€`X€`X€`X€`X€`X€`X€`X€Uõ€µcÇÓ·oß.ûzöì™4X€`X€`X” žÒÁ€`X€`X€´, À°, À°¬V¯^½lèåÞ<¯VtttX€`X€`X¹–_mmm°&Ožœ¬\ò«¡¡ÁÆvmÙ²Åfþú·+°êêÖ°ÆŒù-€U&€U[»©à€¥ò°Ê°êêZ XƵXeX*ïB–Ú7€•¹o½çž{ºðBE–tèÐ!Ó»wob; ÀŠ °2Õ©8ÌäžoûXaÛ€`XVÙ½E˜klØ·o¾¹óúq¬¾}fN;í ÀŠ °ÂÔ©¸,•“OûÎXÙ´= À°¬²¬oÜkŽ9æÄ‚Öõ×w^?Ž€uå•o™Ï~¶§wû,+À [§âXù´ït€•mÛ°, À*9`|rMbž={ôiþþïë#¬“Nª1½{¯óìø«gÿ^óå/4çW;À8ð}óéO_hÞe¿À°ò¬lêT¹V”í;`åÒö, À°JX^8σ¬žv¾œc=Ùƒ«‰‘¾ExÁ]ÿŸ8Ù{øNŒå[„Ÿýl/»h®û Àʰ²©S©kÉí÷šmw?`Z7l2úӟ̶æ§Í¯®«3/Ý0Þ¼tc½yièóò°‰æå“ÌËßlZFM1¿®½Óüú¶iæ×ãî6¯ŒŸn^©Ÿa·uLç.½uJhÀв}§¬\Ú€UõìÙ3i°,‹™Üó¬T*F X_ùÊ,sÒI—Ù-kòùÏ÷ó à-«B+›:• °6뤈]X¯zÆ”ÓLî¹´=«@€… À°¬BOÓP–®½á-kòWsÉ%sÌßüÍ™V•LÓ‹«+—¶`X€`XiíÐüB€`XeX½zõ²i —{ó¼ZÑÑÑ‘¬\ò«¡¡ÁìØ±ÃlÙ²Åfþú·+°êêÖ°ÆŒù-€U&€U[»©à€¥ò°¢¬osÊ)—eXuu-¬aÃZ¬2¬–ººÈëßG2fΜ9æ'?ù‰wÛ¬}ë=÷ÜÓ…*:Ƚ­­ÍÖäÉ“+ƃuÁsl@agŒÆq¦[·k¼Êú¬<ò)WÀÊå;JX/ê@Ä„ü…»Õ{ GMXOxß°¢(ïršÉÝŘ|á ½,k‹Ôƒ•,¯n¸áXy°¢jß•0“{1EXa¾À*`åRÞåX§œÒÇ\~ùóöWô 7ì·1Xû·ý XßøFg^Űrm߀eßZñ€õ”÷äãåFMM0`€™?~žEØ»÷ sâ‰çƒ•g>EXa¿À*<`åZÞvÒÆï.3[žØb'l|uÅóÐu›7fšF6›æ1›æÚÅfñmKÌ’Û—š¥ã—™ew¬°Û:¾p袬ëâ‹1Ÿýlýå|ôÑŸ4_þò­^¹ï-`}íky·¬¨Úw¡ë®ÛÿÕ¬ùþ3æõ5oØ:õò’–œëÔãc—ØsæŽx¸"«iÔRóë%¯$ÚÞÃ7,̯íyçO¹cZN€•kßÊLîXçœ3Ñ|þóý½Ïïlš†«®ú9ê¨ã Xßüfçõ Xaò)_ÀÊæ;r¬:ó À*\yÛgA\Xå:“û¹çN4§ŸÞßûü~A¦iˆ¢}g¬|ÛwÀ ÓöRÖ± ë÷^ªDÀúʇˆ]X3½þ<[Àʧo°*°.¿üg^ZWÐy°ü³å°ü×/`…ͧ|+ÛïȰüy`®¼« °”W_ÿúº‚̓UûNXQ´ïL€¶íX•Xùö­VÌ«¦f•÷ùý¢M4Z(ÀrßQ(ÀÊ&Ÿr¬\¾#Ÿ!B+5`EQÞÕXÿø«<3ß/ÊD£…¬¨ÚwØ!B«ò+оÀŠ9`e»vUµV®kWeXQ­M`åXQ”w¡K6º4iÒ’’Vª¼Š`EÕ¾ãX;½ÌôשBL4Z)€õ <.Ÿ¦M{0-`EÑ·X,•S€U‰3¹X…ŸÉ½Ð¬ûÆM4ïÿËd³gÕ³fß¾}f÷’'ÌÛßf¶]7Ül»áV³í¦QfÛͣͶ[jͶ·™í#o7ÛG7ÛkëÍö± f»÷ÿ¿?ÉüþŽ)fû÷î0Ûo½Í¼8¼®l—Ê)äa1grǃU=1Xx°Ê°®½ö¯¦{÷‘æ _¸ÆœqÆ@; ó!ïX€`}ÔáFì v¸€`XXçž{—ùÊWf$ èÿüŸé¦G[, À°, À°¬\ëþÏóÍ Aï& èÛß~Çüÿq€`X€`X€•+`}üãŸèR@·ÜòWs䑟ȪýãöŸš©µÿa6>½Çüå/1«—ì6£¯yÛŒùç·ÍØï¼mÆÝ°ÍÔݸÍüËÍÛÌö›‰·n7“Fn7?ý{3õ{¿7wÝö{3­n‡™^¿ÃL©ý½i¾ÝÜ~Ó¿å5MC˜ Ø|¦ip)Ûð¿ ý­Yùh›Í§5KÿÓË£m9å“¶'ŒØnÏWþ»ëO¨ŸaÆßøóÌrWm9—…ûŽÛ‡lËkš†0—• X*ï'ûÏ‚•w\ëÞ9÷Þ{o"M®Ûh'¶š×µÛ¼zúñ÷»Ô[ÕAÙ^?t›}>ؼÕ™OwŽý½ùáíù¤Ï:6þ&ïürž¦!ŸöýdSáÚw¶Ó4„ ¤°Ê°ôꕿ˧ÿà,YÔ×? `•°üå­`%/ï1¢K>ÝrË»±,yÿà?°¬HûÖ3fXå<V1­ûÞOlÞl¨ûi—¼Ò9+ni°ð`ÔƒµbôOÌ›nL´ï_þë’´ïz°î¬Ÿj^¨ý©ùÝ“¿´vl]öB^mOÇõ?¬Š¬C‡™Ù³g›)S¦$ŒnooÍLîŬUúŽˆ]X¯z÷qXƒŒØ…•´AưlyGìÂj÷ò À° Xÿ¡ÿ‹Ø…•´¼ X¶}À…`X X‹¼ðbeÜ©¹¹ÙÌœ9À°, À°, ÀÊUC=£ÛÚÚÛ»wï6ƒ°, À°, À°rUŸ>}2 î°¢¬ßx PžB—~«s#¬g½²ñ€•¼¼÷zçúóé%}€`•9`%mß1,¿ Kõ¢ €`U `õìÙ3Ô¾.UŸÆx9aÂÓÐÐ`aKýÛßûÞ¯.O´ûn¿½Á\zéå‰tÙeÉÓW¿š<¹ãº?—j¾úU3Ñë 'x Fß9fÄsù¥—~”.»,yòþ/iòŽõñ’ÿ;ú~íkf‚÷ uuÖŽá7Ý”ù;Ò\_©wà;®¹ê*Óà5*]b}½¹Ö«Èù~Ç?zŸýß1Ìk,<&Nœhÿ~£W¯¼òI©Æ÷ÿ×û<Ækh½%;Æyu#ß²¸¼¼•ly{eaÛ¼÷0Cy-𮸢àåýÏòøoо+¸¼ãÚ¾ÿo ¼o÷ /Êò>kåÌñó‚x¢ª=X!„B¥VÙÖïž={ºÄ` 4ˆ’C!„€•«šššì›ƒNúÜØØHÉ!„BÀÊUÙ΃”?6‹D"‘H$)×TQ€UMʶð°;¨SØ Ø x°;°ƒ²ÀìÀ !„BX!„BB!„€…B!„¬RiÇŽ¦oß¾]ö}øá‡fΜ9æòË/·Ç~ìÖ÷3“¨jZ~ÍT¯4nÜ8óî»ïvݵk×&].(.vlß¾ÝÔÕÕ%ŽÇ͆°åTv¤;–ï(åbÇòåËí½{÷¶ÇeÊ(N6Ä©}g²#í;Šçp”JW‡ÓµÓ\ÅÉŽRµo«Œå_Ïȯ¹sçšW^yÅ~Þ»w¯¹ï¾ûÌÃ?l·‡ bV®\i+›Ò²eËÌuZ¬Ò§-[¶˜úúú¢=€£¶c×®]æúë¯7¯½öZlË"L9•‹éŽ-Z´È,Ö©ÿ%Mâ«mãfGmm­iii±e¡ŽsáÂ…f˜v‘ qjßéŽÅ¥}çûŽZéêpºvšë±8ÙQŠö `Å´ü þÊ¥[«~{¡«ùÕ«W¯Äç;wZ’ÿàƒŠú 7J;Ôh~ñ‹_ĺ,2•S9Ù‘îØÐ¡CM[[[—_ÉT¬òˆÒ†¸´ïtÇâÒ¾óyKî;ÓµÓ\ÅÉŽR¶o+f€USSsXãUƒv®PýŠZ½zµyï½÷ÌÆÍÝwßxø×e,õ8W;Ôhô–«Wîw­P^¬a©¨lHw¬ÜìHw¬Ô ©GeGPòLèWoœlˆSûNw,.í;×¶_,ùëpºvšë±8ÙQÊö `Å °Æo]¡ruª½üòˉ†­Æ$V !`!„ª°~÷»ßÙýz£P\i{áÂ…ö؇~ °äåÒ>BX¡ª¬iÓ¦Ùýëׯ·Û žüßdçŒ1Âî{íµ×Èl„€…ªNÀÒÛ}MMMvŸ Ëéºë®ëJ7n<ìo¾ùf»­y§Í›7Û}:öÞ{ïÙ}o¼ñ†/„°B Xn¢QŸëÍ@MÉà×¶mÛ,$¹ÀöyóæX‚¯ÁƒßÒÒb¯©x,ýÿ€ì[ˆ!`!„BX!„BB!„€E „BX!„BB!„€…B!„,„B! !„BÀB•“íÒ/ßÿþ÷I˜T¶*c„€…*¢Ôõ«_F*4©lUÆ! !TD¹ŽQ¾! !DŒ(_„,„0¢|BBtÀˆòEX¡:à÷ß7æ7OÚ’ªœ³ ÀBÀB•C¼x±1ž´?KõìÙ³ËßJU„Y`!„,„¬N=÷Üs¦W¯^æù矰âf¹Vª<°BB–Õ”)Sìd–ú `…¬Ty`!„,„,sèÐ!Ó¿sðàAÓ¯_?» °–,Yâ]f éÓ§¹ãŽ;ÌŸÿüçÄyÚ/oÎ7¿ùMëÙqÿãOnß|`®¾új»ýᇚ9sæ˜Ë/¿Ü¦Ù³gÛ}Ò/~ñ {ÝÞ½{›É“'§Ü'=üðÃvûÎ;ï4“&M2íííiÏϰÒå™ß&åÕ„ òºO ! !CÀzå•WºÀ©¥¥%)`-\¸Ðìß¿ßÂDSSSÒå[6mÚd pØÿû··lÙ’Øž7ož7]Sé‰'ž0sçεǮ¸â ³sçN»Ù²e)÷-Z´È,X° qM ÙÝ}÷Ý)ϰÒåÙüùó­]r½O ! !CÀš9s¦Y·ný¼zõj» °ürž›dª©©I X~]uÕU]@D×u€¦c¿úÕ¯;?¸oРAüœ)}ûöMy~€•.Ïô²#¨\ïÀBÀBŰ44å†û4d¥íL€%iHP’çEqHnøÐn&ÀJwÝ7ÞxÃŒ7Î; bRí Eú‡$“`…ɳ r½O ! !3Àzë­·’vúo¾ùfZÀ’‡ÆA…â©–/_nöîÝk½2Ù–¼UA– ï÷Þ{Ï‚[ª}òþøc ’)Ù5r¬Ly&›’ÝO®÷ `!`!„bXŠR<•_еzðÁ¬ßýîwö³€HÁÚ÷ß¿ÝÖ0—‹hù!ÊÅ¥,Å*577'b°åb°¯¤€ø;v$`.Ù>Ý‹®á@MÇ\lS²óó¬Ly¦û_¿~ýaÿ—ë}XX¡r,½Å7iÒáé¿Þîóëú믷º_Û¶m3C† éDŠ«?~¼}ËM@%ˆp  X$Åc))ÛQîro&,]CoêºJúìâ—îºë.»OžϧÚ'é{ã¤áÅ[o½5u©Î*‹,˘gÖy¢ôÝþ·s¹O ! !T€…(_„€…¢F”/BBˆQ¾! !:`Dù"„,„0¢|°B¥ÒW¿úUsÄG*8©ŒBñÔÿ¡[õ}çn»IEND®B`‚VectorRendererSample.png000066400000000000000000000763741463604235500344220ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/xy/doc-files‰PNG  IHDRXr5˜|ÃIDATxÚì½}Œ•U¶ç“Éd2L&“L&“ùc2™äfþ˜L&“I&“ÉdBŒDc§sç%Ò/@ƒÐ-/¶È s¯à ã xé¦U®ïŠ‚¨W¼¦ûêUTê‡Õ¥H£ò¢È› "TÁó{>[vÍSçœ:uÎ^«Î9õ]ÉÎáÔ)ηÖÞk¯õÝkïg¯?É$‰D"‘H$IåOÔ‰D"‘H$"X‰D"‘H$"X‰D"‘H$"X‰D"‘H$,‰DÒ¦NèOþdP“H$,‰dÉøÿaxòÉ'/ú¿ú«¿ô;ÿþßÿûQM–bûûÿïgÿøÿãìý¯ÿuQ¿4Áªô÷þƒðÂß{Ùe—e·ß~{vàÀQ3†ÿãü^‰DK"ñ“Å‹ cKq¤d«ê(_|ñEvÍ5× úìOÿôO[’`EÙ¹sgöãÿxÐgÿäŸü“ìÈ‘#"X‰DK"I)÷ÜsÏ à3iÒ¤Ïøwñ³»ï¾{ÐÿݺukvÝu×eÿò_þËéú‡ÿðfÿößþÛláÂ…Ù‰'.ÂÚ½{w6oÞ¼pŽ‹ßåÿüóþϳŸþô§Ùk¯½6¬àøê«¯²ÀVßÃ+ïùùPÁöܹsÙwÞ™ý«õ¯²¿÷÷þ^ÃçÌ™3sêÿ4ów×K†úÝÿú_ÿë Ï9“Õȸ–qè¾ë_ÿëþ„ó‘G ¿KŸckÿæßü›ð¿óÀ\ô· §D°$,‰¤eÒSÎh ‘úOÿé Ïz{{þ‡ß!'Õ‚ùèÑ£¿ÿì³ÏROàêwn¾ùæš¿Ã終m™`4JVŽ?~ѹ¦Zÿ§Ù¿;Á‚¬”û£‘q­ôY¥ÿ1/gJc{衇îË, D"Á’Hš–ÿôŸþÓ ôÆod÷w7ègÿù?ÿçßÿàƒ. |²åË—W ˆ[¶l´!n` ¶K.¹ä¢ÀW+sUüùõ×_°y-þ¼ÞŒX£d…³hå§Óx_íÿ4òw7J†ú?å̪FÆu8g½j¶fúGK"Á’HZVî»ï¾AhöìÙa+¯ø3¶w¢”ϰbñçl!W]uÕ ŸÇm£(¦/fQjÅ2©¼!Û·oôs¶•ª}×ÓO?]q ³žà\«uuuUýÛù»­VùwØŠkd\Ë8œÙCÖ®][÷gq[µÑþÁ’HD°$’–•½{÷ @œ½áüLñgüN¶ë!1psÿRñçl­5Ù†+Ÿ§ªDâv]³¶^=‹waUÂkäïöÊ`EÌáŽk5œ²Nõ|Öhÿˆ`I$"XIKËù/ÿ¥j@-n"µÎèT f嫚 е¾« °"X|?¶gΜžÎêooäï¶"Xl³?g‹¶‘q­…3ÜÏí,‰DK"ii©t!cl|V”r¶¡xwV%)ÿ~ÜzjÇ V£ÿ§•2XåCþl72®) –2X‰–DÒ‘²ÿþª‹;ŸŠBy˜âç?þøEßwòäÉl̘1uÿ~Ì¢TË’E)ŸÕáJdÇŽuŸÁ ‚ÕÈß]«ù›É´•Ç‚k2ây´áŽkJ‚Õhÿˆ`I$"XIËËûoÿ­â•eÙ¼yó ßùgÿ쟅kÈxqà ÁøÈ~µßÿÝï~>ƒ¼Q¢§øþÅ¿øƒþÏ{ï½~^ÞÞâ)3d8OŽÁjäï®ÕÃÁ¯Õ^ýõ†Ç5%Áj´ší‰D"‚%‘˜Kùq|Úo~󛊿ˡî¡îµ*°U«VÕ}RíN¤ZŸÕ{ŸÔH¬Fþî¡ú¡rÁöW^y¥©qMI°í,‰DK"iy)o²=ÅϪ —”ÞtÓMá-îRâ÷ Îÿîßý»lÚ´i!#R;wn(¹ÃïƒÁ†d°Ê7u“5á»ÙЇ›c)š˜ñ uñÆoÞ×s#úH¬áþÝõôC½ä‚ÿ©b‹+7j•Ç©w\S¬FúGK"Á’H$‰D"Á’H$‰D"‘ˆ`I$‰D"‘ˆ`I$‰D"‘ˆ`I$‰D"‘ˆ`I$‰D"‘HD°$‰D"‘HD°$‰D"‘HD°$‰D"‘H$"X‰D"‘H$"X‰D"‘H$"X‰D"‘H$,‰D"‘H$¬FäÏÿüϽ¿á†²ë®»î¢6sæÌŠ?·hŠ%Ý„%Ýd#ÒM62Zt›3gŽVQþ÷ÿþßÙßýÝß]ÔvïÞ]ñç­S±¤›°¤›lDºÉFF‹nÿçÿü¬zÖ‡~è6X[zzܰ¶vu¹ýG›7ûõcw·«nžxŠ%Ý„Õj~ÄÛGv¬8Æ5ïq«†%‚U'ÁÊ®¼Òm°¾93{oÃsœ¿û]vfâÄì>è¢x'§MËv®X‘½õƦX|?º}™o×+¯˜ëñäöÓý æXßOž°>züq7¬m«W»ô£ÞHè†\° ûð©§:Ê=çZô#¯\iŽõÎë¯ݬçYÙ&ßý›¿1Çzûoÿ6ë¿êªÐŸÖXèC\Ó£±³×^ëÒ;~8;uÝuÁVD°Ú€`í[µ*8F¬O^{-8=wÝå‚·ýí·³Ã¿úUöͬYÙæ_´5üßÿ>ôãùŸü$ÛŸºµóßñæ›Ù·ßœÖѹsƒS¶"’Û6mÊvÝöÝÏòî{îÉþ¿—^2Ãê]¶,;þ‹_§E ýà™gÌúÑÏ[7ldïwd}×\Æî“ßü¦¢cn7{ôžkø‘csæd_Ýz«YÿÅÖ»vm¯­õW.>òëE‹‚MZãüaÍšìôõ×»è„M|™¬Ïï½7;ý³ŸeŸÛ þ–yE<ûø­·”Áj‚Å~î‰Üð­W¹‹l« Åz5÷ªÉÀúw/Yb¶ª‰X9²ç~úSÓUvÄcÕÄ$‡¸Dɦֱ¸ç£ÄA¾ž7/¬ÞRâ±p*¹Hì ©‰'ÞH鯸`Ì;‚7D(õ‚c$ìÑ{®±ÈÀX’b°¶<ù¤Éú*'Æèdå‡Üç°°îyî¹Ïö|ö™yFîÐüùÁƱ;«3XŒ ó‰1Š™oÁj#‚Åy¯h”Ö“,ž-Ã8!Xç‡O?m¦oñ,dnßâÅ!Ý#˜¥Ä-Ÿ›ÃùWÙà¥Ä,ãѧl‡¢Á`Àj¼fw¥31h³²'kAІ|Eûm³ÚùC‚ã±ÃV?]¾<Ûô×íŽV£o¸XÑ^RêÆ\')aì¶?úh˜÷ÍÚI½ö˜bŒä\ÃÆÑ‰ njß±Š$ rbé#£O4Ï*9aA²Àâ3s†Ìl1I3š)ñâ\Ä/#ñ ÅÌi5,¬$X±ºå–0¡]³gù žà쉉ƒdrœš2Å—‰WÙ§¦NuÁd¥Ã¶hì[VZ–x¬àØö%UÎö˜ ·qrTì•´¹'Xçò~µ:“SÄ:3i’™n8옑aÜ‚ävjm «9à5×X¬ARÏL˜`:Ÿ!=qC¾­pö,Y’?ÞDÆ‚ïe<,3Ì—ofÏ6=î‚4æ þÎ*!û _­õ4¬!VÀbóÈ`ñzs§kÁ*¦#®ÅÁJLƒe¥›w¨ d2b¦Ø2©gÅ´7w)0ë]õ<ûì ¾md x8+Áž~:;?nœÞއ 8s¸xžXõâ$¾¼ýö¦í¤Uì±<¿­æš•ï*b1ºŸÞŒ0Xú~ 1mOÁ¶À´È`m[µj†Å“ÿÌóˆñY•³qÊ`µé=XÖ[„e½p„¬Ì,œGµ~Œ+*&¤ÕùšJ-êúþÚµn÷®¤Â¬×‹}»éÕWÍï“ñÆ‹Ž­¯Fð<±†ƒ—ÂNZÅ‹6‘êLV%<+ßUIJöåszÖOcZÇ5¶SC†9·+,¶Ô#†UÌf~Ĭ²îÁjó{°:Kº KºÉF¤›ld´è&‚U'ÁRSSSSSSSÓ,e°´ªnêGé¦~”nêGe°t«Ó±¤›°¤›lDºÉFF‹nmI°Ö­[ªT;6»üò˳E‹e‡Ÿñzë­·f—]vYh7ÝtS¶ÿ~e°´ªnêGé¦~”nêGe°jÉܹs³îîîìüùóYöÄOdÓ§OŸMž<9{饗Âg´µk×f'NÔ,5555555Á®\zé¥á•¬ĪÒgÊ`iU!ÝÔÒMý(ÝÔÊ`Õ)===!«·{ì±lÆ ÙÁƒ³7Þx#[²dI[Á²®ÙTl{wìp5ú=;wúí‹÷öúêæ8nŠ%ÝÚË{®yú­Nö‘<×ZÁFÚž`mܸ1›:uêÀ,HÕ 7ÜHÕ•W^Îc}ûí·‰UlEY¸pa S‘‘òÊûxÁX|_þ<Õû-ÝÝÙ¹)S²o¾iŽGiŽþÉ“³ƒ9µÒ§øž‹íÎΜ™zùe¼S¿øE¨àŽó·ÆÛ’“ü¾Ÿý,ûzÑ¢lßÖ­¶x¹>ý'†º[Ÿ¼úª­=æz›4);¾`Á@=±NÁã_úñTî?»pÁ­9^>ßNä¾Éz~»Úcþþô¬YÙg<p-íƒW‚ý¸#ŸÛÖöøÑæÍY_>½pAª‹ÿÏíÛ¦Mæx\dzîê«¡³¶ì½o„ Ÿµ}ð¾wýú`ÿ[»ºÌñˆŸg§Oñ´üy[¬U«V…ŒÓ±cÇ~6cÆŒ¬7@QÖ¬YÄ·S-BjjÑ<°<«ÄÓ(˜K=' [ßTL¹ šRkͲæVl±îµÊvçA´X Ô¢)8 µÿ?«[ÿÁânn°¦/¹É¼‘21­ˆç­6 ;óš²rÁHÚ£÷\£@6«)weEK°b!sëÆÂ ;´ÆyݺP÷R1­±†¿Ý{Ç!žU›ÇmK°`ˆt¯v+õ,O‚ÛÇ[:ßâÞ±'ÉŠ+ù¡ 3å¾8ΘbÒ¥êº5µ×Žå¤ž Ý»l™ ñ‰XÍÏï½7‚«úÔx BüÇ }‰½0†©JŸŒÞHéæA´FÂ=çãsrÚ´€eYR,d%r"Çü²$ß[i¹?þzÞ«PR¿xEL¶š¬±Ê}Ûè¡íáØ¾7^t|l§4‚ç‰U/^Š>l%{,ê“êà{%¼¨Kê…aË £žµï‡8Xǵ¢VX‘d•w—Râ1?jez;ê ÖH,Ý®+Ýt³t“nêGé¦~T+1ÁRSSSSSSSÓ,e°´ªnêGé¦~”nêGe°t«Ó±¤›°¤›lDºÉFF‹n"XÊ`iu&ÝÔÒMý(ÝÔÊ`é –šššššššÎ`)ƒ5ÌFÅq/,ª{œ'ÞG›7»êæ‰çi#žX¯[OOGÚ£÷\“Ñ\k%©†%‚Õb¥r¸7¤òd“B³åÆe|`qÓ²‡nR.ÖXÜñrvæL³:‡Õð¬Ë[иùž~äVaʹx`}™Ï›ôÄóÖvföì`#Öõì<íÑ{®E?âqésÐí†ÌçYÑÿŸ›4ɬ®bÙþÏ_sKkúïÜ„ æw{ÅFLëŸ:Õ¥¹P{¬„%‚Õ‚µ¿¹í¶P:ÆëógžIzÙæPí³—^ xÕnÄMÙ>~ë­ìØœ9¡TƒÇDÛöÎ;¡„ ˜Öý¹ã÷¿õÊ(ƒ³ûž{Lõëà‚á"?Hu}LO‡À),Ï3,KÔÚÜë@À¹ÙŸxÖ[å¢^¬$XFísZYШáE¡Tëêí±QÔùÔu×eû/vqZQ?ë€6ØV¯úQëÍZ?Vj¶0É—-3]!¤äóƒÛ¬=²>žxÞº1Ç) ¦5–§=zεXà×#“E1i2þƒy žùVdÞoø¬„g‹aaébÆ‘ââcEenwï~áÁj'‚Åy/V¸°c¯§˜Ð«´ˆGÉ À²Â¶"ÅssL?“‰NF„Én±[>HÅf(àºãá‡M±Šd»I½}â‰7ÒºQDš1# ¤¶Ë‘²GϹI°ÎdEÿ¡“Çq ²ªnk_Ì.Â×wÞi®6ŽÝYŸ?¤¿¾ž7/Ì_þmun›&ë¿‹#=EØF‹;5¢QzÝßA;wÀ8=î Ái¹é¦°’'{FÊ:e?—ï&abd˜qË$%f¥»PpÈ8fpcF2fµ{W°œ3:WŒl5𡬆Eâh*‘O¼ØŸàybUÃcK#d™Šç˜šéѶGϹfÉŠXwfŒ¬·@Á*3’¢1>ÇÎò&Y`}/ULDRŸ»Å~{²~ÄÊ¢è¬6Ë`a$8ï¦O7Å.¯rƒó]´(ªn™éCæØ’Áqž??)fµU¼O—/gFRaVÃCG2`²ÒNYku†í°:Å~p8êX¤Øâ>™"a,!#žx‡n¹¥an‹áY¼ØO‡¹Ã#‚5ÁŠd‡ÎåÕú)£Jàý*O(¤^•q»ŸÞ<ƒU 7U ¾Ö †­°"‰MqÀ¨SJÌzWg3¶FÎÄ g%èG6 tv„†ð<±êÅKч­dåùm5×,|H-iáÃvqN¾¾ñF3ßO ;°páÞðÉYf*ö—E‹1¨FSá1&C‘Qe°Ú´¡õ½!Õô²:g0T?¦Ä­…' [0©ÌPº¥Ä¬×‹˜>­6Û <¶ ÅóĪ¯Ø‡Å-N°ÇTó»^jßUíÜœµO¶öýdñ­ãZQ+¬jc‘o¨ñÖ,Õ"q,é&,é&‘n²‘Ñ¢›–jª©©©©©©%nmI°Ö­[—Í™3';vlvùå—g‹-Ê><ðù®]»²›o¾9»ì²ËBSK« é¦~”nêGé¦~Tk™;wnÖÝÝ?>ëïïÏžxâ‰l:OÃäràÀlÒ¤IÙ–-[:â V'aI7aI7Ùˆt“ŒÝ:f‹ðÒK/ ¯Ë–-ËÞ}÷]ÁÒªBº©¥›úQº©•ÁjFzzzBV ?~| Yl²=a:vìX[Áòª8ný4ÌHãyÕr —gV V±eáÂ…!Ý)¯¼÷_Ä÷åÏS½ß’ÿíç¦L Õé­ñ0ŠþÉ“³¯Þ~ÛLŸJx‡^}Õï£Í›³Ó×_Ÿ}þÌ3¦ãUÆÛ»zµ=^ooèGJ]lÏÇÎÔóÅ öxä®»ÂÕæöïˆÙ¡¿½ï>—ù.Fœ5+;òÜsuÙ:Á÷ìÜ™93Ü«eݼîݱ#Œ··[ãÑ}ù˜1ÏÜüÿ¤IÙÖ®.s<ìÿÜÕW‡ñ³öô_ÿµ×ý<üñ'yŒ9›ûúÑïà† ÁãmõÅÏÛš`­Zµê¢ «(-÷S©œ“‹…ÚdX‡^~9;›~¬fÝä«ð(o`õÅ„·¹ Öc5³oëÖp~,ub‰õeîø¹ø²ÿª«BÙKýÀâ†vjã}ax1áHàyë†`Ôèûð©§:ƽçÚÁúÈó€Ìƒ57ª{e¿Ïíñã•+Íq°õþ©S}Î(-Y’ÊɆ ³3'fGŒk9Ò¨±H<;Øi,"™ª²L¤cô³ýèGmE°>Í çá‘*¦c§7,×ƒÇ ¿\ظoñbS§WQ?ë€ñ¶­^ô£.š•~‹â½6&yï²e&iøˆE¦x¬Ë×èe›­„7Rº}ðÌ3¡È9˜ÖXžöè9׬‹<±($ÌBÆÃÿùàƒÏ‡~;qã.[ƒžÞõë]¶W)ÍÃXYŸÁ¢ Y,ÊÝQg°ºººªž«zöÙg³åË—|Îù¬+V´Á¢±Â…{áÅJä^«4n¨¦¦+l½y&ŽŸIáÇD'#Âd‡YãD±j¾íxøaS¬"Án(îÜ)xÞºý1¦ŒÁÒ.=íÑs®Å"ÏÖ™,ú<æ26Hà¶öÅì"x9l»³Ö‡ï§€õaÖ l„0’õÆßµ8jK‚5f̘Š-ʃù€rÈ­AÈV___[,Øp4J¯§0H ËZg‘éã´( ËJÞ"{V^U012L ‹-“J«2Žܘ‘$5߬=U[1a78gtd…ÎX£}\ ‹>ŒÅËdÄ/ög#xžXÕðBý¹;îY&Î1¥èÃáØc»Ï5ëLVÄúÚ5aŒ¬·@Á‹™ÓmÝ||ö¯XacXH“,°Î(Å$A$õxŒ=;ÄÊ¢è&÷6"X’ÃHp±Ú½Çýq•öÅ¢EÁP-pË÷…@æØ’Áqíx衤˜Õî&Á!ƒ÷iN¾ùw*ÌjxèH†LVÚ±x¸Õ/Ø«Sìg€£Ž5ç,î“)’Æ2â‰wè–[ÆóÄ ò†ãŽg¦šÁŽ=¦˜#=×b&‹mÐÔ~«ˆÅ(g­ücÄÃÖÐÅ2Îíá|™Õ÷3gˆ%Œ dÄâ^*0ðuqkºX»3%cýÇßþ6`à¿Ë™YÕ"l`•+Á{­o¾1V¹g2{d”pðlÙE=-p«1ý¯0Ä úz܃™üföì0ASaµb¢#a¶Ê`•W[lÓœ?þ‡ñÌõµ¼Oý"¢`G,VÉàybÕ‹é9=yrÀ‚pYÛãùŸþÔÔ½æZïÒ¥É}H¥l™¥dL¾ÎI‰e¼ñŠidáønvbÐË"£1ðq[JÌ”x±Ÿ Œ•vx”Áj`a1ãÀ«õSF•&Àûk׺dÎʸÝÏ?ï‚˶x‘Pz<…TÆô8à?’˜±YŸ‰ñÆ#›@à>;a‚9ž–WzÙ£ÎÆgåC¬ýcÙoäw`áÂðý¼ZÅ4f²&qŒ5¶Ô(©Ájñ›Ü­T5½¬T%¼h¼l‡¤œìµÆÌs(I‰Y¯=1}Zm8¶?xlA5Šç‰U/^±‹[íjsÍʇÔ:7gí“­}?™Eë¸VÔÁ «ÚX¤ÄƒÈÕÚVKµGKº KºÉF¤›ld´è&‚¥Z„®F/Ý„%Ý„%Ýd#£A7¬¬E¨¦¦¦¦¦¦ÖÞMK,­Î¤›úQº©¥›úQ,ÁÒ¾¸tS?J7õ£t–Î`)ƒ5ÌFÅq/¬Xµ½ñ>Ú¼ÙU7O{饀Gyk¬ßz+ܸͱmÛ;ï„›ðc©K¬¿ÿ}¸øIÙKýÀâ†vjãAê¬/ÛõÄóÖ Á!Z>õTÇØ£÷\ë]»Ö¥Èsð‘kÖ„;¬IqlßÞtSöñÊ•æ8ØúÙ)S|¶Ð–,ÉŽçó̋ńg¯§Æ"ñ¬·Êeà"X-H°¾zóÍà<žx#¥Ûù†"ç`ZcyÚ£ç\³.ò\Ä¢0 ÿô±ÇžùVdÞoßçÜkWæ€C<‹GÆÊú eÈbQnÁj#‚Åy/V¸°c¯§b%rëUZÄã†jjº±Â¶JÏÍ1 püL <&:&»ÅlùL A›ù~òälÇÛbÉv“zûÄo¤uûテ1# ¤¶Ë‘²GϹ‹<[f²ÀŠÛ Ç)Ȫ¸­}1»_ßy§¹>Ø8vg}þþ¢€5ó—[›Æ¦ÉzãoãâHO¶Á*¥IaÙhœ^ç–( ËJÞ#{ÆÄ È01¼¶DqÈ8fp=2’Ø ÎYáÇŸ³–ºéÃXܹLF<ñØêH]ã΋-5Že*žc²èC/{ôœk^™¬?¬YÆÈc 4fF,12Žæ[žùBÚ#Y“–ç¼{v ˆ•õØV‹f°0œÕî-±Ë«Üà|-ªYw)å 2Ç– ŽkÇC%Ŭ¶ªÀ!ƒ÷éòåáß©0«á¡# 0Yi§À¬µ:ÃvXb?8u¬ÓfqŸL‘Œ0–O¼C·ÜÒ0ž'ÖPx7w<3Õ N«Ø£×\‹™,¶ASû­"„„³Vþ1âakèbgÈöp¾Ìêû™3ÄÆ2b‘Q_·¦‹µ;Sâ1Öüíoç Ë$N¬ V¹º¹Ñú¢»; b¬>?Ü ÞÞ߃gË®ÑÊáÍÜò†xA_{P “ßÌž&h*Ì¡öüé_óùŸþ´iÌzÎàÔØ¦97~üã™ëkyŸ úE2DÁ (X¬’ÁóĪÒszòä€ájw{ôšk½K—&÷!e¬H­ü#crbÉÓxãÓÈÂñÝìÄ —Å™¨ˆÛRÊ`¦Ä‹ýa¬´Ã£3X , ²Cçòjý”Q¥ ð~•'R¯ÎʸT·Î`ÑØ/ÊT)øZ+˜2fŠ­˜¡VL)1ë]EÌØIŸg%èG6À}v„†ð<±êÅKч­dsmã‰K¹WÍGZøÇ²ÞþÈ#&$îÀÂ…?xÃ'gy˜©¨‹E‹1¨FSáaKC‘je°Ú´¡×½!•ŒÊ +/Û!v´bzÜõ4xlAyàyaû°¸åÑ®öèã=·<ü£µï÷8ê¿<Æ"×ȶ°–jº7é&,é&,é& º‰`©¡«ÑK7aI7aI7ÙÈhЭ- Öºuë²9sædcÇŽÍ.¿üòlÑ¢EÙáÇ/ú½×_=3fŒ2XZUH7õ£tS?J7õ£2XCÉܹs³îîîìüùóYöÄOdӹΠ (¼`Á‚dKMMMMMMMmÔÁºôÒKþ½oß¾á:uê”2XZUH7õ£tS?J7õ£2XHOOOÈj!ÇŽËf̘‘=z4¼OE°<÷s÷|ö™ÖÞ;\ÞoÏήºyâyÚˆ'V§ë¶»··3íÑy®Éh®µ’TÃj{‚µqãÆlêÔ©g°æÍ›—íÚµkàój b[Q.\ÈTd¤¼ò>>žß—?Oõ~KwwvnÊ”PÞÇ[û'Oξzûm3}*ázõUs¼6oÎN_}öù3ϘŽWoïêÕöxy¦)u±=;S{Ì/Øã‘»î W˜Û¿#ÒßÞwŸË| #Κ•yl`‚³3g†»¨¬ûW‚ãÆííÖxôc_>fÌ37ÿ?iR¶µ«Ëû?wõÕaü¬ý#ý×íµA?üIcÎæ~„~´Æ;¸aC°Çx[}ñó¶&X«V­ '²VEBU©µS©nS§6™Nñlnø^µù>|ê©€Gyk,47nsA¬Ç])ÜYÄØÅR'–X|?_ö_uU({b©_,Cm¼/ /& ÂÓ»~½91“Ò<Œ•õ,ÊÅ¢Üu«««kPÖªÓ)FV¸ÈãÙ²X‰Üz•ñX]SÓ¶Õm¿ÅssL?“‰NF„É ²¾w… ŠÍ|?yr¶ãá‡M±Šd»¡¸s»â´n¬²3Bj»){ôœk™,°è?t²è»‹Û»yà¶öÅì"œ0.(m<ãó‡ô¬™¿¡ ¹Ñ¹ilš¬7þ6.Ž:ê¬ál¶#Á‚ G£ôzúƒ$5Óãi œicVòdÏHY§ìçòª‚‰AabÄ-“”˜•V18d3¸1#™³ÚŠ »Á9£#+üøs¶ÀÍPVÊÛk•Ȉ'^ìÏFð<±ªá±¥Æ‘²LlÙ§èѶGϹfÉŠXX³&Œ‘õ(x13b‰Ãøì_±Â<ư&Y`QŠI‚HêSâa·Ø/cÏŽ±²hºÉ½VtP8 ªÝ{aÇUé»Ô誃-×áùó]0qÈà}º|ù@!okÉP€ÉJÛÛauŠýà pÔ±N›å9¦¸½ñÄ;tË-fxžX8pw<3e…ãi^s-f²Øµœ[lröÑÚ?bsd6-1Èöù¶ú~æ ±„q±"¥`àëâÖ´UíNƹv\ofVk‚U®nîAv¼ùfÄX}¾ZïÔ+Aü¡ $Ç ·Ó÷ÄŒÉû·Ö 2‰ƒ‰}ìqï zFÂl•Á*gFئ97~üã9{¶é}2èÉxk<2 `±JnÏ«^;T 7Õjg¨=ø¸ mfËg8xlÿ€Z3˜õž/ˆ˜±5r&f8g¼ñÈ&”ÎN˜Ðž'V½x)ú°^¬Ts VJ»¯×¤Ì˜Tó‘þ‘¿ûÈ /d_ßx£™ï'†X¸ðòs÷Ýf1‡™Šýeq&Š1¨FSá1&C‘ÑŽ:ƒ5[„#u+¬Õ!Ñ¡ô²JéÕ)q‡ÂŠ+ÄT޲¼¸íÓ¬S«×‹˜>­6Û <¶ ÅóĪ¯Ø‡ÍlyÔ‹•bÔÂJi÷õà¥ö]ÕÎÍYûdkßOfÑ:®u°Âª6)ñ†oe°T‹PMMMMMMÍ©‰`©¡«ÁI7aI7aI7ÙÈhÐM«kv*–t–t“H7ÙÈhÑMK,­Î¤›úQº©¥›úQ,ÁRSSSSSSÓ,e°†Ù¨8î…«¶{5ªÓ»õcO«nžãÖ©XÒ­M±œçš§ßêhÙÁs­lD«Ånrç±ÖþÉ“]îÛâÑS°<*ÒÇÇ‚ÏΜ9¨ˆe;3{v¸å׺¦ ú’»Ò¬n.b›4)ÜËd]-bqɦu_o¼‘Ð-Úˆõüö´Gï¹ýVêš›Õ|VÿÔ©¡8·—ÿÇ&SÜVÖùk®(™dÙ°wô²¾zb€Ì=ùd°ë²F\åñ³ïúë+b‰`µ`©œ¯- ŽÑ‹ çÖÅR‹mûÛo‡ÚƒÔÁ²žlÛ6m õ©¸IØÃAr?7™÷_uU¶{ÉÓÉM?î[¼8`qÛ7ãgÜÀâFø³×^ú’ûŒ,²'ž·n±Ju­‰–§=zϵO^}5;3qb(ùc~€9'ß`¥.ü]­}“Ç 5¹ôô´i.:aëÄ5/rELÛeplyC QâsM¬6!XíçØ#‹U4H/’U4LÉòPÞ„BÖ«yŠÂRJ… Ý»l™éªž Iý5ÁU½„˜bÒô%öÂ6záf«áyë+Dx-O{ôœkŒµá¬ Ô’•9æ—G¶‡9L/óÅìm'Å3¯XÙ®'Q ‚Õ‚‹ó^8],V<[æe˜Å³l`QÐÔj˰|nŽŠîÔ®²ÚÊ(ã¡ß‰ë¯·¼reRÌ2“œU/XQÇTÁ ÒùC¶Ô¨[Ié˜Ô[lžx#­›%ÑI{ôškØ8\n£·®>M°-oMæ8ÏCÖÑz›¬æ¡ûîsÉ^ѬÏ1—c˜^¥¸¥§Ûˆ`q§†ë/ÞßQ4P¤…ÎåûBŠ+X»ÊònœVq+ƒþM…Y gLf„ +{‚u ÌZw¼l[½:ô)c‰%“^£¶T ‹ñ«´Åæ‰V£ ƒábQÎB·JD«œV°G¯¹†Nà͊Ř-|‹ú ›°ò"g½Mcîs½²W÷RñÝ=þxÅAJ<Æzó‹/VÝyÑ=X ,°Xè‘÷^Y¥C7ßV2–ä®Ì¼ÃáÀ ˜ûBâ–a,¢ëqJÜÊ85eJ2ÌZx8g¶òp±¼õ/Œ%˜8ž‡s‡j…UÞbï^œ—üíoͱ¢?°Ò­H´š³V²G¯¹¶ëþûúÍÊg±XéËý¢•Œxlåž?ÞÄ÷Ç>" óÉk¯™ÅlëäÔ©;1¥—+í¾¤Ä ãǧj[‚Ê`5H°`ÇE‚Å{,V³7w˜œÕ`[IJZ|=¸OÐÄmŒˆéõt ŽÓ³Ü·Ö˜Þx¤éÁ‰˜–xXãÆ™byö¡‡=zéSœÏVÛ…E ô²ôûV¾¿Ó¬ð{ï¼Ó#ÎIËØ3ò4¶¼uVÝän ŠXqeC Üjk²R?Zá5f8}V¡¤~=ðŠ˜l5Yc•û¶ÑCÛñ}o¼èøØNiÏ«^¼}ØJöXÔ'ÕÁ÷JxQ—Ô Ã"–F%<ägÊ`iU!ÝÔÒMý(ÝÔÊ`U‘¹sçfÝÝÝÙùóç³þþþì‰'žÈ¦OŸ>äg:ƒ¥}qé¦~”nêGé¦~Ô¬aÈ¥—^ÚÐgÊ`iu&ÝÔÒMý(ÝÔÊ`Užžž¹îg­zËëN&šGµñò£Á^XÖ%-FrÜ:Kºµ'–÷\óô[ì#;y®µ‚´=ÁÚ¸qc6uêÔŠç¬j}±Š­( . é¾ÈHyå}¼ÿ"¾/žêý–îîìÜ”)Õ¹-ñ0ŠþÉ“³ƒ6˜éS|ã8;sfvèå—]ðNýâÙ—?œíîí5ÇÛ’ù¾Ÿý,TŒß·u«-^®Oÿĉ¡œÆ'¯¾jk¹^ç&MÊŽ/Xʪ˜Û¿#¤€~<•;ÁÏ.Ü¿fŽ—Ï·K–˜ÏoW{Ìߟž5+\®¥}ðºwÇŽÐE¶¶Ç6oÎúóòé…ûÈ\ünÿÛ6m2ǃðœ»úêlÏÎæö½÷M˜ô³¶Þ÷®_쟺ŽÖxÄϳӧ‡xZþ¼­ ÖªU«BÆéرcÃú¬Õkâ€= =Óä¤Ç£Èslûrë§ yжwûöAµÐ¬uû"wsb@ijZ® èÇ}‹,ŠÝ2~VY°*Õ´ÔÍ Ï[7lΏóHÚ£÷\ûêí·C=ÅÝ÷ÜcŽu8Ö`Q›ÒÃGžÌc’uBµ_û®¿ÞE'l¸æëLt©m­¬m¬MÈ\ë¨  ‘ÃìÃý¬lߣÐsdà• eZâ ÓRÇ¸ŠˆµÐäcûžá¤‹x”Ð86gNÚ”"± >‹ IM9ÁU}j¼âÍÒÅzƒŒa37·ÞHéV©¸s'Ø£ç\c|(ð –åeÈJäDŽùå±}·'÷Ç_Ï›gŽÃ èdŠ­qbÁ瘵õ WÄ2Ë3Xíb¢ £Î`uuuUÍLÕú¬]Vdü^Y¬²a¦¬z_«EÁQ&zœˆ–¸Ô£Ü[Ñ![b¢ß‰|…xêºë²W®ÀÄÙ¤®·È$gÕ VÔ± Rc²}GÁcJ‡PPš÷ÅÏ=ñ¿”ÙO¬ZD+uzÚc¥¹fƒ“ÁýfÖ¬°Ø°œÏØÛòè˳Xç!ëh]“•¬& ‰vŽeô¤¾ÃRã0Öqn–ãVÇ݃5f̘Šm¨ÏÚ…`Á†#ëg5h‰]dÞÑ@qXV˜e¦_\ X¸0)n¥UN«¸•ÁÄO…Y gLf„ cI°Žõ쬞ٶzuèSÆ'J&¡ÌZXŒ_¥-6O<°Xñ7’ÅðĪ…W‰h5k'#må¹–§:C6ëpN‚Rû®b–“>cÛÕ²€1xø`ˆ°F¬§ˆ“-³Š-àì¹ûîÝ‹ŒR‡Jä*%òyYmçE7¹7H°ÊUÇ=ˆV8¤šLÄcuæuGïÒ¥¦zVº/„bË8áÔ¸µîAaÕsrêÔ¤˜µðpÎlåõ_}uÀ;Þä˜ÖsÇ 6DæÜøñ?ØÑìÙfXÅ-6V•à±-e÷wÜåX£]÷ßßÒXõà‰V3cÖJöÈJŸ€ÞŒMÔƒ÷ÁÌÀw±¶?òH–—Ï›Å9)HI1ÖXø~²‹1-Gf¾`×÷R±u =ïÌõ²º+ôS>æGn¼±âÙaÕ"l`E‡G[J-²á¢ñ7[é~8+Ï"®Å¡ØJL¿<ÙS¥Æ‡ZÁ1S¸¯gÅÄê'f½«³¸•Ñ æpV‚ÞxlÙ„y¾zmÏk8x1sÝŒ´Š=–ç·Õ\³ò]E¬"ÆfƒXÀß½÷ñÇCvÉÒ÷”cðýGÿâ/Ìb‡è‹ýe‘ÁêzùåªD1^ÌÈ×"£Ê`µi-Bï»CFêî—‘Äõ¼Ã'®J-Is¥ ÃjÛâÐv+à±BöÀóÆò°o{ô˜k>ÄÃÚ÷{Ø÷ˆ_cÑh_‰`é&wÝR,ÝÔÒMý(ÝÔ‰±D°T‹ÐÕ襛°¤›°¤›ld4è&‚¥ –VgÒMý(ÝÔÒMý¨ Öè:ƒ¥¦¦¦¦¦¦Ö~MK,­Î¤›úQº©¥›úQ,ÁÒ¾¸tS?J7õ£t–Î`)ƒ5Ü;J6oîH,Ú–*E1Û«“uS?J·–ëÇžùÈ6ÃÚÚÕåÚžxÕlD«ÅJåpß·š{\h å%¸¥ÚëJtx\Ø õ¢(kQ®“g‰ÇÍéÔy³¼ÿ…ïæflî/¢Ö›åLE,n/ï~áó{s¼ðFJ7.‘dÎYÞßãiÞsFME W{Ü…´¨oWÍFð‘^þŸò2÷aQv ñÀ¢Qôž;ë¬ïÈŠ‘Ó•lQ«kîzþùp“³Ç%mÛÞy'8aœ~Ê"¶U ÿÍ7«·M®Û¦M€à ™l8d˪¬˜¸„’r L8ê£YVL齂)§lÄÖ¬1Áâ†vô¡üó3 æ‰ç­6¹bÎQRÈ’lyÚ£÷\ïËÜwƒeM~>~ë­0¿(-äáÿ¿^´È¬8ò ìÕ“Of' ˰ 8>æs,ê®Æx¶ýí·Í‰UŒcÄ5e°Ú„`±ŸKQI {íÃø .8{ËŠô¯ZqÛ”„«¸/N`‡ V\i§Ä,â‘UÂ!pX¹Qt™š‹©0‹X80Šéî[¼8`1ŽO$éÍ`–ÏPƒJAZƯRȬFƒ¬'VRU‹l5k'#ežs +H$cgá»À"órhþüP.‰Çz~>ò‹îîɦ¾?·ã¿þµyŒ‰±ÌâLýDÙŸðmøÄÔµJqKg°ÚŒ`qÞ ! ‹¤z<ýÙÿéI“L+ÅW[ €y8wb–Ov°ª.®´Á$X>IBV ÃXÆsÍbÖ:H !› /ƒ0¼‚¤Á¦RÈ,ô$£×ÊXµð*‘­fú°ìÑs®AzÁ‰Ü­|dûû)Sýgå#Ý|ó@Ñt RÂ÷’•c·Ä*¶5ÅŽÉ^±Ð³8ÇKsUÚ…I‰ËdUÛyÑS„ ¬r¡R/¢U¬x¾ë¾ûÜÈ]×2ÅÑÖáC%-[ï>}Äô8iÞYè_¯³_,^ì† ¹Ûÿá†÷Ñcœ3–x^X­]K—š÷¡§=zÌ5²YÖ>¤è-¶ã˽9¡KnÇ =[Ç´}wÜ1ðý1Ë”|«xÕª «­bæIÄ`[U÷`uÀ=Xçò“°Bê6\aTÒË·R?oܶð¸%f$˜¤^÷®¤Â¬×Sôíplß/fzxmÏk8xÑN¶?òHGØ£Ç\‹[aTÂòð˃Ŋõ!wë¸ ™+¬"†UÌŽ¤´ZÖR,݃ղw“`´œm`¥à©ŽŒ- ϾL…Y︥èÛáØˆ7ý¹ç®»ÆóÄ^ ;i%{ô˜k©1t–ú±RfQ÷`é&÷–Ä’nÂ’n²é&-º‰`©¡ššššššZâ&‚¥ –VgÒMý(ÝÔÒMý¨ –Î`i_\º©¥›úQº «µukK‚µnݺlΜ9ÙØ±c³Ë/¿<[´hQvøðáðÙùó糿üË¿ ?ƒ<ÑŽ;¦ –VÒMý(ÝÔÒMý¨ V-™;wnÖÝÝÈTöÄOdÓ§OŸ=ùä“Ùš5k~÷ÙgŸÍ–-[¦3Xjjjjjjj:ƒ5\¹ôÒKÃë´iÓ²#GŽ üœÌÖøñã•ÁÒªBº©¥›úQº©•ÁŽôôô„¬rÙe— úŒ,Wùg­~kïŽnX{>ûÌÕè=ñ¤›úQº©›>_ÓÛë«ÛÎÙžq­Uú±í ÖÆ³©S§œÁ3fÌE¿Ségñ|V™`-\¸0©ÈHyå}¼`,¾/žêýÖ®®ìÜ”)5¢,ñ¸0±òäìÄ’%ÕÀ­ñÎNŸž}wÇYïÚµá½Þ–îî¬o„ìÛù󳯟}v ²º5Þ‰oÌŽ­^}òÚkfxLæþ‰C¯C÷Ý—zõÕlÛ¦M&xèunÒ¤ìäœ9¡€ôž'Ÿ Åh­ìßr*ôãéyó²#wÝn¡gXÙÄû>·ÉË—‡²èÛ)öÈ ÷ûV­Êöå?³²ð>'<àQî³—^2õǼöå‹÷/sÛ°ôWñýG›7g}³f™û+^)Ét.·ˆˆéxåï{ׯÏú~ö³᪀ ñõ¢EYÿµ×šÍ¯øû#~ö]wÝ@½ÅâçmM°Vå“™ŒSñ»U˳Ø3Ž—B™ÔV²¬ÚN«TÜ¢"}l”æ¶e YSÚ‚›±cÙ‘X±<µ)ÊKY §‚ ^ÏsÏ ×T˜8¬my0£80UÝù=QÄ·ë•W’bRæûØOî£y@?Š‘‚M5ùb™fo¸‹RŸß{o¸¥ÛÚÑ>Ÿ1®åùÒ¨ŽÃÅk¦™'VÑFö-^ -3ïŽåd¬ŸzjP¾fíÄÓ½ç56ù~Æ‹¢ÅØ<}gá»Ð>çƒgž1õ4ˆ>d5ue‹j8Vßm3ŠñÌ'ŽE¥X–#~¯»ï¹'Ì©P0ûþû™îˆ  ‘ƒîe™1cFvôèÑAg°Æ×V Ý0ÊXMݪj{qï¸hœj+}‹{ÕÝ/¼Í™ Ù0&|È ˆ…³¢OÁ;9mZÀÄi‚y fyž AŽÜ} /7‹YÆB?ŠÏB¤ÍŸÈk¬fÏk3uà*/À¡P rßä+nlýЋ¿!è¸p¡ XýW_Ý~žX•ð" Ú{Ç”€&¼™>I{ôœkd ŒU윀—ÚwE,È"8§¦L1àAºã¼µ >|ïñnÈöæ¶g¥G¬Iɕՙ(0*«”x`0/ì™ì:öIpGÁêêêªzõÂÓO?ž,>E¸”Êô ¬rÕq¢EбXQݲ°hùlŽÑ²"}¥³làõ-®â­îA!k¶ïÎ;“bÖÂc"2!¿X´( f=gq4¬RÉÆ4ƒYúA˜?]¾<;tË-nx»ÿïÿýÁ‰ç¤§ì$œ‘ò]j#ߘ—8KŒFÎC»¬ksöɺä’K²•+WfgΜQ-BÝM"Ý„%Ý„%Ýd#º«2Ãö݆ ¡sˆ€¾ÿþûªE8J°¤›°¤›lDºÉFF‹n#rÈò6”³ùïÿý¿W=K¥ –VÒMý(Ý„%Ý„¥ Ö0¤¯¯/<Ý÷ãÿ¸m–šššššššZKžÁB^yå•ìÊ+¯Ø"¬t—•2XZUH7õ£t–t–2XuÊĉ¹?úè£!“Õ.‡Üµ/.ÝtvBºI7õ£tS?¶ä¬v¾¦A« 馕§t“nêGé¦~lÉ ÖKÔpj1Ñ,555555µ¶?ƒÕ®Ë“ SQÝ ‹jãžç‰'ÝÔÒMýØnºmíêêHÝ<õòŽ£Õ°D°Z¬Ø3å ÎΜ ˜ZWm§–Ö¹I“Béð¨ƒÆÏ­p#S)nK͵ˆû8Uô€5a Ä<óÌEu¤RaF< eSlùó{ï E|)°[É–šÁŒXTsgì(ªË-Ã>õTŪîÍÜp]Ä¢Ð)E{{—- ºQ.¤R=6o<ú[je¬²Pp™Û§©ŸGÃjXÚÉHØ£ë\ËýÅwwÝè? BãÖþ¾\¯ƒ „ùeéiøÃþܱ±FnNCúѪ¦b1nbç}×_?Èߧƈýó ›LU×¶<æ|/u{w/Yì°–V‹,Ú¶wÞ zOçÆo˪r^ÿUW…€ÁXáÂô‹† f$\`L¸EËêìkÖGEð<‘Op‚1€y &x8{&8¤çèܹ¡vÕäÑõ‹ÛopšÍbnééÉÞ_·.M‚ Åÿâ+ö)Aa÷=÷„¿¡™:`A8púô5¿°ˆ ýY&(Þx`õ_}uCxžXEùxåÊ0Ø#XeâìdáÂö°G繆¡ÎXÔ ¤ñïÔ>$øãM›Âß.ŒsËÒ/÷®_æp­ÚwÍ6l€É_/Zd_ÐØÂœÚþöÛf‘TŶsÅŠ0n©0°[p*Å-|GµìœÖ«\uÜ‹h«¶¿û7ã‚ ûßµtiÒª÷õdì \8{\Vk1ºåÌ÷6lÅB Ð8kLðXmãdp/+LVøe‚$2ž )%ž'vY$^!«>ô²GϹÆbcïÝw›Ï-æ•5FÑ÷[|Ž’-cýÃw3{¶YýNl |þÎ"VF Z¥Ájç½*ª—õ²ªH?T?Zà5f©1뱑T˜Ã±Çf1‡kûžx¬VÁâ2ÔÊXÃÁKa'­dsÍÊw•±šÍ,Õ¼|?ÄÁ:®íË I,Âl…U$¦Äc¼kÑ–ºÉ½ Öh¹›Äº"}5Ý,p‡êÇÔ˜õŒ[*ÌáØH³˜ÃµGO<°ØRkt…ì‰5¼vÒJöè1׬|W+õ9/݃Õävñã8+¼¡ÎݵT-Be°t7‰t–t–t“è,,55555555݃¥ –VÒMý(ÝÔÒMý¨ –Î`u<–t–t“H7ÙÈhÑMK,­Î¤›úQº©¥›úQ,ÁRSSSSSSÓ,e°´ªnÂ’n²é&Q«]ûžW\qÅ Ÿ>|8»õÖ[³Ë.»,´›nº)Û¿¿Î`i_\º©¥›úQº©uk(3fÌ@+ÊäÉ“³—^z);þ|hk×®Í&NœØ–,ëbÏÕô²¼H¯V?¦Æ­gÌRbÖk#)0‡cÍ^À8\Û÷Æk¦³'ÖpðR\šÙJö˜úÐjx¾«ÒMî–‚çáûù~ë¸VÔÁ «ÚE£)ñ†Ž=ƒU&XcÇŽ Ī(—^zi[žÁ¢€¯g‘éâ¤{4àŽÌXB¤bÐÊx`Q¼ÖÏ Ë³=ìÑK]<0<|¿Glñ°Æit<:Ž`­[·.{ì±Ç² 6dÌÞxãlÉ’%‰UlEY¸paH÷EFZ©Øsñs^S¿÷Ä«¤_Ñ`=ñRë[VlÞxÍöïp±Šz6k„× –•ý§š­j)æ·Ç|Ê=ü¿Å÷—íÃ3¾xÄK ¼FçOÇ,HÕ 7ÜHÕ•¹ÒœÇúöÛoÛò –ÇJ©’^–¸ÕúÑb…[Ϙ¥Ôµ^I9{l¶o‡kûÞxÍd•<±†ƒ—b>´’=¦žßÕð,|W¥Z„–~9tLœG–,bXÅÐjz¤Ä*ƒÕ±÷`• ÖŒ3²ÞÞÞ÷kÖ¬ÉæÌ™Ó–g°¬÷ú[é –E¡VÁÒ,ÁÒ¬v<ƒåáû9»t~üxsŒx>Jg°:€`U:oÕ®g°ÔÔÔÔÔÔÔtVK,®e`›á°ûªU«iÒ=Xº›Dº©¥›úQº©uÖ0®i(^×pìØ±@šxšÆY¬v=ƒ¥»I¤›úQº Kº K÷`é&w­*¤›°¤›lDºI7õ£–Î`©©©©©©©é –2XZUH7õ£tS?J7a)ƒÕ)KûâÒMg'¤›tS?J7õ£Î`)ƒ¥U…tS?J7õ£tS?*ƒ¥3Xjjjjjjj:ƒ¥ –!¶¸Ù¼^½FËMî^·K[`ê&÷ÿ‡µ{É7Ý:í&w/{ÔMî£3«¤›ÜE°Zò V¬Ýµý‘GÜ÷ŽÁ=`”Ék¥Z„^õÑ,0U‹ð‡öõ¼y‹×Mý׿ºuZ-B/{ô¬E˜ÚwUªExÀp§#ËÉÂÙk¯ÍÞ_»ÖÔ÷oôÑ K%r’LÕ"ÁªE°ªUͶl[»º²ÞeËð6þîwfX[º»³÷×­ “LÀ¡ùóMqaúïmØõ<÷\öñʕٮûïϾÌÇà›Ù³“÷/Xï¼þz¶ùųŸz*àѯû/Î.XÿÅ/’b‚GŸu½òJÐ>ýä7¿ÉvßsOÐñð¯~•};cFL°ÞþÛ¿ ¤âkÖýv<üpöéòåï‹ÛoAàèܹ٩)SšÂë­7Þㆭ€…n|ðÁ0~Ø úa;ÇæÌɾŸ<Ù /ŽaÿUW5„7\ÝÁºúê¦t+Ú„#bí½ãŽàÈÜtSöÝôéMÛ‰·=zε-==A/²dưwúÍÂwá#™c;W¬ýfí—¿úõ¯0˜ãŒKÄ -0øÛùþogÎ 6N\³Â83iR°ç=wÝü`÷ /„qKMâXÈaÌWlœy…ïP«É –±ŠÃê…<~à ¦¸`±?uÝuÁ9áà1NËÚ™‰CÃáâx™8}&yÀ]¸0é¤@¿“Ó¦…à:♂”ºÆ@Á@?‚2“‘€†ŽÛV¯Î>xæ™$˜qåIf†~|'<ˆc¹åÉ'CóûÛV­j‹-°¾ûùϺA¿Ïï½7èG§O½ñB¿ç¤§xÛ£×\‹~‹1ûfÖ¬Ð<ôIí»Š>£˜5µð}æx–¾°=° Æ–,l,0-H)*ìŒD ;ÄöSá1#eî0_ãÂ>$f‹`µÉ¬È¼‡Ú÷M‰5Òg°,pë³”˜õÚH LÁJs.ªUÏ`¥°“V³Ç”s­ZvÂÂw•±¬ý2x¾?î–X~Q‡6o6Çt6+!žÎ`é,ÝM"ÝÔÒMý(ÝÔºK,ÝM"Ý„%Ý„%Ýd#ºK÷`©©©©©©©©é,e°´ªnêGé¦~”nêGe°tKûâÒMý(Ý„%Ý„¥3XÊ`iU!Ýd#ÒM6¢~”nêG,ÁRSSSSSSÓ,e°´ªnêGé&,é&,e°tKûâÒMXÒM6"ݤ›Î`‰`)ƒõCK]õ¾^Ý,p‡êÇÔ˜õŒ[*LÝäþÿ°(ÑXÃÁKa'­dsÍÊw±¬ýc'ÆÊÊPÖË ‹ò5• V§Æ£DNµÛÜ;2ƒk¼âŠ+.úù®]»²›o¾9»ì²ËBkÇ3XLdêu?ÿ¼+n¬z¿ý‘G:W˜íëÂñJ¡áNÁòêÃNÂñÄh´e+ù~J>Åš¦Vûî¸#`PÓ #eñZ$Œj5;î Ö˜1cZQ8Mš4)Û²eK’ V¼bóXUD¶LÛuß}.+Š~RÌÔ²Z|™éƒAAbŠ§Æ­´ª x+…tw®XŠu¦Ä¬¶Šy÷oþ&èHßRˆ4f­Õxñ¥È*«G ’6ƒY ‹â´]¯¼+{ÆÑ/a¥s#xžXµð°Ë÷×­ E¥É’E2׌Œ´=zÎ5ïž¼ß,|W‹‚ÂýLHINÞ,}$ ±ñÞBmô ø·U¶Œ1ƒ‚÷ø»báêTxƒ¶ÿ¶Û1-ÈîØ3Xe‚µ,wï¾ûnò-Bb50Á¶n ÕÇ¿™=;;““EKì½;vd;~88ZªãØßpƒæž;ƒs'ˆ|3kVÀdrì½°Ò9˜0ƒ¸ç³Ï<+(pX­Q©‡ÉäD˜à$éOô¡/©´NyHç˗'ÁÜÝÛ n[½:è±?ŸÈô'+¬Ø§‡æÏÏvßsOÖ—ÿ¬™•=Xe…}Òô'«z°¿ºõÖl×ý÷‡¿Á/’žFð<±Š6‰côÝϲ`‚í`CÍÚ‰§=ºÏµÜèÀbŒhaä>3µïÚ»}{° Æ }N̘aê“ää;;a‚ ãÂ÷ýå/³MnyדY¦±½}F[„`@®À‰1Œ÷Ķ”Góï­·°ÃjXG°ÆHÖå—_¶!LÇŽ«H¬b+ÊÂ|•ÊÖcd¤¼ò>z|_þ<Õ{*€÷çxo8YA[âÁÌûs‡{*×á>|ê©Ð¿Åß}Sâ\´(;.‚hÐ'âÅßz6ŠG0;—;_‚Ê·¹Sù6·÷/ò€Iøø­·.ÆËê—ym½ÎçcvvÊ”àX¾Îu;–ÛÊçy0%x—ûsDðXé_ØN®=ÒýS§fßå6 ‰ÚŸ„C¯¾šý1rdAÊÿ?,6lhØþÁë»þúìxN:pÎ{òÅÆþƒ^åßof>Œ„=B>ŽåþôèceŸåä¿Ü©æ7xøHâW¿þu¶ÿ½÷²-==ás¾œTþ’ïÅ>¾¹í¶°0何 Ûv¹?<—Û~#ö<Ü÷,ÐèGȪÅ÷Cäz×® ï±ó3¹}lÛ´)¹>Åñ¦ÿ޼ðB˜_Ø%ÑÈ{0è¯øžïÅäÄûô¯~ì¿’¿è8‚uÉ%—dÝ8éóç³þþþì±|²ÏÏW‰ítËbk®VúÓS7O<é¦~”nêÇvÓ­¼ýÔ)ºyêåG«au$Á* DkìØ±zŠPw“H7õ£tS?J7õ£îÁj”`Mœ81;räÈ ŸýèG?Ò=Xº›Dº©¥›úQº©uV£ëÙgŸÍ–/_>p§'[±b…2XZUH7õ£tS?J7õ£2Xù¦¡|]Ã>¹³5ÙêëëS-B5555555·¦›Ü•ÁÒêLº©¥›úQº©•ÁR-Bí‹K7õ£tS?J7aé –2XZUH7aI7Ùˆt“(ƒ%‚¥½c5555555ÁêØ –õåle½¸±–´­ VëLjûþÚµn+˜ÌuëÜVL©0ëµÇX®‚Ò›þú¯ÍW‚#Ç ââyb wÜš­ds-b¤.Ä]IJöE<ëK@ãÍãÖÖ1”9‹p[Ålì–q¯VºH¬ ×ãSBƒŽå•÷û¹Åbœ)IÇP{Çшhä®Ú^5uRQjþ»éÓ“b…G飓?ÿyÌzÏì, o´îpÎ2|úÀ®xGçÎ 8} `öĪywjêÔ¦ ·’=z̵¢ïJINŠXE +’5È÷'"¤å˜ —wçf1¸UÔÅâLs#~?õ"-Î`1O"qJg°¬¢Äæ‘Á*V??nœ™že¦ l,@J±VË õcáÖï'Oþ¡Ú{8-3XÝ/¼í[¼8A>9mZÌZ«3Vð&¦V…}cÜF1kaáàÊEw¿¾ñF7<ŠƒEÞ²ƒm5¬ZxªbÑõfǬìÑs®á/É.Zø®"ÖÞ»ï6÷˽ë×›bxÅ´˜UÂÎ Š{†øP乘YN‰¶Eœª”hQ«É-BbUl‚e•Q+-™²áÜq‚;W¬È\ÈØYâ‰U4X0Á·Àûà™g„ãÐüù¡Pg\™X`v½òJ 91¸ì¹ë®ÕhjLôèyî¹`3¤`ÛêÕ+ù”˜8' 2‘ûüÞ{CàæoñÆ‹Ï"v»`•IßKŸALR÷¡§=zÎ5Æ‹~caÈ}eä» ÛòØÀX‘¹²ôËøBübÌê›í^,Y|†Õ÷ŸÿÉOÂ\ã‹Ûo7É}Ä\Â÷±…oãT9né V›,*©ãØ= qn{çìÈM7 bÿ‘옹7߬j `Æà’D·M›VÐLùûSbRÉɳgÕ†~>õÔEÁ8&ÕÜ!P8ý˜‰ hB´*ÿf0Á*g©öçŽãã•+«~§'Xž‰òÄŠ6R‹T¥´O{ôžkà´ÁbD,|×Ço½•¸þú@´Së¡Ú׋…±²Æøž4Ü).ÃBeÍS‚wab<ÛþöÛI1бªL´ˆk"Xm@°X)ÍW.©jVÃêËÞÃq€ÑŸ¯ž=⬩S­ =ð˜ÜgËJîïs“&§Õ»l™i_±ÊYªvÇ)Ýj‘ªv´Gï¹F;óË_g©WÑGBâ<ü´³¾èvnÂó>¤±(éËÖmwdÀ³œcƒˆVÞ•""X-ø!«êNÄ «¦îîŽÄêdÝÔÒ­åú±§G>²Í°È¨zö£'^5ÁÒ=XjjjjjjjºK7¹·+–t–t“H7ÙÈhÑMKµ]^º Kº KºÉFFƒn"XÊ`iu&ÝÔÒMý(ÝÔÊ`é –šššššššÎ`)ƒ¥U…t–t“H7Ùˆ2X"XÚ—nÒMý(ÝÔÒMý¨3X›Á²¾Í½Vm4/¼X‹[§»^~Ùmk}¥ºÔµ‰˜Íð®×‹}Ûè äñý‘Àã6ïFñ<±†;nÍÚI«ØcÑ&Þ}í5S?‚.©‹0W²¼03âYû~.µŽkE¬°ïX÷Ð*f3?j•GR«A‚oj-Ö"ò YÖÕëÁõºuwû£Žnœ04²D4n ÷Ä,÷­5¦7%gÀé»öZse,w/Yú¸ÌZX|7¦Ðï§Ë—‡ì'XÔ÷k¤ž'V-¼rñØX´¸;i{,ϵ8•ðÐ HÜá›oNî»"•yV­í¢xGçÎ ÄÄÒ÷S;ò°áî$qONz±iìÙâLT$>1vIVJ¼@®òy¹÷Ž;‚ ”³~w â[%Q.X° - ;NÖ£¢zdÞ• Ô:ƒÖ÷“'‡-„d,3XŸüæ7É1@¦Ä,ã}ôøã!¸à()¸1Ák¶i çOð‹à‰ŽÅm’f0+­ÎÈŽÌçNÂQ.òë‰VÊ3QVX•ð*«}8ÒöXi®¥À)ã±}ÃAxøîÔ>¤ˆɯ~e^Ìšzdd¬ ³x8Ô`&v8-Æ2‹ cM†´R KÁŠs³·:ú V%µoß¾lΜ9Ù©S§Ú’`Åì•×y//rU\qV[ X4& XÍf‚c½³l…ù ÀXž¡D‘q„T‘e!;aŠÛ€Ø ch9~žxÞºÕ"Vílžs­ë•W‚ÍsŽÈú¬[Q`yœÅ!ç39BÐIñÌ+–w^jÙ^ǬcÇŽe3fÌÈŽ=Z•€µ:ÁbÞ#{Î\­_ïJ®¶¿ýv]†™¢mÛ´iÐ…¹c|óÍXX}î¹ë.SgL?B;‚'ãgE¬Àª´ h©›ž·n؈±ò´Gï¹öÉ«¯fg&N cgQÚëÃBçòðMƒRm¥uýô´i.:aëÄ5Ï„Á®ÄO“ÖJ0×FÁš—3ÿ]»vÕÌpEb[Q.\öScÊWÞG‚ß—?Oõ~Kwwv>ŸÌqÀ,ñp¸ç¦LÉnØ`¦Oñ=éüþœüzùe¼3¿üeöåƒf»{{Íñ¶ôôdý“''²/ÿ™)^®Ï¹I“Âê“@cj¹^Øã·óçÇÅûNÁÃAÒ§ò9ÏBƒ÷–öðòùv"'ÖóÛÕó÷gs¿KvÌÚ>xÝ»cGèG²µÖþø£Í›³ó×]—}v{ù«5 Üó9Ù³s§¹}`ïÌ5úÓÚ>xÏ|ÆþÙnµÆ#~רÒ-Þq«x6k¨sZ­z“Ì ô¼ü æÖ¹³÷ÔÍsÜ:Kºµ)–ó\óô[í#;x®µ‚tä¬á|®Z„jjjjjjj©›–jºœt–t–t“ŒÝ:⚆ZÛ€©–êCI·Ñª›úQº©¥›úqøXºÉ],­Î¤›úQº©¥›úQ¬‘!Xjjjjjjjj£æ V§g°¬ïŠª¦—Õ9CõcJÜzÇ,f=x±¾Xw“w´Ô«[,%Á#í)o;o%5f={þ±†Y³˜õž/à¶æ¢ž8íáœeðÆ£t 8\:Ù^#X§'M2Õ-E¶’=ZÌï¡üHJTÍG¾¿v­ Y > wÜYaà×cÝ×ïî¾Û,¦·Šýeq&ªëå—«­N…k«Ö*Œ­3X ,¯Êãe6Ì%‹–UÛ+1o&õ¶À¸Ço¸!yÿÖZÁ°z‹N̪áEÎà•W­ë)Æ´žÕ:rùcßÕWÌã bÖ»$«ÃÍÆçÆoJÇáàí¿í¶VNz>¿ç¬“?ÿ¹©nÔBŒ…~±k{d®7;jaANNj>×z—.5ñÑE,k¿LýËb¬±ÀðŠiÜ~H\>_ðñ,JAe„-?n–ÁŠý´?'LÊ`lz–ʉ©Õ³&dßMŸî†M@µ½.dì¼p)ãÁdõÔ@дJG,VULv ›‰Xeòaõð…'ÞHé !Ðøw§Ø£ç\+f®¬±X€înàŒ_C+'$ÕÎú¤ÞV=qã>Ç]ò…˜=޹`댕å’8Øz­óe"X-H°pð8C,VÐZ§÷áë|òr¸b`©_Ü.‹ó'–Oµzãy놀G°&!žöè=×<2WÅmw²ÃÖÙýØ Ý¯\iŽ1 Sæ²+³dI° ,Ž›cv<ü°9É ÈcܦÁjq‚Å$î›0!‰!èË'˜GÚ»ˆGÊØc%s:bÛ¬E<&œ5qäû±µD,œ£uöÆóÖvrÖ¬pÞÊãŽ#/{ôžkÑXg®t»pFÔÓÿ{ì^`箾Ú,«Y^ÈôçDÄ+™ÀF<ú"W K«kîùì37¬½;v¸>鉷gçNWÝ<ñ6‹ŒÒÀœ¬›SáaKƒòBÊ`%$Xqõ›%K.ï熦Ç7½ä´¼wÌ…z}ù*LVƒ^{Õ8ÇØ×{ð¬Þ(upv„d˜CíùÓ·\jyzÒ¤¦1ë9_€Ž½Ë–…qïȯ~ez–¬(7«Ï{à‘¹hÏ«^<.þ¥@.8‡n¹¥ííÑk®Q:'µ©„ubÆ 3ÿñ,üa9¦ÉÇýË®.óDA¬Jbq&*b0åj$)ñbì¯F°t«JåÀ†YµrÝ¿EaÖjÌGŠrÞsÏÀjÐ#ƒµç®»Bú5®x¬÷Źå7êIi”T˜µVL±FÛK8‚f1ëÉÎ}?yr(Hû•ÒX±ÜL¬ãÇ­èžxÍèç‰5„‡BµŒ$«œV±G¯¹Kçì4ð[E,nÆgŒ¬ücă~n\ˆùëyó²Ï3/9xŒ ±Å"ƒcëéË4¥Ä ²Ç sfßâÅUGèÈ ¬ñŠ+®ô³uù$ž3gN6vìØìòË/Ï-Z”>|¸­-V ·.s8L¸X‰Üë|ÙÁ ÂjÝ£E¨°ž;zðªÕŽJ¾wŸ“ofÍ 5á<Î$Q7Ús1xZŽe™|üÁÐY{ãyb1¦â+V˜Ž™—=zÎ5¯¢ÏØAÜãü NëòMØ÷.ƒ-ÈrÃ<Ê(Q›Û¶œ?…Ç¿âêÙºo[‚5f̘V”¹sçfÝÝÝÙùóç³þþþì‰'žÈ¦OŸÞv,ÑÃ(#óŽÆi}(1âá¤( JaS+‡U\U°J'3ÀÊÐ/˜%K‚C¶ÈB–WL1ú“Tù§Ë—'=ÀZÆÂÉX’O¼‘Ö3$ØÛ(©ír¤ìÑs®y}‹ùDßyÔmÝñûß‚e½Ðe»ëë;ïtKX?ÕGOb’À [`k³Ž=ƒU&X•äÒK/m+‚õÕ›o§á±R" Ó«–ºyà”n‚­|v¤íÑs®ye®ÀbÊüòðÿG{,àYãÐoßç¶î±SÁÂï€ÃS‹‘3V–÷`AàX@`ëøàŽ=ƒ5Áêéé Y­v"XßÜvÛÀÁ@ëöy¾"㬗•“/·9 `½BÛ¶iS ´âÞ¼UÛÚÕH#ÙGëôþö·ßN˜3KýÀ \äX‡õ+3ò1xÞºa#à¬Iˆ§=zϵ޵kÍ3W>rÍšöº2çÛ¼?^¹ÒbpvÊŸ‹8—,ÉŽ/Xà‚ÅqbÌãóËñLäñ“W_}kãÆÙÔ©S+žÁ‚XÅV”… 6S~¼ò>¬ø¾üyª÷[º»³s“&e¿õ–9ÙŽ¾Ÿý,d̬ô©„w$wöÖxmÞüÃÄU«LÇ«Œ·ÅŠlwo¯-^þý}&B€˜Úc¾@ÁqŽÝÆ­Sð øôã©Ü î¼ù³Öïä¬YÙ±G ó¼SìqÏÎÙéœ0’½²î?^÷îØük<úñlNP=üqÑÿo½ðtŸµýŸ»úê0~Öþ‘…LÈ6æýéá!<,úÑïp¾(ÉoåÏ;–`­Ê+Ù¨cÇŽµÝMîÑùz´­†éŽ4“ÙS7Oás:wŠÖ箊g@úò§‡óºå«v¯UgÄÑxa±ãÌNΚ•ü,R«àybÅqóØÆ {ôškÑÄCú>ÒãþÃx`¿o—­HX^þ‘1óò!ÄÏ3Ž[ºèV K«kze“¢á{êæå8FB7OÒÂ?òwsïÖ€ï7*ÆÌÕ|ÿwwßmÓb]ÈØ,ÎDqCÃâ cR Cg°š$Xå‰ëqUÈ‹xþôXÁ0Ñ\ “V¸•˜>¸T*OÝ¿µV0`rIgJÌ¡VLen]OYïêlËãgg&LxǼµy8+Á?þö·áâ=/<î°‹±lÏ«^¼žgŸ 7”7Ó‡­ds ŒC·Übâ£ËõS-ý£‡ïç|˜møÄŒa|,2XÔ9ƒKgˉˆ”x$qÉe°:¡!ÛhONêŠM™ †‰í…oÃ¥`«f±¤ÇÁüÕÃMס0Ñ‘:mÜPÎÅ®àqÁ¥^¹ÔŒ'Þ‘|-ñ<±Š%t,q¼ìÑk®‘e :YÎ-ª&€aí©.pzÒ$ H„ä›Ù³M/kçË\˲o‘øX—{ƒ$Àp«ˆ`µ Á‚ {È,2o ǺlN™éë9¡³u‹ºeÅ’)1«­bB™ˆœèDg–³Öꌾ$HC°¢ý€×h­ÂZX|g¥R3žx±?ÁóĪ…W©ðs38­`^s ùåGÜ–²ðYèY p¶µO†ÀYW¼ ~é¾U«Ì㾈º‡,ÆšïŽåqŠÕRg°°ßjut;2ƒÅ¾çW\1ègçÏŸÏþò/ÿ2[´hQ O´cÇŽµÁB/RŸL¯û;pêLë²9Žjë¢Ïå}qðÐѪ¤GU5Õê šïU©U•òÌ cH&ؤ<·W ‡öÙ„þL]jƯt³*ü<’öè9×Љì·ÕÂ0bA÷-^lî“÷ìÜ™õ_u•ùMäØö7†Yæâ6±Ìú^ªÃâ™2+¼Jq«ãÎ`3f åÉ\ù5ù #ʳÏ>›-[¶¬í2X‘õ{`Eö¿ýÑGÝžZ±&WåUuûX}Z>0PÄc•Ë–NÌ"=^ÄâûÑÏ"HWZ‘ù /qš©µ7ÞHêV)kÕ öè9×ÈÒ’%³~ª }Ê©¶]ïúõáÌš5ÎÇ+WfÇñq7æ£Í›Í±ˆ'ôdÛò ÉrüêØ3Xe‚5-w"GŽxøðálüøñíE°žzÊtߺҹ+ËýëzVç­¼jS±Ò%˜yd0"!hf;©‘sVÖºyâyëf•µI{ôžkÖæ 5[‘ÖX؆W1kRã“=| k˜WºNŠgõıŽ#X—]vÙE[†åŸµ:ÁúzÑ¢°ÒôÀÚõüóæç®Ê«3/rõñ[o :b·íw‚#!ÈX˜m›6eûóÉËjƒ·ÖX•ÎYuž»n¹Xf­Fʽç~‹ƒÚdxOH!^þÿ»_þÒÅ?’AíË ª‡Nœûûvþ|¬¸#ó¹ñY¹"ÉÂGÁ*¿¯ö³x>«L°.\öScÊWÞG‚ß—?Oõ~KOOvnúôlûÛo›ã±âìË ñËÍ›Íô)¾ÇÉŸ›2%;˜Olk<ÒÑ}8ü|’YŽWoïã›ã…33f„­ìů?'ûW¬öbmÿÞxgÉZåÄ*n_XÏ7ÆíÈsÏ…që4{„ zØ÷âGØÎµÆ£ÏÍš•íxóMÿßÝÏIÏÖ®.¼s“&¹ùÇþɓëñ³oÚ´ÐÖxòy=ßÊŸ+ƒÕ‚7¹3ɼ°<öÅí‘_p¨›'ž'V$ˆç­[§ÚˆüH{âuj\ó¶‘jXG°fä+©£G:ƒ5nÜ8Õ"TSSSSSSskG°ž~úéðä`ñ)Â¥K—ÖM°æÌ™H–šššššššZ£ >Ñö×4¯kî=X‰D"‘H$#-¢.H$‰D"Á’H$‰D"ÁI)^ß ¦¦¦¦¦¦¦ÖhÁª“x KºÉF¤›tS?J7õ£––°¤›lDºI7õ£tS?Š`I$‰D"‘´¶ˆ`I$‰D"‘ˆ`I$‰D"‘ˆ`I$‰D"‘ˆ`ušP ûŠ+®ô3Ëâ+áÕóY*¬uëÖ…+ýÇŽ›]~ùåAGj8Z`­]»6›5kVÀ¢÷üùó³}ûö™÷#òúë¯_TZ)%V¥ê–xÈ®]»²›o¾9ôe­¢æ­ª[%,lïÖ[oÐ馛nÊöïßo¦[¶|ùò`û|¶bÅŠ¦0jÍ' ?RÏüMåGja¥ö#µ¾ÏÂÔû÷§ð#µ°,æÚPº¥ô#žºÕ²ð#µðRø‘Ž'XÕýÉ'ŸÌÖ¬Y3ðž‡Ë–-3Ãê³”XsçÎͺ»»ƒóÇHžxâ‰lúôéæX4 öÚk¯5íG„ªñ ,HÒ—Õ°RS½xÈ&Mš”mٲū,GŽ ØX“'OÎ^zé¥!˜Nœ8ÑL·|0ëéé ÿ>~üxö›ßü&{ì±ÇÆ©5Ÿ,üÈPó7¥©…•ÚÔ‹•ÊÔó÷§ò#µ°,üH-¼Ô~d8vЬ©…eáGjá¥ð#£f‹°läÓ¦M ÆP\e?Þ Ï2p×ó}—^ziÛaUÃce˪ãÔ©SIûÒ‹`Uû~ó»ï¾ë‚U ¨ÿîw¿3Ábuˆó²‘rf‡ŒR Ò_éï·ö#µúËÊ>kMÊqóÆ*§•)cYû‘2ž¥jlRú‘2–µ)g ?2j V9mÊÀ¥Ú’i5‚ ‡©[c±X¿~}Hõ[醑Ϙ1#;zôhò¾¬D°˜l´+¯¼2{衇²3gΘá˜qޤ¤±Eª·§Ú¶®ÕO¤Ù§L™b¦ÙV~6lÈ<˜½ñÆÙ’%KÌð.¹ä’‹1Î9•瓵©5-w-_‘ÒÔú> ?RƳô#e,k?RƳô#µÆ-µ)cYû‘2^ ?2j Vµ-¼N#X7n̦Nšä V-¬¸eÁÒ©VºÍ›7/œ/ð3²8ÆÛo¿Ý”ÓÕ8΢Xëv÷Ýw;±Ò gxà 7gH€áÅ·ß~k†GŸ±mGÒ—ï¿ÿ~2‚UžOÖ~¤ÖüMíGja¥ö#Õ¾ÏÊ”ñ,ýH­¾²ð#eYú‘¡æoJ?R +µêûRû‘JxV~¤ž¾J9¯+áYù‘Zº¥ö#•°,ýH%¼~dÔ¬bzµŽ7®c‡7YÅX÷c%±$!–}9Ôwþøc󹯼b~¡3)ÎNÔÒ-b¡cñ¬M*K?2ÔüM9·ka¥ö#µ¾Ï çï·ð–~d(ÝRú‘¡°Rú‘ZX~d(Ýšõ#ºÉ]"‘H$‰$±ˆ`I$‰D"‘ˆ`I$‰D"‘ˆ`I$‰D"‘ˆ`I$‰D"‘HD°$‰D"‘HD°$‰D"‘HD°$‰D"‘H$"X‰D"‘H$"X‰D"‘H$"X‰DÒ>òûßÿ>Ú¥|H%¡D Ÿó{‰D"‚%‘H$u uÚ¨7öüóÏú9…›ùùC=¤N’H$"X‰D2\¡h,dê½÷Þ ïßxãðžŸK$‰–D"‘4 }}}Ù 7Ü]vÙeÙúõëÃëÌ™3³3gΨs$‰–D"‘4*ÇÏÆ2W?ýéO³cÇŽ©S$‰–D"‘4#*,‰D"‚%‘H$‰$nŽ;6vסD"Á’H$’&å¶Ûn ™«7ß|3¼ß¸qcxÏÏ%‰DK"‘H†)ñš®e( ™,~Îç‰D"‚%‘H$uJ¼h” E+ þÿíÜÁ*taÀq—fADJ‰Ü€•ÆN”È”r Šfšl,fãeG\€RÊJ)Vò|ßsjNbæ >ùfü~õ.fŽ·3ïFÿÞyÏø¡Q@`,€ÀX  ° @`,` ÿq ýº °€o,ëX€à°^@`‚ÃzëX€à°^@`‚Ãz zp¬¯¯÷o=<<ÄÎÎNŒÇèèhÔjµhµZ_¾ÿððð?]ÏõõuLLL,@`ÿO`Åìì»Ñ-°VVV¢ÙlÆóós¼¼¼ÄååeñÞOÊXë , ïkdd¤«^ö÷÷ccc#¶¶¶Šù÷÷÷e===Åüü|ùú£9'''?Æl±S–×?Z X@ßÖÒÒRìîîÆÅÅÅ»ÐÊ­½½½òõññqloo—ñs~~þ.†ªæLMMÅÍÍMqŸÃÃC,`0+w—2€&''‹Ý¬ÕÕÕ¸½½-®ÍÍÍÅããcù·FsQoã§óºjÎÌÌLœ}zM X@_Ök¹»Ôh4baa¡ œn£*°ªæ\]]ÅòòrLOOÇÑÑ‘ÀÐ_õÙ§»É3R)wœzÏêXUs:îîîbllL` èŸÀúŠ|b°sþ*Gž¡Z[[+®åaõƒƒƒâ Ô?ðú V·ªš“g³ò`|¾—‡Ý °€ ¬v»]tÏóW¹sµ¹¹Y>õ—2¸ò\U^_\\,¾F¬ ¬ª9õz½¸GîržžV†U·¯ °€¾,ë€àX€À‡õ Ö , °€o Žß6ÀúCãC›÷IEND®B`‚WindItemRendererSample.png000066400000000000000000000441731463604235500346700ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/xy/doc-files‰PNG  IHDRXr5˜HBIDATxÚí pT×}ÿ3ÓfÒ4ͤéô‘ɤm&óo;m§Óv2íd2m†a`DÉxpÉðyÙæa–,!X¸Ž‚ Ù&`…GŒÀ˜\†G $ØÌÃ(ز1Xla9,Y2Ø’-#ëüõ=ñUW«ÝÕ®öîî={?ß™ïHûÞûÝsÏýžßùßùŒ¾â3H€ÁÀ``° FGð™Ï  ëŸ,òó73À<=ztÀã=ôЀÇçÍ›7àñC‡ xüßøFÖLI:ŸqîÜ93þ|{üŸûÜçÌg?ûYóÅ/~Ñ|ï{ß3ëÖ­3ÝÝÝyc°¢¿¿¨cþÒ—¾d LEE…¹rå 'ƒ€_¸ýöÛ\xüãx|ܸqÿ§ú§WWWx\ïtƒµjÕ*ó{¿÷{1‡Ç¯|å+4XÃù.‰ŽÓ£ÌåöíÛ9!À`ü€.ª‘Ú©S§ö?öÉ'ŸØHGôÅøý÷ßï΄ <¶cÇŽ@›Õ«W'e8"ß/ Ë#& À´··¸ÀþÝßý]ÿcš.Œu~úé§ûŸ£HOäcmmm5o¾ùæ Ã8bÄó /X3)ã(öçþçym°<455™›nºiÀc_þò—³ú0Xä-4íy‘õò–.]Ó`•••ÙÇ/_¾wú0Ùh.æÊëú¾`é½w4–/_nó»”+õWõW6o(U³Q^^>àùÿüÏÿlnܸ1èyÊGúæ7¿™Öwž3gŽùÎw¾c§Þôeì¾öµ¯™É“'[C7” ’áÓ1ýë_·Ó™±ž“ì±õÜÿøÿð¸´Ä+¯¼bfÍšeu×±|þóŸ7ÿøÿhõŒŒfFŽÚ‘ÞËûÝ”ïö“ŸüÄ>Wǧ©Zz=¦çÈÜFcïÞ½Öz:ê¯në~ €@£¸¸xÀ…Ñ»xý×ý×€ïÿo}ë[öqMF¾NÁ’ˆ¾/ú= 9“!|ûÛßÖtØp¾óPßu÷îÝ Ÿmz2i°ô{G¶‡M›6%ÌW“ÑR4Öçè±X¯Y±b…5°±Ó .LxÌz€Á °Ð”_ôPE ¼ûÁðþ×W‘Ÿ»îº+îÔa²+ÞEÛC´‰ÓÅ_$E™R5XŠ¼ÄŠÔ¥j°†úÎ^TP&J:ês,X0(z–Êg$k–†c°ôý"—N‹/¾8ÈÐèx~øá˜F'•\¯¡tŒ6}Šê³õ7òþŸÿüçœÀ`°&:;;D)1zöÙgªhC#ó ü¥Èçè}R5Xƒr½dì½ÿ>%lG>ß{Îp V¢û£óÒ1Ñy?É&J§ú•Ä®"ž©äŒåÒ`iš-òñ‘#GÆÔ~¨cîñÅz,Q´1•È!ƒ@Nók:GÓVñžmVü2XÑeÒ1Zíùüý×¹ŠP9^^Ôn8ß9ÒœŠ÷ß¿> ªÁŠN¨WõþXQ¤XZ¥c¢=F y]<££ѹU/^Œ;E}ñõË`EN]ŠªÝ4\³¡íq¢_£<²'NØÇ?üðC»Ší«_ýjZß9^ôŃ•(¢—ªñ‘–Ú(º‚½—ûýØã?>è½Õ6¤¡ß+:K¥"bý†ä`€Á ðÐÔPôÅNQžHxæ#Ú¤${QOõþèzWª/¥(†¢jÉæÄªŸ5Ü•{ñîÎ]’‘‰U´u8+ZÿcÇŽ¥l°ñÀý¯ÓTgäcÊÍÛ¶m›5Óú ´¥WŠÁoƒ=méíÉ*B0X8‡Xæ#zsgÊŒ~Žâ3e°MQK?ê`yˆ.8‹JLîwŽ.ÉàgV¼ÚPé,™ÂèÚ\B}}}Ìí’2ƒ•èX©ƒ §Yš!ÞÞ‚Ú :ú9ŠddÊ` o½õ–-×à]èe~b}×T eÿІ©’¸ÞWSo2šS”&™ÕŠñîW„Gù^Þ÷Õ´[tDf¸Kï-çmQäUHOÕ`éu:^M±©¢z¢íqš››íg*·L«!¥•ŽMµ¾¤¡W™ÞoƒåE²ôÛGVr×m*¹€ÁÀ``° ƒ€Á,  `°0X,€ÁÀ`e<ðÀ€ÛÅÅÅfÖ¬Yçüùóøžèƒ>èƒFè]Õ§´´ƒå—Áºï¾û̳Ï>x677;ñ=Ñ}ÐкªÏÿüÏÿ`°Âf°^zé%NRôAôA#ôA  B!„îƒE ¢ú ¡ú`°0XÌï£ú ¡ú`°0X¸FGèƒ>Ї‹,!„b°0XŒŽÐ}ÐÐ}0X,æ¯Ñ}Ð úƒ…Á"‚Åè¢ú ú  ƒ!„B  ÷>èƒ>h„>è@ ƒEóûèÑÐ}0X,FGèƒ>èƒFèƒ>, „BÉÁÂ`áþ¡ú@4B"X,r° ú ú ú  ƒÅè}Ð}Ð}Ѓ…Á‚B!9X,… ÇŽ;à¾ÞÞ^S[[k*++­!;::b¾>•çÁbt„>}Ð}Ð'ï Öˆ#ú‰úúz³}ûöþÛÛ¶m35551ß#•ç’ƒÅü>ú@ôA#ôAŸÐLF¬9s昶¶¶þÛ­­­¦°°0ækSy.,FGèÑÐ}Bk° MFß7œç’ƒ!„ÂЬèÛñîKå¹^~V´Á*//·¡KÏ]ëoo_¸p!Ðß}Ð}òû¶Gô@Ÿ|Ö‡V#XÌï£ú ¡úÁJË`™öööyU'NŒùÚTžKóûèÑÐ}Bk°¶nÝjWF® ¬®®Žùü¡žK„BC[¦!²\ÃPµ­" u° ú ú ú ‹JîÌï£ú Ï<ú‹_ m}0X,Ü?ú úøÅç÷ï7fÂslß>4¢ ¡ ƒ!„~ðÂêÕÖ`¡„, î}Ð}|âµ’Ó;y2цЃ…Ábþ}Ð}üàs‡™O Í)SЈ6„>, î}Ð}ü`c}½éé3XwÜF´!ôÁ`a° „о¹l™é™2Åt”•¡„, î}Ð}üàG·Ýf>™4É\]¼hCèƒÁÂ`1>èƒ>éò×Û·›o¿Ýæ_]zðA4¢ ¡ ƒ…ûGôAŸ´/B«V™wï¹ÇtÍžmÞ¨ªB#Úú`°0XºÀ ®„ƒÙ9g޹üƒ˜æÎ5MkÖ  „, î}‚®¶^QñÊã{ö IÛÏÉ]»Ì©S͕ŋÍûEEæÕõëш6„>, ó×èã‚>2XÍ?ü!š°ý\¬©1—û:÷ëóç[ƒõÒ–-hDB  ÷>.èÓ;i’­Ž&Ák?×î¼Ó¼²i“ùdòdóÁw˜S;w¢m}0X,]  XªÆÒá_ý =DMÛjzP¦ª{Æ »’ðÄîÝh! ƒ…ûGôéž>ÝôÜr‹yñ‰'Ð%@íçüÃÛºWŠ`µ—•Ù(VÐL0çúÁÂ`‘ƒÅü>Œ£ÏûóæÙHIÐj,…½ý¼{÷ÝæÜc™ «W›·~ð;•‹F´!ôÁ`a°pÿèãˆ>؋÷Ûç.ÌíçùýûmTñèæí%KÌÅêj[ÍhCèƒÁÂ`Aè5ý¤‹¹*…“ã ž­«³,ýßQZjš~üc 0„, î}\ÒGy>J Ö]y?®—ÊNø‘«”Ëöó΢Eæ7>jÿ×ô­ò°d´8ÇèƒÐƒ…Ábþ}ÑGSP׊‹íßÖ{ïÍ ƒ¥®¶™Ã§M³Ó„Š(j¡ ŒÊtqŽÑ¡ ƒ…ûGGô‘±zçûß7oUTØ©B—Ë5¨´ –9sÙ~Ž,ß1bĈ˜L÷¹ä`Á°³eéRs¥¼ÜFHT1\uWE,å0åÃo£)ÛßöõO¯ÿèG¬ò„ƒ•´µµ™[o½5®Á"‚Ñ'9}šW­2o÷u *×àmÍâê1©(§ –JäCû‘¹R.VóÊ•æª*Î1ú ôÁ`eÕÕÕæàÁƒ¡6XÌï£úÈ`]껀«TÃémÛL׬Yæ¹C‡Üì°·l±ËhOÚJh¨¬LoÓš5œcôAèƒÁÊ,._¾lfΜ™p:qÔ¨Q–ú:ÛuëÖ™îîî˜ÆÊc$ÊËËíï¹ký âí .úû¡úüvíZsí‘GLwŸ±Rr¸þ^>rÄÉãó Ö}+ÚVž9~ÜtVVš7>­´(½?%çúä³>¡2X+V¬0Gú.ÉN%Ê`UTTƒa z Ô*ÓpfãÆß¶¬©qòXTù\+ˆ% R¥ŠŒÊ`yµ°üH܇ÁŠ 9˹sç¦ôšÞÞ^3zôhr°˜ß‡1ôñ –’Ýe¬TsIÉE¬Î9sœo?šÔ¡þW‘Qå—qŽÑ¡+cXÒw!8}útJ¯éìì4ãÆ#‹ù}CE}®ôµy»ç]E…- È‰W*ÀµUw2X^äÇåöãýú_…`ƒXeŸs }ÈÁʃuñâESÒ7¢*±½¬¬Ì444ØÈ•ÌUmm­©««#‚ÅèÆÐGSOš‚j¬¯ïŸZsµ–öe°>™<9ícÈuû‘¹ò*ëûq<œcèƒ>¬¸q:{öìkwßH¯´ïB1räH3~üx³aÃö"„0ÁÊ;«cûöÙªáNˆ?5X]Ÿ&ì»|,ʉs¹&„ä`"XŒŽB­Od®OP#%ÉRå ¼$÷t“ÂsÝ~dvez9ÇèƒÐƒ…Ábþ}Ôç…§ŸîO ×߆§žrö˜”¬/ƒ¥ ¬Ó­æžËöãJ4‘s }ÈÁÂ`Ábtãè£äi%Që%»k%ž«Ç¤í~d°´"2Ýjî¹l?Þ´-ç}ú`°0X:ÊHƒ¥&ÑÕc‘)‘Ár}ï>m£mrhŸb°0X¸ôqTŸÈ‚–Š^]qx€á,¯ô„«íÇÛƒsŒ>}0X,æ¯ÑÇa}dJôWùWÚôÙÕcÒêA‹Sl¹l?®TnçCr°0XD°Áúx+2šå"5Õ©c‰LÜw±ý¸°‚s }ˆ`a°ÈÁ‚0~tÛmæøž=N~÷§M³Ëe£˜õÈ $ Ábt„>Qt¹À¥Ì•óþºÖ~N?ù¤¹vçœcôAèƒÁÂ`1>ù¤+ Ö‰L–ËíÇÛ|›sŒ>}0X,Ü?úä‘>®—8ðË`åªýH{ýœcôAèƒÁÂ`A˜GÔô ¦ Ãn°rEö „ƒ…ÁÂý£OêãÇ <"Xé­„TñWÎ1ú ôÁ`a°˜¿FŸ<ÒG›=kž«›>»œƒ%͵á6ç}ú`°0X¸ôÉC}ÁR$‹+9ÇЇ‹,Ér:Ë¥„’ƒ…Á"‚Å覨K+Ùò)‚åšîœcèC ƒEóû0}TËÕHŠË9X®E9ÇЇ, ,FG0}´Ñ°K¹@ùÁri!çúÁÂ`‘ƒaŠty?ä`å¹ÁJõõõfûöíý··mÛfjjjˆ`1:‚)èsuñb{Ñ'‚Å BÎ1ô!‚…Á²˜Ó7ònkkë¿ÝÚÚj ÉÁ‚0Å ¾‹›>»šƒÅ„b°rf°Fe9¡¯]·néîîŽùÜ‚‚‚AS†Ñ÷Ábt„>/ 9e¥(¬ìÐÅêù©häÒêHú ô m’»¢S2XIG»bÝçågE¬òòr;7ìýøúÄÛW®\ ô÷C·õùÍÞ½ö¢ïÚñÉ`¹Ö~.]¸`z¦M³+ ]Ò;úo¼ç·oÜh—“»v…ê|KVŸ°ÞvEŸÐ­"TTjôèÑD°¡O†ôÑ B­$tmÓg#XŠ\ÉÌæ[’¡j/+3Ýz«é™2…s Árfܸq1+**2ííír°&NœH„)Rµ°\›¶r1ël]“Ó±ñøÜ¡Cæª*Ó5k–jî4É\¡_…ä`e}£ ††’¹R†º¾N)ÖàÖ­[íÊÁÈU„ÕÕÕD°ÁõQ]&ׯ]Œ`5¯Ze™mèÅ'ž0Üq‡iYºÔ=pÀ.”øxÚ4óêúõœcV±{÷nSZZjFŽiÆo6lØ7ÇŠ:X}üÑGu™\+àb,E¯År¹ ÉL]î»É”ÿzûv{ß‘ƒíôà)Sœ,ZK„>93X]]]624f̘GSt;vì ’;î}×Gæê·}ç,V&ÒHFJÓV¯¶Óƒ‘‘¹÷çÍ3×çÏçƒD°RAUUU̪êgΜ±Q& „nSQ•ŽÒRr°2H-"¸1uªs‹ ¢)“ë¸~[QáœI‡0çK‘«;wÚ)¹è2šÊÃ`áþÑÇm}UqmÓg×"X®® J#%·+z¥)ÃW6mâƒD°RŠ~Ê\Eç@]½z5n  óûèãŽ>ŠBȰ¸]q-Ëå„ñ4R{ùè¶ÛÌ™ l©ç÷ïçƒä`¥‚™3gZ3ÒÒÒb –ÌVcc£™={¶MHÇ`áþÑÇ}}>¼ýv§6}v-‚åê ÂDÙ¸ÑF¯^Ú²ÅþåƒD°RÄáÇä`ER%0XºOí‘çÒ 7×r°\]A˜ˆJj×15¯\iÞ\¶Œó’ƒ5œ:uÊ”””Ø)Aå]iá‰'œ*A‹Ñúħ”]а¸Árua<”o¥:XZMèš9§BŸPÖÁÂ`1¿>¹ÑG¥\Êr)Ëõ„±4ÒªS•kЪBå_©>ç$+E¨ÖU¬\«… šM}£ î}Ü×G•Ü]ZåæRË儱4RΕêa)zåUt磯!‚5 (™];ÎGCû²ŠÂü L€"ä`±‚0™|½ß<úhò¾¶Ëá‚ä` 2Q7nÜt¿î£î}òCM_É`ؽ›–ÏÔô«öêˇ6ÔðÔS¶4ƒWpô½’’ÐÖ¿¢"‚åKkqßèëüùóýõ°.]ºdî¿ÿ~û‹ùkôÉ}4åʦÏ.å`i¯Gä̇6¤HÜÅšû¿¦{n¹%”ûÒ‘ƒå‹Ázæ™gâ–i8Ôw‚a°pÿèól`#?©è9õCË?ªÊ¹K5Æâi¤6®è•—Ð.3Öý飉`ù¶ŠðرcýeTÙ}îܹÖx±ŠÂgÍñ½{íÿüêÕN‡¦±\ÙOÎ¥,M½º¾¡‰‹,åñFU•y«¢‚>’ƒE™"XŒŽ2ǧO77n¹Å^t\ÕGÑ+E±ˆ`ùÙT•|×Ï1E­Tj"²ƒJ5¨š;}}4¬4ö"ŒÞäƒÅü5ú äÕòróɤIæÝ{î1­÷ÞÛŸì’>Z~ïŠp%KÓh®˜ÖD©R{d´Ê«í”vN„>N,Um÷ö Ä`áþÑ'6_]¿ÞtOŸn§P´jLµ‚P¹;}”¬ìʦϮD°\_A(ž9zÔæ^E'³c®è£‰`¥‰³gÏÚr çÎÃ`A‡'wí27¦L1Ý3fØ Oc}½=¿¿SÇ¡|!¶tq%Ëõ„¢*¶»’›¡S+Þ B,Ü?úüŽ2WïÏ›g£º„íCRÕG+Þ\ØWΕ–ë+5Xèž5Ë èo裉`a°ÈÁb~?'TîÕ[••ýÛˆ¸¨".žA$‹„Š^½¿r%} }49X¬"$‚Åè(wÔ Bm¢‚A™JU™+™,"X¬ Ô Am¹)Ä9ÓGÁʸÁ:}ú´)..6–ú_÷ù;wÚ¥Uok̘1¦²²Ò´¶¶¦Y# æ¬#Ù²Å^T¹%¹%Š• 5=¨i-r°XAØ´f¹B? ÉÁÊœÁ:yòd\3ÓÐÐà«Á*++³ï©‹===fóæÍ¶¨i<ƒE Iå\iëå­hšP+ ]ÓG îÊ%#‚Å B 4h ¢&‚•!ƒ¥h•ö"ÔþƒZZZÌ’%Kìc™†êp…Ù`1¿ï–>Š\)©Y¹+Aˆ^¤ªò…\(ÕàB–Ë+µsÛ‚ôAôÑä`eÒ`iJ°««kÐýׯ_k~üœšTT+žÁÒç‹ú:ÛuëÖ™îîn"XŒŽrJM©È\É (¤€kú(o(è+ß\ˆ`¹¼‚P«a½ïNDM+Ë«³³3£ëÈ‘#föìÙqs°"ÑÖÖf VEEELcå1åååÖY{?¾þr›ÛéÞnÙ°Áš,ݾöÈ#æ½O¼KÇ£È[sŸ1 ò÷•Á ºž½……¦±¡Á¹ß_ƒ‚÷﹇ó™Û¡ºÓ)B}š.Ìäᆾ‹”"LI¿Fy[JŽ'‚Åè(—lxê©þ•c^NV. wG•ŒÜÔ—VêTÕsrÙbQ{ FF^éƒè£‰`eÈ`:u*n’{&VêÇNò¼"jãÆ#‹ùýœ/m÷Ý=³’˲ÃÑG« ¯ö ªÈÁJoE©ŒŠkç“Ê#¤¢&+KedxJJJúsžô&ÌÕ‰'F­"Û#WÊ\ÕÖÖšºº:"XŒŽrÎÈqE2>ž6Íß³Ç}d‚^ª!è,™T·—ygÑ¢A«_éƒè£‰`åA¡Ñ¡j[Eþ¿{÷n[3Kû$Ž?ÞN+²! " RRW¾¯L¡*Skø”¹’Ér©žÚ¹Óu±~„N¬è2.m‹C‹ÑQ¨}Ü”“‹½ ‡« –Œ,ò˜\ ¦²Ï?ü0}}4, {BwôÑʼ\ìñ7\}4Edƒô,ê ÔT¢WôAôÑä`a°ˆ`1: tòp.6®>šÖ òW#X.® Ô”¦ê·ÑÑGÁÂ`…Æ`Áü¡¶‰w ܨsÕª@oóä,×VjÆG·Ý–“)l1X,Ü?ú¤Mm?’íM ‡«6}òFÅAŽ`¹¶‚Pß5Ñô5}}4¬ ¬dˆÁbþ}’¯‘•íM ‡«¶IQN9X©ó­ŠŠ˜ÉâAäóû÷ÛéÌDeDèƒè£ÉÁÂ`Ábtx*2¤©Â ë£Z^ZIÔMŸƒÁz¯¤Ä¼øÄN´Ç7—-²„}}4¬<¨ƒ…Á‚ùNE±”ïâÂ~E6r¹Í«9XÚ*éÄîÝÿ}ežÕsUB  ÷>¾çè´Þ{oàõQ¢öË?N+EÓ¢­’\(ÖùFUURÛ8Ñ¡, 9XÌï;AíS¨ísTº!Èú¹yPs°~½}{V§€Ómƒ*‚KD>,"X0oôQ„lD±ÒÑ'È«á‚ÁjZ³Æ\î먃Þþ”„Ÿlû£B"X>¬·Þz ƒa™ëM “¡¦ƒZª!¨9XZAx±¦Æ‰Ü+EÛ8!̲Áš={¶Y¸p¡už,Ü?úd†Z½¥ rPõQ¢¶¶‰`%Ïö²2ÓX_èv÷ÚÚµ)B¥B"X>O666šE‹™™3gš}}£íÞÞ^ ó×èã#•ÿ¢(V&+h§«OPK55K…d³‘[—Î*VÕbKÅÒ¡9XÊÁjii1555fr_G»yófsíÚ5 î}|¢Vq)+¨ú¨ØhK51‚ôÚa^ôJuºèƒèƒÐ'@Iî2V2X2ZÕÕÕæÒ¥K,Óä©;3ÅJ‡ÚôYÅQÉÁJn¡"XAnoZá(“–óK«%ég 3«5Ux ïb <­ 8“§EË=&c:òA­æRM¢ ¶}/Uû&‚•Ü Â+îW´EÓûóæ¥\£ËÕsLæJ†7ÓX"XD°2/OkΜ9,æ÷}¥öHS®ÈP#Ð|ÐGÛª(™<Å)ÓÕçÌÆY+ŠêzVÐWÊ\ 'éò9¦ Ö¯ÝygF ¿’ƒEVF¡<- £#?ùNŸqO¦Èe¾è£U]™Ø:]}dþt"‚•Ütê¹Ç f?²e‹Ýúh8Sf®ŸcªKä²üñOÇ|Îø²ùL߯”ÊkÒaôçEò¾ûΚ/|á†åâÅç²Ò ï¿ÿUóå/l?ó^í¿_£l-wר3›é;èû|éK›ŠŠ3YýÍTs*ÞôÍÝw7ÅüÝt_$3ITŽ˜þß½û¨ùýßïMøyÙú½d°¶n=a¾þõÌg?ûIÌ6›í6ÌïëLt^¦ÃÈã_7úñ˜–lŸ__üâ ó•¯|h6n<•5}¼©ÂÓO>™öñ'ÓGøÝG':þd4õóûÄë³³ÕÆ‡ú}Òi¿,‡ Ö7¿Ùnz¨Ñ¬ZÕhþå_:=^]ý’m¨‘.Ökürÿ±>/’ø‡=fÓ¦ÌæÍ'íÉ“ ƒ¥NAŸ¹}ûqó'ÒÝA×Ê5Õ`JF#?GGû·×í ª ·:Ðáþfé,ñŽbýüçGÌ_þegÂÎiÙ²W̘1oglô(ƒ¥Â¨wÝuÞÜtÓå”Ú˜ŸúD¬ï|çSZú³eËI«ÑpÚ´_í'ra¼ß#–>C—éÐ;þ«n¾ôÙÎA«³Ýé×¹þ“Ÿ4˜û·w³ªÌ•ίÈÞpÚk¬>"“}ôPÇ?”¦~ŸX}öP¯©«»à[ê÷I§¿Á`9d°>÷¹OÌ¡CÏZêÿèÇuA(*º0 ÑÅz_ó×±>/’rü?þ‚å_üÅGY ÍêdÕ èM³hd5ÊÄüþŠ/[=†û› —Ê߉ŽbÝrË›¦¸ø|ÜßíàÁçìïõôÓÇ2–ÿ h¢rYþþï¯Ûh‘.ÜÒ(ÛúD,}_ý깸ÏI¦MûÕ~´‚PƒD¿G,}†:/Ó¡wü/L_n¾úÇ×rÞ)Ò¢ï#CœlŸè§>š&ŒÜú)öÙGd²êø‡Ò4S׌È>{¨×|ãûöõû¤Óßä½ÁÒ ÅÚÚZSYYi ‘ØÑÑ‘össa°"“¦U’y^¬×ø=¯‘Ï›w±jCôl™«ýû›ø‡köÔÔ r‘¢§Éiä·>¡é3fÌhöo–ΪÉS§ÚÄrÝö¦Àýn ¾fFºšÑü‡èämà¿öµ®¬ëm°döþú¯;íE&ÖtE2mÚ¯ö£„Êê÷ˆ÷[fÂ`Eÿ]E¯å¼Òo¤H¢~³?øƒž¬ë£~Eù„šŽO§½F÷Ùè£M&£©Ÿß'²ÏNæ5ž>~ü†Cý>éô7yo°êëëûœñöþÛÛ¶m³ENÓ}nP"X±r3²1ÚOÔ™x·îõFûú§ÝY3XßúV›ÍÑ”VÒý¿?z;§é}n÷¦”²ý›]zðÁþýÿ¾ýí6rüÌèï3räÕŒä‚DRÅè Œ½Î+WmZKï§‘»Ú¬7šÎU›Vt¤yåÊA¿ÇPúdÒ`éøŸŸýy¶dMÿñ¡RÔQÑÇ\裒 êg”ŠìñGÇè>"Ó†&Q¿KÓL¯ÏÎEê÷q.‚¥¨J1Œ=ÚŒ1bý„Ê=´µµõßnmm5………i?7Kó¿šÍG6ºX¯ÉVËË×ÐÅHl˜«Ù³_7K—žéŸŒ·j0‘F~êóÕ¯~h;Né+¿ Ùß,Êh*Š¥mt¢“¦cývúÎ ×g2‚¥È¢¢3ú¬Çûµ©­}Ñæ¢äBŸHƒ¥÷Óïõä“'bNW$Ó¦ýj?ª%#:Ôï‘Mƒõ‡Ÿ¿a^þî³mí³ ÏélõAš>’!VÞÜþgkÎôQ?Ó¶`Á°Úk¬>"—¬d5õëûDöÙɾÆÏÖP¿O:ýMN Ö’%Kb«L¬‚‚‚AÓ€Ñ÷ ç¹¹0X<ò¢5ê$L´š!²ÑÅz_óû^‚k¼F®Õ!ú\Ê"G'™¢¾ŽUßgê_6¿S·VM"üÌÁRÇ¡ µÞÿÁ_öoæÇ”œ¶ÐI¦ƒ* î‡>ª6¯šdÒG‰½C­ÐÉ´>žÁÒENÓ5j³K–œV›ö«ýx«Ô’™–È–ÁZ;þIógôþçt¶ú  ^³ÓºßøÆvú;—ú(J|¡º:åö«ðK/?•ãOVS¿ –×gµr9ò1O?~á~Ÿtú›œ,™–;v˜žžžŒOÆ2lñL\²Ïõò³¢ Vyy¹ýá=w­¿~ÞÖÀ÷»páBÚßG'nÏ´iæRSSÆŽ7•ÛŠÎܘ>Ý\ê;¶3GZ­‚À\éW~/?Ú]!7eJ`ÚOÐÎ/é¤óK·Ì'·ÞjÎ=óLÚïç1Ýï”ó<’¿Ù»7úx¿×Ë/¼ˆöãý^~¼ŸŸí'“Ç›3ƒ¥èP6O¬ 핦„ò‚Tø/rK‘Wò»GZ7ÉÍ6ƒ¦¿MrÑ~37¨ ¯Õçi(­Bmxꩼ+æë sb°”•è•PTTdÚÛÛäUMœ81íçºl°ü˜ßת4/q:×TŽQÏ-·Øeí‘÷«£ÓÔU”©2>h”I¶,]jûíR |Çt7Z÷KõÑ™ØÍ!ŸvKÈ;ƒÕÔÔd>œƒµuëV»0re`uuuÌ)À¡ž›/Ëù}mØ«ÕiAhÄ2R±ö¶ó–P{% ²©O>}Ð.ØP?ãE÷ôÿ…Õ«¡úh­DÍ7ƒÅ^„CäEe+É}¨ÚV‘Ÿô:XArÿJ˜Nf_¿LSK£½Š·»ýñ={ìT¦rˆ@}@4ò;UB‹5ÔÏD®t7Z÷KŸ×Ö®µ«©‰`a°2b°ò©’{æ¯U\áðœ„W­²…D=GÃÉÇ‚úIMj¥§6½ŽŽœË`Éhåú;zÛùƒ"ƒÅV9î»E" ë媃S¾CtîU¼iÄdó±ˆ@ ú ÑPTdèõý(æcÚ¥@Iï¹ÖÇë#ƒ2¸$‚•%ƒuúôiS\\lWé‰ú_÷a°‚?-c¥“6×WÛPÑ«áäc‘Cƒ>èƒF‰¨+å}Æ›ôŒ"H¹ÖGS˜ñR(ÈÁÊCƒuòäɸӃ ¬€»…Ä5E˜ëåâÝv›þKö5^>ÖP+|ˆ@¡A4ŠG¥FhÚm¨¾V7ÿÉO}´±zô^‰D°òØ`)Zµ¸¯á]ºt©ÿ¾––[á]a°‚=­äöèªàÙ¦¶I6z=²ä!nm2Õ–J&-áÄîÝæ“É“íß\¯²Ž7•IV,M vuu ºÿúõëfÔ¨Q¬€»°J.w)zE}Ðü2Y©äi §ð¨Ÿúh0šë1¬¬ÎÎN –ó×*^—jm)?©¥Çš¢Lg494èƒ>h”éD¯ðh*ÆÌO}”Ò¡üSr°B6E(‘ ¹@¯ð¨"YÉö]~ë£AõõD°Âb°(4êæüµ¦æÚ,ÈIcUR»¦3•{E ú e‚Ú»U s’Mx÷[Ÿßö]«ÒÝ‘¬¬ÈmpØ*Ç]÷¯Õƒ¹ÚM˃5Tî•XXXˆÁ èüµæñUÅ=›Ÿ©éHÉcA!>µm¦)ÉÁ ¡ÁR-¬Ù³g›ÆÆF V@Ý¿ö´Òrßl6Ðw-Êú¾‡Œ®Ñ}Ð(ßôQ©ˆK>H+Ì9X$¹wþZ5\²9UçMIf;zE ú å›>¯®_o¬ä`±Šƒ@÷¯e¾/?þxVKB´,]Êèšèú úøn‘Ë]8ˆ`eÉ`íØ±Ã”––ºáÂ…fÓ¦M¬€Î_«ÕÉ]»²òYúíŸu|Ï:\!ô½“&e|' r°rl°”kuåÊ•A÷···›Ñ£Gûj‚vîÜiÍœÞw̘1¦²²Ò´¶¶¦”#FëwÕˆ•lž­“SU‡sU1žÑ5ú å£>¹ªcH+‹KfçÆƒî×}#GŽôÕ`•••Ù¥{{{MOOÙ¼y³™;w®/¹aaÊÁÒ.ð:9³Ñ(UOÑ«lEËÈAôA£0è£,åb‘ƒ•ç¬Å‹›óçÏ[ã#\ºtÉÜÿýö±LC+Ãl°†ãþÏÖÕÙe¾Ùh”J¦Ïå¾YŒ®Ñ}Ð(õѬ€VÁÊcƒõÌ3ÏÄŽ;tèPFÍÕéÓ§mT+žÁ’ù'ô5„uëÖ™îînr°žýt“ç,”Kð¦"ɽ‚B™Í29X9\ExìØ1SRRb§ eh4m'ã•I9rÄFÈâå`E¢­­Í¬ŠŠŠ˜ÆÊc$ÊËËmèÒs×úëçm5N?ÞïÂ… )¿^'eó–-=>ý}¯¯x·²2cïŸ)}Ât}Ð'ÝÛÑ#»ú¼}ø°é,-ÍÙñé¦ö“weâ%§oذÁF˜:::’~/M_&JºSVçœ9æ…§ŸÎèñ=¿¿^å*÷ŠüôA4Êg}´¡ö$$+#X]]]¦ººÚ®ê‹4BEEE¶„ƒß“T¢{ªèìì4ãÆ#ëÙìlzaõj»ï ù!äÏ ¡OfÞûÃÛoOzÓir°4XUUU1#MgΜ1ãÇ÷Õ\8q"aÔ*òó#WÊ\ÕÖÖšºººÐç`édÔI™ÉÏ8rð éž1#§Kˆ!„0ß©ýd³Y0š¬,,E®TŸJF&z*Ïï2 CÕ¶ŠüwŸ‘PÍ,}=M+²ŠðY{2fz“çß<úh ¢WŒ®Ñ}Ð(ŸõÑ~²êo‰`å©ÁRR»Wž!Òà\½zÕ÷B£TrOþZ'c&7yVôJ{ªÖù!äÏ ¡OsF³™ìÏÉÁʱÁš9s¦5#---Ö`Él566Ú~±¶ÐÁ`åÖýgzÄ£÷Ît„ŒÑ5ú ¡Oß{oÙb:>]IH+ ÖáÇãNÝ 'ƒ•YêdÔI™©U-Š^eêý!„þµS†ò]ÉÁÊã:X§Nꯃ¥œ'­ TBºKKK'£NÊL¾k/+ctÍè}ÐfIŸLöéD°`°òaÈÁÊdݽ·V'žÙ¸‘üòCÐ`–ôÉä¬9XHrOuß? VnÜÿé'Ÿ4×î¼Ó7CyûÜc™÷JJÌs‡1ºft>h³¤O®VÁÊ‚ÁÒt —ÜŽÁ öüµ_{WÉ\õÜr‹]1¨Û2U]³f.z!„ùÎ×ô#séÁÉÁÊGƒuöìY›wuîÜ9 VÀÝ¿_›<¿øÄv»ïökkך÷çÍ \ôŠÑ5ú å»>Ù¨mH+G+Þ ÂX{b°r;­è•¢Xé~æ›Ë–™æ•+û£W2WA^‘‚>èƒFù¬O6vç  ƒEk*ÿJyXé~¦r­^Ù´Éþ/c¥éÁ F¯]£ú QôÉÆþ²D°XEHV†OB-¾1uª9zà€½}}þ|;EHG !„¹¡_ƒgr°`°"£SD°Üpÿ~¤SäÊ«u¥è•BÓ^²;£kF×èƒF0ûú¼½dIÖºD°0Xä`yا-Þª¨èÏ¿ÒTaÓš5䇂>hs¨O.V’ƒÅ!¬Oéצ Ê·ÒFÎ2lO›Ö?UÈèšÑ5ú Ì>~•à!‚0ƒµwï^3wî\SPP`Y\\l~ùË_b°6íG1ºç÷ï·õ¯”Юi «WÓBaŽùÂÓO(CV,™’xSƒ+V¬À`Èý«NŠê¥¤óY2hï,Zdëëm¢{¶W­0ºFôA#ôLõÅÙ`eÐ`)r%#µ|ùrÓÔÔd+¹‹çÏŸ·æJíß¿ƒùk%£«^J:Ÿu¹¯]¬©1­÷ÞkÞ¨ª"ÿüôh}ÁR$‹¬<0Xš üÙÏ~÷ñ­[·šÒÒR VÜ¿_›<tÛmv®?r›FŒ®ÑÐ!÷úøUHšV ÖØ±cMwwwÜÇ»ººÌ˜1c0X˜¿öc~^ï!ƒ¥)BUr§ã„ÂàP«µš¬<0X£Fòå9¬Ì»?V˜(ÿê½»îPdTd!£kôA#tȽ>ªƒ¥zXD°òÀ`%SãŠ:XÁ˜¿ö£FJÛ‚æZq±­ƒåݧB£ŠŒÙd‘‚>èƒFaÐG•ÜUѬ<1XɃ•{÷ïG•_Õ¼ê™2Åß³Çæ_)á]…KOîÚÅè‘Ñ5ú ̱>~åÚÁ ©ÁJå3´¢±¶¶ÖTVVZó$vtt„2+Ý}ª^|â k®~Û×ÀTdôƒ;î0Í«Vvƒg! #ýX-NV@ê`e©¶úúz³½Ï xضm›©©© e+ÝMž[–/7½“&ÙiFM ªŠ;£GF×èÑ(XúøQïkHÌé3mmmý·[[[Maaaèr°üØä¹{út›Ü~¥OUs'ÿüôh<}²¹’¬<4XZ™(Nèûq×­[·T„¶í‰ž2Œ¾Ï3V#Q^^nxÏ]믟·Õ8ýx¿ .$~|×.›+5Ü÷?sô¨ý®o?òHFõÈÔí¡ô ûmôAŸto{DÜëÓ²aƒÝ-Ÿ§ëB˜ÚO¨6{VtJ«¢¢"éhW¢X¾æ`ù±És¶æô!„¦1ݶe‹P“ƒE+¥¤öx‰ì£GN+‚•ï9XÙ.>G~ú ÁÜèãGJ9X!"ŒFgg§7n\ÌÇŠŠŠL{{û€¬‰'†&Ë{Õ¯zeÓ&ò ú …@,-r°0X)¡¬¬Ì444Øh”Ì•Ê0ÔÕÕÅœÔ^ˆZ9¹Š°ºº:4,ïýµ½jW1z„èƒ>h”ÿúØœÛ,¬ô&‚•gk÷îÝvé‘#GšñãÇ› 6Äͱ s¬È0qØæÉ!„0ÌTέroÉÁÂ`QÉ=î_Ié*8§*ë]³f1z¤ÓEôA£è£œÛ–¥K‰`a°0X™˜¿öŒÕ«ë×›w-"ÿN}ÐB¢ fc%!9X¬PF°´µÍ{%%¦yåJóFU£G:yôA4 ‰>ÙZIH ƒ•÷+Q-mòÜ´fMZÛä@!t‹=·ÜbŽh}Ôÿë:ÉH9X¬PF°tbµ.Zdk`iÉn˜ó°]£ú QØôÑJÂ7—-Ëèt!, V(s°d¨®”—ÛÈ•LV¦CÅBƒC­ WšˆþWÉžLì'K+”¬æU«L[ßÉ¥mrd°ž;tˆÑ#DôA£è£AõwÜá„Á"‚…ÁrjþZ<_+.¶ër_£ ÿN}Уս“&Ù¿™Jx' ƒÊ–»fÏ6uuŒéäÑ}Ð(dútΙcU¦ö&$‚…Á e– Ö'“'Û䯣ÐÉAaȨ •ì ºÁ" ƒå”û÷ž{Lïĉ¡.ÏÀè}Ш·bPû^¬©±Û¥)é ƒåÃüõõùómô*ìÛä‚>èƒFaÒG»v|hB}ÁR$«½¬ÌîêѼr%9X, Vºîÿè/~aß[£:7F×èƒ>h>}´jPå´’ðï?#§‰`a°B™ƒulß>:6! )½ü«×zÈ-­$$ ƒ…ÁbôÈè}ÐÐ'MªDÃå¾ëZçw˜+¸žÁÊ3ƒ5bĈ˜L÷¹ù–ƒÑ}Эr°½º1uªýË5 ƒ•ÚÚÚÌ­·Þ×`Á‚èƒ>èƒFaÔGEG½-Ó®e`W"Xyn°ª««ÍÁƒCm° „ÂX|÷î»M÷Œ¶à(×0 VÒ¸|ù²™9sfÂéÄQ£FYNèkëÖ­3ÝÝÝD°=BôA4 …>ZMøñôé¶×0 VÒX±b…9räHÒS‰2X1•ÇH”«vHssÿ¯¿~ÞVãôãý®\¹’‘ï—/·Ñ}Ð'³·£ÿ¢Opô9sü¸é),4ŸL™âûûë¦ö“w+^rº|îܹ)½Woo¯=z4,F}ÐB£võè8‘k¬ä°dÉsúôé”^ÓÙÙiÆG„ÂÐPûfâzCV¬‹/š’’’!ÛËÊÊLCCƒ\É\ÕÖÖšºº:"XŒ!ú …FžÖõÆïÔD°òÐ`É8={vHƒµ{÷nSZZjFŽiÆo6lØš½!ú ú úxÔj\Ã0XTrgôÈè}ÐÐ'à$‚…Á œÁRá7€ó'¹XBÃj°ÈÁÂ`ùÆo¿ÝœØ½Û6Î#Ú*»ŒŽ=¢ú úÁÂ`a°ÒY2;¾ùõöí¶qÊhÉp‘ÿ@~ú ¡O 9X,ߨ­ ^~üqÛ8O?ù¤¹v猎=¢ú úÁÂ`a°ÒáÛK–Ø]ÎÕ8_Ù´É´-X@'!„, +¾¹l™¹XSc§Œ– £#Fèƒ>h„>D°0X¬4¨ªº—|Ð6Nïæ÷É@ôA#ô! ƒ…ÁJƒ^ÔJÓ‹f1:bôˆ>èƒFèC ƒ…ÁJƒ^Þ•§—E'!„, + z+Õ8½…ŒŽ=¢ú ú^¡ìHƒõÜ¡C¦wÒ$"X¬Ü,¯ö•§W‹ù}òÐ}Ð}‚ÎÎ9sÌ O?=À`ß³Ç|tÛmä`a°ro°¼êíjœ^UwFGŒÑ}Ð}‚ÎŽÒRóÒ–- Ö©;ÍwÜA ƒ•{ƒå…YÕ8#íBayuñbs¶®n€Á:³q£i½÷^r°0XÁ0XÞaºû2zDôA4BŸ¬Mw®\iÞ¨ª`°´^+â‰`a°a°”{åM2¿Oþú ¡ lZ³Æ\î3‘«yÕ*Kr°0X0XZ=¨Æ™î>„ŒÑ}ÐÐ'[Ôô ¦ # Vºå†ˆ`a°|¥Wh”}!„ºB­z× L¤Áz¯¤Ä¼øÄä`a°‚a°4_íetÄè}ÐÐÇ=pÀܘ:u€Áêš5˜ܵ‹+K{ªq¦»!ùèƒ>èƒFè“Mª¨¨Š‹zK‹µT~ˆ, V –æ«Õ8Ó݇Ñ#ú ú úd“^ýF]ÃŽíÛgºgÌ…>,G –ö#TãdB!„.±½¬Ì4Ö×Ûk˜·õ[Ž;¯ –†cÇŽp_oo¯©­­5•••Ö‰1_ŸÊs³m°Ô(Õ8Ó݇Ñ#ú ú úd“ÞªAï¦UñD°2X#FŒèg$êû\óöíÛûooÛ¶ÍÔÔÔÄ|Tž›mƒå…WÓ݇üôAôA#ôÉ&[–.5V¯¶×°ß<ú¨ùmßµ3 úäÝa´Áš3gŽikkë¿ÝÚÚj c¾6•çfÛ`)!P3Ý}=¢ú ¡O6é™*]Ã<³E+ VAAÁ iÀèûR}®7}m°ÊËË­³ö~|ýõó¶]ExáBÆÞŸÛÜæ6·¹Ím¿okZðú¢Eööne¥. Ãñç½Áоï¾TŸ›í£#ôAôA#ôq‘^b» –—ðN+ +^¾U¶"X®,òÐ}ÐÐ'[ôJ3xû馛êBV@"XEEE¦½½}@^Õĉc¾6•çÁbôˆ>}Ð}’£Š‹Ê`yEG‰`åÁÚºu«] ¹2°ºº:æó‡zn¾,!„0›Ôö82XÞ¶9ÔÁr´LCäôáPµ­" Vë`1:BôA4BW© že°¼Ÿ‰`QÉ=0•Ü™ßGôA4BW©b£2XW/>¬,Fèƒ>èƒFè“UÓ¸r¥5X—ûL, VÞ,!„0›¼¨]Qú ÖUUä`a°ˆ`1z„èƒ>h„>~ðÌÆÖ`­«#‚…Á"‹üˆ>èƒFèãOíÜi ÖK[¶ƒ…Á"‚Åè¢ú úøÁã{öXƒ%£E ƒE„BèU\Tëð¯~E‹£Gˆ>èƒFèƒ>, ùèƒ>èƒFèƒ>, î}Ð}Т, 9XB!Ä`a°¡ú ¡ú`°0XÌ_£ú ¡$ ƒE‹Ñú@ôA#ôA 9XB!Ä`a°¡ú ¡$‚…Ábþšù}ôAˆFèC‹DôA4BôÁ`a° „BˆÁÊwƒUZZjM„BÃMy @ÁÀ``°œ…r² „B£ó´1X!1‚}Ð}Ð}Ї q¢ú ¡À`  ƒ0X®àÀfĈ Ÿ£Ç=†¯¿þºY¸p¡)((°DŸÿCkk«Y²dI¿6 ,0—/_FŸ>477›±cǸ¯··×ÔÖÖšÊÊÊþ%Ø¡Ô+–>;wî´[ƒŒ=ÚŒ3Æê¤6†>ô×Éêöþƒ@hgñÅ‹'ÝÐÂb ®\¹bn½õVÓØØ˜Òë¢Ïí·ßnvíÚeƒ¸cÇ3cÆŒÐë¯ã®¯¯7Û·o�mÛ6SSS:½âéSVVfl[êéé1›7o6sçÎEúë¤ô¡¿Æ`---vÔØÕÕ5¬v„ fÔ¨Qææ›o6Ï<óLÿãaéb«ÇfÏžm.^¼èœ6ºø=ÿüóÃê " ºFBǃ>±;î9s昶¶¶þۊΆV¯dú›0·§XúÐ_Çׇþƒ(hz¢¨¨È´··§ääc=ïØ±cfüøñý¯_¿¾ÿbrðàA3sæLçôÑÅO'­¦+nÖîæ‰¦t¦¦t~úÓŸš}ûö™«W¯šC‡™•+W¢OœãŒž²9j#Ÿõª¿9}ú´j¡ýu2ÇEÁ î¾ûn;gʈ2ÑóFŽóq]H¼Ç\‚¾sä”…ÌÄ¢E‹ÐçSÈT[S¥‘Ÿò±®_¿Ž>qŽ3Öq'sÎå«^‰ŽýÈ‘#6R(+lúÐ_'>.úk Vàh,F?¯A*\­DT]\5bˆ|íp.$A4XÑ'–¦ÅÐçwÐh:2T®ü"M_ Oê¬0êï;nذaPô}诓1Xaï¯1XŽŽ(c5äÉ“'Ûi¢k×®ÙÆœoH͹GæÌßýîwÑçSÄÊI&g&,úDçÈéAÑ™‰'†V¯XßQ ÜŠB g }è¯ãGúk –óKsÓÞJ1-“•ëWcTÃÌ· ¤Vy=üðÃý#i儬Y³}>…Ê2hšÐ-z‘ô‰ý·nÝjÛTdûª®®­^ÑßñĉIåÌ„UúëÄß‘þƒåì ­Æ§è„–æ755Ùû”ì§‚¨%èùvÖ®]k“&jÖÉ{ãÆ ôùêÈTËIÚˆÊÅŠ—ƒ&}âMã¤R+ŸõJfšk¨RaÔ‡þzh}ÂÞ_c°0X, À``°0Xƒ€ÁÀ`  tˆÜÆC[rhkŽââb³wïÞa¿`°¬SÔÝÝm~ö³ŸÙû´! €Á€4 –‡åË—Ûû##Yû÷ï7¥¥¥ý‘.°¶¶¶ïkcÛÇ›™3gÚ×iƒÛªª*ÓÕÕ…ø`° \«©©ÉÞ?wîÜþûd®š››Moo¯Ù±c‡}\÷%z¯S§NÙûî»ï>ûº;wÚÛÕÕÕˆ Âe°d†tAAAÌ×éñ‘#GÚ¨T¢÷*))±÷µ´´ x_E²,µÁêìì4fÑ¢Eææ›o¶÷G¿6Ö{=:æô!¹Z`° tëüùóö~­(d®t{óæÍö±žžž¤ –¢\ºO† 0X€P¬+VØûùË_ÚÛš Œ~^ôkc=§¨¨ÈÞ×ØØˆØ  œK«û¶nÝjï“Éò0cÆŒFéСCƒ^;{öl{[ òNœ8aïÓcW¯^µ÷9sÆ/ òÚ`y…F•|®•*ɉ‹/Z“ä%¶¯[·nÁ’ù*,,”ßÐÐ`ßSùXzýøñãí*D `°0X,€ÁÀ``° ƒ€Á,  Çg>: qƒÅñ €áàx,†ƒã`°Ž0X Ç À`0/ƒÈwÃñÀÄe4®_¿nªªªÌ˜1cÌèÑ£Mqq±ÙºukÊŸ?bÄ_§¹¹ÙŒ;ƒÀ`‚c°Ì„ ƒË`-\¸ÐÔ××›žžÓÛÛk^yå{_.!³æƒÀ`œ3X£F²Æ*~úÓŸšŠŠ ³lÙ2ûúŽŽŽ~ÔÕÕe&OžÜ{¨×<ÿüó}_c‚”éñdŒ €Á8g°JJJÌúõëMccã £¥ÈÖ¦M›úo>|ج\¹²ßü¼ôÒKƒÌP¢×ÜtÓM¦¥¥Å~ÎŽ;0X  ? –¢K2@ßýîwm4kÑ¢EæòåËö±‰'šÎÎÎþçÊyyQÑæÇ»è5ßûÞ÷ÌÉ“'“>& ƒpÒ`EBѥ͛7›Ù³g÷œXLd°½æÌ™3fÁ‚ææ›o6ûöíÃ`0X· V²«cA9R‚"Nñò³â¬D¯ñpõêUSPP€Á`°î¬T ƒ^þ•¨ªûî»Ï>¦dõmÛ¶Ù†‚J'Dæ`Å2C‰^£Ü,%Æë>%»c°,@^¬]»vÙDwå_)rUYYÙ¿êOáR^•Ÿ?¾FLd°½fùòåö3å:vìXBckŠƒÀ`œ0X/ƒ ƒÀpp¼ ÃÁñ0X€Á  #†#l @ñÿ¨·ï-ôº™0IEND®B`‚XYAreaRenderer2Sample.png000066400000000000000000000463251463604235500343640ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/xy/doc-files‰PNG  IHDRXr5˜LœIDATxÚí}lçzö‘ªªªªªê?UUUUU©ªªªê}UUUU! jš("%ÅR"PÀŽcÁ'¡p0æÃN 6&pL ÁÆ1æØ±K 81á3ƒc €q01çàš“TäÄ åy}ÏÉìÙïÇÌî|îþ.é’½Þ½ü̳³×\Ï=÷ÌPÀVÌ`0X, À`2lç4cFƒ€ÃxöÙg£¾|ÿöoÿVýßÿý_øñÉÉIõ7ó7QÏù³?û³¨Û÷w7íuåu"ŸóÔSOM{Î_|1íË_î ŠQÑù;¿ó;ê÷ÿ÷Õßÿýß«•+Wª_þò—,›ÑÕÕ¥íüǨ?ú£?R¿÷{¿§~÷wWûý?ÿó?UMMMÔ¼·½äïäorssUYY™˸ϴŒ†`°ð£££ÚNäY¾xtlذ!ê1yî§Ÿ~ª™ŠÈû÷ïßþ›ÆÆÆidpppÚ{_ÛøÞA1XFþÉŸü‰º~ý:Ëå±ÿçþgõ¿ÿû¿–þæÿðUsssF|–Å,VVV’X ~Á®]»¦å‹!’4I~|lçÎÚß¼òÊ+Q÷ÿõ_ÿu8A0&^’<Ä‚1åJ tƒ%œ5k˃±õÕWSÚ^A7Y‰þ70Xxˆú§ŠÚ)ÿÛ¿ý›š9sfÔ}òãããÚÑäã²LcL¯þôOÿT}óÍ7ÓÞ/ryP®ÈD̯˄ñ¾¸duûöíÓL*Ë>üÅ_ü…–xž>}ZýðÃÚ}’ÊÒ³1=LöÇú»?þã?Öæ4 ¶âÊ•+Ó–ýŒË|òœHlÙ²%ê9þç>-½Ò/#"—ÿê¯þJ«ãJ´LhÔ#iÙ¦M›Ô_þå_jÚt ¨%K–h_ÈR£#µQòÚk×®Z>hFRŒ¢&ïk„mÆ÷’/ÿ—^zIýÁüÆâââ˜ã*c"ã)¯/ï#c™ì 7]vl³¯ bÞ#ÿVÞˬ±ü×ýׄËÓfÿãûˆá–×Ò·ƒ¤»{öì Ï“7ß|SûœÈcò1çF>|X3úÜ”Ÿr[îÇ` ƒ|qÄÛI¯Y³fÚó%I/ˆx“h¹OÓŸ'†&²Ø>Öß_Ûøå(Ø·o_B“(_ަ¾”„¦ –|ÉËÒTäc999Q›®61ÉêÕæÏŸoj9ÌN]Nl‡X¯2#Ÿ+ÆÅ¬Á³b|ßTÆ%Öc±þFHþñÿ1æc’þêXµjUÂí'gzb  d~ýë_Ç4Lòå.ÅBSSSÜ/‚îîî˜c<{PŒÂ¶mÛžM˜Ì4\¼xqÚ—¤;vìˆûå$FNL”‰¶W¼ÇâmGãóu­,€Á HTû!©S€Éz¬æ¢ñ¹L˜ìõÕËÄú[)b7öÿJ¶<«ð\Î’4CãrªUm‰þßx÷ßÃ̶±S—“¯i„$yr¶`䙃²¬guNÊ2[¬º9«ÿƒ•9“ì11mfžoLW1Xƒ€O‘ÊY„©ìÔc5G+õ4Æ#ýTþx0¶¥X·nzøð¡å/ÄO>ùdÚýræY:ÚR1XÆ~ef^ËN]N¾f$d¹:ržJÜW_}•Òë‹é¥0•ÿÁNƒE‚ dŒg7I,c¬ù—I{§n\LÄÈeÂd¯ÿÌ3ÏÄí,¯C ”üO‰’‚T¾,c})uD©jKÅ`ëà"—*ãýºœ|Mœ©i¼¬“~'ZºNÔ˨S’H½~Ëêÿ`§Á2Ö`I«ˆXÉ/5Xƒ@ ½ªŒËRã"4&#ñ ÞÍìÔå‹-ò9ÒÏ)²VH~7ÁëË„É^_–üŒ 'å GIäuåŒFýôy±ÎF´‹ÈÍ~YÊë ŽüzšUm©,c¿+éñ%¯-EÕñþÆN]N¾¦ V› «Kw‰xôèÑ”ÿ; –qÙRL¤ ÙY„V—eÀ`à0äZ„FS“ìZ„±.kfGþúë¯G=G #Œ)…®ÅÌë744L3„ñ¾dŒ-R©ÁŠ„,SM›| ëõXV´¥b°$}‘†˜Vÿ'»t9ýšVÛ˜ýÙfÆ~gVÿ; – •>X,€ÁÀg06§”ëF֜Ȍñš±Œ‘™¹ÔËÄ[FÓaì«¥÷¢2ûE1<<¬™'©±’³¥^G¾(e¹QR½C»üÒ ]ÿ•%"cz`åËR`¼L±ˬ¶T –àÎ;Z»ý’Ô'VXªcæÄvpÛ`I:+¦J–ؤ£z¢Ëã˜ýì6Xz’%Û2²“»ÜN¥“; `° ƒ€ÁÀ`  ƒ0X, À``°0XÁĆ ¢n/[¶L-Y²Äw,,,DšÐ„&Æ Mhò+V¬À`¥j°JKKÕ'Ÿ|â;^¹rMhBš+4¡ÉC®_¿ƒ•ªÁZ»v­/7êùóçÑ„&4¡‰±Bš0XÁ4X%%%¾Ü¨B!Ä`‘`q„&4¡‰±BšH°0XÔ`¡ Mh¢ MhBS†¬™3gÆd²Ç?~¬ª««U(ÒÌ“ðÁƒ$XhBšH°Ð„&4a°ŒW‹- ¬xhhhPÍÍÍáÛMMMªªªŠ,!„b°Œ¨¬¬T]]]I VAAfÆtÜ¿_ååå‘`¡ Mh"ÁBšÐ„ÁŠÄèè¨Z¼xqÔòá¬Y³4Ο?_ÕÔÔ¨ÉÉIí±ÜÜܨ¿•%Cã}Ô`¡ Mh¢ MhBSÖ¬-[¶¨S§NÅ]:ƒUVV7ÝŠuߌ3Â4&X²au÷,?ýp{``ÀWzä§N?—Œ“ß¶ßàà ó‰ùÄöcû±?ø|’Ÿe°†‡‡µ–ù‰ )ÕìÙ³mI°¨Á‚BaÆ'X²d×××—ð9>TsçÎÕ~/**RQ5X ,  MhB5XhBš0X‚¡¡!µ|ùòi÷«ÞÞ^-s%mjkkµÇµ3#Ï"”yj°Ð„&4Qƒ…&4Es`ï^u¯®N:vŒqÊ&ƒ%FêêÕ«ÓîïèèЮh““£æÍ›§ê¦&Gä’ }°Ð„&4‘`¡ M‰y±±Q•”¨ÿY·NÝ+-U½--ŒS6¶iàZ„B¡½æêë5k¢8X]­Nvu1F,®Eˆ&4¡‰ Mh²Â¾ƒ£Ì•$X‘&ktê{ñ³æf¶‹k¢ Mh¢ Mh2à ­­jlíÚ(Cõ«§%YÂ/jjÔÉ?fÛa°H°Ð„&4‘`¡ Mqß»­mš¹Š•`Eòî”ù:?eÊØv¬´ Ö¼y¥ª¾¾_uud½BaFPÌ•,ýÅ3RÉxm÷nu¢»›, Vê+7wZ³ækµví˜zûíª½ýGb1"æ[o¼¡ú8²GšÐ…&ßkêéèÐÎŒgž%XQiVY™kg’`e°ÁŠdyùˆç©–Ö£¥0òÖÖ­áÛð/~A}šÐ„.4ùXÓ™ÎNÍ%2Nñj°¼L³¨Áʃ¥SR­êêAÕÚz>kŽÄN?®ÎûÕæÍÓ>dw¶oçèMhBš|ªéìáÃZ U2Ãd6ÁŠäH(¤Îµ·“`a°Ì¬ÿ÷ø˘jíÝ; Ž9‘ë̺±’Q¼˜K²&!„þ£tfO´ÿ¶‹WßyGû¾  ƒ•V‚‹%%¿IµZZzÕñã'ïæÅ0%3V‘G=^œ]Â+šH°+4ŧ4 ½]QaÚ$¥’`E¥YååªÇæ4‹ ƒÅPȹTËéõhù@^©«S÷¦&Ù•¬Û_Ûµ‹š 4¡ ]hò˾üã-™«Tj°b®h””h}³ìZÕ  ƒ7ÕÚ¶mÈÖTË)7ŸŠ±Š<ê¹ qÄŠ&4¡ M>Ð$ææËˆ‘ÜJ°")æîlg' +õ,³,+»«jk¯ªÎγ¾š$é+#ÏØða‚B˜ž¹Ú¶Íñš+³iÖå©ï—L¬ÍÂ`yœ`%JµššúTw÷ ÏܼÆJ?ê鯯çˆMhr‰\R;vŒ¨ööÆ MaÞ¬ªJ{_n7ÓI³H°0X)qýú{Zªuø°ùT+Ýõh;•qÝ^zcQs&49Oi|,íb6nü•¶/Ù±ã†oÒq¶Ÿwš®ïÜi˾ܑ4kíZí»ÇjšE +mnÝzËTª•ª›—£)D·ÓXzüÔ®£h4eª&9yfÆQm¿±nÝÿD¥ã55_¨cÇN1VY¨Iöïvíˤ–f>L‚E Ö×®SR­]»®Ùû‹±ºñöÛÚZ¸kî~k×a&QÀ**†îCJK喙º+)• À`òÊž=¾¨¹²’f ìÛG  –w”icãŨ¥Y7鈴Š<êñK»Ž¢Ñ”‰šäº¨‘ûˆÈËÈïªýûûíËÇöó^“,»9±/wƒRVrúèQ, –w”#RIµ::>Mºívbe\·÷K»ê@Дišöíˆa¢~eª/ßÁƒ}®-¶Ÿ{šì4WN×`Å£”­$:AŠ, –k|ë­»ÓR-¯ŒU¼£?´kà(M™¤©µõ‚VceÜ$J°¦'â·]¹~*ÛÏMŸ¿÷žãûr79TY󻃋,ÏjµNèPƒÕÕž«xôS»ƒN9ËX>ïví;¤MŒßZ;@k”˘ùiŸo[šUZª.¾ÿ>5X$XÞP?b­z­_u¾§ú_Ø¢¾(,W·²^ýÔ›…ñ¨Çí8ŠFS&hêê:©]P>Ùþ :ÕÚíç¬&1 N™+/¬Hnß®Í"ÁÂ`¹ÆŸ­¹6VF~¾x‹º¹¬L®Zëê‡Á¸nï‡v Ô )4IÚ”h`¦+Yãc»[;°ýœÓ$giËþÕ­}¹×iVßÁƒÔ`a°œåÚ5÷ÔîŸô¨c…û§LÔ1Í•‘_¾®¥Z^$X~h×ÀQ4š‚®IZ-˜M´í8‘Æ®Öl?g4kow¤¡¬¨¾Y»w'=ÓƒE –õ:«Õ#jÿ+«3ù;M™ªXX²Y -ߨFW¹»^µ¶–Z S¤œõ«¨ÝizÕÚzo®üÌ»7ªÞ–  VúÜ´ê¦ú`i«ê}a[ŒeÀ7R6[’j}õê:WŽz¼n×ÀQ4š‚ª©££ÇtQ»] –­˜Söj’3ëtçÇ+R“œ)òã1X,ë”Âõ#…õêâ o$H¤ÞLÙ`鼜¿ÉÖT+Þº½—í¨AS5=zZ••ݵ8ýÊáfÇÖ[;0§ìÓ$—”‘ƒU¯êiý@£& >¸b+Kê«Þ)>£º_|×”9J'ÁŠÅëE!5òÚ:GŽzäTbŽXÑ„&s”´è7n¥tV±×J5ÛÚ9e&©;º[Væéá~K°Œe(^¦Y,×`I}Uý+¥U_e'õTëÞjûjµ†¶m£~B“|竾î½'ܾ}БÖpº¹º½ysÖÖ\™åHy¹:ßÖ†Á"Áúm}UÓ²Õ…Åo¥d„ìN°bñÆÒº³²4í# /Û5p¦ i’«3¤RÔîV‚e¥µs*=M§ŽÓŒƒ[&EÚúÈÁõÕ7©ëE›ÔÝÿZˆ+’rÉ Çc°²Õ`m[yYµýå¾[6LF1ªg>‹ƒår‚ZuKX~4åú*¿&XñR-½V+Ù†í8ŠF“ß5Éòš]×&õW!üË­²}NI'»L•ô:”}³ì£íü~£fçIPNŸÙ(i–“g²c°\2X[Vݰ¥¾Êo5Xæ[=T$lõàE»ê@ÐägMvuj÷²+™&iíà£åç9u}çδM•\-USeåûÅ‹eÃt{sÉÙìN\jƒå°Á’Æ vÖW)ÁŠ¥I¢äXuZ^´k ™A“_5YéÔÄ˨IŒVGǧ̩š®íÞ–©’>†^|¿\}±ÜÒ™æ^÷æ’Ë Ù} Ár Ko z*WÆÕWÙE¹$Oä‡ÏËv ú‰V;µg %­{ûíôЊàåº:k&au‰–ÉþÕ/ûz¯– Så`uµmiVF¬™3gƤàñãǪzjÀB¡f„F‚å&ùr‘£÷xG®b´ŒŽ%A“x¡§G=|X;ûHêhä,°k»vi§}WThg&M+¢]òì³w6gTb”Ë)£å朒V\R••CªrÍ•¸'HeC‚ed*‘v+Á²záèŒ0XÅÅŪ··WK Ä\Ië…ÚÚZí±Æ©œy¦`eeeÒÇ̬¥>5XAg²eŒ FÕÞ½¶-ÍòdW×oÒ§ƒÕ•={Ô;Ôí8æ)¿|e£/>o’ší)>÷À&(´;Ѳ“Ò~£½ýÜÔ÷ÒUU^>ÖüÆÊ/Ôù%Uì÷§-nN{ÙÐ &»ptF¬Ž)¹bÅ •““£æÍ›7õ!«‹Zösª –3šd¹Äìõ½÷>WÝÝ'HfÐdO;¦ÕYH uuê@mhê KLÔ½ÒRÛŽ¢å²Q~ûìI]—ÔzI?¿ %Xv-»ç””445õM}Ç j…±.Ÿ–Ì\ec‚elH-gOú1ÁФì$½¦“»Ü©ÁrF“,]X«ç¸«êëû--j‹²S“ôµé™:(“j`ß>­×Í­­[SJ¡R©‘/ c­Ÿöb´ö®øD­ûé]_×`™1Z’tKzäÖrD;+Kvx_ÔÔh)Ô­7ÞP#¡P¸ÇWݤÝîc”êþ@Ò•†åÇÔÆÕ·Ý›+ÑzŒ%Û/¤2Ïå ®µõ‚ª©ùÂôådßfÆ\‘`M_6”k+ú1ÁŠºÔÎöíZ ŽÁJÓ`Qƒåe©Âé* v-TO{»ºøþûÚõÚ¤˜üfU•Vxšl)ÏKÚ}á]7('žZÖáˆÑr“víŽùMzUÕMËä–åW®Qëβ¡—ò ’c°H°|©)V»«”bÒææÏâîPI‹‚©éLg§¯ŽXÍEùʆ@ïz_ئ—uª²U·Ý]^ŒV¬¤;Ñ<ïèèÑ–+*n§Üm_–\¿¸'ëöån,úq Ä`QƒåKM‰Ú5XåæÍ·ÕÁƒ}Óv¨Ô;S“ô ñCÍ…•:9#*SöòÙü`i«v Ö _QÀZZzcÎs9CYÎvî¼®Õx¦û^ÒsLúœeã¾ÜeC?î0X$X¾Ö$—÷°s‡j4Z¤EÁÔt{óæ@%XÒ<ÑÍ¢v·ör ÖŸ¿ÜžR½¤Ÿ®(©Ô¡C½Ú%xäd™­[¿ÔzîÙVoöÓ»ê£÷fõ¾Üi^/Ú¬î¬\‹Á¢ ÚÝ®Á*å ŸÖÖóÔ2Rwå÷Þ8‘¼·ºD])ؔџS1Zé&Z™ÊT“+˜¥q¯Ÿê³0X$X¾Õdµ]ƒUnß~ÇwF‹+ùò _k.biú¢°<«ö‡_z_kž¤Ë)Mb®d<Ø—»¯I®å(7, 5X (Íøœîųu«-j°óN(䛾7Éj°_.ËÚý¤6‰Œ–j°œÖ$©ûrï4}¾x‹ºµb ƒE‚–uíˆUŒVGǧ¤E>Õ¹<è÷k¸x=ûƒ)þwÑAõÖkŸg]‚%g[²/÷‡¦Ëù›âöÏÂ`Qƒ•Õ´£]ƒÊ)Øo¿}Cuvž¥æÉg¼úÎ;¨»º³²”ÏnŒD«êµþ¬¨»’æ¬ls?öÏ*×.Q…Á"Áâ¨'â”p§®–èˆÕ+£E‚›r1Õ»ee¾O°¤îCNg›Ç ÷«·_íÍØK.š-EÿìËý«IšýºUÁ¢Ë÷šÞ)>ãYÍ…ÛF‹¬Ø)¨UÛ_»øËîäŠËMR/gb°¨ÁÊZ~ºd‡vʳvÂÔhy¿<èGJ-ŸÕÔ)—‘ÆÂ~ùœ[ᮟœsÄ\A7;ÂoÒNLÁ`‘`eåQ²é±J§çºº+ª«ë$ –Ãì=tÈòuÿÜ-j_« ûñs´ý$Z5Åg=1Z©ì$}“:Q¶]fhºú¢½…ð,j°¡É‰v vô½Y¿þž­F‹¬é¼¾s§¥ëþ¹É±Ÿþ¶S»?wAÝœÉßéºÑ²º?2'Í5XÞiú¢ðu[ á1X$XÐäD»;Ïd²Ëh‘`–»»ÕØÚµ¾M°äŒ$??èYR­ö½ÒíŠÑ²²?&ªç—T±í2\“\z'Bx 5X¹ÞYhÕ-ß×cØhe;?knömÝ•t‰æ³éå²Yb´äÂÉ^Æåš‹n˜+èó–ú¥w0X$XÑdw»'{ñ¤j´H°¢9¸}»éëþyÝL”ËyM’hI;'zã™ÙÈ¥»ä¤¶]öiX²Ùr!<‹¬Àh²»]ƒ×C£µwï€:~ü5XyêØ15VRbêºnRŽdeg„Ï]¦î.,~KÕ¿ò‘­‰V²ýAùª!-IcÛe·&9ãð«W×a°H°2K“ìTK׌"Á22Q“-¬ßòÒ¦®ûç6¯½ôz`>w™ž8È>¡iÙ‡¶$Z‰öe«n¹n®H°ü­ÉÌ¥w0XÔ`ŠA¿ž™Y£?QC••>l&º‘Ï¡©-'ê4żIûÆÆ+„wÆ!‹+Pšìl×àåõÐÄh½ÿþt£E‚õžìêŠyö — –, ís—m‰ƒœ óó—ÛS2Z±öb®¤ *ÛMÉxsÙôBx 5XÒ$;» Õ`%cyùˆjié¥ËÀ½{“^÷ÏuWÔ`ùS“­ƒË[2ZÆý”#H{¶šR-„Ç`‘`J“ì8í:ƒÈËËÈŠŠÛêС^¬9\Q‘Ðð¸`I‡ç ~î²=qÐ-i­`e }·¿ô>ÛM)ÂK}‹¬ÀQúá¹+™Ñjk;ŸÕµWg:;ãž=è_.ãs—”³¥Í‚™Ï¡<—1ƒéƒE‚8M/ȸ˨iëÖ[ª¹ù3_û`}þÞ{IM[ –ÄýAþÜ‘8LçK[UÅÊkq?{ò8ã„& ++×È{_ØfK»?Ô`%ÓTQ1¬-fS ÖHyyRããF –\Ä9YÝ5XÁÕÔQt0ÊhÉgO–'4a°0XY}„!ZÍä+^{‡LO°zÚÛM™§,¹ˆ³ÔQdÂQ4‰Cb)¬WÛV^VïÌ8¡ ƒE ´³]C(gJ{‡îîÌì£uµ¶Öwq†B  VÖaÈéÓÙ”`M_J¼«¸äJ–› ÖPÈóëËW6fÔQ4‰šÐ„ÁÂ`±Fn‰fÏ r Vò†¥wÔ¾}–/*íǬs&—¬Á’S«3­„š4¡ ƒ…ÁâÃ÷®ø$k¬X‰ÖþýýêãO6Áº¶k—i#äD‚e¦™( šÐ„& 5XO»Ú5dׯ¿§êê®®FëÄñãênY™§uWfš‰B!‹+ã5É^¥ã2 ÖtnØ0ªöì¹¢Ž=ˆë|k«%3dw‚%×ËÔ£h4¡ ƒ•2Z§vÎ+V¬P³gÏVsæÌQ¡PHÝ¿_{læÌ™1)xüø±ª®®Öž/æIøàÁj°¤)v ™Pƒ•Œk׎©ÚÚ«i-7j°¬,Ú]ƒ•NÝ5XhBš2Ú`«ÞÞ^Í0=zôHÕ×׫°ÁЇ††ÕÜܾÝÔÔ¤ªªªH°¤)v ™œ`Å2Z»v]Sg}—`èîV÷Ö¯÷$Á]•^Ý šÐ„¦¬["œ5kVRƒUPP ÆÇÇ÷%õÊËË£+@<›¿“Ú+ ,)S55_L­3¾©¿êmi¡î BH VÌU__Ÿ–jéKÌ–pþüùS_.5jrrR{,777êï$3Þ§ýÓ3f„iL°$šÔݳüôÃm=.õ‹ù©Ó‰×¯(§?²Ä¦§@ÉnËïVžïÆíPè[WÞOŒÖ®]·UOÏõpB%?e9Ðx{`` áãéÞ~çp*%Kz:•è¶N³Ïu{hùë¶ÎGý³ÇþÀÛýA&m¿Ëù̧ Î'ù™qëÔ©S*???\ƒ I«Ä`•••ÅM·%^Ô`ùSSªí²¡Ë ««UGǧžÔ`¥²TsçÎÕ~/**RQ5X , +`L·]ŒæÖ­_ª¶6wš‹VW»Zs%ÍD¯lâs!Ä`™AOOOÜÔ*ò C1WÒ–¡¶¶V{¬±±Q;s0ò,ÂÊÊJ¬jÚ¶ò2 –ÍܶmHµ·Ÿs,Á’åÁ±µk]M°œ¼ˆ3 šÐ„¦Œ3X‰z]utth=²rrrÔ¼yó´eÄÈ%Aú`e†¦–¶Rƒåö³¯Ukëß,¦Zƒõå+²²„š4¡ ƒE'wŽ0Ræ™Ú5`YÓ´yómuð`Ÿ:~ÜžËð mÛ–²Á²š`ÙÕL” MhB‹kfËW QCåËËGTSS_ZæêdWWÊ˃n_ÄB1X$XY}„ñNñ,5…B©­‹i™&+ Ö…¯gõQ4‰šÐ„ÁÂ`±Fîj»j°ìÑ ÝQû÷÷[Z:LgyÐJ ÖÍeeY_BÍ šÐ„ÁÂ`q„‘{–T[j×@‚e¯¦²²»ª¾¾_uw'6Z§Qc%%Ž'XN4%ÁBšÐ„Á¢++¹ó'ç©“ò<ñº«êê.«?>Ó`õ××;^w%ÍD?_ÌçB˜¥ëÎ;êÂ… ÇÆÆH°8ÂH›?¹Ë'šÖ¯¿§-c¢ukëÖ´ T¢kì§k]²ÃT»j°ÜÕTÿÊGqvŽ[´~UVŒ–±Ë­f¢Ô`¡ Mhò¥ÁjkkÃ`q„á «^ë'Áò™¦Sù»’n·Á—ËÔ誵–,YnäsÇþMhÊjƒ%Åë………jxx˜>XÐQX~”š'q˪–¶ŸY£EÝ„>XSxþùç5“%ìµµµjrr’‹# G(ÅÔ$XþÑ$†7•í(FëÎʵ1,i&ê§‹8“`¡ MhòÌ`ÉY‚~ø¡vv ­¼¼<­É(‹5r'(EÕÔ`ùC“™åÁD¼^Š2Z¿ÚXæI3Qj°Ð„&4ùºÈýÑ£Gª¹¹Y=ýôÓÓš 1Xa¸Ñ®ËMÛV^¶m›J1û•¥êæ²ræ8û4¡‰ˈ~øA555©§žz*Ћ,³£è õO>`Ó²™Bj°œ6Xjþüùá%Bi,Ê!GN°ï…7UéšQ,5I» i›ÁGšÐD‚å ÁZ¸pa¸ÈýÝwßÕ’,ŠÜY#÷ª]5XÎk’ñgŽ£ Mh¢‹6 $X¦)Q»,ç5µ¼ÜÆGšÐD‚å´ÁjooϨF£Ô`ùŸröuPÞP–g/,~‹y!¤ËIƒ%ÉÕàà éç_¿~]-[¶Œ‹# ÇÚ5`9«iÇk}Ìqt¡ M$XN¬E‹iK„K—.ÕZ4\ºt)ªK~ÐÎ.,**Òž»xñb k䎵k ËYMrÑmæ8ºÐ„&j°6Xß}÷ª¨¨ˆÙ–!+++Õ÷ßÁâ#m)¬'ÁrY“Ó˃ÌqÆ Mh"Á2`hhHmÚ´I=ñÄÓLÕœ9s´Çä9Ô`A·Ú5@û¹û'=Ì=!5Xn7Õñõ×_«sçÎiœ˜˜à,BŽ0\m×@‚åœ&iòÊGšÐD‚å‘Á¢MkänQº‰Sƒåަõ«G´Ô9Ž.4¡‰,  V†k:›¿“Ë%MrRs]hB ‹¬,a¼v Ð^ÊIÌ7!5X,¬,Ñôî+'H°ÖZuK]|á æ8ºÐ„&, 5XÙ¢©«ð=j°Ö$&–9Ž.4¡‰ÏžGKzbIŸ+iË ítHƒÑ–– G®´k Á²_“˜Xæ8ºÐ„&>{¬­[·Fõ¿Òqùòe5oÞj°2O“Þ®,{4m[y™9N šÐ„&?¬x˃B)€—­®®.Ç –ñ Æx÷͘1#ÌH¬]»V]¹rE?^}òÉ'ÚO?Üð•ù©Ó/z««Õ¯6nÔø?ëÖ©¯×¬Ñ~z}ûÛPÈWzô1JôøÆÆ¬ŸOr[>wìØ~vÞd>t>ÉOO –, &2Y:»»»}`I¡¾ $ /N™1 0=ŽM}N9œ‚B=1X‹-RÓîß´i“Õ¸nêèØé3 ¥î+R‡Ô`-X°À´Á’ËUwÐhŠÏ3á$ÆO š¦[[·2Ÿ|¬‰±Bš²Ì`Åëu%-V­Z¥ýþí·ß&L“ì@cc£væ`äY„r ³«´´Ô—UbJ4%çø[oùÎÌèËqAÑÔ__Ï|ò±&Æ MhÊ2ƒ%uVCCCÓîS%™Y®óC,¬`kú²®Ž+ M^.2Ç+4¡‰+–-[¦ž{î9Õ××îè.]Ü%zþùçµÛgΜÑnû¹“;5XÁfï¡CÔQ¥Á¡mÛ˜GBè'ƒ%Æ*^a{[[›öœ… j×+ô³Á"Á ¶¦ÞO?UcSÛ+5M<{ùD‚…&4¡É'K —Æ‘"v©Ç’eÁÂÂBõá‡úºo5X™§IRj°¬kcz²«‹ùD šÐ„&¿¬ ‚+ó4õ55‘`¥ Iúˆ1ŸH°Ð„&4ùÌ`I!ùêÕ«µô*Ö2aP 5XÁç©cÇ´bmjª¬QŒ)óB}f°di-QƒQ,Ž0ÜÔt»¢‚Ë‚&Y<ÑÝÍ|"ÁBšÐä7ƒ%íZZZÔ£G½DH Vfhº²g5X4y½<Èg¬Ð„&j°,½=5Xax­é|k+ –M½SGÌ',4¡ M>4XR´ôЬÌå‰ãÇÕ½©µUÉ)ãäõò „Rƒׯ_W'Ož$ÁâÃ7šnVU‘`™Ðtãí·™O$XhBšüj°¸©È¬ÌÑÔ¿?5X&4ùay9ÎX¡ MÔ`e¸Á"ÁÊMg;;I°’hº[V¦-§2ŸH°Ð„&4Ñh”,hšÃ>j×àG^Ûµ‹y!„~6XãããÚerä9$XaøEÓÀ¾}$X 4ÉÙ–Ì',4¡ M>6XbTÄHåääPƒÅ¹o4õttPƒG“Ÿ–™ãŒšÐD V<ñÄZ£Qé…%=±ô– ÝÝݪ¦¦†‹# Ï4‰‘ Áš®éjm-ó‰ MhB“ß –, êFŸ~úi588õ5XÐ+ÞØ±ƒz«<×ÞÎü€B¿,©¿7­¯¯×~ïïï×– I°8ÂðJÓÅÆF,Ç6of>‘`¡ Mh ‚Á:räˆf²eeeQõWùùùÔ`±FÓGª±’j°"8ÒÔÄ|¢ MhBSÐÚ4<|øP3Y²4¸páB544D‚ņ§šn{Ø®Á ÖE^‡9ÎX¡ M$XôÁ‚ã•={¨»ÒÓ«Pˆ9!„A7Xß}÷Ú²e GžjºÐÚJ‚õ#¯ÔÕ1ŸH°Ð„&4ùÝ`}óÍ7jÕªUáå@)j×!g>÷Üsê™gž¡‹5rO5I¿'In¨ÁZ£>íè`>Qƒ…&4¡É﫲²2ª ý©§žRª©©I;{PL×ýû÷I°8Âð\ӵݻ³>Áº3e2Ål2ŸH°Ð„&4ùÜ`=ùä“jçÎZ,)n—Hú`‰ÙZºt©zðà5XÐì=t(ëë¯.×Õ1 „0K–¥ÎJ‡¤Ub®V®\©¾ÿþûÀ¹“`e®¦“¬Æ¦¶o6'X²<È|"ÁBšÐƒë:ƒrŸÞÕ=h‹¬ÌÖtkëÖ¬­Á®¨`>Qƒ…&4¡)è+¨mH°2[“œA—­ Öçï½Ç|"ÁBšÐ$ƒe†Ô`A?P®¿—µWÒÉþô‘#Ì!Ä`‘`q„ጦ;.·kðC‚5´mó‰ MhBS&5¥‹5r¿iºñöÛYWƒ%¼f>Qƒ…&4¡ ƒE‚ņcšÄld[‚u¦³“ùD‚…&4¡ ƒE tŽ'»ºQƒ…&4¡ ƒE‚ņãšÜl×àe‚5X]Í|"ÁBšÐ„Áú-Z[[ÕŠ+ÔìÙ³Õœ9sT( _4:Q+é _=õ¥"Ïó$Lt=Dj°²—n·kð‚Ÿ57³­!„ƒõ[«ÞÞ^Í0=zôHÕ×׫¤âTóÔ—ŠŽ¦¦&UUUE‚…&ÏÚ5x•`Ý+-U'Žg>‘`¡ MhÂ`%¿°t2ƒUPP ÆÇÇ£.>——G š,4¹Ù®Á««÷Ð!æ šÐ„& V|œ:uJåçç‡k°"!i•¬²²2KŸž1cF˜Fƒ%ÎY߸ò“Û™{ûÎöíZ¤› ù™ ·ùúëêDw7Û›ÛÜæ6·m¼HƒﺅuuuZáy¢BuI©¤ž MV5¹Ñ®Á‹ëÚîÝÌ',4¡ M$X±qéÒ%­Ð=>|¨æÎ«ý^TT¤&&&¢j°,X@ š‘`¡ MhÂ`ùÏ`Qƒ•t£]ƒ<{ø0ÛB1X$XaøCÓùÖÖÀ'X·+*˜O$XhBš0XÔ`±FîMR·t·¬,Ð5XVÎd>Qƒ…&4¡ ƒE‚ņ+šœj×àF‚%EúR¬Ï|"ÁBšÐ„Á¢ úŠN·kp’Ö!„b°H°8ÂpM“SíÜH°ú÷ïg>‘`¡ MhÂ`QƒÅ¹?59Ñ®Á¬SÇŽ1Ÿ¨ÁBšÐ„Á"Áâßšœh×àt‚•Êò ó‰ MhB‹,èÞ‡ÚÁv NñÒl;!Ä`‘`q„á_MN´kp:Á²º<È|"ÁBšÐ„Á¢‹5r×5ÙÝ®Áɬ¡ÊJ¶5XhBš0X$Xaø_“ÝíœL°úššØv$XhBš0XÔ`AÿÓ©v ¶7:8ÑÝÍ6ƒB  GÁÐdg»§¬Áêj¶ šÐ„& 5X¬‘G“휪ÁêmiaÛQƒ…&4¡ ƒE‚ÅFp4ÙÙ®Á‰ëÞÔ‡<åAæ šÐ„& 5XÐu:Ñ®ÁNÊ™Žl'!Ä`‘`q„8Mvµkp"ÁJgyùD‚…&4¡ ƒE käži²«]ƒÝ5X’¬IÂÆ¶£ MhB‹‹#ŒÀi²«]ƒÝ Öµ]»Øv$XhBš0XÔ`ÁàÒÎv vQ ðÙ6BˆÁ"Áâ#°šìh×`g‚eÇò ó‰ MhB‹,ÖÈ=ÕdG»;k°®ÖÖ²í¨a¬Ð„&  GÁÖdG»;¬sííl;Ž¢+4¡ ƒE  >íj×.ï„Bl!Ä`‘`q„‘šÒm×`W‚e×ò ó‰ MhB‹,ÖÈ=×”n»»j°Î>̶Cc…&4a°H°8ÂÈMé´k°#Á)/gÛ¡‰±Bš0XÔ`ÁÌ¢íÒ¡¼?ÛB1X$Xad”¦tÚ5Ø‘`Ù¹<È|"ÁBšÐ„Á¢‹5r_h:ÑÝ­îM}°¼¨Á®¨`Û¡‰±Bš0X$Xad¦¦TÛ5¤›` ìÝ˶Cc…&4a°¨Á‚™ÉtÛ5¤Ê3Œ?„b°ìÁÌ™3cRðøñcU]]­B¡f„F‚…&/Ú5¤š`IsSirʶCc…&4a°l5X³fÍÒ8þ|USS£&''µÇrss£ž+Ë‚ú}‰£ zÙ®Á*¥¹)c!„,Ç ‰”¬²²²¸é–~_¢Ç¢þé3Â4&XMêîY~úáöÀÀ€¯ôÈO~/''_ÿBOz°ys8•’Ÿ²˜èö·¡¥çë·eyù”Ùó‰í—}ÛoppùÐù$?i°Œ…ìFH5{ölÇ,j°Ðdw»†Tj°Æ¦ŒþÉ®.¶š+4¡‰ËY<|øPÍ;Wû½¨¨HMLLDÕY-X° écÔ`¡É‹v ©Ô` mÛÆ¶Cc…&4a°ìGqq±êííÕ(1WÒz¡¶¶V{¬qê NÎŒ‘`¡ Mhb‰k²FîKMVÚ5X­Áœ:@`Û¡‰±Bš0X\‹#Œ¬Ót¾­Í±«¯©‰m‡&Æ MhÂ`q-B˜¼›F‡öx¼7õá=ÑÝÍøB!‹‹#ŒìÔtcÇÛ,iÁ¶Cc…&4‘`QƒÅyÖjºøþû¶×`õ¶´°íÐÄX¡ MÔ`‘`q„‘½šN=ª]/ЮënY™:qü8ÛMŒšÐD‚E Ìn޶Ю!¯íÚŘB!5X$Xa é²‰v f,¹4ÛMŒšÐD‚E käY¯ÉL»35Xn.²í¨ÁBšÐ„Á"ÁâÃ÷š’µk0“`]­­eÛ¡‰±BšH°¨Á‚PçµÝ»Ó®¿:×ÞÎXB!5X$X¸y4éì;x0­k¤¼œm‡&Æ Mh"Á¢‹õh4ER:¯'j׬K®kȶCc…&4QƒE‚…›G“_nÝšr‚uöða¶š+4¡‰‹,Ø·/¥Ú«áŠ ÆB©Á"ÁÂÍ£)%…J%ÁØ»—m‡&Æ Mh"Á¢‹õh4YmרëLg'ÛMŒšÐD  nMñøEM¥ËËåA¶ šÐ„& 5X0LÖ®ÁÈþúzÆ B©Á"ÁÂÍ£)i»†©ùc&Á’¶§aÛ¡‰±BšH°¨Áb=M©´kˆUƒ5´mÛMŒšÐD  nMf(Ë~f¬‹l;41VhB 5Xšj×ÐÙ™´öJ–Ovu1^BH  nMfùÕæÍ ¬ÁêjÆ MŒšÐD‚E ëÑh²¤aÏž„5X}MMŒš+4¡‰,,Ü<š,ihk‹›`Ý›úpÊÙ†Œš+4¡‰‹,­´k8~\Ý+-Yuãí·#!¤‹ 7¦Txë7b&X½--Œš+4¡‰‹,֣є å"ÎÆ,¿,²í¨ÁBšÐ„Á"Áâ#šÎ><-Áº¶kã„&Æ Mh"Á¢ Ât8 EÕ_ome\ „,,Ü<šÒá55áënY™VüÎ8¡‰±BšH°¨Áb=MiðBkk¸ëjm-ã„&Æ Mh¢‹ 7¦´Û5twk—Å‘ë\{;ã„&Æ Mh"Á¢ B;8´m›º 1BH  G¬h²‹ÕÍ÷ÞcœÐÄX¡ M$XþÀÌ™3c2Ùc?VÕÕÕ* iæIøàÁj°Ðä Ïtvªë==Œš+4¡‰,b||\-Z´(l°â¡¡¡A577‡o755©ªª*,4¡ M$XhBš0XFTVVª®®®¤«  @3c:îß¿¯òòò¨Á‚B!+£££jñâÅQˇ³fÍÒ8þ|USS£&''µÇrss£þV– ÷‘`¡ Mh"ÁBšÐ”õkË–-êÔ©Sq—Å`•••ÅM·bÝ7cÆŒ05X²ö«o\ùé‡Û7oÞô•žX?ý OÆÉoÛodd„ùÄ|bû±ýØ|>ÉÏ@,c±ºŽááaUXX˜ðo%¥š={6 šÐ„&,4¡ M$Xf ‰R___Âç<|øPÍ;Wû½¨¨HMLLDÕ`-X°€,!„b°CCCjùòåÓî/..V½½½Z:%æJÚ2ÔÖÖj566jgFžE(ò$XhBšH°Ð„&4a°~4RW¯^vGG‡Z±b…ÊÉÉQóæÍSuuuQK‚ôÁBšÐD,4¡ M,:¹s„&4¡‰±BšH°¸!„B©ÁÂ`‘`¡ Mh"ÁBšÐ„ÁòÆ`Qƒ…&4¡‰,4¡ M,,4¡ M$XhBš0XÔ`A!„¬¬2XÒþAL„Ba$Å#`°< ƒ€Á Î`̘¾ÄŽŸèG]hBšÐ…&4eÛgƒ•†ÁBšÐ„&öhBšÒÕ…Áb‡Š&4¡‰ýšÐ„& €¿ÁÀ``°0X™ŽááaõÄO$|ÎãÇUuuµ …BáS4 ~J°’érk¬¬ªº™Ì¤Z<ëÆœJö>^Í)+ÿ»Sã$FÆãdE—ÛsJN¤ŠW·êÕœ2£É­q’4{hh(|[j¬¤|€Ë3¹t ´dÁ‚¾2X>TsçÎuD‹þÊ‘˜™IéÖ8™Õäæ8ïƒïÅ|2£Ë«±J4×½++Ÿ?·Æ)Ñûx5NVþw§ÆIŠ#k`O>ù¤çãdE—ÛsJ)­ðÓ÷žMnS¬z«x5XfÇ ƒç¾ÆÆFíÌò»ây©)òì0™dršhmm­í:zzz&D^Œ“Mn“œÆ+g Éû|ÿý÷Z­“¤k^Ï'+ºÜ«dsÝëÏ^2MnS²÷ñbœ¬hrkœäݱcGxŸ _Ò?ûÙÏ<ŸOVt¹ùÙ“d&Q¦ceV“›ûsY&Ô)}µ$qÊê6 Æ%ÈßÝìbVSGG‡YJ =oÞíÞ½[+&—tVL,‡ûa>™ÕåæXÉþàêÕ«Iç½›ceV“[ã$ÿ§ü¿²Ý„R‹Yƒ•Ê8ÑÉÀf`°0X, À``°0Xƒ€ÁŸÁÊE á»ï¾S‹-²|q[t²~æ™g\éhÀ`2ÈÀåò>O<ñ„Z¶l™:|øpF,¹ô†¼Îàà ú裴 ÖÊ¥TbA.o$Ëó£££Ú¥Bä⸠X60“““êСCÚ}r¯L€\ØVþŸ¥K—†ï“kÖÉ}ò¿FB.$+÷×ÔÔDÝÿæ›oj÷ 0iÀ`€5ƒ¥cÓ¦MÚý‘IÖ… ´t+77W£ü~îܹi¯Õ××§òóóµDL–ä¾þúkuéÒ%UPP Ý'ŒŒ„ÿîÈ‘#Ú2œž ‰±Ÿ¦OÿýúõëjõêÕa’,%Z¾Óÿ—æææ¨ûå}äþ³gÏj·»»»ãKùŸä1¹À,ƒ),11r¿^³ÔÛÛ«Ý^³fVÏ$”ßuCe4X1_r{Á‚Óîs¦CÌÕðð°züø±jiiÑ—û⬓'OªGE=_ W<ˆi“çˆÉ‹Ä?ü6ŒmmmÚOI¹$Å3BÞK  <€Á€” – ¹_7Ë—/×n‹Òqûöí(³ëµâÝ'f%ä}¥ÞI<–ÁŠ„-¹oöìÙ1_OÒ3ýïÄPñÍ7ߨ¼¼<íñgŸ}6a¶xñbíywîÜaâ€Á€ô –žXÏ‹|Žƒ¥ß÷ðáCUVV¦%PO?ý´öZñLU<­‰ áõÄ,Þãb¨Ì,Ñ(Ï“¥R ,,9Û.2rÊ`‰¹’ßëëëµ÷Ô)7 –¾D(é—»'Z"Ä`€Á€´ Ö–-[´ûõVú¡, êBõT—õûb7; ÖÄÄDÜ%ÂuëÖi÷?~\»}êÔ)í¶Ü úáØØ ˜7XR¼®·*“¥C?‹NÎØ“e½o¿ýVû=V‘»ƒµpáBí÷þþ~í¶~&Ÿ]K0gΜi-ô6 ò¿FBoQ!G‚"w0X`Ù`é…çbFä >i`„)I«äyBù]Î.4k¦bÝ'=ª¤mƒ^Ø.ý§ì6Xz›†¦¦&í¶ÞhTŠÆ‚4 l4*ÐÏ¢”%M ²z£Ñ¢¢¢”_CïO£Q0X~Deee¸I©Up©0Xbàûï¿×ŠÔ#œšEqq±Ö,•‹=€ÁÀ`  ƒ0X, À`à÷âŒÚF ð£Á€¹ À—"`.0Xð¥˜K |)À\ƒàK0—,üÿ¥øë_ÿ†cæÌ™¾?Ÿ ‘/ƃ ìKñòe¥–-Sª¨è7¿'øbΚ5K-^¼xê©—i†‡‡ÕO6Fƒ")Käc?üðC”!‰õ^ñn§c°,QFÑ©S§´4,0X+KÒŽx:®)‘奢¢"ÍèÑ …,ɲ”þÜXF&ÕÇô¥*ãcÉn§c°,QÆŒQ]]*))Q<07—,0X?bÍšøîaõꄦD–­jkk÷%i‘3оùæÍT˜1sçÎ2 ‘IcLg¤nÉmƒeqˆ2bŒ.]º¤º[šK ¬±e‹R6Ħ<–Ä””——‡¿ˆ¥í$4bÄD˜1R”-5Fò7cccSo¹%ª¾H Š^_$µF‘õEn,‹Cø1êé鉛Za°À`øR4~)ONNªÕ«Wk_ÈR˜-g¨ å¬73æá»ï¾ÓŠÀ¥Hꉤæ'ò 91ræœP~—„&]ƒY#eµ]C6ŒQ¬ñ‰õ\ ,_Š€¹ À—"`.0Xð¥˜K |)À\ƒàK0—,üø¥¡]`°2ÿ) µl®/óÄIEND®B`‚XYAreaRendererSample.png000066400000000000000000000512521463604235500342750ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/xy/doc-files‰PNG  IHDRXr5˜RqIDATxÚí}lTWzÿWªªªªªªÿTUUUU¥ªªªªª*UUUU–…eš("%i£D›ÅÍzEÖ(Š–²Þ6‚nµUBðš0oâuŒ‰!YŠ¡à `°16˜Wƒ!!¿6–Q¶‚ ‚úüæ¹ñœ9¾÷Î}›™ûòùJ_ÙwÞç;gæ~Ïsžó<ßP V| 0X, À`Røôo”0X¤O?ýtÉ ýOÿôOÕÿýßÿ¯ðàú“?ù“’ÛüÞïý^ÉñŸýÙŸÍ{\yý6O<ñ„ï×tùòåyFC.K+äµ÷»ßUüǬ~í×~Mýê¯þªúÍßüMõÔSO©ŽŽKã¬,óõ å=ÿÖoý–jllTkÖ¬QŸ}öYf¿OGŽQ/¿ü²ú‡øë=ÛŸ·üÿÿøÖç­¿À`Qü÷ÿ·õ㯟å$hã•W^)¹Nn{êÔ)õ+¿ò+%—ïÞ½»xŸ®®®’ëä¶“““¾_“ùœækJ^}õÕyZ™üÝßýÝD¬0¯Åë}ÚsÙÓӓ͈÷ÿ7ó7êÿ÷ùñ,²ŽM›6Í‹8ˆ!’È‹ü¯_·qãFë>ßûÞ÷J.—èŒ=37#^2£3ú%üó?ÿóÔéú“ŸüÄ× W7/y0X6³h²ü¾÷—^z‰€Á øë¿þë’Àßÿýß«ººº’Ëä66¦§§­H„~½,˜Ñ+‰Î|ñž_‡¾<(‘=ú“¦eÂO>ùdž9=Ïœ9cQ‰`ˆûßùL,W¯^µ–‰õë~û·ÛGYÂüÁXØ>úH=|øÐõ½Ëç €àâÅ‹žKYrÜFÇüã’Ûüþïÿþ¼è•ñò }yðþè¬ü.¯eBóuŠyù÷ÿwõ‡ø‡Ök¶qáÂõíoÛ:JN̯ÿú¯[ÝÚÚ:o¹¦©©É2˜b å¶b”ä½I¾š$?ÇÕ_×_üÅ_O¸:$é¯þê¯\ßï|ç;ê7~ã7,®\¹rÞc}½~4óm cÊþîïþÎsé×ïçd>ä±Écɘ‘ûIDuÛ¶mÖmåýÉR­ŒM¹Nn#æÖÄ,#dë(åX. ™dè¯W 9i t®^½zÞíÅ0ÈÉÊí>a–õä>öýÅ èIøNg>§yìܹÓÓ<Ê |ffÆõ1Möõõ•}û·j9Ì|.1=åòÑ‚¾^?šUÊ`‰Y1ŸÛFÏÉé:§ûÈ$@ ¬Óuqµñýïßó=ËõQ ßýñÄì€Á 'øå/éh˜ä$/×9¡»»Ûõ¤tìØ±@Ïoî#±nÝ:ÏÝ„åÌŹsçæ(%¢±aÃר91%r;‰ŒH™*‰¼˜–0ËÍh˜¦4Èë­d>X¹ûÈëÓ¯A?§ ¹^åt4MŸD å¹å¯~ùþç†þnI4- ›6À`N‰Ù’ïÙeÞgÑ¢EŸÛÜ=¸ÿ~Ë8‰ÞH˜¾”ôÍo~ÓÑèÈ T¿\Œ¥ÌÛúYÞ‘Û„É¥2ßÏøø¸•Ïäù˽ÞršUÒ`™·±_[ÐÏÉ|ž÷ßߺ¼··×÷u²”*ò ¦æY[jD‡ä`I¾™þØE0XÁ²òY¼"0###óî¤,ƒ‰ÑCò“dIÈkÙ±Ü ]?±yQ7!ò¼’“$'^‰Þ™Ñ(?f#®–“É0Ÿ?èëõû^ªÁ’<§0Ÿ“Ûóxiåv¹YÃÞ kÞÞ~­A ß½nœ¼O3— ‡WŠD—¢F.¼àT\Ôú2a¹ç-WƒÊ¼¯$…›uÁ‚æ! ̼¿‰ÒA—ë¼ÞZ,YfÓ¯¯¯¯õ9…}N×yE£$¦KäÑÞ%jP0XäavÆi°œŠ‹ºQ_&,÷¼ftÂi'Ÿ³\Å¿üË¿¨{÷î~²ÛO¿ý_þå_:>·ì”„ø°+Ìë­¥Á2ê%Ç.Ìç§ÁªDK–Õõï“äÁIé0Xä fÄEj6™u°t#·Á2—½¨/–{^iGãVqÞ†y¯^ÑŒ ïÏ)"'Ï144d]/˱²‹Í^> k°Â¼^¿ïÅ4ÜQ –ä!™Ÿ…ÔH³s‘‚~Nq,3KJE8}†~r°¤ƒÙ~J’åý.€Á CZUæRˆäŽÍb™n ïQ –œ|õûJ½'½_›üoFìeÂrÏ+KhfGÙù(y\Ùéhoñ˜¹@òÚÌ$s¿ïO"mqUrw»<Ìëõû^Ìž“'Ož l°¼ØßßúsŠÓ`™Ë–bˆav:•Õˆ²Ô  €”Bzšæ¥\/B§f½QNÿöoÿVr_‰˜0£ökôó¼óŒ¢ÛÉÎ,qõÄèU[L/ƒÖ`…y½~ß‹[N^Tƒ%¦Ð©–XÏ)Nƒåõ^ƒÖÁ Z* Å’%KJ~𥠞ÿ"Kfo@'åÄ!ù)fÙf½-»¶“ßç½uë–eF$gIvÙÉò—œÌe¹Q*¡ÛÏå½Kþ”}¢—e,3Âôýɶy).)+Ï-&C–Åä}éѺ +Ìëõû^ä±E3yL;²);Jƒ ¹Ÿ¼_Yb“Šê^íqü~Nq,;’%åEôJîr¤’;  ƒ0X,  `°0X,€ÁÀ``°bÆÔÔ”ÕÁ½±±Ñ¢fggU{{»Z»v­zå•W,Þ½{—O,Ÿ}ö™zî¹çÔøøxÙÛvvvªžžžâqww·jkks½½0+V¬Pßþö·SÁú§JÍkEGt‚èˆ~è˜eíZZZÒg°Ä 8qÂ×m›ššÔôôtñøÎ;jÙ²e¾ Ö~ðõóŸÿ<KÍkEGt‚èˆ~è˜eíþõ_ÿ5}K ’˜¬… Z˃b‚Ü–ýÌåCY2ôZRL³Áb°£#:¡#úAtL€v©4XõõõjddÄ2K=R»víR«V­r¼m]]¯Ëìü,Ó`µ¶¶ZîÔPþrÌ1ÇsÌ1Ç{§Ö`™Q©††"XÌ@ÐÐý :Á ‹gŸ}¶$¯Jðøã;Þ¶¹¹YÍÌÌ”ä`-]º”,ˆŽè„ŽèÑ‘,²pÆ ż«ÑÑQõæ›o:.vuuY·×ï»~ýz"XÑ Ñ¢#,›7o¶’ÜeiPÌÖÇ VÐ:Xi6XB!LSk°*"XÑ Ñan#X,ÖÐÑТ:’ƒ…Áb‚ŽèÑýБ‹,!„’ƒE‹AŽè„ŽèÑ‘‹5ttD'tDôCGr°0XÌ@˜ÉAtBÇX8xäˆEôcÁÂ`A!ŒëÄÔ×§N}ðZ@r°0XÌ@˜ÉAtBǸxîwÔð¾}®Ñ­‹Û·«ÁÇÑqH ƒÅ:¹èÑÑ/'ÞzË2Yæåbºn´µ©[¯¾ªÎ¸0ôc’ƒ…ÁbÂL ::prÃuaçÎ’ËŽ9b+›cèÇ8$‚…Á‚Bè‡;¦n®[§.oÙ2/z¥,ózÉÁÂ`1a&‡N](Éíb $Š¥_~~×®ƒucýzôcÁÂ`±†N.:AtôÑwßµ ”D±ô˯½ñF‰Áz%º3‡ä`a°˜ #:Atœ£ä^ Ô\-,ù{Ó0Wå݇ŒC"X1¢®®Î‘QoK„V‡W6o.(»Ö#ÿÊo¢;„ä`Åh°*q["XÑ «CY´ ”] ËÌ¿ò“èÎ8dÁÂ`±†ŽŽèѱÀúûK ”] KÞ –W¢{ÜúÅѾ‡qˆv©6X ,°¸dÉÕÑÑ¡Aô;£µè£5xø°ktm¬³“qÉÁÂ`1AGtBÇdS–Ü¢š+¡Ù'0ˆ~’åVþÒ¶m¾ ›2!,r° „01ß½;ƒ¥´ä1[ô¯3v7^Ù¼™Ï ’ƒ…Áb‚Žè„ŽÉæµc1XzÙUxfp0pþUØ|/Æ!$‚EDGtBÇÄ0l{'^Ý´©h®¬¥¼S§ç_ù¡ôGdBr°0XÌ@ÐÐ1óùWv•uÛ\YyTZ¢º¥¸hÐçr«øÎ8„D°ÈÁ‚šóœfˆâ棞U¹úWAèVñBr°ˆ`AtD't¬9/;ôâäŸAÃ<¶[ÅwÆ!$‚EDGtBÇšSoA7¯ÿìg¾ÜC÷?ô!còÆ`Á‚èˆNè˜ÊwÇö9==e_ÃGýý5mÏÃ8$‚•ƒUWWçH'ÌÎΪöövµvíZË< ïÞ½K„¦,Á݉}=Õ×úñÍŠïf:ËÍL9¡³³Sõf86º»»U[[,ˆŽè„Ž)Op·vú0XgöïôzÅwÆ!Ì|Ë/šššÔôôtñøÎ;jÙ²eä`AtD'tLy‚»ðöŽe_ƒ4nŽÔdZ«øÎ8„™ÎÁƒµ`Á‹K–,QêÁƒŽ·mllœ·dh^F ¢#:¡cúÜ…oÜX•(Ú Ñdšq3¿‹P¢Sb°Ö¬Yã;Úåt™Ÿe¬ÖÖVËÚÊ_Ž9æ˜cŽË=ª>Û°A}º~½eRäo%Ž‡æª¹ËóžVÃÚ‰O^Ï•Ÿþ4òó õõñùrè8»%*ÕÐÐ@‹:¢:&ˆo½UÑè•m‚ô$t)«pÚ¨Ç2eÖË5ð}&‚åˆ{÷î©E‹9^×ÜܬfffJr°–.]JDGtBÇ —g˜Z·®âK"L'*>ïù]»æ%¥_Ù¼9òóŒuv2aös°V®\©FFF¬h”˜+)ðuëVÇ%À®®.kç ¾‹p}aÆC ¢#:¡czwê,Y¾³ŸWZâL; ' 爨Ï3ác·"ãïpê V_áËÔÒÒ¢êëëÕâŋՎ;\s¬¨ƒ!„UæÀ@œۖï$]êV‰ÉÒ_O‘´+›6ñ¹Âì×Á¢’;3tD'tL.ÅðTË\Ik´»û«zWÚóŠÉ³_OÏ#} ‡0w9X,ÖÐÑÐ19”hOµ –ä`ÙùQ’e_~âàÁb.X\Ï¥›6Æ!Ìd‹:¢:&“ƒî=èÁ’ÝŠV®UÁl— {{­Ë$>®ç²MãÁÊ™Á‚šŸl"ôý ËKÛ¶󯊻þºº¬×s*Æ×“õR , 3tD'tLêîÁwÞ©zëêÆ%ùWÂË[¶ÄžvvÏÆ!$‚EDGtBÇê³ÅEͬëííêÂÎ¥ý —Éë-˜¢¸žKžƒqÉÁ"‚ÑбêÔó ªÁº¹nUšÁ¼NÜ%>®ç’‚¥ŒCH‹,!¬*?ø ê&ÊnÌìDY¼´eKlÏ%Iôb"‡9XÑ «Æ‘wß­ºÁúÌcIòüîÝV|œÏ÷‘Ö÷qÉÁ"‚Ñбâ4ÍkÁººi“•çó ½ÿ>ãÁ" B«GIOJþ•ðF[[Im¬88šáR , 3tD'tL k‘àîÁªí¶<ŒCH‹,ˆŽè„ŽçGýý5‰R}Vå²·mcBr°ˆ`AtD't¬㬘žäVVK5ð}&‚5ý…YS]]ëõrÉÁ‚Âgñ]]‰Ê¿ªX^WÁÐñyÃÌç`IXnõêÕe ,ˆŽè„Ž•ååëM%9‚%“ƒåË`éÇ+W®T###VäJÌU{{»Úºu+,ˆŽè„Ž1qdïÞÜE°¬ÆÏýýŒC˜¯–~Ü××gí6¬¯¯W‹/V;vì !„ÆÈs9©eòtF?Cê`QÉ:¢:&€Þz+—¬sï¼Ã8„ô"¤DGtBÇÊðâöí¹ËÁNÞ7ãÒ‹DGtBÇŠðʦM¹Œ`]{ã Æ!$‚•ƒ!„µàdª¸'?` @ ,ˆŽè„Žñsêõ×sÁÊR_B¾ÏD°0X¬¡£#:¡c‚øáÑ£558µÌÁ÷ö2ùc°ˆ`AtD'tŒ—R ª–§Ö¬±®.Æ!ßa 9XB/O8Ûü+áå-[ƒE ¢#:¡c¼<³®#X’àÏ8ä;ŒÁ" ¢#:¡c¬<»gO®s°„ÇaòÆ`Á‚èˆNèø Æ j?½±·É©uK(ˤŒC¾Ã,r° „ð«ÃûïGî§waçÎ\ç` G÷ìaÞ¸QMõö:^/¦JN2#¿¸¿ë㎪O~ò5V0kèüxòg?³tÙ»7×z\ok+Ž·©mÛB=ÞłɗûÛ&Ç~¼¼Ÿ;tˆïÇÅãT,1HN$‚Å $É:JAG¯-Ý‘²g׿Nx’ã"ѽVÑÐÜãDÝ^ŸËñV0¹b,D?‰&æõ{û¡‘;6ú"c“Ö×LʘâüA+VÃå})ÑÎÁZºt)9X°¢:Jµ¹„g/ɸÌôŠØbÄݘ3BÛdIQC˨mÜÈx‹°¤vY, :ž~ï½ù»à ^«0Y ëk^{ã r°8gdÛ`éÇ]…“’ìÔw®/Ìxˆ`ÁJê('01RöIK"Qúvv§+?ÅdMÎP¦^ñÁØQÂ<~oeÇ›9¶Âäôé"X_q0¥Ÿ9Á l°¨ƒkAY*[1C²#»´ÊméSO(­?æÕ¤˜\©/vmãÆíä³É«&b.ƒ.o™¥-¤Tõ¯æ3-…‚!u°¨äÎ $•:êÑ(»¶•þ#lFO¤tC˜óZ–jHúx“ÚN^QA1´yýÞ:U_­Ê–¶ÐÆÛ™ì Lb+)KÏœ?ˆ`a°XCO•Ž²Å¿=:|Ø5‘º\4êºÑZ#ìv÷Z–jHòxÓ?'/Êg•åï­èà4Nƒ¶{±K[Œhõ²$*˜C“¤¬4·ÍáüA‹H`ÊL;He/¥†ýCzÆe)à¤v›rùTNEƒp¬†»–’<ÞNøü > ‘Ø&e£„YïK WYãn=77>õèÌ…ì Lb+jë!¾ÏD°0X05E%oÌíÈ s’6sœ†{{‹?¢\–ÎîÙãß Í™,©懼֥»[Pûœ¼x:!ÅF+E1J¦Y’ åt™Ò6hè¥-ô#~6eä¶/aŒ;$ ƒÅ ¤*Õªõh‘_¥l‚™ãtN‹4¹%Ië²ýš¬ë91~XËR Ioç|Fk] ¿âvïž×<üÌþý¾´±7hèË×7Ö¯/>Îõì Ljk¨Æm¬8ÁÂ`±†^ñÝca"=¢£”U2Rn¡$Ñ×0Or2ïÖ,…a-K5$y¼Mø4¹£{ö$æ{+ËJfÄT–¶£ôL”² æŽV/J”Ö¬u%ß ÷7bf’–ƒ•Ö„œ?ÈÁÂ`1 \*!è–|ÑÑNà5›àN?ææLÕO~KVêî$y¼Mú<éJ”2)ß[)ƒ`š)Éó3w¥ÉÄÁuƒ…A1àæØ· Õ†¥¼Æ$4yNr+lólÎD°0X0Ô#IÖ–|¿;Æ ·³·±Ë‹W­*1b%ä_ÅÅS)\ލ4ýÖ“Ü·¤¼f‰–ž5"jbå½×"¥²üég ÊÞIiŽý¨ím$§ËoŽ[^™æ–9, 3O:%òúM<×î;¡--:lý¿dœ ¯Ö uH­òˆ’:Þ}–hHJ±Q[G1NfDMZ¯˜9„r™œ²ß‚ sÚ-vCEÑ<&çBîzÍK+µ°8ÁÂ`±†îO ‡ÀYŸù6ŸhK(W´]Sú Ë©æäËÔ"/¥V¥’:Þœ>§$µu´óœŠÏZãl` ˜WhFVÝv:™ðɈ9‚2éÄù[ä`¹2)m˜8ƒ…ÁbR•s7•côãðá’±,3–+¿`ç@9õw«kUª!©ãílÀeÚ 5Ò*©£˜+kÓÂÜrž˜¢’ÄéÞÞ¯ëQùøÌǵ‰‚¾du ¯órG,¯¦Ï5nÄÎùƒ V|÷`I¾Má¤Pî¾fÕ”áp›µÛ‰ÉWj°ü,mê÷·#*'2Ø 9‰¬› iÃÄùƒ‹5ôªmÑwª[eæP™9v„Êí„'ùµZ,Û1æ]nz}s¼ÉV­ÍJ˜:djœ,:ê›2DG?Í¿{DÍûOÍí$ô[d4MLbVµ¾“œ?ÈÁÂ`1©jk¯=éèº<8g’̱ ¯¥‰Œ]­Ñò`5—¹dùTO¨7Ç›\Wk³f LZ¾Ôú{«É2ó¯‚~æÒÈé„ïçq‰`ÅT>%BXÎD°j†Þ ïÅ_T ª±±Q­ZµJݾ}Ûñ¶uuuŽ$+£=è´¶Ž=vºåPÉ}ü6®YaÃ*T$—•^ÉÕ’çSk³æsšJÀNB½ìä ú©U5âQÌÒ)¢*†Ì.  +Ϭ÷¹„ÍÁZ¹r¥Q³³³÷NªÏ<óŒ«Á"‚•–Kpv«O£çP™3b‰ÌH+É?æÕ¨H.¹Mzs¼Iâu­ÍJØÏéxªáÛ:êãV’Èý,sz•çñàÔóòBÄ"£D°²[Íó,W,X° ÷‹5ô¯ 3ºî¸Ó ‚ÛŒßÌé¨M-*´a5*’[JK¨/oÅêéµ4+a?'³çdµ¿·a–˜½j-I$Ñ©œG’Ê+d=+HƒyÎä`%Ò`=zôH½Wøq”%C7ƒ%æK¸dÉÕQøyðଠRJ”ÛÉ'ѷ݃n3b9YM$|æïö¾*Ñ~ÆÞ¥·´Ýiµ4+a?§ZFDÇë!ʸM„Ne¤œG­sóÁ:ï£,ç"X‰5Xv>•˜¢/¾ø¢ìí§§§-ƒµfÍGceSGkk«åNmå/ÇÉ<–Ƹòc+3ZûG×éøü™3óîïuûOwìP7 ³·ë“r, Þ•Òwxp°ø|'NÌ»~lÎ`Éõ× ÿ×j<ÈçF¿«sËmµ¿ã##êvˆÏ[–c‡Nr||ÇÛ¿þºúø7;~³v,F~Ÿó}œúÖÇ­¤÷+Vøº½älIr<¬üå_Ý2ê éÕÛ½fĹ¹ž‚Š•ܵTRF`®Tƒ>Þô%ÖZöa û9]ñˆU|YÓQK Fx<"Xñ±–cŠó¬XáeštÜ»wO-Z´ˆ¬œÕ¿òÚqgö®KjNG”]eqîδõÓÇ›žT]«K&ÏŽÅZkTòÊýW¬¦:H/Fr°*ÇI­Íçr°Rc°^~ùe5>>nE£¾üòK+KŒSb»¾ãPÌU{aÐoݺ•Vó¯œú² Ï¥’:#Ž£é³è¤ SFÀÞ±¨7=©ºVfE"“i, y1Bm*§ògÊ”*!‚U¥jî (ÿÁùƒV`ô÷÷[ÆIןxâ ÕÖÖf™''ƒÕW˜Íµ´´¨úúzµxñbµcÇzfAN*—ŒwIß!ö}¹-õIBü`À~zò¸S­+3©ºf%ª±ÐÛU“QJ'8™ê,M+k¸£æ´Öýû÷ÕúÂüÂ… KÌPss³•OE%wf ORuü6G–ˆV"X~Û'ß‹[üËmû—¥X}¼9åüÔ¬œséA™ôž„S{'–”^pø£ê@+Æb£5ÜQËù#§¬×^{ͱªúÄÄ„eÂ`±†wý+3t/;îŠÑ™¹‚iÏÁ’%Rý}9Q7“Af×zò¸ý<öxsŠÖ¬ˆÙˆ´­~×®šŒÝÿçP³*Š©2Ù ‹ZXœ?2–ƒ%‘+©¾.yQf!PYÊÃ`1‰³ÿ ¥n“}³øbZ#Xæûrâe-ZrÊg”ÉIß´R Nµ•jaV$2E;3²Y­±e¼9Õ?»ÑhÁŠqÙ>`¤˜ó¬Xª®‹¹2ó¥>ÿüsß»1XЩȥ_JsÝâîCmIÒÂ3eØõ–~w:íJ³ ë-†jmVnD<ÑJYÆn¹<Ÿ¨FVwÙ’ƒ«Ázþùç-ã" šÅ`‰Ù’]Ë—/·Ò1XÌ@õŸ+œìƒþðÓB÷fí¤4G°Îy,IȲž]ÝÚMé³Ò´T9Ÿ·¸g5Þœš ÛfåDÁ Yê jtF¢l•ª%ëØ}çÈãM/ìúáÑ£¹[†Kú÷U>cÎD°R]Ƀ•_†©#t}.ßFo“z5}–úyÉêýýås›Úψar[tc¹eB¹>ìrÝXL¥ .ûŒêÅEY¦úšõ®qätÁx)»”ù&«jËmyÐ,Û€Áb⇗BÖ’¥(§â”iŽ`yÕ šp0¢åêô¸íÐ#7p·Z¹è™K¤†ÐŘvÎyÔJÝ8Æ›½ •·69iø¾^«Á†Î9Ž`a°XCµaȤ^i­rÖ¡†Sšs°Ü*­»•Suhµâ·¯]PÊ-š¹tA–V®Å¸s®šm~dy/Žñf×Sš7ƒ•ôï«,£?z”ó9Xµ]"”–7—/_Æ`1 Ä›!ø†{{çUqÏBË-Š¥ï tKö³ƒ0ŠN^Iìæ’cjó7cÔNŒw5'qŒ7{sÃÙ˜rш`ÅËjŽ)Îä`9âÓO?µz b°`%‹Œê% &"ôKSË­Œ[´ËkaËpNIìf•ŸÄø8K4­K›Ó¥HêXÎÚ䤅g«8¦`Îs°Ü õ°(4Ê $P¡IêReÙi‡\"XV„N31ne Ê×tÒ'.Ì{§Í ~–V†bÎ;*Õ‹»vWãíæ\ÁÑ 0¤=‚åU>…ó¬Š,1W{ .ƒÅz "£rNdÖ?é¿‘ö,Ý<ÙÅkéèCã´ƒ0.NÏUƒwjã$Ñ=îe±jU¡·wMÆ5ÞD«ËG“ƒU9^ÕJipþ «fIî¦ÁÁ`1ñbÐRfÓg'‘•–ÞÐk©Ï­b¹åר:™IìNIø~šåƽŒ)ËvÕHt·wMÆ5ÞDÏ+GÁª`¢»Œ)ÎD°je°$rµzõju÷îݲ÷ïíí-)mllT«V­²Úî¸EÆÚÛÛÕÚµk-ó$ôzr°ÒÅ8êe™7çêOy-õÙ;Ð‚ì Œƒú@N-^üÔòzo•jš]«åòî&3}Í"SÒ2f4Ë/$^ZêˆyîÛ·O=óÌ3Ž·íììT===ÅãîÂÉDv+ÁÊåœÇqЯ×RŸ[~H¹¥·¨:™õœv9úi–ëõÞâÈ_«d÷8Ç›l¸‘±±›¥ïëPÂ[æÁJq«\í«¨u°,XàxySS“šžž.ß¹sG-[¶Œ¬ŒðjŒõ²–ƒµzy¹¥·8tÒ—âÜ"p–It¯HRrÝå9ò8Þòú}®rŸKÎ9ÊÁª”Ázôè‘zï½÷¬%C'È¢¹dh^F+½¬DôâÓœEܪ——[z‹C§“sísœzBsÄ>ø lÿÂØ£~UHJ¾8}ý4‡Q§<~_«Ýç’ó»cÉåSôÅ_¸ÞÆÏev~–i°Z[[-wj (9NƱä4È«Ì`íYŽÃÎE‰t}żVúùÏÌ“<ßè\“b§Û_)|Înãa´p]%^ßÇs5¸*9~eé–ñ—ŸcIgà÷;_Ç51X²d×ÒÒb-ëE`=|øÐJz_±b¬ŒÎ@$ÊáT ¹RU«óQ˜0ò°ü$`Ç¡ÓøÜ¬~ÈèCè´Ò‰göïOmR²N+ß×+ /Õ@+#,11b¤êëëcËÁr«ŸÕÜܬfffJr°–.]JVŠ(¹*ãáõKªù“Çœ)M`×Ì:qèšzýõªèdçy:¯S%[ÃØË—kñ4—ÔOV>¾¯òâüAVÅ Öc=fEìh’äQ Ž;¦:::ÊÞÿå—_VãããÖý¿üòK+KŒÓ`WW—µsPßE¸¾0ã!‚•J‚³ü0IÏ6=!Ú¾œV¼í<>,|eç^µt²>WmG¯{T›¯dk?ENãhñD+?ß×j6çü‘Ó–, Š9<ùä“jrr²ìn@ýýýV©¹íO®McÈxË×÷µcŠó¬y3µwï^õ /e Ó2 „’W­*î6¥…OÜãZJNðyæ—•S,O\¸pA=óÌ3¡[å`°²51—Q˜9døjUqw‹šU"·ñ–¯ïëhBK5ÁÊXë¿ø…êëë+Y.$‚ÅzµòkÈÁJ—NfÒù…*TíîþúG´ð[Ghóu3Þòõ}­d[Î9ÏÁzøð¡:Rø‘Z½zuI»ä`Ér¡W V~f µ\F!¢\$jU*îÅ]Ú²¤l±?U0YQǶDâoùý¾^ܶó¬ø –]PÔÜEXëâ¢ä`¥£D„ºÉªt¹ëd8WàT¢WvÕù¨cû†*×Lr©˜:XbZNœ8A,f ¾—Q˜90MV%«¸ CnÚTŒ^™´P`¼åüûZÉújœ?rÁzÿý÷­Ö8Trg =è2 9ä¾Ô‚× &ÎŽ^Ùm}"µ:xñÆ8´Æçr°ª¶‹ƒÅ $)Ë(DÐI/ iG¯œv2¥ìJd¼1½Zæ|ÔßËf "X9ßEûöíS---ª¡¡A-\¸P­]»VݹsÇuYÒ‰ä`%˜Ë(&%¢elË}t„^Q¬Ó￟ÈLq¬ ²###V×£GÔÛo¿m+u3XD°ÒE§efÄD’B©ÑvlOlßŽŽŒCÏ(–äùa3,"X±A’çón°²²†î´ŒBN¹/IáÉÂÀiÜ?rd^¿D“úr#ãq(ËóòO·lQo½Åùƒ¬d`ttÔŠjyí\.Y²Dutt¨ÁJRÕöcÇÔÈ»ï&f…ˆ:yñ̾}®‘×r'Æë;où‡z![½EØÕ¹Ý«œ?ˆ`Õƒƒƒjùòå®9X:¦§§-ƒ%õ¸œŒ•M­­­–;µ”¿y>>SÐûZáDçã_(ðöO~¢N:dß,üèØ?šòWf¨sœ”ã«sãÔÏyý¤0ŽÇ >§ëe|£Çúñµ·ß.ù=.P®—ßCÎ7é?NµÁÚ±c‡a Rý]ò¶$9žV8J'ø›ëÖ¹.“Di‹#ø$¿Åi…1‘ƒÄ4viubÇ=íÒÌWZ¤ #ãpÞ¦‰¯Ó#öíó\>$‚E«*—(‰îA!u¸¤‚<9X![ØNÖC{{¤dß’œ+íGE–X®W¡€$¹Eèw«;ý’Óõ…“¨[éÆ[¾Çá m²ª70w3êµ8H‡L„1S9ÈÁ·èµÒÛõ‡b®Ú Æ`ëÖ­D° ”Z+ƒ‡ºìnÑÍP¯[ò¯˜9H{«»uÔÎ:nÔ1:³?:2©ïsn_~îwsþÍz_N˜áV¹ÚVúÿ}}}VͬúúzµxñbkY‘^„_›¥¡€³¤qc™#Ž¥BúÂ4qjݺ’e›ryñäh´Ô©Fcj˜NêO½ýS-vz­\L¶·;Ž{˜±:XTr'‚u©ð£vÏž`u|Œ‘{³Õ¸ï t µ½þСyÑ`·(—,¥ßÔÌ:2Š×šc(ÌNBÙá¶H©×ùCv;Êk:EÔ|ÖÁÂ`ËÁ’ý©×_Wvî Ö#И‰_ݸ1òëv*¾HN¹/I¦y¢Ñûš“rKàŒ7Æ¡vs ÝØ5@–¥å>^mxž?ìI°Ûr°èEHË!'D Sç˜tø1‹Ú7«–‰( SêuÛ¼–¹Åd]ËÍBGÆ¡kÖž=ŽcÈg^¼¨MTÃD±¼Î—æ&Ö×ÞxCE+Ÿ+вÜÎ_Í’ ?Nš0;ìð‹ÚÖAÖö9QÀ4q̘É'- Ó·3UOp/6_·ÎªŸVvÃÒáþ›IGý޳D9X¬LD°Ì¢ºQ’-¸~+®—KÒ ÃZ—e ¢€NQ“…’/ƒŽŒÃÐ;S æ†Ëï ]0hñ Q,¯ó‡¾#޼["X¬ìä` X_R{V#[nKòI|Ö7‘Âwn»ªŽG¨‰u“œr_RF3÷PòÑ‘qX)–ÛQèdðƒF±ÜÎA‹;ï–, Vf"XúÚ¾ÌjÆŒµþŸK|vÅu'ž~ï½P¯Ù m3#&rê ÈÇÝ_èÈ8¬ÆÆ }âd’ê¶äèvþš+.gÞ-, V&r°ä˧ϮeVsÍHºó¹3D¯¸n2ènD½õ' ˜FJ$¸ÜÄÂØò´¶o÷,¡vƒFâÒqåÝ’ƒ…ÁÊDË)qr^>‰Ï„RYØÏŒ>PB‡íí̈‰¤iÙ&j'ÆãÐoºDŽ®Ìÿs[nt‹Â8õÐŒšwK ƒ•ú,3×Ê~·ÞŽ»4«²5x¸·—œr_R[Ñ]r£Êe¼1Ãî^ º<èÕêÉ-è¢Ã$=jÞ-9X¬ÔG°&|þðßô¹“ðb™­èa¶{EŘ9HC¹+v¢#ã0Pî_á½ê¿ÕA—mwˆ„¹Ea&] l¹¼[y޼4ˆ&‚•³,ÜSí9ʵþp¢¨r4Š5FB˜bJ>ã„•ù)r¶<ˆšöûí¶C¶\Þ퉃Cçæ’ƒ…ÁJ´‹Ú@YzX…©â5Š•´>„DÐ Ñ/é%Bd"ëgÂëF§–7NQ¯]Þåòn%ýCÌ™ß:‹D°0XéX\¼óœâq7}FÄüTvëmHN¹/èѯÂ&Í¡‘´SQ¹MH’æšþ17Éö1y' ƒ•íUNÁµÌB™BvAêUÙeƌТ_ˆ‰…ɲYrŠÂŒ8”hð»Ôh· Úï–+Ñ “t{­Lu^§V a«Ó‡Bk\¸ÔGz¹T“qŠöäÙ*/Q˜ “ƒ•ƒµoß>ÕÒÒ¢ÔÂ… ÕÚµkÕ;wo;;;«Ú 'q¹˜'áÝ»w3Á’­³aw§mÅtp”eGfÄDТ_|”Üå¢0嚘{ÕPÔ7˜ÏE+ÁkåÊ•jddÄ2O=Ro¿ý¶zá…oÛYpØ===ÅãîînÕVøà³šƒåÖ/ЇbjÅà¶ xÞNÇfœtBGˆ~Ñh uÊ#’‰žµà^Ý9¥ÄhdÒMV±`ÁÇË›ššÔôôtñX"]Ë–-Ël+JÛ·€aj­øÙ,uT˜9@GtD¿ÚÔÔ*…ñS‚ÄiùïäÁƒ¡&ÝD°ˆÑÑQ+ªå„ÆÆÆyK†æe¶±²©£µµÕr§¶€ò7ÉÇãƒ%3:ûGGþ9>Wø²˜ÿÉ®]ïrOOÙ×+ëûA_ÇsÌ1Çñ=êúû,†ÈÏã]ù¯ÿšwÿós}ýö²»<éçϸSo°ÕòåË]s°êêê|]–•V¹]A£Xa–ݶ‡é“ÈŒ˜È:Bô« õ$u3 ãw…aÌ¡ÄÓªG¹ÊïD°f°vìØa ¯¤u¿¬¬ä`•éè‡Ò{Êç†mÅà´ x^ø9¡?Œät :¢_(»¸¥n¢SÑøîݾãrG‡¯¶j2ù'+%KÞ$º—Css³š™™)ÉÁZºtif#XqUFó#ÕݯFè³æµ XÂÅ̈‰@tD¿ÚRò¥œ¢0bœ|+¤¢ûœIó*4æQÒV‚ –¼¯¨•¾ØÕÕeíÔw®/|!³Z+IËn^[s¥?nB˜œeB›²úàÖƒÐOEw§Õ‰‹Æ®Eê`%Ô`‰r¢“ÁÊ[¬rÛjk¹ 8Lø™1‘t„èWùeB= S®Eμ]ãZ;·®W|äåÁ¢’{¢×“T¸Ó«xi”¥Gr:Ð !úŸLxà@IÑÙ€›¥ô(˜<–Óm®—iM+Ù.úÔ©Ä}q]jŸL%°‚;3btBGôËå2áîÝ%Q˜‹SMôDw¯ô´(É–”i)çtN#‚•ƒ¥Š{5û]9¡ƒBXÃe€Fç%ºûè_8˜Âb£ÇªônO0X7X£.kߵ䰶66ǚТ_…7%ÍUd7ÛÜøåÈ»ï–~ `N’B9‡éïá¬C¹ "X90X—KÞ—Öa'áÅ2 DÉé ÷!úU—ŸìÞmý>‹Q sÙu(æÌ+¿6ÅFÏïÚUv×%9X90Xû÷'îK{açÎy¯óz{;3b"Ñ/a:Êò×D„ZŠ—·lñ\^ô*Ýã´4—„%Esg¾¼G"XyÜEè±ö]+ÎŒü˜Aa)­Ò*9–hïœâC‡j¾¤(%'ÌVq“6ƒ•Gƒuå§?MÜÖÜš›ÄD|fÄ脎臎•×Ñ©­Že9±ÖíuœvD:µ#‚•ƒu3dßÀJò¦±57h;r:Ð !úeCGYnó½i«p>ŸË «Ý bKÙr°rf°n$4y\o£†9ÌˆÑ Ñ«[|zž¹éìT—òªI·åÒÓï¿O+ok2¡ÉãCÚ`<—À<1!„Uª?×\ºl_Ý‚¹2ësUµp·Çj‹ßd} V† ÖÇ •îÙS|"ìPaFŒNèÑ/Ý:Êf,_ƒÂùLRLÌ|§ªå4oÞì{w<¬Œ,ÙΚÔ\}]v’‹@ÎDGô˧ŽW7nô—ò2gøNøŒxU+zeï¶$+GKDRgrRXTŸ•0“cÆ Ñýò«c¹úVz{š3û÷'*zeå’»ã‰`eÜ`ÉšpR¿¸zÝ› nò !„°ò<[¦ü‚¾Û;£u%‘s[’¢WN¥2Áòc–ìÛå!‚%Q¢$ç"HUÞ“‹@ÎDGô˹Ž2é>~ô¨ûŠŒ¶Û\zVë<$ M 3Ÿƒ…Áú¹ª”¥·$Ïä$ä{&½™£:¢¬¾Ž^ŸõÝæAjgE¥D¥ü¾~}é2³9X~ Ö‚ ,.Y²Dutt¨8+›:Z[[-wj (“v|ñøñâ—CþÊL$iÇçö3«\CR_ÇsÌ1ÇÕ;–Rnç·É9ƒeß~X32•<ŸŽ8àûõËÒe¹Ç˼ÁÒ1==m¬5kÖd*‚5òŸÉuvº¶`FÌŒÑýò¥£¹¯¤‚zá:¯ÊéÛ, ¶¾t™Û–‰ÙÙYÕÐÐ)ƒ%3¤ç"HRãÅÉE g¢#ú¡£™(^ÒW7dåô¨<¿kW(ƒ˜Û,÷îÝS‹-Ê”Á²w=$y&'ÅåÄñ3“cÆ ÑýÐQ(«/óŠf><ïvZ-ÅJ2è9ʮ畫–~ÙÊ•+ÕÈȈ¹sÕÞÞ®¶nÝš)ƒ•†ÚRâöoa_!„ù¥S Y4owí7jÚÜÙuóÖd³–Wm+ýÿ¾¾>ÕÒÒ¢êëëÕâŋՎ;2µ‹ð£þ~frÌˆÑ Ñ¦NG½µ™SlRj)Vt7þÑ£ó–&ý–j ’{FË4ÈL.9脎èÓ¨ãq£mŽlˆrÌתpOB‰F}ívn½3j°Æ´]Ì䘣:¢L“Žf¯A·ÍPý½=—J]® ¯]6˜ÁʰÁº¼e ?BSÉñÎN_­j$˜” 6åü›Ù^„¬Ò¤\šl?—°ÁJ™Áâ‡B!|Õ*…`š_¥´ÆÑazš¼¸}û¼„©7X·nÝR=ö˜çmfggU{{»Z»v­ež„wïÞM¥Á’„>frÌäÐ !ú¡ã«ÖŠŽºà~zbºWi¿¼]ÐÎN¸×Ûý¤Ö`ÕÕÕé…ÎÎNÕÓÓS<îîîVmmm©4XåB™ä"ÓNèˆ~0O:Jû¸úûƒåM½úUÛ8–mí$ÌL¶Oýa9ƒÕÔÔ¤¦§§‹ÇwîÜQË–-K¥Á*· •™3btBGôƒyÒQj[Ùe‚P–ãX´µ“b©Ò´zL{ÌÌ¬ÆÆÆyK†æe¶±²©£µµÕÚ!`oÔ¿µ:>³oŸõAŠ[¶¿ sÌ1Çsœ×ãÉÔÄþýï/;òovtÄúzÎÿìgêVÁ`ÙÇ™7XN×{Ý'É,¯69Ìä˜É¡:¢Ì›Žçwíªyn[;i÷sC+ù@+E«\rÈé@'tD?˜'ÅÔH $j—yƒÕÜܬfffJr°–.]š8ƒuòÀ+Q/J›frÌˆÑ ÑæIG‰݈P(´’ÚeÒ`é—uuuY;õ]„ë b$Í`IÁ4½ì¿ýRƒBam™‰2 f¹ýÿZÖÁ’ÝnÝ»uÊvQ)L6Z0^·+×/‰™3btBGôƒèH+Ó•Üí­šnÝ»uÚ[E/heÿûŒ"¹ä" :BôCÇäk‡Áª€Á:nT\÷Œb M–3c7ÊÌ0˜0“C'tD?ˆŽD°2k°†÷í+ÙË8é·•eB·ÛQH B!„ä`eÆ`I]§j³ŽyUÆöÒãsýŒÌ(פð-3fr脎èÑ‘Vê –”Å—ò æåNÍ#£XãdÞîô{ï¹æh±†N.:¡#D?t$+óKEŽÏ5x´)µÝºzÑ)§•g÷îwéÎ „™:¡#D?t$‚• ƒ%yQ²³O"QöegŒü+¯eB§æÍÆNÂKÛ¶1€!„Br°òc°ÎÎõƒU‰ÆÌÌ@Ð :¢:ÁʵÁŠ«þ„BÉÁÂ`ͬ‹Û·3a6Ñ Ñ!¬8 Öõ¶6ÖÐYO‡è„Žè‡Ž¬Ø Ö?ÿ33f#ÐýÐÁŠÓ`µ~ï{ !„’ƒ«ÁúÎw˜0脎臎Vœë‡Ï=Ç:ëéÐýÐ’ƒ«Ázúif ÌF :¡#ú¡#$‚«Ázê)„B³Ÿƒ5;;«ÚÛÛÕÚµk-C$¼{÷näÛ¦Ý`1AGtBGôƒèH+4:;;UOOOñ¸»»[µµµE¾mÚ kèèˆNèˆ~ÉÁ ¦¦&5==]<¾sçŽZ¶lYäÛÁ‚èˆNèˆ~èsÁjllœ· h^æ¶ä`A!„0·9Xuuu¾. r[;?Ë4X---ªµµÕ¢´Í±ÿç˜cŽ9æ˜cŽ9v;ÿ@  †H„ÁjnnV333%yUK—.|[€Ü¬®®.k7  ùýúõÅc} °Üm0Xª|m+Ý`­ƒeBÏÍ‚B!ôËÔ,àn:¢:¢@Çôi‡Áâ ‚ŽÐýÐ`°’  ƒ@lp«ð$éüÁŠøaÙŒ ûöí³Êñ744¨… Zå(¤˜ªÀ«D…×u=R6l°ï±ÇSo¾ùfâôó«c­³¤¥¼ï+VÌ»üûßÿ~äñ—§1gc``@-X°@?~œïnÆ`ÞÿâDÞ¾Ïqœ?*¥+a®xåÊ•jddÄúàäzûí·Õ /¼`]×ÙÙ©zzzŠ·•"«mmme¯Û¼y³µþÿâ‹/ÔÆÕ®]»R©[ûdIKyßÒ±`rr²xÙçŸn]uüåiÌÙD)P,ùîÖ~ æy,Æm°ò¨¡_ÝnS)Í0X1°K–,±fÆO>ù¤5K¶¯ïïïWÏ>û¬uÝòåËÕ7|?¾ÜGÐÔÔ¤¦§§‹—‹Ã^¶lYÙëdÆ¡Cö3Ï<“è/„|y׬Y£~ô£•Ì ä>{÷îµt–”Ò€ó¿øEæµ”÷-] ^{íµâeÛ·o·.ó3þîß¿¯ž~úéÜ9{Ö¹hÑ"õðáCõøã[ǺÎ2›­Ìñ•w£ŒÁ_|±Dÿ Ms³<õßA§ßCý:Î!ñèXMÍ0X2 'OžT‹/.^/?DöqäÈõüóÏûzl™5ˆ»x5ºöº®¾¾¾ä$"PhR –Ì vîÜY<–eœÿøÿ(ÞGf÷îݳޓü¸ûm•”f-å}ËÌQ^‡ýÞŸzê)ë?ãollÌ÷seyÌÙïOÌ»@ ”Ì\Mak,fË_y×1Ê”ï³ ýF?²>ƒÎ!Õ3Xqi†Áª Q®—Á¾Î ƒƒƒÖLÅ^ :pìËV­Ze…1%ô)Ï=<<œƒå”ƒ%Kò£­ëeÏ Ì÷iG!²®¥ýür²—0´Dôñ—ç1'¾œ ,†ôÞ“¼~·±—7£ŒA‰¶H®Š@þÞ¾}›±Áp©œÁŠS3 VŒ¬ühHN‡½|å÷wJÐÛ±c‡5»Öû,†uÒò?üá‹ |ò£øÍo~3±ÆÔÉxyii‡r³¬¥ýžäD%KT2»ºuëV¨ñ—ç1'}ìe?yrì5&õñ•g£ŒA ÿÒK/1c0œCÂëX-Í0X1ÀžIÈŽ,)H]„öó;ABèú²…ææf533S<‡-Ñžr×™*Î&“h°dÙÁ G»ÝG"Xú 2«Zš'*=§Å¾.ÈøË똻zõª£y¿|ù²kKr‰Ð1ÚÈγÞÞ޲˃y‹æùÃ)"Å9¤ò:VR3 VDHž$ d)AfpvîF˜/‡ \Ý=ëÜ ÍÛÿí½×u:&&&Ô·¾õ­bø3‰K~€åõËÉM ³d=ËÞÅ$×Ëm·nÝšy-u­>þøã’%ûº ã/¯cNf¨òºuHNŸä·ØZMMMg£°vL¡c´1(ûÄOX÷e,Î?H´½t-¯OÆ$çÊëXIÍ0X!!?$²t MfÅùP%H(‰Úa¾^Ëcaëq$´+”°¾}IªÁˆ~2¿ûÝïÌev"kÛò^ä3­°¶˲–^ZÙ×ysÏ=÷\qYˆ,yÉ÷ØÖB^¯½"µ ,€oƒÐ €Á`Ð €Á`Ð €Á`:0XŒ:0X€ä‡»wñëêê0X  åë—¿Tjûv¥ž~ú«¿rìa~„ ,PÏ?ÿ¼š˜˜H¥Áºuë–zì±Ç0X  ëúu¥V¬PjÉ’¯ÙܬԵkžægvvV;vL-[¶,uË6‰^σÁ`°á ÖæÍ¥æÊf{{Yó#&«±±±äú%…ûJtëÉ'ŸTÅûô÷÷«gŸ}ÖºnùòåêÆÅûtvvªÅ‹«††ÕÒÒR|ŽG© 6¨… Zl/¼&¹Ì~L¹Nž_î{òäIÕÕÕ¥üqõÔSO©Ó§OG2r, Þ`­^íl°V­ò4%b®zzzÔš5ko'†GŒ}ŸíÛ·«ééiëøÈ‘#Öò¢@£»»»hœ>þøãâsttt¨½{÷ZÏ%|ï½÷ ~psñ1ÅPÉýÆÇÇ-ã¦û‰¬a°,@e Ö3Ï8,¹|Îô˜¦ÄæÚµkÕ—_~éú\õõõŽFFÌ’}ÝÓO?]4W¦ñ‘H”~ÝÇKL›Ós¹c°,@u –D”œÌ•ÍÿùWS"KpÍÍÍ–Y²qûömËtÉ2¡,ÝÙ·u22a¯“H•ÓuåŽ1X  :ëÜ9oƒ52âiJdioëÖ­Åc‰FíÛ·O}ñÅ–ñòc¢-ZTbÒôë$ZeF°$· ƒÀ`’k°NRê•WÜ)×—1%?úÑ >ì+#&e$Š%†IŒ–ƒ%‰ë’‡%÷ùì³ÏÔüã’,1qv–äcé9X, <ƒ¦)yðàZµj•º{÷®•¼.»ø„²3ÐÁºÿ¾•(/9S’s588X²‹P ˜ì.ÊÿÅŠj°ô<2·r , P5ƒ…N `Ð €Á`Ð €Á`Ð €Á`:0X€XôGÀ`Tÿ‰!àlX2kIEND®B`‚XYBarRendererSample.png000066400000000000000000000402551463604235500341320ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/xy/doc-files‰PNG  IHDRXr5˜@tIDATxÚí l×™þ+­V«UµZUªªªªªj¥UUUÕjUiUUUe!,³Dˆ–«®Ba“àMHIÂRul6µ› ‰]“(Á Äë8€)”@`II€„u‚å˜ Gö&ðç#öú "C¬Ïÿ<Ÿ»sÇ÷sîõ×õï‘^ùÎÌõÜ™gÎÌüæwÎ|Æ „B¡¢ê3X€B!`!„BX!„BB!„°B!„,„ÆX'Ož4>ø ù›¿ùóñæÏÿüÏÍ_ýÕ_™²²2³xñbsøðáÒØ©?ó™œbÊäXï”ëmz!ûË™3gFÌ_ã°*1­\¹2tûä<'û©X™Ö ÀÊ¿mGÝ_¼žzê©ß­¯¯ç@„,„JI7nŒ ViÀÄ”<¸GlÛ…ì/^_ÿú×G|÷›ßü&X•’¾öµ¯%èŸ}öY388è¦Ýºu˼ýöÛîŠû+_ùJI0`åX…ì/RðöàŸýÙŸ¹à6!°*AðŠ+W®D:A…OFÕÕÕæ{ßûž«KQ}ŠêT¾üå/›þð‡æ½÷ÞË{~Ò|àê^tòÒ<ÿò/ÿÒ|ãß0µµµæÓO?-:lhYƒßýÒ—¾dþ÷ÿ×…Ö%8íG?úQäåŒÅbféÒ¥æïþîïÜwõ?_üâÝï¿þúëY—=Ÿ[€áïeúß}ûö™Y³f%¶¡þjXã³ùÚßßo~øaóÙÏ~ÖÅ’%KFüÏ¥K—LMMËÞøõþë¿þk·ÞÛ·oŸ€uñ ÞTý–Ú· €…P J'ò0D,_¾ÜìÝ»7#´ä{â‡æŸÏü¶lÙ2âä ¨ Ãðo ¾ð…/$ß¶m›ikkK§ïè»Q–³½½ÝÁg.€9–€¥6é5=Óï|õ«_ÍXgÔÕÕe>÷¹ÏÛmÚ¨€uñLúÿH!žÛ„ÀB¨„´lÙ²´'8Âw¾ó³nÝ:sûöí¼NR:Y褣ln„GÙš\ç÷þûï8¹k¾Z®L'ýBKêèèHÿ÷ÿ÷æ[ßúVÒ8}'ÊrêI´ Œ}þóŸO<}vîÜ93mÚ´‚+_ó™«à8e¢´úŸ)»–.½ÂîÙ³ÇÿðÃÍ=÷Ü3a«ý%üô €S·yšX• tÕíOä™BÀôÉ'Ÿäu’ J'èàwu;(דÚ}÷Ý—4^À–jžºåRlÀ’æÎ›ö{Ê@D]NÝV Ž饗’~W'éï~÷»cX?øÁ’Æ ¥?üáIãu»0Ý|ô?ªGJ·Íõ98Mó{u[Ž&`²¿„Ÿüíoë.BxšX•°t«J'Ìð‰/ ,Èù$¥Ú”gžyƬu»H56QOjÙn%e‚¶|O¬©¤¥²Káÿ ÞŒ²œª7 Ž¿víZ¤e/6`©Ö*8N€˜ õ½Ló ?S±¸Ïp©vÏÝD¬Bö—àíA_¿¥ÛÅdªiÊ÷äµÎ'—¾‹ò]ÎðÉ9ê²°2-W: ÍwÙtK0“_­­­£ XáßÎ4o}·Ðý%Uç¢é‚Û„ÀB¨¤+ætten2eˆÒØþáþ!i¼:fÔ£ìQA œQIUß’óN]ä Vð6P¾Ëþ¾¿¥X*¬l¿í{CO•ÍÊåvo! ¯£j¿¼þøÇ?¦]ϨûK*@OÜ&DB¥Ðȇ¯Ðï¿ÿ~W´ìOòª5 à œrɤˀdtó»÷Þ{G<É–N¯) À ÿ~0‚µBù.g.ßW½O:_U㤧ñ¢úšÎp –ºH•…ÉTƒ•müüã¤å×wår»·…×QÛñæÍ›.Â5wÁmu ßÌV¿…€…P V®áŸ–óÒ#êÁéÇwãõHÊ„ žS@ºùéL8s¤eˆ”UÑ“w¾?¡b­¯—úc Ž×Óoéž"Ìw9S}ÿСCnÚÅ‹'z¯T=€çr‹,¯éHà~ŠPÊç)Â\Æ Ð|q{øÉÅoûÛ£ÚîSµÇtñÖ[o´¿¨ý‡oú¬ Ï †3jÜ&DB“\á{ºÇÏõèyXéúJÊô({&H7?I59Ùú‹*6`¥ëK‘®Ø=ßåliiɹ,Ý:Ju+­_G«¬|ÇC€žª3ÚbkÍš5Y·“Ô(tùÅ/~‘öÉS¯p§¶Ü&DB“\ºÅ¡,‹º ÐÉÚ?í§¿ºÅ¡ÆÕ'S*)3#˜ò/ê¶Îßþíߺñê¹Ûƒƒ¦‡³"©@ Ýü¼Ô㹦k¹´|:‘é7tKEOŸårRΰòéÉ=xÒÌw9Uó#¿ÔÇ–÷_ (ƒî5]ßÓw4?ݦ ?…–¯¯™þWÛlöìÙI=¹k8—žÜ³W¦NmÎ÷v/´L=ö˜ËÞ•t‹U·+ýr(ôYËÌô²¿¨Ï· ê¨6,Í3[?qX!„BÀB!„°B!„,„B!`!„BX!„BB!„€…B!„,„B! M¡–Ç ‡ÇKê]\=GŽÐxµÍbïg©^14š¿í×/ÊïNæcÎT8®"kÜ¥×iÜsÏ=I¯ê˜>}º{·×T:¤ûÞhHŽ=jþñÿ1òtÄÉb¼Úæd¬àúE¬Tß°€…ÜËRµ“<øàƒæÓO?uïÓ»ÑüËm¬Ñ?nßzë­ÈÓ'‹ñj›£ X£½ý‚ëu°PJ}îsŸs;‰ÀÊëÃ?4_ÿúדvžƒšï~÷»îe« }ˆå'þsgg§ùö·¿í²e^Û·ow©z½\Wýë_'¦utt¸—²êûŸýìgÝ‹|ƒ¹îè~º^8«ª_‡üàæ“O>IúNø…¹ÙÆë…³z±æWVV殈ó9ÉëL/œ O׋z5¿?üánXYF kûH|ð Å÷SiË–-Î Ÿ ÕË”/]º”äÇáÇÝvÖwôòÞ?þØè¾õ­o¹qš¦uÏuûek'×®]s/pþ¾à¦ç;ß1/¼ðBÚm¬6ª—¯Zµ*iÝjkkÝø“'OæÔæ3ýn6¯R)×ý0Õ>&Ïõ}Ó~öo|ýü9]ûLÕ6sÙ¶{÷îuÛÑïËÚ·£¬C>^æÚF2­_!¿›îøÎ‹lí«µµ5kûËåØ˜ËöJÕVÂÒ ¹5­‡|ÕK¸Ãí?ÛvEÖ”ÓW¿úU·“üö·¿uààÎé¥[ˆúάY³Üt…>ûe¾€åê~¼B~þ·nÝ2±XÌíÌ~§Õ´Ù³g;ô·‡~8oÀÒÁèöíÛn>8ÓÁ8JË{ì±ÇÜ\aÌýïäºl<òˆiiiÉyú‚ Üü^zé%7¬ÛÖ @zñÅݰÂcñýTÒ¶ }öÛ3X’ÀþÊW¾2bœйn¿líÄ·×}ûö%Np:Q¦ÛNÚž:á(ü>100ྣ•~#—ö’íw3yUÈ~ÞÇþú,€ö'ç+W®dlŸ©Úf.Ûöþûïw'qyxï½÷ºqÛ¶m‹t,ÉÕË\÷ñLëåw³Òy‘­}Åãñ¬í/—cc.Û+ÜVÂzï½÷ÜrÛŽÖ)ø?¹lW`M9é '|¦+¢àç{ßû^RVC:sæLÊ“`.€¥6˜1óóÿý÷Gü¿Ÿæ³þĦ«µ|+(„5NWd…VP¾PVWª¹,[¿Ü Ù¦···»ùé Rã}6â›ßü¦›®+TM×÷ÆâûÙ¤yèsõRÙþ'¼ý²µÍ+܆³-ƒ2“§}D҉џ4sm/Ù~7“Wéö…\öÃð>¦“jª“]ºuÈÖ6sݶ~Ÿð™¢|%¹z™ë>žiý¢ün”ãƒ÷"[ûÊ6=—cc.Û+ÜVÂR=n¶¶“ËvEÖ””®ú´ƒ¯dº" `RX” ΰÂÒ0|›2<-]*>§–#8 Xa0È&Ý~Ëô AªéþÊñË_þr"+¡,šþjÚç?ÿùÄç±ø~Xº2׉AYÝRQûÈÖ²Ëe»dk'Ê8øa­ƒj/^¼˜qüU¹/‚öp™OÖ6Óïfó*¬BöÃtûXºï§j{Q¶mxŸÈwrõ2ßúÉTëåw 9>dk_Ù¦gkóQ·W”¶“ËvEÖ”–v Ýk÷·ì´SŽ`¥›¿ä¡/ÓÖD,™ËEßÕ´®¤óîë¤/^ì~KWê:.[¶ÌÿÚ×¾6¦ßO•õÑÉKIŸEmÀÊÖNt;LWþº½âÿ/—L£n]jÞº ¬vúÅ/~1¯ö’éw³yULÀÊô¿©Úqª¶eÛ†÷‰B+“—ùì»éÖ/Êïz|ÈÔ¾²MÏÖæ£l¯|Û€`¡,ð! ÞžS”ÆûÙ§•òõRZ:˜þÍå*'Ý­"çðü½T|©iáâñ\*_pJwH5>Õ<§§”z¶ßVº?X+‘Ït_'¥åÒAÔLý6ð·ÆêûÙ´cX¹´/ÝÆß:J·íŸzê©Dm‰ÏæEÍ …7—v•ê6L¦ý0Û>¦‡ ²-kº¶eÛ¾ýöÛIûD¾ë«—ùVºõ‹ò»ùÂ^dk_Ù¦gkóQ¶Wªï &õ9ÜZ¾mXS°RzºEW8Šûî»Ï÷OÊ(%­aq+í¬bK_ÐíÓÕþÐØØèN8ÚUDšË­ßÖx îtÅèÓâ*,õWŒþä ÑÁ¥Ø€å$z)¨Tãýÿ&¼ª­Ð7Õ÷ÂÒ€•ný¢ün¶ãC&/²µ¯lÓ³µù(ûb*/ü¾l;þ",Ÿ¶‰¬)'=YRQQá #}:WúMTðo…>ë`ã¥N5 Å´iÓ\ñd®;´v~(”öÖÿëÊÍK¿ãÖta°‡b–D¾ÿ¯`Z;Õxÿ¿:IÉ;ï‰Ö9Ûok~ò8²M÷uRòÂÀu@ó· ·>FûûÁuÔSF~;Ê“åË— `ek' _g¨®IB~Ý2m{ImYãu[)¨\Ú|¦ßÍæUªu̶fÚtÁ¤Û»ú?]À¨‹‘pÍ`¦¶—˶Õ4eË‚Ëçá$Ê:äêe>ûxºõ‹ò»™Ž¹x‘©}å2=S›²/¦óMóüÒ—¾”(¢èåÛ6€…PΙ¿¨ìѱèäÒš5kܶ^¹reI­—¬`_P¥Þö&âúek_¥Úþ€…PQKu êx0êt4öRÖ']àd’níÙ³ÇÝÎTFÒ÷ˤ[@S¡íMÔõËÖ¾J¥ý! ¡œäo ¡Ò–n]k;—x¨ÛÕÅøž¶uk)×þÌÐø´¯RjÀB!„°B!„€…B!`!„BX!„BB!„°B!„,„B! !„BXÙ|‰±ô“ŸüĽ™œ(<zè!|ÀS|ÅWOKÖ×ÇÀʰž|òI÷òQ¢ðˆÅbø€§øŠ¯ž–¬¯ÿú¯ÿ `Xc===ø€§øŠ¯ž–¬¯“°D•3gÎL744dš››MCCƒ$E<Ï: À"‚ ¢X1iKo—÷Tkk«Ù¾}{bXo8ojjÊ: ÀâJ O |ÅW<%È`@+¨êêjÓßߟîëë3UUUY§XÔ à)¯øŠ§5Xi«¢¢bÄ-C?.Ó4‹+-<%ð_ñ” ƒ•°ÂÃÁq™¦…ÁÊGPµµµŽd½ÙúË0à 3Ì0à 3&ƒE‹+-<%ð_ñ” ƒ•°-Zd’ê¬*++³N°¨ÀS_ñO j°ÒV[[›{:0ø¤`cccÖiWZxJà+¾â)1å3XÁn‚Ý5ÐAAôƒEOî\xНøJà),‹ VOñ•À×|¢sï^c}ôîÉD^ÏkÌhÌ}÷³`1>hÌCó“Ÿ£—ÿô§ÆüìgÆ,^lÌý÷›ž¶6<¥ À"¸ÒÂS|%ðu¬‹¶J À"‚ ¬"A €Ep¥…§øŠ¯,2X€Eýžâ+¯ãX[žx"é-#»w玲Rƒ`\iá)¾d°F°h«d°,‚ À*2`Ô`XWZxНø `‘Á"ƒ`XÔ_à)¾øJ mÀ°¸zÅS_ñ• A À"‚ ,j°¨Á°®^ñ_ |%ƒE[°,ê/ð”ÀW|¥‹  À⊀ÀS|ÅW‹ ,‹ ‚ ¨Á",‹«W<%ð_É`d°,ê/<ÅW|°h«Ô`©îܹcÖ­[gf̘afΜiÖ¯_Ÿ˜644dš››MCCC¢‘Æãq‹«W<%ð_É`d°2iÆ ¦»»Û}¾víšyá…ÌÖ­[Ýpkk«m×Ûßmoo7MMMAA A V&)k”2TóæÍsŸ«««MbZ__Ÿ©ªª°¸zÅS_ñ• A+“¦M›ænU^^îþVTT$×÷Âã,ê/ð”ÀW|¥‹ +¤+V¸Û€ªÅ@8q"Xeee#¾Ÿj\°U[[ëŒö4«¿ GöÅ>{ö,~ŒÂ°|ÅÚk¡Ã§)*`½ñÆ#~ïÊ•+´¯ t¾*9ÀÒ-Áºº:U*toll´í÷>2XA5X5XÅRgg§{rPZ´h‘HªÁª¬¬°¨¿ÀS_ñ•,‚¬\uêÔ)Û.ïw %µµµ¹'ƒO*Ã`Q§¾â+5X5XY¤ÛƒŠ%K–˜óçÏÓW¯xJà+¾’Á"È`Ñ“;A`QƒE €EpõЧøJà+,Ú*€`Q§¾â+5X5XWžâ+¾Xd°È`XAA 5X€ÅÕ+žøŠ¯d°2Xõžâ+¾X´Uj°,‚+-<ÅW‚ ,2X€EAŒ?`­ª«3o=ö˜›_oG‡‰­YcbkךØsÏ™XS“‰57›ØóÏ›Øúõ&¶aƒ‰ýæ7&¶y³‰½ø¢›Öeÿ‡mA €Å§øŠ¯V°šì9ÈüøÇùÏC¿ûÀ¦×m €Å=mOñ_,‹,‹àêOñ•ÀW‹¶ `XAÄ”¬·ŸyÆlÙ²%¯½öÛ†,‹+Oñ_¬Bk÷¿üKÒ“ˆííí´E2X÷´ <ÅW|°,j°,‚«W<ÅW_,2X€EAX5XW¯žâ+¾X¬±P__Ÿ©««3.–-[f.]ºä¦ ™ææfÓÐÐh`ñxÀ¢þO |ÅW‹ +“î· qÏž=¦fþüùnZkk«m“ÛßUãjjj°¸zÅS_ñÀ"È`eRyy¹« ¦OŸîþVWW›þþþ¤lWUU€EAX5X™´k×.³uëV³ÿ~sõêUsøða³zõj7M· ƒˆ…Çy°òTmm­KzšÕ_†£ ûÀâ Ÿ={?FaX¾âíµÐáÓGŽL8Àzã7h£x¾*9ÀT-¶ RP5×6>Õc]¿~ÝM+++ñýTãÈ`Q§¾â+,‚¬€-ZdÎ;—VÍÕãjhyd°,ê/ð”ÀW|°j°RÔ[¥'øHªÁª¬¬°‚ ‹ +“Ô-ƒnú UKK‹#©­­Í5¨àS„W¯xJà+¾X¬LR¿Vj8zšP¡Z,_ƒE?XÔ_à)¯ø `XÔ`Ñ“;Wžâ+¾X,‹ ‚ ,À°¸zÅS_ñÀ"È`XÔ_xНø `XÔ`XY<ÅW‚ €E À°‚ ‹  ÀâêOñ_ñÀ°È`XÜÓÆ<ÅW|°,j°,‹«W<ÅW_,Ú*€`A€5ej°nܸáÞ8cÆ SVV–¯—1wttX\xНøZ”8¾¿éùÝïÜïõ:dzþýßMO[›éyåÓc£çÕWM…•ž;LÏΦǞƒzvíº;\D°È` `­]»Ö•¯S§N™9sæXÜÓ&ð_ñµ(Ñ»aƒ1Ï< lìt‹˜T5XÊ\í²Wzùr°¤iÓ¦X\xНø `Xd°òÕôéÓ\IAÀºzõª)//°‚  À¢+_=`7´`åÂ… °['Ož4 .´mâq‹+Oñ_'`íÞ½;),Úꄬ£G&Õ`£«« Àâž6§øŠ¯°‚@¢°h«¾›†'NØmþ¨»%¨º+=AØÙÙI7 \xНø `Xd°&šÒeÆ$ÝŠlnn6 ‰Ç,‚ j°,brÖ`¥Ÿt0T,õ÷÷Ûv·À}nmmµmr{bšWSS€EVO |%ƒ`å]ê3ìÀ»ý‹eë[,Ô¿Ø{{öÁ*ÀR‡¦‡rŸ«««pyõõõ™ªª*‹º<%ð•,+ˆkþÿöow—%¸óç»ß2ÿüÏê=üîo/Yb̲ew—å‘GÌåúzj°&û«r.]ºäžXôª¨¨Hš®[†áqY<%ð• €`MÚ,=I¨nÔ'– géÒ¥æÃ?,*`­ZµÊ;v,)‹–*³– ¬|U[[ëHÖ›­¿ 3Ì0à OÜá·m+*`E]žÓGŽL8Àzã7"¯Ï§šw€5Ú߸–ú¼JukPOž9s¦(p¥}H - 2Xdð”ÀW2Xd°Æ7ƒõQmmRßbû÷ï'ƒU,ÀRg¢uuuæâÅ‹ ÐÑ{u;o±S¤ùwww'SWI5X•••u-xJà+5XÖÖûv|p96ØmD V‘KY£ÁÁÁ”Y'Ý2,TçÎs}l…ÕÖÖæTð)BÁXdð”ÀW2X€5é3XõõõiûŸÒ‹  Õ»1OŸ>=b<ý`AЀ`•T?Xá§ûÖ­[7büŸþô'»úè£Ä¸ .¸ÎA‹ÕÑ(€ÅÕ+žøŠ¯ÖHÀŠÿèGÆüû¿„£ló°ÓbÏ>Kk¢w4zãÆã¯_¿^”ŽF,‚ À°¦\ V:ÀRïîW¯žâ+¾X¥X-vvÿêWæÍ7ßÙ·ØæÍwûÛ²ånb/¿üý‹Ùqÿe—Ÿ V†[„º¯é¥Û…Ü"¤þ‚ÀS|ÅWkjÖû c¡*Ê<âÖj°Rèĉi‹Ü'Ò“„Y<%ð• €õ€õ–]Íã¿õÝ)X“®,=-¨2ë– BŸé¦ ‚°¬‰X“®,:åêð_ñÀ°¢Ìãª]6u3ác×®]d°$¸766š3f¸Û‚^‹¬‘õžâ+¾XVÚyüwuuÒ“ˆMMMÔ`Ik×®Mª»ò:uê”™3g€ÅÕ+§øŠ¯€5!kRe°”¹R:ohh( °¤iÓ¦XA€`MÀšT5X*j\IAÀºzõª)//°¸z%ð_ñÀ°È`嫬q‚½G€%Ø:yò¤YhÍ~\À¢þ‚ÀS|ÅW À¢+?=ztT_ö|þüy³|ùr×c¼ÂK ×ÜÜl%Xdð”ÀW2X€Uý`©³Qõ}¥[‚ª»Ò„Ï÷Ê•+¶,p±°Z[[m›ÜžÖ£Ú(A5X€E?X$“ßyç”ÓªíéïïO ÷õõ™ªª*‹¬žøJ À°&wkß¾}.s5ךùôÓO»:¬bJÀ$£õ¤¢n ŠümÀàíBË0<΃• jkkݽXo¶þ2m8ü Vö?Š?,_ñcò¶×·m°¦`=ÿüó‘Û˱C‡ÌéÿüOsñâEsúÄ Ó¹w¯9}䈹h§>zÔ\vÌ\ü¯ÿ2§ß~Ût¾þº9ýÎ;w‡íôtóSÀÚ¿ÿˆš«Y³f¹§‹%ÝnT—àéÎ;fëÖ­fÅŠ#žXôJ5Ž Y<%ð• €552X®üâ‘·ï„È`ééÁ¥K—º[sÒG}äj¯V¯^]TÀ g©|×¹f°,‚ j°,kÒ–ú¿ ßÔ³gÏ.`Í·&Xg%ÝsÏ=‰Wñ $Õ`UVVXdð”ÀW2XÖ$,m›·V¯vÉÅöí3±5kLlíZ{î9³Ðkn61Ý>\¿ÞÄl[ˆýæ7&¶y³>k£d+Ýí¸böÞ®'×­[—¨»êîî6ë­‰R[[››ü®Þ‰`Ñ·žøJ?XÖ䬷ìgóë_çi£€uàÀ¤˜P€•©*Š6X³Tä®[ƒ‚­Û·oÓY<%ð• €`°‚·*Ý2`åôäNAX€`dð_ 2XÖ$¬Oíø-[¶˜·W“°&›,êZð”ÀWj°¬Ò¬¸¦yìÖx Àâê•ÀS|ÅW À°,‚ ‚, À-ÀÊÔ¡'€ÅÕ+§øZz`ëèp·SõMt套Lì…LìÅMÌždc›6%ú'굟, ÀÊ£“ÑÑê–À¢þ‚ÀS|X׉è7¿‰tòŒ[˜°,+ Ö›o¾ `dð_, À°ŠX555tÓ@`X€ULÀÒ{ÿ–XƒôZ‹ +€§ø `¥;y^¶ãÔ?‘½Ö À°¬^™Ã-BêZ<ÅWËŸ<}455X€E7 Y<ÅWÀ°¬q¬îîn»l‹]ñ»BŸ5À"‚°, ÀŠ wß}7mýUWW€EV€ÀS|°, ÀÊWÊVé©Â>ú(1îÂ… ¦®®ÎM°¨k!ð_, À°"ô‰uãÆã¯_¿žÔ!)€EV€ÀS|°, À*°,‚  À°¬Bn*õî¥Û…ŸE˜©o­¡¡!ÓÜÜlFÄãq‹¬žø `XÖä¬'N¤¡BŸ$ÌÔÇVkk«õn{bX×iǰ¨kÁS_, À*‰nô´à£Ö4ÝTès1ºiÈXÕvƒô÷÷'õ._UU`‘ÀS_, À¢£Ñl€å¡m®5i£Ýx·nÝJÔ~¥[†áqA€`XV)[%Àª¯¯O›ÝJ5.hTPµµµîv¿¢Õ_†£ ûÀâ Ÿ={?FaX¾âGþßê$TDÀŠº<oÛ`XcXj%ÿªe©ÊËËÉ`Qׂ§¾’Á°,2XÅ’º~˜={¶û¼Èn¨¤¬ÊÊJ‹º<%ðÀ°,+“–Ø ze§Wê–aÓ¦MnZ[[›{r0øacc#€E`XÖä, gzÚ¯íÝ»×®ÿãfÚ´ifΜ9¦¥¥%é– ý`‘ÀS_, À*IÀÒ­:–€g" À¢®O |°,kÒÖéÓ§]†éÌ™3YOñÀ°,«X}Ue ‹ À°, À"È à)¾€`Mn,êZð”ÀW À°¦`ݸqÃu0cÆŒ¤Œ•Šß;::,²žâ+€`XV¾Z»vmÊ[‚§Nr]+XA€`XyJ™«]»v¹nÂ5WzºÀ"+@à)¾X€`EèhÔ÷¬«W¯&Þ`Q×Bà)¾NmÀzÞoyúi³sçNÓsèé±PÐÓÖfz^yÅô´·›žW_5=ö„سc‡éÑw::L½x×p· Àšr€õ€5N°ráÂ…D‡£'Ož4 ­Ùê…À"+@à)¾XîÜÜéäyÓÎÀ°¦`=z4m z €E`X€A'Nœ°ž=ên ªîJOvvvÒMYOñÀ°,‹~°êZð_‰‰Xƒ?lvïÞmºŸ{À°¦`gG‹ ‚ €`MÊ~°Fëa¦yæšÁ°¨kÁS_, Àšt€¥ÌÕ»ï¾;&· ƒÀ¼uèk°*++,êZð”ÀW À°J£¬±R°ÚÚÚÜ“ƒÁ§,²xJà+€`X“°tÀêîîsÀ¢,‚  À°J°®^½ênÍV7 ôäNVO‰©êëÕšß¹Ó]< râö¤·'¦øŠ&nañ•+Müç?7q{B‰ÿò—&¾f‰××»i½ˆ, ÀšÄ€¥ÞÚG³,‹º<%¦ª¯7íÉØ´µE:yÆž}À°,ŠÜ,²žâ+€`XVR7 “AA“-, ÀšÂ€¥÷±*r°È à)A+·“g§=éý¬>ô¤5€`X“°Fû]„u-xJPƒýäécË–-€`XY<Å|°,‹—=s‹ À°,‹ €E¶OñÀ°, À°¨kÁS‚, À°¦ö-B=µræÌ‹¬§ø `X€U,]¼xÑz¸À"‚°, À*–ô2æòòr‹¬§ø `X€U,¸Ú±c€E] §ø `X€Uì"÷0ÔXd<ÅW À°¬ˆ€¥ÌUMM‰ÇãA€`Xd[ð_, À°Æ °²õUŒ~°:::ì¶xÔeÄ***ÌŠ+Ì… u^ÍvgmhhH‘)c`Qׂ§5X€`MÚ Ö­[·¬oË\=éqD©›‡®®.SŠ]»v™yóæ¹i­­­Ö»í‰ïêMñê{ À"+€§ÄxûzäðaÓùÚk¦³³Ótþþ÷¦sïÞ»¡qŠ}ûîÆþýwãõ×ï†>ÛéÇ*`­­­5Mö„³sçΑˑiYì¸w°,k"Ö7\ÆIpUWWgîܹSt€›>}ºû[m7Hb|__Ÿ©ªª°‚÷ĸƒyÄ“g¯=Q °ÜÉsË–H'¾Ëõõ€`7`]¿~Ýz·ÈÁU½Ý)‹-ÁÚîÝ»ÀIºeî"<À"Û‚§Äxø `X€U©öi¡5WpõôÓO®‚Ý>\»v-1.Õ÷R• jkk]=†?àê/ÃцÃñ§ðá+W®àÇ( Ë×±ø½ÓGŽX¿ü²›ß-}oëV­rëó©æ `XS°´_ ` ®æ[7k×®µ§oß¾íŠÞË2Xd[ð”(á ÖÏ?o8`nØþxÖE{RÑrü¦X¬±,Õ@©îIpµnݺ1énÁ÷¯Û‘I5X•••A“¾k·=%NžãXþäéæ`XÖØ–RíÁÕúõëG¦ô4âÉ“']vêOú“«ÁòO&¶ÙƒŽž >EØØØ`‘mÁSbÒg°, ÀšÂ€uï½÷:¸Ú¼yó¨e«<èºjГƒ³fÍrÝ0 Ò} á)1¡}°, ÀšÐÒ“;Y<%È`X€EG£¼*‡ À°, À"ȶà)¾X€`XõBxJPƒ`X€`‘ ð” €`X€E`X€`Xd[ð” ƒ`X€`Q×Bà)5X€`XA¶¥4=íÚ¹Óô¼þºûÍ{ î±'ôžW^1=íí¦çÕWM=HõìØazô=EG‡éÙµËë²ñÀ°, À°"qd"ž<ãöD‡€`X€E O§0`‘Á°, À°¨kÁS‹, À°,‹l 1Õ2X}ö ¿{÷îD¼öÚkø `X€`q"'ˆBË}455á)€`X€Å‰€ ž–`u¿úª¹i®7oÞ47;:ÌM{2»iü7íõ¦= Þ²ËséRsÓžànÖÔ˜›uuæf}½¹¹|¹¹i°ÿY¤ €`X€EPƒE VÉ–º™0µµ‘®# À°, À"ƒ…§€`X€`5X€`XÖ¸j×®]vý7åååfÆŒ¦¡¡Áôõõ¹iCCCv_mvã¼ñxÀ"ƒ…§Väƒë‹õõfÕªU‰8xð €`XVéÖ»ºººLݹsǼüòËÖ—‡Ü´ÖÖVëÝöÄwÛÛÛÝÁÀ¢k´ãÌå§Ÿ6—/_6—÷;s¹®Î\¶;ßåŸÿÜ\¶;öe{¸¼zµ¹¼f¹üÜsæ²m——×­3—Ÿ}ÖM{ß¶Uj°&&`é¤\Ÿv[X€`M‰[„Ó§Ow«íéïïOŒWf«ªª À"ƒ56àÛƒdÔc¯=‘Á°, À°&Œº»»]VKª¨¨Hš¦,Wx€ELVÀ¢ À°, À;vÌ,´Ð×`•••øNªqA£‚ªµa݆ñ™ýe8Ú°©²¾Å¬l¿wöìÙ1[ŸbÖóÏ??®Ûçìž=E¬ãÇGZžÓGŽX€U"€¥ýºd«¥¥Åeœ‚Eìd°¨Áš¬¬7m[ܰaC"öX( ‹ €`Xd°ÆT"Gº‡µÈn¨¤¬ÊÊJ‹¬ Xþäé£=EÑ;5X€`XÖ¨©³³3m× mö £Sð)ÂÆÆF‹( À¢ À°, À5©¦*UÐ,2X€`X€EOîÔ`X#NžïØï¼¿aƒ9þüˆ¾´>µ'¢L}i²'L-ËÇêkçλ}rça/<\\«V™Ë¿úÕÝÿÓ|ìÁËÍÇÎ÷Ãá§, À°, À"ƒU2€UÈÉ3fIËrS˱uk¤ƒÚåúz À°, À° À°, À°,2X€`X€`X5X“°ºìgõ¡u}޼Ȁջt©›Çÿ³7 À°, À°È`MyÀòF7ˆ€UÌ“g1ëÝ={L··×ôZ°éµð§^ï{7m2½/½dz[ZL¯= ôÚõíݶÍô¶¶š^럶Ó, À°,‹°¬P¨Ì/ùä `X€`Xd°, À°, À°j°, À°, À°È`XS °Ô#þáuë, À°,‹°¬bVâä `X€`Xd°,kb–›‡¦E‘Á"ƒEÁ"ƒ`X€E+:`-²j`` ©«²²À¢ Àšd€•tò°, À°Æ°ÚìAGOŸ"lll°¨Á°, À°,+Ÿn‚Ý5Ð, À°, À°èɰ, À°, À"È`X€`X€`Qƒ`X€`X€E À°, À°,‹  À°, À°,‹ €`X€`X5X€`X€`Xd°, À°, À°¬)Gÿ{Ó¹¿éìì4úlaÉÅk¯Ý}û£xýõ»¡ÏvúÑáÿ°, À°, À"ƒå—U"{`Šz`ì±'; À°, À°,j°, À°, À°È`X€`X€`S°Þ³'ª#;wX€`X€`‘Á*`%Œ€`X€`XÔ`X€`X€`‘Á°, À°, Àš˜q\ý7:t·ß§pŸO™ú}²ãŽÛ¿i]Ži=ˆÜ‡Õ‘Ç, À°, À° ¸ЦM‘}Ü&R«wÃcV®Œ¼ãÆ, À°, ÀÊ ¡¡!»¯6›†††„ñxÀÀ«, À°, À°FY­­­Ö»í‰áöövÓÔÔ4a+¶f‰½öšƒ÷yíZ{î9³Ë³™ØóÏ›Øúõ&f¡!f\±Í›MìÅÝ4õµTj,MÌn3çǾ}¹{¢a;À°, À°¬1PµÝ ýýý‰á¾¾>SUU5aËmÔW^‰ÔècÏ>;!ëìr8p ÿñÿQpšU«"í8š^ ÀRÿUïé `X€`XVjUTTŒ¸e`X¾ÑûhÎ$EÉ`MÀJœ<, À°, ÀJ­²²²œÆ êqkl]]=×:ØÒßb×ÙÆ¿rÅŠ»ãlcrÃvCÖ-]jjígý]iXWk?ë¯ÖôåË‹¶ü{‰åñÿ?<¼L¿i×Á}ßn'ÍC~.ÏÅáñO /Dzáωåö+ëú ÿUûxÂ.Ãr;¶Íkû®Ðôáù¸åɶ>v9ž¯v>Ám“×öµÿ綯¾g?kÿ oßå¹n_ «½ÛùÔä»}íÿk>ˇ—£FmÖnçœý°Ó—Ùï'¶¯ýýšššÜ·ïð°ÚVpûjyo_»=ÜöÕ°ß¾v¾+üòçÒ^Ú¾òQíÄúªíû3më|ÖGÛWë¤yëxb—¥&ßýWÇ3mßáyh9"o_ Ûez2Êöµÿ¯ãÑÃm_ó¨>®äÜÞµÚ¾úlߟwžnßl~ØuOlßácQªí›q}4O¿}‡Ûˆ–#¯í;쥖GóÓ<´}5œ·ïðrü4M;óëóDàû)·ïðùÎ-¶ÍðùìqÑE !„Bh|4©kÑ¢Ef`` ©«²²’­ŠB!+ªÚÚÚÜ“ƒ^úÜØØÈVE!„€UùöƒV¸ ‚ "]LÀBGù6<„§øŠ¯OKÙW q ÀS|EøŠ§ø `!„BMlX!„BB!„€…B!`!‹ÅÌÌ™3“ÆÝ¹sǬ[·Î̘1ÃM[¿~}bš:yÕ+pÔ“¾B¯¸té’›VhwøšÚW½*UàifO3M£­ŽŽ¯´UcvíÚå^µR^^îë B<¨éÓ§»¿ÕÕÕ¦¿¿?)+SUU…¯úÊI+𧙦ÑVGÇWÚjjårŒŒ: O£ù`¡q9¸N›6mÄÉ^ )U««Õýû÷›«W¯šÃ‡›Õ«W»i¼”{t|Õ|t0QÌ;×lܸÑܺu O³xšimut|¥­Ž”²}ÊÀdkwQ§ái4ߊÙV,”óÁuÅŠ.­ªT¬ä‰'Pü/^ìNþj”ªº~ýzÚ+‚©|E[,_ƒÒÕ˜õõõxšÅÓLÓh«£ã+m5YÇŽ3 .LÔ ejwQ§áiá¾ÚV,”óÁU)à}A¡^¬}ß}÷¹i‹-2çÎK|WZr•5z¾†%_SÐð4ÙÓLÓh«£ã+mõÿÔÒÒbž|òɤbt2XÇÓb¶U å|p «³³Ó=‰!ùûÞAùq‚„Äx]aTVVâk¾†588hfÏž§Y<Í4¶::¾ÒV盛§Çe‡•©ÝE†§Åñ­¶ `¡H×S§N¹'Ü|:VÝèv–'~E!µµµ¹§4¼ôYW·øZ˜¯Á'htÐÉlÓ¦MxšÅÓLÓh«£ã+mõ.p¦ëB!S»‹: O£ùV̶ `¡”Õt}€(UªP#<þ|b¼¹úñÓU3äk…è[ht|Ý»w¯»]¨ââ9sæ8øÂÓìžfšF[_i«™ûW¢¬‰ãi1Û*€…B!TdX!„BB!„€…B!`!„B! !„BÀB!„°B!„€…B!`!„BX!4±´gÏ÷ºŒùóç'×ë1æÍ›ç¦íÞ½£BB壇zÈ”Þ+æµÿ~7NÓBÀB¡<‹ÅÜËZçÎknß¾mîܹc*++Ý8Mó:zô¨yàÌôéÓÍŒ3ÌÚµkÍ7Ó8à^üªé3gÎt/…íïïwÓü‹eOŸ>m-Z侃BB¨¤µyóf@ííífûöíîó¦M›ÓOœ8áÆ=ùä“îöá®]»Üpcccâ;‚+™¦wtt¸é,A›ÿŒBB¨¤¥ÌUUU•™5k–ùþ÷¿ï>ߺu+1ýÑGuPtáÂ7,ˆÒ°2Y©¤é‚)Ÿ©òPuæÌ7 !„,„ДPWWW„”± ª¼¼<1-Òàà ©¯¯7+V¬p€VQQ‘4¬BÀBMY¥!k/]öIp¥é/¿ü²ùãÿèê¸,„€…B@H…éòäÉ”ÿ§[áÿ°BBe¡ÎÎN7~áÂ…æêÕ«nÜ©S§xIêG+`‡°BBe!Õhé©@Õcé–áœ9sÜÓ‚Ò¹sç|ùÂö7X! !„BÀB!„°B!„€…B!`!„BX!„B „BX!„BB!„€…B!„,„B! !4q Ÿù ‘g „€…Ê X¿BB`À/„€…ð !`!„„_! !T`øäcN,„€…Š Û·3wîÈÐx`!„,„ÐèVYYYÊk…s´—ÀBX¡Q¬‰ ±^ !`!„ưjjjLOOûÜÝÝ톽¶nÝjêëëÍÓO?mžzê)ÇÝø;wî˜uëÖ™3f˜ŠŠ SWW—öwü¸TÙ3ÿ78?Ess³ç¿sðàA3þ|3}út³páBsîÜ97íwÞ±«<×”——»å°BBhBÖÅ‹Í<à>믆¥ÖÖV³eË–Ä÷Ž=jV¯^í>·´´˜7& (ÓïÇ¥»E¨yíØ±Ã ¹Ø½{·Ù°aCâ;›7o6ýýýnøÐ¡C‰å5k–¹pá‚ûŸŽŽ !`!„ư2Õ` füñÔH•••fpp01,ˆ™9s¦û|ï½÷šÛ·oçr¹–æ„5Í{Μ9)ÿGË1mÚ´Äÿ½ûù…BB¨Ø€•IW¯^uÐÒ××—”¥›_TÀJõº˜mž§N2Ë–-3ßÿþ÷Íþýû,„€…š8€¥[tK–,q·â¼”R¶(•”]J5Íg–ò,Í/œÁ4åmU `!„,„Є¬K—.™ ¸Ï* ×°¤÷öööøÄb±D –n%¾ñÆ#æU]]íj¤$eÃlÁßö5Sáåàé·| –걂5XéÖG5b7nÜp˦bw !`!„ ¬7ß4æ©§F†Æ§’t·ûôÔ ž”Nž<™ô¡ ÝU‹¥Ûu<òHŽnݺåžôSÖHOðù§õtŸ Еɚ7ožÙ·o_ é6žR0J‚8ÍOóR賯ñÊXÏ<óŒû¾²mǰBB¨@ÀBø…°B~!„,„À€_! !0 üBX!€¿BBh¼È/BÀB!„EýÏ3ÿ/x™>IEND®B`‚XYBlockRendererSample.png000066400000000000000000001011251463604235500344520ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/xy/doc-files‰PNG  IHDRXr5˜€IDATxÚì½ °Våuÿß™þ:N§¿Ng:¶Óét:Óv5Æh4Þê%“6&5jD£AQÁ ¹¨¡¢@4(^¸©((7E¼jP‹x… â¨õŠ&6=?¿çÿ÷ùìÅyöÞ¼‡ï3óÄs8ï»÷³×³Ög~µ~¯ÍÃÃÃÃÃÃÃã¥ã÷<,–‡‡‡‡‡‡‡‡ËÃÃÃÃÃÃÃÃÀåá±[-Üßû½DÝå;wÆyyxxx°<<:yüñI€ÿÒ—¾Ôö»ßýî‹ßÿæ7¿iû§ú§äßüõ_ÿuòß{ì±Gásõ9ÍÿæØc ¢¡?üÃ?lûÓ?ýÓ¶üàm‹/Þ¥ë…^hëÓ§OÛ?üÃ?´Ÿ×üÁ´ýÉŸüIÛqÇ×vã7¶Ïé®X]³=z´ 6¬íÍ7ßìvëãßþíß À,êãõ×_o’ÍADÁ²1.½ôÒäwú·K–,iûýßÿýäç·Þzë3uêÔäwú·/½ôR‡€EÝqÇ»$`ýâ¿(Ì õ—ù—]°::–2×L9}úôn±.‹cÆŒñF–‡GýqýõמLˆô$Fÿ¿ùw×]w]ûßôíÛ7ù¹žÖ4ž|ñ‰Wÿþý+jIŸ±«Ö5×\Súüº#`5´«CV™ëæááaÀòð(5ößÿ$üó?ÿsÛᇞüLÿ¦1Þ~ûíö'Í¿×ë/>½ÒÓš÷߿à .0ãßéµÚ®X›6m*À¨æoéÒ¥íç÷ᇶØ_üÅ_t ÀjŒµk×¶¿þmþÝŸýÙŸµÛ‡ËÃÀåá±Ûçž{®ÃW[úþMó1bDòoþæoþ¦ðôªñÄ«j¯òë¾ûîkò¾F¾“þ[?ÆÆÛ.¸à‚¶½÷Þ»íþèÚÿF ¨|´x û‡rHòó3Î8£mÈ!ÉÏôÙŸ}öYá»õêiß}÷ÝæwLÎ<ó̶?þã?n×yçWø }Ÿ¸q¾;ͽŽ_@—ßå—_Þöw÷wí×µ Tä@ƒsÒüšYcÍš5m?ùÉOÚþöoÿ¶ý˜5ïÊÝÓ¼ >·u¬ÊWÓgýýßÿ}ûßéIé„ ¾s½’•­èwú7‚XŽªöaÀòð0`yx´tš5hР¿@(¨mëoöÚk¯ÒOI4›>iÒ¤R7pàÀ¢~ß<”ÛÅ'MU^ß)Øó©žæá ƒªõšŒß!èáÏ+¹WtsçÎíð߆ZX‚~GcÜ|óÍ»@ëÝwß ¿G¿‹þFp/P~§'©uíc{žbzx°<<<ÂñÉ'Ÿ„À¤ ¯ßECÀ²­àõÈ#Tz:ÐÐe—]V*Ð1¨ëÉžjè›Þx*µzõê$ÐÿùŸÿùǸ~ýú¶#<²CÀzòÉ'“ÿÖë¾Í›7·ÿ[=‘á“—:€µ-iWA”ÎUߣ7>=«òUŸ.F ¡ãhþ½æCcåÊ• Ñq;6*¹^¹ùªj,–‡G§(Q[Iðo|ã…¿ùîw¿[(¤N8!º(Ðq ½JC¯2£úÌæŸ7^55?‘k~òÂïÔë­æÿ^°`ÁÿV¯žêb~‡ÎAe*:ÊGã4TÉ_S¾[ók¹VÿMãN<ñÄÁRÞKGOd–/_^ø›FY†²KpsÎ9ç”Þ}¨Á$ûÆ.Foý; –£hN¾¯ ƒ|eÚª'XÑñ3°+K9T=]äw×£V?Áj̹ÞËuȶõ=Íɶ~WÕ> X,N嫨VUP¨úïYÍ¿Ó+¼Žþ®£§FQð®ú”©# P’yó`>ж¨ËÎǶ~®$v‚b.{G–^³5ÿ^¯[5r5ÁªäzUý]Uû0`yx°<êì"luÿøã·øZñ„‚ÿ>÷”)šƒm%²k·_óïöÙgŸp¡v *!¾.`±œÆÅ_\˜·XLœ¿êª«Â9æ¤Çà'X,.9øF5œX« Z X[¶li;ùä“ ¯&;ú;æØ¨€† ¤F96jU³­êóÑxò}'“É•“ÕÈãw6æP‰ñúwÚÝÖh3T°¶õT¦€E€,óªƒÅyUÉ‹FŽW™9 j®Z XUíÀåáaÀòðhéP­*>9R•Ä’ÛJx¯ XI5Ž:ú|¾–Òî0míÓë5îl$ªk7 ú v-zúÄœ¢æW§,á°=;÷¶õs~¿‡Iñu‹=&Ÿxâ‰Z×mÞ¼y_ü]4çÚyª'Yz’¤]œR ­¬ªöQõ\=<< XÛêEÈW)¹^„QSßV–ž|”ùüªuŽT_k{ê`±¸ª>K%££ZbÍe/꟢µ2k[sYöº þXƒKã¶ÛnËÎygVû0`yx°<í½÷Þ‰öÚk¯D{î¹gA{ì±G¢/ùË•ÅÏ ¢ïýÊW¾’ˆÇþÕ¯~µ ¯}ík‰8'_ÿú× â<xà‰:è ‚9äD‡zh"^?éðÃOtÄG$:òÈ#}ó›ß,ˆvB}ôÑ¢Ü÷Jû,Ñ'Ÿ|’èƒ>(è׿þu¢W_}5Ѻuë ZµjU¢'žx"Ñ‚ š3gN¢©S§&š0aB¢k®¹¦ #F$"`8° ¾}û&:óÌ3ÑéH?þñýèG?J9³ÿ÷Oôƒü ²ø?üáýÇüGA'žxb¢“O>9Q¯^½ úÉO~’ˆN¶OŸ>É!6ëüóÏO9ÞŸýìg‰èx/¾øâ‚.½ôÒDÿùŸÿ™høðá‰.¿üò‚h'#GŽL4jÔ¨N¿‡Ç!ñXy><_·Ä9ã¼rÞ%^^?^_‰6@;¡I´5Ú#íU¢MÓî;cmI\Ã\çô}ý ýDŸD¿Ù }ý#ý'ý«DL?M?.Ñ×30^HŒ)Œ;ŒK’ Ë€eÀ2`° X,–Ë€eÀ2`° X,–Ë€eÀ2`y°j h¨ÞÿýD[¶l)hÓ¦M‰Ö®]›håÊ•-^¼8ÑüùóÝu×]ÝvÛm‰n¼ñÆDcÆŒIÄÀ# 2$Ñ…^˜èì³Ï.èŒ3ÎHÄPÇá·ÊÁósO8á„D<®SN9¥ :üŸþô§‰"Xb¸à‚ E Ê Î A0®¸âŠD’«®ºª «¯¾:Ñ/ùËD×^{m¢qãÆtà 7$¢­ÝtÓM?¾²øü‡Äcåùð|9çŒóÊy—xmxýx}%Úí$‚ Úí1º¡¡MÓî¹.¸nZaunhèOèo$ú$ú-ú5‰¾þ‘¶Fÿ*ÑÓOÓKôõŒŒc ãã’$ûꮀµqãÆ¶cŽ9¦Ã£"ZßÍ7QÿýßÿmÀ2`° X,–Ë€eÀâhÞŒÒÑÐõ™>}úÿ}Çw´¯m–Ë€eÀ2`° X,V ÕÑ]¼ýöÛ_ü·^µÊ¾ X]dÈ@s9V9˜Š€jÅŠ‰-ZTЃ>˜hÖ¬Y‰n½õÖ‚®¿þúDW^ye¢aÆ%ŠJJpËðYg•(Ê aEg9ëªð$q5M.W*ÚVÍÜš–èÌ™ãÁ-ƒøèÑ£ $ÂdâĉÝ|ó͉¦L™’èöÛoO4mÚ´‚î¼óÎDºSlÖŒ3²š9sf¢2ÃïáqHúGúOúW‰>˜~š~\¢¯g<`¼ˆ ‹q'ÊýÕ9í΀գGÂ+CþÌ€eÀ2`° X,–Ë€U°¢ßçþ&sçÎ-<™Œ¤kkÀ2`° X,–Ë€å'X%ƼyóÂù¥þë¿þË€UeÈ@s%r0U¨xà‚|®»îº‚è¼é¬så$:âSO=5Qäx™CѹQ¾F™ü):^nùæ¶yæÀHtÄÌ­¹ì²Ë âµ`~ó‚$æ|Lš4)QÕ„ ÈìÙ³ âVó{ï½7Ñý÷ߟ( ¼{ãvõ‡z¨SÄïÙÖd³x><ßhû=çŒóA'¯ ¯¯¯D DPF[£=F7N´iÚ=×Et#•ËãŠò©:#·2úú$ú­èÆ)Wêþ3ºôÁôÓôã}}¸"èb܉J;¨TÈî Xëwß}7ÉÁ’ý° X,–Ë€eÀ2`°j¾Tí2ílÞE¨5eÀ2`° X,–Ë€eÀ ŠŠ«Uu° X4d ¹*ì9˜*TQ®Éäɓѳ¢µDG;`À€DÌˈ¶;Ó‰vV…õªŸ£ªÏe«2pûö AƒEU½¹Ÿ[øÇŽ[)¯g´å›¹D òJ¶¤hKGy¤ Ç{,·‘³õ“O>YÐÒ¥K-[¶,Ñòå˳zúé§•ù~Câ±ò|x¾œ‰sÆynŠxmxý¢|1Úí$‚2Úí1*!A›¦Ýs]DUçs è£Ü¨œÿhUÞVÎD7_ô}ôôŸô¯}0ý4¯§D_Ÿ®2yZŒK’ò̺ó¬5 X,–Ë€eÀ2`° X,–Ë€eÀ2`° X,Ön XÚò¼yóæDlÔɪ¼ÑâÈU´éDÙ¨4j⛫º~úé§'Šbn+v«ò©rå˜C!±b5b”?•("n­—˜gÁ¦°Qn)+>3Hb.ƒ|d[K–,ID‰€õêÕ‰Ö¬Y“è¹çžKô /DÏuñÒK/uŠø=QÎ#•çÃóå|Hœ3Î+ç]âµáõ‹òÅh´“Êhk´Ç(?“6]À$®-®?®O)—ÕVé Ÿ}}ýc®:¼DL?Ý|U®ºw¸.$]–Ë€eÀ2`° X,–Ë€eÀ2`° X,–Ë€eÀ2`° X,–Ë€eÀ2`°ºÂÒÁ¯Zµ*f£šA¬ƒ’ƒ©¨†š(Zì}úôIÄ:6'Ÿ|r¢V9·V$¬çZ]Dí.~ö³Ÿ%Š’PsÅv'Qk&"Ó!ÞsÏ=Ñli7ÒSO=•ˆAþ™gž)èùçŸODØxùå— úÕç³Y¯¼òJ"&Ëò&Czýõ×½ñÆY½ù曕•ûL‡Äcåùð|9çŒóÊy—xmxýx}%Úí$ò'´5Úc´‚6M»Ï˜ÄµÅõµ£ÉµÚÚQ‰ñenòèy¬ô¯}0ý4ýx]ŒQí,Úí†qIÒF–Ë€eÀ2`° X,–Ë€eÀ2`° X,–Ë€eÀ2`ÕjA§Éš:Ì©`7u‰íX+%Z„9 Š{¨r-mê֠ɵ°éÕ«WAÌÕ`ÍÕ £† ’ˆ-C]ï›Å¹f ¹ýöÛEÎy2¬‰ÕQb-&Ö|bÄÜ!ù7Dx lDÐÂÚnï¼óN"5Hm–ÚKPï½÷^¢÷ß?Q‡TGü‡Äcåùð|£6#œ3ÎkTwˆ×†×/Êe£ ÐN¢d´5Úc”ßG›¦Ýs]D­œ¸¶¸þ¸>%®a®ó(o‹¾¢LKžVÔâãgæ€+‚®pEÐÅyf¼Sw—$Õ&3`° X,–Ë€eÀ2`° X,–Ë€eÀ2`° XÝ}h'}H¤nX"ÍVvÓÖ¢s£3ã¶êhË>·L³½B™’ \ÈlS!q ôŽpB‘#âq T©~ýú%b>GÔ²†ó¦kÛ¬›nº© )S¦$b¾Jž$n‘fk–(ÿù9Ìå‹`‰¹C90’¸È ¤|ôÑG‰>þøãDŸ|òI¢O?ý4«ßüæ7;EeŽçÃóå|Hœ3Îkä`så²Ñh'Q®mö•óÈA××ĵÅõµïâæ:§è+èOèoÊÜ<¶Â×Ñ¿J<¶pE¥8gQ™ ÆÆÈoéø XÛ?”çøÚk¯eÕ­KŽFµZšKÉÓ§Oÿâ¿ï¸ã޶«¯¾Ú€eÀ2`° X,–Ë€•z<¯…¹uëÖ°ÔÀóí·ßþâ¿åô´P X,–Ë€eÀ2`° X ½òÓö_98fÀêÑ£GòoõÊ?ëhhË0·MÏ™3'·C_ýõ5*·;×)¹-öV8™ÜVæh[5+ óØ£ ëƒNÄ|Ž«®ºª n-gžBTõš[Ú™»°páÂD V«|3fýúõmÚ´)Ë D°ÄÜ¡Iº©hVðùì³Ï:ÔÿüÏÿ$úÝï~×iýUU+¾—ç“;)rœw)iQ.m€vB;’hk´Ç¨2=mšvÏu•áÚâúãú”¸†¹Îé$ú úú›¨+E®üL™4en.éƒë”v`<`¼Sw—$•´0`°¶khnذá‹ÿn¬æÿßÑÏ X,–Ë€eÀ2`° X¦H~‚eÀ2`° X,–Ë€ÕBàjŒæW‡,-”²C hîܹ‰¦NšèÆoL• `€±©¨TµäB Šœ«(ŸrÊ)‰”×F±é+·fG9f¬}Í5×$?~|At*³gÏN5I}ôÑG1§Žù,ÑVz=!m·çG¥X €å"Xb®P0ÊÁP¨±ò ÇyÀ××W¢ ÐNhGmöH{•hÓ´{® ®‰k‹ë/ª Ï5ÌuUŒ§¯ ?¡¿‘è“è·¢nÛ \uK;Ð×3Dù§Œ)Œ;ŒKÒqÇgÀ2`u`ÉÈ´s°yá˜1c X,–eÀ2`° X¬º€µ½u° X,–eÀ2`° X¬ïÿû…¼ƒ &$Ò±f 6¬ &¢sˆr :£ {¦" ÊU\—x~tQN«Oš4)‘ž6RÌ y衇=þøã±2ösÏ=—ˆÕ¶£ Ýo½õV"nÏJ!°sx~ûÛßÔU`©yN;J;*¬³ Œ6@;¡I´5Ú#íU¢MÓî¹.¸n$®-®¿(o‹k˜ë<ªO_AB#å*ÄÓ¯EÐÕŠF÷9à’èë¢ócLaÜa\’¾óï° X,–Ë€eÀ2`° X,–Ë€eÀ2`° X,–Ë€eÀ2`° X,Ö® XUòªµ¾ÆŽÛÖ³g϶cŽ9¦½lˆ« ï~÷»m“'ONÄ$ÍáÇ'ºè¢‹ êÛ·o"&dFµ¥XÇ¥5­r0­.¤Ÿÿü牘Õǹå–[±UÇý÷ß_“nŸzê©DÏ<óLAëÖ­KôÊ+¯$bÝ¡(©8OejMí¨ _'›A¾L;š(a»YQ¨V(÷½QkžÏ·+mà÷”©Ñ•0‰6M»çºàº‘¸¶¸þ¢Äx®a®sú‰¾‚þ„þF¢O¢ß¢_‹ «3€‹~<ª%ÈxÀc—Sw—$Ew¬*ý…o¸á†¶+V´ÿmQË!Ù—Ë€eÀ2`° X,–«iTé/¬§VÍCOºêôg4`° X,–Ë€eÀêÖ€U¥;Ë‘GÙþûæqôÑG°ºÊ³«ùˆ# 2$ÑyçWPïÞ½qaG‹°Îâ® T‘ÓÉU”cÆvz4Û¬)S¦Ä:óæÍK´hÑ¢‚rùTQý.¶&yï½÷E9/ Ø9xjUnEO <¿?ü0ƒ3çHbËæEŠà5§ÜgFígx¬<žotÍsж3sèr1ÏsÄu®­2y[\Ã\çô}ýIÔF‹>)\tU®2~9úúz~/ã…ʸø$uÔQݰªôÖu×ëD­E­›eË–Õ,Õ¿|ýõ׳2`° X,–Ë€eÀêöO°ä †ÚUzš§Í'žxbåïœ9sfÛ‹/¾˜•Ë€eÀ2`° X,Ö. XÛÓ_X ´Ñ€ÕEÆ·¾õ­Âûv>ggtuê©§&jÅÂÊ4än¦" ÒvØf=º ¶m`»Œ»ï¾»  $âð•+WDƒfÞHÔ²†™Á•4 œ­”­€¥('‰çÃV+”(I´Y[¶lIÄ9Œ‡oÞ¼9ËlÚ´)+^¿2ÃïáqH‰~+Ê­ \ÑpgäÆ2^HŒ)Œ;ŒKÒa‡Öí+×_x[¯ Ÿ}öÙöy° X,–Ë€eÀ2`°ðJ°£:X,½”ôÔOO]ë –Ë€eÀ2`° X¬n X;c°:iqÄ……Ê »ìŒuOWífüñ‰ZQ1XÊmÿeµãÈé0—!SÒí·ßžhΜ9‰XñYZ²dI¢Õ«W'z饗 bpe Œr…r%àZÐrðåF}ôÑG‰ÊÀç€Ûï#Ù¸qc"æÖpÞ×®][Ð /¼ˆù8z$ŸÓš5k•ù~Câ±ò|x¾œ‰sÆyå¼K9(ãõ•r€_¦z}›ågæJ?Dy[<ÿ¨ çžëœ~@¢¯ ?¡¿‘rÀåŽÒ÷Ñ?–)§“ë°QÆ·30^HŒ)Œ;Qõ÷ƒ>Ø€eÀ2`° X,–Ë€eÀ2`° X,–Ë€eÀ2`° XõÆ¡‡Ú6xðàDuª²×Y„;¢‰hT™ÛŸs0ÕÃ?œHÛc)W6Ÿ¶‰3O†Ûï#h錊ê9x*“ã•àVyõ(€ýêsç×,Vß~þùç âܯZµ*·Ú/]º´ ^Ï'žx"QÔx{ñâÅ•ÅÏà÷D¶Åcåùð|9çŒóÊy—xmxýx}%Ú@.G° „uVy×_”ËÆ5ÌuÍ=¯'ý ýM]ô[Qiú>úGúOúW‰>8çë#ߊ›ç(Ÿö€0`° X,–Ë€eÀ2`° X,–Ë€eÀ2`° X,V½qà¶]pÁ‰Î<óÌD½zõJÔª­¼|/¯ÿÍ:í´Ó êÓ§O¢þýû'jÔ iˆ%($VMæéȹѲ läDׯ_Ÿˆ[ë£ªÞ >¹ ëR+ò©r&Ê­ÉÁSTú€y@Ì_‰`‰9-Ë—/O!† zy=çÏŸ_Ѓ>˜ˆM}ï½÷Þ¬Ôã«Yeþ†ßÃãx¬<žovœ3Î+ç]âµáõ‹r½h9“ry\Ñ:hÅE®b|Tº‚k˜çK? ÑWПðzFÐE¿E¿&Ñ÷Ñ?ÒÒ¿JôÁôÓôãernË”àaÜa\’öÛo?–Ë€eÀ2`° X,–Ë€eÀ2`° X,–Ë€eÀ2`° X,–Ë€eÀ2`°JY³fµ×,Q¿¡=z´×3Q‚©F®oQnì¿ÿþ…DÇÜ‚jUÇu¶O(³ ƒ_|q¢Q£F%7n\Aìd϶QM«\{äDéhYC(‚–Ψa•«Õb}£(y9OLš–XóiÅŠ‰"XZ¸pa"ž@-¼Ær(ͺóÎ; š6mZ"&3ÀI´­[o½5/ñ3ø=<‰ÇÊóáùF Ç9ã¼rÞ%^^¿¨®m `„Ñ£V;kíp sGç—®hÐ'ñG¶EßGÿHÿIÿZ憛~}úÿV·¯¾új–Ë€eÀ2`° X¬ìSp÷k¤ÝæáQGÕþ¿Ú¾ª­Â!g¤-¯,–Ë€eÀ2`° X¹1uêÔBŠ@¤nXZäZLze¨¡W†ÍCN?ëhì»ï¾…ëUk\•©{åmñ{xýúõ+ˆ5»®¸âŠD×^{m¢[n¹¥ Ñz³Øé>jmQ5¿ª PEκNP¨šOå‘ðXß|óÍD¯¼òJA œœ#Öf’XãI ¶YóæÍ+ˆyLzeÞ,=µ¥h&Ožœh„ ‰¢öH N´­±cǤ×õUÅÏà÷D7 ‰~‹~M¢ï£mÑÒ¿JôÁ¹x!ÕÉÑÍÕÊŠ¾gï½÷6`°Z3?üðv)ÏJwqŸEÿ΀eÀ2`° X,–Ë€UrÈ1ÈùéUžŸ`° X,–Ë€eÀ2`µphG¡ÆYgÕ¾`›s°dœe‡ÞaŸzꩉZñ>½N÷tvzg'xé²Ë.KtÍ5×$š4iR¢3fÄ ÁV%Ñöt&ûå`ª PÕqðÑß°­ÈÖ­[•ɧڼys":üçž{® æß0°>òÈ#=ðÀ‰˜GåB &Nœ˜(ÚžNh3fL"n_9rdA—_~y"ÚžvîRÆ «,~¿‡Ç!ñXy><ß9gœ×Êxmxýx}%Úí„v$ÑÖh´W)—·ÅuµªSê“®2yZQ‚1}ýVtsBßGÿHÿIÛ“èƒé§éÇ%úúªe¢œ¬(×k¯½ö2`°¶o¨>‰”³j±È‘é΢11ºûlÞE(‡jÀ2`° X,–Ë€eÀê`h¡¨Tƒv{ì±íe´k¦ñJp{ê`° X,–Ë€eÀ2`ùa‹‡Þa³[zg,Bœ¤×›ÍÊUe—Ø-~üøñ‰˜WªØÒ¢E‹­\¹2«SK¯½öZ"Vo.Sr¡3œwTbU±ßzë­DQ>ÕÚµkÑ™³Ò´ÄjáÜö×]wÄësóÍ7'Šr¡´#FÄ A;:th¢AƒÄÀ2`À€D´Wé /¬,~¿'ºÑà±ò|x¾Qàäœq^#(ãµáõ‹òáh´Ú‘D[£=Ò^%Ú4ížë¢L©‡ÎZ³ôô'ô7}ýýšDßÇkCÿIrÕßéÇ¥ª7íenÜ£Šñ{î¹§Ë€eÀ2`° X,–Ë€eÀ2`° X,–Ë€eÀ2`°ê ½Ã®º¥6*ÓÛRË­½’òÊš5dÈDQ^ ·«³ ö=÷Ü“(rÞÜ6Î~KêóH©Z~³¸%¼³J.ÔÉçxã7ýêsçѬ¨¡2焹%\â\3?'*‘ÁÍVš–—\rI¢Ž)çŸ~¢¾}û&b7‰MoD¢N½{÷®,~¿'jÀËcåùð|9çŒóA¯ ¯_È´Ú íH¢­Ñ£ ´iÚ=×ÅŽÊ›,SÚþ„þF¢O¢ßŠæ„¾óLÿ•¡¦Ÿ¦—ª–þ‰Êÿ”‰;{챇Ë€eÀ2`° X,–Ë€eÀ2`° X,–Ë€eÀÚ¡£Jeí–UJ€Š‹Kze«]¨¬.2ô»jÎUôþ<ט3 `¹ QÕkæ|°r1¢F¹CÜÎÜ-[¶ôÁ$R=²fEN´NÉ…VT…Þ°aC"6ß]¶lYA=öX"nµg•o‰Îù¦›nJ]?h–(`.‘4pàÀD„n—)tø§Ÿ~z¢¨‘,›ÚžrÊ)‰¢ÊÒóËü ¿‡Ç!ñXy><ß(O†sÆyå¼K¼6¼~ Óh'QÓlÚí‘ö*Ѧi÷\­ê¾P'O‹¾‚þ„þF¢O¢ßŠJ©Ð÷Ñ?ÒÒ¿J¼~¹2}=í•ñBªSèË_þr·,ÙÿôéÓ¿øoåÊ©4S4”ß6gΜ/ìJkE>Ä€eÀ2`° X,–Ë€Õ4Î8ãŒö'–ÍO©ô„/êè"{jª—iÀ2`° X,–Ë€eÀjUú 7úMj‡¨vʪ¯^ã° X,–Ë€eÀ2`°šÆá‡^êg‚*ͯ Jó¡5)[©:ôJ2²Ê€Uq(I°jr!“â%&ò^pÁ1©–í>èˆ%&ÈÒñ²}Ä3ÏIôÙgŸ%ê¬ú89˜’ØFdÍš5‰èd£6÷Þ{o"ÎóäÉ“ b2,ëçDIÒ ÐL¼îׯ_ALð&<_·Ä9ã¼FÉõ¼6¼~ Óh'Qb5möH{•hÓ´{® ®©*pµªÞý ýDŸD¿E¿&Ñ÷Ñ?ÒF­ªèƒé§éÇ%úzƃÈ«n¶ò¬¶ö%Ùm3(i]VZc‚§22`° X,–Ë€eÀÚåKÐ$[kÎÁÒùG#Ê·ª“ƒeÀ2`° X,–Ë€Õ­KùPÚ9ؼ‹P Û£×…*ˠׄ']jà=xð`VW2Ъ9Wì”.±›:ó;¢ZEW\qE¢k¯½6Ñ”)S ÊÕ¹b˜uëÖÄt¢Q VÕoûÛDÑ÷ä€* O>ùd¢‡~8QTwhÚ´i‰&L˜(ʧb«¶Ôˆr3X/‡\;h(¶ÝÈÁ’DgMˆ¡MG5vÊ8ø¡2ÇÆóáùF–ƒ²¨½¯ ¯_T‰6@;‰ZÑÖh´W‰6M»çºàº‰ +\‘¿à:¯S;‹þFÊùЍ½}_Õ:Y}0ý4ý¸D_ÏxÀx!1¦”ÉÉꎀ•«ƒÕ Xú¹~¯Ý„’r±êä`° X,–Ë€eÀ2`ukÀÚÀeÀ2`° X,–Ë€eÀÚu+—s½?g» /¼0ª4zôèD'NL4cÆŒ‚æÏŸŸˆN3W‚ARÒ`³Ê´½iE^ÅÖ­[éñ.ÅvÌ©ˆ‚ç€[±ï¾ûîD·ß~{A̳Ð;ÿf >¼ ¶Ìà5Js0Ï'Oór°Ó΂£®¢:På¥å ,j‰E ÐŽ$Úí1ÊϤMÓî¹.¸n"èâúãú”¸†¹Î#XjE ú-ú5)WÊçOÿ*ÑÓOÓKôõ¼æQ›©ª9YîEhÀ2`° X,–Ë€eÀ2`° X,–Ë€eÀ2`° X-¬\•öèýy.ç*zOÏ­ÙtÑVì… &ZµjU¢—_~9Qäs%êTeþ†Ÿûþûï'ÒöZêWŸ/ôf±uT™ž¹& ,¬’}ã7Ä­ó¬ÐåÐ1׆•Â#;aÕòcgd4'9‹*ÓÓh'QÞmöH{•hÓ´{® ®‰k‹ëëSâæ:J.Tõ/‘É•qèûèé?é_%ú`úé¨ÌFÕœ¬(/+Wé]2`° X,–Ë€eÀ2`° X,–Ë€eÀ2`° X¬nXj©ªÉ*$Ö³gÏv£÷ë¹¢du‹A!W¥=ªÔžË·Šr®fΜ™hÁ‚-]º4Ñ /¼èÕW_MôÎ;ï”ÛVݪªì¹‰(?ìùçŸOĶQvn«¦ÌÁ”4lذDÜZß·oß‚hÌljÊyжê4x-£ñ8¢|£\ƒ2›ë¨L kä¢àÔóÌãh´“ÈŸÐÖh´W)\\QI®-®?®O©jŽg”çY'dzLÙú>úGúOúW‰>˜~š~<ÊËb<ˆ:{䪽G¶µçž{° XÛ7tG·|ùò/îbT‹D¨¡;35xl •Õ¿úê« X,–Ë€eÀ2`° XUG£¡£Šý½ýöÛ_ü\ Z†gÀ2`° X,–Ë€eÀª0V¬XÑþTK£GÉï´ðø³Ž†Þaçr®Ø6Ú~ÑE%â;øë®»® æHð]ÿ¢E‹ Z½zu"n£f>„ú2QŸ~úi¢V”`(S•}óæÍ‰Ö®][+-?öØc‰¢¼4V´æöõL•ªhû= í¦³ò©Êõ\y\ãcéôÓOOÄóÀ Ú~ž?ƒßÃãª6·Ž²3`·LÞí$*ûÂ9ÈW]´{®‹¨<×××§Ä5Ìu^¦ú{+:EЯIô}ôôŸô¯}0çˆ~\¢¯g<`¼ShãÑÆW¾ò–«uC.ÛÈÁjnüØÑÏ X,–Ë€eÀ2`°8¦NÚž†”S·¬I“&µ <8Ib÷,–Ë€eÀ2`° Xu‡^¡G¯Ì©n XÚB+‚äÐ6g-Øæ,95–Ë€eÀ2`° X,VC- ¶UzAö´s°y¡ZI”J¤¤s¦³;ÿüó ºä’KѹM˜0¡ ¶`È%´—IjÏ9²ºÎ¬j+éÍ7ßL´~ýúD‘3[¼xq¢x ÑwÞYç•mEXC( N `Ú<Ѭ(2`·"@GÉØ:Ù(QœÎ™çÃZLÑMƒJ£4‹6ÏöRÿþý+‹ŸÁïáqHѦ Ú4ížë"òI\[\\Ÿ×0×9ý€”«•U§_Ô’'wÓ—Kzßé§£–fœWÆÆ ‰vϸµÑÚ{ï½ X¬íÊ©ŠÔx%¸=u° X,–Ë€eÀ2`°vÛW„5 X,–Ë€eÀ2`° X-{íµW¡ky¯^½õéÓ'ÑÀ ºì²Ë;6Ñ­·ÞZ/óØÆAÊ9/æDõ©r9WQ'{Öœùè£Õédµ½¡óš5kV"m§¥ô³YÇOÄ6QpÊåWEA°Á6×z%j¿BPˆr¡ lÅ2`À€DQ+ ¡C‡&b-Ÿ(—Aë‚¿—øü‡Äcåùð|#€äœå¬Lk£VAu¸¢œ@Ú4çˆë‚ëFâÚâú‹jóq sGõîè+èOèo¤:~‹¾þ1w(ÑÓOG˜¾žñ€ëBbLaÜa\’¾úÕ¯° X,–Ë€eÀ2`° X,–Ë€eÀ2`° X,–«ÞÐ6Wæ^°«9×!C 5jT"¶©ˆr‡|ðÁDÚ-Ù¬¨-ŦM›åÚàDy U󭢜+m$hÖ믿^кu멳}ôÑ‚hÐÜþu®1bD"^ÛHªæW•ª:[ö#'Êü›³Ï>;Q”È|œ‹/¾8!‡s(]y啉˜Ãh’k¯½6/ñ3ø=<‰ÇÊóáùFywœ3Î+ç]âµé¬ÒU+jáE»çºˆ®9××_t¸†¹Îé$ú úú›(/«ådè?é_%ú`úiúq‰¾žñ€ñBâõaÜa\’¾öµ¯° X,–Ë€eÀ2`° X,–Ë€eÀ2`° X,–«ÞÐ6WæYÐQ±ë9sD$æ2Üu×]±s=+$¿ôÒK½ñƉ¸Ý9W¥½LîB”ÿðá‡&bÅÆ Z³fM¢'žx"QÇ”«Ê.ñZpÛ?·ðŸzê©1PÖɯb ¶ùŸtÒI‰˜÷U:g‰æjD[¾/¿üòDBÍ 7ÜP·šß|ó͉¢¼BäÛo¿=QTõšŸÁï‰Ê ðXy><_·Ä9ã¼Få!xmxýx}%Ú@+€+ªêM›¦Ýs]D~,Wý½Ì"×9ý€D_AB#UÍÉ*Síþ“þU¢¦Ÿ¦—èë/$^ ÆèÆp¿ýö3`° X,–Ë€eÀ2`°:UЇwT„Ü€eÀ2`° X,–Ë€õÿù„éÓ§ñßjwõÕW—úÛ·ß~»ýu¹«‹ ms­Z–Û%æšä9K¬ˆœsB‘#ÊmonE•ö29WQžEÎEÐyË-·$bàŒ¶73·†Ûñ™·Á:Ûís@U^æ±ÐÖèT¥Áƒ'b¾Q”£D{?~|"BœÅ-úwß}w¢¹sçtß}÷U?ƒßÃãx¬<ž/çCâœq^9ï¯ ¯_”'CÈWèŠr½hÓ<®‹(/k‹ëëSªzã(UÍÉŠò²êädÕ)7“»qŒªÛçBGöȘ’+Û }ýë_ïv€¥\GRcèh”U•1`° X,–Ë€eÀ2`5=z^ògÑÝè)²s° X,–Ë€eÀ2`°‚¼ª2?ãÐ.ØE‹ÕúN½¶<•‘«ÂÐ6W:¢sÏ=7l´Å6W–aþüù±2›ŒnÞ¼¹ át"ÇC§óì³ÏÄ\ 6Ž‚zn;sTÕ›ÛÑYáš9vQnT¼˜ª.*æÅDçwÅW$R>B³˜'#8”ÓÐ,Þ™Eùp?üp"V°ŽËã?žˆ6ÀßKü ~Câ±ò|x¾Q¾çŒóÊy—xmxý¢ ˜ü–ꔡMÓî¹.¸n$ž_ÎÏI\Ã\ç´‰¾"w#¹£n&#°£¦ŸŽ:RÐ×çÊ6D¾Žq‡qI:à€üëó!›QNZÝ¡›æ‹F2`° X,–Ë€eÀÚ%K2Þ}÷Ý$KöÝÑ­ªæšË€eÀ2`° X,–+S§Nm·£æ]„J^ßÖëBõ‹Ô\lÏ0`° X,–Ë€eÀêÖ€•«ƒEÀ’ݪ¾« Ž}÷Ý·àxØBƒõrF]ФI“Íž=;QT+…-%^~ùåDìô.±<»ÅwFm˜¨> kÃð\$&2y™õޤk®¹& Pëµ¢íMY‡ˆ5‘¢`ˀ̖.#GŽ,ˆ­ehk¨Î™3'Ѽyób–,YRÐòåË­Zµ*QTßè¹çž«,~¿‡Ç!ñXy><_·Ä9ã¼Fm}xmxýx}#è¢D5­êlºÈµ×Ẉì“k‹ëëSâæ:…é+rõþ:«æý'ý«DL?ù>úzÆښʸµÄ:ðÀ»`íŒaÀ2`° X,–Ë€eÀ2`° X,–Ë€eÀ2`°vKÀR«¶¿èß¿":Ö†‘˜ßÁÚ>ªÏA=óÌ3‰êä!°[|<„­[·&ÒêÕW_M”ë0/1ÈÕÉC`NHÔ¾D•›ÅºC¬$ÖO£E5­x> ØQ}°3f$ŠjK1 ñÚ¬\¹2Q>¬U´aÆD¯¼òJA›6mJD»áï%~¿'ª±Æcåùð|£ Ï9ã¼F7¼6¼~Qm€vB;’r7ur¹.¸n$®­\NV™üSú‰×‡þ„v#Ñ'Ñoѯ•ÉA¥ÿ¬“J?.Ñ×3D9Œ)Œ;ŒKÒAdÀ2`° X,–Ë€eÀ2`° X,–Ë€eÀ2`° X»`ɘ9æ˜J[:scÿý÷o;ûì³±}·j7® iÓ¦%âVå(Ç%çT"ÐÉ9•:eص~Ë–-ýêóجիW'Z¸paAt*S¦LI帰UG™v¹œ«:%r[Þ¥\pŠZ­Ð–xþQPŸ9sf"–(ˆà9Kæ‘DàÃÚ„²R‘ÍæÄÏà÷Dù8gþ‘ôâ‹/&bÞ¯oK´=´?þ¸ 'VÊŽ?ƒß­+χçËù8gœ×¨º=¯ ¯¯¯D Ðޤ\)‡V”ẑª–¬‘¸†¹Îé$ú ú“(ï®jÙ†:þ1ú Úý4ý¸D_ÏxÀx!1¦ä*»K‡z¨Ë€Õy€ÅÿÞÖÏ X,–Ë€eÀ2`° X~‚eÀ2`° X,–Ë€µs묳Îj/Þœƒ¥|ƒ²ãßøF!'‚Õ™sDÊUnªýæ*·GU…Yy¸jŽAÔà”9Q“é\Y†(±Rö 7ÜhĈ 4(á7\ðiEÎUïÞ½ ÊŸ+¯¼² :QÎQTr¹BƒÈÁ3G‰ù+ ,1Ð0¿…[Ü¥ÈÞrâgð{¢ Çcåùð|£3Îç5Êmãµáõ‹n¾he*tÓÖÊ4,ßÞ›ˆèF‚ëëSâæ:: ÐWäÊ6HôIô[ôkRÕUúרº{®²{TÝ=WÙ]ÊÝ€2.I‡v˜«Cë4ò½Qù›Ý °¦NÚ¾`›wŽ3Æ€eÀ2`° X,–+;´.£ºŒÔnQ¦¡¹\ÃöÖÁ2`° X,–Ë€eÀÚm«³†še²ÒrÔbNE´•ždÙ|–Û£­È¹ªíu*·GAÁ‰ù,Ì_‘Ø—ù*÷Þ{oA¹² Ñ–o6›Õ+àfrÊ)p ‰re¢ünag 9çœs â–éË/¿‰~+ºIh…¿ÌUvJJÐ×3DÍÇSw—¤*º X,–Ë€eÀ2`° X,–Ë€eÀ2`° X,–Ë€eÀ2`° X,–Ë€eÀêÌ¡näìPκ5ìrÞ*‡ñÊ+¯$zçwµÂaD3×gÆ ­Zµ*Ñ£>šèî»ï.hòäɉ´»³Y‘ÃÈÕ½Šj÷T­{ÅÕbë’óÎ;¯ &œ2™yüøñ±öͼyó1ÙWbÛš¨µQ¨ªÚQYEu×rjÅ÷ò|rÀ%qÎ8¯œw‰×†×/ªoD DÉË´5ÚãŽZ¹ºX×0×9ý€D_AB#Ñ'åZçDísZqCJ?M?.µâ†”q'ª«wÄG° X,–Ë€eÀ2`° X,–Ë€eÀ2`° X,–«Þ8øàƒ 9ÇOÄЍ„þý÷ߟè‰'žH9kveoE]¯:u]^z饂ž~úéDlu1sæÌ‚&L˜ˆeèСõíÛ7Ñé§Ÿž(ªÝSµîUTC¨NîIÄ£|œ{î¹'s3¢¶7¬±Ã–!Q+™Îª\ý*)²·œrµ´:¸$Îç5ªoÄkÃëÇë+ÑÊÎ:¹ˆÛ[+ªÅõÇõ)q sÓHôô'ô7}Rº9¸¯S7~\¢¯g<`¼Sw—¤#<Ò€eÀ2`° X,–Ë€eÀ2`° X,–Ë€eÀ2`° XõÆ!‡’Ýn?nܸDÓ¦M+(×úá…^(ˆ­Ôâ§Y]©;üÒ¥KÍŸ??ÑwÞYsäÈ‘‰¢¶u¶§çÊ2ð÷ÑgäÊ2tV‹%K–$Š@œåÊlO¯š{åFRhGQ¸÷ß?QùüžäªæqE¹ˆ¹r%Qù ^^¿Îj•[ÑÚ¨º.¢µQ¦\ •ë< Tôô'ô7}ýVt£±½þ3ò¡ôÓQk1úú2­ÅSw—¤o~ó›,–Ë€eÀ2`° X,VÇ£jamx8p`[=ÚeÀ2`° X,–Ë€eÀ¸í¶ÛÚwß6†ÖŒ ­FC¯†õz\Å\ý«‹ÖE]”(8#˜È9ˆZr¢€åžäDƒ9AgÎAD¬ûô铨W¯^‰¢êÓU+·Gy\Ì5éׯ_¢(_,—k2kÖ¬‚˜kÂJáëÖ­+(—sÙIÕ<¦b¶nÝšˆ°D ‘˜Cçï%~¿‡Ç!UÍ)‹rkr7œw‰×†××W¢ är£üDÚ#í5ÊO¬ZÙ=ªîÎõÇõ)U½AnRs7¨ÑMjîU¢ïk`ånP£›ÔÜ jt“ZÄ:ê¨nX*C"?Ъ–¯œÂh¼üq¿"4`° X,–Ë€eÀêhð5Ÿ®Ë¶^ý ¼Y={ölÿ7ƒîðuⶆ^IêÉYN,–Ë€eÀ2`° X»$`Eý·ÕsQ»(µîkü–[ni·ÁªCv­CÊ€Uqzè¡•s¢jÍ ,H´lÙ²De'K´¥½ê6ã°rÛŒ£< Ü6ã¨t·ç;—ÙŽ•X¨Z±:ª‚«XUgÅj6kªÛs[®Zu™ŠÕQ~QUàˆ öøÖ[o%Šr”XÖ€&*}ÀÏà÷ð8¤íÊ(/+×á êrÀëÇë+Ñh'´£¨ËA®ÃAÔå je÷¨„I®|IT„ëÚ€eÀ2`° X,–Ë€Õ™œÁ¼Yë'Jçgð{¢ÚY¹µR¦•Nn­D;×:'Ú@îF¤ÌÍH:;°"°ËÝŒDI_Aj°èפª€)·V"ûl`1î°vsÀÒ™=ªô-2`° X,–Ë€eÀÚåKÙýZp7nÜaS¥o‘Ë€eÀ2`° X,Ö.X'tR;d©ºª BÆØÙ£JÕ×VÖÌ™3 âbyúé§ueÀÊå•D¹%¹¼’(·¤`åjû” ¬íS°XÛ'ªïÃÚ>`U­íÕ÷ÉÕö©XQ­©\ûæWI*Öö‰Z„ð3r-¥¤\͸:€•«W¦f\T¿‰6«ÕŒ«XUëÅ•©×*ÀÊå+F€•ËWìÊ€ÅxcŠË€U€‰SÕU–µfgŽ*}‹ X,–Ë€eÀ2`°vÙ$w®œé¿þë¿¶Ãå'X,–Ë€eÀ2`° X‡¶<+ÙüØcítÀªÒ·È9XÎÁr–s°œƒå,ç`°vIÀÒ݇j㡌©3G®o‘Ë€eÀ2`° X,Ö. XZ€$÷‰'Öê T'ïËu° X,–Ë€eÀ2`¹LC®äîJî®äîJî®äîJî®ä¾ûŽK.¹¤”v*`Í™3g—›X–Ë€eÀ2`° X­šg¥éôìÙ3ɹV¾ô¬Y³ X»Ó0`° X,–Ë€Õš¡WiѦ6]³ï}ï{¬Ý ° ”häÈ‘‰n¼ñÆDÜz.-X° ˺uë â¢Ìm=¯XÜzm?Ïm=¶Ÿ«ÞY³¸õ<Ú~έç (Hw9Í:å”Sqëy™íç¹­çÒOúÓDçŸ~¢F~_³t—Ö¬›o¾9‘ZGQÌ?ZµjU¢ 6´eË–D~øa¢ßþö·UÍÕûôÓO ¢=¾õÖ[‰¢|1æO±ä/ñ3ø=Q kÜÎç•ó.ñÚðúE9f´Ú íH¢­Ñi¯R®¤IÀâÚâúãú”¸†såL¢’&ô'Q¹‹\I“°r%Mêm1,úzÆÆ ‰1…q‡qI:ꨣº`éÉÕìÙ³Ûç“U”ŸmÀ2`° X,–Ë€eÀª8tLšËF^vch-·ä‰›Ë€eÀ2`° X,ÖîX§vZ{ª®‰Kóºzõê¶ÓO?½­_¿~¬ÝirÈ!m]tQ¢#F$ºá†Ýyçq;úÒ¥K1ÐDªß{ï½DQÐË–ÊX4kóæÍ‰¢Üš'Ÿ|2у>˜hÚ´i7.‘¶Â6ëg?ûYA}úôIÔ«W¯D'œpBA*,Û,þð‡‰N<ñÄ‚´ø›%GЬ¡C‡Ä-¼&LH¤„Nêá‡NÄÜŒ2 ^ÆNªG”ÇÅü–(G‰ÛÕiãÑ–v~¿'Ê#¬šs•²àœq^ËN^?^_‰6@;‰¶ƒÓÖh´W‰6M»çºàº‘¸¶¸þ¸>%®a®sú‰¾‚þ„þF¢O¢ß¢_“èûªúO)g'Q§úzƃ¨£c ãã’ÔkáÂ…aq©³ëb° X,–Ë€eÀ2`uKÀÒÐSºsÏ=·ý• ò®ô¤S×Ðe X,–Ë€eÀ2`°v“aÀ2`° X,–Ë€Õåk[¯;»±k¬ª"‚‰œƒˆ %ç ZXÑgäDƒ9Ag.I“› ¤sÎ9'‘&›õ£ý¨ `ñ÷Ñgð{xѱæ@\}1)ÚÉ’%KE ru¡X¿IªšôB í(ªÅA„¥¨%?ƒßÁ_™V8%´K¹ºWQ?¯ ¯¯¯D(8«®‹hmT]ÑÚÈ­‹hmä6 IUoP£›ÔÜ jtóÑ ÀÊÝ F7©¹Ôè&5wƒ*}ó›ß4`° X,–Ë€eÀ2`uGÀò+B–Ë€eÀ2`° X¬N,­½ÆÖëK?ÁÚÍëàƒ.Ôq>|x¢\ —2u\¢À™«ãåOU­ãÕÒÊuƒÚú<ýôÓ‰˜g2sæÌ‚r¹&Q^Sß¾}åòLÊäšäjý”©÷ÕìbK\ëéž{îIÄšI+W®,ˆu£r9Yí¦N+™\S>‘½åÄϨSÓ*—S­¥\ÎUT³‹×†××WʵơIUëÃE5ârë€ë&ÊOäúãú”ªæ&Jôô'ô7}ýVïÛ[G0ª%˜«#ÕÌÕŒj 2î0.I-©ŒÞbÀRÝ6”Ž­`é:4ÖE£œ@¶Ê«I–Ë€eÀ2`° X¬nXÇsLûfÍg=ÚçPã‘Giz™êM©B«¡§¾W_}õ6ËO° X,–Ë€eÀ2`u{Àjn•ó¯ÿú¯íתùw¹qÆg´¿flŒ_ÿú×íölÀÚÇA”íO‡8eÊ”‚Ø ~ñâʼn֬YSÐ+¯¼’èwÞIåÖT ”QîI®%HÔ®…-A}ôÑDQ[˜É“''ʵ)Ó$jsSµ%ÿ}Ô>G‹¼Yçw^A_|q"–ñãÇÄvóæÍKmO§³Þ¸qc¢¨¥ MgWÙ\®œZñ½9 Š‚/çŒóÝñÚðúEm´h´Ú‘D[£=î¨uk!µ‘â:§è+èOèo¢6R¹RQ©:åJèƒé§éÇ%úzÆÆ ‰1¥ ˆqÄ]°”¥õ£¡œf‹†Úå”B=õâ+Cþ¬°m’lZOÈÒ,–Ë€eÀ2`° XÝ °”°ßè98lذ$7JOAË”|¨ó¤JO½XúΪãç?ÿy8¿”Ë€eÀ2`° X,ÖN/Ó ùðè Óüã¶õë×·ô V” ¯ö˜vD?.Ñ×3Ðn$ÆÆè†´%9H]¬–ìL¹mÍ9X²×²@÷Ýï~×€eÀ2`° X,–«{–v6^6¶ŸWnL:µ}ç`ó.B= ^êé®Ö¥®®·žôé)±Ë€eÀ2`° X,V·,åYiW'‡žJ•y}—«ƒÕ|ÎsçÎm‡9%Ïï{ßk›4iR­c6`}>´¨Tc£ÊÅÈo|ã…FªÌ‡¸êª«Mœ8± Ù³g'RÍf­X±¢ n¯³í¸NpÊm;Ž*?ÿüó‰ÊT&nEësÏ=7QTÁz{Ë6D¥”/Ь޽{ÄÅÇ'Qß›nº)çè¾ûî+hÑ¢E‰X* ª¼Ox`¾ KDÁ‰¬ 5Q0Šì-'~¿'êFÀcåùð|#åœq^9ï¯ ¯¯¯D DŽœ¶F{ŒJT-Ë•<áÚâú묎ô'ô7}R+ÊÚPuÊÚDå<èë/$ÆÆ(Wï°Ãër€%ˆÒ:æÐÏZRV¢ÆnXÛªÒZ¥(™Ë€eÀ2`° X¬Î}‚%»Pý«F=,%þëxËì"4`ídÐjUŠ’° X,–Ë€eÀê<À„l«…ÎÝ€µ ÖöléÔ8à€ NdðàÁ‰F•(r¢Üš½`Á‚DË–-+¨«4/eC^V´–Ö­[—ˆ¹)¬Ä,)Ù±Y9"å‚·G¥ªnW/|"°;ûì³)‰³YѶjæU(g Y3fÌ(ˆÁˆù*Qõwæ1…×—ù,m[à#(Ûºuk"<þ^âgð{¢uÀcåùð|9çŒóÝ4ðÚðúñúJ´Ú íHªzQæF"W’¡LY†¨øbî”~@¢¯ ?¡¿‘xé·"¯ê£Ï íÑOG78ôõŒQ9ÆÆÆ%éÐCí’»U­^±T¯ U¢A7 ¥áÄ€Õu«nQ2–Ë€eÀ2`° X]«Lƒk'å[ù –Ë€eÀ2`° X]°´;”q[¯>ÕP;üTPÖ€µ‹=ÁÚž¢d,–Ë€eÀ2`°¶°T⨑¿ª¡ÖÌÁÒ«CÖ.X¹¢d¹±ÿþûœkÒ\qʼnÆWдiÓ=ðÀ‰–,YR“8Ù‘IœƒSZEU[çH¹:/ .,H5Jš•«‹ÕÆb0êÓ§OA§žzj"ÖÊÕŪ“ô.±} 7KD‘¶Äój¬Íœ93ƒ~”ŒÍ€Å–/LÊ’ÀÐh”E6›?ƒß?•çÃójZqÎ8¯œw‰×†××W¢ ÐNhGR.©½Œ Óî¹.¸n$®-®¿¨v]®îý€D_‘«(åZãDµÜªnŠnh¯ôÓQB>}=ãã…ĘB;Šê¥rÈ!]°TBé©§žjŸ Ý4 J›W†Þފ΀ÕÅË44?†ÜÞ:X,–Ë€eÀ2`°¶o(™½Q–AóÖˆÓÏ>ûlûÏ4¿UÒw XÝ`° X,–Ë€eÀÚþ'Xš/ •¦\©°hº4¿-©´›äøüž(‡ÇÊóáùF-ˆ8gœ×vymxýx}%Úí„v$UmƒS'ç*;®-ž ×§Ä5ÌuN? åê^Ñn¢ÎÜÍf™Î\[œ¨5ý4ý¸D_ÏxµTbLaÜa\’:è .X¾_üâíóªsL)§YCEGµæzöìiÀ2`° X,–Ë€eÀ*;ôT˜i'×:'jŸÃ¶¹mÉe¶&sûsTºA|›µb@S”fEìšk®ID'3`À€‚ªædµb‹»”+åå‹]xá…‰Øú‚[bŽ m-jE2gΜD p=öX¢(G9K«V­J´fÍš‚"PˉŸÁï‰ò§x¬<ž/çCâœq^£A^^?^_‰6+Á uÆMA.ßJâÚâúãú”¸†¹Î#¸¥¯ ?‰òîre¢’49ß—k‹S¦5Näûr­qhkcJ™–Jx`—¬]uèÆ'ºá  X,–Ë€eÀ2`° X,–Ë€eÀ2`° X,V·_ûÚ×Ú~ò“Ÿ$ªZÙ½LÂüùó RQ¶f½ð ‰Ø=^R¯fmo÷ø¨lC<Õ:¡T`®YܪAÕœ,é¼óÎKD`Îmy—ê”r`<餓Ñ®¤pEçÇíÚ¬u`~«FßsÏ=‰¢ªå¬¶MˆaåséñÇODàï%~¿'êÀcåùð|£œÎç5*¹Àk“ƒ)‰6@;‰à½j )WZ„ë‚ëFÊå\Ey“\Ã\ç´‰¾¢NþiÕ’ Qþ)ý'ý«DL?M?.Ñ××É?eÜa\’ԉĀeÀ2`° X,–Ë€eÀ2`° X,–Ë€eÀ2`° XµÆW¿úÕÂé\5cõT¢¸}û¶ÛnKtï½÷ÄÀÂ\:Èñì§96<¶Ò?ýôÓ‰˜—p×]wtË-·$âÖ娒4·ÒžsÎ9‰r. ru*e3ÀEÍtsÀ•v £e^Ì•W^Yíqüøñ‰ì²A/sk¢ ÝQɈœøüž¨Y0•çÃóÊ_pÎ8¯Q%~^›LI´Ü—üÊr€ÏumCçÚâúãú”¸†¹Îé$ú ú“tvÄÍdvôÁôÓÑ}=ãAdŒ)eºX|ýë_7`° X,–Ë€eÀ2`° X,–Ë€eÀ2`° X,V½±÷Þ{rz÷îˆNõÒK/-h̘1‰&Ožœ(‚‰œ#âÖåhûrgl]Žš¦²±jÎ EŽˆyQÞs˜'sÕUWÄkÁ¼˜³Î:+QÔä–ÛäëTÎÎ—Ä È-û¬ò-±º6/·sK—_~y"Âç 7ÜPЄ q;~”×Ä@rûí·'âï%~¿‡Ç!ñXy><_·Ä9ã¼F]xmxý"xoE¾í1*í@›¦Ýs]D~Œk‹ëëSâæ:Êyänéo¢fÎQ’&*Aœ»qŒn/$^ ÆÆ%IH X,–Ë€eÀ2`° X,–Ë€eÀ2`° X,–Ë€eÀ2`° X,–k,]·F³ëÆ\(/7Ô±A ¥ X]h|å+_)8*&‡öíÛ7‘:oSUëbEŽ)×a^Ú´iS¢wÞy'ÑÇœˆÝ⥪IïeêÅ0I5J|g;‰G}´ Ö3b07n\A#FŒHÄkÃZ?‘£:å”Sµ¢½N”¼œk·Ã ùÙgŸ(j¡Áäe¶taB7çP"¤0 j$¶–a"oÔ~†ŸÁøy¬<žo”ÐÍ9ã¼F–×&×âFjPÑi¯mšvÏu]s®-®?®O‰k˜ë<Ú“Kj6ÚÔIj§ï£¤ÿ¤•èƒé§ËÜ(æj^I¼>Œ;Ñ Õqìn€%;SíºÆÐ&Õ¥ëh¨¥Ö Aƒ X,–Ë€eÀ2`° XÑÐ:{ûí·¿øo=ÑÔ“ßmW_}µýéòÖ­[ X,–Ë€eÀ2`° XÑèÑ£Gá•!Ö²½þ~÷ÝwÛÿÛ€ÕÅÆ^{íÕv '$ªZKbý‘(_…ŽŠ-BôØ“Z¿~}"vzg'xv‹—ªædEyYeêǼòÊ+‰ž{î¹DQ‹‰ $bý#æ2H Ь'£GÇꢋկF$\t• œ¹zFÑ÷0Gy?0²æƒí€qŽ$¶MaŽÈ°aà "èp]ð÷?ƒßµâ±ò|x¾œ‰sÆyå¼K¼6uê§å`ª PÑ^%Ú4çˆë"d®-®?®O‰k˜ëœ~@ªZϯLÎUä·èûèé?é_%ú`úé:éë¢z~¹ºWÑÍ—ê8v7ÀŠ i[à$0Ú°aCößå†n¢'”Ë€eÀ2`° X,V·‚% ŠTuè‰u´)‰2`° X,–Ë€eÀÚ%«ù•_#K¯Öë>ý2`eÆìÙ³Û“ØŽ>úè¶ž={¶;eMúölélŒ=÷ܳà$ÙÚ‚Ž,Êyá–onw޶šÏ˜1#×Ò¥K b'w%ø5K†Ù,æDyY­h§ÃíÎRÎy­^½º ¶œxàE¹lœWæð0 Gù8 N|÷å¼tpEÁ–‹œy?x>ÜÂKÐæ¹í_êß¿eñ3ø=Q¹+χçËùˆŠó9ØÎ¸ÆTÓÖx>ÑMmšvÏuù$®-®¿¨- ×pî&P¢¯Èå[•ɹŠòMéûèé?é_%ú`úiúq‰óÊx•V¡Ý3îD­·Tf¨»ÖÔ©SÛw6ï"”½–(V¡;ÐåË—è§L™Ò~ÇYwK§Ë€eÀ2`° X¬¶]®–kŒ£Ž:ªÖ–N–Ë€eÀ2`° X]°vÆ0`5 mýÕS­ª qÑØc=²U’阣`tÑE%ºâŠ+]wÝuq 4;°/Z´¨ :3í hÖ[o½•ˆ9R®Ú{+¶?GÎlóæÍ‰Ö®][«$3ßs$M›6-Q®ú{”;”®(ç…A°³¶ìçtä°é˜y쀜~úé‰xþQîWT‰>'~¿‡Ç!ñXy><ßb:žê”æˆà=—_Ý$ЦsUÙ¹n$®-®?®O‰k˜ëœ~ ºñ«sÓ—«Ò.Ñ÷Ñ?ÒF7}ôÁœ£¨;}=ãã…ĘBª÷«ÌË€Õ²!—ƒmä`UÙÒiÀ2`° X,–Ë€µÛÖ¶¶[Nš4©mðàÁÉûX?Á2`° X,–Ë€eÀª9´=V‰î­ÜÒ©ñå/¹à$9©¹ŠÉeB=º ‰'&š9sf¢hKtÕœ,V*Žª½s;t畃È5ˆŽ¶o³jò²eËEU¹Eš †YE9j]¸$Ú›í–iüÛŠ ^Âäs@&Ñ™Z¢Ü¯Vˆß–,ñ|ëTâoìÒh'‘?ÉUt“@›¦Ýs]D帶¸þ¢î¹ Qãæª¥c"ÑoE7yô}¹œ«(ï•>˜~š~\¢¯Ï5rŽš93îD¶¥b–k»†Úl«ôBnK§Ë€eÀ2`° X,–+óʯ··–Ë€eÀ2`° X,–w¶xD€•ËÉŠšnž{XE™ïà%6V¥Œò.\˜ˆU†_~ùåDo¼ñFAï½÷^¢Vl‘.Ó š[³™!ýêó…Þ¬gŸ}6QTýýá‡Nt÷Ýw'b~O]ÜâU:gµp– ˆì¤jeðVåmÕÉÊAZåÀ§Œ"8ÚYs’«¼#m€vB;’hk´Çè&6M»çºàº‘¸¶¸þ¸>%®á\ †º›sþ…~M¢ï£¤ÿ¤•èƒé§£ôôõ¼žŒí„q'ZÊ!6`° X,–Ë€eÀ2`° X,–Ë€eÀ2`° X,–Ë€eÀ2`° X,–««ê 'œ(ª[ÃÚ>lÿÁ$Ç(ñ‰’Q †ùóç'ÒËfÕédŸK@­›„ÊÚ6[·nM¤Í"ëÔ¬Y³¦ ÎÁC=”ˆ…Rb ¶>|xAC† IÄkÕKc}#&S××_™¶7\ç,U½9‹Úqñ8è×$ú>úGúÏÈ7ÐÓOGùµ¹œ«(?“1¥LÝ6Å/–Ë€eÀ2`° X,–Ë€eÀ2`° X,–Ë€eÀ2`Õz‡ $¹œ¬(/+×JGºä’K]y啉¢<‹;ï¼3Ñ<èñÇOôÌ3ÏļŠ7ß|3Q´Ý™[¢[\Q®×G}”(\t1hpëyÔn‡[±9Ï“'O.ˆ[³¹mþ²Ë.+HÉ”Í0`@¢~ýúÄ ÎŸÓN;­ ÚcÊ¢|#Úx+J.Ô)í­·ªm}¢¼IÎç•ó.ñÚðúñúJ´ÚI´ÍŸ¶F{ŒJ¸Ð¦i÷9˜*T\ŸRgäkF¥è“è·è×$ú>úGúOγDL?M?.UmƒS&çÊ€eÀ2`° X,–Ë€eÀ2`° X,–Ë€eÀ2`° X-êFN_5'+ªöNçmÙgnq”÷sóÍ7'š={v"nÅŽ*ŸçJ9lÙ²¥ >ø ѧŸ~š¨Nõ÷2[±sÀA,«Q/[¶¬ Ç{,ï¬Y³ b¥l:âèú5*+tGzàÀ‰Î?ÿüDQUhV '<œ~úé‰"HëÕ«W"„¨j9EÐ)ó7ü‡Äcåùð|9çŒóÊy—xmxýx}%Úí„v$ÑÖh´W‰6M»çºˆnVr@Õª.ôô'ô7}R®ƒDßGÿHÿIÿ*ñúÑOG¹–ôõ´×(o²LΕË€eÀ2`° X,–Ë€eÀ2`° X,–Ë€eÀ2`°Z6öÚk¯Êï¾£mâÌaЈ*ÿüóqN/^œhÁ‚q®™»qË-·ü×\sM¢(@Ó¡3ß#*ÂÜ ÂCß¾}E7Ì?" DÕ¨™kRFü ~O” Åcåùð|9çŒóåÐñÚðúEÄi´“¨4möH{•hÓ´{®‹èf¥j~U Šn¾rUÙéo$ú$ú­hNreè?£|8ú`úé¨4}=ãAÔç®NÜq%w–Ë€eÀ2`° X,–Ë€eÀ2`° X,–kGÙŒ^Åê•{£¯z\FC¯Ïõzÿè£nëÑ£GûëYÙ†«‹Œ½÷Þ»'R5'+ÊËâ6ò¨*4IÿþýE ¢¹Å{üøñ‰î¸ãŽD÷ßA‹-J´råÊD/½ôRA¬üÎ;ï$ê¬íÛeJ;°Â3›Ï¾õÖ[‰¢Øk×®M´zõêDQ.÷ƒ>˜è®»î*ˆ×‡ùQ€;vl"Ú@T œ`@;b.QiÌ+! Ð^%6µ-#~¿'Êqá±ò|x¾,qÎ8¯œw‰×†××W¢ ÐN¢› Úí‘ö*Ѧi÷\Q'…µfé+èO¢ªìôIô[ôk}¯ ý'm@¢-Ñ^£ü>úzƃ(7c¢’&Ê!în€%Ð>}úÿ­ëtõÕW‡ÿV0»|ùò/lOytª‚oÀ2`° X,–Ë€eÀìè‰ecüú׿nÂWvuÔQ,–Ë€eÀ2`° X¬æ¡W}|eÈŸEC¯žï¾ûîöW†¬.2öÙgŸÊ‹¡ pq!Ôi æ€Lš4)ÑŒ3 š7o^"V7¦3—^~ùåDešÀvF>Gô7l.»uëÖDï¿ÿ~"ÝQ›7oNÄ ×Ñð+V$zâ‰'Eã¹Ý^¡YQ%i6›8qb¢n¸¡ ‚Á˜1c±u”ïwùå—'¢í±D4lذÊâgð{x•çÃó`‰sÆyš­óÚðúñúJ´Ú íH¢­Ñi¯mšvÏuÁu5fÞQy“ô'ô7}ýýšDßGÿHÿ8}pÆÍqÓ.)‡¸»Öá‡^êgü½$–­WºI‹n©n X%²UIˆ3`° X,–Ë€Õýž`©Å’XA›^ªm<8ì°Ã²ê–€ÕQ"[•„8–Ë€eÀ2`° X]°ôŠU6Ñœƒ¥“e‡Ä°¶s4Ù¶7!΀eÀ2`° X,V×S§NmPÒüÐDk8z]¨×v²Ù¡:h <Ø€Uw0‘­îãÄÆØwß}+×,‰ê‘äj–°Ö–”«•Õ¯_¿‚d<ͺâŠ+]{íµ‰¢ZLlÁš;K–,)hÍš5‰èð[Õv£µ³ô¨¸YŸ|òI"ÖÜ‘x¬o¾ùf¢(1~ݺu‰8GQ]W-ØfEAbîܹ‰ØF%J¬&Lž<9Ñ„ EÉõ¬DÛŠ %j”?ƒßÕ&â±ò|x¾,qÎ8¯œw‰×†××W¢ ÐNhGmöÝÐЦi÷\UÓªN{+úΑDŸD¿E¿&Ñ÷Ѷè?é_%úà\¼ªÖVŒbF®¶¢¤MZÝ °ri?Í€¥5¨7\zàrì±Ç¶¿½Òf VÍä7&²ÕIˆ3`° X,–Ë€ÕæJî»`5@*%&²ù –Ë€eÀ2`° X,V‹F#‘m{âößÿÊÏëÔ0‰þ†ïÓ{õê•(jErÁ$ºøâ‹±•Gœh˜GÂŽóÒ“O>™(\tå€+‚®:À• eji•ÉÛâùmܸ1Q8Ÿ}öÙD̽á}ú$ʵ×áöu‰ÛÓàæÌ™Sƒ[yD99àb{ ‰y$zbÙ,æ‘HUƒF”{’ËÛŠ`­GrAÛ<ÿüóq{ºjÂ5+ F‹/NÄV,¼žóçÏ/ˆÂ¶#´PÌa*ó7üž y¬<ž/çCâœq^£r%¼6¼~¼¾@ÑNhGm-—OåTÕ*~&×_”¿È5œƒ©¨èOx=%ú$ú­¨\ }_®í ý«”»áŽZØä|”ǢRæ†{¿ýö3`° X,–Ë€eÀ2`° X,–Ë€eÀ2`° X,V½q衇¶åöíÛ7‘ªÅ7+Ú.›+åPg[îÉ'Ÿ\Ðé§ŸžˆùcLôóŸÿ¼ v‹ç–wæÀH9àŠ‚<(+3¿öÚk©*³>øàƒD 4e¶Ÿ·"÷$ªz+ÿN×7Þx#‘zlR¿úÜù5‹9=”qîW­Z•ˆ¥–.]Z¯'˰’¶LNü ~Od[úGúOúW‰>¸e{øQÆÆ%é€0`° X,–Ë€eÀ2`° X,–Ë€eÀ2`° X,V½qÄGšÍrž}öÙ‰:«¹g+a™†Ñl¶;zôèDQUïpE¥Xy™ù,Ì_‘À¶lÙ’è½÷Þ+hGlWò¶rNæÖpû}TõšsÀ 5þeІ qÞ×®][Ð /¼ˆ ˆ£’k™¿á÷ð8$+χçåFqÎ8¯œw‰×†×/ÊÕ« ORgÜ$äJ“H\[<ÿè€sÏuu† ¯ÈÁTTô[Q#ñ\£æÎºyÎåÛ2^HŒ)Œ;ŒKÒÁlÀ2`° X,–Ë€eÀ2`u½1`À€Â|G2`° X,–Ë€eÀ2`•º®êKœ“«âøÖ·¾UØÊ{É%—$ºð ©ÿ!uê©§&ÊUö-SÝ·ÌVÞSN9%‘Fïíét˜t\tq‹4«*KlÆÊ\›•+Wôâ‹/&Ê5½ßær\¢|ªV´€EÆmð[·n-ˆçSʘ'Ã@É9ŒJJ@X7mÚ”¯_™¿á÷DÉcåùð|£Ü¡,E% xmxý"Ûê xŠ€¿jŽ`d7¹&ç\Ÿ×0×9ý€D_AÝäÑ'ÑoE7“ô}ôôŸ­*Á“ëÚÁx!1¦0î0.IzmeÀ2`° X,–Ë€eÀ2`° X,–Ë€eÀ2`° X,–Ë€eÀ2`° X,VWÇsLÛu×]—hĈ‰† ’è¼óÎ+¨wïÞ‰Ê,ÜVt\çÂÍW]¹$x‰õeØ’"êd?{öìDóæÍK´hÑ¢‚Xψ ÏL^–X‡‡uˆrIñR®%OÐêÆÎ€²(™žçÇàÊúFÑæÖoŠ2µ?Ê)÷™Q=1+χç]sÎÙ΂¥:5«x¬R.aë"ªCǵÅõÇõ)q sÓHôô'QM«\{t3™ªVÜG“»f¼Sw—¤£Ž:Ê€eÀ2`° X,–Ë€eÀ2`° X,–Ë€eÀ2`° XõÆw¿ûݶɓ''ºæšk ><ÑE]TPö:;â]?v™<­(—-'˜·6nܸ‚n¹å–D3fÌHtÿý÷ôè£&zê©§=óÌ3±Å óFXß( ò йÚZRg´èi”ñØ <—(`GàÖ¬(_¬Ê}oC<žoÔfGÁR–5¹šU´W‰6M»çºàº‘¸¶¸þ¸>%®a®sú‰¾‚þ$jñU5¿ªPÕ©WÝ<×i{ظø$õìÙÓ€eÀ2`° X,–Ë€eÀ2`° X,–Ë€eÀ2`° XõÆ÷¿ÿý¶;ï¼3Ñ„ 3&Q”£´³º´W®2yZl'!ñü.½ôÒD£F*èÚk¯M4iÒ¤DwÜqGA÷ÜsO"¶Ôàp)—·õòË/'ŠÚ}¼õÖ[‰˜´\×ÎÌᩳÍ?§äv„êëΚÇ:9tQ~möH{•hÓ´û2ùT\[\\Ÿ×0×9ý€D_Aµ…¡OÊÁTgý4ý¸D_Ïxc ãã’ôï|Ç€eÀ2`° X,–Ë€eÀêxh]ýò—¿l¯mÖ°ml‰†6Lôëׯíè£nš§¿Ñ–Ë€eÀ2`° X,VÓP¡ÙéÓ§ñß²©«¯¾:ü·Úm¹|ùò/š›kGªvd°jmû=üðÃkÑî¶&vîܹ‰¦Nšˆ…™/ ÑA 0 ;£Kìžž®:Ü£-Ä9àb¾€DÑ¿ÿD_|qAW\qE"æŒ?¾ Vtæöîx  æ„<ù䓉V­Z•è…^(ˆÛÓ¬¢ òÌya%ð>ú¨ æåòºvf®Ðî¦:¹m¼~Q¾m€våÒÖhQ¹Ú4ížë"ʧâÚâúãú”¸†¹Îé$ú úú‰>)Su€*º©Íý¸D_ÏxÀx!1¦0î0.IÇw\·,][uZh =‘Ò¼—uJW°>rƒ J« í° X,Ë€eÀ2`u]ÀêÑ£Gá•!¶­±bÅŠö§Z¬ŠCNEïZµË¨°¶—v X,–eÀ2`°ºÆhŽïýŒCEnõ:¶N–Ø¢W¯^YuKÀÒ+?5¿Tî'{{hWC[hyä‘DsæÌID‡rýõ×Äœ‚\Ãh©OŸ>‰¸HO:餂¶¸"èÊm1–˜SÀc¿à‚ {ЪW]ÜÊUMfŽÄ€Ñ!žqÆrÊ)‰rùQNÄŽÊÛbeåPÜN™°®ºêªD‘½ñÆñzFy2 ¤³gÏN4wîÜ‚hKÜ²Ï /q›?ó9¸Ý;*ÀêáåË—gÅ’eþ†ßÃãx¬<žoTfƒsÆy‚ ¯ ¯_K´Ú íH¢­Ñ£ü©@q]pÝH\[\;*Ÿ*ú›œÿ _“èûèé?£Oú`úéèæ9TÑc ãNT6D9p¬í²c=6+–Ë€eÀ2`° X,VÉñío»í¯þ꯲2`° X,–Ë€eÀ2`°vî¸è¢‹ •¤ \›6mJå„T.‰ÛtYÙ7ʳ5jT":Õ&êÛ·oAt¢¹ŠV5•.“·•«9Qnµf#Y‘(KvÐ,:YV´–x-ÆŒ“(ª”Í@:iÒ¤DQ®×´iÓMŸ>=ƒ¼4gΜD̺ÿþûE¥˜KDy衇:Eüž(¯‰ÇÊóáùr>$Îç•ó.ñÚðúE°D ÐŽ$ÚÚÿkï|^¢úÞ8þ]|–ý-úý‹~%š„`%µ(h‘D"Q„‚ £¶Z-Ò 7Š„Qn Â."¬mB­J Vbt¿ßç~™ó¾g¼3ãXçøy½àáãŒóÉ3gÎ}ÎëÞ9÷9:u¼Zè˜Öq¯Ç…¯j¹[åTX¯Åzªr6œÏ[Ãé;ùÒܧùQó§ï³Ð¬yÚWr!O¨|'é:§è¼ãÛÀ. X‚…`!X‚…`!X‚…`!X‚…`!X‚…`!XUÑÙÙ™üøñà ®/_¾8¡ß']yÂe¡“„nöé›l>|è„nÎÚÝÝí„/WZ‰ÙW9ï¶k‹Z¬Û*çVlM¼yæKĺ6ÃÊw‡&f ÝôV“õ;w2¡É[o­¿ÿ~&´"÷£GœðU¦×I`hhÈ ÝØ'º–HÄ·®PC'žrþý;¾uMÚV}?ú~}›ëjŸi¿j¿[èg£ŸŸo‚Ö1 ãÄ·²Ž5:^-tLçÉ“ïD*︮Õzª¼ãÚ·Ö+o'_•yÍ}š5j~µÐ¬yÚwrRéú*ŸPé¼ãÛÝÞ‚…`!X‚…`!X‚…`!X‚…`!X‚…`!X‚…`!X‚…`!X‚…`…€í†¾´´ä„ ×üü¼:ð}Ò¥n{`¡µzt!¯n©a¡5utA­. õMòšÌó¶ß±Ðz2—.]rBÃúÄ®ÅbØr’u9 åµÞnÒÖÖ– ]@«‹Œ}RfB_ºxùöíÛ™ÐzG:k$‹ÞÞ^'T táµO&úúúœÐ±¦Bb100Pqè¿¡GÛa¡mÕ÷£ïWûÃBûLûÕWkJ?ýüôóµÐ1 ãÄw#ŠŽ5:^-ò¨¯ÕIQÞÍ+¾ëÚ6Í'¾ú}yÛÚøDUsŸæGk¾Úušƒ5Ok·Ð\Ÿ'S>¡ÒyGç% _‚…`!X‚…`!X‚…`!X‚…`!X‚…`!XëX°Ž=ê ã÷ïßiµdWXÓðíÛ·²ÿmKŠöoGžpù¾ ÏÛnÇ·úôô´ºÝ‡Ö²ÐZ>###N<~ü؉{÷îeBwƒ×IÂ':hM_¯tKj·ä©tW9 ÿâÅ‹Nè`‘·¶Ë'eZ›H×ÖX][·n9¡xWWW&tŠNðÕ´Ò9Eç—,ì‚…`­Z°Jag¶¶À“'OÒ³S ÁB°, ÁB°â¬j.šÌÌÌ$'OžD°j-Xvéxvvvù± TK.‚…`!X‚…`Å%X•^4Ñoµ¬*«¾¾> ;Èí»òÅÅÅôwûÕç*¬J…Ë']zð|þü9>|pâýû÷NLMMebbb‰ññq'ôvvßNïºõƒÞªì[¤“®#ñÝ6®ëFtk 4,tHž€U;Qä­Ñ¿ë+¡œN"åH™ö‘N4ºöD'ðk×®eB?ƒ›7o:á“ ½Å½££Ã ßz£Z„þ_©m«¾}¿¾í‘´Ï´_}kõ³ÑϯYÒqâ“¥¼ãÀ·~ªÇAž<ùŽ=†õ8×>²Ð\¡Ÿo\hNÒ¼åÛZLsŸæGÍŸš_-4kžÖO¦Ê*ßÜdó×z¬j/š X5À:ÞËj˜”êÔJ:ÁB°, ÁB°¬0¨ö¢ÉjËn×®]¹±.+ï’ŸuxCCCM®`YbÕKÍv¶8|¿Ny_%øŠ–³«&gýZàÌ™3N455e¾«.ŽãÇ;qìØ±L9rĉ8±wïÞLìÞ½Û‰;w:±}ûöLlݺՉ-[¶8±yóæLlÚ´iÕ¡ÿ¦þ]m—ŶmÛœÐ÷²cÇŽLè«}´gÏžLh¿î۷ωýû÷gB?Ÿƒ:qèÐ!'>œ:þT”Ó6}?ú~µ?,´Ï´_}cZ?ýü|IYÇ€ŽG1ú~ô8×>²Ð~ÕÏÆ74'iÞÒ¼f¡¹Oó£æÏr œjžö\æ}õï;iÐ9EçßÜdó×z¬j/š¬F°lülذ!7þe҃Ű ¾sssÎåD;ó®`!X+`[¼yó&íh“+»ÃÀêç¶0ÑÁ°Ÿ­¾ Ä%XÕ^4A°ªÄ¾·K¡uuuIsss288èØíjê` XawѤ”H!X€`• ŠT©"ä X , , Á@°, , , , Á* _íŒBýŒ‹—¾|ùÒ©óZ[Ÿ={–n”k{EÚö¶ï§OŸ‚lëóçÏÓ¶ÖÖ'N¤í²ªÁ¡™™™tŸµbBlo,E€cèϘÆjL9 ¦ÜŠ`!XQ V)†‡‡“ÑÑÑåÇVI¶··÷¯·yzz:éèèpÚZ[‹·5²°‰áÂ… Á·õׯ_ÉÐÐPråÊ• Ç@©bz!¶7Ô>Œ±?c«1倘r+‚…`­ ÁjmmMfgg—Û™¢í´þ7±3@;ƒýþý»ÓöÛªÔ××ÓÖ5¿!¶7†Ï;¦þä¸"·"XV4‚e½ÅÙ³g“þþþdqq1ý]µ»y¯vYºxÌâ$Z[‹±3í±±±ôë‚ÐÛj¼}û6=ûŽ¡­*!¶7ô>Œ­?c«¡ç€Xs+‚…`Eƒ¥˜`uww{“m©çþׯ_O>~üèmKhm-nƒ…­[˜ŸŸº­Æäädrùòååu-!·Õ×–ÛzÆÖŸ±Õr@Œ¹Áª=§OŸN6nܘVÎÁ^ê±³[”™‹¶u¥ù¡µµ˜¥¥¥tÁk{{{ÐmL:;;¬!œ½®Ô·\ÁZ[Á µí¡ŽÕR„–V:ÆBË­ÖŸÿùŸå‚U% ISSSúsñ%cÃÎ[ZZ‚œ Bo«Q×Ûj‹[mA®Óµ½1ŒÍ˜ú3Ö±zˆ9·"XVßébre·ã ¤¿Iï)`?÷ôô™Bkë7’wïÞ¥ýúóçÏtý…q‡ØÖ©©©’·]Ç4Bmoè}SÆ4VcÊ1åV ÁІñññôΑººº¤¹¹9½ô^ ôú'!×j±Z2&¯vóÀ©S§ÒÛšM`ClkŒµÐbjo u„béϘ¤<älIDATÆjL9 ¦ÜŠ`!X€`!X‚…`!X€`!X€`!X€`!X X X X‚‚±òêÕ«t?Ïxo½í÷ö:@°|TRA¿VÕö,ž¾¾¾4Y=}úÔyÞöu³çûûûé$«$ÃÃÃÉèèèòcÛÒNÎVûZ ¢ÇÎ"-a½~ý:}<11‘>¶çÁZ‰ÖÖÖdvvvùñׯ_“óçϯúµDÏÒÒRÒÞÞž466&cccéÛÚÚ’ÅÅE:ÁZËŨ׀ú\5¯E°`]0??ŸžIZâ:wî\Uë"àß'X–3Êy®Ò׮ċ/RyÊ {‚* Áª”¿q«æ X°¾"lhhH»ó!‚U.W¯^Mæææ–Ûºª–––U¿Á€èéêêJ¯\Ö+LNN¦íy@°VÂî8¶» ØÏ===Ë‹¿Ì{-‚ë†B™K|ÅØ•,{Þ~V)òj[ V­ê`!X4…B£¥êÐXR  Xë ÁB°,@°,@°, Á ªäŸþ/DmÁX{, Á@° zþ å%X…¨ôæòIEND®B`‚XYBoxAndWhiskerRendererSample.png000066400000000000000000000345571463604235500361460ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/xy/doc-files‰PNG  IHDRXr5˜96IDATxÚíÝ T×yæñ‰d[–ü¡È߉í8ë8Y¯×É:ñzm¯wL˜ h…¥ )Éh‘a1,òCáQ¬–J”傈µ,•cVŠPhÁQi¥H+VI‘C,%%*AE"•Љp Ûán¿×}'=MwOÜ™¹Ýó{¦NAwO÷ôsß·ßó¿çœ{º/!"""¢\Õç,""""€EDDD°ˆˆˆˆ`þÃÖ×7®ÍÔ÷TÄãÀ,*\‡’µ .¸ ¹øâ‹“ $ù—é@åØ¿ï}ï÷ÜêãûÕ¯~uÜãŸûÜçÆ=¾wïÞq¿÷½ïmû=u Ý$Ï<óL²bÅŠô¸G^¿ò•¯L^÷º×%W\qEr÷Ýw'?ýéO{°}–“›o¾99zôhÏ~.yä‘dõêÕÉïþî咽³xÇÿ?ýéO§ñþçþgŒM`U·;w:X9ÅâÅ‹Ç=÷ë_ÿú¸Ç/»ì²qðƒ÷øÆÇ=¯°jë+_ùJrþùç7Ìí·½ím…ô“Gê=ÿŸøÄ¸û#V™žzê©ä³Ÿýlz,ãuãõ?ð$ÃÃÃçLÝT¿~äLÄã=ïyO:õWOñZ•ÏûЇ>4ÖáV*Ö#}øÃîèvš‡µ<53ÚÖ”UÇ%r¹RÍÆ¦úïDþÆkeŸ“Xïö§ú§c'l1U5$‹ß ¸m·ž´£—^zÉ #,*`Eq¬¼Ë–-ãž·fÍš†@<ž):©l¤ kßúÖ·Î%‹ßiåŒz¢i€ûï¿¿áïG‡V}_u‡S=×N§WK+W®÷ü¬‰Å¹•ke*ׄb:°òyñ:ø«åcÿþýéèF»SjÕú‘|d nî¹çž†k¢¢3¾z¯_ õô±}¬­é°<Ža«yXËÓdVäYõßÎÔJlj=Vë9·Ýv[ °µ‹çíÔ“vùgÉ,šVÀªÕbT¥Q‘Ž3ü8K+ïÿ‹¿ø‹±çTƒÁoüÆoœSx³µD­ŒEç;Πãê¡êQ‹V½FGQï=Gg£VLùUwDá#ά³ûb$!ût|ÑI|þóŸ¯;uت¿zru\î»ï¾ôþ˜j™7o^CÀÚ·oß9Ðüüóϧ¿ûƒüàœN3ÕÇ0ï§új¾¸¼™Ž¬™÷þòË/-ˆcó裎ªj‹N<ÖUþN¼N»þêù¨„¼h×ê8Ö{~L-Õ;f±˜¿ôTÇ-Ûv¢ÖëǨg3W€U{h÷óÐÌ1ì4ëyš¬«Bk½·VcSýw2¯>)iôXL¥vROšUœTŽÈ†WÀ¢)¬L1ZR=gæ¡Ê©«l I­b¿W©üÇLÞô¦7ó7kM 6) ±v% tLåTZL>µÞsåsêṵ̀B1íWy bqnv;¦¸B•{fÅñ¯‡lÚ°]õžÓ'µFb­QÖù5豆«RM;Ö’vG óÁjæ摇íÂR§#XÙg´ÕØÔû;ŽU½ÇÚ­'ÍèСCéšÏÊ…ýÕ' D‹¦°2Ȫ|,àh¢Ñ‰Îô+!¢ÑpýD‹‡cÑu+SH­NÅT¯Giö¸5 XÕëNb­R5Ì~æ3Ÿ»¯ú‚€ìwÚõWï±qh´gûöíMV,ü®ÔDûQå¹mBõ4g³ ¥[}yæáTVL³U>>kÖ¬¶bÓ®¿fFL[©'#•k?ãÿ1J°hÚ+¦Ÿj¸ÉÁŠÇZQ%ŒDûã?þãsÞo§€U}™§_µª×žÔšV©¾ 4äX¡©Š5`µF³MáUwÔ•‹Ë«ó¦ÖU}y@F(®ö«^óWëïÅÈi6ZØÎ1Ì3§°ªÔÇ·´›<k2F°bóÞÊ\Œup±uÀ¢i¬žÊÑ“h1]ª^3—v‡â+I­™ˆ¯&© •ëzšQ½³Þ<«r /Z¬åȰ¢«öQ½¶êðáÃu§jª;Á¼+â^©êµ0¦ðªxÇš¬lí^uüãJÒj…ïXgÖéq®ÎÅl¬X„Š÷W±eÓGíÃ<ó°ZFP[ýÞ±ž¤úwbDa²+¶Vˆm9²Ý¼cª*¦‡c®lO«FÏØT$TNï9r$“X¿WÜÅëÇñý¤b”p"€kU±ž,^7Ö“Å߉¿' 1JÓÌÕ¡®¶Í+kåx§ì«‘²Ò[ý<ÇóÂoÄ F¡Ä4›¼«•z°`,""""XDDDD‹ˆˆˆ`,""""XDDDD‹ˆˆˆ`À""""XDDDD‹ˆˆˆˆÀêL7ÝtÓ¸Û+W®L>ûÙÏNj[±bŤÿ©n½èI¬øâ‹'¾ä`»mhh`Uê‹_übòè£Nj;räȤÿ©n½èI¬øâ‹'¾ä`»íK_úÀšjÀé¹díEObÅ_<ñ%V–¦iš¦i½Ý–,žÄŠ/¾xâK,k°ÌÑóÅ_<ñÅ—5XËÙ_<ñÅ—Xñe `™;Ö4MÓ4 `Ár†Ã—XñÅ_|Á²Ë|6_<ñ%ùâˬ֞={ÒR’9sæ$ëׯOŽ;–>vöìÙäŽ;îHï xŠ6::j‹'¾ÄŠ/žøâËV#­Zµ*Ù¿ SgΜI¶mÛ–,[¶,}lûöíÉ®]»Æ~wçÎɦM›¬ÁÒ4MÓ4ͬV5{öìôߥK—&Ç»?F¶.\h‹'¾ÄŠ/žøâËV+:pà@:ª÷XŒrUß—UÖ*5<<œÎÍf?þÍûö?ýÓ?MêëOÇí£Gö”Ÿìvøê%?½šµþ/õB½¯é¬]X?þx²dÉ’±5XýýýçüN­ûŒ`ñ$V|ñÅSÖ’ Óø2‚5íÚ²eK D•‹Ø›Á²KÓ4MËføÒº°‚>c¡{µ–/_žœ8qbܬ+¯¼ÒO|‰_<¾Œ`5Ò¾}ûên½°cÇŽôÊÁÊ«7nÜh,žø+¾x"|Ù«‘bMU­f,sôδôðÅáË–ï"ôÔ´ÿ\8yQù² `Yƒåhô€/Ÿ-±«Bûê–€e –Âbý _>[b%V3Ê×TxXF°|`ñå³%Vb°–5X>€šæ³ezfXñ°–,gÚ<ñå³DøXËú@k°øòÙâ‰/9°Œ`ùŠ•,O|ñ°¬Á’¬š¦éÜøâ `¬"-î,âÂW£"<ñå³Døâ `MÛ–dµ®‡'¾Ô ±â `,Éj‹'¾|¶xâ‹'€°|{w=Žijk°xâ‹/€°$«Q±+ñ"|,€°xêíu=b%^<ñ%À’¬FEÄÊO|ñ°–dÕÄJ/ ÂÀX‹'#Xb%^<ñ%V¦Xs1wîÜq÷;v,¹ñÆ“ÁÁÁ´­^½:yá…OÖõ8V®ŒT/øâ `Dýýýc­R‹/Nî»ï¾äìÙ³iÛ½{w²hÑ"€Å“Q_<ñÅÀj´*500‚U¥fÏž °xÒt|ñÄ_«]ÀÚ³gO²uëÖäÁL^|ñÅdï޽Ɇ OF°t|ñÄ_«]À ¨Z¹re U J0Öcûl²fÍšdpp0mK'À—XñÅÀX« õ÷÷µJ=z4¹öÚk“ƒÁÒ ð%V|ñ°Àj´*µiÓ¦ä‰'ž0E¨`òeñ´/0–ƒ `¬ž¬… ¦5gΜtz0€itt`ñėέë:9(ùX…¬Y³f%û÷ïOΞ=›œ9s&Ùºuk²víÚš`•µJ §k»FFFÒÿæ};;™¯?·³dí?ÙíðÕK~ŠšEêÄkfÖ‹"å`žþŠæ«(ñÎ>W“ù÷z°* 500`‹'¾ŒÁâÉ–Ï–¬vkÑ¢EÉñãÇÇÝ7oÞ<€Å_:7€ÅÀòÙXíÖÎ;“Í›7­»:pà@rçw,žøÒ¹,ž–ÏÀje›†êíîºë®t‘{L l>}`ñÄ—Î `ñ°|¶–ÜL¾tnKÊA¾ÀR0O:9(Ö$ûê–}óÀâI¬tnK¬®¬nð°Ob¥s›ÄÎÍõr`,€°x+_rP,€°L¾tn:7€%V `,6_<éÜ–”ƒ `é´O:7¾ä X `)˜|éÜtnK¬ÀX:m¾tn:7€%å ÀX:m€Å“N`ÉA9°Àâ‰/›ÎMÊA€°–N›/›Î `ÉA¾ÀÒi,žtKÊA€°–¯óX:7›”ƒ `,…EG sÓ¹,98©¾ö•ÚÂR{k©_j}¥öŠR{{©ý÷R{`,€¥X:7€•{çÖ‹#Þr°ôs¨Ô~­Ô^U†ªZí¼R»°Ô>_j§ÀX `,€Å_õ¾]j¯mVÕ- ëãÍCÀX `,9È—XÍ,_O¶WY{}©ý¡ìIÀ:räH2wîܺ?üðÃI?ÀšÁ…Åâ}€¥ÓX|5øùq©½»b­U«í5Í­ÉX]XNY«¥‘‘‘dݺuKa12°ä Àâ«ÞÏŸ•ÚÛ„«¬]'{rа@=÷ÜsÉÐÐPrêÔ)€¥°,€%_õ~.í`ô*ko—ƒ3°FGG“åË—''Nœ¨ `KaXKòÄWyН¯Ãö 98#ë†nHž}öÙ†#\Xe­RÃÃÃéÚ®˜bŒÿæ};:¸É|ýVo)YóôW4_E‰wÑò/n)Vyú+ZG ^̼z1¡¯¾œÚÆ«[êEÏVåÚ¬‰ÖiÁrFjKÊAžf´¯‹r€«óåàŒYƒÕÊãKÁXrPŠÕŒõõ©ë r`,…¥:·^Ü~B,±š_¹MæÐVnÓÐh`)˜:7¾ä XñUççXßÏ6 ídûÿ‘ƒvrX ‹Î/9È_ã¾ÚÁÕ„¿&V€ezFÁÔ¹é´å Xõ¤¯X‹ua‹puI©=%VN€¥sS0ÅJÊA±ê9_ñ¥ÍýMBV\5xq©Ý+ÀRXÄŠ/9È_ÿÜX™zS ° øú¥R{o©="ÀRXÄŠ/Wp‰_ÍÿÄÂ÷o”ÚÜR{m9‡b¿¬ß)µ¯—ÚÔ €°±âKŠ_r`,ɪ°,9(VbÅÀX’UaXb%ÅŠ/9°–Â"V|ÉAžø’ƒ `IV¾ä +±â `,ɪ°,9(ÅŠ/€°–Â"V|ÉA±âK,€¥`*,|ÉA±âK,€%Y€%'þÉ6yœ_j¯ïûÙ&ñE¼¿W¾ß&ê…:°–dUX–lágc©½¹ïg_G’í µw–Ú¯–û+±R/ÔA€°$«Â°ä`ãŸø¢Ýy¥öö¾‰¿öæ eóE»ê…:°–dUX–lð3Xj÷µöý‚ñ…¼O‰_ê ÀX’UaXr°ö´àúÚûç-V|©ƒ `IV…`ÉÁs´¿¹M¸ŠöêR{T¬øRVÎ:räH2wîÜq÷íÙ³'J’9sæ$ëׯOŽ;°±â«x9øòâõ¾Ú|±âKX9ª¿¿¬UjÕªUÉþýû“³gÏ&gΜI¶mÛ–,[¶ `),bÅWñr0¶Ÿß!`½Q¬øRÖ$ÖDš={6ÀRXÄŠ¯âåàë:„«¾2 ‰_ê ÀšjÀ:pà@:ª°±â«p9Ø—S+¾ÔA€5•€õøã'K–,©¹+LÖ*5<<œ®íIPü›Ý.bçVùþÚ½]¤dÍÃOÑâ•ùÊ#^EËÁ¼âU¤Xå•S6‚õ õB½˜Yñê–zѳ€µeË–t4jttÔU„ÎÜÄŠ¯bæà§ûÎݱ½ÕöV±âK4‚5E€ô ÝmÓ °ôš¯Ü¦”rjbÕ¡¯ÿÙ×ÜîíÚgÔ ¾À*ëÔ©SÉÆÓm*iùòåÉîÝ»;¬}ûöM8j°±â«9_Üüž®$¼ Ôž+¾ÔA€UÖí·ß^s›…§Ÿ~:¹üòË[Þ¦¡òujÝßhÀRXÄŠ¯iÍÁ'KíÂ6àêçJí¿Š_ê ÀªPŒ\ņ ±WU5üÌš5ËNî’•/98³rðÛ-BV,lÿpßϾ$Z¬øRVå¾TWÕS|/¾øbº;À’¬|ÉÁ—ƒ‡JíÝå‘©FpõÊR»®y¸+¾Ô‹X×]w] 2Ï=÷\ X[L·Tˆ¯¹X’•/98csðÞRûhÕˆV@Wlé0Xj#b¥^¨ƒ«Ž{챺k¥š½ú`)˜ _rP¬ø’ƒ«JO>ùdrýõ×§S‚±î*® Œ+§ZKaéùXÅÔÓ’R{s©W5Õô«¥¶AÊA±âKö `EKaééX •Ú«›X4ý†Rû¾”ƒbÕÒÏKíÏJí?—ÚkÊŸ¥ø¼}¤Ô6–Úÿ“ƒkŠ«ÞôàD[*,SaiÁÓ¬R»¨¯µ/ ¾SÊA±jê'¶×xg©]ÜW{³®×—Ú—ä ÀXKçÖ;±ú£¾ö¾‚%.ÿÿ¶äK¬þü¯R{U“Ÿ©­OõýlSY9°¦kŠpÓ¦MÉ3Ï<°¦8Y{ñ«Wfta9Ô×Ù—¿¡¹Î@§-gd¬žn®*!ëwúšÚZc2|¥MûZÔåQÇlÄñ¼ò‰èÇÊWïö2`=ÿüóɪU«–‚ÉW'žnéëì „cñûr/±ªùóŽ6?Wo,µuSï+–,XP¨÷Ór®(¯;¯¯ñ7#Ä÷‚Žô(`Å~X6U0ùêÐÓ¯ä0šø[]ƒ±Àx{© ôýËžQ1²ðoKí6 ŒÕ‹Iðõ­?WOœ—+çœÝâZÔ¨!Ûz °®î½÷^€¥`òÕ©§ r¬ žƒûJím ¼žW.ªkä z‘£¯ßêðsõŽòÕ…kjrðó}í}ÏgŒv=Ôƒ‹Ü«`)˜|µè)¯5qEÍÁíåiÌfíÿG ŒÕ‹œ|½ºÃÏT¬ýùm€5ekQ_ÓA¬Þ6qÝèÀŠ‘«uëÖ%£££KÁäËVíŸÿÛÆãW•!ë´ä©'/¬)‰Õpù«Ý8Å…B_ï¡5X6í¢‚™m°· âеŠýO}6Øë…5X¿^Ð|GÚä OÅX9Åê½9ÄéS]Xí}ÕÓû`+µo”Úü¾Ÿm<×W¾Êë÷Ê÷ÿ¨Ë æ“åä}g«ÕÞ\^̹`MK¬Ö¶¹î rã+ôuoùJŸv}½~ꬬórè´_ °¦$V¯Ì!V¯XŬåËsßYcjãƒå‘‚_.µ¿ê’‚ùíò¼ö«š,$Ö¿Ly¬ž,ç\»Eå5Í@N¹¯OvX,ß<õ ŒVÖ;ÌÁ÷•O¬ÖŒm4E8Y€uº 7À€¯·öµ´ÑÙ´ÌCmì¯ôúòq°þej=}¢Ô.i£˜8Ô×ës(–¿-mLÜA¬þK‡ïù¯ëXF°êøñãÉÐÐP2{öìÞÁúT‹‹q/*Ÿa?UàNû×ÚLÒèè×u `ÅèÏòR{wÅ4þýÅRûÃR{¦KëPܾÅbÒß O‹¯<¦g.XÒWû M[¹"£Èë_¢pþû Fè^UŽë5!Ó>Ðþþ&Gû[[Ø‹ÛO¬I¾(æ÷+>cq¥êo–×ûuÓE1+Ú¸R7jç[Êu4X3euZ+ h÷îÝ) &gΜIïß»wor÷Ýw·¼¦«RÛ·oOvíÚ5v{çÎéwN`ëp*#@äñÌÁñ’¯ yS ÓŸ1*ôñôTþ|· X±»ù‡j\¶üær¾~¡5hœ_ç÷uÝc€UuQL­ÑŸW–/ÊXÝEÅ|ªÈŠ‘á·7¿`å¸õµÓ;(0-€SƒW¡ùóç'‡÷X«‹ç+µtéÒt 2Ó±cÇ’… N`ÝÙÁrÖ°`þ|Û' X0ãCø†6böúò”a·ŽÄTÙ¬ö·Ô°®`5}òrq_stütú.ŠéÕœÑ;¹¤ÍµX•¯ÆîÆÜcýULï…Ö®]›lÛ¶-ýÿÁƒÓiÃN+FΧ «ïËÀ*k•NßÛÈÈHz€âßìvSAýx°·7ØÊ÷×îí)[ÿráÄžòðÓt¼~\^Ñ.<^ÔÜš¬ÌWñ*Ú^lyÅ«)_+:ÌÃ_onq^ùWDÀš²z‘M»·s¡Åo5Q̔֋z‡Ë#t•k6cðÏ471™õ"¯Û`åý´”ƒoíkí»_W¡l2½ßi¬‡z(…¬ÐÍ7ß‚ua òJë_¦Äן•«?×ÍÌÆÓâëP‹…²zñ¯Lýã=‚õá¾ö¾Uàå M\ul+¯QÔ7”×À5331+¿QÔiߦáå—_N!+¦-Z”>|xJF°& °zõ{àòXÿR´+¸~¿|vÓ‰§·¬)õ5ÔäÖ'Õg¤Ó´ÀxFïÅÖÉÉæ«ûl °ò_‹WÂÿ› ¼£\WÖå{Ó”V,l饗r´jÀZ¾|yrâĉqk°®¼òÊ©¬W÷èV§ ÅvnÁ fû*°¦Ü×` uQy‹iZ`#ceSX€°¦w‘{ÀÔ½÷Þ›,[¶l ´Zݦ¡_•óÕ6!+Ö9ýfÁ;·O¶¸gÔ ¼þåÂp_¥°–¬6Ûb°V.zê©§’k®¹¦ÐßEX¤èòÜŒ®åd]Q¹9o‚áý_)øú—NG®¼h`éÜÖøŸ_ΡÆ}L¬Àj['OžLî¿ÿþqÓ…=1‚•”/õœÓ÷³¯"ifľJ¡0ë_¾PÞ_èWÔňÕe¥öèô0 °Ö4Åê‹®E­5¾&V `µ¤Ó§O'<òH²nݺq Þc VL6ZÞU€•ý|© Yÿª ¶ÞZ¾<ôãåý9ѹM[Áüd_ëû|Eç±æÆJ¬ OÀ^×`½³Ï¦° `µ¢lCÑê«[Ý\´«+›ŠŠõ>óûþåkY^[áúz_Ó»ÆêÜ&ÉWŒ6þǾ־¼uËÌŽ•Xþ¬mr¿º½¦Ï¦° `µ»V@ÌO<‘Ë>X]X:·îèÜV—‹ûù À*F+9°šú‰“È7õµö%ê;Ä `¬–uß}÷¥_S$,Û9£ÿ£Ô>Ú÷/_õ öã hï0Ú(ùjÙÓÚòˆ}£«ã¤æ—úl °V/ `éÜÄŠ/98Å —Ú»«.й¤<Êõ¿Å `,€¥sÓ¹‰•”ƒb°ÀX ¦Xñ5}9X”&V `,€¥sãKêÜtnê…XKç¦`,±Ò¹©rP,€¥`Š_:7±’ƒr`,_rPç¦sS/ä ÀX:7`éÜtnê…X `)˜b%unê…”ƒ `),‹/›Î/9°–ÎMÁX:7›z!V×Ö±cÇ’o¼1LÛêÕ«“^x`)˜bÅ—ÎM¬ä XíjñâÅé—JŸ={6m»wïN-Z°L±âKç&VrP¬v500‚U¥fÏž °L±âKç&VrP¬vµgÏždë֭Ƀ>˜¼øâ‹ÉÞ½{“ 6Ô«¬Ujxx89räH222’ ø7»]Äέòýµ{»HÉš‡Ÿ¢Å+ó•G¼Š–ƒyÅ«H±Ê+ÿòº[‘ÞzÑ=õ"¯Û`åýtK½è9À ¨Z¹re U JIë±Nž|xìö®]»’¡¡!€¥`Š_:7±’ƒr`µ«Zë­¬ÁR0ÅŠ/›XÉA9°:PlËÓ„¡Xì¾eË–š–‚)V|éÜÄJÊA€Õ¦FGGSÓq5a´X‹e –‚)V|éÜÄJÊA€e'w_rPç¦sS/ä ÀX:7`‰•ÎM½ƒr`,S¬øÒ¹‰•”ƒ `éÜø’ƒ:7›z!ÀÒ¹)˜Kç¦sS/ä ÀXKÁ+9¨sS/ä XKaX|éÜtn|ÉA€°tn &ÀÒ¹éÜÔ 9°–ÎMÁ+9¨sS/ä XKaX|éÜ_r`,›‚ °tn:7õB,€¥sS0–Ô¹©rP,€¥`Š_:7±’ƒr`,_rPç¦sS/ä ÀX:7`‰•ÎM½ƒr`,S¬øÒ¹‰•”ƒ `éÜf<`©‰•ÎM½ƒr`,SÁ,`±+›z!V—Ö³Ï>›¬Y³&LÀR0L€¥s+9(V:zôhríµ×&4‚¥`*˜Kç&Vr°s°–Iô`mÚ´)yâ‰'L*, &ÀÒ¹‰•ìÑšÑ žz°.\˜BÖœ9sÒéÁ¦ÑÑQ€¥`*˜Kç&Vr`¬v5kÖ¬dÿþýÉÙ³g“3gÎ$[·nMÖ®][¬²V©áááäÈ‘#ÉÈÈHz€âßìv«òýµ{»H…%?E‹Wæ+¯xuz; K‘ÞOÜ.R¬Šp<Š/õbfÕ‹¸ÁHQÞO·³ÏÕdþ½ž¬Jh Áš¡g¤½¶,£beKÍ0‚5-Z´hQrüøñq÷Í›7/7À²‘¢°è´À’ƒêàŒ¬;w&›7o[wuàÀäÎ;ḭ̈L…Ea‘ƒ `ÉAupFVè®»îJ¹ÇÔ`ÀÖéÓ§–¢°,9(VrPXEÜÉ]ÁTX9°–TÀRX€%ÅJªƒ `)˜ À’ƒêÀ’ƒ `)˜ ‹Â"Õ €%ÕA€°…`ÉA±’ƒê ÀX ¦Â°ä X,9°–‚©°,9¨^,9°ÀRX9(–TÀRX€%ÅŠ/u`,SaáIªK,€¥`*, ‹¯™XEir`,S禰ÈAõB¬z¸^¨ƒ `)˜ À’ƒb%V|,€¥`*˜<ÉAõB¬Ô €°LSa‘ƒK¬O `)˜ À’ƒb¥^ð°–‚©°ÌXÀr—z¡^¨ `)˜ ¦ÂÂÀR/ä O `)˜ ‹N€/õBò°rÐÃ?œô÷÷,SaÑ ð¥^ÈA¾VIÖ­[°L…E'À—z!ùXyè¹çžK†††’S§N,SaÑ ð¥^ÈA¾V§M–/_žœ8q"½ °\Á¥°èøXr/€Õ¡n¸á†äÙgŸ»]°*;õJ 'GŽI§ãÅ¿yߎÀNæëOÇí,Y{ÅOv;|õ’Ÿ^Í¿J_â5¹·‹Xê…ú^äzÑs€@U«Áâ©|Åû)R+#XF°ä O3t›S„>€|oÙÙ•Xñ°ä > `,ÉÊ—&Àâ `,ÉÊ—,±XK,;¹,žf„¯X„)V|,9¨ÏXK²òeKÚÖŶ.|,€%YùÒÄŠ/±â `,€Å“,±â‹'¾ä ÀX>€|Yƒ%yâ‹/€°$+_F°ÄŠ/±â `,ɪ°Xƒ%V|ñÄ—XË/#X|ñÄ׸÷䫵ÀRXø²K¬Ô ¾Ô €°|L#XbÅO½í«kÀX’•/MòÄ—°–‚É—³Q±âK¬Ô ±X‹'k°ÄŠ/žø² `,……/g£r'¾Ô €°L¾4±âK¬4€°|y2‚%V|ñÄ—,€°¾¬Áâ‹'¾Ô €°$+_ÎFÅJ½àKÍXˇPÁÔÄŠ/žÔ ±š™€µgÏždhh(HæÌ™“¬_¿>9vìÀâ‰/#X|ñÄ—š°ÚÕªU«’ýû÷'gÏžMΜ9“lÛ¶-Y¶lÀâ‰/k°øâ‰/5`å©Ù³g,žø2‚ÅO|©+/8p ÕX<ñeMÅt¼§¢4±âKX¹éñÇO–,YRs V€UÖ*5<<œ‡fÄÿæ};;™¯?·³dí?ÙíðÕK~âߣGö\þU~®ÄK½P/ò½ýw÷wêE·{°¶lÙ’ŽFŽŽºŠÐ™_Ö`‰—Xñ%`uª ÇXèn›……/k°ÄK¬ø’ƒ+íÛ·oÂQ+€Å“5b¥‰_r`µ þþþš `ñÄ—,ñ+¾ä À²“»dåËz 9hý _r`,€¥`:+ñR/ä X,€Å“5b¥‰_r`,……/g£F¦û8Ûå :(^ `9pæÆ_|Á/±XÖ`),š¦iê X,#X ‹37žøâI/€°Ìg[{À_|Yƒ%^ `͸³ ŒðÅ×L÷¤ÁXÖ`iš¦iš°¦VgÏžMî¸ãŽdýúõ)wE÷Uï³ÓŸ«™æ+Fâb4èÔ©SMV‘s°]À*’'€U#8Qô²²ߢÝh*£Z'OžL.½ôÒ±³ö{î¹gì±Ç{,Ù°aCúÿ-[¶$wß}wàÉöT©˜B»îºëZzn<Õzoqœ|0=sÙ»wïØ{éÖXÍš5ëh ì¶X…b$.ÎRC­|q{7ù갊⫞§‰ëÖXÅßüó?ÿótʰÛ}EÿTù½½­Vs0›ò‹¶`Á‚ôïþô§?í*O«NW9lxÌÕ·2…ÇS¿üòËã:•l„âŠ+®HNŸ>=%ž*uÛm·%?þxËÏnOµÞ[@ÕÊ•+ÓK|ãì#>XݫȵâŽÜ‹÷ðä“O¶XE‰U䨒%KÆ­©höýw“¯N«¾yjôX·Æ*›Î‘Œ˜†ïv_7ÜpCºn¸™<ìÆŒ‘ê&O«`UŸig\õZ‹Z@^~ùåçü~3ÏlÀŠ…¸ÕóöÝâ©ÖëÇYÛáÇÇn˜Ä0y7Ç*ÎF³…Ÿ1{õÕWw•§8+¬ùm4‚Õ;Ų|5òTë±^‰Utª±f3NкÝ×D拏s°›ûa€UãÇ"éêÅqóæÍkdbñnÌéft\=Ý“)‚_ï±É¬è¸³Ô­<·žj½·Zë'ZYSQäXeÚ·o_zõ]·xŠ…¶1\­Ê)ŒPœ©Æ™e·ûêtk:}5òÔŒßnŽU¦VF‡»ÅW«#XEÍÁj`ºì²Ëºª¶¬ÁÉ“ÑtI½Kå+ŸPöÐC¥k¸²çÆôb¼^6¿#HÙÜo\-öï|gÊ:íé©·Þ <ÕòÛ2Ä4av†“ u{¬2=ýôÓé•’ÍLÏÁSÀ`½³Ð;v¤ï¥òs£sÝî«]Àšn_<5ë·Ûbõ"®Zñ“Ÿü$]ƒÕ-õ¢Ý˜tkV^ap'™ßøÆ7ºª¶Û¦¡ÎqÐcz&În¶êÍÑfÏ‹iÅ ëZûoÄ»8S‘•+VŒí» ö"ibš$þNW/4ò û·û·M¿FQ<5ò XG+Z|h­Áê–Xe~"f•k+ºÑSæ«}°ºÁ×D±,ª¯f§•ºÉÓDï=öˆŠÏT¼X3U®Íéæl°º!ï¿ÿþt¹G¼ÏeŠ“çn곌`Á""""XDDDD‹ˆˆˆˆÀ""""XDDDD°ˆˆˆˆÀ"""""€ED=¯Ê¯Üˆ¯Å˜;wn²råÊäppˆ`uX™â»Æî½÷Þô¾øþC""€EDÔ!`eºõÖ[Óû+G²zè¡ô e³‘®°ãǧ_Š¿¿cÇŽq¯ñÍo~3½ÿðáÃ4,"X?üáÓû—-[6v_ÀÕ‘#G’³gÏ&»wïNû^~ù庢:u*ýÝ“'O¦ˆÅï,"šñ€P÷Ö|^<>kÖ¬ªB7ß|súû1Êzøá‡ÓÛ6lp‰`ÀªX1JµvíÚdþüùéý•ÏÝ¿úÿ5kÖ¤·cú0n8pÀA&"€ED+tèСôþ¸¢0”PmÛ¶-}ìÌ™3ç<÷Ê+¯LGµFGGÓ‘­Ë/¿Ü&"€ED+Óm·Ý–Þÿï|'½ÀTý{ÕϽçž{ÒÛëÖ­Kÿݼy³LD‹ˆV,R«ã¾€¬L‹-Jï;xð`z{ïÞ½ç<÷èÑ£ãöÕŠ« ‰ˆÍXÀÊ63gNze`¶X=Slµ°dÉ’±…íwß}wÍѯU«V¥÷]uÕU.,"¢<´sçΰ¶lÙâ`À""ÊC×\sM Xÿ÷ï`À""êT±.k`` Yºt©ƒAD‹ˆˆˆ`,""""XDDDD‹ˆˆˆ`À""""XDDDD‹ˆº ôõi97"XD°Ç“ˆÇ“ˆÇ“ˆr<‰` p<‰` p<‰`QÏÁM7ÝT·UëäÉ“Éí·ßžÌ™3'HV®\™ìر£å¿ßßߟ‹x­[·NÚë,"XDÔ6`% œÓjÖš5k’íÛ·'gΜIΞ=›<õÔSé}Ó¥©eË–%Ï?ÿ<À""€EDÝ X³gÏNÁªžb4éæ›oNn¹å–ôù£££cÀsêԩ䪫®:€ê=ç‰'ž(½éHY<^°:” Õ¬xxßóçÏO¾ûÝïŽ=¾yóædpp0¹üòË“ï}ï{éHܼyó’+®¸"ùë¿þë ßÀ""€ED¹Öõ×_Ÿ|ó›ßL7C† Á0€ÁÀsƒe˲²2 V,[¶LþéŸþÉé˜]uÕU~i°ÌS{Tæ²¼¼<¨?/ÊD¦§§S‘ƒ€9ëg?ûë+¬ZµÊíqëMËÊ`5Yî¼Ï ¶ÿû¿ÿ+‹¥ÓßÕòKä½÷ÞÓKcŸ?$$D^|ñE=nŸ}ö™6`ßÿþ÷ƒÚ`Yñ—¿üE""":Ývå•WÊ©S§0X`°À`Ù;P8«`u¥Rc{›:Ϙ1Cçx”iQFåСC¦?¦#>,'N”믿^›Æ¯ýërÓM7Irr²6G® î×ñ¹o¹å9þüe÷SKI·ß~»Ó×1uêTùæ7¿©™””tÙsLžµÏØÛw¶mÛ†Áƒ€ï –âˆ#ä‹/¾0Õ`Y¡r<ŽŒƒY‰‹‹³kŒÔ·ãßÕ2“3¨%¦®Xm5©eMÕþ“œ›­VÛûÛþ•£³·ìé­\›=mžŽ»íÿ©««Ó¯ªªrû6µ”ª`ÛJÁº”üúë¯wú»Z.Ä`€ÁÀ«Ë •+RY§Ž·© Š7 –ímÖꇙQÁkwŒ¤+“cV˞ɰ}}*Ç¥2TÊ(¨%EÛÿíÉøzÛ`ÙV°TΩ+ãîèÿ8+G·) ¶™4{÷·jÅ`€ÁÀëËj²l³N¾0XMŽYqÕ³Ê݃¥mîÇÝ ´§ã¥Bì*Èï‰Öž4Xj™­ãí¡¡¡]w3öëmΪÝ=Cƒ €.(ÚÚÚ<6>]9@:ª~˜ùÛj†½3ÿÜ:Û¯ãóÜvÛmvŸK%¨ñ]¯;ï¼³Óß,XpÙûáOË6P¿bÅŠ.»™‹ ,üÎ`ýío“Ñ£GwºM"o…mu@å‰ZZZºt€|ê©§fbÌzŒº|MÇ¿]6ÊÀ¨¶Π.cûüê1û÷ï×·«œš:‹íꫯvËH9ú»£ê‹˶ªÔýDeálÇVu°·f¿<w3 –mKµŠ°÷’Áƒ€× –3ªžCVüüç?÷x¹Íö6ujþÙ³gõÁÕ^xÙ¬Ç(¨%·ŽS@ÕåTEEU4vïÞÝÞÀT;³:¹;ú»mvIÛP|W –ÕüYùüóÏ›ºŸ¨3=­ðtÜÍ4X¶Ë–êìA…®žEhÆ3,0X¨*®L†:Ì“ƒ £³Í~Œ%%%—uaïêÒ¶á¨=ª`zW –mK33XŽzCuw?Q¦Ð¶7—§ãn¦ÁröZ»Ò ƒ €n4¬¯Õ2‹: ÞTIÙ¦žãÇ×Õ%w‚ª{¹2mêñjÉJu(·m´iÆcl5´¶¶jó¢2NÖçQúUÏ)Õ9Ý^‡tGP§ý«Ç¨Nâê9Ôs)“¡4ª*5óÓƒ¥*/))ER[Ûè·cÿç?ÿƒhëÅ_ è/‰@ÖÏØ£ýèVýõõõ²fMžLºÏ02'L3DŠáá›ú|Š´ÊÌ™5²aÃfÙ½{·ß=+ „Bh÷ìÙ#eeeòä“å¦V­|Ř˜dÉ‹477ûÕ¸b°¨`¡Ÿ±G?úÑßKõïÚµK/ Λ·Óôª•·+X9hÐ_eþü2©«k¤‚…Á"G€vô£ýèï9ý;vì dÆŒý^¯2™™ÁrœÍú@~¸JÊÊ*É`a°˜…¡ýèG?ú}¯_™«¬¬2aÂ+>YÆóv«c>1±Þt“Õ++XjÍ5$$Äáí/^”ÌÌLIKKÓIñÌ™3.o#ƒ!„0©–ssóeÒ¤—.oå®Éš5«^ÊË+{tœÚ`ÿüs§kòäÉrêÔ©öí“'OJll¬ËÛÈ`¡Ÿ±G?úÑlúU!!5µÙçU%_d°ìe²þüçM²mÛ62XîB-ãM›6MNŸ>­·¬ðððË– ­sv,ô3öèG?úƒI¿ês•ž^fã>7;¾¯`Yÿo«,[V¨3gT°ÜÀC=$Gmßvf°ìÝfý›³Ûl••‘œœ¬]­uàÕO¶Ùf›m¶Ùö·íçž{Nrr6ɰaïµUU²Ÿ`ÞŽ‰9(ÙÙ›äðáÃ>ÿ€4XÊÙ#,f‘hG?úÑþËsWùùù2}ú¾ ž÷TËÊl–ŠŠ**X]1\ŽÐq)Ñš³Š‰‰qy,ô3öèG?úƒAmm­,^\áÕF¢þ˜ÁêœÇ:.?^Üånï½¶–­Áê¸m±Xt¨¯ã™‚ééé.o£‚…~ÆýèG ëW-Ö®ÍíñKàôtKqذ×$;»DWô¨`™`°èƒ!„°·²¢¢Â8V5e¿«®ðÑG+tØŸ>Xtrg†vô£ýèï’~uæÜêÕyþA¨`Y¯Y¸jU¾ÇU,®EȵÉA ýèG?ú57oÞ,sçîò cÓÓ¬Žœ;·Fª««É`a°˜…¡ýèG?ú=Ó¯*4k×n”AƒÞõ Sã/,ÅÈÈ7Œ±Éó¨ŠE«—,!„ÐÕ™ƒ?^KîÊ.,‘ÆÆF2X,f‘hG?úÑ~÷õoܘ'qq‡üÆÐøSKqôè}RPPL ƒEíèG?úÑïž~nOOÏ“°°~chü)ƒuÉð½'Ë—oÔm,È`a°˜E¢ýèG?ú]²²²RRRüÌÐ|ìwË„óæÕJMM , „Bèšê²8#F$kå‚qq-R\\J ƒÅ,íèG?úÑïú~éé$,ì*X.5µÊªUyT°0Xä ÐŽ~ô£ýÎïÓÔÔ$K—ú™ñ· –•K—–º•Ã"ƒE‹Y$ÚÑ~ô÷býuuu’œ\ç‡Õ¢ýôÒ9n]:‡ ,!„½˜ª{ûƒ>CÆÊMNŸ¾WÊËËÉ`a°˜E¢ýèG?ú³¨¨HbcRÁr“j¬JJJ¨`a°ÈA ýèG?úŸAñ2XnrÀ€#’›[@ ƒÅ,íèG?úÑï˜ëÖ妡• –gªëRÁÂ`A!„NZ4dKXØqòUnRu»WcF ƒÅ,íèG?úÑïË–mðÓJÑÇ~k²Ô˜QÁÂ`‘ƒ@;úÑ~ôœÁò× –»‹ ,f‘hG?úÑO‹ ¬®¡ªªJ$,,ÌxÓÂeÞ¼yrìØ1»÷ ±KW·‘Á‚Bh\¾|,2XÇÝ2X½&ƒ•””$---rñâEÍêêj5j”[=uꔌ;¶Ý`QÁB?c~ô£?Xô¯Y“ÇY„žE¸z5g:E¿~ýܺ_zzºìر#  9´£ýèG¿=æäÐËÞwßë1#ƒe.\ÚÚZ½dè Ç— &tZ>TÆL1::Z²³³åܹsT°ÐÏØ£ýèHýª“û!‡©`¹É!Cèäî4_¥ŒÏÙ³g]ÞÉ’%²wï^‡K‡Ê`¥¦¦Ú5VVvDrr²vµÖW?Ùf›m¶Ùf»§¶Õ…‹§LyVU5²¶íoO™òœ¾¡7Þ€¯`?^‡Þg̘áô~­­­ïô>*Ï¥‚óT°ÐÏØ£ýèDýjEçᇨ`¹Éٳ륮®Ž –383F )))ràÀ§÷ikk“ÈÈH2XègìÑ~ô¤þmÛ¶ǪR2Xnò±Ç,ÒÜÜLËŠÙ³gË¡C‡tÅéË/¿ÔŽ]™ŽK‡qäÈ»­Žg#*s•™™)999T°ÐÏØ£ýèHý{öì‘åËsõ%`¨`¹>ƒpùòz̨`}å6•9RáôˆˆÉÈÈÐÉ‘ÁR÷}ã7.{ž††ILL”ÐÐP‰ŠŠ’¼¼<®E!„0 Y\\*11¯ÑçÊxàe)((òÚû@'wú`¡Ÿ±G?úÑDúժάY;¨`¹àÌ™Ût†Û[û‹>XègìÑ~ô‘~ÕëqÑ¢B¿Z&ô· –›´´B=VÞÚw0XT°ÐÏØ£ýè2ýk׿KDÄ[T°pèÐC’•UâÕ}ƒ€ B!tFµô¥–ÀÈZÙgRR­^Jõæ{€Á¢‚…~ÆýèGéßµk—,^œ'aaPÁ²¡ºVã’%n=H‹ 9´£ýèG'•ʨQûÈ`ÙpâIJiS¥×÷ ,ô3öèG?úƒP¿j:š–Vl˜ŠT°Úu¼gŒI®ðy{ßÁ`‘Á‚B¤ÌÏ/”Q£^$wõÇß)%%>{ ,ô3öèG?úƒTSS“®b……ïõ¬ŽÊâÅŲ{÷nŸŒ=‹ ú{ô£ýA¬_uvŸ2eO¯Ï`=ø`­ÔÔÔùlì1XT°ÐÏØ£ýèbýÛ·o××Ü4èí^[Á:ôYµªÌ£3©`‘Á‚B²¦¦F’“Ëý"ðÞÁö”‹Û]ÛÉ`QÁb‰vô£ýè÷ ð^,ãÆ=×Ë*X'dæÌ*©¬¬öùØc°È`¡Ÿ±G?úÑß ôïܹSV¯Î•!CÞê5¬1cž‘õëËzdì1XT°ÐÏØ£ýèï%ú·lÙ"+VÊ Aï}+:ºEV®,êÒYƒT°È`A!„±¾¾^žx¢´Ç[7x“‘‘¯ËÒ¥Å7%ƒE‹Y$ÚÑ~ô£¿Ë¬¬¬”… + “u"è*XoÊ¢EEúìÉž{ ,ô3öèG?ú{¡þŠŠ IK«òI%ËW¬Áƒ_5Ý\‘Á¢‚Å,íèG?úÑï±ÉZºÔ"ýû ø Öðáû×b¾¹êU¬ªª*IHH0\w˜ñ¦…˼yó䨱cvïb— /^”ÌÌLÃÁ§ió¤xæÌ2XB{ Õ15=½XbbÞÐÌÕ ‰‹{FV¬(òy¯« 3XIIIÒÒÒ¢ ’buuµŒ5Ê¡Ár„’’)//oß.++“ŒŒ *XègìÑ~ô÷*ýÛ¶m“uëreÚ´ª‚¥šˆÎœY#99e^ ´÷ê V¿~ý<6X“'O–S§NµoŸYùùù²pa½a\Þ÷û Ö!eÁ‚R)/¯õ˱xƒuá©­­ÕK†Ž –2_ŠÑÑÑ’-çÎÓ·©åÅŽPÕ0Û¿QÁB?c~ô£¿7éWK†™™ù2cÆ‹~YÁ0à¨LŸÞ «W—ùlI°×U°¬y*e|Ξ=ëòþªZ¥ VjjªÃê–½¿YóY¶+99Y»ZëÀ«Ÿl³Í6Ûl³èÛ{÷îÕñ›ŒŒÍ2fÌamTÊj”zb;"â#™}¯DD1¥‚¥Î=ºEy¤Q/Î3ŽÕEúø® ^ Ž}@¬ææfmœTp=""B·VPæÉžÁjhh0Üv¢„††JTT”äååuZ¤„BèUKe~JKK%;{£¬^] ‹UËüù»eêÔ>ü-]å²í¯.ÍÞ*C†¼%ãÇ·ÈÌ™OIJJa¨ŠcùF)**Ò±êgÕ«ú`ÑÉYÚÑ~ô£ß¿ Wcc£î ¯V‡ õjÑÚµë ã”%kÖä蟫Veˆ u;k/ʺº:݇Ë*Ud°¸!9´£ýèG?ÚÉ`a°˜…QÁB?úÑ~¾;{~ V,!„’ÁÂ`1`†~ô£ý|wRÁÂ`‘ÁB?c~ô£ýh'ƒE‹™ ÚÑ~ô£íT°0XB!$ƒ…Á¢‚…~ÆýèG?ú{ ,ÖâÑŽ~ô£ýh'ƒ…Áb&€vô£ýè结 ‹uf!„b°¨`1“aìÑ~ô£íT°0X¬e£ýèG?úùî$ƒ…Áb&À, ýèG?úÑO ƒE B!„d°¨`1 C;úÑ~ô£ ‹µlrèG?úÑÏw', ,ô3öèG?úÑÏØ÷ž VUU•$$$HXX˜„‡‡Ë¼yó䨱cvï[]]-‰‰‰ú¾ýû÷—´´49yò¤¾-$$Ä.É`A!„°×e°’’’¤¥¥E.^¼¨©LÔ¨Q£\Þ÷Â… R\\,ñññí‹ ú{ô£ýèG;,èׯŸÇ÷ TƒEŽíèG?úÑv2X^…ªJÕÖÖê%CwpàÀ]Õ²,e¶£££%;;[Î;G ýŒ=úÑ~ô£½÷V°¬™)e|Ξ=ëòþ{÷î•I“&µg°:âÔ©SÚ`¥¦¦Ú5VVvDrr²vµÖW?Ùf›m¶Ùf›m¶¾‚uþüyzŸ1c†ÓûåååéŠÓ™3gÞGå´Tž ú{ô£ýèG;,ÎŒÑÁƒuÐÝÚÚÚ$22’ ú{ô£ýèG{ïË`Íž=[:¤+N_~ù¥Î`)³ÓqéЊýû÷;¬Zu<ÃP™«ÌÌLÉÉÉ¡‚…~ÆýèG?úÑÞû*XÍÍÍÚ©pzDD„dddhƒdÏ`9ëuÕÐР{d…††JTT”^FäZ„B!ì•}°èäÎ, íèG?úÑv*X,ֲɠýèG?ßd°0XT°ÐÏØ£ýèG?Ú©`õ"ƒ!„B2X,fÌÂÐ~ô£ŸïN*X,2XègìÑ~ô£íd°¨`1“A;úÑ~ô£  B!„d°0XT°ÐÏØ£ýèG?cÁ"ƒÅZ<ÚÑ~ô£íd°0XÌÐŽ~ô£ý|wRÁÂ`±Î !„B ,f2Œ=úÑ~ô£ ‹µl´£ýèG?ßd°0X̘…¡ýèG?ú©`a°È`A!„ ,fahG?úÑ~´SÁÂ`±–MŽýèG?úùî$ƒ…Á¢‚…~ÆýèG?úûÞSÁªªª’„„ “ððp™7ož;vÌî}/^¼(™™™’––¦ ’â™3g\ÞF B!„½*ƒ•””$---Ú )VWW˨Q£ìÞ·¤¤DÊËËÛ·ËÊÊ$##ÃåmT°ÐÏØ£ýèG?Ú{}«_¿~vÿ>yòd9uêTûöÉ“'%66Öåmd°ÐÏØ£ýèG?Ú{mëÂ… R[[«— íA-!Ú.Zÿæì6*XègìÑ~ô£í½²‚¢©ŒÏÙ³gÞÇÑßœÝfk¬¬ìˆäädíj­¯~²Í6Ûl³Í6Ûl|ëüùó:ô>cÆ *XÌdÐŽ~ô£ýh§‚e&Ô…ö0mÚ49}út§œULLŒËÛÈ`¡Ÿ±G?úÑ~´÷ª ÖìÙ³åСCºâôå—_ê –2;ö–ù,‹>;°ã™‚ééé.o£‚…~ÆýèG?úÑÞ«*XÍÍͺUƒ:s0""B·Vhkk³k°èƒ!„Bú`ÑÉ™ÚÑ~ô£ŸïN*X,ֲɠýèG?ú{ ,f2Œ=úÑ~ô£  B!„d°0X̘…¡ýèG?ú©`a°È`¡Ÿ±G?úÑ~´“Á¢‚ÅLíèG?úÑv*X,!„’ÁÂ`QÁB?c~ô£ýŒ=‹ kñhG?úÑ~´“ÁÂ`1@;úÑ~ôóÝI ƒE B!„,*XÌdÐŽ~ô£ýhÖ ÖÎ;M¹,rhG?úÑ~´“Áú ‘‘‘’íðö¬¬,} 3´£ýèG?ßT°ÜDFF†„„„h£rîܹö¿«ß,X o[½z5 B!„d°<ÅbÑFjúôéræÌMõ»ú[EE,fhG?úÑ~¾;©`u»wï–ððp>|¸¦ú}ïÞ½„ÜYËF;úÑ~ôóÝI«;¨­­ÕU«@¨\QÁB?c~ô£ýŒ½ß¬çŸ^W­bcc5ÃÂÂdÏž=,!„’Áê TµJU­f̘!gÏžÕ,õ»ú›Êg¹ƒêêjILLÔÆ¬ÿþ’––&'Ož´{_k•Ì–®n£‚…~ÆýèG?úÑ,u† õ,ÂóçÏ·ÿ]ýn=‹Piè IIIÒÒÒ"/^” .Hqq±ÄÇÇ»¥áÔ©S2vìØvƒE ýŒ=úÑ~ô£=àû`©^WÞèƒÕ¯_?·î—žž.;vìhƒÅ, íèG?úÑv*X^ïä~àÀ]Õr…ãÇË„ :-*c¦­› vìÏÕÑXYÙÉÉÉÚÕZ^ýd›m¶Ùf›m¶Ùøkªö“&Mr˜Áêˆ%K–8l¡–•ÁJMM¥‚…~ÆýèG?úÑxgš…¼¼#u2räóùº ôW OþøÇ2pà'ÆÏôö AGäþû_—èèeâÄm2gN¡,Z´Y,–Ù½{w§ç\´Èñsªçsôœ³gÆ«\ ·èçdßG?úùî$ƒ`K„ýúõóØ`Mž«e’ž^+ûöíãýd›m¶ƒv;à ÖÞ½{Ì$‡¬ŽPÕ*e°RSSV·œU¼¨`¡ß´777˺u›$1±Z/ÕyRQrTÁúÃNÈw´ÊÏ~vL®¾úSùÏÿüBóúë?‘ÐÐç £Ukü¯·ºTź\Ã{·K~x“”•5³ï£ý|wRÁò7ƒ•——§M޳`º-T•J…ã=©`‘ÁB¿¿hWæJ…ɇ¾SþÉ]Z3X¶æê–[ŽÊ~p²ÝXÙò¿xGbbjeذÝzÙÏ £yXâã+dÑ¢j÷bßG?úùî$ƒå8xð º{ж¶6ã‹=Rÿ>mÚ49}út§ VLL ,ôû¥öšš™3Ç"ou£zÔ¹‚uÏ=ïËM7µÊ5לuh®:V³Üe˜»mò§?½oZ5kìØ­ÆQ¹ìر›}ýè结 VObÿþýN«V—ù:žq¨Ì•jË““£o³X,úÌÁŽg¦§§Ó ú+*Œ}¹\ h5ÅØ(Þ}÷ûrà ïJŸ>wi®¬¼êª6ãq/ˈu†92KË Ã´=+sç–ISÓÞo!}°z Îz[Ù¬††Ý3+44T¢¢¢ô²bÇ%Aú`¡ßßµWWo5Í\Y+X÷ÜsÜcsÕ‘*g¦ÉúÈ0m{eþüMNMû>úÑv*Xtrg-›A·©ÌÆìÙjYð/¦˜•ÁR™«Ÿÿ\- ~Ú%se¥ªdÅÆ6ê3 Í2Yª’µ`A™Ã¥ìûèG?ÚÉ`a°z&°}ûvÝ1¿´´Tòóó%++KÖ­['ëׯ—ÜÜ\ݤµ²²R· `æ½æ¡ÉÉ›dèÐWL30úÓ)ùå/[åºëNwË\Y—  xJx`—iúÔráØ±Mòä“UÌ‚Ñ~¾;©`a°‚‡;vìÐæiݺlIO¯¹sŸ–Ñ£_“!CÞ“°°uÃÉxSÆIæÏß*Ë–å¬P¶lÙš»ÉLO¯—‘#Ÿ6Ѽ|$wÜqL~øÃºm®¬¼úêÏ Ø ÷ÝwØ4j›2¥RŠŠ¶²@É`a°{& :‚oÞ¼YÖ®Ý`¼®]ñ¾Û‡ØØWdáÂÉÍ-’mÛ¶1 3µµÍ2}z…i-ÿûãòÓŸ¾'W]õ…iK±oß2bD•ig^jJúWyøáÒËÎ,¤~ô£ +`Ö²Uo¥ÜÜ<ÝèþûßíòÒÎÈ‘ûõ5ëÔÒ"9‚îqÞ¼JS—o¾ù]ùÁ>1Õ\Y²O¢¢ž5U¯º¼Î“OÖ‘¡A?úùî$ƒ…Á ¼™À¥®à$!áé.5®´å€Gä‘GÊÄb)cÖE–•í” jMy?þÑ’á„üä'ǤOŸ/½b°®»î¬ÄÄÔIÿþæUþ¾Ì˜Qnì£;©@ ý|wRÁÂ`ÕEw³²reÊs+ꬲ‡.—ÒÒ2·;tÃpö쉊2·zõ‹_¼oJ°ÝûÛC2dˆ¹ûRLÌ>yâ‰ö !, V`Ì”ñÉÍ-„„]¦;š¬yó6Kee5³0;'TUUI~~©,[¶A,Ø`˜ªuòÐCë$1q½Lš%ññeܸ™0Á"#F4ËС/Ê AGºô^„„|(}û^Ê^õésÎkKU±bckLmÛ ªX eÆd`7ô£Ÿ , –ÿ¯e«œÔ£n65Dmo¹ð±Ç / ¾÷Æêë¤Æ|íÚ|ÃPå˨QuÒ¯ßóò«_½%?þñ‡º'UŸ>ŸË•Wþ]þýßÏfè¤Üxã[ò›ß¼$QQÕ2rd™Œ["£GWÛ/èK̸û>ÜvÛq¹þú“_õ÷?^­býþ÷/Èý÷09‹õ”äå5’¡A?úÉ`‘ÁÂ`ù÷L@1¸jUžq ü‹×Ì••<ð¢dg—ôÚY˜kÕ/lùò<™>½^î½÷€ÜpÛ|~ûÛmòõ¯Ÿ—ý× ¨ ×w¶È°a•†áØl˜-‹ÄÄìÖWïÁ7¾«Û)¨ç÷fëÒE¡ß•èèfS÷¡ÈÈ×%%¥’ úÑO‹ Ë¿©ø3fl󺹲ž]8gΦ^Ù'«®®N23såÁëä·¿}]úö=îôÒ4ßÿþò­oµ]f®:ò›ßü¹ùæ7 £Um™9²J""9iÍpB/zÓTuî‹Õ¦[6¸cüÜ_n>!3gZܺ4„’ÁÂ`õÈL@e¯22ò»œçé ‡ 9$YYŽf¦ÆxÓ¦Mòøã¥2`À!õʵ׺¾,Í•W~!ÿöoŸ;5XV~ç;—ÿ÷ÿ^¨¨:ÃhÕ?Ÿ¶›}ºãŽò_ÿu¼ýx»‚¥øÇ?î5Lß«¦îCqq;¤¨h ô£íT°0Xþ¹–ÝØØ())›}f®¬U¬… Ú/«Ì9uf¦º´Ðüùu†:¢—Ýmìùï|!ßøÆ—n,+øÃc2xp½ V'#F4^Ö&á—¿üGþÊ,Å;ï“ÈÈK&køð-í¨nxO®¾úSŸe°òÕ¿ªÑÔýG-i«³^Éu@É`a°ür&°bE Ðêsƒ¥®-·n]aÐÎÂTæ*%¥Nîºë¨Üxãû]2&— Ö….,Åï~÷¬a°jeðà-½S/ÍþìgïJŸ>m>­`]ý'2bD©ûjK1w®… úÑv*X,ÿ\Ë^²$WÂÂŽûÜ`©ÿ¹|ù† Ì¨³—-+–ÐÐ÷äÖ[ßéòÅ”¿õ­¿wÙ\YÙ§ÏßôÙ…*“uÿý-†ÁzÇæ,¿ÿñIk̘2Ó÷¡9s ½¾ï¨J¤êÛ¦z–•––Jvv¡¬Y“gLLrtSXµ¯Y“+ë×çJII‰^öVgȺ{Å2@è'ƒE ƒ¤¬E‹r|n®¬T¨`›…©>Wë×çHLÌ›òë_¿íÖÙ‚Þ4XŠ¿üåU/±±Õrãíô?|QÁò¦ÁòÖ¾£®Éi±Xdåʲpa‰<üðV‰‹kÑK“áá­òªjšj¢;zt‹Ì»ÕøL•ÈêÕ´!³mªË,ýè§‚…Áê%¬'Ÿì¹ ÖÊ•‚n]U0æÏo»ï~O~þóºeJÌ2XªWÖ AÍ2lX­„„<ãÓü•· –Ù>mÜX*iiÅ2iÒ^:äÛ^Ògúô½z ¾¨¨¨Wö}ƒÁRåûÄÄDã &ýû÷7¾ÓääÉ“ß7$$Ä.ƒ¹‚•žÞ“¬‚ š…©Ë߬]»Qî»ï]½RM6»g°º—ÁêÈë®;!öykäHó3XøŒ¬Y³Qï'd€ÐO‹ VÀ.öë×ÏãûªÁên¬„„í>7Xsæl•ªªª š…åååItô›†9"×_ÖƒÕ•>XŽøÓŸ¶ê³ ##}Þ+&Æü>XiiÝÞwJKË$9¹Tg¨|±ßGF¾-K–”ê|— ÂSAA?,*Xe°8 +UžÞW,e¶£££%;;[Î;g×XYÙÉÉÉÚÕZ^ýô÷íýû÷Ë¢E›ô ;<üc8ðýóÒ2Œw¶ 8idòäÙgŸ ¸ñr´­–~V¯.•þýOÉí··¶›U!²šO·¯¸â ùæ7ÏÉ׿þ¿Ú ©ŸVveûß8¯¯YY'?ùɉnësw[]wqذ½¦îOª“{VVc—ß/en,– yè¡­Æs~èÕýýòýÿo²`Aµ”•m–W_}5(ö¶ÙfÛõvÀ¬½{÷ʤI“f°Ü½ï©S§´ÁJMM ê –â† yÆð ŸU¯Fj‘¢ š…©j\JÊ¥pû/~ñ¾)•Ÿï~÷s·¯Eè.CCŸÖË„¿þõ«>«` ¸Çôf¶&¨Ö [»¼ïX$!¡¾ÓÙ€¾=Éã„Ì[«M–»-¨B Ÿ ¬ƒZ¢Q&çÌ™3¦ÜWå´T>˜3XÖ¾M))å>:°| iiEú4ø`ʨ%ŸéÓŸ“»îj•¾}OšfNTÐÝLƒuË-¯†g«Ü{ï>Ÿd°TÐôèòöNòf™“Ù³‹ôI]ÙwÔ²à¬Yµ=f®:¾Ž´´-zß!Gƒ~2Xd°üÖ`~»>ÀÛ,L]ÐùÞ0-å­ û5×üM/Øä“ Ö/~ñ®1.Í&ç˜Tþª¼KûŽ ´Ï›g1Õðu‡j¹pÑ¢¢NÁwªè§‚EËo –Ê9«Du ¯;»oÇ3 •¹ÊÌÌ4ŒGNP÷Á²R5D\º4O zÇ‹g¾¨ÛB¨ÊC°õ7ÉÎÎÕí;î8b˜–ÏM3(W^ù¹|ó›_šf°®¼ò36¬F·kðEÿ«?üaŸ òŠ©ûш{¤¸Øó¾RªÃãø,Ðî.#"ŽŸ‹ÜËZ8@éƒÕãpÕ¿ÊöwG÷mhhÐ=²BCC%**J/#ûY„¶g¦¥•èNÕfD† {QÛ;. Ó,lÕªlý:o¿ý¯¦›3— ÕÅ£GŒ¨ØØMrÕUŸ{µ‚¥ú_U©—…Íì5gNI»I÷dßÉÉ)6^ûó~e®¬øÉ“Ÿ“‚‚"ªè§‚E‹NîÁ”Á²5Y©©%&6=aÔöÊâÅÅvÍU°ä22²ôëýÕ¯Ì7XW\¡Ú5œ3Íd¹ÙxO6Éu×öjëw¿; ÑÑ{M5$Ç?'kÖxÞGJuRŸ7oS\µÀÕY…ÖÏÉâÅ×ñ ú{ ×"ôÐd-^œgÌ^êv3ÈÉ“ëeÅŠR§ËÁ0 ËÌÌÒ¡eUÁêêÅñûßÿÜ´*–ª`Y.qqåÒ·ï{Ò§Ï—^«^QÕåKÍ8ª^Íž]¢—ú<ÝwÖ®Ý$C‡¾âWæªcKq̘—®ŠEýŒ=‹kv!“••U(sæTIDÄ_< î‘Q£vãQ*%%µ}º»Ü¸q£^Z½”Áú»ßV±þýß?“¨6X+·Þú®a„>öŠÁ }Þôê•Ú§²²ª<~oTåtΜ2/ó‘׺½«*–« DCÉ`a°‚`& Z8¬^](óço’qã.õ3RFºԢ~ªíˆˆ·døðç%>¾FRR,ÆL¼Öí0;gºWÅúö·ÿÞíkª³££UÈ}‹„„×]Öûôi3ùÌÁwdôèSÏÔ‹Œ-²}ûöní;‹•év!þj°l3X—ÎÂ=*99|vÑO‹ ‹ ³0ë¥XÖ®Í6 Ì;rûío›~vž#“õo¸^.¼îº2dH£ÄÆVFë=‡UÉR™¬ë®;ëV ýþûwf¨Þ´Ê• £OÚ Ï\ugYÐÕ¾óØc¥^¹27+Xj Ö¬É峋~*XT°0Xd° •‹ERRêå®»Zå†>ðúµþT&Kõ¶úÖ·¾pš½êß§DDl7LÖ—ý{ï}_ľ}?røo¾ù5ªÚàNÓ2Wªüy¥RP`1íz•))Ðÿêr®X‘Íç B2X,*X̬ܽ{·¬_Ÿ#ÑÑoʯ}D®½öSŸ\Tù»ßýL®¸âïv«Y7Ýô–a^štû„?ýé}—Å?üá„îéuË-­rÝuŸvªZ…‡?+ãÇWiCdVÕjòä톩È××5sßQ­DüÙH9ÿeË6ðÙE?,*X,2Xäl›³.YR"!!*8Þêµ3 í-ªjÖW|n­KÙ¬>}þ&ÆÕÉСõ2hЫne€l— Uwú_ýêé×ï7®BFŒxÊ”%Ae¬ÆZžx"_JK˺Tµrµï$'ûwËÑøŠÁâ»ýŒ=‹ V7¨®#˜“SoëŒV¹Ìž]&3g–Hbb±ÁÝrÖ¬ ã¶-òä“MRRÂ,ìÒRaÜu×Q¯Þí-UѺ暓ºkûàÁMŸñ¨‚r©GÖ Ãœ½f¼·MÆ{Ÿ%é陲t©zï÷ëÚ² zÎ!C^3žc»aB Œq*ëtmA³÷ý@Í`ef’ÁB?,*X¬ Ì`©jBqq½±C”ÉŒ•2bÄ:ôe}]ÂÎÕ‹ú€pß}ÿmŒ_ÔÕÉ“+%!¡Ü8(7Iuuð_‡ÐÕRannža@wè¶ }û~èS“uýõgdøð:7®ÜЫѣî”I“Hlì›2`À»—Ô#"Þ6n{U¦OßkÜ·F–/ß(7Hmí¥Ë©îâª:W\\¬ÃüK—–»ýœññ—žsÙ²ÎÏéí÷aáB‹_ŸEèèZžkÖä‘Y +˜*X—ŒA­ñZJdäȧ¾ºÐó O³W×&Œ‹Û!Ó¦•ÉüùõÆõé^7 S•¿ 6ÊÌ™ÍrÇõ™ÉúÑ>’˜˜F™8±Z_C²©i‡466Jyy¹ïo®äääHfæzã@ž#Y²nÝÝÇ«°°PÊÊÊôý e¶=§z>ŵk={Noìûéé×+&æ€1¦ôÁB?,*X¬ É`ÕÔÔKjj±Œ½Ë´eUň‹Û-ññ›eåʆ{ºWå”ÉR•¬¹sëä·¿}[ŸYèÍL–ºÐòˆ5†¹ª”””ÍÒܼ³Wç ,–:½ÿR+!¡9`:¹“aB?cÁ¢‚優‘Ÿ_.3gVÊ AõÊD=ï„ uòðÃ[eûöݽj¦ª6ªÊóç?[$,ì¹é¦w»}Y{=©BBöɨQ52eŠZž­5¥T ¿ºáôéUÁzì±2®Eˆ~*XT°0XžÁRã5kŠeÒ¤f@öæÁD=\Ü.IL¬’úú]ýáê «««%#c£LZ'¿ùÍkòÓŸ—>}þÞ-cuõÕmÆs½j« ÃÀVÈܹ).ÞÒëÆÖÙäá‘GŠu%5òW*ßøä“yZ7ï„d°zêÀ•˜˜hÀäÿþ’––&'Ož´{ß‹/Jff¦¾2HŠgΜqy[0V°”¹ÊÌÌ—Ñ£÷úôTöáß5Þ¯òv“Õ›faªª¤rKK—æÊر[äî»Hß¾È5לõÈXõí{B~ÿû ÃZn˜c‹a¬ò÷²ÜãŒSo˜E._^­OÀ„ Öðá/Ha¡…*ú©`QÁòƒ•””$---Ú ]¸pAŸåo÷¾ê"ªêg… ßfdd¸¼-Ø2Xj†œ•U(&<Ý#e²’’*¥©iO¯Ì(s[QQ!+WæÉ¼y†Ø"¡¡ÏË­·þE~üãôbŸ>Ÿëký©3•¡ºõֿʽ÷¾hŒ]•Œo‘3 l‘äåÕê¬9ûlhØn|Tûe?¬Î¬òÈ#›ô²&9ô“Á"ƒå·K„ýúõ³û÷É“'Ë©S§Ú·U¥+66ÖåmÁVÁ*)Qí¶õèAG-ΙS'ûöõîYXss³TUUÉÚµ›äñÇóŒ1É1Ìg¶$$dɃfÌ1˜+3gæKJJ®¤§[Äb©Ôcéž~uîÁƒ_õë Ö!‡%;ÛBýT°¨`ù¯Á:pà€®jÙCxxøeK†Ö¿9»ÍÖXYÙÉÉÉÚÕZ^ýôÇí¦¦&ã@]&œÔ3hë—¼úéËmõÿ'On”•+wûõx±øÛµµM2ujµÜwßéÛßoŸ0¾?ªÉÆ>Þ/¶ÙÒí€7X{÷î•I“&9Ì`…„„8ü›³Û‚¥‚¥ú\¥§çIDÄûÅ ^õÙš>½\™…¡ß»úŸx¢\bb^ðË VtôK’•UÂþƒ~*XT°üÓ`©¦†Êä8 ¦›QÁ ä –ê¯siiÐ2cÇî1Æ®9 ?dž®Ã«¬TQQ­¬ZU%iie’’b‘ùó ôE‰SR dáÂbY¼¸Drr*¤¼¼Ê”e@rÿȽ͚Uª;¥ûSkÀ€£’šZÒå9ô£ –WqðàAtw…iÓ¦ÉéÓ§;å¬bbb\Þ ,u†Yzzîe—4éitL¦L)3 rƒlØP)Ë—[Œ±-Ô¦ã %9¹T’ôôz)-­÷›ƒ‘»}°  óäÙe‡FzJæÍ³È“OÊæÍ•^yͽm¹iSƒñ9¯1åbÕæ\çoòÐC•RUUCýT°¨`ùŸÁÚ¿¿ÓªUÇe>u^uv`Ç3ÓÓÓ]Þ }°T;‹Ù³›üÆX…†~(÷ÜsL~õ«w “µCx`›6Ê`Øö-²uÙŽOHPÕŸJÃlmóÛžAj9víÚÝ›J]£±;—š0¡Ù0h’Ÿ_ê“kø3׬©–ñã·x½ï›;}á&OÞ*¹¹e¼/ÒË? –2PöhÏ`õæ>X7æÉàÁñ suï½ïËm·Õ= ®½ö¬ÜpÃ1‰‰ið0¿õ¦Œ½]~¸Ì0[ýjì-–F™3g“6ƒf^vhìØ]²xq¾¾X2³È®ë_¶¬JÆÛÚc&Kýß ¶É† UÝT” ú{:¹÷úkªå¥¥K‹{¼ÐþpB~ýëwä§?ý@®¾úÓ¯º‘ÿþ9xðã>G»tžqãš$9Y5/ÝÞ£c¯–+VÔÊäɵ^½ìЬYRP°©ÛÕ¬ÞšAQï“2Yª’åëå°°ãºrµvm™¼úê«d€ÐO‹ ++XªÏÒܹ[{¼jõË_Õ3;v%ïÓçœþ©šh†‡·tùù‡ k‘36ÉÆM=2ö*DšZ&cÆìðÉe‡&NÜ!ëÖv+›ÕÛgð«W×ÈÔ©> ¾«@ûŒÕÆ>Z®MôSÁ¢‚…Á ð –Ê—M™ò|™«»ï~O~ñ‹w 3Õæðò/·Þúß2hÐ3Ý<€‘øø ãÀéÛëñ)s•œl‘áßói•pôèg$#£ Ï@ó–”4ê³ /µpðÖ{wBO}t“”—×2î’ÁÂ`K+??_¢¢Þê1suÓMïéK¿Ø3VÖ –ºøñ![MÉ*ÅÇ«Îè>{U‰˜?¿â+såûñ7îi]ÉêÊr!3øä%K6ûM¥ñ9yÅD£uB"#ËÌ™5†¶\f„ôSÁ¢‚…Á ð VVVnœš~iY°Õ¡¹ê˜Áºîº³†I©6-®LÖ† ^û¥Këdôè=šo›:µÙ0ÑŦɠtfEE³,XP-Ó¦UéV]ý̨ǩÇÏšµYV®Ü, [ô£Ÿ ++X«Ve÷H ý¶ÛŽ8]ìXÁR9r³‰y—#:“¥.ö뭱ߴi—LœXë§ü''—鬳ÈîsË–í²lYÌ™S"S¦TK\Ün]ÙRûÔ¥³BOt2óêïC†Ô=ÎTŸ­ùó‹%3³Òå…›ôSÁ¢‚…Á ð VFF–ÏúwÜ¡í§š+[ši°¬Á÷¹s˽r*¼ês5{ö&¯-ؕ˭X‘KŸ,©ö›-[štÜÅ‹«eÁ‚ryä‘M†ñ*2Xh°ÀØ.Õù»¥KÕ™ÕÆý·të!, –Nzõê,ŸVYÔÒà7¾ï–©²V°®ºªÍ0X^¸O“¬]»Íôq_¾|«Œ½Ç¯ºâOŸ¾MÊÊÊ™E¢ýèG;, –/Ö‚7nÜ(¼ã³í¿þõ¹öÚOÝ2XÖ –jßS祾Q›dçÎݦùŽ»eæÌ2¹ÿþýìâÁ­ºŠåîY…dPÐ~ô£ «›gŽýšOòêò77ÝtÌíeÁŽgºÕKí ¶ËºuæU˜™Ù¤/þ±_,ÅY³šôE½™E¢ýèG;, –—¹yófyè¡§|r€¿óΣrÝuŸx”½²öÁŠŒ|ÚKù¤7eîÜJÓÆSåº.][ð#¿cdä›ú¬Q2BH ƒåƒNî 6øäÌÁ[n9ꑱêØÉýþû_ôš¶øøÍ²uk÷Ï(llÜ¥Ï,Sg’ùcKqÑ¢—g°1ƒG?úÑv*X¬n®755Iff®×ƒîjyÐÝp{Ç – ¸V/zï’%êÂË«Ww?쮺īÓñÕsø‰_¬¤¤íRQQAýèG?ÚÉ`a°¼í¤óòòdâÄ^=°ßuW«üèG§<®`ÝpÃ1‰­óª¶¡C_‘… ë»=Þê9†}ù«P¹V°ââZ¤´´”Y$úÑ~´SÁÂ`y›*ø¬zùx;åîÙƒùÇ?î•!C^ðª¶ûîûoSrXsæTúmþªãkݰ!Ü„’ÁÂ`yÛI«ë­eeeFÆ{ín¿ým—Ûmùƒœ•‘#«tGlï¶0xO’’,Ý襤òöK¨økKéËÈÈa‰~ô£íT°0X¾X ¶X,òøãµ^;°ßvÛÛW¯BC_Øå㑘XÜíñž9³¤ýùü5ƒ¥¸lÙrèG?úÑN ƒå '­.ï²aCŽŒØ/ Vß¾'$.®Ög¢6Ã`©çøG¥èã€6XÌàÑ~ô£ Ë$ÖÕÕɪU…†98Þ£ëê«Û$2r›DDò™éHL,1Á`•ôøÅ]ó„,_žÐ_`BHËOÐÚÚ*tzŸ»tu[°T°:.>ñD>››Á:âv+4ô96ÌwÝÐ/e°Êº=Þ?¼Y_’Æ¿3Xï&:Y$úÑ~´SÁêÜ1DöpêÔ);vlûs{«ãR¡jÛ°`Á6Sì¿ùÍQ¹æ×gÞ}÷Ë×(aaø,ÇdÖY„ê9¬gúkK½Öœœ<¯ì;ä8Ð~ô3ö½0ƒå©IJOOׯ dƒÕÕ™€zÝê"Ð))M¦U²~÷»wå¿þËy¬{îyYÆŒ©•|[2«ÖâÅý¢_W°†?@,ô£ýh§‚Õ3ëøñã2a„NíׯŸftt´dgg˹sç‚.ƒek²T%K-pÜ«ÜUæª_¿çdôè†vsåÛæ›»dýú­Ý³ÜÜúöNîþJw;¹C!$ƒeºÁZ²d‰ìÝ»×áÒ¡2X©©©v••‘œœ¬Ë†Vg«~úbûðáÃÝzü¾}û¤¨¨HÖ®-•‰´WeÔOµæÉö€gä¶ÛÞiïЮ.ƒ£~ª³‡ Û&#Gî5îóQ§Ç[—Ùºòÿ<Ùž>½R^zé`·Çûé§Ÿ–iÓ*Œ×qR"">óšÞ®oŸ”´´b­ÓÕëyûí·}¾¿š¹~ô£?0õ«ãV wwõ÷ƒ¥ññññNïsñâE Ê –=VWW˺uÙÆëª5ŒÃÑnä°Þ‘ë¯ÿD›,õ34tŸŒ]-C†¼b÷þ¾È1ED¼)³gט6æ))›åþû_÷Ë Vd䛲fMžO÷rèG?ú{*X)))ràÀ§÷ikSm"ƒ6ƒe»ví’òòrÃhmÔÔz9ò°Çù¬{ï}O~ûÛÃÒ¿ÿ32n\…ÄÅípÚçÊ9¦Ñ£·KVÖÓÆiãÆzã9wøekÖ¬-ú²Hä Ð~ô£ –W –íߎ9" —Ý/))IZZZtåJ™«ÌÌLÉÉÉ ê –#ªËê¨ Á§§Èüù2qâ>6ì-³tB·Pg­Åƾ,ññ; óZ" dʨQ;½~ùw8hÐ_u{†]»ÌŸ9sJüâõÙ¶gÈÈØ¨³ud „ –©mlÛ5Ø,e¤ÞxãËž£¡¡A%44T¢¢¢tø;˜Ï"t—ÍÍÍRUU¥{gåççËêÕdùò ºS¸jf™™™+ë×çKI‰E›2uÕÌ4%¥ÊMSàÝ*ÐØ±M’›»ÕôqY»¶Òxnÿ »'$4ïC ³Hô£ýh§‚E'÷`]ËÞ¸±@ÆŒÙçÒx3Ç4lX‹ÌŸ_!{öì1ýõ©^b))ºBææjðà¿ÈªU¹z™—úÑ~´“ÁÂ`éL@U²–,ÉmoÊéë Ö€j)x“ÔÖzo¹¬¢b§L™RÓã—ÎQÿ?-Íb¼ÖZf‘èG?úÑN ƒ쬯¯—ÔÔŸ÷½RY¤©S«$7w›×_ãªUU2fÌÓ/;äYß«­RZjaŸƒB2X¬Þ2Pù-g&Ëì –Õ\åäÔûdìÕRaZÚ&1bo˜«)SöHvv¾GKƒÌàÑ~ô£ +Ö²•ÉJK+²»\hfK- úÊ\u{uÖž2‘—L–ï*Y&<¥{^uõ¬A2(èG?úÑN ƒà3ÕÈtéÒ\=zŸW*X*Ð>sæ&))ÙÒ#c¯LÎÒ¥¥z¹ÐÛ™,õüÓ§7éÊUwZ20ƒG?úÑv*X¬   ¾geåë®êªw–Y}®&LØ&iie²mÛö}}j™nÕªM†ù©vîïÎë?³[º´,!„ +gªeBMMn)ðÐCuºyiW–ÕÔåoÆŽm6ÌF©X, ^iÅÐÕ±/+«5tËèѪÙj«iÙ²I“vÈŠyŸ-È,ýèG?Ú©`a°zÉZ¶ª¾¨eàc, eúômý’®lÙvH·v‹:ôÃT픇Þ$?^.••=b¬Ü{õúrrªý¢È0Z» CØ#yB?nÚ´m²xqž—šZµ"ƒ‚~ô£íd°0XA<P9"U•)**“5kŠ 3‘/ æcœküÌ—´´bY¶Ì"ëÖ•KEEß\ ƱW—Õ±X*eÉ’"™=»LFÚmÅ—õ¢£ËEE½"'î6>%²jU¾¾¶ 7^33xô£ýh§‚…Á‚A‘A+/¯’ ÊåÉ'K%5µ@›HÅ´´<ÃXÊÊ•E’—WÖ~Ù!Æ BÉ`a°˜ 0 C?úÑ~ô3ö,2X¬Å3öèG?úÑv2X,fhG?úÑ~¾;©`a° „BH ƒE ýŒ=úÑ~ô£ ,rhG?úÑ~´“ÁÂ`1`†~ô£ý|wRÁÂ`‘Á‚Ba¯Ë`µ¶¶ÊÀÞ'$$Ä..^¼(™™™’––¦Í“â™3g¨`¡Ÿ±G?úÑ~´÷Î –­Yrv?G())‘òòòöí²²2ÉÈÈ ƒ…~ÆýèG?úÑÞ»3XÝ1X“'O–S§NµoŸmÚ49}út§ VLL ï20XÎþ–””$---º:¥Ì•jË““£o³X,úÌA+Ôïééé¼ËèËQo+[ƒÕÐР×ACCC%**JòòòÚoó´–-:f³ „Bå¶®‚Õá集~´£ýèG?Úý[?‹•/ ÆýèG?ú{ €ƒ€ÁÀ``°€y¨®®Öm%T·ùþýû몪=ØkUáiãU³á‰¦î¶Éèiýþ4þ­­­2pàÀ.oO¿öôÒgÁžþ@ú,tWO¿³}$ögúý}ÿw¦/ö}³ô›9ö,/£ccÔ .Hqq±ÄÇÇ;|cý žh*))‘òòòömÕè5##ï^º~娱cýzü}¨=ßž|/é”Ï‚#ýòYp÷ àŸgûH ìÿÎôûûþïL_ ìûžŒ¯¯ö} V _¿~Ai°&Ož¬w\+Ôì!66Ö¯^êì¿cÇŽ€[=žŒ¯?¼?ºc°aüå³`ÝGmÿwµ±Àª/PŽÆ×Wû>ËÇ8pà€vÚŽÞXµC(FGGKvv¶œ;w®Ç2îjò÷‹m?~\&L˜`Êk퉤'ãëï…«/*ÿ,Ø3XôYp6þòYè¸Úþïj÷÷ý¿£¾@<8__îû,bïÞ½2iÒ$‡ëî¡fêMMMõý®4¹ºŽdOcÉ’%ú=0ãµúKÅYŽ §ß gÿ/> ÎôÂgÁÙÿ „Ï‚í>hû¿³}Üß÷gúaßw¦ß—û>ËGP×L|ôÑG= û)ç¯{þgšü¹‚¥B¿ŽÖãýuüƒµ‚(ŸWÿ,8ÒŸ{ûH íÿÎöqßÿÝÑçÏû¾3ý¾Þ÷1X>ÀÁƒuøÎS´µµIdd¤_½gš¦M›&§OŸnßV³‡˜˜¿Ð’’¢KÆ4þ¶HOÆ×Þ {ø@ú,¸2XþþYp¤ßß? Žö‘@Ùÿíãþ¾ÿ»«Ï_÷}Wú}½ïc°¼Œýû÷; tüìx„zSÕ©®999=ªß•¦Žú-‹>cÄ õ» ö4Ž9" 7þ¶HWãëoï…­þ@û,8; 2> ö –¿œí#°ÿ;Óïïû¿3}°ï»ßžØ÷1X>ø’sÖS£ãï ºGhh¨DEEéRgOÕ¦Žúý±–õóÆo¸ü`ùËø;Ú_\¯¿¼ŽôÊgÁ‘Æ@ù,8cÿ,8ÛGaÿw¦ßß÷gúaßw5¾=±ïc°L ƒ€ÁÀ`  ƒ0X, >êêêôå2ÆŒÓéïê²£FÒ·ÕÖÖ2P x‚øøxm¤ÔµÈ¬Øºu«þ›º 0Xà!Z[[õ^£££åüùór቉‰ÑS· º€ÜÜ\]±*++“òòrý{NNÀ`@W¡*W±±±!ƒÖ¿Ÿ;wŽ`° ;hiiÑ•+Å—^z‰`°À X `°ƒÀ` €Á `°0X,€ÁÀ``°0Xƒ€ÁÀ`  h__ûZ¯%`°^3X¼nÀ`0¼n €Ñàu0XŒ¯0XŒ¯€Áø¥Ñ8wîœìÙ³GV¯^'=ö˜¬\™);wîÔÇ`0Xà¡ÑP&*++Of̰ÈÕW ÿ÷ÿž“ïÿ#‰/—ÌÌ,»&ëÓO?•åË—Kÿþý%,,Ìxì ±X,ë 1õuµ¶¶ÊÀ1X  g Ö®]{dêT‹¨?Ùr„rÙ¾}çeŸ;w®”””È… äâÅ‹røðaý·ž„2kVb°,@¬¥K×éÊ•=ƒõ½ï”E‹2/{|¿~ý´±r„ÂÂBIMM•'žxB/9ž9s¦Ý}þùç2|øðömWyî¹ç$::ZWÊÔíî- ƒèQƒ¥ÌÌ¿þëv Öÿù?çõí¶HHHÜÜ\9tèÐeFKU¶ Ú·ŸyæÃÄ-m7?¼Ì 9{LDD„;vLÿŸªª* ƒðƒõÄÎ+Xii—W°TuI AƒéjÖ¼yóäøñãú¶˜˜ikkk¿¯2FÖ\”­ù±n;{ÌСCå…^pûµa°,@¬ªª=2r¤ý VLL¹lÚ´Óés©êRqq±Lš4©ÝàØ£3ƒåì1¯½öšÌž=[,[·nÅ`0Xÿ7Xê,Á´´ 2bÄ¥³Õ² ª\)sµpá·[5¨Œ”‚ª89Êg92XÎcÅG}$ááá, àÿËj²òó÷Hrò¹JNΔõë·;4WêŒAkþJQe¨}ôQ}› «—••é3 T넎,{fÈÙcT6KãÕßT؃À`Â`yŠºº:tWù+U¹JKKk?ëOA.•«R·OŸ>]/#:3XγhÑ"ý?T•ëùçŸwj¬ì-1b°,@@,^7ƒ ƒÀhðº,Fƒ× À`0X¼nÀ`L5½•€Áè!ütÍä-ˆ‘‡˜IEND®B`‚XYDifferenceRendererSample.png000066400000000000000000001476731463604235500354740ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/xy/doc-files‰PNG  IHDRXr5˜€IDATxÚì}iL£Yvv¤(Š¢èS)Š¢(Š¢HQ~DQEÊ(ŠZ£nõh¤Q+Íhª lнØÍ¾CQTAQ€Yml ÆàÝÆ€ .ö}§¨•ª^§»§ª«¿ùFd”IŸÏ÷Ò6ï`¯ís¤#°ýzyÏ»Üçžûœçü ¡¡¡¡¡¡¡¡…Õ~C€†††††††† ,444444444Xhhá´ƒƒøàƒàÿüŸÿ¿÷{¿¿ÿû¿ôGôëh¼iýÎï¸9,4´¶Ä`çý÷߇úúzøòË/C¨>|ø‡èõ³ƒy-t{ôèdggÃßüÍßÐãG@+¯ÿñÿ~ó›ßÄ ÀºÌ9‹öïÿþïx ¡!ÀBC‹†Á꼓Z¥R °Üž¿sçŽÛû½Žšµ´´Àïþîïú=†ögÆH€u‘ßr™s6–Œ€È¶¶6œ¤ !ÀBC‹V€åô`¬?þã?v{ßý×…ô:ZðÖÑÑôñ‹'€ê9K×-,44Þ¨öäÉøñìöF¯_¿¾ô ‰Bxì“O>¡KcgcùÎ;ïÀúú:üïÿþ/üú׿¦ìOÿôOc`…ãœE€…†† í­pÿú¯ÿêö:á·ø{o °!Á§¦¦Â_þå_R>ÑüÁÀßýÝßAee%þö€‹Û·oÃ_ýÕ_Ñ¥³p|&¤oÞ¼I¹cÄ9Ž×x~ôÑGÀåráþáèç“ï!Ës?ÿùÏajjêÂûèÍÈvg#ùÎÿùŸÿñØŽ,%ýÓ?ýÓ¥ö-==þíßþÍ­(á/þâ/è~@è¼òvL. .sΆ÷óßCxlä³þú¯ÿš¾ðÝD"Ý–ìYªýÛ¿ý[úÙ†€Ûóf4)tÆ‘ü%Éó°ÐÐ`¡ÅÀ"7þ³¯“Á+ÒkppÐ/§ˆ †oÞ¼ñù;ΰáøL Î?w~àV(¥pí£7û—ù— -‡]dß-ÅMLLøÝÞÛ1‰ÀòwΆwo¯y{áëí5R`à´’’¿ûL^[Y@44XhhW °È¬ýìëd¶è½—Y"ÜÙÙñtHv ³³Óç`hðÄg:\§ííí¹ Üò'³³³ôµããcøÁ~àÚ׋üoFŽÅù Ëe³ÞöØßÿýßSE~'ùž¢¢"ìY(ßqppÑs6Ô¸‡Âõ Çó d Éw“¿gŸ?ŸåD€…† -FÖùmȲF$Öù C'h ƒÑÙçÉŒ¯Ïu[ê Çgµ¸¸è3~ø¡ÛkÎ¥#§‘¥;g&å"¿Ç›‘ï¿ÈÀê¾y³ó¿õüöŽI$–¯s6Ô¸ŸÿƒÁ@Ÿ×jµA¿F2šÄÎK)˜#’%gŸ'Ë…°ÐÐ`¡Åa‹pF" °ÎWúò`€^¸?óü@|ö»ˆÓÙçõ«_ùŒùE~O$3Xöáq dIñüwŸß>ØAÿmd°œçl¨q÷õ=þbåë5òÎsÒ¼möúB€…† -†Y²8û:YêŠ$À ¤çt‘ï çgúz>”lÒE~7;Ïû –(ê¾ûyè·^%ÀòuΆ÷‹îŸ·×ü¡€jXh°ÐÐb`''·¶¶F`Ÿé{«Š uÂù™¾ž?ÿþ²Iù=ÞŒTûýœüÇôúY¤Jâ/ºoÿüÏÿìö|uu5|ûí·ŒX¾ÎÙPãN€…,44Xh°¨M!Òf弸¿2öp¬óß)•J=ÞOw¢÷ì÷…ó3}=Ìw83)ù=ÞŒ´Ç9ÿ{È{VWWéëDÀ•T±ýùŸÿyD²sáXç³J‘ƒ|9†dYŒdiœœŸ‹ìÉð}r²ìv>#sQ€E>›Ä€|¦óÜ# é‘:gC‰{¸–3“õÁ¸)¹“ÇáTrG€…† ,44444444Xhhhhhhhhh°ÐÐÐÐÐÐÐÐ`¡¡¡¡¡¡¡¡!ÀBCCCCCCCC€…††††††††† ,444444444Xhhhhhhhh°ÐÐÐÐÐÐÐÐ`EŸÕÖÖº=ÎÍÍ…ÔÔTÆ{FFFTüNŒ#Æ cˆ±ÃÆbì `…°***àÁƒŒ÷ÝÝݨøGŒ:Æc‡1ŒÅØÕÔÔ ÀŠE€µººŠ'>Æã„1ÄØ¡c ¯(v°b`¡££££££_#À :Æã„1ÄØa 1˜ÁB€…ëéGŒÆc‡1D,œà¬ㄎ1ÄØa 1ƒ… 9XèèèèèèèÈÁB€…³Œ#Æ cˆ±CÇb ®§c1NèCŒÆ9X°0ƒ…3tŒÆc‡1DÇ r°ÐÑÑÑÑÑÑ‘ƒuAÓjµ——ï½÷¼ÿþûPZZ Ÿ~ú©×m¿ûî;àóùÐÐÐ@Áño¾ù3XèQÇÙÙ9Œžk;tŒ!f°Âk677)x"®Óé 11Ñë¶2™ T*•ë±B¡€öövä`¡Gmgfæ€ËùlŒžk;tŒ!r°"lï¾û®×çÓÓÓáõë׮ǯ^½‚k×®a =jãh2-AYáç`ÓNcœð\ÃØ¡c 1ƒûío ccctÉЛ‘%ÄóK†çŸCz4¹R¹w ÀÞÙÙaôï‹–Ç$ŽLÿ½Zí èã.ÃdŸÏ·(~ìtŒžxþÅÆùs¬ÿùŸÿ¡¤÷ÜÜܸÎ`ázzüı»û)¨3‡AψqÂs c‡Ž1DVdTz³¬¬,xóæëg?ûY\s°æffð"‰R^©l½w öM ¸eÆ8Ṇ±CÇ"+|VTT{{{4õßÿýß”ƒE€·%ÀÑÑQZ9x¶Š°­­-n9X\½vÄoÞjÅ % }rrúšwa•Uò*#Æ9Xá3‹ÅB¥Håàüc*»ðí·ßzX¨ƒåî ÓÓ𢶶Ôj¼ ¢pV§Vo‚ì¶öÙ )²`œð\ÃØ¡c »‰y=*¹Ç{/B›Eº²á‘@ÒçÏÛlÈK`€ÀT ž'¥A_î Æ Ï5Œ:Ʊ“N×#ÀŠ÷^„¶‰AêÙðü~sÐË„d»¯Š Á:3³º+'¸?en3|””½™6°Û1Nx®aìÐ1†W»Ù³ U&#ÀŠ÷^„3š.¸kK›¬¶”Ê Þ³©ÑÀÑ­\0™‡ðâºboiy ÇÉi`I2ÇÁbYĸ £££_¡k ¨,V¼g°,ªf¸5Çþ\QÐË„ûú@/I…IC;Îê®ÐÆeèj> àЏ*m †UŒžk;tŒáÆnd²Z-‰°âƒ53\¥›lH{~uÜ¥¤wÛ/šÍpÐy°`JQ¼„«œ%i6AÖdw¬‰ä^úÆ Ï5Œ:Æðjb737J)òÙ°â=ƒõ ¿r²!é£$0V¬&ܦжX`,ÂYݺDrÆz… `Y[A©ÜÆ8Ṇ±CÇ^QìÔ3=0¤H c*¬8ç`Í 8ñä`ÝY̦Ëþ¶ÎçCÙv:¤'ÁlO<°Ûñ»B‚û¯Å°Yµ0$ÜÂØ £££_‘jó¡~…+Þ3Xó33`æÂ—Iôd  é·Àg5áêÄl n»¶7HÒ`ÉdÂYݸÍ6<Þ×p”QàX›¬bu` Ï5Œ:Æð*b77; {ÕBÖ#6¬xç`- Φ'‚Ó·{(ÏÊÛö‡ Hå®m…c7hE!ò®ˆàÞ´çWÄwYè¿»qÂs c‡Ž1¼‚ØÙlP ^w‘°â8ƒµj0Àˆ*à `ÍKêi¦Êc{»¾æq!ïQšk["ï°=,ÁYݸZ½ò“ÀzÂ΀î:Ì`Ṇ±CÇ^Eì,SRüž…+Î9X„ÐÞ3‘â° Š2ØÐé<¶%©Ï§­·Ý¶%Õ‡[ý­x] Áýly]n‹xgùÌÌÌaŒÐÑÑÑß²Oë» Í„ 3Xß‚úy–h+‚m/‚£D¾a§«ÁmÛì#Àê®i¢;Sgu½í‡°“ÄóXÂò0›1Nx®aìÐ1†o9vSšf¸3ËB€…¬°=(€êew€Å·fÁžL敯µÒï°ˆ¯ô”Á¢Å³y ³³sÐQ¶ÇI©k¨Ô+'<×0vè÷»éáj¨XE€…,‡ï Û»ËvL5+7àP,öÊך®ôXfi ¬á¬î-Ü%¹FpE\Sª½±Qœýb 1vÃxÝŒˆçÒ•D€ç¬½ÎHê°x;l8tzl»¡Õ‚NYâ°¤ã\*>Š™§“ÆËΚq +Á]¹ãì¯k²X ££;tttô·}ÏïËsS`Ås«Ý3#EÐ÷fï-ÏåD…‚‚©óÛ·Ûóá¡H„³:/n5ÏAiá/Àl^ ¯]ë̱nyXVN/H¥{8ûÅ Æcøc·d6ƒQ|Ó}EV|r°i}µ×`¥=O‚õ®rÏϓɀo½é±}ñ^wt /Á[E‰zê¸Ç`0¬…·‚k]6×+ÀZÌ» "Ñò7ƒ±CÇ¾ÅØy#¹<f°ÀåTUy&âKÝ\*Ëpvû‰R½²ÏKr)o+UÝ/3«›´B+gÔêðq¢ˆ‚{Åõ#xš”é`íf–@W×Sœýbc‡Ž1|‹±#²GC2,ä`=  ìzU¹W€e’äz¨¹Š„P¶Îòº}î Äú4ø¤$ælÓxÁ}ïz¾DÙS “…of8.{ʉWpEüqJ´µcüÑÑÑÑߢ ­9Vì,NðÞ{ïÁøChhh€W¯^yÝöwÞñêñ–Á"œªQC‰WÀ¤”ßôPsßvP¼·í.I€i‹guN€ÕbEÚ(ˆÅ‡aû=#6X`ÕûX/’’¡¨ðX­ó8ûÅ Æcø–b·=Ð U«ìØX677á»ï¾ƒßþö·0<< ™™™>r°Ð%½ÎE^R¯ k:÷ƒ„ø~¶üÔ›÷kaÂܼ„ï]Qg‚)v;ôõ\Œu¾ú(´÷qçàIÒMŸ‹¸ª\ ÅvÔÄ cˆ±ÃF{ìv{š `ŸK„ï¾ûn\¬@³‘G4­xJ­–DXRºm¿ÞSé!épÞï›ËЊ³ºï}¨Ü +¬*è¹½s!pURò ÌS ®çtºug*ý‚+âDὫåÊDàì3;tŒaäc·×^7^&Å>ÀÚÞÞ¦Y-_‹€/â?ýéOAà¿ùÍo¼+§ŸµÊÊÊ“ŠïOFò7¿tìwùN6¤~– ™_fÒ¿ä„ ï-¥ÂŠNè¶ýFw±ëõóÛ;×/°`B_ñ Çc1g's@P»òûgf¶ánÝ1hnYO?¯ÿSîÀ—™™ðYꉊ;ùëí±´À Ãj\Çãc|ŒßÆãùyØè¬ðc`-,,@ZZšOÖY{ýú5Xõõõq—ÁzÃãAúó¯™(²Žl­;•t°ZaVÀñ›½"NZ(”98«ûÞ7§i;o¬Ö…ÐÄDÕ[ ¯1Á([&õIF‹_³ OØ3XÄçY 0Ôºäs¹g¿˜AÀØ¡c ôD85V/Uù1°(úæ›o‚~ámr|˜`å²A-MF^u*qÆ$;Ã9c´½MHZW’*ºËâÀP‘.Vήˆ?e§Cû)´ÜzÜuàæ¿Šhhäo` 1vÃxõ'Ž1R¯.]€ENBtÕ¾ýö[øàƒâ*ƒEš3ïwÝñ ”™Ý(Éum¿21£Š´€‹¬? aöÁlÜÏê&'W@ž:JÁŽ>m€¤PÞßwŸr©Èû‰ÐYµ ÓÅ ñcv²ñ]"9ÄÙ/f0vèÃ0û#…%± °È‰á/ku–Ø~¶â€+>Ÿýýýq¥ƒµ29 Ëâ[>žf¢Öu:ŽÝ°ˆ«À4«û ލ·k“ÄÜX[A5ºÁ½£t“./Rm+v&Ô±öà À:ë‡i¸ßü<¢Y,tttô¸X´.òb`Ò¶:ûÿÄÄÕÌúÁ~?ùÉOè²b¼U®ÁôpµO”|̆£òi¦‹.êdÐaL `‰Õ 0féûYáP™Ú\|(EGð(²œ8?åž'¥]\¹²XÕËbáìcˆ±ÃÆ«¿p¬šÍœØæ`¡’»÷õôy›Í]ÓJ£ƒªÒ/Pê1$Âòè PšW à®-8€Åw11v¤.ÊKئÀŠ›5V9ŒÔ[ƒ~¯ftÆYÝ—TÞ²X­Êb!ãò¾½½‡qÀóceNÚÊýº¸²Ÿf ÀŠ·^„„Ðþ†Ë…yëé¿#—ÃðD±_ ÄÝeÃb¾VqæYA,ÄÔúʸŸÕI{7a™UMÍ>»†¸SA¿w´Ås¬[aXĉø©Xüg¿ s""[Zú X§ç0xþa £Èçcä þ=¯ã!¬ïE¸l2Á—å%`Ÿ:Ufß“J¡}&pU ~8‹6°4)ê z98€U¾Æ•2/î/º{ë°Å*¢ †H+ˆ3Á ”LÂ.›€µ—Q EÜW0=½€7G¹Ù¼eE_€EcÅx £G‘“*û­ÞFXñ˜Á"éÑÝ2˜ž»^;”H n10i€ª£îv0ÉJ¡h+8€E2_ yzÜÏêz¶à;ËlÊ®?öТ²ÏÚ—ú)trW?>o ÚKw üÃG.‚{$\\»cck8ûek4›p§ô9˜†±Y:žèŠÛÄØGj`Å#‹ôœ(›òžëµƒþ(Þ ˜Ù}½»fûrpÄ •î1"Nè'.“íBŸãœ™ì3a<ðüÃF‘ohµ`G€—¬§]] žä€UvªÌ¾Ó×4`’M”ÂÆm6d= n{§TƒÍ6·³º™™yè.Yq5š˜”îú`6Ù^‘SÞ|“] =í‡8ûe …G äM€¦y ãçÆ0ŠœpšÍ\XñÆÁ¢ä»ÖV(ÙË€Bžëù­Þ† SÞ£48ª¸éÏ‚XÃ#×A?3·‘Y”ÚÜ@ÍxBŒθón:õ`Hìyë‹dȺJV)Ä$3œÏÓ™ h0c<ÐÑ£È÷¥Rh¶§!ÀŠ· YÞëk¥ÚÞ“MËIék]Åtù/XÀʶN‰ÍÔ}¯€/fu:Ý(Kôn f–u4|w¹ Ã]X›ß:À¢K–аò°pö{™ Ây¸ßò6S«AV>åQ]ˆ1ÂócÈ\?÷CÍf2¬xã`팎´ª™象LªàNdæz BL¡:‘jPLT»ýÒ0ú—Å…nr±ÊKP«7A[¬r4D²a´éÀªR‚Õt%Ë–xäÝ‹W'ôŸ˜XþæMø(£ú9§“RQÈyƒ Ï?Œ!“9X¢¨8JG€o¬#‘¦OÛûÇR`S­¦êìS¹Xõ ,Phyî(¿¿fºRanj<ægu„°<Ź*6Zç^!¦*ÒÂ*«âJ‘Ïàì—NùHƒ>KM…î¬Óâ«uʸ_À”ÎŽqÂócÈP_ë«…ü§)°âƒuÜÞ•{'Ùªfk"ìȤ4‹¥ÍŒ(À"’jYÆ)Â×(A­È‘:l&yÌ_pDÈs¾°Í Ð|Sìü+XÏØéPqý!ÄãUØ“)¿epðÌ•Cô¸ˆ2Œ®cb2-C÷ŒRÔÆBGg¬ÖdÏ'VŒ¬M‡?o¿©/NuÙ: vE´¡PÇŠ(À"ú±´|õ¡°W&CîQt'€eR󳺾¾Ç°‘UãN,g'COžûvÒ$•›VÖÛöÑëR0êçãnöK–Þ¸Ü7`³0ã÷óe¡ …f°i£tÉMº†+,8˜aöcÈĘML€T™Œ¬¸X}}Ð5_áv°çûËa^Ú·æXX¾úZFkcú‚#ƒ7¿ùÈ+ Ñ¦I@¯_?ÙN2êñ•,ZÙØÙ¢SÖ]½¢BÄWù[Èù0xkÞuLYµ0$<X’}˜½q$¹££32ƒ¥—C׸XDïêÿAþc÷†ÎE,õ•ݸ9Üžó ³¢â˜žÕ‘ •àö¦W@3q£›VŒ‘í¦ºÆ®Ddô|%!D·ÙïÄÄ* VÚ@œ1I›,‡ësy¼70k )•Û ®6¸2X䘈ڷOŠ%îoÒÇ=Y³W1û‚1Dÿ~|=£ç8§Ð!Ì`ÅSËn‡/:;<ö€©V:rƒêC '•zòbš— Ó­ƒ¬qÖ+ !šWdÙ‡l7~KKµ¨®`=OJƒÎBÜñ7ÇIY¢sÞPÈ·ÂV•XÂû ,ºÐ¤/D¢‡`çuÑãA8Xûì6p°úêÖhõé`ºžfÝppCþÆðêÁÕ/‹¹°h>é¸`QÝ…†yr°â `-LOÓÞVƒÝºP[M©A÷!Œ„Ïôž*ÊÇâ¬N.ß}ØW@C´°Äƒs¹–X5W °ˆ÷eÏ„Eª!šf¿„ãd(†Uvˆ;°,–E¨á} ¦áЖ\»»ŸÀæÍ*W‹€ÞîêMš±ê.\¤\>]Š˜vÀ³/ëuÒcwžŸ Oº»é8f”WAÅ*+¶3X: à½÷Þƒþð‡ÐÐЯ^½òºíwß}|>ŸnCÀño¾ù&¦Ö²É›‚&ƒ]¼jX!5n·ë¥7aÉñûbõ$*c…Ì+˜Ùaó@Ø~2KTsTp]5ÀÍTÁ_7ÖÑÑ]°dwÃvtVm¹*;/·ì¸MÅ`¼'øfÍT.‚óš‚*·jÓò˜šZa®•>6%´B±ú©<€Í&‚qŠÇV RyLjah 2ðvرÍÁâp8°¹¹IÁÓoû[†ÌÌL¯ÛÊd2P©T®Ç …ÚÛÛc `‘ʆåáÛž•}ÇIð°âÃ7‡Û‡´°®×Ç쬎ˆŒÎñ½‚²ÜÓÓ´s¢•5‡ì¼+XãiB/,^f¿CCû`Ïh¡û?P6C9YΊ¾ËÕ;‹×@Õ|ÅÉvÖÉ`Q±Ñ;ý¼‘T¥‹ø>пf_\àêk®Tø¢ˆ¼),„9ë4Æ0Ìn6Š¡×É/nÀ®à>X»S ûˆ¬wß}×ëóéééðúõk×c’éºvíZL¬ ìú¦°4n·÷Nœ´ì‰U^Bw÷SØÎöÞþæeÒ è¨Ù£Ë?ýIãWªå’j`·€Tºwa=©häoˆÅ‡°’T}09ƒŽIמЬAiîç`5^¬b€Ô|3ŒÖ™CªfUŸVÍ*ðtt‰W1‚° ù1È:ñÇú³ªb°›¼ßG ?è‹RžÀë0†áÎ`é»àÞôIÕ`Å~!T]‡Œ§ìøâ`mooÓ¬–7{ÿý÷=– Ï?í‹ýXÅ•)_ÞdgÁ®L³3c>ÿ9&™©ÎÖðyF–k°$IfÂùx°f fM{ gz}ým?>L.‚ÁŽG!ï9(ç~33[Qw¼†:ŽàQ ÷¤…QJ6Ô$<„Íä˜Hès‡ñBŸ/—?†é›|ñæƒ~?‘h˜*ô8>JAX·E«NÇ«·f×ëù˜º^ðñÅ$—ƒR“ 6ù]¯¯?5a«ƒ&S¿Ûë¯y\X\™Ãx^â±MÓ·6Sƒïb ` Ð “?ÒzƒEP&!º²¬¬,xóæëg?ûYT,g¥ÈYá3⇠Ü[Ïa$À"b£û=Í1Éí •dâÆeÿ¤òÒ¨Ÿc"Ÿ‹.[V.SpÒ’X¥ ®¶.ÑÄßè*^õÊ#ˆ²LÍ…>“ˆƒ’÷Þ:–ƒü ·JR'k'³n§n»–1‰”Nº¤f'¦¡¤è—`³Íã gü¡E‹–{Š!ó ôæ{`Â>è5°Á¬ht“îù¤Šãæ~ä`]`‰ )©ýü¸Ó,‚¼ýe­Î.ŽŽŽÒÊÁ³U„mmmQ°¬Ìð¼” ³]i𬌠6û”ÛV¾›ÌH€Ez"îwÔÒJ—X›‘ÑÑZ‹_0cæöƒ lñÊEFÏúP©ÆÆÖ¾×y nÐ&mf4ÕzZ‘ÇÄÙ¯¯ý ™ºžü9ŸÙ¢Ló”¼qL\7;J·ÅËPé)x2`Ï•¾ãlŠݯ¸.»àøkÝ i¬C%÷(Ur7X„X‘ƒØcHÓDÏi•IO#ä?Ma,ÀZ쯎±ÑPfuÉÌùï/¸“U·ÓvÜ–®ÚÍÍDIÄûp/yÌ2sÀV3ÚäØ,¨ƒ®®§Œ›ýùƒJÞ§`RÌx}m$Mé3¤?äDOèçf'gÙ£’ß*L¢ÔžóÀí»,¯Õž7k¡‡#)r*X+«²)à V )‰–CÑÖI[‡‰E+ ÏK4Øú ã Ûñ÷t qC«…™þBP©¹qÃ@"­®{…m>)/€¥ïÛâ¬LN:&¢i^Ç4ìEcK3yÚL'‹ôBšRÞr½¶Þ] yŸd0`‘ö$ÕkÜ’ÍY̹ãÌcA–nu5¡›D T”mvµCè  ²¼7”ªsûn'Ë›%ç:öi‹V:’ÇšRMXb‘ƒ]}ð—'º¤jY°3Ðë!Ñ0ªH§¯Û»³N'Ý (9 ”gÃ`AH´ù‚Å_sÀn5X]°—CcêŒíÆ;Â^„1°äãå.=޲uÀ’•^d|žODÍWUÁŽ\s3c’ÍÙË(ö fÈòOÙ‡!2êR˜O+€:îs˜duÂ<«ÔÕþQ£wr‡îß}F3iöK2‰£é möäùéË.âÍ7Ù¥0R0š6Ži†3µôý»l.ïïU¡bI‚Î`‘ó¥öÃ8JÊ¥"' =>²/„¨®¼îºÈ–°Å£¥…㉦Y˜ óß@íÁ®©tÐHXcx¢Ï…ÓÆ˜‹!ÉømÜËë„$0ÀÒܦ…Naì-ÇßvS"f°â`)”9´É$9ˆ$<Û—{rqX­`p ®ˆKL%°Eb£Á:Éæ'¥08ëÏØ©PyýØùTxt”㻕Ñb"²N€Øwg›q\ ¾{»0Ëj‚J/Ë›¢`N¼ï;SÄΆÔЈîg³b„ Œ(¨A2ãèyóçìÓsË–Ûya~ô(­`³ê`x些Èå¿Òˆ-oƒFû PÈÓé²yþ@"[ ) ï¿³fV*~V]êSÈ4š}¯ç.ôêÁª \ÉN8l UŽK{wx®!+–¥)TòÀy -‚ ®È¼I’Ëè VÇ|<‰bjfLäÖÛO£\YNM(ÑqßÀÀ°ƒåV×ûd·í”“Å” ©ä¯ÃÓ¤LèÊ_ðJ¾Í¾ü/ã&¨Àj~‰D§^sK•[4«çWŽÕäQIê/ƒuÞ—“kA,:@àG,³iº·{é¼°ÌÅ¢ l¸š®h×úÇR\TŒ=q/}~D–ƥߒ¥°ƒ¶j° ׯ\‹!Ó@.m³Á"á&¹÷çÀû 8‹O:¤H{¡z™…¬XXÖ9+èĉþô` +ShE 9‰È:»¯5a&xýV!<íîŽ)nìmÜŠZ€uÖÅ×µ`³xßOÒ·ÏP2êÚVS7 Å6c80„Ä>˜5~²Y&Zyöuå õ·ÿ„‡6%œú;µ;L'Þ;­X ¨…¥ª2¸ ‰â`yK°y ìÀ Vçó¡x?‹.5îw݉š®økg“œ]Àê»ã[1ïâ[=1Âs[ï*÷9ža+†–ZSJ›}ºe…¾¯(±«:):g2‹8á¬NL0þ –Û!Âl… &(#¹§•„Ví4k¬2¯íeZï½€íí«çY­óPyí€Ü·ö܈îƒ):·FϾZæT|ø~^0ß+sÄêlË›9N=|CH¹F·„á`pìAÿFä`E¿Ûû‹=úàÝ|’{U×Ü2WNÏ?`ú ‘*[ús\q•ü¦Ï­­ç­Íò};˜Õ¾jÚf'ê–®î›Oùk$3EÓ‚Öfw­‹Ë¦‘e¦F3e>Ç3ä`ÅÀUçBÕªçµ ,‡C'id¦g°æ†ïF…ب·™±·>wÁ#X)hŠ €õ<)V’Šø¾§I1°ô }0)œ¢äý‘%U ÷µíhôÄ^ù±" ¤…ÏÙß6^$ƒÑÑ]Ç sä CAƒ›ñ„.˜ì7»ãʲ_€ìþ‚{FªuÞÔ³S ˜ûÊmùŽóâ¢W°—Âóù¡f° I} Ó Ñ=2XË&至B¾Ï>è/Må(íWë|ŽhaMͨ½ÆT$juÕ§KÏÒáMa¡_IƒhpÝPªÇòjÃZ><íêòØvK©¥†wºÝ< ¶:«aÀT„¬XXæY=¨%×½̶ù°Õ”J×Þ™ÎÁêyPb£ç¹Jå6 di³Ý³ KX·3àÊÕ“¯iúîïSÒ»¿m'jU€õüÊÕ@錋àît{~;ˆúiƒg¢ÊþkÛOñôô´Ýz‚ÌiW‹üí)˜w[t-ß5΃N·q¦Ît×û×I •ƒEé5ƒ‹°â€ƒEøªÎƒ¡¸IV «Ã]®ö9T€T›Úé¯1$“^‘µÂí3óïQu÷hÎþéE'g÷+ýyŠWðH„Eù3y®íHaÀÖ½|h›/DV¬¬q³€^ ÞfÑA&ì×°¨VÓ3XÕ;xÖÙu3c±`fSš¡/wÆÅÉ! KW¬Œ)€µÌª1gº*6('Ë/)ë‡áá§W®àÞ—g‡§ç*óöÒxÐZ¸r["» h´¸ ˜ö×/Ól•3‹%‘ÂDJŸ×÷OU Ñ×]í(ÊÆý~g¨,¢½¥ê´#ÀŠƒ á« Ç’C¾ÏÊuIÝoËI?DéM½×ŠÀº·]M’ƒõUVÈ«ŒgZ­ÂPÍ ÍV Ò¦À0¶íu^³W”èž]üŽg® FÒ7Ð’ÓÖct¢Ç®¢ùY¬‹¦ aµ§Ü­‡ž?-,rO¾ùÄÈ-ÜŠŠŠoŸü+SG‹!§¯Šï¸íitýšÇ¥Åg‹öjYP²›‰¬XX*}½@|Pg”é,â/Zï1^lôì̘T£‘Ìá#Ͱï‚Tr²,ÑU·})ÑHfö&L‡Ê„‡°{³4à¶GéùÐÕõñÕN<Ô› Ol’¬ï³ @RbuË@)Ktô5’Åj-ÛU¥Áïgð›]U~ý]û°Ê®k‹6æ®0"‰ƒ ÖæPÔ,…^¼Ôeˇe~[=R>ªñÔÂZ_Z‚ç÷›=>Ã:RïWõœé®×7PmHoñ™­q«f'Ù¬‡ü&í•}è’®À V ,<Ï£"”5a&ùAo+ãSÏg¹„S£¹1à£ì¹½Ky8½ü’À£½t0ÛU½Ø/’>(Ú ºB0X©Š¾üÓå7Òoqœ+ýþØçB%ëv²Êý~†¢ÎDßGÞßSµî3ÛuQá›É8ã°â€ƒEÙ/R^¿šëÍin¥dÉK9’všµùžƒtd·Ãºð¶ÇgTU®ž†áöÂü¯á)²YX­ºÔ'8•ëKh‹¹³U”†B_‘‰ÖG}?úÑünóÎ;ïxõ¨É`ÉÒ¨ m  ¬EióÉ %3cEǘeÝ9z¬X‚‘‘]Ð$Kb\…êÆL1 ¶¯ì8õµìlª÷dͺ–øˆ2ûtVgHàó·„Â#ZMèÌ|†3ƒES%™°â"ƒÕßÔ}ÿ¼sÒa·!ÉMÖ‡,éØðÀ ½œ Ÿ—äЉîþø8ØF=>C:Q-¬™™9(á|6µ9²cæh¦Ï&Í3ö¾ïåè\ŠÕéëBŽsÌg°‚KÎí¢5ƒe³ŽÃVÝ5ÚSŠéà)ŸVÜŽØÌ("YžÙm×–ª¡«a× tÅ«Sn©°›Z¸’cÓ^¹6})§ðâ)% ­RCzÿã”lhk~Jõ¯”ICa9á—‰’ °âÀ·;«i·ŽPï±LíÖ&¸5)N9fÃnõ5êifG¬KíAjCgÅ·%Ï „„MØqmU¼‹d2²,Ûç˜Y¿œJ«Û>tü/°•„mŒ‹¹%ÂXXFeܶ³.…¨™äÚñ[®NðLŸ£Ê„#Úd×Ui—ne†=sN²/„›¤*Ò¼ýë¸ Âò…°ï“*]æòèÙ¥œ§P?CØ´ÒöÚ@;˜†úù×aƆ$Ö3XÛeºÇ½Ä£òkg}([_¼ ¡cÞSëéÎ|fX4 ÏwÀ ò6Mù`ì‹Ð4g­øºÏø~G}§’¤²½r;3X—Xï¾û.õŸþô§ à7¿ù ãia Iñ¸P¢™ƒ%˜­b¼–“ÛaÕMÃàuw)†§É™ +^ð‡7‰ô,”çjA?H–ÞNŸ<"†ª,ч}ŸÆÓ„ô³)I½u+$-—`k¹ø¼5X`ÕÃP?_ž …iµ5îV,s°H¿¼åÞ²ˆÞ‹Ieúó–R¨ÛÈóxè@]ö>MÀé„`ž;Ãi]‡Î‚eзD.ƒ5nî‡AE‚ÏýÎyȆަ“íív8¾Ï£Š9XÌP½~ýš¬úúz¯ÀÊég­²²’^ÌÎU¿} 7%w(Ú&H™L'böõ8ã‹ ¿¯3áñÝU‹ÅWÏ`ïììÐdz3mŸB² d tf^¤d¹=>ÿz¼<þ"ãDÅþ8%6SoÝÜ‚W°°°ñã¥R½„ÙqØ÷o*©ôúCú}ýwvi¥h¨Ÿ·Ypš2á0¹(àöNåóÇØb˜×.1öúy[Éu«ûw4?³#U½s'ÃaÕuÈ=Nóx½â(Iz/µ?¤WiYÑ0kXt½®VoÁPž ͳ‹ß˜­ædŸûó£TØë¯£ÛÚCaË…âçk¼k€Eì»ï¾ƒ÷Þ{Ñ,Rå ÓæÆï*ÅFé:~ÓL'Þ‹ûLU(ÞyçÐÅaЍ6Y÷XË®ûï' öƒ{'U€e+"¦AWX^;„§I™‘).H쀱nÔŠi‘Q­4ª¢ˆß}ñ”òÙTlô2û@úÞ*;†IÑé¹JZX)ÓF@^9-ÕXU@ý°ån• ZƒÅÁÛaiܬo¿ý>øàæ,»¾*áAîÃaYf’§§R-,²LçvhJ5a•ˆE–Gè†ÊŠxómîkxœ~²MV1ˆø'™‘΋·B ˜]„ƒ5ǺE{0"+v9X[j5(uÅ¿û3Q~«»îRû0:º½åË l9]Î&…#æäâNE®?©2‡.qúÛo£$‡ê_펎‚VWÖØÅÀ:û‡ÃÍÍMš¹"àŠÏçC?c9ð;ÝáÓå`š?êletŸ+'·CvSëÑçÝ?HZ7ëR»”¢lï­ÍˆìÓ.‹–-°Z ?Çr%1 ¨8Ï®‚‘ºiä`Å‹(‰ŸŽTð9ˆw×ð1f¢ü¿äRûEÀÔhé$ŒÔžž«Ý;°ÂªAŽ-bñTÈn”·PÈ3©š;©&ÚÊ»˜”i8/×pöÿ G à?øüä'?FW® `ª ¢fšïõ¶0ZlÔ93–°µHf1û¢.ÕºHâ‘r¹|´ÕúˆìÓvt×nQ5ö¡Ì±+‰aPŠó\Ì`ÅB‹¶iáz6>‰ u¡ðÊ2XT³°»ðR]7h+­ÂrOW+Dw×餵7ÃêÒ› §OO«a¿*°¬QÏ8 ÖtxÚÝ 5›9˜ÁŠ'%w¿ÕÆÿÊésÃw]­ ¼ÍޘචH®«cR­=’>Q q)™GL`´ï1ÌsZ#Ç#+ß R ‘б —â|o.6|ŽŸµNÀÇù°lt—-xÜ×5YWzŸ6‹ó.µÒÐÖv oRñ^Ò˜^»µkT?o0}Ì'WsnfæÂß9¥¨‡î‰Àý[-‰° €—--~Ûá +ÖþдÙ2b6ƒ5®k„¹ïë"ì ;aS£¡¥ÉL™O‹Æ@ FÐböŚřl/r’ãF}¿åÿŒÏ‡ü‡ÉWšÁ“f]x¥azz ‘ CzÚ˜fµ¸KTX*ué͹«ÙYøº°lvó…¨5fA:Õø ´ßvÀ’µÂ¡—„˜ÁŠq€µ#âCÙ:+f9Xʉ:Ê1 ™º©‘JhXLÙ¡ZøšËõH•_·ÃxO ¤Bä-%Õ‚Dr±cCnԂƈî×P¹Fº—iµ9Xć3´®A 9XQ °´]`êO‡=©Ô d¼æ@ú3ö•q°ˆ(Ó(O颭Vq•ÎʫӧP™£€5V9© Táõ7GŽä.<Š(ÿŠød±zK–-Ñ1Åj‹8× =ò>3X ·æXtRí|ŽT¶õZ¹W~Ÿî0&Â’îbmÍÀT5LÏUÒnJÔ±M³ZN^#Ñ›“ömxí^rÐÌ¥±”v¨á½“i)à÷‘¶>ƒcÁ·»!jîw’¡g6üR° °7‰³Â>a’óf˺"h|à"»ly”Æ„™±$IKɘšB˾f°â `‰†5+ìëéLòôç©ð¸<Ñ£”¶x“}ü+¿éí¬¬Cÿõ1¬ ¼è8)º‹ƒçÍZíÀ+|MI±y«ç_QYömhÉX+ ½/ÊÁ"”°m9XQüû ˜²÷æÒÕ íp­$$Kr‹ oí^ìoÌ DðUû…ö­½ý2Oúx’û(¿r4âyWt’éÝñN¤ØÉ*.„!a æAW¢¤²,âøä~hjì$æ+®Aú³ÈÁŠ'€EJGIj6–3Xç;»ŸUÞ謸r•÷yµ F®#ˆº`ö¥'ÎU–°ÚHaêÂh[`”¡5uã‘_æÌ,†ä]ª7ÅÔ ùmDl3XÑûûÉr˜Az²¬ÕcH„%½ŠÅÐo)~k÷acFõ2 ,Š[ªôm¹ûÂm‚Ú_6#wí0˺sª7Wãyþ’êr™&d·êa.³f²Ú¨`©ßU‹¶{n…¾ïÇ‘‰,,‹ò9Ä*ÿ*Ïóaaúí«Tånçÿ¦=Œ't!ˆº ‹óÌA÷#4´Ï]¥à)°þÕ#˜ç¶Eü÷?¾‘5‰ût¹“©1&™B~ÅVÐ@y¾®×ƒTæZ޳ËïÃñý{ý––9i73¡(y¿H«¬Þ¦·óUYf€¾ŠeXbÕœêÍ•­{HjÊVCL”Þ†g)™°É.¾ž#¿‚§¨. Ô1f C€Å`€51R|!‰†hË`ùò!u ¬Žéß:¸â¾‚Iñ‰¨ž©QMž/‘}‘ej‚îG8Z=Ž„ÂGŽÑ<9ŽÑQRäyqdæ]~íˆêõ05ƒE¼¯tÌæEÌ`Eë§LmæS0ó ¯–Åoõ~ëoÌ •vã²Â÷‹T·ÊgÜ‹2xbè(X¡„÷Ó¬Ö¼ÇùKÈêU’˜k¸/²³)¶»qÞœÕûÄ›ð¶L#åo}¬Â V¬qIÆ…$¢‰ƒåÏïM'Âuï[½Ñ‘*•ûUПn‚1ý*èK&`•U ê‚ü!Í ûŠó-°šZMUŸýµÎ ¢„¢Š¹¸‰a0.)²ÐÊ,ä`E§ïKÄP³t2™&÷üµ¦$YJßêýÖߘA~ÓøPÎ…¤TôU÷¢Œ¼¸“¶áÖÛUZêyþn ;¡°x:ûࣜ:ÙòæáÓòB°L«¼ë_i4 ÔòÞúX…¬hX6 T^»°À\,d°È gR|¼C¢óå:Ý:ŒTLQRswéªhMS&‚¨‹e_Œ‰|ÐÊüdö*Θ¤7ÑŽæG~³12Ù.³qàôºŠ‹r 3XQê{‚6(Ú:X©Ž{>é¡Wx˜Æ˜ áÄŽIR/VAXÄw;W2‹ †}àV™­)V{ˆ®÷ÝN—Šf²>âœÜƒÕyZ8¬)„‰éïEIr9M¹˜ÁB€ ÒŒ ”ƒ×ã–E¼`Ÿí¸¨ÓƒW<î×AU ùs2xOæŠèż–\ U×ö±Éó%œ,¯jö –tìþeòÛü6‰î¿·EEL1¾§n*’ÐsùLÑéíun•Ô¿m'…ã‚Ä 4y~ ›YUœÁŠÂ3vú©ÞoЃ{¹Òy Š„zº|úqa!Ýn&­ìw `ÜÔí¨J¥ÐbOC¬KU=ЧOˆÈl$ZœÌäHoθÕô¤Ê ?¥åû—‰»XxË©õ§7ƒÔ\À/‘}±'6‚ªc6`܇ët[Ú:£V Ré¾w1Æ™9èåÌÓ&ǘÁ:õÙ>ˆÅ‡˜ÁŠBŸŸ™m~åÕßoýŒN)ƒ19¬ãýAõŠ%E…ù¯áù Ïkõ<§‘èÍI¥î}K[+¡|pŠÊU|Ê=YNÜL¨Q>ôc>—ZIÅ#f°`ù'¸ëšh5I$ÖÓ£Šè.¿ÆéÀ‚£“#3p›{tée’¾;Ûn%ù—áÅ +‰ tÊW‚™çRì*ï®cæûÄ;ÿjläI²¸ŠaPZX™•>c†,†ƒCƒ \=À 4fAN^„Úø²(,ÿÊôŽÉP[ÃQÐzs"Ñ‘›ž•õ^ÔZ`eb>-.¦Û=L)aWèUÞ+·ETC9X°ü—·Ž”Ñ&”ñœÁrjÂèMmã¥éžþ™K5&3®NÞ*Ma‡#«€¬“~„#eÆ7âyè,ÛpÍjŸ¦Þ¤*íNÙ³Ü:Íý阭ê¼Ì¹Fä$ÚÛŸc+ ³Ç•ŒÎ`yHè UÁF€Þ„Dž¥¯z5h½¹®®§nzVã÷+¡Ma‡y«~Á;*}žS½mP¨ó½÷î6Qj f°`ùç` äÑIñÌÁ"Þ0ϵ¦4`¼Tw- Ê•_jO*%9FLaîG8œoð¯eXi‘Éí}½-{09¹BIqá/a i4ªuæ#ÿÊ›–œò8¯P + î2È&J£ê¾,´ð(ùÜ·…*·=A ÕÃgõ¬dU R»|V^Ÿ8¶û„WÍívÐex×Áê«‚¬Glä`!Àò¿&?%L¿T“çXÉ`0•"pyð`å ¬Þ¨‚––—~KüU:‰Ö˜Á Oö…tJ2ÇìÁDþ€ÛûF¦A©Ü¦Im¾œ.5ª’‡bºèà²çZoÃfÜjaEsë‘PmóܨÊ`qö“á)¿Ýo§ Ò7PÉÕ9AHná×4›íÔ³ð×¼ú¤—éÇ ò9‹/o§ðKÜðúË]¼K›˜Áòc}ôüèG?ò»Íwß}|>(x"þÍ7ß0 `‘5g™"9¢ëéÑâOØ ®^éçÍÒ²_~óQP×½¹²wÁcù 9X—ã‘åVA²ÿ%Bà,§×¹½ÏR=HÕÚ»yËnåÜO“2ã.†ÁúPj„!+ºüiWÔnä2žƒuÞw»`Éì»I­ÞÃMqðZn ®6Y«Zô¶ßsÏÏZZà+6÷ô8Æïg`èIû¬ÍK?Çè¼/tM–vvÂW7oÂDâ˜x= LV¹GÂII£bsK„Vzz:¼~ýÚõøÕ«WpíÚ5F¬­!!åÅ;ÿÊé"M¦z|WN®€4Û@/Ò±:­G¹o°.ÉŸz+M}ãÍ C0%ŸöÙö¦£ñ¡Ç{ž'§CeÂC8`çc ƒtcá@P}Ñ™ãK&“<4Gå}ùþ| …¾'¬½«°Àªúü]ºÙ¾‡'yU3t4Ë]}aw᫼<*$Jª GÄ•0ixô!•3+±wëý÷ß÷X2<ÿÜU¬þ– ÷ ŒÅ V›)Ô“·ý®õ«Ó¤ô"],n£3œPcN*Õ*®y,A}ž’‚ƒ÷%³/º˜zÆ},ƒ¨fÑëûž±S1†!ø|êm<À V45yÖé`n¨‘÷ÙPÇŒÌ'Ið¤í®ÏæËÃ- !µ£¦‡”CkQÜ‚ŽÛ¦Ó1Q¡€WmŠM8›Ã}- 65{ô!è1ƒu•ËÛëÞžsò³Î¬ÊÊJºÞï¼ Éßp?~(h€¬)®GÖw0ØÇÙ¯²/õ~&=nÚH®Øg¼ÆÆžƒ5¹ãä"Í+w¬Ïae%´øÏO/Ãàu%ä†üý„͆ß\¿_ed¸¿³¯ããÓǯ²³}¾>—Òã÷Ì^ã?5µ š{tï˜ÎóC}?©®?‰øý‰‰ŽŽ¢ò÷©T ³Ô0â~{þo0ïߔܣ¹9ý[_\„á*+ì%—…t> «7÷տÕêú¼ Çg¿.,„-˜3›ŸÂ°`Ôc5nñ\Ò@÷TÒ•ÄÏ×x‹,†e°ˆBîJWñ[Ÿ0Ù¹»lPÊ3|/íõï«Æ5êh~L5XB‰ûä •fZÎΨ>u€†_߸Ÿ²X˜¥ºDö…V–è|a·@Ï“Gï¾;@ø·þ±ãïUg°ÈrªàÎf°¢ÈŠDpk)=*3XÄ•úr*3qžWöšÇEÁ²óB:‡Uœ“ž„º~;–ݹj€E4±H[-aŸT*ŽÛ÷Ú5½ÐlÅ Ö•¬¬¬,xóæëg?ûcINŽ#÷꜂°Vœ6»wå`Áý]ÚœÙy‘Š›V]Õ(AK4´[ÀšØì6p~^\ _:ΗÏ`]Êɱ‘ÝÔúìýh½ÉÚ}{é˜}ÿ?¢Ï€uYÊN‡žŠ5¿BºÈ{bXÂÞÖ+Æ —ׯfÁ“n÷¾€6ë<«ç€5¹—ž“¡œÃ¶ÔVîÂX'$Ý[ÀÍ©¯ÕÓ9 *y–»°©ªnͱƒõŸÿùŸÐÖÖ?üáÝ?Z­6ìëìs£££´rðl!ù-LXÛJ% ë 0ƒuÎGd ´¶·˜µ×¸]Ⱥã8ï†XAhEV­ë3~áUÇÍÍðq]|•˜ˆ@éÙÒ7PÄö.6*؇…õQ»ß/rsáã¾õY@x8*V»¹K^í Wø†`‹N¸KÛåzœµ,¢9õËb.Í,¹ W&A9’ ×o‡<ñØaó@ؼ ÓA*ÚñQ³üû› –¦¸w?QÖ^IBÆe°îÝ»çUVáðð~ò“Ÿ\Z¦áüçžýŸé:X$eÜ2—ýÖ5M˜î¤ñµÖÜîµÍ ¿bË}TÒ"ÑÃâ.âÙÜ*¿JI-µ>îé_&$ ˆº¤†“øºl/qwÜ(Ïf£Î9øEI äW­ƒEµ„²'½ŠNf ”ûeÈK稃Ù B›ˆË˜{ìEÇŒÍÞz7=,›¦Ú7aŠ3 _…x]ÉØwÆîUÓ춯ص´¼€1 ìöS‚ýøhÑ•ô!dœÉ\ét: vÎgœ~ðƒĵ’;I·o'c뜓Æ×jC—6+«0PawoÏ’UæÑÏ]YbNªVú²m®,ØgŽY׎“(ꎎÂ×ׯ#ˆºdöE•0SƒSžÍµë×á!;'z—‹‹áeS|†,g82Xªt™W±Q£ljy/Áh\Æ ƒ*´ÉŒ¹Ç^t̰ Wšá4Cm©ƒ»&ˆÚ÷àÿed„ÌO4f Àà½P*}ó ïߊÁ`³~ïØpΕ-·2*ƒõî»ïRpu>»ôÕW_Á{ï½·‹’‹x”s„Ü+w'©_…ʳe!D*ËÆÜgA7Ò $ù%ð³¡4åÌšgö L?ýŒ_:ËGm' ¦Fø¿°.¯Ñ”ȇ þ„Gì»xË®&ÏQéeep$Ðs†qîÜlhÛ-p¯ô€ö}Dî3|EÞ÷¦£þÞ,×óh¶ßµª *‚F[•ËyÖÙ ed„t¯²¡¯QG[—ùŠ]oïPõ绉ŽIRÕ‡ðÊVJJ *Ÿ~ú)XlíííAZZÄ-ÀZ˜ž†Gw¯t6ÂT'¯µƒIÞûØK½*ˆŸˆ\ÂÔ¨ÕoÜ !^*u‘ÛÿÓ¨§¦\3ã¯Ï1ÄÍ,[â]ÐÖê=DFù3Q½ßŸ:Ö¦J_9î]LÈ`‘8«ú=—·§¡·xñÂ]0ƒn‚ªUvÔg°:¬7O+ ív° ²à–Ebñ!<îëƒãÂÂЊ5nASÍ:%³ûŠXüd¢Z3užŒæ)8¬üRŸa æçç½r¦ˆonnÆ-À"=7·¯t=©Nˆ [u×ÀlÓ¸ÅL(|óÜ6¿™“±v“߸B¼™}ò;üUr²·ã99XIxIþÐ:« äÜ1‘ÑáLmtKÇý`Ùh„/srÁÁ"…ò»6Ïb‚+ŒŽB±,†øô@!£*/:fT:@â¸ßÅ+—dBƒQI;j<‹á¸¸8$Ù“s‹¡´ä¥ßÆåÉH›Aoh:yn¤øÆDÆÅîÊd666 //. Þ© $³¦ÙÛX$ÍjiÄ –¯’ày˜äî<¬®®§°™Uå»ý«†Vúm‘Ó»íÒÑ"™ª/³³ÝfÆ;n_ ÀºTöe—ÅéM½GæPÇFí>nɧÕÕtÖþ™¥_2ËŽ )÷œPLá8WJ3¾˜ÁbFÂGå ù4)ê3Xœ]l [\jêC긣¤m›vGGḪ*øŒ°ã>û²®>`Å+ùl‰°† X4›a¶7²ØŒ‹]Ìé`]À"¼/= sœŒÚ±:ä\ùqÕH¬NL¸ì\îxœê;{@*û‹í~ãNÄ pÜ?--u/¦®¤]ÕIEÔÀ­ÅÀ{†š^Þbn6/A_Éég¥ÀÇßW:¹«*|s¦’ðc$¼_ˆ?¤NÔÈxJ¼¾evkquûœ˜{CC'ŠÜƒƒð‹Kžáà`‘‰BŇG`žskG4žØE³´½]GÈÁb€OêïÁ}3³*/3fè‡2¨Ö¦ ‰N‚ï warr…>÷¢)øIÔ§%%°j0Œáoò»7`ùn"ÈôùŒ£–ÛÛÛáÑ£GQ°¦§Õ`ídþdàRÝsÇl¸x? 3Xœw˜¯JKA7¼Lé@­‰ÕJå¶ÏT³¼Èà6hî‹Dn3ã9«Þ8žÿä{põŸ×®ÁÇ7n ° 1ûbIl…±æñSõ|ÞxT‹Œ~A,_žNÿ§ß7¼¾Ê qCbŒ Ny‡CÂ-˜cÝ‚Gì,è¹½‹,ø˜¶í‰1“ÁÐÜ ZX»í•Tݽ­ó1]a Y­š›ƒXÕÕô=âGè!m­Oà°âäûì3àp8Q°,“ Q%ÂZO]ººPÛ„™8nm”cäYã6YLð§ÁX) xÑ’,É€À;¹W.ßñ‚¡S€åP[^RÔ¤ ŸpnH–âR’Ÿ•…À*D·'6‚®â´ tàædT‹Œ’Œ!õ’}!ÙëO¸\Fü.ZØQzºL(i]ƒ5V9åÁuVl"Šn@é&;fîÇm¦X \,öq2åÆ:i/[[ƒ:oÉöù™Õƒ@ÞÞ~ Ù‡ŒŽ £ÑÊF¡Q‹¾“ Æ$i°l2]è‚#¥Þû=ÍŒ˜Dƒ×.'ƒ¹A -/\º4Òì}æNZêÌgΰ¾LOwKQ;gÆŸæçÓåÃ_’*—;wàcÇãpŠ4+ã¬MV1ÈòO‰V»Õ ¤‡dFô¬œª]G%V&'áǽ ¬'I7A”2áàºk·àˆ}’]ë,]ɦÏÑ–ÁÒ ¥^9o(œcÉÆ­ŠnƒA^éGùðóܵ¯O»ºàEÙÝ—<•u6†|þ3ÈÝ/ctì°¸R«ÕQ °Œú¸3Ë¢(žôaºÐ Â1¨¯ˆ›±ž NÔîõU·à0…/…ô+$} Ïǽ»û lÞ<-#þE^Õr9Ïíxî8ˆZ÷WŽ×·ÉrP³Xýšðw VÉdY¢%OP¢õ(Ï@@4gäȹàÌX/X­ðqÕR‘â`9ì¥ÓLñ‹Öi±y^Th‹É~„ÑÄÁ"«¤YJ‹VÅ* º8 ãBÎF­ã¾úÔµ¿G"<B ë…ã»­PGà/¶2:vŒ#¹Ÿ4Q‘ÁRÜ¢­\ÊÖY ι˜–R fÇç`+Hyõ=ð¼¨4¨g tÆC˜¬ãñ^Á³é§•aŽYÔ™ÎðΙñ!Ÿß$$ÀÓövšmü,Hñ¼`ñ¤¼ÿW))ð2''¦3X¤Â­úÃ=°”‰]~´:9fŸ;Î× 9; _;Î.ÁÍ W‹ø,ëh«õ´÷ ¨xö´à#K“ý£)ƒ5=%©8xµ)f°‚X$sU^^ß|óMÔ,ëP9pwÙþŒ ûU×`NRGõ?B‘mع5°‚uöQ6´ÜWÃó ”Úb¥‡Š5Ñê¸ûÈm»_œÓÀrúÆÈü*9ù„Ôl·Ãçee…%)!þç?§Ü‚@âz¯³³á%‡ó<,gCíh÷Ï© bGðK†póÙy0”ª£K±Ê¢Sµ|U† û^±›Lý Ð%ÄÔý˜haíÔ%BÕæM(1 LvšQÜ…ç55Ü߆4f’ -r°b]htVTäZO''Éf©”yðUIU™ æd!kÏ‚™2Ì`é¬Íb¸ûÿÙ{§6º,mü_›šš­·v¾ú¾ÙßÔλ¯m@“sΘœs"'‘ƒH6LÎÁ&ƒ ‡IïìNíÖžß=ÔRKÝRKHBÂMÕ-[RK꾺}ïsŸóœçHgnYËö¤‘ŽÉ"îä«2–Y"Ëä÷æÚÏ Àw˜»ÍÎÂÔã CËÍ þ†ÊÀqh®÷)2Þ‡‡?jë15Ô㥧³ïa©N¬»fÉ>D߱ƲeèQ'q :•@wÇ¢È`=`èÉ0Xä…âöÄ`a¸s3á xº@dGtt¬2×+È ËÝŽ¸cÃÌðÌÎQ‘Áâ}eÏ>XÓ•´Vžvç¶'RóP!ƒ…€©KÁ¢K(ÀšIiíÖ.D@¹îEE‘š}Ž»û¶¥šI"@ij>m–·`tä÷2T>çŒœ× e†ü‘>‘Ï9#7âYTÔ£Ö`=¦víè»%l½%$¾7¦îš…û]ò‹B—`Ö/‹yNé˜ ]u¢ë![·<Ššqê“?<ßïÝ»Ñ`©êÅâ¿Qõ,9VÞÀŠzî>>t 4*³orêD ÖcX¨×–zsv® ¹QÖ*³˜Ò.ú¦²úx‹ –ÀÐ_O-ö«ªàD€àSÔCƒnXõ­°žUo’:|ráâ{äó„ìŒQ3‡¢w½+$„–P¹6pÜ%yýÙ½cØQd°ì¢Ýßl­©‰5&– 8 5±Vb]ÂtçXóˆfYet”ˆ>XÙ-þ¾Ê°$àœ± CmvÃ`±Š?Wͳ 5S³QRŽÓ  ÊÓh[¶#2X9DˆéÙ]müÀ(i9ÊÊôúcaVÉIn¶¨­2¢EµB±D”'Yƒ²ÔuÖߨ¸£±õ,Æi‡|ž›÷ÏÄóðpØ­®6Ä€mÕÕÁǘÑ/ËNÚWX”Ëu6[6ôJžCÜ/ÛðÎE­9”QQ õ€­_ê 6 ÒX[AP9™•k”Ͳ«¹ù˜Œ¹â5¶YûNssáT_Hp0l¶´—, œ…¼¢w¢KûïË—/Dëï~÷;»g°Ð½¶Q®ùÈRôÖW¬´ÍŠ›ÜجVÅ2 ÏÑ´Þ÷™Ÿú”YVÉLó}¤.ãpJцÖMη3Ƭ±Ï>>z½«.Èy!«qm@#v…lCg'œc˜Pd°l¿Ý¹ùÏhd›ªÚ9™/L-¥d‰>ÔN*@V«5fXd°ªMŽCw­ºÈ³äОÄoË[u§¥hHh…ââ#ð[yaW ‚ôŠUÎ*%'z@NÉ&ÅðÆôåôôKþ‡Þ"ƒ¥ ZHýö·¿5 ÀB­bò¦¤¤ÐÏÆÆ—hlXÒÀZíê‚¢a'½ï÷Ön"#8'dš¡ÖÝ ³MÙ6O·é Âc7 ýBÃ}Øwï†edÑ}´4ަQÝ–šý:IHÐqq×§í¸ €ŒOèŽ@邜ê¶®ÈfBpºôö¦@ý$+‹fŠ,ýàFÕ¿u­¨ÕûL@3§–²°f ½k÷!šî6ˆ¬‡²hn†™:ƒÐi5œÓ—Ás!‘¥/-’½¢Yr/:ûíFƒ¥ºž‚êuNñ±sæ³ÈH“jù––€ïzŒ¨ÁÒüûýïÝdaD`ôÓO?ÁÿüÏÿÐ秦¦ ªªÊèÏkiiÎÎNæ±L&£u ù–9¬­¦&H™q2øl—fÂë±1ÎA‚ìÖHGªÈ` ÌëQPTt̰¬ÞðVVM¼Üc8rS;‰Ÿ’›aIë&×·3>"€žOèŽÀë8;›÷>)Io&᥯/½Žý²²{ ~Ô 雿`ƒ€õï®®÷òœºoá{;íº:A¿á?ž>Õaº¬Ñ‡höÚàÑ+2X¥¿*€’5ÀrœÈ‚è’e­V?§Ì…†®[Sfñ.¸¹Û ƒå4› 5ëœYòGzÂçXäÙ”*(••o!d!Cd°4ÿ04ˆà ÿþíßþ Y¯ûçA&] ;ªþnnnàÉ“'VXkUÔ–ÁаT•Æ;€PGÔ2’(j«¶©RÆÈ}S.¬qŸbÆ7™ø}IæÛI8- Þ ßÝïåçóê«03p¿¢‚·_^õe†„Pð^h—vÊ`Y£’ûüˆlœèŽ˜€Ñÿ9üÍw‹Š¸“ººàÊPòô„¿“ßùÀiLö¨‘ô‹Z¨‡*‘£H‚¬ ‹†ŽF¨kZ‡ˆÖæ97…º{Õb¦ÀnæfÇÑ|¿Al¨¯:$M^€EÖU>B_«­Ý…ˆ—%V×™Ù´ õWïß¿§ÿ";ÁææfúÿÍÍM64öY0í¡ösš A¶?ýéO”1ûç?ÿi:À’êÍQµ—ɼ.µ˜ W4-2X[x§œÅDQÝ‹€ÅåsÌ0Xš¹ÈXYY:¾eúvÆ+dܢؙS¸NP•îu]zÂ?—aaTÓ…áÉ+›Õ=4ƒ…N÷oï@ë^u5=­Å7¸£á,Wòè—†lCÈü÷ÝÕU‡Õ´J:»@ü“-˜˜¬‡°hh‚˜E'f¡¬@ãÑyˆ®xÃÚ}[»apðÖæ@._Ć—vÃ`¹õÖÐsÖ¾îõöv8LMåŸ?bbèhlb5z¨Ùªò”§1{: ˦¬Q²ˆ!È¿ääd–ÊÝÝÝ$ S˜*d½`á9p+UÓü‹‹‹£ñ~Õ ½U Á'Lã¿Õ~<¨xAÝÝñ}šïÇ/¤RHß×û~cûÞøšõóléqRË_cúïzRaÈèn‘B-‹j±Ò|¼. ƒúŠ}ÚÿCC›Ð’6żþÁÓ“: /Íβ~Ÿ½½=ßKõxvxþëéS¸&ïÕþ¾ÒPàŽÇ½˜€Owô¸öù}$ÿÞñ‡Ç¡ ßÇwþõø3¹¾ÿ$׉ ‹ïø›;PaÑó †·d¢Æþßëì„ã„«÷Ç'77ø3é…7otƵj˜›ƒo0_ãø>ïšÌ!_ÈX» \óuí-u=J× ,›ä<{}Œ÷©=œoo›/D¿½]/P¯T(Ý¡¯§UnÑêø|TÃÌÎÞjEgg— "ú;¸û[|~Õþ×”ÏónSÀÌÌ®Îõo Á!ÙÀòÍ/&õgÿ¤ ŒZmýñ> ç©{àº.h½}p›†_ý•d”$¨[9>¶(ƒÅ%×W`Ú ƒU.§†‘o›"\G@­JaE7ò€}O‘ÁÒŽÜ!^+ CqBt/»?¨L^b<°ú^t©Ù+²x¿»cGŒÙ¹«K§Fòöf’°xôøjÚÞ9‚£ëÊFJ­°v—~~ðW!-«h°ÂÃéNX•r”–fõ¾ÀqvNÀ‘¾1u*õõúÀýI+l­LLÜhÔELÐ,,‘Á²n‘çíø§Œ!gàp 45ÝJJëÖÀi>²XQE+:BnïÕ8»`°¼*•´Æ«N¦<™ssùõª|™ríY‚ø¶a«­?>Ñàž¶N‹1¶Ç`¡°ý/ù‹Ù­|È¢ôíÛ7–ëçŸ ðþð‡?˜°Pÿ³T*,ƒ¡z4XÇ@e¶_œ#j«„R´ëaS¹®SÇQÀ:rv‡Ò˜[€U_¿ ã±RõëQQF§ c»"»/íPŠ—¯XðÈŽq-º˜5xx§+ ø >ÛÐ5&`³æÎø¼ÎÉo´ØÛË€Q,Wôîï ,X£/“P•]ú.;ûAõvõÞLJlÖi³J%tÔ©å$Éu³ÐÛ{«µjm[ÉhHö} ½ŒY]½A³yváåWÆí§ÌW<ɪÝúNY™Î1Öô›tΆ†ºM‘Á²&À’B»w7$äíBBþ.„„~ƒÉÉ™;M踷v€d#Ê+öYïÃÂÉÁý 6Ï`¡VxÝ8/{÷Ì•§cüŠÌ+h´lJŸâ\ŽÆ¦VK°­†Dé8 ”؃…ÀJÓ\™£‚\5³Mù3䃥 °¨þ Åôü㡎ü°¦f¢o‡¢#RЃÂF®Rh”ÙÕc~ÇêƒåÖS«#¢DíÒ•€‚ÏØ¼ûiú3z`m{„0¾F4LÇá¶oÈ_ç”3íP2‡wšÍ‚§\…€ýýYîñ¢£ ‘¶&{u@A úÔèc Möp"ŸIõ]€’ŠùÑ #¤¥¹žÃ7r¾¯&ô Ä7à†'ù“íÚZXknÖÉ@µf=Ç=‰/”¤lp†sD,ó7 Çö…wÁB‘:Š¡Ù÷´|+'Àg&‡IÄѬ™ÞÑmó>X’…x®ãëƒwdSqÊ‘ù‹¬ð OÒˆ–X´eµâØM_¿²ÛôÁú¯ÿú/¢L’¦Ñ¨+ÙÉ£—•f¨ÏÖÜqPtvGúaB7$°]UÌiÑ?å#2XB'Ú!V¹cµKÏ[èn1'ç”fTQ@DÍmž9†vƇ˜i‡zpáܪ¬ÔÙÁ}Dí6HÑ*º"[±jÀëx{gGp”šª$ÍÁ¾ hùùÜS¿†RUZ5†¬®†ã{W6Z‹FÎáY Mø8¿={Æb$@“]0Ôùù²5[cÜËxWd°,×Z¤Ë0ÿ<‹S&¢j‘u“*奈•æóCCóS3có ¤¦ÉøË0áF«eèl:ÜÜtüiÙ Ù °Êú-ëY÷"<«í¶ýZ„¦ºHñ";7Ð2Ŧá¡Þ,Ò‘PA?Œß®V*Ru Ø*K!bE"ê«„ìö|!°jRGœKµK¸3Àüô;”‚¼uŠ2÷Ô‹ž»;eÁL¹¹wKKuB=_0ŒÄ‘ÐðŽ€mðtCYL!á%«°4¤‘©ºôð€ï¤Oçî¼Ûö (ûb Wô?“þ?áb÷´Â®ï´¼§¶àL`V —’ßå09Ùà˜Àä†_àhõô¤Œ Ö²¼~àòBSáeÔgIÔGY¶¡Îª)|>øp':ݵôÖIˆ¨œ%kâŠN,¦pÝæë:Aq/?ƒ…„ÄGY³ÏäþŸ6½|SYÝ:-/d€U7Em5¥ ’6]ìy{{ÉfOµÑ6&XÐãùN‹åqº!ÂÒDð~ç,2XBvH‹1ÐÌãª]s̃æ²E¨M›¿ ƒáÂÀëÁbhg¼ÞÔÄ õ @ùX³¦yRþ¬íK”@ F°Å¡yÂKÖª±‡"m¬¨y ÛUU”Ê77ƒ…¬Ð5– 5n¶ Ò6õD7}kõÏ 9‡ÍšA“þ'2¦>jGŸWÈ€MOSSÅ+Lhx@ë­‡?„…Ý–œ25ìú•]ØWd°X À‘áŸa×Ù.È8_г‘«é„¨Œ·Ð×·¨óZJÉÝ`Ú2ƒåØ]õ##z™]4Õ¹¯È¦Ã,U««ß§9ëÔlŒ«œ‡±±×àÓ*¿-sdË Ö_ÿúWª‰Ò Úƒõ¶²^, ï-‡éìtgʃÙ<ˆ,råAa7·Û¯PíÒ´cÔ¦ÌCkÚã¸~J~cSµ4Ô£<>è©SG™6__³qB‚&{¶D€Í—XÈ&]óœûZK‹^àgª~Ù2´°@—^€…¡5-šzåäX|>} 3J¥ Iÿ˜Œ©+®ä‡˜f,\’yï¡4XªV–·CCP&ÕÕ›¨K½¨ÁR·ññY(NÛQWlå¡õŽÀ}I3ã´_+ª]Õ±°µ5á½ :ÆôÜdcq¯iøìãs¯>FËɸ2ñO] ®x•n("ä],Pg3¬ÿþïÿ&ƒnbÈD£)xG † ùŠ4Û"À–Á[ÂÅu“U¬‚ϸ@È[¼lr7b )¿ÚÏ=묅–¡I£ëj¶I<”‡ÏAOB·Z/¥‘ÅgìÎX;Ôƒ€MŸOÒ{2Áhj™0£L3{Y#>ýŽÅ=žÈyóãì4Aµƒ… ä  >è©S¦ ãjïþõ¥~[Bàþ•€<¡“þfuµŽÐÀ·2,êÇ4›‹ÖgÌq2&BÚ˜|CNlÂîÁV,ì›ú„—·÷Wh(ÖúÁoœŒ¢´aœ^¥Ú4ƒõ$vƦ&õöÇQ~>k#ŒRŒ>Ü«üPϸ+ê¬"QI«¸5¸~ÑÛEkFÚƒ¥2ÕÎ"4Å\ô¡Rš7aÁàz,\?ÕßàÍÚÁ,É; ¼ßIÔWiäÃçð4n›²ð¹'ä¹ÑqîpÄ>¹q…h—Ðd1×g &"+™Ø¬ZÛPÃPϵ†@›êtRSõ.¼š šïiÖàBϬ_±ð,[¡~èn¡ÄâÏó< …eÇ))z-,¥1º"ÀŽ/„ç¬ $ͦÁ"Ÿ‰ìØqv6¯‘)>N@玸¾ŽP×aáþ¡;í¸8£&þ žU¿#.°˜A¨ùú[5¯ù!4XÓQeÔt×”…­3k†hÁ]K‚¥r¢#®a´×þ4Xhæ:ëšJÇÏanî½úÀ»zÌ¢™„B× œŸSÖYÖ¸öm6Ì`QI…Æ8G‰V5¸/÷¯˜æì!™~Ƙ\7·Ü2X u¨ía˜<›Ñ`ÙÛÀ ¦ávã2ІaVѤŽÏ×û@à¶Dd°´Ýr§JáEÁ6+Æí —BéH/ïM6§Ç{H»Å?Ù†cg7:éª+'dgüÜDÈTPi÷Ɉ¡aĺ:‹X"¸Ÿ¦¥é=¯KÌôãa™Le_P—2;>NÝìù@%Õ†‘>æÌâlk£^d¦ºÃ£OÓ65ÔŽÈܡʼD}à±Ö5 /й»;gZ+D¼˜@MwM§Í/ÆAáY[)˜ŸuÃcvÇ`Õå/ÊSÄ­Áluõ½>+¸jš2EÍ`…Ô‚Gá,+‹K˜…µ ®ÇHæF4/VoŒ:h{šÒB¤´Ì \íûÂ/qÛfY.e,¶Ö­±›Éì´É,B{Xdboï5êÊšp„™Î;AõØ lÇ=ÑK»E7Òr’ž&¾ÿ¬¾ ú§”zo\Ô.SÇï’,~'F²œŒÓ]¨YŠK?¿{fZÛ# ÷=¡M $I_™[3„ìŸÊSŽÏumoyv¹^<Òº£;ü“'f)¸@î–•” hT±ª˜L–¬×Éã\NôAA·:<+賎Ÿ»CNö©IãT9 /3@Z³caÓdGïBoÙ˜Ýi°ª¨,ÇöªK†ZLÝ•J<ôüÝ4¡µJVF’ÚF]ÖU Ý1qfMË~Å”…}£e6ê5Z NYóà´l–ëF!=ŽEÕwz·w‚d%Ê>|°ì `íÔÖBåx˜Q?Pê+'ë¼](”ò|¨îv°éÊè°:zÈ®x|ên©Yrƒ¸7.¥pãæfTAb\¸ °6BvÆoËËo]¾1 ÿ0Ü·(ìµ–G’Å[DËð”«hi†îË`¡î,%…©ÉyÍS.‡êÚx2=1iäD˪Õߟ=ƒ3OOóŠñ\æúûá;²ª¤Ï¾¸ºÒÂî,ö,¸ïÃÂÔaH2~ÑÝ U1œh-<šîbÊØqZ8‹NÑP·fQ‚iøÕq¯¡#Óþ¬òàY8–¸Âg27¨ •›ÚÒeJê–þknvcK–!]ÑÏŠ08Í&Cf‡0|•Åd|£¤bñerT-©m œbYçZõ’®!šÏß§y7)`xXm¡£è¤Ò‘Á²ÀBqmÒ’¿Q?Pü‚Ûã)Û2Qá«NO·ÅU?}} R2O3 ¦2!š fƒZ(__½‰u„ÐÓB´êùˆ‹0ù¼·dò0ÇÄ|Ïén©všœÌ+pg&ÆÂBÞþ5E?„»Øw¥¥Œ6ã³F¸ŒU`™ôëÏî³.¿rª¦~'`íCPùÊ‘ë^–Ëþô$à ëڵ.Ñ`öŒìæUYŠÿI®s·¸˜÷Þ +…‰ÑtרL@¬Wí§¤ìLeÒ’EAJSÓ&´D Ak’Ò®4Xè%õ¹Ý$ñ¨ÏKHC@MPóknv³*סhXÁ*xŒsty—°ë@Óbdtq|ÿJÚ}û…–Ë‘0`‡žÏ|¤U-CB¿Œú'š£|*'XYž)ý]à8š/j°,°ŽÈD¸ãf\¡È'j¢k{«‹E Fƒ…n¹h^˜^µBÅ“è\8jxq;#‹’1âp} ·1;cê  á u3ÐÝÌñõÑZ)üä{¾ðè¡• jjx >›Â`{x0Ðñ»?c–¢öç#ðøåš}ÉÛW¤²Dñ(È¿0àoTˆ¸Ê)]óÀ0—À 4¹U1d44ZGVLthOS ˆæ¤->=4i¤4Ʋ Eø#‘õP>nW z`u87ÝZtÍW1y£€æÐ8 rÍ@ U×´McÃàÐU«NB"@£A.ÌpvA¡€ïd®ü~¯„𭤛ôÍx¶ú<Û›@&_‚²á~pì«0‹É¨;‹µl¬›é‘Á2#ÀÂÅà]~†QXØ‚¶$ÐÛì«UYøZô¿â3/^§;…ªÚ-p™O„gur˜2ì }˜”¤·œ‹vCŸ¢ykj‡z°Ô NB¾…€¶ =¶fe¯ÐA?)!6Íè…u [º¤Ë  °†û¾‘ßT/ð# íX£ÆäñGd‡L©Úú­+__“ê¥$'Ã7ò~ÔlreT¤§SKŽÏ j‚ÈÝ’«Õ¤ì}ÑEY"c® æN–[7øÐ9‹–Ë))9„•T¨°³„m³´ö)jé®Í Í{ ÏšÚt~~ÞÕDuH#SàÐЩXý¥ÐÕ³(XñÜûæWØjÇ*g,}|«Æ)ËÚ99b–>“ìúA˜t‚íM65lð³E€eÀ´òåJãcá>û­t…¹òpp?²íÊèf`·™å·t?V•÷–ÉÀ½EXŒ~?+˨:~¸pÊø²3¦¡žˆøBÀ‡¹& ZÕ@ù³,2ùp#H8×0U½/ƒuCÁ©¦ø•«~ß ùl}çµÖѧqq,A:Šâß“{×?1\©†KC†em¸ŒN ‚ïšøN€2û…³§`}A¡oc#k¬¡ ÚZ5)_F•mՀƗƒ.¥ôýuþÃ-—SXx û^APé=Îë…e‹ VOá(Œ;fÓPøGÙÊB‚ا±{lÿ)k®§.à]¥dœæjzkÎ ºëLM¸—ôŽGå88ÈoÁž¢Òå·Õ1¦^NÁ³…Ž1ªÑ KÑ߯6¤}9Ï¢ßsÅ8Ær~‡°LX8YN7_\òù±3,§:@ÕÅŠ½j°0õ·¨z“É )^„H…LØb&• ®ã'dáªíÀPÏ ÷,ù íœŽ!#«„Éä¿£á0Î×V 9ã1õ4Eƒu’”ËÝj_8´[¸Ôbm } Ô{^(ÇÕŒ µuäzöh¼4`! zI®óTCÃ…emLù©Ð= €îÚ¹Lj?‘óVYPhŽ54$þÌ#ú7w[öK„ŠŠwF]Z3Ì:%߆ÝÛ-V.YˆÂü#8uvf÷.Þï±E VWB¼vJ¤Œêé=ÝÊk€†;k­hÅ^§Fî=ŒMO³‚Úi“QÍÝð¹ŬÃ%¹V:;áÚÈ! ×¹¹ÁVCeØT×Nó÷ù-ù^ûJ®mö.4¨9ÖPø{e¥LÒ·n>ŸwL ¬ª5˜wJ ïï‘Ô@¯Â2 j½*ó6é÷(ܤÐݽls ßï+ è¢Xò d+ m~M} Ù|56Ó;Õ,NLÎØÖŽÁØØÃýƱëuBAPÏ[K(ogYJ˜j2š/ç—ÔÖƒ÷r‚°Ì°¶¡n,ʤÊXÝÖÖ\‡K˜Úh3öäÂjÎEé»@³Qj2jdé}õ?²ƒ`fœ4P¼}âïoùE6:šn ‚M†Î ¸¦cæãG ±,Èß4KÅp1XÙÙ¬ "ôÃÑf i)¤$Ãf¯ÉÉ  ûìêJ@´E¸12tIêê(ƒôáîZ1+ñØLÙ¡:GøŒ¡k KZ+‹ÔX«†ê‚ ØÜ& 9¢Á2, ;Õe/Ðïq*YûªM±T3X*+2'uYö&×.Æk·¨È,ßÖÚÏ2ø´¤s¹vs×@Í z.Ž"›p -³¬Oh•ëùøÞN–Þ¤k(êÁQ=k‚fó'Àš†ÅdqKII¡€Û÷ïßï},ÀÚ­­…‚—6 Tì•ÁrWÔ‚\®ÎhBeTý7LÏÀZ NçˆÇÜØ1†z¾„‡Ãª–‘ä}Û~u5œ˜1Ž‘1mÈ¢A¶|¯ŠÓf°0{ïï¸[Ïϧ²ƒÜ\þ*sQC÷†8m7~  )¥A³.ï¾ ‹o«4È õ“Âs= b€Ïr­gw²èuféB{¬¡UÇ™•2I+ÒV)[$Xxž² úÞ)§Lè*›6[?hÞó¨õjÏPÒïÁdcåŠM1XÈê.æùBÛÃ99/žlÀ!é#Ì"]ç)¢nl‹•õ³Jˆ±6§^ðì-}k†äð9 ½õ«Ù¡´¾pœÈ¢Ú£ÈRÛ-ø-´0³¾†Ù›-Ãüº±ÎÎ=­~œ«¥¥…\`'óX&“Aaaá½åXÈ,D¯yØ4P±W š¹ÝGÏñÉËK½î*÷òòÌ¢ÁB€`aÖL„L&!«ÇV¨³‡L/¼>,§ÃéåîNËÞhzE!h»äcà ;"÷v¨ "Ðú¶yLFu2ï©Ôsêîù÷äÚ> ÿ&‘ÐrV­@Xèr/@£vߦ=ÖŽrrŒ2νOkIŸfÂoB65…±Ì{øtd› \…†|e„Ô¨õJ•ÑïÁp[MþšMi°6k+ xØ™ÚîL½Tƒeû´8´©í`0ÄBZVû8½N䮸® û:ŽæœíÄÕ¨5“ܺÙÀ…Z ”Óò=‰e+6 °äcð¬µù~ y¿vè‘]pú„uË'Àòðð€/_¾0onnà ™¨ï{,À:ÎË¡‚u‘Á²@¡çŠ)ÞL!!íCHˆ ÿ 4ºÛ°p Ýò2)“€ôC²äŠvqq‚ê'â1g\uÿ\]á/ùûózò`òµ§'œFDТÒÖþýÑ#ì-†¹ÛX””€™m h2ü6aT>©GµÍqcÌ{Q‹ÕÜosÎÖIÈôßdØ´ŠŠ·°žu;ΜŸCqüúƒk}˜ìáž.¨“ßÚdN9A{ZÓÙž4 3N)4ìüAƒQ½wñhù ã®cÐ<ÚñùÛ,GsÎp×P„´ôA°\&èœÓ9Ÿ?eOÀ¿Ut@AÁ©ÕûߘÇÞ•·Å©MYŸþ· úÇWÿ>„—Ïé¼_d°Œd°âÈŽw¬-ÑæÙ {Ô`¡x3¹õ~ZŽõêjA^XèºnŠ.ÆÚí4/Ï¢Ìî°ÑaAèZŒÔqz:¼ÕcEÀ• ‰~Z\™‹Û%%TÌ$#øùÑ,MAei°T’»;Ëž†==Y†£È^}•H}æû¸8*6¿o¡^SƲj§VÐàaÛö …¤ð””¨c>(Jùõ„˜åÛÙÆ¼ËåHÍÓ?YcÐãX 5ÛôqNÎ{8Õ¨P9oü>EIÀ~Œ ªæ/©ÜºFó©wW•Ï8œ99Ã9?÷-‘£Ué"¯çœ?³š_QÒSÆï.î k€¢©6¦”úW©ÌBùÖ ±û–OsJ7ž7uƒÿP ÕÊÙ2ƒ•ÚßmZ&ázd•m´0Áñ]´Êôå£b°|||àÛ·o,]ÕÏ?ÿ|ïc9yoW¼Íƒ{Ô`!›!»ŸPüMw·N&—Éèg&£¶àý®¢N4êì™»]!K$“ >,s¡å0š ˳ü:j‰N´ JÏÉgpe.¾! BÓj­ „èÃ(MH€K__–;<^AAL†¡*,zíå%è3±dÍeL Z[ƒ…Y›§©©VX‡’ç÷l¶%T´.KÓ“’¾/ÃKXï²m–Ð]CÜ$¼rJ…Š”ªõ*ÎÙg}OW¸::V¾OÍVœQŽAGÃ3Öü•0ïí>.@«{\ÍÁšZ§¦ø‚yµÈ9çÏÜŠ-jÎ\=É«£òíi¢îæªÏ+h¥ó®Îš¡¡WÂÈBb%wÿµõ@PÇ­~Ï–VåàI™„QònÆ6Èн›Z¾¦£k{«­­fjfp† k`¹º‚T%ꥌ ó—öŽÞ{g‰ ô™¬þþvQÏl§¾Žî /-°Ð\‘Ë:§äžPY`‘ed¨ôîî02! °bcy3‘]Dqð™‘êoóóáŠ2MwxªËKIa˜bÆà¹ÀÏ]$}sCŽ’eiî†V§YYVóÂB…ÿ.8ÅBK¸À>4ƒ…àäD£Îž¹†ÏŒegö5JГsÛÒª£Çåt¤!Ö7”¹x‰V(F%Àd”髺:ø®S÷o§¨ˆU×Åí'<Ð4…ýæ^(…Œ5Ôà¡ýÇ{+ ÝUmGÒÐ ½Å—×}Ù ¿Û±†[Œë#í>®ðŸ†c‰+4ùôBsó&´eN²¾g:°è–A3â>陆¨ð+³:6@EŸ#§‘´Û—ŸPæÕX3Íà²Y]{›½ ÈË;¡`2»l[ǪηRn¯;¯ú~ʼ0kÆ;op)SB\É ujT4ó²8è…V¼Èë°o+ÉØ}uHïºïÇ‚ïÝŠ¦ZRGtr¿‡“{üÓ§±á.j° €%Usyën‰;4ÓDõœï»@x·CÁKÐÞ]c÷¾™®h¸ÐcÕ@MF.Ü­ÁÂ:{'´jøLÀŒ±˜>ߨœP+¤¯Ÿ0Ô…v B3ßGÜ–AÝÔ±“Qf’#çñ•€!m¶i¹­•Iˆ¿ÿ^I‰pVÔ ¥Ð±†`5`­d×@C¾Î.Pæ÷ŠwÿïÜØ - +‡ ¡¡ß`\Ã5AM¾}·¶’J¨®Üyrë{V¼ã¡¬ìQ÷©‚€…”˜jAa63TE9ä*9ç<ÏÚA‹¿Ž(]ЙsƒÓ˜~©®Þ‡ço´JÃÐô¼ž['ç×ÜKÀª5ÿïÓÒCõ^^Õ£Y¼Ä©{£^XCƒ‘¹oT¡ç‡jQuS¬uÈPóY!}z øÞ•6¯0áV`™ °þýßmÞ˪ Öñ-CÅçìµá™»¬ÔáèÉ*d.êì²ðsƧî/Eµ¾"¿ÈfìæçÛƒ…ìÒIF†Å<°®ýüL ]ÝxxP7v a!»£¯Ÿ ¼'@I3DwPPÀê#` #o9¼²x¿ë7r°MøýÖXè‹µÑØh{¶<}ˆ ë\­²¤^Ü¥s¨.*{OçøiÇ4çŽÅ^Å„‚¡j5(S(– Ë½‰~Þ„c侨ƒ‰µ¬ïÙw Ùޯܧ­ÉãPôbˬ `¸'—Z3èl6÷|!°zÒ¨ºŽÆ¶ÄŠ%­O䫬ösÖàÑ*£Ð€&n+ˆÖpZ‰`Ö L6 íºµ°iœ€àp “qk,eÊ1?ÐBÏ‚=ÄÚæ ZXh¶ Ù\Jwß»XuÀq¸PX÷­E(êª4JÛl†Sâ&o…÷€Éb(,y N=ê2 SàU©dí&pG&‘ö™åF:JHÐë…eÎÒÖÈVz: ,®È$]¡qb ÇÃÂàÜÇÇ þJÕŽ áÔË‹É\ÜÕÓÿkR)ÍE€…4£ú‹‡múâêÊ„?£9¦o-[jÕÕð_~a¹Ü[²ÉÜÛ ·W·ü 2UË:ÇcágYŠðð>†ë2bAž¤^ôQ[5ê\À˜—flÁëv-†ÂoŒ­KC& >~zzÌÆ‘%SQ»Ž|1b[,«×˨ZÑñtzÑÛMM·:©ŽŽ5xÑ9ÀXd® iáX)²QVh ')º5Âkü ,è…uý …ž…6 czÈ›¯m!£ÆéýÐî¹¼QX?À²ƒå?.Y‹¼åB†êhê°õ8MaÅ^zÉ6D¶÷ÓB,‹Âv«7Ï¢·Ÿ“ÃÒÜÜÇdô¡, ‹‹pbÎFL#7ÅÐ5=fømÝ1A†ú 3 ï²1D§/sq¾¯&*œ{{ÓrAæèCÍLÂ+ò¹¶´K³Î¦µJç ¸V¹nù3·gŒëelZbF_+†KV¡ÉO½è7ÔlÀœÓ F–ä¶ k :ßUž¾®‚ãë;£öŽN|;·Ö²@6N¯R™5L û„é2‘© ûj3毆@}X匎•_ ïk”A¨8¦=Úe"Àú–µ4X¡ýMà[òš·Â{xg7½ñ_”­Ppå:‘ u ›Ò×Í*ù@MF›Ì㥳VW§× ë?Ÿ<,Z~h  ™UTÀ©¬I:èâ®ã7V[ 7è†ú kÝy:]¹¹Ì\ü‚¡D£,$ôZ8ûV6¾ `ŠAÛKe ?ùúZM‹…!º¦FÝóÁ=–ÕÑ>þ³7Ô ×O";Ö7µÝ01rË:VçoÀªS8ÃT%<Ý‚wî~:ßUŸø’Z!é;d-º]ë`(²Ñ¬VÊÚ`ðÝÓXÏ:k¡~Ȳ Šü]çÙ^ŒYuoÆA%f²˜µÙdHïäÀÒÎ×Ô#Jµf`²Qíè ]1¼B[zñ6]‡Y4´r²¸|÷.2}^µC"À,óµÈ¦Q©g³Qš¯×MRï–êÚmx>› ~uÃéç wƒãP±z -ªÅ<;¿…ÎN~/,²@}st4«`­’)ǰj@ë‚]ž7†ÖVÃ3³w& †ú kª2 …d.~òñO!!F[Hð2h «yibXô¡¬ÓÈHÊþY`aˆ®¶xcqß…ñ˜Î÷”û×Ob–]WT7Í>ªºe‹·à@âÁ r†/ctÂ}|}×,]¥ú°ñÀ2&„f‘{¥¯Ž—fLÇoÁЄe“"¶Áûeë»3‹öÍêÛ0Às_UÀÍla7·Ç`k÷‹Uk†CK+tMŒóèòÖÈu"«I‹A“¾´ÇÅ᨟ÔÙ4GÃú‚5Ùº!µÒÒX JâÆ{ ö Âù`0´¦œ2¡ûE/u?/Ž]tn}á­Tg$ä{°84–¬™õH§uÍåO6T«!@ªKåµ}ÈÄÔ¨¿ûÔ²sNYÂzüüVÔs2†ýås¼l¢sT}lmLL¾|” Ç]|W¯ µ-¶|A¯þŒ«…HÇY ™°DKg&øX2ËV¡P>ÎYá FãJVé”~dêTÈow¹m“ð¬µY½Ãz• 9J³ÝH7..œ^X¨aù`W ‚Œ 8z£m©«Ë*ìË,½èøþžü.×L^÷rrà&$ÄlZ©9ò9Èjb1èY»‡f°¶**Xe„,ÙI*NXÓɆC_ =¯`nfÉ­…7_»!›4é•OÁO³×­%@]Ì” s÷)†––Mƒ}‡ ­$z™2aËÎÑL–92{ÛÛu­z0ϧ­Ëâãup‘ µÖÇ3F'³¯5âe‰Úg äÝK¼z8orÞtÍxç îõ}\Ý&i,@VÙ¦áõíÈK×gƒªÆ_L#X¡"À5XžU‡žà·+¸ê8šÐåÕ¬BeÏ8*su_'-£z™¡­Ã#¾ÀðØíŽ`ôå8<«íVïš&² T>i¶éÒÏUޱÀÌ9#ªÛÛ‚ kñ½7`Õpf‚6µh¯&&ÌrŽBú鸠μ½e..·¶ÞzZ™­1“…ý»R©mf èÃÅŽø& Î¦¹Zyì"˪Ù©ü¬ê“Åu|¿¤\°á$²I¯]’(ø©sCGó"tD÷ :¯ùçÉP+Ý6Øw(önŠe¹ªbö{&&fLÞðÔÉu…ÒhÓcy€…}ÛѯÖÁÎè€GÔÊEö¶©çvy=¯MŠ¿kÇèšsv`Kï£X8†1|êB€¤Þµm+2ª–¾w“e#¬ŒL`‰ –Z/5[Qé,m”ÞÒ6Õ7¯11|®”å¢ÚUÞ mT†yX&§¹Ç|©¾È"Î%Æ èÚƒ5sçïtæâ«+ûÇÓ§pêéi”–Ð‚Çæê§weep§Bt¯‡†à{PYÔ¯||à#iÚÅ í…Áz95ŪÓhq/¬ÈiªŸdY4d¯ñE¢…–L©)ߦ™‡ø>¹ƒ¤™‹0Ñ$è¼Ö%aP]°n°ïåên ïÙ“øByÚk¡ û ʱ£Aê †83Óú»->N(%5N3ß?ÚH3µu@X»ÚªÁ«¹›×« 7¿/éšsvxGÏ£XªJ¾kqúë⮆C¡tÍè{7»{˜Ú\ˆKÔ`é´ÔŽhhØ‚ÀòW‚2-ÜR*Vž#G!M`u-üîó–NÆÞÁEQÝ ó™ãädZ‡+sî 3Óî&…·åå¼%S0ú7tàö÷¼xâ{n¼6WÛ®¯§532ÄAý4&>€·næ¼Öl7V´jèˆP°)¬ Ø5Ç{ük§Dh¬°*s7`KĘ”æ,ÃLH¾ óÚ•øAEªaË…ŠŠ·0ï‘Ä„<ËÔïAW÷˜ˆO0Ü1Å\ K‡]m©”],‡®Z¨í·ø@Л\3Ï|o’¼Ÿj‹´A‘¶êL.ÒçU¥r‡GpbgqÆ6¬¥<Ÿ¥`½NiúñàM1ÆŠæˆKd°˜–SºKoÌšú ðPÔ<Þ³n€NT¸³}.kå,Þ¬Ï{&°«œ–¢oÁZG³Nêõ½¼°Š‹93®ð¹]Vì‰ÁRežsk_©ûîáÇaa‚O •~2cÁk!ý´ÞÖF‹7ïáÎnÎv@€ÝeHˆN1h»a°HÃ"å­”I8ÖÀ²6À°“vé͆ ÒBâóÄ%Ø—ø0á»Tç5Xñ‰t^GÎîPÍNŸýš­/êí~1'°ë¬¶y(ŠPƒEœ—²£w¡§rŠå™úFÚõ³¦‹M¥œ&£-mÐ5iùì; Ûf”l3ß›Q?§c'€¾Tq›ŒçSpù¬^¯ª¤Šep;  svéÀУXÔé^Y¡`UÀZuï6 Žƒc_…°LXÿþïñàväñè4XþkqP\|DE}š¢ÏA2›¬·DŽoù4='&ï†~]†««Q¯H”\Îdú÷ M‰oh 6\Öøš=i°hMžöä«­ˆ–'wFžB.Òg11VÓÑ…©·®£¢`õBtèÝõ™ôáBŸmŠx…ôá{Ò—VX%¬Ðþ_™ØÈ{<2RÕéK‚®µ4r‘±a8”<‡Ø';ðÎÍW𹕅Î1€aBùb¯¡ºpº[ç`¤TR×~X”IJÞS6Ãd9¢È¾!jZÓ&Yš­ô]è¯ÔïcµX—á«N:ó¡£TãÓ–¯[‰×•È|wvå+”Ë8´—lÐH2SQ%ú—¬ªUðx ŠJ¨}Ô S„¼S?AÑ_E£3ÆÞ»]J%­©+,Ö“'ñ´ÿè,4þDd¯)|Lk|N=5 Ù÷åŒQGÖ3zêÔ®Sôtˆ³ž3ôËi‘–Ô©žœâ-`Ab€…^H+FdÎÙ ƒ…Œ/x¼°4Ò,C#¼²0|ŠaTk²/x _Ðzá¤7<Å í‰ÁÚÏÎÖ[¥ÀœmÉ;‘UXC+¯Ãòô3KñËœ™WšéîŠ+}­“µh̹Õû3nîƒ-“Ð%i¡åzºj áɦ¸ÂV§ñ ކÕ@M¤æÂ»U“О¥?ñc©"|öÙ&£NÁàÞÜeµ±ôgð-{ !Å‹òö©N¸JºN5U˜_®ß0³¼f\7b¨VŸròQ,ôP‹iÖi— ΈռwGÇ_Á³†`™ °ã!üMö£Ó_åT®ÓœöÀ©×ø}pê­ÉvË8U¦Îð .™gæ© x—OéMs-íb¼°‚JæÌ»˜ À ‡V峫«Í2…û œÖèï´[Rgqq‚(d½ö¬lW€šª¯aaÔ§ìAê:’ïç*mW5 ++õV)0gÛu  ¶ š ÛQzßS=¯ã„›µ¨Ð+PŽÌ0ëü†u5Œ9·v/#)e(aÚ)ƒå*Ïõž6Ÿ.fÑÄkÙô„rŸ)† à f§W+4ÇëËÅ‘àrªµá\Іàö‡‡s+•!)ÚßÐŒ¸êeƒYÎs‰ð¬FaÑBÕ¶Ð0BYóRH:lÒ† Iüµ"À2`¹¸ÄCìTå£b°üvÂ!/ÿ˜7FÙ'5µÛy 1ÊZÆW¥´WÍDUÌSß+&Íu×kôOTRe8È¥ yçIMÝÌÍø\{èîŠiºáa»c°¨Ð€(.á>¦î#+tnÀ26Tj®~2·pÝe«¿>\’Éø«˜¹aè.,䆲иèæä¼‡Wý™‡S:¡~t„¯síƒ>éã»ÔîÙÁ2ÿå3ækýN匛{MØì¹ÖÊŸ7ÒM$²X……ÇpüÜZ¼» V]±¯%‰PéÏ?Ff&'a®<œ3¡'^!·©ñÔÕµ ’þrZ蹨zÓ /YÐx <¯ëÔàJU?1ºpMÿšXöÊä{·ehœÚšiØXXF,8ÚzL¬De£ >œ˜ð¸”Æ—àÔÜm#j•T¹ÄÊD@}f%«†¦%2šŠ3w 3Ka½,Ýe_ýÈÝh†aSÊ4ãŽõ™{’ŒÅïîîpFšÐós̃®¦7”¨÷ê‡K/oìË@Þ¹D¿¿&ÿÖnbÈ¥”q…G,2]é¥äe/Ðøv´!Œ#¡§ mË? Ù:¯öx>Ÿ ÒÚmƒçQŠAðm“ÿ÷ZrÉÃ2é4ò|HõÔ½îÝäî>š0 ,#–Ÿ_¤Ë”ŠÁÊj|m” 3pB¢.aX#9KºÄr°Å"Vx×› ójj{è+¯zÍì7Ñ'̪ÓÚ_Åb؃µ00×nn¼Ù€§‚Sø¯Éb†aTk3Xb»¢½Æ+Y5LyäB}ݵh¨Ïyc˜Y i™ìö^žP(!õÙ2õ Â°]•ï8eÉQˆ>äX¬® úìœ%'e3‚ÖES4ËqÄ¥.HŸeòú×4²3ëÖ€tÎéÔÖlSV£8b‰ ïem Pp‹Â—{z «3T7ƒ°³êG†mjžóóÿââ—d>%c[õú™›Ä>Ù…#rNB¯g[¥Þ¯AQºÈzýu”òƒ7(T_?O3'5û¿®d–že@ÒXk>u{UÙÃ69^:Æg &ý‚¨UƒÇ‡…}ƒ×óo~ˆûivv ¼ªGáù¹;<®Åðmÿ´¶¾ƒÄ¢C~9g–ï³K€¥-N7†ÁâÈÿË¿ü‹Q :GÌå= +¶úµYJÔ †!V6@ƒÏeP;*L,YHVp¦á–)mµ£ÎBCÕ!B2qn56Ú733= '‹+²Pòü¬U°ù: À` ? ¤wkjDËN,ÔÎ!89³R&¡±Në§Î.ü:¨Ð\H•¬Á[‰|!U×4;>Îéã†ãü i»ÕÕpª%‚?vÖ`§0XÈ®¥»,Ãh\ëù5ÿxxáºCC˜Œ)iD?ã³uËðÌ@eÀ4|âá²vaO”Û¦«g\ !QŸôzþQïa¬‚– ~ýL©!´çˆ‰ùl´Éè£f°|||àÛ·o, ÖÏ?ÿ,轿þú+üá0 `¡ ;v¢Æþ5XGîP¼®·¨pÊuë_RºKFLMO z_Ãd/„$œrÖÒ2Gý¾S o¨| «eïڢì,ªÃBF3ÊX"øðpƒ)üçT/j°ìSƒE­\]­fÕ`Îv„ÌÓÓ=8‘¸Ã•¯/{ìr„©˜?* Öd2xOÆ6oÒ†  Ú_8lÂbPx¹ùsÚ…C ‰…®5¥½m¯¡×© >¦íþ¬­ ÚG”69¦&¦¦!$ô› Àð£ÝÃÁuc Ù°’–¾)NöZƒÕFMÌÔÌ",((à ÃÊÊ e®\¡½C ÙͰPÐýb¤Åî,ô­Ê¯6ÏM…nïÉ5oÀAÖùFd¢È^öApÔGA;,SÊå¼OJRO¦d‚^½Ó+Ù33ƒ×….õ¸ø'$°3ÌÈcC)üg!!FMq÷k; µj ‚ +fš³Ñ¬??h%Z`†ïG•‚åövªÑz¯'ËPƒ…ß‹aÅC7/^†Nõý¯üóXÉ?­QC°èG@¡Ä®S¦þÖææ9<‰Ýåø+›WB7Ð?Ú=ß6 NS™V¾f±{÷‡ðÁÒXdq "“Óoû[øãÿuuuFe"À©#vï…> Uµ›f3µKÎ}’Z(§¦¿oòå…|3 ‹ÆU.ç=Lè‚üvËThØÜí45• ÜwóóYÏ¿ËÊ2œÂaTÁk±Ù^;‰ç,›d/ Ù×-Ë”C2–5-H„]{yѰ8n*ÎŒ(fÎ ²œŸëÍ®ýÏ'Oàœ€µ· ÈÏ?†þÚqhNPBüÓm8rò‚sòЦÍ‘)ÀÚ ¿ÚqLÚaËì'y-TÖìXì;D'w#m`¡Y^fýœÝ3X.C¥fËÞC€{ q ÛY¸Ñ˜ó,M8%º±…žmuW·ÖÑßÉB°ÒÔÄz~·¼Ü` ÿ)¹é— ‘Á²ckŸ€‘«;¡»=¶KrîûEEìkÒ2ÊÅ,÷w¶*X ûBO­M! –û’ïhÜ›ï%îTɤÔ?k_âs{nØJÖAò*õv“ú:ÒZ'~¸ñ÷ZAÿxVŽÑ:Œ"ƒeC Ó9³kVì^ƒå!ã/ a À ý}ã¶3Ù¼"ëkH\¨Êˤ¤UèÙ–u ³ccð=0^²“ V[Z ¦ðgd˜µLލÁ²~®76²ì:ì­a±ñͺ:Ö5íÔÖ²X¹_Ÿ=ƒ×÷«>€%Dƒ%ľCï¸ ûgN®:v‡™™´T˜[oÍ] Â"hìzóÿÇа´‘_ú–A°^ƒem€…†s9EoížÁò®7:[±~«²óEQø NÜééFz¶õ]ÝÌ„.˜'‹†õ2X`™»LŽÈ`Y·ÜØqˆð39÷e9»î3¿T±IìÜheÈž¦¥ñ ûÍÁ`aHò",Œ~×¢B¿ûAKp¿SYI7ØX‚nd[äÔSId°ì¯)&”àgž+‘Á2#ÀÂŽËË;Cû­AøÎ*§˜›i§¨¾…„Pæç1_§*…ÿßBâêJûC."¨±ß†ùÚÃãáÁ’‰™Œ—^^´x¹v!åKww5ع«±Éè ‹‹Y•ÌÝ0ˆYǪï»òñ¡ç¡Éºm45ѤOåH›•d–™Vqñ!ønEÚ-ƒå´ ñM?Ö";#ÀYþ1ìê>“…÷œg!:&;ôƒ²2q÷kç ékÿW'ÏŸÃß1ÐÐsåç§Ã¢"àº$ FŽÃ²P,}aM ¯°ß ZŸdg«³££YßwMšŠu ¯W‚Óë$È*ßü!Çßci3í;`™°**ÞBøB–Ýj°œfR «}B\Ø¡.á"4”תá() 6µ„ñ¢~Ã>ÇÚET”UÍFuV` |Žˆ\`œfÈÕ.6ŽúÈ«;€EÃqUUº!Džï2‡ “¶5 x÷óòX #—ÞÞL’ f‘»ËZAZ»-Îub5XæXh6𠬳[˱¯ªúÇÄ›ãîêÓÓy¢ã¬,ÐŒ¸ûµÏ±†š¤ó‡X\½-/§l”±Ùz11œI)×wŸuM® ÍEµƒ¯ÜÝ-Æ`ap­µU½h°u£‘©yIÎmîNt_Þ«„À¢7 “­‰sØDËÜ ]~“û»ìVƒõ¬½ÚGDÎclÛdçÍ•ÂO§²+7%T*6ÛkûMe-ÙЗ ýÔNh7ÆôCй®éSp0eå®4Ø"MƒÝ+OOõ÷›\^»¹±¾C˜ß5F>…†2ÚÅž±)Žÿhîb5X6°pç’.²KKrìOã¶aL9#ÞpW·ÔÝÍÚy3áÁ¨(Ø× »ˆ»_ûk[MMj ’hâã)£ƒžlçaaFeë½åÑ^DFRvI€–vÆï¬R ×x©Äõ´£‹‹Ù,usZ6._Éç«€Ü'rnªçQúÕ¢iq®¬`¡Tnã¼]j°$»~àY3$ÞT—€Z® ³£”°‹¨ß°ß±FÍ Ý©°EèÓÓ43ýæÎ‚½ WWêyÅuMg´!+÷1:š~¶öëªbæÒþŒ J¥Ù2‡‹|ö¬VvíÇÀ@z>²>h•¥2—@ZœëD –°´ÖððäUl› =Þ÷#/«1X˜AÕ2,ÞtWGµ,d±Ðñ¿ÊÊÒÙ¡‹»_ûk¨I:ÓSŸÏÒ¥nÞ––2ç‚Z¬÷¾¾üáDpÞ??ØliáÖfdP±ùñƒ»vûzËpÐs'ÁÁæË"Ô²… :72ßãw¡ÖïœÄ¹Nl"ƒef€…µ÷r ßÝ º€cüø­ÇZ¯á«TÈ—M‹7Æ#ndœj¦ÏŸ’þQA'+ 6ûlóCCp†e]`]¹¸Àz{;+\ùž€>¶ëïp¦AȬñ]ß/,„Ïäøýêjn†+.Žê½PDŽåûN£¹»§ö÷í–”PÇ|Ö$ Rwb5XVXh.‡¥aü·¢ïxÞyƒsò„¿É¶ƒ…åšäóâ ðˆwuÇÙÙ,áñqL ìò„eÄݯ}Ž5 g%'?L©x£Q¦ Ù´ge„ÒR¸&÷ÑŽkÇŸØD –…V×Ä<‰ÛÉ¡pü×â ´ô€V]GoFŸ2ê:æ!'ïØ" –ÓëD•wBzÕ2=ÐÁhko¤Ç¾«Ã…è Y,(lw¿¶Ñ‡ÚºÂ`™åBÕWGGÎó@Ðó?ñ{P{•’¢SîÆ\c[3™Ã’Æ2X(ÔÿÇ/¿À'U]Å´4ÊúòéÂÐH•O7&Îubû!¬÷ïßÃïÿ{½Çüïÿþ/C ¹ù8±í¬Pœwý§_–Ý0ÍD¬¯ß†¸ñZ–ÅÓØ]NP&”ÁBÍ•W ;=èÅBÜÕ‰ýô¸úpµ­:Žó¬€¸ñõ½·3÷òóù¥®.øFÀœµL91l‡ÎïÚ j¬~}úôÖZazŽÓÒXßÈu,óØ2ˆãOl"ƒe"Àâzë9•>K`ÅÅÅQ´ªêPüçöŽÓl=Jhœ¥¡8Õëƒà´μ® á…Ig`yy äò]HRt3¯ûïăcÒ¸²Ž×~¿¾Ç˜¡˜ÔßÇy¾âcñ±øøñ<^œŸ‡<{ÝÜ(›£ø/>F›ËÐP¸vuå|]èã?òN¡à=ŸÅ¹9øzFÝ…Ô¬qýßÂÃá£ÖùâcÔX©Žß¿³À×?{zŸ%ªQÇøØ’í`éÓ[=ƒ•ß3 Žãêz‚ñ•o`lì5ózRû(Hâu˜¦ÐâÛ¢§g ÒZ¦˜çƒ¦ŠáyÎ8m›Ì`9öU@ñ`¿¸Ãwub?ý}ˆÀOcõ‘€+,”üéž Öwâæ dÎLLXµßЊä’KílÇk__æ˜ErÎWôµOäµâ˜ïa‘Á27ƒåããß¾}ci°~þùç{¬†Þ—j/¬SxQ¼SSêp\vçe¸X«ˆ-_¤¯ ,@–tQ ¼äà_ö $+Q&k°ž5Ê w|B¼D]‚ØO?@~ ¢¥i8Vt4¼+(ЫÓdÑ@Þokýv’”_4ü·T%zÎ##Y¾pW·…ªá %Esâ=,j°Ì°4Ÿkkk£™ƒšY„dâ¹/ÀRô.€ƒ¢ò8íùBZÅëõÚîpËc,r\JÅÊ#üä”m3¯EÔM@lÃuz7`ahÑ­®O,‰#îêÄ~úAúÝ9u:'óÖVM &[4ÜzÚZ¿½ÍÍ…ÂæP’~8%‹›æqçä1öÅ×gÏ`½ºZsâ=,2X¦„ µÃ‡šÿ·”úLI:™r4ÕlD‹‚wÇ~vÍBÍãeçˉ+Íþ‹-Y\ù8Meš°œc ¸½GübÛÒµ„ÜšÀè$#ÖÈæò³!¼¡†Ÿ}js×½]Q¡ñ\rrXǽ+-…æ$}0cÁÂçb›èänF'wl¨·òjègÀMe»ÞZ1<—µ²AÐJTVï2ÇÏv8Íþ˪Z…êž)pÍ7ͪa¢²zEý•¸«ûéGéÃ=²qä2ÝD°±OÀºªÖbzŒiט&'Û\¿­¶´Àw-`‰¡Ðݲ2Öq˜ÙxééIËùˆãM¼‡EËŽÚøWN1õ[ZØ –R9 ~ÒQ¶}.jkw˜cÐl4hõ}MË2´÷Í‚c_¹iKÑCJqð‹º±Ÿ~>\mo§þN\Àh¼6?4ĪhlÃÏÞ-,´¹~›'Àñ¯Ú×LίYó8´l¸ ‚ž9\â=,j°l`a *Ÿ¡æ .eÐÕµÂz C€¥³l“ÑÉBhjR3]µµ»ñ²$½U —¯PÖË©³Þhp%9|Žñû0¦ EÅ]ØO?J.ööÂgŽ!>‡Žç(ô¾öö6`}%@mµ±Ñæú ³ÿSË©ýš´e­:œ0oBB`«´Toâ=,2Xö°âªæi=BwEµ]Ðy½|$ï< >\Ï*¼Œ¥uÛÀ£v€šbójè3`mA€TÔˆMl?RCqåé©kúü9­³‡å^®‚ƒïeÑ0;2b“׎nîš”Ÿ|}u MÏÜÕœ·@j±‰MXXéÕKÜx5÷ÀððœÎëU+ ÙTg öt@W×*óz{û:$v C@õe¼&'gÀ¿rÒ0 Ò*©ãô:r;fÅ.îêÄ~úúZ`¡]äù³St Gß›P.‡fß‘÷Ùjß}ŠŠ¢õ€@‹`?´G—x‹ –°Ì° jW¨pÝ—€"M,U+“n‚ór´ÚŒ´}ˆUNY¯åKÓ¢Þ%†•ͳzgiƒ«§±{൪~n¨ûâÀu b?ý`}¨ÍPaíÁOAAÌëL*øü‘¼çšöÝYy9+CòSX-£#Ž+ñ5X`ÕÖoäu"5å4#mØWjÛ…Ô†èëS#_,­~½½Ìs •o˜†¨­z»Î^juäN©+Ý+cžókî¥rMøâ®N짬/¢£i‘ch`áe9ë8+ˤ‚Ϙ‰xF@‹­öÝvGÕˆ1ç!Ž)ñ¬Ç°P°î1\Ò)Î×ÛÚÖÁ¤ŠBéUËTifFÄ~„Z j;»f…)—㹎鋬ò9¾¼Ë§ ¡d œŸSF+¢xI,ê,6±ý€í4- Ð®à 3SíU\lRÁgÌʳE‹¦MMQ†?`"S'ޱ‰¬G°PCåÝÖ qmܯcf`L—‚G™%;¬z…Š‚C¿Àд’VT•Ë R4ƒgÙõÙÒ4+jƒ¢ò=¼N¢0t„w#â®N짯 YêÆÁ6++™×wjk)«e,Àº!k»¤Ä¦ûþ´Diǹ¹â˜ïa‘ÁzL TtReÜ|}}‹߬¤ÀÈ{)BÃ¾Âøø¬ŽŸ–æãúúmp›½­aYý "e}à8‘¥´/ÄBRÛýnï&x¿Ì¡ÞZb<]Ô%ˆýôãõáN}=ÕK1Ö ÏžÁJS“:”ÖÐwEƒEƒfß½¿º#Èz'Z1ˆ÷°¨Áz\«·wB‹!OÎ °0³0­jœVÃ!±xƒoè‡C/e1Hv ©br†»Àa D °fR ¨ýÕGVÌAxýÈdkânDÜÕ‰ýôöáfK œßY5 ˜ýOžÀk ÉÁF[œûù Ï$€ FÿñË/¬Ï±Å¾ÛËË£B÷s ‘©Ç”x‹ Ö#XX0$}ªÿÿöÎÇ©©+‹ãýÓ§ƒƒƒ#]Ôn-lŒƒÁŠ?w‹u«-³Z¬UJ¡¶P«°D-¶•bµ SÅŠˆ#[ëv(uW*ÊÚ™22œÍ¹íK_â#$$Á¼äó™9//òå$÷{Ï=¹ïì9ÇóÝÝ=R»ÿß²éèŨ̕ÆÉ“×dã'ï›k6¶ôKã¹O¤ôı ÁZÝý†¼×öë2ã‘“½²åÕa³A)IO™'OÊXUUpçõ7m 9-p~tË–¨ÌÕX ~ÆÈ®]rùÔ©”îZaÓJÛyÃVµ#z°ÒÀ`i?Õ–ÝcòѧÎ{­è¾VUUåïg¾Žú¤[7l9é——3O]büä«ÏåM![2´µÿºÕƒnLZUõÀ9f#ÌêÐ)ó4¼ÚÑ!£[·šê•6}xßxêü¶mÑU¯þøG~ë-×h÷ÝHT/ôÊ+r=ìy¼†©`¹Ü`iÿTUÍ=9sî«÷‰íò5ZÚþÞ?ÄÛðEpo­Õï þäÇ![=œ?ßÃz:} 蔡^îì”ÑÀ›ºV¯Æ¶*ÐÝÍGwíŠÎ`y½¦§ËMÚMüéO2xÞzÝArŠ×0=Xid°4|¾)ÇMFZ•ªysHv¶þ¾ìè9Þfú¸ÌžW~â¸k<³fuè”yê%q~¼gM•–JŸƒÉÐËéèVQ¬Í›åzØ“S]»‘íÛÍ®îWl{ ¼†©`¥‰ÁJthÅ«z׸¼Ýy6x[õßOÊš¯ÿl VUÃWO}‘ ˆÌŒž/¿”Ÿclž6õr:#ýkTëÇ-[LEÌMÏÿÖ›oÊDà¹_>{–| èÁÂ`E­†ùªHç…ß÷ÆzõT‡¬þ¢Î¬š·®1aV‡NhhB/è¬4¾ì÷Ï{~ìõ×6Xk×ÊÝmÛ\s¹K»o>þX&«ªL%œâ5L ƒE+tÉñíÏOI©¿A^¼½^þr¸ŸõtúÐ CLT¤sS6â°–ý;ãkÖÈÈk¯¹N;5„j0¹!¯az°Ìèè¨D¼OVV–c¤²Á ֮ϥôãÍ®íŽÜd6¬Ð0êø×ÛoËȆ OU¬¦_xA~°]{ð;mÖi×.’Á$x SÁZј%ë~n«`=µÄ…/¥´É/k®l“wßý–'"ú>¥†¹óÛ^YVÜñxäeeÁ]àõÚƒ.ip'z°–Ðh¥»ÁÒX÷A‡Ùáýý÷‡˜0«C'4Œ:tÎÑêêPƒµi“ügçNc¬ŒÁ ˜-ÝÒíÈ?‚ VL+''ÇDqq±4fs333®3XùÅw¢CZ[YO§/Ð0ê¸ÖÞn>eh7XÃ÷µïŽ1ÆÊì¿~½«–ÚÈ?4¤+ –ÉÉIc°jkk•vvfy*¦åXõë³8ÞÝ0X äôé!Çóׯ_¦_º«Žè±ð1ùÿ±Éþ}·ÆéÇÀ{žÙŠáå—åžÇ#c¯½&ƒ}$Û·Ëýòr™Øº•ü#ÿ8Ž3ÿ\i°"õ[ź877'¹¹¹®«`í?sFªÿ6`.¥Ã ‚ ˆhC÷ÂLC–K7ç¼]_/÷´¢õê«hEô`Åg°?~,………®3Xç:¥:Â…YO§/Ðp¾ß½;Ô`íßoöŽlm•Ÿ6l:„väAÖÂË~›Ïç“þþ~S¹RsU˜±566ºÎ`:ÿ…lÞös)ÖÓéK@'4Œ%F^]ÆûÄà˜š¬7Þ0=WÚØþSuµ\ -´#ÿz°æÝßÊÉ`uvvJee¥dggKQQ‘477»òS„ºÃûæÍ¥»»‡Ù³:tBØâ»Ã‡åîš5Æ`x½fo,½]7è|0X½]]hGþT°Ò'÷H&‹ä&"Ö¸˜Xþ÷7ƒugóf¹õî»Ás=çÏ£AЃ•Ù‹Ù:¢.v«†‰ß–ïlÛföÆB;ò¨`a°XOGGtBÃx„3gdâå—ÝkÏé.´#ÿÐ, ³tD'4Œ#¾>wN&Ö¯ÿµ+`°Ü´k;ù‡†T°0XA)æ„c{÷šæv4!z°0XÌFÐÐ0θ[]m¶j©«C; ©`a°XOGGtBÄ셵{·Ü-/—Û‡£†ô`a°˜ #:¡a"â_ûöÉ]ŸO†ššÐŽ@C*X,‚ ˆDÄ?õºƒ••æò8èAô`a°˜ #:¡abàØ1¹ïóÉ7~?ÚhH ƒÅz::¢&".Ÿ:%ëòÙ³hG !=X,f#èˆNh˜ˆÐKâLUV¦Å äRÁÂ`A¤LpÝA‚  ƒÅlÑ ´CC*X,ÖÓéK@' Ñ éÁÂ`QÁ"ÐÐíР‚…Á"‚ ‚, ³fuèD !Ú¡!,w,¿ß/•••’››+ùùù²gϹÿ¾ã}çææ¤¾¾ÞÜGÍ“ÆÔÔ=X:¢¢ô`Ùñù|ÒßßoÌÓì쬴´´ˆÇãq¼okk«´µµOœ8!¤‚E #:¡!Ú¡!Ak!rrro///—ÉÉÉà±VºJJJèÁ"‚ ‚¬H ˜ª–yyyO-†ßF‹@GtBC´CC‚ –ÞÞ^)++›·++++ªÛ¬þ¬pƒµsçN³Þj ª_SñøÛo¿Mé¿Ï-Ǫ#zoKqþ}È?òÏÝùçJƒ¥†È ;ÍÍͦ©i Žè„†hG !¬(Qרî áõzåáÇ!=X+W®¤‹ ‚ z°ì¨{ŒTµ²WºŽ?n>9hÿá¢6Xº„š,‚ ‚ ˆùBý‚ë –}ÉÐiùÐþ}¬û`$›çƒ€ÁJeìÛ7AA̬45‚€Žè„†hhèí0X¼XÐÐ Ñ ƒÚ`°0X,€¤àtQo€T30X þç9]´:ü~¿Ùž?77WòóóÍŽõz½E%Ò.ö‘ÎÍÎÎÊ¡C‡ÌÏ+((wÞy'¥´‹VÃXtN'õyWTTøÀušÅò˜tÒQŸ·^Ðüûï¿Þ611an‹7÷2%ß,ôQ¯]ª_yÍ>ÛÜËÔL´ÁÊ4ýbÑq¾û$K3 V’ÿÑÅÅÅf†¼|ùr3[¶ÎwuuÉÚµk͹²²2Žúçëc”òòr™œœ Þ®Ž»¤¤dÁs:±£Ž{õêÕ)ûâÐrmm­ìÝ»7d† ioo7çå噋qþüóÏi¯£>o½Èy]]]𶦦&s[4¹7==-«V­Êè|³fŸ………òäÉY¶l™9¶k¬³ZÕ)<·2YÃxroãÆ!ÚÇráÜtÍAû{ŸÓ{ ýcFbt\JÍ0XKd.]º$EEEÁóú¦dýcº»»eݺuQýlE¨ÛVô?|À°n‹t.;;;d0Q´4šŠKgÇŽ ëRÎþýûƒÑ™ÆãÇÍóÑ7y­F¤»Žú¼u©‡õÜW¬Xa¾&÷£þ]éšoÖsS㮨Òl¸‘°ôU³eåV&kOîéëØª‚¨~ÑV@Ò9c1ŒKg°¥k Í‚ ît^ÿ)Ö¹Hôöö𙋵6k"Y·ÕÔÔ˜²¦–Bõw_½z5% –S–.=è›·]+k6þ­JDºëhý~ðµ$­ûàKîej¾)ZÊ×Jùì³Ï‚¥}§ç£û|y—IÆ“{ZqÑ~E¿Žg|.Ö0f$Ï`%R3 VÿÑú¢½ÖV´ àÔ°×ÜÜlfÙÖòX<ÎZÆ®]»‚ }úYZZš’¦ÔÉxEÒÑ*릳ŽÖsÒK—©t¦5::º¨ÜËÔ|STkÙOÿF=Ž”öÜÊT ãÉ=Åj¯ªª"ã4™>fÄ£ãRi†ÁJÖÌB߀tiAKê*|4 à„–ÓíË^¯W>|Ò9;CCCòÒK/Ë¡©f°ôXÿvà-Û{°¬O3éy½occcÚëh×jll,d©Å:Kîeb¾éLUÿf;Úϧ}.–NwîÜ ÎJ?ýôSóÉ©L×0žÜSÔÀ>ÿüóæ±™žƒáã…6L[KÖú·i.2f$_Çdj†ÁJú¦¢Kšx:;VôŸ¬=AÚ¬½˜K¤%²ÅîÏ¡h©WCKüÖ@’ŠKQítV únذ!ø¦®3]çÖç¡úëÇb-#–Î:FÒÊ:Kîeb¾½øâ‹Á¥- ]öÒׯ¥ƒþ­Ö’ˆî433“ñÆ“{ 5ggB:šª¾¯é'ôÔÔ3f$_Çdj†ÁˆÁHÀâ¹yófÊíŸ,0Xa¤Ú'¤ÒíGZÀ` ƒ€Á  ƒ€ÁÈœ.ö €ÁXÀ¦««KÖ®]kΕ••Éððpð1­­­RTTd*#Z!±~‡â‡2Õúúzs›õ3õœþ~}ì¥K—äøñã²lÙ2Y±b…\¹r噹tÖÎBÿ&  s V`ð^tÌS…ikk“ÚÚZÇ߯ƒ¶ÞÖcšššdrrÒwww›%2EƉ'‚ƒÿØØXðw444H{{»ù]§OŸ–£G¦š}Ü7Ì@o?ަ:­ÁJti¥200`ªZ, ¬Ta¬Ðœ_~ùeÞûfgg;ð­s«V­ „p#¢Õû¹'Ož„§ß5ßq< 힦··×TÓèÁÀ``°d]Fòz½fÀ·7ÆA—ºtùɺ¯“‘Yì9kI*üÜBÇ©b°ÒA»ææfÙ±c‡LMM-iî í –¢ËSÁc­¨è'Í=zdÌC4F °°0ÄhØÏiÅ%¼ £ýIn7XnÖnppÐ4º?‹Ü @F,eïÞ½ÁW·=ÐJŒúj¢1 Ú|­½Dú˜{÷îɾ}ûBúˆÔˆX}DÚSdï#r³Ár£v}}}óV­0X, VMÂÌÌŒÔÔÔ˜W°õ“húé¶hLÂôô´iöÖ¾íÒÞû'áÔDè'ä4ô{­ÄÄk°ì½PK¹]ƒÛµsÒÍé¾,  €ÜÀ`ƒ{€Á`r0X rä ä€ÜÀ`ÀR rñ¬0X®åÿ?¹²¾IEND®B`‚XYDotRendererSample.png000066400000000000000000000276151463604235500341610ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/xy/doc-files‰PNG  IHDRXr5˜/TIDATxÚíÝheg^?ðDDDDA¿"ø‡ˆ„0!e—2ë, º8µÛ0›¡Ý„e©ß™éî TTSÃÌ„jC‘--aÔed[:KkˆÓîZ©ÎHŠ2 &$T :æùæ9_o¼9s&çþ8ç¼ÞÛ̽çþzŸÏ½÷¼Ÿ÷ó9ÏýT€Lñ)»€Á`°,`°  _JŸúÔ10XÅ{ï½¾ð…/„ŸüÉŸ ßó=ß¾û»¿;|ÿ÷Ï=÷\øæ7¿i'µ`–*Œûï~àÂç>÷¹°´´ÔW«Öë5¯wxx8LOO‡‡–ªŽ/½ôã À`d‹¯~õ«uB¯8Íž»_†­ì·Èh\>ùä“Ì_ûI«•×Íõêêj)ÞÿwîÜ ßõ]ßÅ`0XÙaaa¡e“À`Ü`E^½z57«Â¢›¬ðã?þã=¿0XÃOÿôO;¨üÑýQØÛÛK¶íïï‡o}ë[‰1ˆ!«õ×ùoÿöoá7ó7mû©Ÿú©¾4X|øá‡áÉ'Ÿ<¶íðÃöövaßÿgÏžMt^ºt‰Á`°²Czj¤Þ›ÍÍÍðå/9üüÏÿ|øÞïýÞ¤ïèG~äGÂSO=þê¯þêèvãããáWõW“i§x›Øïóc?öcÉíÞ}÷ݶ–V¶G|ûÛßNzÊ¢1ŒÏ_ßÏþìφ+W®„ÿüÏÿlø|ÿýßÿ~ï÷~/üÄOüD²Njp¢AM÷95»ÏÍ›7“SÙWño¼¯ow?Ô”ýʯüʱí±'«­îÛôóÄý+öùÅûEÃyýúõä¶qŸÿÁüAbøã¶x›?þã?~ìµµ³š!>~|]Qo?™v  ˆ†¨ú ò£?ú£á…^kkk‘j¬¬¬$†¡•ƒ|³i¨ø\Y¬W^y¥fOM…Ñ Ä©¡zÏ—6'5+üñc}Mî÷{#mq{7 V4+éýQA;û¶Ö¶Z÷‰ æ¿ø‹¿Xs[œÂ>éþi„õõõDGLèN…5BìëŠ=Mñ ûšb"rÚ&özÛã”O+f§ÚÔæÀÚêsU¯…Uëùâôaº¬–y©L3v3ÁªÐ|ß÷}_r›´ k„øXiCÖÊAì¤ÛõµÚ#–…ÁŠû(6l_¼x19;¯Ùko´O¥}0Xqš­z{L8O²o=O»ÛNº,  'ˆSÿøÿXs[œ¬uðJ§ •©¢Zø¥_ú¥c·‹šÆe :e°Ò¯­ºw줆#ëûö{‚•nòKwœdßfi°$X @¾> ÿÓÌþÛ¿ýÛIrQ1K±)X«4Ñ,EÄŸ©¾þÏÿüÏ{ÜJêQ/yhvK§%­noåµEƒ¨Ÿ VºÇ(.…Ío££fû©×“¶ôþ‹g™VúÑÚÝ·Y¬“îŸN×€Á€SæcrDœö«¾þ‡ø‡Ã­[·’mqÍ‘‘‘£Tºo'Äãâ¥Ífq¹ˆêíï¼óNKÛk½¶Ø_Ó–˜vij+Ëô“ÁJOËųã"š%×l?¶æßøÆ7Žî×î¾ÍÒ`tÿ0X @Ožz©·\C<¿‹‹‹-­ƒÕhˆF³zkµ²=6”7{mýf°ij´ÎS³ýtRƒqz}²v÷m–ë¤û‡Á`°z‚8%Sˆ¸ôBlj¯œáÿÆ)Á¸R{\ÛªâZOSSSá~áŽîÚc‚UYY;¦ñ6•ƒrœrJ§µfñ~ÑœUB­¬üÝêö¸Ê|Ü5Ä×Mb| ±ç,®,_½‚|¿¬JR—¨^©<^®·Ry³ýЪÁŠ÷‹¦*N±ÅÕý<Èœ?2Xõ°´´VWW.¯¬¬ŽþçœEh´$ÁR[ziU[z¬4fggí[·š¬ñññÄŒU°µµÆÆÆ ¹ÖI™õï"""êÁÊ¡ÁzðàAxæ™gŽMž9s&áÈ¡AXXXûûûɶááác÷S†éë*ƪÂj\¹r%lnÞ?tÀë‡;óVò÷´—>|éãörÚ`]o§/wCïõëö…Þ{÷>,|=˪·Bz‹w9¾Ëò}Üi½…3X/½ôR¸}ûvÝ©Ãh°¦§§ë¦[¯´Áºté…Ã纞)?úh#óÇ< Ó«èz;Ínè}úéÒh-[mi¥—ÖüèÍ¥ÁJ7«W›Õ.\¸Ðð¾1¥j+Áê¦Ázÿý›¥2Xý¦·Óì†Þùù›¥ÑZ¶ÚÒJ/­ùÑ[¨ëÅ_  o³··Ξ=›übb"ìììëÁí©ÁBDDÄü³0ëþýûáùçŸìú©©©°¾¾ž¤SÑ\Åe®]»–l[^^Nά>‹06ÈK°º—pÕÓÛéäL‚e$L/­ôª­«D#õÁ¶K‚e$L+½´J°¬Bõ`õ«ÁBDDÔƒÅ`I°:˜`-I°Ô–Vzi•`1X¹êÁÊ"eê„Á2߯Kmi¥—V=X –³é•`©­”Cmi¥—ÁÒƒ…ˆˆˆz°$X¥=y‘Q –‘0­ôÒ*Áb°üaOæ¿‹l°ô`éå •^Zõ`1X, –Ë{™^µU[z,=XE ß!DDD=X –ѽ,µ•rÐK+½ –,óýz°Ô–^ZéU[=X,£z%XjK+½´J°,DDDÔƒÅ`I°Œ–$XjK/­j«¶ –,óßôêÁò^¦•^Zõ`1XFôJ°Ô–Vzi•`1Xz°‘Á’`=H°$XôÒJ/­,Ëü7½z°Ô–VziÕƒÅ`I°è•`©­”Cmi¥—ÁÒƒ…ˆˆˆz°$XFôJ°Ô–Vzi•`1Xæ¿éÕƒ¥¶´ÒK+½ –ËhI‚¥¶ôÒJ¯Ú2Xõ100P“‡œù033“¤ÈÝÝݦÛô`õŽ!Œ£}‚ˆˆz°zd°êaii)¬®®]^YY sssM·•%Á ##ÇØ£‡Ó¬”œÌ^çHêŸËHXÊ¡¶´Ò[jƒ5>>¶··.omm…±±±¦ÛÊÒƒÕkƒUKoÞ V£× K/­ôÒª+WëÌ™3 Gh a?Ù6<<|ì¶qZ°r]£micUa5®\¹tXœ»á½÷þ:¼ýö+ÉßÓ^~øð^¦×ìrÚ túùZÑ›6Xí<^ÚÜdõz¬U Ÿ¾M·ë{ãÆ»]«_£Ë÷ï¿ÛÓç§·s—+¤·x—ãû¸ Ÿ×nè-\“{L¤¢Ášžž®›nU®k´­•ëòåËá­·ÞÊ5Ón ïzÒæ&³Ç­c°ºù1?Ì¥ÁJ7²§“¨¡¡¡Ì¬n¬»wïvÞ„¤þõò Ø ½Ý6pnÓ ½ssý±OóV[zi¥WmKo°šaoo/œ={6ùÿÄÄDØÙÙ9Ög5::Út[¯ Öææf© V7ôöSBÖ ½çÏ÷Ç>Í[m饕^µe°R˜šš ëëëIÍU\záÚµkɶåååäìÀê3ggg›n“`-åU¯‹^Zé¥U‚• ÖÖÖÂääd çÎ ‹‹‹Ç¦ý:µV^{°úÉ`•‰éæ}ûQ–•Ü ”`-Û`I°è¥•^Z%X VN翳2 ­è=Ísµs–d§ P· –,zi¥—V=X VNGY™…Vôžô¹ëH/‰Á’`ÑK+½´J°,Ó]-õ‰Á`!"¢,+‡ V7›×;`4ù—Wƒå,B#aZé¥U‚Å`å¬+gÖÓÛ ƒU–þ=XôÒJ/­z°¬œ&XÖ{ƒÕÏûA‚e$L+½´J°¬œõ`y}«¢,DDÔƒÅ`9‹0—£‡¼$Xz,zi¥—V ƒeþ»T«Zo§^§,zi¥—V=X –ÑCgÏvì³Fw –÷2­ôÒ*Áb°¬ƒ•ÿõºúðLÂn$mý¬Qƒ%Á’`õµÞZkwÅ«t{/ÓJ/­,KVAçûûÍ`e­·–ÁŠ=Xý Û{™VziÕƒÅ`I°Œ–$XjK/­j«¶ –,D¿Ÿˆˆ¨‹Á2z 7s½éŸ[t!½´ÒK«‹Á2ÿMo“tƒe,zi¥—V=X –ÑCéõfm°j%X½X|Umi¥—V ƒ¥ {·VÆ«Ûkn!""ƒ%ÁêÃÑC™þY'Xµ(Á2ò§•^Z%X –ùïB¬VÏæëF}kõ`õbëå •^Zõ`1X¬%Xý¸4A«¯©õu!½´ÒK«‹ÁÂR¬Ž¾¿?ˆˆ¨‹Á2z8©Þ^˜™fÏÙ VÅX½9pµ¡ÁʺçË{YÊA/­ô2Xz° 0ÿÝ«ê[qL_û™Õ¾0XÞË´ÒK«,K‚e´Ôƒ%Áò^¦•^Z%X –,ì㾯^¬»…ˆˆ –Ëh©”zEH/­ôÒ*Áʯ½öZ˜œœ CCCá‰'ž333akk+Ù600P“a~~>¹}4O‘»»»z°Ì÷çZïi~‹0Ë3Õ–VziÕƒ•sƒ555Ö××ÃôèÑ£ðꫯ† .¬zXZZ «««G—WVVGÿs,£¥Ò&X§1XéiTµ¥•^Z%Xœ">¶··.ÇÔkllL–·×,Cƒe"¢¬‚¬$Õª¬h¶"G a?Ù6<<|ì~1K_W1VVãÊ•+asóþ¡^?Ü™·’¿§½üðáƒL¯ß/Ó›ýó]¿þá‰ïŸ6XíÜ?m°îÝû°ðõ¬¾\&½Ò[¼Ëñ}\–ïãNë-œÁº}ûvxöÙgz°ªÓªh°¦§§ë¦[¯´Áºté…Ã绞)?úh#óÇìgÒ›=Ÿ~º7û4m°Ô–Vzi-³Þ\¬t³z‹‹‹É´]£Fõ˜RÅføv¬n¬÷ß¿Yª77½›œªu°"Õ–^Z饵7z “`ÅFµØèÞ {{{áìÙ³Éÿ'&&ÂÎÎα¬ÑÑÑž,ÄÓ¬j¶rŸôoAÚˆˆ V‚;wîÔM­ªÏ0Œæ*.ËpíÚµdÛòòrræ`õY„³³³-¬ßøÿ›LÇÌÏÿÿžöò¿øO™>^¿_¦7ÛÇ?“3û^9J²Z¹ÿÀ›WcÿþÌ×V3{=7nÜ)|=˪7ŽúËðy-£Þø>.Ë÷q§õÂ`5Zëjmm-Y#kpp0œ;w.™F¬ž<Í:Xz°èí7½Õ¿EØëKmi¥—V=XVr?ÑJîz°èíG½•T«ì”ÁR[Zé¥UƒÕ7 õ`1XFôæ<Á:-Ó?­¶´ÒK+½ Vß,óßôæm¬zKmi¥—V=X –ËhI‚%Áò^¦UmÕ–ÁÒƒ…جg°õ`1X,£% ÖiLV3Õ–Vzi•`1Xz°Ì÷ëÁÊØ`©-­ôÒª‹Á’`-I°ÚdznP‚%å —Vz,=XXjžä7 Û1XY<~³„ Qƒeô@o_%X P«fæ±îöPŬ Vé¥ôÒJ/ƒ¥Ë|É{°,ïeZéU[=X,£zû0Á:z¬pÜ\e5)Áò¹¥—V ƒ…Xœu­20XÖÚBD=X –ËhÉY„§i˜¯c°ºYÛ†Ó•]jŠ—rÐK+½ –,óýÖÁ*TmûÁ`éÓ¡—Vz, –Ñ’«Pµ}lɈL)J9襕^Kb¡×õÒ³…ˆz°,£z%X6[ÞË´ÒK«K–ùozõ`el°ºÑ“¥O‡^Zée°$XFK¬Ò$Xé3;5}(å —Vz,=Xˆ¥éÇê–ÁBDd°$XFK¬RÕV‚åsK/­,=Xæ¿éÕƒ•!GÿiÄŽ,}:ôÒJ/ƒ%Á2Z’`•¦¶ÿ»öhý¥²h‚—rÐK+½ –,ÄÒ°]ƒ¥G , –ѽ¬¬V–”ƒ^Zée°ô`™ï׃UºÚvÚ`éÓ¡—Vz, –Ñ’«Ôµ­gž¬‘ªRŸ[Zée°ààààð`4fffó¹»»« ±$}Yµ VÝ$+e°º±:<"êÁÊ%–––ÂêêêÑå•••077'Á2Z’`©íqC–úWË`I9襕^ë0>>¶··.omm…±±1=Xæûõ`©mÛKŸ½´ÒÛ3ƒõúë¯÷•Á~lÊ0}]ÅXUX+W.çnxソo¿ýJò÷´—>¼—éãõûez³¾7Þí ½÷ï¿[èz¦ Vú_‘ßÏýóZF½ñs[–ïãNëíªÁ.\›››}a°âëiåºz ÖåË—Ã[o½…ˆø˜ÁŠ×Õ3_öbñÙUƒõùÏ>10ƒƒƒáÚµka? V7 ÖÝ»wKõ¤7{ÎÍÝU[Km饵Çz»j°¢ùË¿üË0::š­Øïôw÷w=3XaggçXV|m½4X1Ý+Ó››Þìyþü¦Úö¹Áɹéò¹¥•Þ>3Xùä“ÄÌ<ùä“=5X§]K"ž4áÒ—…¨+Sƒµ¶¶FFFަ×××s»’»‹^ –Ú¶ªW‚åsK««cë·~ë·ŽšÜoܸ‘$Yyþ©=XôêÁRÛVõ6[Ö!OfKmi¥·Ï V¿-Ó Á2Z’`©m·ô64X©ŸåiiÊñ÷Q[zi-h‚õÆoêÇžõ`!b&=Y ÌR½t«Ö}ziº±šÜ,£z%XjÛZÕªÁ 'LÂÔ–^µe°ô`™ÿ¦WV!kÛJÕOKmi¥—Á’`-I°Ô6—z›¬Z½],zi•`1Xˆˆ4±wÊ`Y§ ‘Á’`=Ð+Á*|‚Õí3 ,µ¥•^K–ù~=Xj[½Ý2XjK+½ –ËhI‚¥¶¥ÑÛ®ÁJ¯Í%Áò^V[K"b†)‹Û#"ƒ%Á2z`©méôvË`©-­ô2Xz°Ì÷ëÁRÛRéM϶z»v –ÚÒJ/ƒ%Á2Z’`©m)ôVz¥Nj°Ô–^µe°ô`!"¶a°j5¬ŸÆ`!"ƒ%Á2Z’`©m鬬 V=­E5m,z,=Xæ¿éÕƒUR½–UÈzUözZ‹j°ô`ÑË`I°Œè•`ÑÛ–ùÊR«‹Vz,=Xˆˆ=:ƒQƒ%Á2Z’`©máôž6Ñê¶Ö^7 ½ –,óßôêÁ¢·ã«ÛZ{m°ô`ÑË`I°Œè•`Ñ+Áò^¦U‚Å`!"öÚ`eÙ¯· ‘Á’`-I°Ô¶ôzOb°Ô–Vz,=Xæûõ`©-½¬Niívš–þ9FïeŸ[K‚eô@¯Ë{¹c¦¦Ù”\§´öÚ`ÕÓ-Á¢—ÁÒƒ…ˆØ•ž§Ff(„‘clË`µ˜*uÓ`!2X,£z%XôvÜ`E­e2X,zKk°^{íµ099†††ÂO<fffÂÖÖV²m`` &#Âüü|rûhž"wwwõ`™ï׃¥¶¥Ö[ÃycÔÚ ƒÕN_T7¥,zKk°¦¦¦Âúúzb˜=z^}õÕpáÂ…#ƒUKKKauuõèòÊÊÊáèN‚e´$ÁR[z¦N%X½6XÞË>· V 8sæLSƒ5>>¶··.ÇÔkllL"bF†©Œýiˆ…5XIªU1XÑlEŽ~Âþþ~²mxxøØýb–¾®b¬*¬Æ•+WÂææýC¼~¸3o%O{ùáÙ>^¿_¦7ûç»~ýþÐ{ïÞ‡…¯gYô>ž`­—âóZ¹\­7m°Š¦7¾Ëò}Üi½…3X·oßÏ>ûìQV5bZ ÖôôtÝt«Qâ•6X—.½pø|×3åGmdþ˜ýLz³çÓOo¨-½¥ÖšžfÌRoúÁÕ–Þz,”ÁZ\\L¦í5ªÇ”*6÷“`uÓ`½ÿþÍR½¹éÍžóó7Õ–ÞBkmf ²6XÕz‹n°|n¬ÇÕb£{3ìíí…³gÏ&ÿŸ˜˜;;;Çz°FGG{j°‹Æv Oz:² bi Ö;wê¦VÕgFs—e¸víZ²myy99s°ú,ÂÙÙY –Ñ’KméÍPkÞ –ÚÒ[ZƒÕh­«µµµd¬ÁÁÁpîܹd±zJð4ë`éÁ¢W–ÚêÁê?ƒUÄÚ¦—Åð>ÖƒUè•Ü%XôJ°ÔVÊÑ}ƒUÆÚvË`ùÜ2X…5Xˆˆˆ½2XÈ`I°Œè•`y/ÓÚ×z³Lã$X,Ëü7½z°Ô–ÖLYϨtk:±¢·ÝÞ¯~œîô>Öƒ%Á2z W‚¥¶´ö…Áªè-ƒÁò¹e°ô`!"–„½6X'={1ëèµ|AU=X,£z%Xj+ÁÊi‚•çÚ–Å`I°ô`™ÿ¦W–ÚÒZp½ý`j*ZËb°ô`I°Œè•`©-­=J‘º¥·L‹ÁÒƒ…ˆXôµŸj˜©"OÓé{ÒƒÅ`I°è•`©­«4«Œ –÷2ƒ¥Ëü7½z°è-¨Ö~1XjK/ƒ%Á2z W‚Eo¡,µ¥U‚Å`!"¢~(ÔƒÅ`I°Œ–$XjK/­E5X­üž¡Ï-ƒ¥Ëü7½z°¼—ie°26X>· –Ëè^ –÷2­ –K‚Å`!""öÇšb#A_™, –ѽ,µ¥•Þ®œ‘©¶ –,óßôêÁò^¦•ÞŒ –Ú2X,£z%XÞË´–Fïi{Â$X,=Xˆˆˆ]lºYÕtâƒ%Á2z W‚å½L«‹Á’`1XzèÕƒ¥¶´ÒË`Eöê纥—Á’` K°Ô–ÞÒimç Ûz[YÊÁb°,DDÌÁꋳ;h°úñlF‹Á22¤W‚¥¶¬'Xý`òT[‹ÁÒƒ¥—C–ÚÒ[`­ídém×0ô¢Ñ»×èsË`I°Œ„é•`y/ÓÚs½E3XíÔ6oÓ¬Î"<j2âàààð€3fffƒ¹»»Ût›,DDþ/ yaL¢†††2K°ºi°îÞ½[ª7 ½Ùsnî®ÚÒKkNõöÚ`©-ƒÕ{{{áìÙ³Éÿ'&&ÂÎÎα>«ÑÑѦÛze°677Kõæ¦7{ž?¿©¶ôÒšS½½N˜Ô–Á:†©©©°¾¾ž$PÑ\Å¥®]»–l[^^Nά>Spvv¶é6 –‘°Kmé¥Um 8 ›êc°`mm-LNN†ÁÁÁpîܹ°¸¸xlÚ¯Së`éÁBDÄ<ìõ_1X¹ZÉ]‚E¯Km¥ôÉ`I°¬Â,óßôêÁR[ZË£·ß VjË`I°Œ é•`©-­¬Ž¬ô?µe° m°»ÁZ+‹Ûêc°$XFÂ,µ¥—ÖÒêm'Á*ªÁ’`éÁÒÛ@¯,µ¥•ÞLMG-ÓTOkQ –u°$XF†ôJ°Ô–Vz;n:$X –,DD,÷Z_×*%/w¯ŸÁ’`J°Ô–^Z%X}§µK‚¥Ko½z°Ô–VzK¥µK–ËÈ^ –ÚÒJoÇM‰‹ÁÒƒ…ˆˆX°¾¥"‘Á’`J°Ô–^Z%XjË`éÁ2ßO¯,ïeZ‹­÷¤?¡sZƒ¥¶ –Ëè^ –÷2­…ÕÛÍß(T[K""–c«,d°$XFôJ°¼—i•`©­‹Á2ÿM¯,µ¥•^Zõ`1X,£% –ÚÒ«¶jK/ƒ¥ õ`1XFôJ°Ô–Vzi•`1Xz°èÕƒ¥¶útÔ–Vz, –Ñ’Km饕^Z%Xz°Qƒeô@¯Km¥ôÒZ—#©,K–ù~=XjK¯ÚÒZPƒ¥K‚e´D¯Kmi¥W‚%Áb°±=ƒuìV#áõ`1XFJôJ°Ô–Vzi=­ë‚Á’`5ÁÀÀ@M6ÛvppæççÃÌÌLbž"wwwõ`émЃ¥¶ôÒª¶%0Xz°ÚÄöööáAæü‘Áª‡¥¥¥°ººztyeeåpô?'Á22”`©-½´ª­‹ÁJcvv6ܺu«©ÁOÌX[[[allL"""2XÕxðàAxæ™gŽMž9s&áÈ¡^XXûûûɶááác÷S†éë*ƪÂj\¹r%‰+8þ=íå‡fúxý~™ÞìŸïúõ{}¡÷Þ½{…¯gYõVHoñ.Ç÷qY¾;­·p륗^ ·oß®;u ÖôôtÝt«Qâ•6X—.½pø\×3åGmdþ˜ýLz³çÓOo¨-½´ÒKkõÊ`E÷xáÂ…†·‰)ÕÐÐP[ V7 Öûïß,Õ››Þì9?Sm饕^Z{¬·PëÅ_  o³··Ξ=›übb"ìììëÁí©ÁBDDÄü³0ëþýûáùçŸìú©©©°¾¾ž¤SÑ\Åe®]»–l[^^Nά>‹06ÈK°Œ–$XjK/­jK/ƒõ?Fêƒ>xìúµµµ099ùsçÂâââ±)ÁÓ¬ƒ¥‹^=Xj«O‡^Z³az]=X%^É]‚E¯Km¥ôÒzzÆu¯za°$X%2Xˆˆˆec¯ –, –ѽ,µ¥•^ –‹Á2×O¯,µ¥•^Z[7XÕÿ¬ƒÅ`)Ñ+ÁR[)½´ÒË`éÁBDDD=X,£z%XjK+½´J°,sýôêÁR[Z饵Üz, –Ñ’Km饕^µe°ô`!""¢,Ëè^ –ÚÒJ/­m±ëbI°ô`™ÿ¦W–ÚÒJo©´vÃ`éÁ’`-Ñ+ÁR[Zé•`I°,DDDìoƒ¥K‚e´D¯Kmi¥—V ƒe®Ÿ^=XjK+½´v†Yýv¡, –ѽ,µ¥•^Z36X,=Xˆˆˆ˜±ÁÒƒ%Á2Z¢W‚¥¶´ÒK«‹Á2×O¯,µ¥•^ZË«—Á’`-I°Ô–^ZéU[K"""Z‹Á2z W‚¥¶´ÒK«‹ÁÒƒe¾_–ÚÒK«ÚÒË`I°Œè•`ÑK+½´J°,DDDÔƒÅ`I°Œ–$XjK¯ÚÒJ/ƒ¥Ë|¿,µ¥—VziÕƒÅ`=Ð+ÁR[Zé¥U‚Å`™gFDDD+Cƒõ¥/})1YˆˆˆˆÕœœœd°z  €Á`°rƒØ“…ˆˆˆX‹ VŸ™6z饕^Z饵\z,onzé¥UmÕ–Vz,€þƒÀ`0X ´ÍÍÍðéOúØua~~>ÌÌÌâ¹»»Ût[^000P“Ͷå4¡ži¼öÚkÉOC …'žx"ѶµµUØúµŽe­iÙ>¯eù>ÎÓ±–ÁÊø]¥¥¥°ººztyee%ÌÍÍ5Ý–Wloo‡óçÏí“"Ö¹ŠXÏ©©©°¾¾ž|A=zô(¼úê«áÂ… …­oQëXÖš–íóZ†ïã¼k¬~ ÇÇÇ“7yq¤866Öt[^1;;nݺUJƒUÄzÖ™3g m°ÊRÇ2Ô´ìŸ×"çåXË`u°èÃÃÃÇ.ÇQcåºFÛòˆ„gžyæØ¾ˆ_Ü‘###aaa!ìïïç¾¾õ4­žµ°±±‘$ E­oYêX–š–ùóZôïã¼k¬½Ö¨¡zN¼W¿ã¥—^ ·oß®¹-Žâzzzº0µNk*Z=Óˆµ}öÙgúuŠZߢױŒ5-ãçµèßÇy9Ö2X'(l½Á¢'Xõ´Ç¦ÃJG=D}±±¶(µNk*ˆ¸žÞÅÅÅpùò冡y¬o-”%Á*SM‹úy­‡¢~çñXË`u°èaggçèr)ŽŽŽ6Ý–7¼øâ‹ÉTC#ìíí…³gϪÞÕšŠTÏjܽ{7iŠng_äE­c™kZ¦ÏkY¾ór¬e°:Xôååå䌅 âÿcãa³myÂýû÷ÃóÏ?ÿØõÕg+Åsù$<÷Üsaxx8¼þúëÉß‹/†ýý};,€“âã?cccIrõÔSO…ÝÝ];,€Ó * ,€ŒP™"JšÝMƒpJ|õ«_M’«7ß|3¹|ûöíär¼€Áh•eâ² ÕˆIV¼>n`°ZDe¡Ñ¸ h-ÄH-4 ƒÀ`ƒÀ`0XÀ`0X ƒ ƒЛóSŸBDlH à Àw0X¾<Àwƒ¾<Àwƒ¾<|GƒàË|GƒàËóP4ûÁ~èâ~`°€ÁÈâËó;ß©Íþã?þ#üáþaxâ‰'ÂÐÐPxî¹çÂòòr_HÂüü|˜™™ W¯^M¸»»Ûð>-î†\í‡ 677ç?ýé–nû:ÿò¼^{íµ099™¼Îøzãûbkk«éž¨ÍüýëáùçŸO^çððpøßùð¯ÿú¯ 0X1X##µ™Â /¼–––£Góío;¹®$ñu®®®]^YY sss ïÓânÈÕ~¨Ü¯ÂV0Rç_ž÷ÃÔÔTX__O^g|½¯¾új¸páBÓ=Q›ÅØ‘Ñx~þóŸg°€Áè¥Á:sæLò¥\ß Œ$·ùìg?Þ|óÍ£ƒÀË/¿œŒ–Ï;Þyçdtÿ™Ï|&|îsŸ û·{t»øeÿÔSO%·½|ùr’ ¤$ög¦§§Ãïþîï6L¥ÆÇÇÃöööÑå˜VŒeb°ò´Nr@nÕ`åu?T¿þ, V÷ƒ @ VœZ¸qãFxï½÷PâÁ"4*xàˆ£ûx¿øe^}¹bz*·ÛÛÛ;YÏÎÎ;Ä”à•W^9zž·ß~;üþïÿ~Í×FÕˆ™¾î¤+Oû¡“+¯û!bcc#Is²0XyÝñ¹^ýõäõ3XÀ`ôÐ`ÅQqü⎣ìx@ˆý<¨ùƒƒƒ5ê•ë›Ý.~ùWz†*ÛFGG“Mµiª×WTËL43­¬<í‡N¬¼î‡Û·o‡gŸ}¶…¬Ö V÷CeÊ8¦]ü1ƒ @/ V5bclìc‰ªÊåØ8§CbRTùòO ê]®uð¯L]Tߦ»`åi?tÒ`åq?,..&Ól­M¡µf°òú~øä“O’¦÷Ø”Ï`ƒÐ ƒu8Š­ÉÏFŠˆ="qú"ކ£™9í$ŽÔcÏJõ¶Ø›Òh ¦aggçèrL+âH¿N±úv?œÄ`]­ó/ïûáîÝ»Iƒwë¸Z‡Åx?¤_+ƒ @Ö«EÄ3£*}&‘±÷#¦qJ"ŽÖ+}"'9üË¿üËQÚtóæÍð'ò'ÇnyãÙ€ñ —¨×kûVâm+ˆÿ¯ô®œyÚ'1XEÜwîÜi«ñ»¨ûá+_ùÊÑký¯ÿú¯¤«òZ,`°zd°Þxã¤!6NQÄQoœú¨´nݺ•ô DÆÌI$±é¸²NQ<Ãjÿ±ûÄÇŽIT| /^¬¹†Oå`Ôî:XEÜ•ûµ;}T´ýpÒi´¢í‡o|ãÉãÅÛ=ùä“ÉÒ%Õý[ 0X=0X„Õ¹íû¡öƒ @A¾ß~Žwr'“3“Ì$s&¼HÎÌ™™óœÏ9s^ç}Þsæº@DDDD5ÕuÞ""""‹ˆˆˆHÀ""""°ˆˆˆˆHÀ"¢ýºîº &"°ˆj¬C‡mØÙ^ýõá7ÞX¿ee%|ä#Ù0ÏûÞ÷¾ ÓýèG7=o|žüyn¸á†Ô{1g1¨äü–·¼%¼ýíoûØÇÂ=÷Üþö·¿ X{8&o{ÛÛ»Þõ®ÐÖÖÃüüü¾Ù¾?÷¹Ï Ó$`í…®\¹’ì|ò?„ãN(§|pÃ}qÞgŸ}6 ù·ÿèG?ZÌÔÔÔ¦Àñ§?ýi߬B¿ç=ï üã¬:“w¾óazzº¡·ë"‡‡‡U+IÀ"ÚK=òÈ#›Žøc úÃþüŸß÷¾÷½ä1wß}÷†Û?üá¯W¾ +^±ŠSn(i¤€ÝÒÒ"`ÕÙ˜4jÈÊúvE$`QCéŸøÄ†âÏ|æ3¡©©iÃmqžœ“J@þýccc›ªWï}ï{믾Ú;÷b,ñ´êw¿ûÝM¡UÀÚ;¶XAŒ§©óï{÷»ß¬Ç‘€E´czá…6ö+<ÍçÉ׉'6Ìóþ÷¿Sõ*Wñªtç^8¬’=ôÐCáƒü`²LÛ'ê'?ùI²“Áð­o}kò7NÇÛkñºùŠóçß{² uñâÅpÇw„|àÉòÄyb?Ûý÷ß^{íµ’ËƒÁ—¿üåðŽw¼#ñÀÀ@êû—ùCúPòüñuâéß­Æ šå*öÞïÄsV?ýéO=^Îr¾N Õñ¹rïu¬èþà?X_¾õ­o%ÛF¼/Îx¡ÊY?,°ˆ2¤¸)öÁüµ¯}mÓü×®]KvÅ›¼ËùðOÛÞ^¸ƒÜî<÷Þ{oÉ׋÷—ZÎí¼nN±b÷•¯|eÃ}ÍÍÍžÿÔ©S%mÜ©_½zµèòİQx[aXèìì,ûtlµË•ö>íÄsV°bX)|îJØÓîK{L<ùøÇ?žz_¬øæTîú©ZIQ†ôü#50Åy¼/MgΜ)ºSxòÉ'k°ÒæÛjžÂj¬üĪBü›ûOúÓš¾n~õjnnný¹÷»ßmÚyÆåyøá‡‹îT·ó:ùßæ|ôÑG7‰XõŠÍÏÅÞëX®xÎjE¬4¥UË]Îrz½¶¯JÖO‹,¢Œ©°w(:6Á—Ò'?ùÉMiooßÖ~¹+öxmuJ©pžÂ¯ª?ÿüóÉíñ”gÚ¥$*}Ý4ÇÀúôÓOoxÜ-·Ü²éôRTáiÅøØb¯~ùË_n¸-žVÊ)¾ÿù÷;wnËn-–«ð}Ú‰ç¬6P¤½gå.gáë<þøã©Á¶Ô}¹¾¼JÖO‹,¢¨`Å~’Ü'M±:Sø˜ÜeªýÀ/w‡™6Oa3~îÛŽ…;Ï8_5¯›æ´Ë3Äæêí<6?0¥-Oáòç/g1æRlµZ®`­U (¬`导Üå,ö:¥Æ¤Ø}•¬Ÿ XDR©>x=¬Z|ïEÀŠ;Åb÷W³“Ok<ßš,ìC+<½ZªÏg»•¼­n/|í¼_µ\®|ÎjÖ—xš-­7®Üå,õ:åÞWÉú)`‘€E”Uò-¬¬Ýª`E=õÔS›nßJ+µ<ñË•¾Ån/¼~Ùvž«–˵“ÏYÍc æ¿óïT´œµ X*XD5° ¿é¯Ux¬O}êS™ X…=.ñ«øQñBªÛíÁ*çuÓ¾ûˆrºé¦›Š^?§åååäý¯4`öÅ埪,ö˜Z.×N>g%ëKä/\–XmÌõv•»œµ X•¬Ÿ XDP¼VUᩈØC]X )Öð^iÀªÅé¢rO ÅogE•ó-Âr^7V N¬>äúÒ~ó›ßlú)ømÌX5‰ß¾Ì}í¿Ò€Ux½«žžžä¹cÃu±ÇÔr¹vò9Ë ¥ü³Ÿý¬âå¬eÀªdý¬Å{@$`í âož¢Øê·Ó~,·žVTµ×Á*÷u_~ùåMÓqëÇšœœÜ^kÙƒ+3ñb™åîhkµ\;ýœÕ¬_q\fffªZÎZ¬JÖO‹,¢:WáÅ(¯¿þú ý'ñ[Wñ¶üy:”¹€•«ÄËä_);NoçJnáÏöc½ôÒKÉï4ÆŸ Š×cŠ}nqãcÅ)VU* XQùË_¾\hˆ×3Kë+T-–k7žs»ëWëªâ)¶xEõR?³Ýå¬uÀ*wý°HÀ"""""‹ˆˆˆHÀ""""°ˆˆˆˆHÀ""""°ˆˆˆˆ,""""°ˆˆˆˆ,""""‹ˆˆˆˆ,""""‹ˆˆˆHÀ""""°²­|pÃô±cÇÂwÜÑ>zô(8p`Ác^·¿¿_ÀÊ×<žzꩆðK/½„XpàØƒ×ýú׿.`åëþûïo˜«çž{8°àÀ!`í}Àºï¾ûæÃŠ™™™Ÿ°T°}àÀC ,=XÎ;ãÀ8,,8pàÀ‚‡€¥‹™™™õ`©`9úÀc‚CKÀÒƒ…Xpàhä€߈ƒn¸­©©)Õi*g^,8pàÀ‚GÃ¬í¢¨ÅÅÅpøðá¢Ï¡‹™™™õ`•’†‡‡Ã¹sçv$`©`áÀ8ö]ÀºråJ¸ýöÛK>¶¥¥%qggg +++z°pàÀ V18q"ÌÎÎnëyâ©Ä°SƒUÎ…+F.ñÆ¿YÎ9ë<óóóÆÃx8}ùò冸üÆ£üéÎΰÉYL¬¸à½½½e=×ÚÚZhmmÕƒÅÌÌœÇ`¥k—V¬.]¸p¡¬çZ^^íííz°pàÀ Ž pÔ:`éÁÚ"`½øâ‹áرc[Î?00æææ’ÊU W£££a||\8p`Á‘ŽZ,=XE®a•œ.]º´eÀš™™ ýýý¡¹¹9ttt„‰‰ ß"Ä,8T°T°ü!33³,=X~‹ÐÑ8p`Á¡‚%`éÁÂ,8ô` X*XŽ¢pàÀ¡‚…CKK333ëÁ°T°pàÀ ,K–>8pøÌ¡K– –£(8pà0&8T°ô`133³,KËQ8°àPÁRÁÒƒ¥z°Œ =X*XŽ>pàÀ ,K33³,KËQ8T°p¨`©`éÁrþ8°à¨Ì§N] }}¯%ëÎ;ÿ_8yò=X*XŽ>pàÀKcsìä©»®n½õ_áæ›W“׉ÿñ‹+5 Y»É!`éÁbff®›Þ¨žžåÐÕµ–¼FÎ_øÂj8räõÌöx X*X8pàÀǶ*?bK–>8pèÁÂQ;L¾ô¥•Ô tÛmÿ̇€¥‚…XpÔMïÒ#üϦpõùϯ…ñq=Xz°˜™™¹bŒ\Nú®rýW?ü'×ÁRÁr…Xp¸–€¥ 8°à¨C¿E¨‚åè8°àPÁ°ô`133ïßëm X*XŽ¢pàÀ¡‚…CKK–óç8pàÀ‡,K 8°àPÁ°ô`133ëÁÒƒ¥‚å( 8Œ ,K8p`Á¡«V|#<¸á¶¦¦¦T§imm-ŒŽŽ†¡¡¡$`å+V§bÀÜöcÓnËõg¥¬8¹Äÿfu:ç¬óÌÏÏãa<|<âôåË—âó7rʧs+ë㑹€•«Jµ¶¶îHK33³¬}WÁŠZ^^ííí©÷õõõ…«W¯nèÁêêêÒƒ…XpèÁ°ò500æææ’jT Wñ2 ããã©óOMM%ßÌÿáðð°,8pàÀ‚C–Ë4ä_®aff&ô÷÷‡æææÐÑÑ&&&Š2×Ár…,8T°T°ü!333ëÁò[„Ž>pàÀ ,ËoâÀz°,,GQ8pì-Gü/´ñÀ‚CKÀÒƒÅÌúA˜msz°T°EáÀQõþa¯‚…£Q8vªj¬‚¥KuÈQïKz°T°EáÀ¡‚e<¬[8ö‡€¥‹™õ`1³€¥‚åè,,8T°ô`9އ,ãaÝ¡KËÑ 8T°°àÀ!`éÁbf=X̬KËÑ*XÆÃ6‚CK–óÎ8pèÁ2Xpà°T°}àÀ¡‚eÝÂCÀÒƒÅÌz°˜Y– –ÔŽ‡ –ñÀ‚‡€¥ z°°àÀ!`©`9úÀCK ,=X̬‹™õ` X*X8pàPÁ‚‡€¥Ëùs8ô`éÁÂC– –£8T°Œ‡u ‡ –,fÞÇûC¸å–kIÀêêZ{ó³âŠ÷…™,,G8‹£Ö•¤R§N] ‡½‘¼f¾ÿó?_®;^,8T°ô`9ŽGݬRwÞ¹¼)\Eß|ójÝñêÁÂC– –£8ª ûÍ*X8p¨`éÁb憹ÕÑ£==›«X·ÝöφäefKËÑ=X»ÒƒÃÔÝw/%¯{ë­ÿJ|òä z°l#8pXz°œ?Ç£R޲b%+XÍÚÍpå:X8pèÁª¹âqðàÁ ·={6ô÷÷‡ÖÖÖpàÀ044RßÔÔ”j,8p¸–u Ž}°Š¢077ÖÖÖÂêêj8}útèíí-úz°˜Y/3ëÁª $µ´´ìHÀRÁ‡ 8öeÀºpáBRÕ*öؾ¢;ßüd +++©Á*ç|?~<9M™ø7«Ó…³ÊóÊ+¯ã±gã–ñØéùùù†øüÆÃxd.`ÍÎΆ#GŽíÁÊ×ââb°U°pàÀ¡‚eÝÂC+MIÔÒÒÒ¶Ÿ+ömÅæx=X̬‹™¬ÅÝËÕòòrhooWÁ‡ –u +_çÏŸ/YµÊŸ?ÿ‡1\ŽŽ†ññq×ÁÂGEõ°\ ×Á*ë2 ù—kØêÚVùÿÏÌÌ$×ÌjnnÉiEß"ć –u Ž}_Áò[„̬‹™õ`ù-B©,ãK8ô`aÁCÀRÁrô‡ – *Xz°˜Y3ëÁ°T°pàÀ¡‚…K–óç8pèÁò™…‡,,G8p¨`ë,=X̬ËûÀÌ– –£8T°¬[8p¨`éÁrþŽzãˆÁªÐÆ – –£8p`ÁCK333ëÁRÁ’ÚqàÀ –,8pàÀ‚‡€¥‚åè*X8p¨`éÁbfff=X– 8p`ÁCÀÊBVÚEtœ?Çc‚C– V‚–£8pà0&8T°ô`5HÀbfff=X*XŽ>pàÀ¡‚…‡ –¬úXΟãÀ z°T°}àÀC ,=Xz°˜™™YÀRÁrô,8pXz°œ?LJ,8ô`©`©`áÀ‡1Á¡‚%`éÁbfffKËÑ8T°pàPÁÚ¾â¹Òƒn¸mmm-ŒŽŽ†¡¡¡$E/--¥>¾œyõ`9Ž,8p4|ÀjjjZw¾&''Ãôôôúô™3gÂÈÈHês”3ïNU°Ò~èy/~ðÙÑ8°àÀ¡‚µ!hå«§§',..®O/,,„îîîÔÇ–3o5=X¥ÂR%K¯33³¬] Xmmm›NÞVɼÕT°²°}àÀ *XEVát±ÛÊ™7ןU°Ž?žœ¯Í Hü›6]*DUêR¯WÉtáßZ?ÿnM¿òÊ+™^~ãa<ŒÇö¦ççç3?q:rã¡‚¥‚åèXpàØ¬¾¾¾põêÕ }U]]]©-g^=XÌÌ̼oÖÔÔTòmÀüo§Î¿Õ¼¾EèèXpàØ·—iÈ¿\ÃV×¶ÊX®ƒå&8pàÀ‚‡ë`¹’»£8p¨`*X~‹0+‹™™™õ`©`9úÀ‡ *X°ô`9Ž,8pìyÀzì±ÇT°T°pàÀ µ Xñ|½½½ÿ¾º¹,=XÌÌÌ\}ÀºõÖ[“ÕÜÜÆÇÇÃÊÊŠ – 8p*XÕ¬xÝ©'žx"¹Zz ZÝÝÝá·¿ý­,=X8pàÀaLpèÁª¶É}uu5LOO‡o¼±èCU°}àÀ‡1Á¡‚UFÀºvíZò³47ÜpC¦–,fff®‹€533ófŽè\?E877§K 8Œ ¬JÖ—¾ô¥õ&÷“'O&•,×ÁzjÏèÙùs8p`Á#Ã=Xv™†ZW°}àÀ‡1Á¡‚UvÀzüñÇý!333óN4¹»’»ÔŽXpà°vå:XΟãÀ‡1Á¡KK 8°àÀ!`éÁbfff=X*XŽ>pàÀØàPÁÒƒå¼38p`ÁCÀRÁrô,8p¨`éÁbfff=X*XR;8p`ÁCÀÒƒ…Xpà°T°}àÀC ,=XÌÌÌÌ– 8p`ÁCÀÒƒU«óÎa“?Ç8,¬¤öz VŽ¢pàPÁÂCKVf]ï‹™™Y– – –£(8p*Xõ°šššR]í¼®ƒ•€¥=X8pèÁÚ-..†Ã‡ X*X*X8pàÀ‚‡ V™çÎÛ‘€¥‹™™™÷]ÀºråJ¸ýöÛKžNliiIÜùfr +++*X*X8ªtÚu=Œ,8p4HÀ:qâD˜Ýö©Ä°SƒUÎù:~üxr¾67 ñoV§ ÿ›?î+ë™ç•W^ÙWãQïÓq…1¶˜žŸŸoˆÏßÈa<ŒG¦V\ðÞÞÞ²³¶¶Z[[U°T°pÔ°’eËxà0&8ô`©`©`9ŠRÁ28°àÀ!`éÁÒƒÅ3³€¥‚¥‚å(ªîBU1Xpà°ô`éÁjXŽ ;»°v+´Y¯°àÀ!`©`©`9а¬W*X8p¨`éÁÒƒÅõt/k6žÌ¬KKËQ” – –í –¬ÿ V…vþ\–,ë8,,G8|‹Ðzå3 ,=XÌ®ƒÅ̬KKjÇ£–õªþY²Z1µná°\ Ž}°¬WÙaÉÚ:gÝÂ!`©`áÀ¡‚…£îY²¶ÎY·pXz°˜õ`±uŽYÀRÁrôÃÎÎx¨`*Xz°œ?Ç¡ËxàЃeÝÒƒ¥‚¥‚…‡ ,c‚CÀÒƒÅ,`±uŽY– –£vvƇ ¬=XMMM©NÓÚÚZ CCCIxŠ^ZZÒƒ…‡€e½ÒƒeLpX…k»šœœ ÓÓÓëÓgΜ ###*X8pXÖ+,c‚CÀª4`õôô„ÅÅÅõé………ÐÝÝ­‹YÀbë³€U°ZZZw¾¹¡…•••ÔyÛÚÚ62,¼-¬r.pàÀ£,–¬ mLp¨`éÁbfff=X– 8p`ÁCÀÒƒåü98|fáÀ¡KËÑ8p*Xz°˜™™™1`={6ô÷÷‡ÖÖÖpàÀ044RçmjjJµ 8p`ÁCÀÊÓÀÀ@˜›› kkkauu5œ>}:ôöö Xz°œ?Ç8ô`U ––– X*X8pàÀŽ}°.\¸TµŠ¬¾¢;;;ÃØØXXYYуÅÌÌÌV1ÍÎΆ#GŽíÁÊ×ââb°SƒUÎ…§c91—xã߬Nçœužùùyãa<ŒGƒGœ¾|ùrC|þFãa<2°&&&’ ÓÒÒÒ¶û¶bs¼,8pàÀ‚‡ Vb"Œîåjyy9´··ëÁÂ,8pXù:þ|ɪU~c{þ7c¸ ãããz°˜™™YÀ*çÚVùÿÏÌÌ$×ÌjnnÉiEß"Ä,8pX~‹Ðùs8pèÁÂC–ß"”ÚqàÀ –,fff°T°}àÀC ,=XΟãÀ‡1Á¡KÀRÁÂ,8pXz°˜™™Y– –£8pà0&8T°ô`9ïŒXpà°T°}àÀ *Xz°˜™™Y– –ÔŽXpà°ô`áÀ8,,G8pàPÁÂCK333³€¥‚…Xpà°ô`9Ž=X8pèÁRÁrôƇ –,玙™™YÀRÁrô,8p¨`éÁrþ8Œ =X*XR;8p`ÁCÀÒƒÅÌÌÌz°T°}àÀC ,=XÎ;ãÀ8,,8pàÀ‚‡€¥‹™™™õ`©`Ií8pàÀaLp¨` Xz°pàÀ – –£8p¨`áÀ¡‚¥‹™™™õ`ÕWÀZ[[ £££ahh( DÑKKKUÏ«‚…XpàØ·krr2LOO¯OŸ9s&ŒŒŒT=¯,8pàÀ‚Ǿ X===aqqq}zaa!twwW=¯ 8p`Ác߬¶¶¶M§ o«d^=XÌÌ̼oVSSÓ¶n+gÞ\VaÀêïïONÆJV [ñ¯iÓ¦M›6mÚt9Ó1Oìë Ñ^kOV___¸zõꆾª®®®ªç%"""Ú·kjj*ù6`NñÿáááõéüS€[ÍKDDD$`…­¯m•°Ê½V¡ò{³˜™™™+uݬÝT¹o8pàÀ‚G&+XÂÆŸY8pXDDDD$` XDDDD5dÀ*ç2Õ^òa'uöìÙä2ü­­­áÀÉ2Æ‹ª¦)^Æ"Íõ r–­žÇ£Žzø‹óÌüö’Æ‘Åí%#«ÛKµ,{=&¥ÖŸ,m#¥8²´”ZÖ,ïS2°&''Ãôôôút¼éÈÈHÕóî¶ÂÜÜ\²‚¬®®†Ó§O‡ÞÞÞ¢C½ªœe«çñ(Ôââb8|øpfÆ£ØQÖ¶—bYÛ^Šqdq{Ù·™RëO–¶‘RYÚFJ-k–÷)™X===ɆœSL½ÝÝÝUÏ[jiii耕¥ñˆ¿pîܹÌGV·—í¼ÇYØ^ª Xõ¶½lµìYÛfrëOÖ÷)Ŷƒ¬íSrËšåm$ó«Q8úÂ… Iª/¶1Ä•/º³³3Œ…•••ºÙlwÙ²2W®\ ·ß~{M˜÷z'˜Õíe«Ù¬l/i+«ÛK©1ÉÚ6“¿þdyŸRj;ÈÒ>%Y³¼d>`¥mä¥Îûg!¹ÏÎΆ#GŽ=_ž¯˜Öã 788Xw[-[VÆãĉɘԂ¹+&YØ^J½n–¶—RYÛ^J½v–¶™Âõ'«ÛH©í KÛH©eÍÚ6¢‚Ug“‰‰‰pß}÷•Õ˜9bs`=ªÔ²ea`MMM%ßÈ)þ›+Ó>¶šw/uþüù’©=Ÿ#ÿqe‹_K¯ Ž­–-+ãõâ‹/†cÇŽez< w‚YÝ^ 9²º½”ú6dÖ¶—´€••m¦Ôú“¥m¤G–¶‘RËšåm¤á¯ƒ•ÿægùºKùÿÏÌÌ$× innIYµ^´Õ²ee™LÇÛ‰ˆ,"¢ tíÚµp×]w…¶¶¶ðØc%=VVV¼9D$`UªW_}5tww'•«C‡…¥¥%o  XDDÕ(*‹ˆ,"¢)wаµµ5ivwŠˆ,"¢*uüøñ¤rõ‹_ü"™žM¦ãíDDQ™Ê]¦!^–!_±’o÷ XDDÛTîB£ñ‚¢iŠ u¡Q"°ˆˆˆˆ,""""‹ˆˆˆˆ,""""‹ˆˆˆHÀ"""""‹ˆˆˆHÀ""""°ˆˆˆˆHÀ""""°ˆˆˆˆ,"ªj½î:æ¢&"‹ˆ* XDÖ "‹ˆìDɺAdõÙ‰’uƒˆ,";Q²nX7ˆ,"²%ë‘€EDv¢dÝ "‹h_îD;;‹»„þþ÷¿‡oûÛáÀ¡µµ5Üu×]ajjªìåkjjª)ïK/½ú莬®®ô·áæ›Cfß‹¨ .$U-‹HÀ"¢:X±¢wúŸýìg“êÈW¿úÕpåÊ•ÿ *]ayyy}Þr}Q…á'7]ê17ÝtSøõ¯½mæJVþéÁœÿã?¶®`Õó{1;;Ž9¢‹HÀ"¢,¬|ÅŠJìó‰;ò\PHs©PQê1¿ÿýïÃ=÷Ün¼ñÆðÄOìXÀú¯ÿÚüÄÓ†Ûit¯Ç÷bbb"Üwß}ë§,"‹ˆ2°rŠ}AQ±ÊR¬'©X¨(õ˜œþú׿†¶¶¶ XQÓÓ!>üïÓ…±/¼œpUOïE<õÝ«Z7ˆHÀ"¢Ý݉ÆoÉåzŽ¢cßP¬–DÅí3gÎ$ß`‹Š—NÈï;J ¥û‘b3x¼­sÁ¯Ö—~ÈÚ{qþüù¢U+‹HÀ"¢:X?þxÒÜ{Žbµ&^k)§CFì%Š÷=z49uV*T”zÌC=”¼F¬ì<óÌ3%ƒUÚiµV½½¥N1 XDÕqÀ"ë XDv¢DÖ "‹ˆìDɺA$`‘(Y7ˆHÀ"²%ë XDTÑN”¹˜‰HÀ""""Ú7úÿ]¿eÇKú¼IIEND®B`‚XYLineAndShapeRendererSample.png000066400000000000000000000362221463604235500357200ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/xy/doc-files‰PNG  IHDRXr5˜ß™ïÀyÉ9ûÙÝg÷û<Ïo÷ S!„B(©Æ*@!„"`!„B°B!„X!„Bˆ€…PF4ÈaÃú9hŸ‡Xÿ!rдiÓúôÇŽ«Ž9Ò÷z$Q_|q¿÷Œ5ªßãK.¹dÀçÊç˜ß3iÒ$ß'™ œÞ{ï½Ë"Ï¥û„¼{÷nuÛm·© /¼P}ãßP'œp‚:õÔSUVV–Z°`Ú¶m[Fžà­†¿þõ¯«“O>Y7N-^¼X}ñŬ4nÙ'O?ýt•——§ÊÊÊÔgŸ}–±ÇÑ^xAïs×\sf6Ú£üÿúë¯WÕÕÕýŽ«ˆ€…†€öí󤾣 ÝsÏ=ý^“÷¾ñÆúdf~þÉ'Ÿìû›úúú'¾>ø ´˺¬ë('äeË–9 §¿Éô€eõˆ#Ôûï¿OÀ È6‘ðßØØ˜™'OüW\q…:pà'JzôÑGô<%É(üßüÚÃ?¬ÿæöÛoï÷ü˜1cúzhÖ/éÙ…ù$cË(IºNÈÒö2†rÀçää°¶M21dye_´h'jºüòËû®¾új=Õd~NÞc¨½½]÷Hͯˉß:z5räHµÿþОdÌÓƒ2g¹Kõ4¡Óº²ØÕ«W«®®.ýšLé¾úê«zÔíÜsÏ˰?øàƒ: ¬ô±É¢”˜_>|¸>~d’¤­I›“¶×ÝÝíÈ.£ªˆ€…†˜Þ}÷ÝÓ~Öi>yY<ð@¿÷Œ=zÀÉßñŠç$ãô^ëór°ž7ož:å”S´KJJl?oÏž=ºfI†R!õ:R?VZZê8tož”Z'y¿Û4a¼Ë¶|ùrýù²\²|òÙNüÖíäµ¶%že›3gŽÛ¦eÙ$°Èv–Ú½7ß|3æwHÔüùóõßËßfggÛS?ÛÆm’QTókò9Éü®TlÏd,—pËwžþùzÿHågƯºê*×)v¯Ëiý ÕòYƺ–‘ôÇ{¬o_X¹r¥>&Ékò àVýîw¿ÓAÈØÇå_y,Ï'*é\š—W>°Ð”ÌœÖÒ¥K¼_zjrÐrú»i´T,9Ī‘Ú°aƒk€”ƒyGGÇ€eã=*Ìxáó²l¾¦ûdTÐzÑÁwÜ¡ššš\k<âY¶XË$ßéö~ëF/Þ<‚áwÛ8­9™ÉŒù5 t‰ìƒ±=“±\Ö“ªÏŒ7`IX±~v<ìv¯Ùýtþ.»ì2Û×d¤Ý´·m$¯'"9Fš?OÂ"`¡!¨C‡Ù&9©Èkvjhhp<8Y¯bKUÀr:(zûí·4¥w»víZ׃©õêA9‘ÊtœÛÕ„~—mÓ¦MN<>dTʉ_jÚÜF¯¼òJÍfLUÄ»lFÀ”%ëKF ¬ßý£ýÈõ;š››õßʨùy óñn¯õ.2 ÒÒÒ’Ð~0Û3Ë•ŠÏL$`ɾc7²èw9ýÔzÅÚ^ÖÐ'û¨Ý¾úÜsÏÅ}L•Ñ´Á¾8°P@e­aK¼›äêëßäççÇu Ž'`Éí ¤öÁi(þ¦›n0­`7$áÒ,ëÕƒÏ<óŒ~F|b-›¬'ókr¹w,~¥’‘™X' Gæ[ø]6;Y×™õýNË,M˜Ÿ¿ôÒKãÞ6^N¢òþW^y¥ß²%ã»R±=“±\Rûh½LÅg&°¬ï1Ö™ßå´~ÏÖ­[mƒ­ÛkF]žÜ>ÁºmER aw‹¿’,©733p! 1‚5à AãÀg'%°þq[†ÁXvdóûÍ97[ÂyzШu’© ·iP¿Ëf½PÀ|¯œXëJFåà/ËíÄ4sæÌ¸—M$ÌR‡#'#É”‘‡x®TtªŠgÛxy¿Ýí’ñ]©ØžÉZ®T°z>yøÁ’õÏrzÝ¿¼¼æ´­¬ï7–ÕäøgžNk +"`¡!$·zÍI´›Ê€åö¼[}‡ÓߨÝ\ÔÉæiÂD—-žu%')8¯¨¨¥;Þe“Ï´Þ#-‘[AØ0ãÙ6v…çÖº4 ¾Öiíd|W*¶g2—+•Ÿ™Hû—i6»Ú8¿Ë™Œ}ÏxÍÚ1‰µ¯z•ŒpJ¡¹æP¦B QÅsaX–µ§j­M²“ÝÍElž&ô»lÖûŒyá—ðàt‹™ô;Báô¼õÖrsS¹D<'9§Œx¶Ýw¼üòËž—«ÒÌJÖw%{{&s¹Rù™‰ü­µ`^jãYÎd¬TŒ`I9…ù8*5Šü1'DÀʲ^q#÷À²ÞKЧð¦L™âx×yC„×b¼,ó4¡ße³Ö¯™§¶Ü>Kâ·Þz«0¦o¥¾ÃZ„o¾w™ßesêáÇs’³ £®%žmãôvWÁJQ"ûÁ`lÏd.W*?3ž¶*üÖe‘ÑF£Éïr&3`Yk°äVv£×^j°ä VëÏŽI±¼[i"`¡! ¹W•uÔCjÄÖ¹SÁ{¼+žé€x¦º¬· ú%é-KoU®v4.÷6N Öi6s-üßÚû5F”ü.›õþHrß)ù|)ÐMä*JÃRàïz³ÖÇÈz±xÇ:‘ÉÉQN>Ö°nÜÞÁï¶q[^y¿5àÈv2ê“ù]ÉÜžÉ\®T~¦Ÿ€åf¹²4ÞåLfÀ²N[J Ås¡Ýí;â½"°P†H~‹Ðbý¡Ý-ƒ°Duuu¢Ó÷Þwß}ýž“ž©UÖÞª±Îü.›ôäåæŠ~ÊÖíå4¥+—º'2"èv;¯'2;OŸ>=îmk;ËtŒ5Ê Ú¨ÇJÖw%s{&{¤ò3iײ]¬÷Mó»œÉ X¢dÝËï­" YoŠ(¿»g®ƒ!nëoñÙŽ ,ÑÞ½{uhi3¹ŠMBˆØeŠOFŒ;“KÝ„Ó4“!ëý¿ŒûAųlŸ|ò‰¾¼ß8ÉHoØ®¦È¼Mäû%¨HQ»qeŸü+lr³Í?üÐó6rz^ö¹S¹±\2µcíõÇ:‘I@•;ÀKxµ“×mãe;[®ÉZ•ŒïJæöLÅ:HågzmײÍ%TÉ›ÜQÝíçq¼.g²–1’%ÛË|'wyìçNî,DÀB¥ö ‰!„X!B°B,„"`!„†’¤nÆl„B,„B!B!„ !„Bˆ€…B!„X!„BC%`ùùqßÁþÞ´®ô!|9ûPdOW;óú—;£ËÝÀåßr§mùw¨î_v¿t Ö0®»¡pø çïmذA7ãÇ>åG÷íÛ×ï3vîÜ©®¸â ý§Þ¦ÜX¼._ss³ºêª«ú^—ÿË”3fŒþœwß}W?–Õ•Çòw¢={öèÇòãÀ~Öï£>ªÿN~XØ,y,ÏË$Ñõç•ÑKxOÖ~ãgy¶mÛ¦ÿV¾O~¬öã?Ö?î{Ùe—éçä59X™ÿ¦©©I¿n|¯ytÆîd™Œý7Ö¨²W^·eñ»þ½~§[‹ÅkÝmÚ´I÷ÔeyO9åý㈻k¸}Ÿ×K÷°D2½"Ï]}õÕ¾ö¿Çn/wñ÷ò½±¶Ù믿®?CÚ¼nœ¾7ÕÛMF©äsJKK|DzeËôkò¯Ëïf"ûz÷/?œí¶\"9NËñE^—ýæ’K.éÚü»°dK’—Ä/'3yNV¾õoe®Uþoü¢¹œœØNr²’÷ÊßD"ýËì²¢½~¯HÞ/áÄüºõ3Œá7E»±xY¾^x¡OƒØøLÙiD3gÎì×Ó¸æškôcÙaÌAéæ›oöµ~e§3£,›1ñwttĽþÌÿ÷ÂèåÀ”¬ýÆÏò%ÈãsÏ=wÀsÒ°Ísë­·êÆ/ëwÊ”)ú¹'Ÿ|Òñ •Èþë¥7ì‡×mYü¬/ßéµ¹ñ¹-¯û,îsG(¦e¥ËsÆOj˜ßc±#2n’t-ï}ûí·ãú^»á^ Ö‘iØFÈð³s¸±xY>ƒÏü÷ÒÍ'†¾‘&YF#¥7N¿.i^^—÷ù]¿Òã÷nݺU?~æ™gôcy>‘õgf÷ÂèeßJÖ~ïò8=ç4j'úàƒÀÉØ½,k¼¼Öe‰gý»}g2–—¶eŒ0ʺ“ÇÒ¡ˆ÷øæ¶b}_2k°Ì2ÖëM7Ýä«ÍúÙ㙲qû^§ï2¦­Ì!<Ö÷§z»ë¶Úç’°Ù׃¾ßøܰ.—­XûŒŸcW(–Û‘äh<>óÌ3Õm·Ý¦‡+í$KÞg ?~††§M›¦Sµ ÊèŒ×bB/;‡‹Ÿ†o÷ãŠ*£'= #­ß~ûíú_yM¾×ø¿ßõ+ÁJÞ'£dæéA#éÇ»þü2zùœdí7ñ.O¼…©N!,û¯—åJÇú÷òÉXÖç}ÀïTE¼ë Ö÷¥bKÊdj~øðá}'W?m6‘@`7"žè±Â©Ý¦s»y X~ÚQ2–ß}=ÈûM¬¿‹µ\^ö?Ç®Ð,¾“i™K5ž—Z?j¯ß+Fþ/uK2ša¤ìd,7–d…sÖ‚ ôóÂ!;ÖâÅ‹õó_|±§e²ë ÈN+ï•©4ùì‘#Göí¬ñ®¿Tœà“µß fÀ2z’vŸ›Œýw0V2Öÿ`,cÚÚ.dÇ{`™%CvnS"RXì4ÿoƒõ°bõ\b±xY>ch×Ì'= ëTŠQ‡%Ë$;œ±ãé]v/Ëd'©å2›ü+¡Ímømh^ýö\Ùo¼,O²Ö«¯¾ªŸ“©xÖ]¬õïå=ñò&²þ½î×~¦áý¶}é¡ËÿeÔ׋Ýc}_*k°ü¬¯xN”^Ž…É8VaF‚‹×u—êí¶zõjýº´[e¹¯\¹Òײ${%ºï¥s¿‰°b-—q¬7‡E¿Ç®„豈%_¹RDdÃJqžä ,y]Š…e„E’¨y{ýÞ±cÇöÛñŒz&/,£±Ë•Nrcñ²|2olÔ<ÉP¨–uQæ9eór×××ëçêêêúž3_©ægýФ0Ð\¨.Ãdžâ]~"ÄŠŠ }p’ìüùóS²ßxYžD–|¦Ñåj9@ûßuký;í§~׿נìuý{ݯ½Øbñ9-¯Ls=Uãà+Ë/'#;%ºÆú¾Á X~Ú¬×cº—ca2ŽÆ1MöÙgäŠ4£séÄêí&ŸiܧI ¬eEŽ)æbkã6 ^–ÅË>íw½%ºï¥s¿‰°b-—ÜË˺ÏÈ_~Ž]°d…°œÐeXv@ã¤d'Y¡²¡äý’:ï¹çÏß+áÁø[9ÑÝqÇžX²AåÖÖi ³ÜX¼®/ó%¦bù¿ìfuXò=ÆçËÎd„¢öövOËä$ãJ¹½€5|ųþü2J£b}ã&~r@“ÂùTì7^–'Þ€%ß%=*óç ;žƒT¬õï´Ÿú]ÿ^–Åïú÷²_{9°yású<ù>ã’Yn™þ6߮ìdìƒnß7XËO›õº½ “u¬õ5jÔ¨¾"m¹âË;ÕÛÍY2JeÜÁ¸QçòåËûVyY/ût<ë-‘}/ûM¬€åå(·\2y]:ÒÆÕFík¬íˆ€…B!dYoŸ‘j°B!”q’iD¹"^FefǸÿ L9°B!„âÜXUj°Œ;ÊËT©q_HB!„PEÀB!„"`!„B°B!„X!„Bˆ€…B!DÀB!„"`!„B!B!„+2ÿ¯è§?ý©þöTyîܹ)ýü ^xá…^x‡oqq1Ë-`Ýyçêå—_N™÷îÝ›ÒÏšá…^xá…w(ðÞ}÷ݬt¬wÞygHíÐð /¼ðÂ;x XiXcŒ1Î<°Á¢‡/¼ð /¼,j°˜ó†^xá…^j°Xôà…^xá…—,5XcŒ1b+++ËÖvêííUUUUª¼¼\‡'qggg ÖîݫէŸ.Vÿó?ÿGý×UÓc€Þøµ×¶ªwß½7Ú~Kô¿¯¾ú[¶/¼ð†Är¾•ó®œßy§’€e X^UWW§û744¨ÊÊÊ´¬;ž‹†¼[ÿpkë ê?ÿó%æ¼á…7À~ûíµê«¯Nï×våqKËcl_xá ¸?ýôßœ{ÛÚ®R¯¼ò<ËoÀš3gŽjooï{ÜÖÖ¦ Ó°þò—ñ6°á>¸¼ðxä*9öí:4RwžØ¾ðÂLÿ÷/q<÷~üñOXFÀÊÉÉÑ.((PÕÕÕу^Äö½yyy¦ ­Ï vÀêé9Ùq#wt\ÆÜ5ƵL:µ]±¼ÎzÂ8˜þòË‹ÛîÁƒg°¬’Ñ) XeeežG»ìž3곬«´´T›É^þMäñž=;\Ð꽨‡aŒéâaîíw ëãÀºÓ¹í9rbÂçwë㌸ŠPF¥rss3b믽œ9~xá éÖž=ÿÁö…Þ€zÿþ:¶Ý®®sÁ²SWW—ÊÏÏ·}­¨¨Huttô«Áš:ujZÖ¾}7ò{ïeöm!¨i€7Ì–«­îæt¦_¤Âþ o˜-çW§sïŸþ4‹€%*))Q---z4J•܆¡¦¦Æv °¾¾^_9h¾Š°¢¢"­K®V°º÷ö~]}øá<æÉ1¸ßzk½®×°†«;X?ܤä|;Wð‡2`555©ââb•­&Ož¬jkkk¬‚|,¹¬[îÃ!W•ƒ3=Bx3Ár0þÃîSûö-ÔÓ‚™>rÅþ o&ù7žî»–t˜¸¿EÈ?¼ð /¼ð†„—€ÅoÒC‚^xá…^¿Eˆ1Æã`›€Å=$xá…^xá%`QƒÅœ7¼ð /¼ðRƒEÀ¢Ç/¼ð /¼Œ`°¨ÁÂcŒ1‹,z ð /¼ðÂË‹9oxá…^x᥋€E^xá…^xá%`Qƒ…1Æcj°Á¢Ç/¼ð /¼Œ`°˜ó†^xá…^x XŒ`ÑC‚^xá…^F°¨ÁÂcŒ15X,z ð /¼ðÂË‹,æøá…^xá…—€Å=xá…^xáe‹€…1Æcj°XŒ`Á /¼ð /¼,j°˜ó†^xá…^j°Xôà…^xá…—¬à¬ææf•••åøº¼fgj°0ÆcLÀ²‘¤Ï¥K—Æ XŒ`ÑC‚^xá…^F°<¨µµU«ƒ†:`1ç /¼ð /¼Ô`"`uvvª¢¢"ÕÑÑ3DÉk999ÚªººZE"F°è!Á /¼ð /ˬE‹©>úÈ÷(U{{»Xeee¶ÁʰY¥¥¥:åBþå1yÌcó˜Çê{ì°ì^³{Ψϲ¬ÒÒR½ÓÉ^þMä±|þ¡C'Ùnàîîã~ÿ°aãšö‹qf¶ßHä›úõdžïC°$ Ù™,Œ1#XcF°’¸œdžJ4j°¦NJ süðÂW –¸;×`U³}á…—¬Ì XæÇõõõúÊAóU„i½ŠpÍš•}WÊ´‚ô|%\UVþ"ã¯"¤¦Þ°_…TYy_ßU„Fû•«W­º/ã¯"d†7ìí÷á‡Þw¡Ñ~å*Â_þrWú XA¾ÖêÕæû`­à>:ð¢û`­Yóû`­_¿Žö /¼!ºÖš5wk¿r¬Ç¸¿Eˆ1Æã0˜€ÅoÒC‚^xá…^¿EÈ?¼ð /¼ð›—€Å=$xá…^xá%`Qƒ…1Æcj°Xôà…^xá…—,5Xð /¼ð /‹,z ð /¼ðÂË cŒ1ÆÔ`°è1À /¼ð /¼,j°˜ã‡^xá…^j°Á¢Ç/¼ð /¼Œ`°0ÆcŒ XŒ`ÑC‚^xá…^F°¨ÁbÎ^xá…^x©Á"`Ñc€^xá…^F°XÔ`aŒ1Ƙ€Å=xá…^xáe‹€Åœ7¼ð /¼ðRƒEÀb ^xá…^xá%`Qƒ…1Æcj°Xôà…^xá…—¬`¬M›6©… ªÜÜ\•——§–,Y¢Z[[mß›••ekj°˜ã‡^xá…^–I%%%ª¥¥EõööjoÞ¼Y͘1Ã1`1‚E ^xá…^xÁŠC999¡ XcŒ1¦+p«§§GmÙ²EO:, _â‚‚U]]­"‘#Xôà…^xá…—€åV_%¡hÿþý1ßßÞÞ®VYY™m°2lVii©ž§56„ü›ÌÇŸ}öYJ??há…^xá…w(ð†~«»»[½/X°ÀÓû¥fKŠãÁ¢‡/¼ð /¼Œ`Å[h2«««KåççSƒ…1Æc–Y‹/V»wïÖ£Q‡Ö5X„ì ÛÍWJ¸ªªªR555Œ`ÑC‚^xá…^–YÍÍÍ:8Iáú¤I“Tee¥Ov«©©I«ììl5yòdU[[ËorŸxá…^xá%`q'wz ð /¼ðÂË cŒ1ÆÔ`°Á¢‡/¼ð /¼¬ XÌyà /¼ð /5X,z ð /¼ð /‹,Œ1ÆSƒÅ=xá…^xáe‹€Åœ7¼ð /¼ðÂKÀb‹¼ð /¼ð°¨ÁÂcŒ15X,z ð /¼ðÂË‹,æøá…^xá…—€Å=xá…^xáe‹€…1Æcj°XŒ`Á /¼ð /¼,j°˜ã‡^xá…^j°Á¢Ç/¼ð /¼Œ`°0ÆcŒ XŒ`ÑC‚^xá…^F°¨ÁbÎ^xá…^x©Á"`Ñc€^xá…^x XÔ`aŒ1Æ8ôkË–-I A›6mR .T¹¹¹*//O-Y²Dµ¶¶Ú¾···WUUU©òòržÄi X/½ô’Ú°aƒZ½|¹þ¾U÷Þ«žxäý<=xá ¶uû­­U«ï»O·ß•Ñ×?þ8í^xCÒ~7>ú¨ZSV¦Ûï/£çáÚh{NEûÔ€•••¥æÎ«ç=QII‰jiiÑáI¼yóf5cÆ Û÷ÖÕÕ©ÆÆÆ¾Ç ª²²2mK6bÅš5jýí·«OFV‘OTŸ¡òõ`iiƤ™ã‡7ìçªPgÍRŸŽÕ×~Ÿš9S­‰>Oû…Þ`·ßG–.Uõ7ÞØ¯ýnX´H­Zµ*éíwP–„ YÙÙÙª¦¦FE"‘¤MíåääØ>?gÎÕÞÞÞ÷¸­­M¦-`ÉÈÕã?ûYtMàÆ©SUý÷ÒC‚Þ€ú‰èqkã­·Ú¶ßúèó2’Åö…Þ`úÿ–—ëpe×~×ÿâz$+´KF›ž}öY55$$hIÐyë­· V===zêQ¦ í$SˆÖe°>7˜kåÊ•ªõ‚ l7pÛ™gªŠŸÿœ¹kŒêŠèñAz¾NíwU†w0³*)ql¿­ãÆ©+V„¿È]B‘LÛ]wÝu:hYígÊQ,¡hÿþýŽïñòœQŸe X¥¥¥zØÔHöòo"åót’íî>þø£ßoóÆ8ý¦ýbœ™í÷«o|C¿žÌó}ZVww·®…š4iRBËø,)z_°`AFŒ`U.^Ì?¼ð†u+ú:Û^xC8‚uÉ%áÁjjjR}S„R¬ž É…v***Rýj°dŠ25X$ :Ô`ý&Ã5 ð†Ùrõѯå‚›öÛpÓMjï~Åö…Þ€zÓw¨úéÓmÛïÑðê¬[n¹¥¯ÈýñÇ×£Oñhq4„ìÞ½[F>|X×`I²›¬¯¯×£eæ«+**Òzáš•+Õsçê$-Ó Òó•pUù‹_¨mÏ?Ï\9ƾ ©²¼\ýúæ›ûµß†oT«î»oHܪã0·ß‡£ùAB–¹ýn,*R¿\±"ÜW&ë6 ÍÍÍúV rå L3ÊmºººlV ïƒulyÖ,]ªêR°qé!Á ojÒO®[§*îºK·ßÕË–©õÑÇ´_xá Gû}jåJU=ï÷¡¬}ì±ðßkëÖ­ÜÉ9~xá…^xáÍx^~*‡ß"¤‡/¼ð /¼,~‹cŒ1ÆÁ6‹,zHð /¼ðÂKÀ¢‹9oxá…^x᥋€E^xá…^xÁ"`Qƒ…1Æc#Xôà…^xá…—,sÞð /¼ðÂK ‹¼ð /¼ðÂKÀ¢ cŒ1ÆÔ`1‚E^xá…^xÁ"`1ç /¼ð /¼ð°Á¢‡/¼ð /¼Œ`Qƒ…1Æcj°Xôà…^xá…—,5XÌñà /¼ð /‹,z ð /¼ðÂË cŒ1ÆÔ`°Á‚^xá…^x XÔ`1ç /¼ð /¼Ô`°è1À /¼ð /#XÁX›7oVÅÅÅ*77W?^•——«¶¶6Û÷feeÙš,Œ1ưL*))Q---ª··Wõôô¨7ª¹sç:,F°è!Á /¼ð /#Xq('''”‹9oxá…^xá¥+k×®]zTË)`Iø¨êêj‰DÁ¢‡/¼ð /¼,'íØ±CÍž=Û±ˬööv°ÊÊÊlƒ•a³JKKuÊ56„üËcó˜Ç<æ1yìö8Ô«¶¶V0uvvzþ©Û’âxF°è!Á /¼ð /#XÉŠ‘Bw¿êêêRùùùÔ`1Ç/¼ð /¼,³vîÜé:je.l7_q(᪪ªJÕÔÔ0‚E ^xá…^x X~îmeþSS“¾gVvv¶šÿ[꯷^‘ñáŠ!¼™rþtöõê«ï[·ß¯Î;Mí»q"÷Á‚ÞXγ_eEÛïiÇÚïpÕ:­€û`ñ[„cŒ1ƒ Xü!=$xá…^xá%`ñ[„Ìñà /¼ð o°y XŒ`ÑC‚^xá…^5XcŒ1¦‹€E^xá…^xÁ"`Qƒ/¼ð /¼ð°Á¢Ç/¼ð /¼Œ`°0ÆcL ‹¼ð /¼ðÂKÀ¢‹9~xá…^x᥋,z ð /¼ðÂË cŒ1Ƙ€Å=$xá…^xáe‹,æ¼á…^xá…—,=xá…^xáe‹€E ÆcŒ XŒ`Ñc€^xá…^F°XÌyà /¼ð /5X,F°à…^xá…^5XcŒ1¦‹€E^xá…^xÁ VÀ’ùÓ &¸¾'++ËÖÔ`1Ç/¼ð /¼,—àë}Œ`ÑC‚^xá…^F°|­0,Œ1ÆSƒÊ€•““£]PP ª««U$a‹¼ð /¼ð°’1BÕÞÞ®VYY™m°2lVii©ž§56„ü›ÌÇŸ}öYJ??há…^xá…w(ð©€%êííU¹¹¹Œ`ÑC‚^xá…^F°’°ºººT~~>5XcŒ1&`ù XæçJJJTKK‹¹’pUUU¥jjjÁ¢‡/¼ð /¼,/÷·² XMMMª¸¸Xegg«É“'«ÚÚZ~‹û¬À /¼ð /‹;¹Óc€^xá…^F°XcŒ1¦‹€Å=$xá…^xá%`eHÀbÎ^xá…^x©Á"`Ñc€^xá…^x XÔ`aŒ1Ƙ,F°è1À /¼ð /#X,æ¼á…^xá…^#Xôà…^xá…—€E ÆcŒ©Á"`Ñc€^xá…^F°XÔ`1Ç/¼ð /¼,F°è1À /¼ð /#X,Œ1ÆSƒEÀb ^xá…^xá%`QƒÅ?¼ð /¼ðRƒÅ=xá…^xáe‹€…1ÆcLÀb‹¼ð /¼ð2‚E sÞð /¼ðÂK ‹¼ð /¼ðÂKÀ¢ cŒ1Æ,‹dxo„ ®ïéííUUUUª¼¼\‡'qgggZÖŽçžS{gÎT]çŸ]£ÃÔá³ÎRêçé1À o°Ý×~Ï;O·ßCßûžúóôé´_xá Kû½ùfupôhÝ~Ž•²öÚ€•••Õg7ÕÕÕ©ÆÆÆ¾Ç ª²²2mK6â߯SÏ9G9á½Å½Ç¯™ñiæøá ûÁù˱cÕW§Ö×vÅ‘áÃÕß/¸€ö /¼o¿rî•ó­Ñvå<|`Ìuࢋ’Þ~C?E+`Í™3Gµ··÷=nkkS………i X枯?ÏÉ¡‡/¼A=E{¾Ýßú–mÛ•%=a¶/¼ðÓ™8Q©ã޳m¿ÒqJvûÍø€•——7`ÊÐúÜ`,éå: Å='ÄÜ5Æõ¡³Îrl»:dy&ë 〺çä“Û®L<ûl–Ÿ€e÷ºÝsF}–5`•––êaS#ÙË¿‰§ëëëõ•ƒæ«+**Òzáß/¼Ðv.Xž{mëVæÊ1ðUHr[kÈêýÚ×Ôá#†Ä­0«åüÚ}Ê)νrU°Ô`q¡Ím¬·k0ÿ?¨÷ÁúóŒê«o»/Xýõ_þE½úÛßÒC‚Þ„¬O¯¿^uŸzªn¿rÑʾ‰¹¼ð†Àržmû×U=ßüæÑp=ËÈ3÷Áâ·™ã‡^xá…Þð°ø-BzHð /¼ðÂKÀâ·1Æcl°Á¢‡/¼ð /¼,j°˜ó†^xá…^j°Xôà…^xá…—,5XcŒ1&`1‚E^xá…^xÁ KÀ*..Ö!+U–“NåçÍð /¼ðÂ;x%?°B!„Ò(B!„ !„Bˆ€(?1ÆcìǬ48xá…^xá…whó°Ø¡á…^xá…^B!„P°EÀB!„"`!„B°B!„Xh öîÝ«&L˜àúžÞÞ^UUU¥ÊËËû.éìììŒùZXy7oÞ¬: 77W?^³µµµéײ²²lf^7¦Lܾn¼aÙ¾nûh&¶_?¼™Ð~ýðfBûMoX¶ï¦M›ÔÂ… 5o^^žZ²d‰jmm Mû%`y×°®®N566ö=nhhP•••1_ +oII‰jiiÑ;oOOÚ¸q£š;wnßgdÚöu{=·¯UíííjæÌ™¡Ú¾nûh&¶_?¼™Ð~ýðfBûõÛiíW,sÆŒ¡i¿,Ÿ'&7Í™3GïƤgQXXóµ°òÚ)'''tË+¯ÛëCaûVTT¨^x!´Û׺fzûÅ›ií7o¦µ_¿Û—ö;øÛ—€•Ä’ aš%‰ÛxÎíµL9ïÚµK÷8Œ¿•† .((PÕÕÕ*‰„>`91eúöÝ·oŸš5k–§ud™÷ÑLo¿±x3­ýÆâÍ´öëgûfBû•»-[¶è)ð´_VŠG8ÌsÞÉ! ê xÇŽjöìÙ¶õÒs\VV–1¼V¦Lß¾<ð€ÞÆ^ÖEPå¶fbûÅ›ií×o&´_?¼ao¿FIƒÔNíß¿?4í—€•ÄÒPÁª­­UwÞy§kÑ ðJ¡b¦+S&o_)ˆUçôíëeͤöë…7“Ú¯ÞLh¿~x3¡ýŠº»»uÑû‚ BÓ~ XIº.>®‹Ht]|]Ñu±.|ëÂ¬æææ8>÷/²4ÇN3†¯ˆúO1ÿ2hëB>ÇÎ,DÀB‰¬»îRêôÓû‡+瞪ÔÏîú¹wÜq‡ª««S===ª··WíÙ³G?—N¹(]uwt]\{ºåÄqÌ7D×Å’p­‹’’ÕÒÒ¢—E–iãÆjn4 yÑö—^RõóæÙî³f©Ÿ>´ëB,ásÆŒÞÖÅö躨Ÿg»_46F×ŋχ®ˆÞyçµtéRŸí¤;ê¶o#êÇ^ϺðÊNÀB,äÿqÞyöáÊðÈ‘®Ÿ›““£”Nzâ‰'TYY™ºÿþûõÔJgggßíàÁƒjÚ´itNóÚk¯©‚‚Ýó•דÞËý§óŽŽ\Ù<Þ“éÂð® ó2zÑ#+Vè‘+»}¢íÌ3UÕòåCg]<²B\ÙímmÑuQ¾u!£w2º)Ÿï¯49„+ÃO‡j]° %7`¹ªX¶H¦]üqµ{÷îNé©nذ¡ïñ+¯¼¢VDOÜÆMzÐÖÛßLš4IŸä{dÊ')ËÊ×épâ8îu!Úµk—Éñ².äDu褓l÷îãׯ‡u]ÈèÉ–-[ô2z^‡N²Ý/º»Ã·.$€©ŽŽoíÄÌV<Ì=`- W‘Ï‘Ð'–0V]]m;åKÀB,ä¿vöÙîê»ßuý\9XËmâĉú %µ-ûöíÓ¯M:Uuuuõ½WtF]”õ nг¬1‚õhhÛH{{»Xv£],DÀBþÖ‚JwœsÀš5ËówHoQê|äDn>™Ù:0ÝþæøƒZ¼x±ºîºëÔ³Ï>›ü€uwt]\{œý‰ãé‡s]ÔÖÖª;ï¼³oêÄ‹¶G{üõ7Þh_ƒ=©½øÔS¡Ý/º»»õˆ†X{ZÛ£ë¢þFe_ƒ]/†k]x-ì¶×Qï°ö„v¿0šL)° %°äʸsÎq½ŠãöÆJzNõNL·¿1ôùçŸë+Á’°„õŸ£ëâÚaGG²¹’põƒp® ™V‘ân¿’i’_•—«úéÓõH–L ÊÈ•„«‡î½7®Ûe¿°.§uñ«èº¨Ÿ®G²dZPF®$\=ôPø×…ÿ+ò*ÂÕÄul ҺѰüü|"`¡$,#XÜr‹RßþöÑ`uÚiJ]½§@!Wõb©‰Ñ‘Ÿ644èš‘Ü:Á\SawÀtû©µBWyNê%’â8¶.‹[E×ÅϹ.vîÜékÔÊ.Xl_¿^=TZzôÞOÑŸôQO"hëBF2Œå9|ø°®Á2–ÇóºØ][UÑuñ|8×ERÚ‰Úµ\‘:>ê™êhñ» ݺ0_]*᪪ªJÕÔÔ° %)`% ­[·êÂU©§ž¨ÜkÉ|R—¨ÔIÈëóçÏï»÷ÓÓío–/_®¿Cz­¯¿þºë #¾éÌZ‰MeÖºû=ÉÉTþVŠŸ+++ûÕî ¥u‘œ€•뢩©I_M™­&Ož¬§Óóø‰Xˆ€…B?"`!!Äñ°„âø‰XˆBqüD,Ä!„ÇODÀBÉ=@`Œ1ŽÏ°B!„R¤ÿíñVÁ eô\IEND®B`‚XYShapeRendererSample.png000066400000000000000000000324411463604235500344640ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/xy/doc-files‰PNG  IHDRXr5˜4èIDATxÚíÝ Œ\gy=pTÔ/¡¶¢¢-•¢j*ÔŠV±,¯lE·¶â4‘;‹ƒ]+i² ÍR'”z»”­a ±q»æ+ŸK¼Nâ$Ë&ù(ÄPãàýÛ `'Û8ûþó\:Ëx<³;3ûugçw¤£Ý;3;söܹsÏû¼Ï}ç &o`€€ `X `äæ zÃN#ð,€\ãüóÏ?íäùîw¿;½öÚk£÷§w½ë]§=æmo{ÛiÛögvÆóÆó?æ¼óνïÀé’K.Iò'’~ó73ýú¯ÿzúßù4gΜ´fÍštï½÷ÎÊ|éÿQàßøÆôÛ¿ýÛé=ïyOZ·n]úéO*`Mƒ÷ñÞû½ßû½4þüÔÙÙ™Ž92ëŽï}èCÂ1X3Çg'™âá8Ùðÿø§Ý}ðÁ³PP|û­·Þ:ú7[·n=#@<óÌ3Ù}×]w]Å QéD0ÛV)ÿàþ }ï{ß°¦ÙûùÛ·oŸÇu„Å7ª>‚€0“ø·û·3Föˆž~úéì÷âû>ûÙÏfsùå—Ÿvû;ßùÎÑÊWiÅ+ª2žžžªCF3¬à¼yó¬ò¾ÑCVµÇ€€0 ø«¿ú«Ó>ˆßÿþ÷gSvÅ·Åc ÊFüÅ÷G€*­^½õ­oM/½ôRÙàõéO:8q"»/¦"÷íÛ—UÌÞþö·7EÀ* þ÷ý×=#ä XSÿ?D¥0¦¯‹ï{ó›ßœ½¿, `ÂxòÉ'Ϙö+æ‹ÇcÆ §=欳Î:#D*^Ò篶ç¥TKœü>ò‘¤7½éMÛÛÛÏø›U«Ve!1B`ôxE` }ÑsöðÃûÑuÙe—e;wîܬ¢WŠï~÷»Y?Y„Âx襊ž´ŽŽŽôóŸÿ¼ê Õ¿âûây&óµªñ,pà 7d½qñüñ:1]<ÞIz"ºâÿŽ×üã?þãìý1UÏ9ÞÿpÎ9çTœ&¯EOéëDxŽç*x•ÞÏþó£ûüSŸúTvÌÄ}ñ˜Ú¥øú׿ž…ÀÂ{9~ÆvÜ.`€Ј“E¥櫯¾úŒÇ¿úê«ÙI¡ÒßDÓv1¢šUÚ,å•W¦={öœqÒë„'ÎÒÛJOˆãMÅkŽõøÒFþBoTqeã‹_ü☡4NÀÇŽ7`E…ïŠ+®8í¾tŘèkUãÙ’%Kjž¾¨®Ò`3UÏ9^Ј°Rúõüåî+÷718yï{ß[ö¾¨ÇÇXû"îŸíÕG /¿ürÙÀ'縯¶mÛVñÿôjÀèÅ«Bö7ó7éæ›o΂[-a©Ü•Œî"DE• ª¥¯ýçþçc¾ÆwÜ‘ýmT}ŠoxôÑGÏ8ÑÅãC¥`µ=@Qý»©z­bÏvîÜyFÀˆ0UÆJ'é©Ð5ÏYMЈ÷H¹ b­zjéõo¿”†¾x/–{O~ó›ß°@ÀÈ;J{‚Ñ?Þ÷¾÷ñ7‹-:ãqQ¥ŠÊÌx'˜GÅK”ÞËg5‰i¶r=pµê™Œ÷Xá¾ÒÈxïI ,€¢ž«ký ðPîJ¼@L ÖZ¡¨t{é’±¸i,QÏɯRe£´ºPÚ7V­Gßþö·Ï¸=®V+Æd½ÖX·—®wVÍsM¦®©|ÎjSÚKˆÔ£g2– €€³¥W4ÅX¥ë`EúDOrÔ–/_žU ÓŽÑ/'´JknÕ*üë9ù• B¿ËßþíßV\ɾ€uááx¯QîêÍè#*`2_«Òí¥}tÅS••þf2uMåsŽõ˜ø?K_3ªŠ…®ZõLfÀ*íÁŠ¥"1Hу@ Öª*­EU°´²Q©á½ž“ÜXŒÆßzÃBißLœDK¼Ç;ÁÅI3–O( ™…åb²t ‡è-‹ GTâ ÊÂ%úãéÇ—œ¨JúØ&óµ*Ý^ºÞU¬#ÏØcMÅN–®©|ÎZß{qi½z&3`•N[ÆÕƒñ®"¬çÛ,€IF|aéTÄxßEXnÐj>¼K_§ÒTd\_ÍsWº}¬å ª=Á•ã\pÚßlÙ²åŒZo¯Ð~ô£3‚aœ¸ ýX“õZ•nŠM,¢Y«o“éÁT>g5ÿWø_º>Z­z&3`êYKÀ (]\òÝï~÷i}&1·?&VC¯'`ÅsÅè?‚J4µ®ì‹Ÿ1%‹m:t¨êç®t{è•Ê 'Řò)­Œw‚‹`WF5/V€ÿÄ'>Qözî¹ç²@úãÿˆ€¯ýfQ*^5~ûìè¶€M…Jû÷§ºX¬ŠU9 X `Õ°Ê®™FS¬ÒµfÍštÉ%—L*/»ì²IΩ`#èä%/éä%3«3ΓÅm·õ¥'ŸLuQÀjЀuÍ5פoûÛ“Êçž{nÒŸs*Ø:yÉK:yIãÌêŒó¤ –€•‹€kr4ÂØ:yÉK:yIãÌêl”€•XS°g '#`íØÑ—<ÕEKÀ2Ò1j¤‘Ny©‚¥‚%`éÁÒ÷ÀK:ís:y)` X*XFtò’N^Ò¨‚%`éÁBDDlö,KÀ2Ò¡‘—tÚçtòRÀ°ô`é{ “—tò²Ù5ÞwßþÔßÿ½ôÄ/¾þóé×·°š5`íÚµ+µµµ¥–––´`Á‚´~ýútôèÑì¾Z¾_hdd$uwwg_ø2Èø†m,£F:yiŸórökÜ›öíûŸ×ƒÈHI09•-Ë44aÀjooOY@:uêTÚ¼ysjmm XÕbË–-iûöí£ÛÛ¶mK]]]z°qÖsß¾YýéïRËaJóæÍ«9`­Zµ* nGlÙ²e*XFtòÒ>çå¬Ö¸wïwÊT®NçƒþoVå°š8` fU­BÀа\²dIêééIÃÃÃeÿnþüùgL–Þ¦Kß¼´Ïéœm÷í{®ª€²wïc3°vîœú/{°Ê ¿¿?­\¹r´«QŠ€ÕÙÙYöoËU»ÊÝVèÏ*ÝQÙSÄωn9rdRŸoª¶ Ì«¾Ø>xð`®õ¶C§ýíøqü8~¦sû‘Gþ_Uë;ßùÑ„^/Γ*X °z{{³t§“—*X– –Q#¼¤“—4êÁ°ô`!""6Q–«,#yI§}N'/U°,=Xúèä%¼¤Q–€¥‚eDÆK餑—*X–€…ˆˆ¨KÀ°T°Œéä%¼ä¥€ÕŒk×®]©­­-µ´´¤ ¤õë×§£GŽ{_)æÌ™S–z°ô=ÐÉKûœ—¼°š.`µ··§422’N:•6oÞœZ[[ǽ¯\ÀRÁ2j¤“—tò’—ù­`Ýv›•ÜgóæÍ«ù¾<,DDD=X*X¹Ààà`V¹ªõ¾X¾‚K–,I===ixx¸l°*°YIµúãçD·92©Ï7UÛæU_lO-Õƒ…ˆˆ¨kV¬ööö400¤S§N¥Í›7§ÖÖÖì¾-[¶¼¾3¶>vÛ¶m©«««ìóÔòX,£F^òÒ>§“—*XM7E8oÞ¼ìçªU«ÒÐÐÐèíQÙZ¶lYÙ¿©å±z°ô=ð’—ö9¼°š*` fU­ÀüùóϘ,½­€Z«‚eÔÈK^ÚçtòrúÖm·YÉ}FÐßߟV®\9Úƒ5gΜ3Sî¶Z[èÏ*ÝQYâ/¼)ã§mÛ¶mÛ¶mû—ÛqžTÁjÀ€ÕÛÛ›¥ãâÆt,#2^ò’N^Ò¨KÀªñFˆF÷R¬^½:;vì´¾ª¥K—–}ŽZ«Kß/yiŸÓÉKS„³:`í=–VZNaëÖ­ÙÕ€ÅWnܸ±ìàxUÁ2j¤“—ö9¼TÁjš€!©«YÛª8`Y 1ßë` XM,£F^ÒiŸÓÉKKÀjÀ€¥ï—4ÒI#/õ` X–‘¼¤Ó>§“—–€•ˆˆ¨KÀ°Œthä%ö9¼œ²€µcG_züñT,Ë\½¾餑—z°T°,,£F^ÒiŸÓÉKKÀÒƒ…ˆˆØp=X;wZÉ]À2Ò¡‘—tÚçtòRKÀÒƒ¥ïN^ÒÉKõ` X*XFd¼¤‘Nyé*BW Xˆˆˆz°T°,,£F:yiŸó’—–€¥KO/i¤“—4êÁ°,#yI'¼TÁš¡€µsçδvíÚÔÒÒ’æÏŸŸ®ºêªôüóÏ Xz°õ`Õ°ÚÛÛÓÀÀ@ɸk×®tÑE X*XFd¼¤‘Ny©‚5™S„óæÍ°ô`é)à%tÒÈËæèÁú÷ïK»v¥ºXMÀ:uêTÚ½{w6e(`©`‘ñ’F:yÉ˦¨`]sM_ºä’Tã¼]`9Ì™3'cÜÿÒK/ Xz°›£k¢k<¼úê«YÓûš5k,,#2^ÒH'¼lŽ ÖM7õ¥ R]¬¥+®(°ô`é)à%tò’—Mу5¬uëÖ¥dW¾òÊ+YÖdh°T°Œéä%¼¤±)z°ÊáŽ;îÈ–jˆ+Ï;ï¼ÔÕÕ•Nœ8!`éÁBDDlެÎξ´n]ª‹Vr°ŒtŒi¤“F^ª`Í@“»€¥KO/i¤“F^êÁ°¦ñfX¸paÙ5-J9ÖúÕóƒª>`»»Ÿã¢,«Ù–Q#/i¬Ž_þòw«ú€Íã…ö9/U°,K–¾:s©1®\»ö•1?\׬NwÞ¹ŸŽ¸KÀ°Œthäå4ó+_y,]zéke?XãömÛðÓñC£ –€%`!b­Ü¹ó;écûÅiª±½cÇ£üAÔƒ%` XF:¼¤q"üú×÷¿ÎƒÙO~:~hœ=,Ë4Xæêiä%ö9¼TÁ°T°Œéä%¼¤Q–€¥ ±©z°âËž7lHuQÀ°ŒtŒi¤“F^ª`©` Xz°ô=ð’NûœN^ X– –Q#¼¤“—46\«³³/­[—ꢀ%`!""êÁRÁ°T°ŒyI§}N'/,K–¾:yI'/iÔƒ%`©`‘ñ’F:iä¥,=X"""ª` X*XFtò’N^Ò¨KÀÒƒ¥§€—4ÒI#/›¨+Vr¿ñÆT,ËHǨ‘F:iä¥ VT°vîÜ™æÌ™“±€o}ë[éüóÏOçw^Úµk—€¥ QV-X·n]Z¼xqúà?˜mïÝ»w4p800 `©`5ÒÉK:yÉËüV°òöeÏ ,H}}}é™gžÉ¶×®]›…ªþþþ422’>õ©O¥«®ºJÀÒƒ¥ïN^ÒÉK^Z«ZD˜:uêTö{ª¹sçf· f·º½mÛ¶ÔÕÕ¥Kß¼´ÏyÉK«"vïÞ=z%añ² …\²yóæÆŸ"œHÀZµjUÝ>zôh–§“—®"Ìí2 ñ>±Àg,ÐUíªëͰŒyI#4òR+ßë`Å;ÇÒ …e¡â÷hOšñ€`ÑïtÞyç XØ´¼ûîþ׃©·÷¿Óަ{îÙËDÔƒ•ó€áªÒ2 ™•›pÀŠ©X´0E8Ñħ‚eDÖˆ7mú~jm=uÚÁå—¿œ.^ÒI#/U°ò°Ö¬Y“®½öÚÑe1½Ë4Ä}3°âK Mî_øÂÊ^æ¨KßÃl×ø/ÿò£1?6o~‚—tÒÈK=Xÿ‡Îξ´n]ª‹S°bJ0V)(ÅÉ“'Ç\K³á–iPÁ2"k$_ýêÃiåÊ‘1VT²ò6]è}éø¡“—*X¿ X?ûÙÏò°n¿ýöÔÈЃ…å-·ªêC!z²ø…ˆz°ò;EøüóÏÞÓ…3:EØèPÁ2jœ(7l8\Õ‡B4¾ó’Ny©‚õË•Üo¼1ÕÅ©XÑ;^©Éý¡‡°ô`é{˜ vu=WUÀÚºõq^ÒI#/õ`åx™†¨VÅ”`0~Ûf|™ËH§Y5~å+ûWÞyç>^ÒI#/U°r°r»Ð¨€…ÍÌõë_ó!–pà"êÁúÕᆠ©.NEÀª4=8ѵ<,,£Æ ²¯ï¾²!+®.üÌg~ÀK:iä¥ VŽ+X–,}9×Ó…Ñ“ÕÝýó,XíÞý/餑—z°pŠðé§ŸÎÖø|ê©§,,£F:yI'/iÌ+o –ÃÈÈHV½Z½zµ€¥ Ñ:X“9u‹ X*XFtò’N^Ò¨ËU„z°ôð’F:i䥬™Xû÷ïϦ£ïª¥¥%­]»6=òÈ#– –Q#¼¤“—4êÁªc­ä¾wï^K""¢¬ZÑÖÖ–V®\™80ÚÜßKÿëŠ+,,£F:yI'/iÔƒU+bJð…^8­¹½p%¡&w=Xúèä%¼¤±!z°b%÷›nJuq*Vô]•^=áj×®]iÁ‚– –Q#¼¤“—4ª`ÕŠÅ‹§'žxâ´€Uà?ÿó? Xz°õ`ÕŠ;wž¶ h«¨\mܸ1êaµkpp0«j Xz°›¢k¢«ÀJèïïO+W®Ôƒ¥‚eDÆK‹ùÍoö§¿û»çÓ[ßúrö??ô¡§¯}m??4΂ Öòå}éì³S]¯‚ÕÛÛ›i<~ü¸«‹óÅ .<í¶hXëîîή(¤ÖJÆÕòX=Xúx™?];v<˜þðO–¹¾å-¯¤/ù!~:~hl𬩚"Œ@î–i(Áœ9sFYŒ-[¶¤íÛ·noÛ¶-uuu•}ŽZ«‚eÔÈËüézÏ{^óÃõ]ïúYºçžûøéø¡QÖiØ¿.ªV¹ž", X«V­JCCC£Û1§ºlÙ²²[Ëcõ`!æ‹]]Uõãbóö`W¤©T°°J ™?þÓ€¥·ÕóX,£F^æKÓªUÏVõã§ã‡ÆÆ­`]pA_:ë¬T­ä>‰«\­”J«}l¥«:::²9ë›2~NtûÅ_œÔ盪íÒŸyÔ{äÈ‘\ë+l‡Nû»öíµkW°âqütüØß3sþ‰ód#,Ó `©`‘ñ’ÆY0EhŸóRKÀj¸€µzõêtìØ±Óúª–.]Zöoky¬,DMîˆ8ýëÒKûÒ9示(`MbÀÚºukv5`ñ•7n,ûøñ«‚eÔHg¾56ê2 ö9/U°T°b™†âîÿñÖ¶*XÖÁ²ö ¯±ÒB£·ß~???4ZKÀj¨`5ò’NûœN^š"°0`!""êÁRÁ°Œthä%ö9¼°¬|,}¼¤‘Ny©ëW°Ð¨€e¤C#/é´Ïéä¥ –€¥ Q–€¥‚eDÆËie_ß}iÛ¶é _8œn¹åPÚ±ãQ^:~è䥀%`éÁÒ÷ÀËz¹{÷#iݺŸ§K.Iéïÿ>¥ /LÙïÿð/¦¯}?/?tòrZÖòå}éì³S]°,#£Æ\1ÔG>ò¿é½ïMéMoúÕhðw7¥÷¿?eÁëž{öòÒñC'/U°,=XˆÕòã?šÞþöÊZú§)›2ä" X– –Q#U0*Sù—#ã~p]pÁÏyéø¡“—S°Z[ûҹ禺(` Xæêõ=äjz°xZ°ßùÎS¼tüÐÉK,+¿+®Ôêî~.}ìc¿H+Wޤ5k†Ó'?y$íÙó™Qã´sÇŽýU}p½ùͯñÒñC'/,+ŸëÎ;÷¥«®z)»:«”¶¾ò•Æ80qöðöÛïO¿ökãp½ímÃüBDW Xù Xë׿P6\xùå/§»ïî7"3jœVþÑ7`-\ø^:~èä¥ –€•¿€½.c…«{{ÿ[O¾‡iåõ×?9æ‡ÖoýÖkéË_~ˆ—Ž:y)` Xù X_úÒSU¬oü#2£ÆiçªUÏ–ýÀzãGÒUWý7/?tòRÀ°,ÄzØÕõXji9’ÞþöéïøEúÀ§[o}˜7ˆ(` X¦éä%¼¤QKÀÒäÞMîúxI§}N'/,Ë2 Ftò’N^ÒØ€¬K/íK眓ꢀÕÄ«°ÐhWWc-4Šˆˆ¨‚%`å:`é5ÒH'¼TÁ°¬) XúxI#4òR–)BËH‡F^ÒiŸÓÉK¬Ù°æÌ™S–}ìt,DDD=XVî144”.¾øâŠK˨‘N^ÒÉK^æ·‚uÁ}鬳R]°¦7nLwÝuWÃ,}¼¤‘Ny©K+×8|øpZ±bŘӉóæÍ˸dÉ’ÔÓÓ“†‡‡U°Œéä¥}ÎK^ XV%lذ!õ÷÷W=•«³³³l°*°Yâ/¼)ã§mÛ¶mÛ¶mû—Ûqž°fYÀŠÛÚÚZÓߌŒŒ¤––,£F:yiŸó’—z°¬r¸öÚkÓàà`Msâĉ´hÑ"=Xúèä¥}ÎK^š"°JqèС´víÚqÛÛÛÛÓÀÀ@V¹ŠpÕÝÝ6mÚ¤‚eÔH'/ís^òRÀ°JÁé©§ž7`íÙ³'µµµ¥¹sç¦Å‹§ÞÞÞ¿ŠÑ:X¿‚•Ü›*XF¼¤Ó>§“—*XV,}¼¤‘Ny©KÀ°Œthä%ö9¼œ²€µ|y_:ûìT, Q– –€¥‚eÔÈK:ís:y)` Xz°ô=ÐÉK:yI£,KˈŒ—4ÒI#/U°, ,K˨‘N^ÒÉKU°,=Xz xI#4ò²‰z°,Ó `éÐÈK:ís:y©‚%`éÁBDDÔƒ%`©`‘ñ’F:iäeSU°Z[ûҹ禺(` Xæêõ=ÐH'¼Ôƒ¥‚%`©`5ò’NûœN^ X–,DDĆëÁºà‚¾tÖY©. X–‘ŽQ#tÒÈK,,K–¾^ÒiŸÓÉKKÀRÁ2j¤“—tò’F,«¹ÖW¿úpºå–CiÆÃiãÆ¦mÛ˜¿GDD=Xz°¬zù¹ÏL—^úZºä’t;:Ž¥;î¸ßˆÌ¨‘F:iä¥ – –€U 7múþÁª˜?ÕS ïF:iä¥,KÀª–wßÝŸZ[O°‚yœ.4jä%ö9¼œŽ€ué¥}éœsR]°š4`Ep/\£'Ë\>""6c– –€U3ÿã?ž®*`ÝpÃOŒÈŒi¤“F^êÁ°¬j¸sçwª XŸùÌôè{ ‘NyÙ”=X¦s‚9sæ”e9ŒŒŒ¤îîî´~ýúl'?>m«¯ï¾ÔÞ~rÌpµråHÚ½û#2£F餑—*X*X3°ªÅ–-[ÒöíÛG··mÛ–ººº¦õ*Â;³Ñ½««1F>ˆˆˆ–€•aÕªUihhhtûèÑ£iÙ²eÓ¾V„¬Ë/ù´`¡+¦£ÊeDfÔH#4ò²Y+XÍQÀš7o^Æ%K–¤žžž4<<\ö±óçÏ?cʰô¶B°*°ÙœuáM?ëÝŽ uçßO÷Üs"Ý~û¡×ß7¡ç›êíÒŸyÓÛGŽɵ¾Âvè̳¾FÙßñóÅ_Ìýþvü8~šéø‰ó¤ Ö,lrêT¬ÎÎΪ«]cUÀ|¡Q#/é´Ïéä¥)BWþ_Uª¥¥eB¬é Xˆˆˆz°¬ÜãĉiÑ¢Eeï[½zu:vìØi=XK—.UÁ2j¤“—ö9/y™“€µ|y_:ûìT¬ID{{{ȪQ®b†M›6•ܺukvå`ñU„7nœÑ€eí^ÒH'¼´– Vî°gÏžÔÖÖ–æÎ›/^œz{{+öXÍô:XF:4ò’Ny©‚%`5äádB""âô¬ÖÖ¾t. X–‘ŽQ#tÒÈK,,«9{°îKûö}?ÝÿOÓàà˯ÿþã´wï ¾=$3ÎO|≴`ÁOÒûÞ÷röóúëŸä§ã‡F=X–€•ÿ‘Î}÷íO<ð‹´:ƒûöýÀ¨Ñ|Fxç{Ó¹çþ´ìë_ÿõPv???4ºŠPÀ°r[¹ª® ìïÿž~œv~à‡Ç½¶´á¢u°,+Ÿ#˜+\|ð³ fÔh>]¼õÖ‡Óß82îìç??ÀOÇz°,=Xù›«ž«ñVpïÞ|ÈôÌn—_þLU°ýèA~:~hÔƒ%`©`åo¤óÀǪ X5OW¬øAU°_ü??4ª` Xz°òÇ}û~TUÀºï¾ûõà´ñŸþé»U}ÀæýŠBD=X–€Õ¤¬˜úÛ¿dÌpuÿýCFFàÓ~á[Þòʘ®¿ÿûÃék_ÛÇOÇ*X–¬çrZÅzvŒ÷—Ó}÷=¨ïAÉ´³«ë±ô¿ñZÙÖ¸ýÓŸ>ÀOÇ Þƒué¥}éœsR]°¬†éô÷?]-XZ¹Êc¸2o==ßIïxÇ/N W±}óÍòÓñC£ –€%`5÷î}$kh×s…yâ¶mû³ŠVüä¢,KÀ2Ò1j¤‘Ny©‚eŠPÀjÆ,}¼ä¥}N'/U°,,#2^ò’N^Ò¨KÀ°õ`•â‚ úÒYg¥º(` XF:F4ÒI#/U°T°,=XúxI§}N'/,K˨‘N^ÒÉKU°,=Xˆˆˆz°ô`5]ÀZ»vmúèG?ŠˆˆˆeçI, r `’ÑÚÚ—Î=7ÕE   T°š±ã±2,+7¡N^ÒH'¼¤ó—X¾¼/}vª‹ãýO±ö×Â… ,‹N^ÒH'¼l.SUÁš3gÎ(,h*Lõ¡€–€01|ë[ßJ}}}u±šf{ `’!`X0ª¹4ôèÑ£éÚk¯MóçÏϸnݺtøðá\iÜqÇÓúA_‹Îâ«­¦óÊ«Z½|öÙgÓ•W^9ú™ÉËú4Îô¹GÀ‚ íÌjªåË—§Ûo¿= Á8a|øÃΕÆÏ}îsipp0ûý¥—^JŸýìgÓ—¾ô¥iÑØÞÞž2o"èmÞ¼9µ¶¶–}ì–-[ÒöíÛG··mÛ–ºººr¥±ßgRg­ÿStãsÑEånŸ{ì±tõÕWOë~¯EçL€jÑxäÈ‘tñŧäZg)†††2ÝyÒ8“çž™:ça¹kSsŒ4âÍ]ŒyóæåJci¥%ªBÓq«„Jþ¬Zµ*û +¡-[¶,Wó6¢ªå½6ÝïËFÓ9ÖëFU-* 'Ožœñý^Ig^FøciŒÓý÷ߟ{¥Ø¸qcºë®»r¥1çžf„€5á%FÜQ úÆ7¾‘^xá…tï½÷¦o¼1WçÎ{ÆçL *i1J+‡Òé‚Ðv¦tƈ}÷îÝÙ”až4Æ€dõêÕ騱c3¾ßÇÒºâ\²dIêééIÃÃùÒ¦Y1ýÇö5×\3-my_Æ´ÛŠ+r§1ç ¦4¼Ä{Íš5Ù;>ÔbNüg?ûY®4F_KL½Å ,BË#<2#«¿¿?­\¹r̾‡™1ãiÌKÀªEg-)…ÒôÝÅ4vž4^qÅYßÐLï÷ZöcT‚#`uvvæJc öЧÀ" ÄçSž½Ü°aCöø¼iÌùGÀ‚) /1²=tèÐèv™˜JÈ“Æ!ÆÁWhœŒr÷…^8­{{{Ç­Ît«yXµè¬õšI?_}õÕ¬$NyÒ8S ÏÝqüLç@ª°fRc­^ÆÅ,ÓÕ»X«Æ<œ{,˜ÒðRnÎ;o=X¥Ø¿vµÞt!šƒcÔ:Чa1r[ºti®4ÎtÀªEg=ÿÓLúYÀtpëÕ8Ýû½^'NœH‹-Ê•ÆhÂ.î³ |àÈ­—10-\ ”7y8÷X0éá¥ø¶¸46Jµ…ÑXaä‘'Åxâ‰'²«O¦kº(ÂÜxÕ¶nÝš]9X@üÕ¶]óàÕj,T‚ñ!\ÜO2+霩u°jÑ8žïyÑ9“ÓZµèŒu¥â=#ïóÎ;/k€Žp·}>S«{öìɦˆbnñâÅÙ 7o±lL´*ÄçQ„­˜ΣÎx_>õÔS3~ÞÉã¹GÀ @À°@À°,°, `¢¸ûïË»Ë!Vnûãq@•ˆ¯F‰¯øØ±cÇi·ÇwOÆí===L  VÄw¦E˜zà²í{ï½7ÛŽÛ,€:_è»fÍš4þü´{÷îìçe—]–†‡‡™@½x饗Ҳe˲ÊÕù矟Ž?ΰ&‚TX“„ÂaKKKÖìnŠ,€ âºë®Ë*W}}}Ùv¶·X5¢°LC,ËPŒ¨dÅíq?€€P% Æ‚¢å Zh, , @À @À°@À°ÿ¯ÑÁ~ÁÛ IEND®B`‚XYSplineRendererSample.png000066400000000000000000000407121463604235500346560ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/xy/doc-files‰PNG  IHDRSt¼ûA‘IDATxÚí{LUi–ö;™L&“Éd2ÿL&“ÉdÒÉdҙ̓I&“ÉdB ÄJuŒ; XR XˆÒP‹PÒUA-š@+jcsi)›Â£X|Ø´„ÒîöŠJY^KË+J•¢Pà AÖwÖ[ßæ;À9p€sÙ{Ÿß“<}œsžýî³÷³×Zïz¿'`Æø»3€™ÀL`¦Qw2øÞ÷ư/˜)B‚E‹¹Pþà?ׯ_>?88(ÿüÏÿ<æoþîïþnÌö¿üË¿Lx]}ï¿™7oÞès.\ÔÔTùþ÷¿/ög&ú§*ù—)111²zõj9räHÈ @$Áø÷¶ø'ò'òçþçò¯ÿú¯’››+=ÂL0S8÷ïß—¿ú«¿s±,,,}þÃ?óœþí©S§Œð~¼¶¶vôvíÚ5Á,ܸqÃ<÷³Ÿý̯©æÛIfj<ÿæoþF¾úê+Ì3€Sð«_ýjÌÅR£Ej~®^½j~÷~®¼¼ÜüÏOúÓ1ÿÓ?ýÓhDk|$K£-ŠÊÊÊ€ … @ Ú•±±±˜)f 'á?þã?Æ\0ÿçþǤݼÓ¿±ÐÓÓcRsÞÏ«Y•úÛ¿ý[yòä‰O“õ‹_üBúûûÍsšN<~ü¸‰„ýÃ?üCT˜) ª}óæÍ -f €™ÀA¸råÊ„ÔÝøTþ7~þóŸù›¿ÿû¿Ÿ`˜¬H–büëwuuÍèb®5EÆÌ©é˜3gމ¢b}\ÍbzzºüÅ_ü…avv¶ÏÏvéÒ%Sÿ¥Pk¿´öIkÈÖ®]+Ïž= Ø”hTÏû9}`¾W z>úè#S˦¯¯ï£)ß©ÌÔl>—êÖ÷üÇüGs|„ò5˜)B½Pù3Sùùùþ~hhÈ\xýýT{C£Tã Ùß{ï=ijjšpœìb9¾Þª3RÃ,3¥âñy×’)vìØ1©Õ‹ooï”ï­‘»œœœ1Ï©A æ{¢'>>~Ú)ØÙ~®ÿþïÿžðÚ¡xMf €°àÅ‹>Í‘^ˆõ9_سgß‹ÞøYyZ;5Yäë¿þë¿dë֭ƤMf¦ZZZLôA#-Þ« –™òw·ðÅ_ŒyNM¡~&ýüãŸÎ{XQ©ööö¿—·žýû÷O0$jN5zèoŸ…âs…â5˜)Šñµ;J-PŸ ÿùŸÿ9áæÏŸ?áï4ú¤—©.~Ñònàïâ¨EòÞÿÛ¿ý[ÐÌ”¶oÐ.ïÇ4ÝdaÉ’%cžÓº'ÅøtšÓéýû£GŽÙoÁx¯©ôèxy?×ÚÚ:å> ÆçÒ;ï¨d(^€™ â‘)©g]Ô|A£(ãÿÇj…à/š¥}§ôbîÏT,[¶lÊ‹ùduF³5S¾^ßûïÿú¯ÿ: sämXù{_-‚ñ^Sé?™À»×X(÷A¨´0SDš:ñwñÒYv“~ùfpAÓ ö™3gdÓ¦MLœIòÚÓ½ÀëñÉjz¦Sä>¾ŽL£rãSªÁx¯éê dlƒù¹BùšÌaÁLfóM×L©Q?ë΂¦õ¦kŒ4Zæý¸FWÂe¦ÆGrÆ×yºþð‡?Lx\g±y#Xï5Ùããû‰òZÁü\¡|Mf €°àßÿýßÇ\´ÇÔø>SZ >› šeÊRRRäw¿ûÝhêPë[´ç”¿žVþ^{¼ñ^²&ÔfêÇ?þ±ßð´‡–îé^Ë×,J­û Å{ù{||Ý›wº1û ”¯ ÀLrh/¨ñQ!­yRŽXø+FÔLJ]æïÿôbªíÆ›=m±.3¥éÉñ­´L#)š¾Ô™Œ:[.×Ò¿of4BcÕó½ü=>¾ŸTZZšyíÆÆÆ°ìƒP¾&3@H¡kóO­Lµ6Ÿ¯f›\ÐÆ¿¿t¢NƒŸ® [¼xq@Ÿ'X+êêê&˜Í™ÖöÜ»woBñµš«~*XïåïqjƒÌéö™ æ>åk0S„ ã5þà?S§¢©8}Ìûo-Z4#3¥¯¥Q5>Zp®³ï¬YxšÖÓÆ•·nÝšòµÕàiï+ i×õõë×üy‚i¦¦–~~Õ¡fP€Ö‡itG#-¾Öø¥xÆ×Oã½&û _ýµi‘`ÝǾjºB¹BùšÌÑûÅæb ˜)f 0SÌ`¦NƒÖÊxf 3€™˜)Ìf 3€™˜)ÌÔLñᇎÙ^½zµYE>TÌÈÈéëG‚nÔÄX¡ MhBºeVVfÊï¿ÿ¾üá;::Búú‘ 51VhBšÐ„®@ùÁ`¦Âi¦ÎŸ?ﺃԚ+4¡ MhBfʦf B!„î"fŠÈš+4¡ Mhâ|Ž™¢fŠ;ºÐ„&4¡ MÔL™Âñ£ MhBšÐDd 3!„Bj¦0S¸~îd+4¡ MhBfÊ.fJâãý’\4ºÐ„&4¡ M¡aaá×’˜øZDÞ~û•TW‰™ÂLq'ƒ.4¡ MhBS üè£{.»‹ËöíW0S˜)!„NEH¿ì.\8"Ë—`¦0SÜÉ  MhBšÐ4F|^z-z™ÂL‘cGšÐ„&4¡i*&'¿œpÙ]¹ò™¤¥õc¦0SÜÉ  MhBšÐ4kj.™(”·‘Z¶ì%5Sbbb|rªç0SBat°¥å¸,^i{]˜)ÕLyóbm­I÷oi!ÇŽ.4¡ Mhr•&M便õKyùuGèr¤™Š‰‰ñIÅÈȈlÙ²EÖ­[gŒ’²¯¯ÏU‘)‹:³ïÏççN]hBšÐä&M%%’—÷È1ºk¦ü¡®®NêëëG·÷ìÙ#eee®ª™²¨=§†–,‘ö}ûÈ­C!tu©˜Å‹‡¥±ñ´c>³ëÌTZZšôôôŒnwwwKbb¢+#SJ­›zš‘Ñ¥fˆL¡ MhBš‚ÅÌÌÇAKÂLÅÆÆÆÇÇKee¥ šçæÎ;æo5í7þ17ÔLy³{͹^^NݺЄ&4¡ÉÑš**®3¥5SNÒåøtB©™*,,ôµòõ˜UO5ÞL­]»ÖìtËÅêÏ`nß¼y3è¯ïÊyõ“ŸÈ©ƒƒþyÙîêê ëû…kÛ¢[ô„êø‹ô¶?'¾O|Ÿ¢íûÔÖvIÒÒ†ä³Ï®:îøsÅl>>ÅÅÅ9"2*ÞܼYåå‘o‡BèH~ðÁ}Ù¼ù¦C?» ÌT¿ÌŸ?ßü¾råJéííS3•àÚš)o>ÎÌŒÈR3ÔL¡ MhBšfC]2fÕª'AOïQ35 ²³³¥½½ÝDÔHi+„êêjóÜ®]»Ì >ïÙ|›6mru͔ų ò|Å ³†uèBšÐ„&'h²–ŒÑ&NÕåH3ÕÔÔ$YYY2gÎY°`ÔÔÔŒIëEKŸ)_¼[TdÈݺЄ&4¡É šJKïÈúõŽÖEt—ÔLYÔ¨Ô@jª‰R‘ƒ‡Bhgj/©ÔÔii9îðz/Ì”«"SÊ uuò,=]Ž>ÌݺЄ&4¡É¶šrr¾•?¾ìx]˜)ÕLy³Ë£££¸˜ºt¡ MhB“-5UU]•5kº]¡ 3åÂÈ”òÄ¡Cò*)),é>"ShBšÐ„¦éPÓz))/¶d ‘)j¦fLí=Õ—•Ñ¥f „‰æÃ¹=¥0SQ™²¨½§®UTpw†.4¡ Mh²…¦P÷”"2EÍTÐùÅîÝ2¸t©IûQ7€.4¡ MhФ¦pô”¢fŠÈTHxoùïhîÎÐ…&4¡ M‘ÔŽžRD¦¨™ µ÷Ô‹”Ó2\=„ÂHÐ-=¥0SQ™Rª‘êOK I1:‘)4¡ MhBÓT WO)"SÔL…” äzy9uÔC  MhBSX5…³§5SD¦BJ-B×tß©ƒ¹;ã®MhBšÂ¢©©©-¬=¥ˆLQ3r^­ª’ÞìlzOA! ³³{eãÆÛ®Öˆ™Š¢È”Ňùù¦¡'wgÜu¢ MhBS(5••Ý’ÜÜž°ö”"2EÍTØf÷ ¤¦m©j¦Ð„&4¡ M¾fï-]:Ñô5SD¦BÊ+۷˳ôtc¬¸;ã®MhBš‚©I#QOeÛ¶¯¢b¬0SQV35>ݧ =ÉéC! &7l¸'yy"žÞ£fŠÈTÈy¼¥Å¤ûfÛ̓ȚЄ&4¡ÉâÞ½ç$)é•™Å-c…™ŠÂš)o~^_//““±¢n€z4¡ MhšÍÿ·¶3kïUWUcåx3Õâ1111£Ûú»/™òÏ»EEòg?pwÆ]'šÐ„&4ÍæÿóóJAÁƒ¨+G›)Ý9ùùùÌ5SÓ£öœÒfž—vì ×!„pF¬­½hfï:t"ê´;ÖLuvvJVV– 8ÊLÙõNæbm­ .]*'››¹;ã®MhBš¦Åææ“’œüRvïþ"*ÇÊ‘fª¯¯OV®\)½½½ ”þk/•••288èÓDYôÆÚµkMnÕÚñú3˜Û]]]!}ýÙlw””ÈÓ¼¼iÿÿãÇm©gÖûcÜO7è³óñ7Óm7:N|Ÿø>9éû¤9‹‹»£öøs¤™ÊÉɑ۷oOêéé1fª°°ÈT€é>í=ucëV"SÜu¢ MhBS@,/¿.iiý¦ø‘©iðzy¹<ÎÌ x1d"ShBšÐšÞ¿KJKïDýX¹ÎLeggK{{»‰H©‘Ú²e‹TWWÓgjšìËÊ2¦Š>ShBšÐ„&_¬ªº*«V=‘ÇFýX¹ÎL555™Y~sæÌ‘ HMM ³ùf@Mói»„6Ïþ$2…&4¡ MhòæÁƒ§$%å…8p†±úЩ™š„W«ª¤7;;àt„B÷S×ÛÓÙ{ר˜)"SPCž*ÝGd MhBš¢GSII‡Ç<Üg¬0SÔLJmâ©‹!Ÿnl¤f MhBš¢\ÓÎçÍÚ{vnƒ@Í‘)[òËêjù6'‡ÈšÐ„&4E±¦––ã¦NʉmˆLQ3e >ÊË›v3O!„îáš5ÝRVv‹}™"25Sž8tȤû|5ó$2…&4¡ MîÖ¤]Îóò™âsÆ 3EÍÔ,Ó}ÚŠš)4¡ MhŠMõõŸKjê€i‡ÀXa¦ˆLÝkÖÈWÛ¶™BšÐ„¦(Ф 9µà|ïÞsŒfŠš©`QÓ|šî ¤™'„Bg³ àw°/0SD¦B5»ÏjæId MhBšÜ§iûö+’ÝëØ:)"SÔL9bvŸÕÌ“š)4¡ Mhr—&]&&-­_O3V˜)"S¡¢ÄÇû%whBšÐä,îØqI22žÊ[o½65RZp^[{‘±ÂLQ3…™‚Bˆ‘ÒfœºÞÞâÅÃ’0" ¾–ÊÊ«ìÌ‘)Ìc…&4¡ MSQ#Rùù'œÎ“’†+Ì5S˜)Æ MhBš¦â’%C&"åë”ÎXa¦ˆLa¦+4¡ Mh 2µhÑë §ó„a¬0SÔLa¦ „R3•˜8ÑLi û3Ed 3ÅX¡ MhBÓlm=&K–¼25RVDJ”>ÎXE‰™jii‘˜˜˜Ñí‘‘Ù²e‹¬[·Î%e__5SaÐôy}½¼LN–“ÍÍäØÑ„&4¡É!TãTVv˵ãDÍÔP§™ŸŸ?ÆLÕÕÕI½ç¢naÏž=žƒ¤ŒÈT˜4i#ÏÇ™™£Ýѹ“AšÐ„&ûrëÖ²jÕ“Ñ.ç¬hefª³³S²²²d```Œ™JKK“žžžÑíîînILL¤f*ŒìòìÃŽâbö„Ú˜ž2]Îõ'û# Í”¦íV®\)½½½fÛÛLÍ;wÌßjÚoücD¦B«éXk«Y ù‹Ý»¹“AšÐ„&›R×ÝÛ¼ù¦ëljȔäääÈíÛ·G·½Í”÷ï“=fÕS7Sk×®5¹UkÇëÏ`nwuu…ôõ#±ýøñã ÏßÚ¿_—.•˧N9VßøŸn¯h9þœ¾­ãä&=|Ÿì·]^~]Þ}÷¹Ü¼y×õß§pŽ4SjŽ|‘È”½GI‰ôfg;¶~Š;i4¡ MnÔ¤i½¥K¥©©-*ƉÈÔ4Ì•ïôŸU3• ó<©™ ;ÕD=YµJnnÞÌþ€B›P—Ž)*ºË¾ÀLù7S»ví23ø¼gómÚ´‰ÈT„4i›M÷mhàNMhBš"L­‘ÒÙ{‡šq"253EŸ)ûõï¸ZU%ýii¦0¾$hBšÐ8pƬÃW_ÿyT}¦è€îÇÿg?ß÷lÜÉ  MhBSäfïi_©h'"S¬Íç=|Ø4óÔ¦žì! /uö^fæc¿é=ˆ™"2åÇoÕOÛ»—;4¡ Mh szÏ×ì="S˜)Gš©h¯ÐFžj¨N76R&4¡ M!æ¡C'L—óŠŠkQ;NÔL™r¥ã¿VQa Ò·´p׉&4¡ M!¢®·§©½’’ލ'"SÔL¹–w‹ŠÝÐBí_Çsßô”²1†˜)"S.tüÝkÖ˜Y~Üu¢ MhBSp©Ñ¨ŒŒ§ÒÚz,êljÈ5S®ÎEë ¿§¶íÎX¡ Mhr¢&­ÒåbtÙƉš)"SQàø­~kk¹ëDšÐ„¦YRrª‘Ú»÷ãDdŠš©hâù;åUR’´ïÛÇþ€Â²±ñ´$%½’íÛ¯°?¨™"2ŽÿËêjy™œl"UÜu¢ MhBÓôØÒr\ÒÓŸMÙáœÈfŠš)—ç¢;JJL •ÖRQ&4¡ MS—Š)*ºË8Q3Ed ÇÿÝ ¿GyyÜu¢ MhBÓ4Z ¬YÓ”D¦0SÔL¹€Ö~ÅÅì!œ‚¥¥wdÅŠç&ÍÇþ fŠÈŽ”§”))rµªŠ»N4¡ MhòÃêê/%9ùe@kî™"2EÍTæ¢Ï64ÈÐ’%f-?ê!Є&4¡ib ]¼xçÎóŒ5SD¦pü“Ïð‹dËÆ MhB“58pÆ´@¨ªºÊ8™¢f NÍ[·š– §Ù¨§v5OIy1ë3%û÷ï—ÌÌL‰‹‹“¹sçJ^^žtvvšçbbb|’È”s5iË„þ´49qèwÒhBš¢V“™k±y0Z ™ÂLIvv¶´··ËÈȈaCCƒ$%%š)j¦ÜW7  "kªc­­Ôx  MhŠ:M‡•U«žHAÁƒ ´@ f 3å±±±Ž0SÜÍ®U¯ÇHÿñÈî¤Ñ„&4E&5O¹¹=†¡4RD¦¢ØL ˧Ÿ~jÒ~–™Rc¥Œ—ÊÊJ¤fÊÔTj¦º‘•+_È¡C'ø>Ùøø «™RãóÎ;ï˜7†††LAúêÕ«}>¯5UZ¨NÍ”{4ébÈÚÔóNi)c…&4¡ÉµšÊÊn™™{:ƒq¢fjZ$®†jΜ9R]]=iúmºðg˜úûûeþüùÔL¹LS[S“ .]*_mÛÆX¡ Mhr&í!¥½¤´§ãDÍÔ„(Qss³$$$S•˜˜(gÏžöëäææÊ… Ìë½|ùÒ¤ÕŸé§FjË–-ƸQ3å>j3Oí’~iÇö„Ð5¬­½hŒ”v9gP35iÑx}}½üèG?šVO( ---Æ4i‘ù¼y󤬬Ì'ESS“dee™èׂ ¤¦¦†Ù|.¾“¹PWg ÕµŠ Æ MhB“ã5iDJ—‰QCÅ8™š²ÎiÏž=ÆÍÄL±6µÞÔuüRSåë Îòc¬Ð„&4…SÓúõfáâ½{Ï1NÔLMiË+ͧ)9ÖæÃñ£(]Û&|›“´ÆžŒšÐ„¦phÒ–ÚC*+«Oš›O2ND¦&ÇÒ¥KG зoßn"T¬ÍƒÉûžZ—ž9sàûBh{êL½ŒŒ§žkO—´¶cŸP3™ÖD¦¸ãÏ[eeA)Lg¬Ð„&4…RS]ÝS¥kí…º³9‘)E¦m· 5SîÔ¤FJ['ÜØº•±BšÐd;M×ÌŒ½íÛ¯0NÔL9D¦Ü«I{QiÊO—ŸÑ¥h+4¡ M‘Ö¤¨>¸/©©²o_;ãDd 3íO-FïÉ͕Ǚ™¦H}!Œ[ZŽ›5ö´Ð\—‡aŸP3…™Âõ;JÓíåer²œÛ»—±BšÐvM…Z±â¹‰JÙyÁb"S˜)j¦È±OÊ‹µµ¦0=Ð%h+4¡ MÁдyóMSh®uRŒ5S˜)\¿ã5iªïQ^žôeeMÙ>±BšÐ4Mº®ÞªUOL©p.VÌ8™¢f †…—?þØtM¿SZÔ®éB¨Eæ%%¦›ùÖ­7lÓöb¦ˆLq'’ât]‚æùŠ>k©+4¡ MÓÕ¤µQiiýžkË7¦àœq"2…™"šÔHi 5Vï—ŒšÐ„&eeåw‹ë©!!aD ¾ë^®ÅåZd©EŠ'j¦ˆLáø#JMõu””¸ÞLqü¡ M³ãŽ—dÑ¢×No½5,……_Ûz¦‘)Ì5S0,t»™‚ÎŽ+Vôûͧ ?u‰]óodáBÓ^AÍ•ö­RƒÅ:€:ƒºF^]Ý)+»å9ÿw™ÙxyÒ(”öŠÚ»÷Ü„>Qjªôyý;mƒà#£ÌLíß¿_2=±¸¸8™;w®äååIgg§ynddD¶lÙ"ëÖ­3FIÙ××Gd M!Óuôða“ú»±u«IêÂʯ’’ÌZ€Úm][.\«¨ö}ûÌßRÆñMšÔx¬\ùÌh/_>1ã¡Ñ&M×UU]• îINη²té ‰*i—rí ¥ 6wïþ´9àØC—ëÍTvv¶´··ã¤lhh$ÏÅKQWW'õõõ£»gÏÏG5Sh »®Óreûv¹¹y³tyŽ+^Mfl¼©Ñ®))òdÕ*cÎHGq±1l_VWËù;Íëoi‰z3ÅwÊÞF*)éÕ˜¢mý½ºzjC¥ /u9–;Ï{þþK9Z¿¾SòóJVVŸ‰ù+÷GMÙåå=2'5u§gÕœc]®KóÅÆÆšŸiiiÒÓÓ3úxww·çŽ(‘Èš¥Kû^µ55™ˆ×¥;LdKSˆåz˜ŸoÖÔåptáf½JèO5k½ž ËxiRÿÈš"Åôôçf–ÛøCO –š™òòëÆØhThÍšnÉÌ|l š$©aRã¤J£Iº`°+5Xj´ 1NœÏ‰LMááaùôÓOMÚO¡i?ohäjüc–‰²èµk×kíxýÉ6Ûvß¾ÔÖ&gä–ç»Ð³oŸ<øå/MÊñÛuë&5Sì?¶C¹½xñk¿‡ŸFˆŠŠz<æè±ÔÔtÊÇ_–¦¦ÒÞþµœ9s‘ýǶ£¶m¦bbb Õ=yòdô1_Gd MѪk23u«¬ÌñÅò|§ìGiÔÉW +2Å8¡‰È”044d ÒW¯^=­È5ShŠ]“™)M ¾^´ÈÔeé,DM/2Nhš)uM:8ia·ÎŒ+*º7ÁPiÚï—¿¼Î8¡‰š);Bgö)V®\)½½½cj¦ˆL¡‰È”Ÿš©‡™"yퟥ3uÂS2Nh ¸µ€‡k/&8Ý•¦¦¶Ñçkj.›†–z~ë­!S÷Ä8¡‰È” 빋¾pႉ:½|ùÒÔL© RìÚµËÌàóžÍ·iÓ&úLAÈ gçN­^¼ØÌ@Ôvìè‹:NYj±xnn™¹é‚pé35 ´´´˜ö:ƒoÞ¼y¦õA?}¦¸“AW4idêÞ† ÆTé Á‹µµ¶Mò /µ‰¥Î®SUPð@öíkgœÐDdŠèÔL¡‰±òGíc¥EêÚóJ[/h«…H4eœìÑ3JÛhËmSÐÜ|’qBº0SD¦ÐÄXM§ï•)5Tº.¡ö° ¤a(ãälMš¶Ó®àÚûI‹ÊµÞI›i2NhBfŠš)gAõ§kj P~Öæ¢ìwQ£NÅÅÆ@ét×Ìr,ì1SD¦ÐÄXQ“vg×nìÚZA;³ë¬@ÆÉÙštfž¦ð´Avv¯Yx6Ë­0Nh"2…™"MŽMðÌfæŸFªt›p§ÿ§ÙS£N7Þ6õPºp E匚Ѕ™"2…&Æ*ˆÔH•¦ÿ´W•.ƮٌÓÌ©Q'Má%'¿4é<]óŽqBçsÌ5SF˜êêL¡úÓŒ ³H3ûÄžÔnåééÏLq¹.¬t„ÔLa¦ˆL¡‰± ¯lßnZ*hŸªÏëë'›hª¯ÿÜ´8ÐnåÚ¹<\…匚ˆLa¦ÈG“cGÓ ¨=©t©m§ð绊™ŒS`ÔŽåV³M]òe¦-'4¡ 3Ed MŒUÞ÷Xk«Ü-*’¡%Kä«mÛ§0kÒº(«cùÁƒ§ø>¡‰ó9fŠš)J]¦¦/+ˤþèOzªqÒuó´6êÀ3ì1SD¦¸“A—[4é5¯’’LWõÙÎúcœ|sûö+¦.jýúN[4ÜdœÐDd 3E>š;šBПêYzº<ÊË3i@Æ)8šÔ8½ÿ~—¤¤¼0ëéq졉ó95SD¦püèr±&Ji÷ôþ´49ÛÐÀ8ÍR“¦òV­zb Íu]=Ž=4¡‰È5SFQoªÔT“þ W³O·Q$Öh”¦÷ØR3Ed Ç®(Ô¤©>MùM·8=ÚÇÉ*2ÏË{d(æØCšˆLQ3E.]Q®I‹ÒuIš«UUŒÓÔ.æÒ¨”Ý;˜ó}B5S˜)\?w2h #O67›  ¦,NÆqÒ"s¥§õQ g9öЄ&"SÓGCCƒdyN´qqqòÆoȺu뤻»Û<ã“ÔLAè<Þ)-5Åé¡\ŽÆiÔNæÚ7jÆ{¶+2‡š)™©ììlioo—‘‘–O>ùDÞyçQ3EdŠ;t¹G“)«8=ÚÇI;™ëÂÄšÞãØCšˆL±±±Ž0SäØÑ…¦éóxK‹<ÌÏ—osrL 0ÚÆI×ÑÓv99ßںȜ™r°™:wVYfJ•2>>^*++eppÈšÐåMZœ®Q*Ï—Û/¬¯¬ì–YCO¥,ZôZJJîH}ýç’–Ö/7Þ¶}‘9ß'4™r¨™:vì˜,_¾|´fÊ===ÆLú4Q½±víZã`­¯?Ùf›mûlÝÞ>©™rª¾ÊÊ«’0QRJʰüö·×¶Ù¶ñ¶£ÍTMM‰$õõõùý­«ÒBu"ShB—{4¹12•œüÒ§¤·ß~ű‡&4™ tÇhúTèïï—ùóçS3…&t¹H“ÍTbâkŸ’F8öЄ&j¦‚¶¶6¿Ñ(ï™~j¤¶lÙ"ÕÕÕD¦Ð„."S¶¦ÖEÅÇŒ‘£uS©©/8öЄ&"SÁÇd½¤šššLª9sæÈ‚ L*µù t'3S77ovÜú~åå×eéÒAyë­!Y¾¼ßHY¾|À<ÆZ{ÒgŠèÜÉ  Ma5S÷='5môy±¶Öö:öík—ÌÌÇRPðÀ¬±·cÇ%ÉÈx*‰‰#&Rå&#Å÷ MD¦0Sä£É±£ÉAšÎíÝ+O32Loª‡Ùî3k‹ƒââc˜êê.pì¡ MÔLa¦¸“áNMöÔÔQ\,ƒK—Ê•íÛmóywîÉ8¡ MD¦0SÔLAݼVQ!ËË7žsÃd¦JMTaáײdÉlÝz# –Bj¦0S¸~îdКN76š&ŸCK–ÈÒRµ²žÓ™yÚ/JMÔš5ÝÒØxšqBšˆLa¦¨™Bc…&3þzrsÍ’4±úuåÓî +«Ï›3NhBº0SD¦Ð%ºtŠþòå’˜8âùÙ”"éh§Ö²ÿ#‰9òÍ¢,ù¬ôÇ^€”øx¿äØC‘)Ì„Žauõ—²hÑëÑë˜þž”ôÊU†*TÔ5ôrr¾5Ñ¨ŠŠkr¥¢ÊÌúÓ.êŸ×׳¢ÜLAˆ™"2…¦(ѵlÙà„k™*T1N¾©uPÚ+Jë¢JJ:ÆôŒúã‘#¦…‚ÖSi Pë«8öˆLqÞ#2…™¢f M.ÔÕÔÔ&;wž—„„‘É®i~™š:`¦û—–Þ‘?¾l^Ïíã¤KÀ<0fóÿ‘ƒOùýÛ‡ɽ ÌÎêòœ7‚©²ë±§šµ'×­²2£ùùŠ2£ƒËÃËlÖJä&j¦0S¸~îd"N]¦ÅÐUUWME{-]:h˜™ùX_O¸–-\8bÌÒd¯©).}M][.;»×¤5R£¯©-´I¥¾ïl:}Ûaœ´ÅÁæÍ7MçrÝ':SO›ŽÁPsñ"%Ť;JJäds³£=í ¶¡A®VUÃømNŽ)Â×hÜ“U«LÛÎ}±{÷˜™ŽÓ‰Li?¯WIIæ5gfšNô×ËËåÜÞ½“¾&ç4™ <_ò¬¬,‰‹‹“7ÞxCÖ­['ÝÝÝæ¹‘‘Ù²e‹yL’²¯¯š)è:8pƘ½6é:pš–*.î0µPÞ•_ýêÚ„èÔ[o ͨfJ›RÖÖ^”²²[žïJ—1 jÌôçÜb«ye(iE¡Ô j®®î¬{E]Ú±Cº×¬1}ªc iA;ï_Ѧ׋c¨MLÕ^پݤ3§«%4ŸÏ‹µµ¢]jØœ¹‚ÔLÙÚLe{îhÚÛÛq–O>ùDÞyçó\ç¤P__?ú·{öìñœøËˆL¡É5ºÔh£Å‹‡ÐÒTÿSUõ¥©RS¥KžTW_ zdLWê¿Ó(–¾š5|Z¼­ŸSÿ6˜332žzŒákcæ1‡³BÊã--&r£kþ½LN–Û7ÓÉú¢`E›ÂQ3¥ë$ª±Y¸Pî{.Rv5UœÏÑåº4_ll¬ù™––&===£kÄ*11‘š)49^—F|4Ŧ‘” îÍ(úNMZÄ­GÍ‹wK£hº­«1œIšPÔÛoz âKsm^¹ò™ùÝÛPY5cšŽTÓ©ï¯4XQ¨@©æD#<ÞÑm:¾f(ØfJ_[#Jw‹ŠÌìÃ`E›Âù}RS¥ÍSõ³Û1RÅù]®2SçÎ3Ñ*ÅܹsÇ<§Ñ«ñY&Ê¢7Ö®]kvºåbõg0·oÞ¼Ò×ÄvWW—«ôXÛ#ùyΜ¹è1ßz ÈKSUUõ\½zϱÇ_{û9vì¾ÔÔtš´àªUâ¹NJzúKc°TßïÿÀèžìõÒÓ_LH]jmØÛo{^w@’“‡Ìþz÷ÝçRZúLjkï™È˜¾n¤ôkdè«–énhG3£5CCo¿-¯=|þš©É^ÿ®gL¯ÿö·&ªôíºuòjõjNJ2Q§Çž›Ã¾_ÿZnzL“F›œú}ºúÙg£‘ªg#zõ÷¿·Åù‚ó9çskÛñfêØ±c²|ùòÑš©˜˜˜ ãë1j¦ ©Q'­RC ©,M“3Ef·úúúÏMI Vzú³™N3k|͘ÝiÕ ÍX´‡ýii¦Fë«mÛ̬©RŠN¥µÌFª4ý7©!$2å555Æüx˜™¢f MvÔ¥&@kŒtj¾Î  v:Ê-c¥&Ó—¯Ðº0z2¹ÿûä]S¥iK­ã܇&j¦fÝ1Z„>+W®”ÞÞÞ15S ÔL¡ÉÖºÔDi «¦G#5Œ•nÝzÝgTª²ò*f*оO™Òš0-¢×‚zMɹMÔLˆ¶¶6¿ívíÚefðyÏæÛ´i‘)4ÙR—¦ó4e™¨††³ŒU€Ô4¨µLŽî?-hw²"S³›=©­´oÕÓŒ 3c‘sšˆLM­òEúLA§ÐŠD©Ð6¡ŠDAMÔ™‰Z7¦½ª´ŽLwJPHÍй“áNfš5QV£È@zD1VhBÓô©HuíDMjŸ¯Ùv¥gœÐ…™bm>4EX—öRÒ†–V$*Ôé<Ž?4¡é;êìFk Îx ¦©bœÐ…™"2…¦0èÒ™xZËcu”‰âøCS´k²Ú*èR?ÅŦΊqâ|Ž™r¨™‚ÑCíX®]·µÅ5QÚƒ©ÒŽðZ¬®Eëv_;R3…™âN&*ué&ÚxR›mê²'ŒšÐd?MÚFA×NÔ%v¾¬®ž‘©bœÐ…™¢f MAÖuàÀÉÊê3 %5µ®µß8þЄ¦™Ïþ»VQa–ÞÑ– çwîdœ8Ÿc¦ˆLq' ]j¢t!]¡§Eæ‡`¬Ð„&iÒuuñiù§3Û÷ícœÐ…™¢f †«W”Õpóÿ1 8Ù/:—º¦á½ ÌÌ¿o<׆¶¦&ö ÄL™âN&ºÂµô c…&4E†Þkÿé‚ÊþLã„.Ì5ShòÚšK’š: ‰‰#’–Ö/Û·_1‡{éÆ Mhм©²zTY‘*ÖPDfŠÈš¦ .”»páÈèùñ­·†åí·_Inn+–~áøCšf©b Eta¦¨™‚S0)éÕ„sdBˆ1Uá^úBh/jãO·›)ˆ™"2Å̬©ÆÉ×yR£RŒšÐ„&"SèÂLQ3…&?Ô¨“•'$L>^*++eppš)jòeœ´y¦®›§ÅäšÎÓT0 c…&4¡)ÒšŽ>l"V×ËËÍBËÏÒÓÍR6á0XÔLa¦& §§Ç˜©ÂÂBŸ&Ê¢7Ö®]kvºåbõg0·oÞ¼Ò×ÄvWW׬_ïÔ©ËfÆ]EÅ7RTô\V®|aŒSZÚKÙ°á¹y\Ó™3æϢ›Æ‹ãÏÛ:NnÒÃ÷iöÛj°®ÿîwòíž=òíG¬WÒ繎ÝÞ¸Qn}ú©Ü»z•ïSŽ?W›)ÅÈȈÄÅÅQ3îØqIÒÓŸÓ£}™ÊË¿ (Ú¤³ìªª®šâðœœoÍÿjË-×ÔÖ­7d÷î/Bq‚B7Њ`é,A­»zœ™)Ë“¥fK£ZZƒ5U‹õ‰LMi¦úûûeþüùÔL…ÀH© Z¸pìZvÅÅwGÿF;Œk4©¼üº) ×NãVš./ï‘””t˜†˜§mY,NšÐ„&'jòe²&«ÃŠ3EÍÔ4ÍTvv¶´··›ˆ”©-[¶Huu55SA¦Ö/%'¿œðÝSs¥Åàj´”™™=Ù}3ÛN[8)ÚDšÐ„&·hš¬+Ì5S´Fðn‘ÐÔÔ$YYY2gÎY°`ÔÔÔ0›/E቉¯ý~ÿ4Ú4æ˜Üu¢ MhBSä ÖdfJ;¶;qA"S¬Íg;ãäk6]RÒ¼ýö« ß=}ž}!„Îádfª7;ÛôÀÒåp4]¨iCnÛ»7lí¨™ÂL9æNæÐ¡f6¶xÿý®1õM¾Úø«™*)¹Ã]'šÐ„&4¹ÄLYs²¹Y.ÖÖšE›µîêùŠ£iBí¥³ /ÔÕ™esˆLa¦\_3ÌÙtÖl>5T¥ d69v4¡ Mhrž™ ålBj¦0S¶¾“ÑeVÔišN‹ÀC5›ŽŽ¹hBšÐ„¦™Î&$2…™² 5Šd§Â¯M}“mÒ¶NœM!„Ð]=±"ÑÕš)"S×7éútééÏFë›öî=gRzÜ¡ MhBšì¨ÉŸÁHM•Gyyr§´4h³ ‰L¹ÄLýú×WLmRBÂw3Þ6lè k}uèBšÐ„&»kRƒÕ¾oŸ\­ª’Îõëg5›0œý³0Sa0SZ¨­jüx––Þ h6Ý»…™BšÐ„&4…’3™Mˆ™r™™Z±¢ßçx.Z4ÂÚtBa ݽgb¦\f¦´ži²náv]›ŽÈwhBšÐäT“…™r™™ò·ŽÝ²eƒäØÑ…&4¡ Mh²Qÿ,Ì”k¦–-{)Ë—÷.¬Ñªêê+ÜÉ  MhBšÐ„™ÂLj¨¬µí–/0é=rÞB!f 3Å ‘)4¡ MhBSTëÂLEÙÚ|hb¬Ð„&4¡‰ó9fŠÈw2èBšÐ„&4™bm>!„º˜)"Shb¬Ð„&4¡‰ó9fŠš)rìèBšÐ„&4Q3Ed Ç.4¡ MhB‘)Ì„B©™Šb3•••e „Ba TïÕf œÀL`¦0S!ÖPA!„N‡˜©0›54¡ MhBšÐ½º0S¨|ùЄ&4¡ MèÂL`¦0S˜)€™ L«ù¸¸8yã7dݺuÒÝÝíhMû÷ï—ÌÌL£iîܹ’——'®·––‰‰‰q¼Õà‹NÇíÛ·å½÷Þ3ÇŸ’q²ôª¡§ßHŽ?G¤¥¥3oA#Œ‰‰‰®9ïa¦\½«ÑÈŽ[ Ñ¶O?ýÔ¤ýœ=1®\¹Rz{{]cDTƒ^À”ñññRYY)ƒƒƒŽÖ¤'v5TšjÑô‘®Úî¤4ÄTÐTØO~òÇëЈµFmš››åáÇräÈ)..v´¦9sæL0ˆjÝd¦Æ§ÍU¯ÓRé˜)—ãØ±c²|ùrÇ×Ly”JÍ©kÈÛéÈÉÉ1µ8vúÒz·©fª°°Ðñ4ïÔ¹^°µnÏ-øùÏnÎN‡¨Õ«W¥F^ë§ž>}êhMzœi L;=þΞ=ë:3å/E†™ÂLÙ555®»ƒV ™Z=iºÅº­X{ü]¦ÓOþj¦Ü¦É‚È:½¦Ò‚Fyoݺ5º­&DSèN†ž¿ÕZŠ4»dÉW™)"S˜)ÛB šõNÚÍpËÅÌÉwc ¿¿_æÏŸïh ZÄì]Ó¡øáèŠñÑ µUàìtøªr[½h[[›™ùæ¦óœw©ƒB3) ˜)ÌTä¿ln‹FégI¥w,/_¾45SuÃLÙÞ3/ÕH鉿ººÚÑštv‘NO·¾Wj>œ>E]¡Q7ÔzŸ'4ÕgE7¬è¼[pùòe3cÑéeãÏs»ví2ß1ïï›Fà0S˜©ˆªÛRGÚƒI/Òz—9oÞ˜)˜CCC²zõj™;w®|úé§ægFF† ²s˜)Ož<‘ÄÄD‘Z´h‘ôõõ±S˜)jž0SÌÌVš/..΢“æ`¦`øÙÏ~f"R¿ÿýïÍö±cÇ̶>˜)˜Vkm…à Péãú<`¦À¬¦ÚœÓ´™'M;˜)Ìf 30S˜)Ìf `¦0S˜)Ì È_Ìï}B¿`¦˜)86ÀL¸`Ž 0S.˜€c€™€ &àØ`¦à‚ 8686ÀL¸`Ž 0S€ˆ]0ããýs <}úTJKKå7Þ¸¸8Y½zµìÚµkÚŸ/&&&¨z;::äÍ7ßœÑÿ¶·‹ä押õ–ÈO*rêT`ÿg·}ÑÐÐ YYYæ³ègZ·ntwwc¦ÀLìd¦Þ{ï=©««“ááa‘K—.™Ç" 5#gb¤’“¿3R*?1QdÙ²À •ÝöEvv¶GO»ù,ú™>ùäyçw0S`¦v2S±±±æbí¿ùÍo¤°°P6lØ ~ø¡ôõõžY´hÑèöTÿsâÄ ÏGŠ7‘}>S5}"²páÄÝ‘!ŽÞÞŸ3f 0[35™yšŠã™™)Û·o— .L0¥Ù±cÇèöÑ£G¥¸¸xÔ0œ?~‚ñ™ìæÍ›'æ}öïß3Ä]aë}¡8wVa¦ÀLfk¦¦ã(¦€FJôÿÃþÐD=òòòäþýû湄„éïïý[½ð[uL㎵=ÙÿüøÇ?–Ó§O¬y&‘©•+}ï†åËÅÑûâØ±c Ë©™3°›™ò†FJ´.gùÿsÞµKãë˜üˆÉþçòåË’››+?úѤ¹¹9$fª­MD³mÞ»@·Çšyÿý÷GS„˜)0S›š) ZÇcEOüÕù3“ý…‡ÊܹsCb¦Ç‰JHø®ý³Ïf¾ß#½/4}¨Eè3>6˜)@h/˜:[ͪRjFAZ<½gÏ3“L¡í ¼ë„|ˆÉþG뇴P[‹Àè»Ý‚ÓöE[[›ßhf ÌÀ&fª±±Ñ^kFa´—‘÷\ …Öþèó&ý5™˜ì>úè#ó±9yòä¤&ÊWj,Ô°Û¾˜,Mˆ™3°‰™Ì\0Ç3à‚ 86ÀL¸`Ž f .˜€c€™À…Lý€™p þ/`†’w6C%IEND®B`‚XYStepAreaRendererSample.png000066400000000000000000000222351463604235500351300ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/xy/doc-files‰PNG  IHDRXr5˜$dIDATxÚíÝqˆwÝÇñ‚ˆˆˆøˆˆˆ """‚ˆˆᎠ)¡µ%¡‘æI¨m‰ ï(!!½´%©}ZlIlll¬ !Ä'1¡Ø*µ4¶F{œ m•bš§ Wz¤’Úó~O¿ƒsÏÞÜîÞîÞ^2“¼>ðæ²·w›ùÎÜì¼÷;ß½*‰ˆˆˆHWs•U """B°DDDD–ˆˆˆÁ‚%"ÒòßUWMCD„`Ierà 7L;ˆ}ñ‹_Lÿþ÷¿§î?þ|ú¾0íg>õ©OM»ý¥/}iÆãÆãÔþÌâÅ‹§î;vìXºùæ›Óç>÷¹ô¡}(}ðƒLýèGSOOOHÏ>ûl©×Ù_þò—ÿø^UD%çø@úð‡?œ¾üå/§µkצüãën“Ø>ö±¥þþþ´iÓ¦466vÅ<}ç;ß!ÓB°äòÊ©S§²'õÚ'·xrÏs÷ÝwO»/~ö÷¿ÿ}vp®ýþ/~ñ‹©ßÙ³gÏŒù_ÿú×ì¾;ï¼³á¿Ùl™¶ÅuR\gU¬"ŸøÄ'Ò믿N°J²MâEǾ}û.ëçŸÈ-[¶èV Á’Ë3?ýéOg¼’!Š®Lü»ö¾‡~8ûüàÓ¾ÿùÏ~ªóUìxEw$²cÇŽ–öe>Ø»sAtª.XAoo/Á*Ù6¹\%«ý_„`Ieóõ¯}ÚÜ·¿ýíì”]í÷âgòœ9s&{…]{T±{õÉO~2={¶®x=øàƒéܹsÙ}q*òw¿û]ÖúÌg>SÚƒmíéÁèÌÕvòÊzš°Ñº‹uþãÿx†\¬KW[tãtzí}ÿøÇ³ý`‰,©`^{íµ§ýЧùâgjsß}÷Mû™OúÓ3$*ïxEŠßêŒI+Oį¼òJ6×r3]1[³aÃÃÃéŸÿügÓNj٣իWg‚±`Á‚†²T{z0fÈâÿhvš°øE—ïÞ{ïMŸýìg³õÑÉòßzë­™ÇòÆÏÆ2Ǻyº?þñm‰J,Oí}ñÿ3—ubðýï?}ä#ɪ»^cÄúŒÇÿ'Öålݹ,W7¶C«Ù‰<~ë[ßjz ºÕå,þ?!ÕñXùºŽÎóÏþó©¿…ýèGÙ>÷ÅÏ„€óë_ÿ:“Àüï/¾Æíø>Á‚%R'ñäÜè oÆ 3~þÂ… Ù“p£ß)ž6‹nVqX~ݺuéÉ'Ÿœqðjç‰øñÇo*‡qàyûí·>^qh?ŸGª×5ˆšòŸ ¡©}“@½Ó„ÅÇ-8»±üEb}¶r0Îâí·ß>í¾ËÚÌuÙB6f›W[²dIÛ§çº\ó±ê=f§‚²R|ìNj¯w_½ß‰K_ûÚ×êÞé<±¿6ÛFq¿n¥,‘Bþõ¯Õ¦8HÆ}õ²wïÞ†O¶ÅwÆ,V³Ù7¿ùÍ´mÛ¶LÜZ}þÓŸþ4ã >^‰Çã4zâ/>Öádz߉NKí÷C8kS|÷`ˆBœælönÂÙ¤¡“å‘ ‰ŠŸ‹ŽDq½~å+_ikj»W###]]· ÏþýûgˆDˆmt7çs›wc;´;GØŽPÄv­×Ylw9Û™õšm{¥/ö—zûÍÓO?M°„`‰SœÉ b¾Y¾ñoÌøk®¹fÆÏE—*:$³=¡‡@/Ðè øÆoœq $R<õâ8ÛcÅ`í÷¿úÕ¯N[†â»õ«_e¢Ó¬;Sü¿bF­¶[×ÉòSüÙ8mÓ®`Åã?ÿüóÓ~¯ë6.ɳu–/þNjï{æ™g.Ê6ïÆv˜í1ç*õÖY»ËYü:TWl›Ý—Ïå/¥Û6£.ÉB°„`‰4é`ÅœFþD^/Ñõ(þN~Y†F]¯xŽƒF£þÊ•+[zŽàV¢ö Þè±f›Gª==˜ÏÅ©˜f§Eg;pt²üñÿƬOð¢»ËÙìÿiåñë]ž¡[붸^k—¯øF‰Úk°]Œm>ŸÙÍV¬§N–³Õ¿õVîk´­Š?Ÿ/+Á‚%òŸ4›¯ˆîM·Ÿ ã‰9†²ã:8E±‹èV¿Ù,J£ßiUFj¦õ..ÚˆÚÓ„³­—v—?ÖWñºe³žª7x^œ‡ 1,žîæºmuû]Šm>_9—ý%N³Õ›kw9Ûù»˜í¾â ¢Vö‚%K®øtò.ÂvŸ ã ÞèÝyqJ°Ó\: õæ·Z]ÖF]ƒH½‹‹6¢ö4álë¥Ýå/^N#.Ü—ºh÷`ùÜsÏÍø~¼+m.ËÖ‰`¯³v±·ù|>æ\~·80³~,g7KK–H)¾ƒ(®U¼V ¡Ïå 2µ›nº){…žŸvŒ¹•â°xí5·šu:®¿þú†W”ϵ̶¬Eé¨%)žœm†¬ÕõÒîò7ê"tr ­÷®Ñ˜#šuÛèûÅù½ÚS•~§›Ë5ŸÙ‰PDýÅe‰nc>ÛÕîrvS°Š3Xq©ˆzÝ]3XB°Dþ“¸VU±{3TA±ÃÐhà½Ý¡ÝÙˆAÛÚ/¥ðâ‹/fßSfÅË+ÄŒW¼²WÖñNÆü­é–#HqÉ‚¢Pæ—;ˆƒ^ñôeí¬Pü»øê>ïÔͶ^Ú]þâ N,[qˆ¼Õi<~Qp¢Ž|~®ëv¶ï¯w×øŠÇŽëF¿ÓÍåšÏÇlG(šïrít9»)XÅÓ–ñîÁH§ï"ìôSD–T"ñY„E9˜í³ë] ´•'ÅâÿÓèTd¼å¼˜Fóa‘Ý»wÏÁvfgêñÝï~wêçï¹çži÷ŵ¯Š)~hv¾[Y/í,³K]tr ýÛßþ6CÚâÏcÍuÝÎöýèÌÄÅ2Û­©[Ë5ß9¹ˆíR¼¦Y»ËÙMÁj¶vr,‚%K.ë/òŸ³W;×§ñŠŸ½WO0ZyRŒÇŠWÛ!/1Ôž¿û-¾Æ)Á¸èåo¼Q÷wc™B.òáìü*ÔyÞ|óÍìþxœx¼µ8ÅéºèŠÔ^á¼Þ¼/3®ˆBU›¸®T£ÓhyŠ×˯EÕêÁ¢ÕåõWCϰ±>Š…v¤‘âÇç±æ²n[Y†¿ÿýïÙåòšb[Ô›ëtÍÇv˜ÁŠ¿¿ª8ÅWToöñ8­.g·+ïdÅöª½’{Üîæ•Ü –,‘*îPžÀED„`‰,!X"KDD–È•”˜U©EDD–ˆˆˆˆ,‚%"""B°DDDD„`‰ˆˆˆ,‚%""""KDDD„`‰ˆˆˆ¬‹—»ï¾{ÚítóÍ7Ï+«W¯ž÷ÿC jPƒåWƒÔPί<ÁºãŽ;ÒsÏ=7¯¼ùæ›óþ¨A j°üjPƒÊYÃ]wÝE°æƒ—_~¹ò€jPƒl5¨A «T‚®\ÃWƒÔ`¨A j Xf°œgWƒ,¿Ô 3X:X _ j°üjPƒt°ÁÒÁRƒÔ`ùÕ 5è`™ÁrŽZ j°üjPƒÌ`•H°&&&Ò¶mÛÒÂ… Ó¢E‹ÒöíÛu°Ô 5X~5¨A :XsÉ#<’FGG³Ÿ={6=üðÃi×®]f°€¬N]«ÚŒ§åË—ë`©A j°üjPƒt°:Í‚ Òäää´ïõõõ™ÁRƒÔ`ùÕ 5˜Áê4ëׯOûöíËf±B´^z饺‚b•S›ááálåæ_»}{lll^ÿbÜ>~üx¥—?¾æTuùãvl‡*/|]²$ác¶?;¾]Üã[%+N nܸ1“ªtß²eKºñÆÍ`%…à\züf°ÚÎÑ£GÓC=dK j Xh€}A j0ƒÕV^}õÕtÓM7¥Ó§O›ÁRƒ`_PƒÌ`µ”8= ¥'N¸–Ô@° ƒ¥5è`ù,BÀ Ì`f°ÃWƒ––}A j Xf°Ô ‚3XjPƒ,,¯RÔ ƒ,û³t°‚e ÁÒÁRƒ––}A j Xf°œgWÁ‚,û³Ì`,†¯5,,û‚t°‚e ÁÒÁò*E  :XjPƒ–,ç¨Õ` f°ìÏj0ƒE°¾Ô@°t°ì jPÁ2ƒ,˜ÁÌ`é`y•¢,è`ÙŸÕ ƒE°œ£Vƒ–,û‚Ô@°t°Ô ‚û‚Ô ƒe 0ƒ3X€,‚ÅðÕ ‚¥ƒe_Pƒ–,5¨`™Á²/¨A KË«5,è`©A :Xf°,3X€,‚ÅðÕ ‚¥ƒe_Pƒ®\Á:}útÚ¸qcêïïÏX»vm:uê”,5¨`Á –Ô`«ÓÜtÓMéСCirr2cÿþýiÅŠ:XjPÁ‚–Ô ƒÕiúúú2±ªMoo¯,€`Á `«Ó8p íÚµ+=õÔSé­·ÞJÏ>ûlºÿþûëŠUNm†‡‡³ö`n°ñµÛ·ÇÆÆæõñ/ÆíãÇWzùãkNU—?nÇv¨òòÇW‚sé±?ÛŸß.îñ­’‚R500IÕ’÷Ÿ9bëwÞ1ƒ¥5è`Á –Ô`«ÓÜvÛmé7Þ˜º½oß¾488hK j X0ƒ¥5˜Áê4õæ­Ì` f°3XsH\–!NFbØ}çΙ4é`©A  :XjPƒV‡Ϥ)ÞMÄ,–,5¨`Á –Ô`˕ܾt° ƒeVƒÁ@°Ì` X:XjPÁ‚–Ô ƒuù–sÔjPÁ2ƒe_Pƒ,‚ÅðÕ ‚¥ƒe_Pƒ–,€`Á `K‹á«A :Xög5è`,ç¨Õ ‚e˾ 5,,5¨`AK jÐÁ2ƒ˜Á‚,À ÁbøjPÁÒÁ²/¨A Ë –Ô@°Ì`ÙÔ 3X:X^¥¨A :Xög5è`,Ë ‚ÅðÕ ‚¥ƒe_Pƒ–,çÙÕ@°`K j0ƒ¥ƒÅðÕ ‚¥ƒe_PƒÁ@°Ì` X:XjPÁ‚–Ô ƒeË9j5˜Á‚,û³Ì`,†¯5,,û‚Ô@°Ì` f°3Xm¦§§§.:XjPÁ‚–Ô ƒÕ¥œ9s&­\¹Ò –Ô@°`K j0ƒÕ­lÙ²%=óÌ3:XjPÁ‚–Ô ƒÕœ:u*}ï{ßk(V9µÎì5_ÁñµÛ·=¡—gîd>¶¯Ûö‡ª –¿G·Ý¾8·+/X÷Ýw_:räHé†Ü=¡{ÕîÕ¢ýÁ¾`_PƒV%+,qÕªU¥|¡'ts'æìöû‚Ì`UR°6nܘFGG ¼j'X°/¨A :XÝÈo¼‘Ö¬YSÚë`yBwíØì €ë`UN°†††ÒŸÿüg‚¯Ú ì jPƒÖ•r%wOèæNÌ;Øì ö5˜Á"X(^µ{µh°/ØÔ ‚E°`î„`Á¾˜Á"Xðª] ö5è`,s'æìöû‚Ô@°¼j'X°/¨A :X æNÌ`Á¾˜Á"X(^µ{µh°/ØÔ ‚E°Ì˜ Xöû‚Ô`‹`Á«v,ØtNÔ ƒE°PÌÀþ`_@°P¼j÷J‹`Ùì jPÁ"X0wB°`_PƒÌ`,xÕîÕ¢ýÁ¾`_PƒÁr@1wûƒ}Á"Xðª`Á¾ 5è`,˜;1ƒû‚Ù5˜Á"X(^µ{µh°/ØÔ ‚E°`î„`Á¾˜Á"Xðª] ö5è`,s'æìöû‚Ô@°–Wí^i,û‚}A jÐÁjž'N¤uëÖ¥þþþ ‚s' öÀ Ö266–V®\™Ž;¦ƒ¯Ú ì jPƒV7²uëÖô /8Es' ö5¨Á V·²lÙ²L².\˜ a¯+V9µÎVnn°ñµÛ·=¡(ÇŸ·ç»‹u;§ªË·c;TyùãkœEªòòÇ틱?TR°,XFFFÒäädš˜˜H»víJëׯ×Á3X€¬¹VmB´úúú˜ÁRƒÌ`uš+V¤3gÎLûÞÕW_M°À –Ô`«ÓìÝ»7mÛ¶mjîjtt4mß¾`€–Ô ƒ5—<òÈ#Ù{œ ÙºpáÁ3X€,Wr,5è`,‚À –Ù5¨`,ÐÁRƒt°˜ÁÌ`,‚@KçD j X €,s3jPÁ"X ƒ¥5è`,OèÌ`f°Á ƒ¥s¢5,‚f°Ô 3X t°tNÔ ƒE°3XÁ,5¨A‹`€,³?j0ƒE°,5¨`,f°˜Á"X ƒ¥s¢,‚E°˜Á2û£5,‚@K×A j X Ì`f°è`霨A«"‚ÕÓÓS‚f°Ô 3Xs,,ÐÁRƒt°˜ÁÌ`•[°z{{3–¼ÿ̱cÇŽtþüy‚:XjPƒV7ræÌ™L°6mÚTW¬rj3<<œÍWp|íömO蔓±±±y;þç·/‹wNNN¦¾¾>,PŠŽîe!XçÎK×\s Á¥˜I¬¤` ¥‘‘‘¬srõÐC¥G}”`¬Nóä“O¦ÁÁÁ´`Á‚tÝu×¥;wz!(Íuá.ª`}útZ¶lÁ:X¦¿¿Æ)Ãâ÷0ƒÕFzzzZú^>ŸU¬ÁÁÁ´qãÆ4<<œÉV|uÛm·ÝvÛm·ÝîÖíp˾ƒ%"""Ræ”B°n»í¶ôöÛoO›ÁZºt©­#"""«ÓìÙ³'{ç`žø÷–-[l!X¦Ýë`S;›0TN°ªvW¬Ô ˯5¨áÊ­`ùTƒ–Ô 5,‘r‡`‰ˆˆˆ,‚%"""B°®Ôħq/Z´¨éÏÌõ’e¨!>†¨—:È>Š ¯¯/-\¸0[ÇqÑÙ*m‡vj(ëvØ¿Z³fMVC|¢ÂúõëÓÉ“'+µÚ©¡¬Û¡6‡nºLe^j¥†²n‡v–«¬Û¡ʼ?œ8q"­[·.Û§›}ÚKY¶ÁªóGÕ,»wïNûö훺Eݺuk¥j(ÛÁ#ÏÐÐPÉvމ‰‰ôÄO¤U«VUj;´SC¶CÒ¸|ùòÊn‡Ùj(ëvÈóòË/§ 64]Î2?/µZCY·C;ËUÖíÐN eÝccciåÊ•éØ±c•Ù«Í?®[o½59sfêvt'–-[V©Ê~@©Moooe·Ãl5ضÃl‰®[tDß}÷ݦËYæíÐj —ƒ`•u;\‚‚ô /Tj;¬6ÿ¸ªðÁÔ­Vl‚%K–¤;v¤óçÏ—n[ŒŽŽfˆªn‡Ùj¨Âvˆ.ÜÁƒ³ÓmUݳÕPÖí§4j?§µÙ~]ÖíÐN eÝí,WY·C;5”u;„ …dÅèE¬Ó;ái¿²l‚Õ…îOÙŒ¿å ËhÓ¦M¥ªáÈ‘#é–[ni:¿Töí0[ eßùéæ˜_8{öl%·C+5”u;Ü~ûíÙÌI+뵬ۡªð¼4ÛrUáy©u[¦í°`Á‚iã»víÊf+˼V›árè`5Ä0pY²sçΦ¯Nª°Z©¡ìÛ!ráÂ…l`|`` ²ûÃl5”u;´3l\æÎI§ÓeÜf[®ªtÖÛY·eÙ!XUÛ«M9©mwG¢;±téÒJ Ö¹sçÒ5×\SŠeaØx•2[ʼZ­¡ÌÛ¡˜FOdUØf«¡*Û¡Ù~]•íÐÎsSY·C³åªÊvhgÝ–e;¬X±bÚ\Uäꫯ.õv X-<Ô~oÏž=Ù;òÄ¿·lÙR©jßa;O¼õÑG½äË}ôèѦŸ*l‡vj(ëvX»vmöNX®÷Þ{/›_Šn\•¶C;5”u;̶_Wíyi¶ʺf[®*l‡vj(ëvˆu¹mÛ¶©çטoݾ}{©·ÁªÙ8ÚØµÿ.óõfZ­áÉ'ŸÌÞÕ-×ë®».;UÖå¯Úvh§†²n‡¸^Q<ÉÆëâÅ‹³ÁÒx¢­Òvh§†²n‡vä¤ ×Á𭆲n‡Ù–« Û¡ʼ?<òÈ#Ù{t£C¶âô™·Á!X""""KDDD„`‰ˆˆˆÁ!X""""KDDDD–ˆˆˆÁ!X""""B°DäŠÊo~ó›ìÃjã£5ê%>F'îŸ!X""-&>Ã,>«ì—¿üå´ïÇ‡ÂÆ÷wìØa%‰Ái7ñ¯!S/¾øbvûÙgŸÍnÇ÷ED–ˆH¹páBHýýýéàÁƒÙ×Õ«W§óçÏ[9"B°DD:ÍÙ³gÓ²e˲ÎÕ 7ÜÆÇÇ­!X""sIÁ‚%"Ò¥ä§ûúú²aw§E„`‰ˆÌ1wÞygÖ¹úío›Ý>räHv;¾/"B°DDÚL~™†¸,Cm¢“ßûED–ˆH‹É/4­—¸© ŠÁ!X""""KDDDD–ˆˆˆÁ!X""""B°DDDD–ˆˆˆÁ‚%"""B°DDDD–ˆ\´øª«p#"KD.‘`‰m+"KD„Ŷ!X"â ,¶­ˆ,a±mE„`‰ˆƒ°Ø¶"KD„Ŷ‚%r%„ï¾ûî†ÔæwÞI<ð@Z¸paêëëKiÏž=mÿÿ===]­çÍ7ßL‹-jùç_ýõ´mÛÏÒ=÷Ü›~x{zíµ×füLÙj=pà@Ì–%–ióæÍéôéÓK„`‰H™+-Y2ƒ¢`­[·.íÞ½;MLL¤ÉÉÉôÊ+¯dß»” Éi%££¯§»îÚšV®üß¬Ì N¦Ü:C²ÊVëÐÐPÉ–%–é‰'žH«V­"X"KDª.X½½½Ù¾QvíÚ•6mÚ”~øÃf¿;>>>%Aï¾ûnºá†¦nÏö;/¼ðÂû‹°$ëØÄý­ˆV+Ù¼ùgSr•sï½'ÓO~ò“ÊÔZ»ŒK„`‰HÅkÍš5é±ÇKÇŽ›!Ñíyüñǧn?ÿüóéþûŒ—_~y† 5ûÅ‹§“'OfÿÏþýû»&XwÞy_½RÓ=÷ÜS™Z#£££YW‹`‰,©¸`EÇ%¤àꫯκ'ëׯO§NÊî[ºti:wîÜÔφ,äsQEùÉo7û믿>ýáh¹¦VëŽ;fv°V¯žÙÁ*s­GŽI·Ür‹,‚%"—ƒ`Õ&:.1ú\$êÑL:šýΫ¯¾šÖ®]›®½öÚôÔSOuM°þç^_²þë¿þëdúïÿÞZwнŒµîܹóýå¿cêô"Á!X"RbÁjå]„õsC‘èÂ4šYj$Í~'Ï[o½•úûû»&X‘§Ÿ~=mÚô³÷ë»'=ðÀOšÊU™jS1èÞζ‚%"—H°ZM¼‹.ŸI b®(º)‘àÞ»woö·H\:¡v.©žt4û˜WŠañø^ €wS°ªXëÑ£Gv­–Á‘ Ö¡C‡²áï˜IŠnN\‹©ö ³FqÿêÕ«³Skͤ£ÙïÜ{ï½ÙÿŸ_|±©XÕ;í6×”­Öf§ –Á‘ –ض"B°DÄAض‚%"ÂbÛŠ,qÛVD–ˆ8Û¶"B°Dd^¸|‚%""""ÿÉÿÌÓh(C5IEND®B`‚XYStepRendererSample.png000066400000000000000000000242031463604235500343340ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/xy/doc-files‰PNG  IHDRXr5˜(JIDATxÚí݈\w¹Çñ‚ˆˆˆøˆˆˆ RÄ?D‘%dÙZ[’›x’Û4m~°q—4diwkÙji©’¤¡µ!6·±1„HBB±*µ·±m´KLhUÄmƆ­ Ù¤651ë~oŸsﬓٙٙÝÙÍ9›×>lfL¾ïyæÌ÷3Ïyöì ‰ˆˆˆˆZª<DDDD‘€EDDD$`‘€ED³ìÅ醮2‘€E4Z¼xñUð7Þ˜þõ¯}ýòåËé _øÂUßó©O}êªÛ_üâÇÝoÜOù÷,X°`ìkÇOwÞygúÜç>—>ô¡¥~ðƒé£ýhjkkKé…^È}P)ùø@úð‡?œ¾ô¥/¥ 6¤¿ýíoÖ5¬I<Ÿ>ö±¥ŽŽŽÔßߟ†††®›cù[ßú–0MQ^túôélC*aŽ©¤xયÅ÷¾úê«Y°(ÿüO~ò“±ŸÙ½{÷¸òç?ÿ9ûÚý÷ß_3¬ÔÛ®uPhdÍáO|âéOú“€•“šDpß»wï¬>†#DnÚ´I·’,¢¼éG?úѸ.@¢?þñÙ¿Ë¿öøãg?óï|çªÏþóŸë|Uv¼¢³Ú¶m[ÃA¥¨+ò‘dîîî®ú8=ôÐCÙ\[Üü?qªw¢Mw*ë îø??ûÙÏfµÎûœLxüÆ7¾QóÔw3ë¬ü"TÇ}•ëèÞþøÇ?{.<úè£Ùq_‹ï‰^©ŸÿüçYŒç\i1nÇç,"‹f™bc©õb}ï½÷Žûþ+W®dH­Ÿ‰ÁïrE7«rX~ãÆéÙgŸ·ñ6³‰<ýôÓuÃalšçΫy•Cû¥YªòŽG­Í+ºsëׯ¿êkÐÊ5ÕõEبü\eXX¸paÓ§^§º®Ê3]÷9Ù€a¥ò¾'Ã^íkÕ~&Þp|å+_©úµèî–Ïùz5НëV X4‹ôü£j`Š >¾VM{ö쩹QTþ6`ÌbÕë}ýë_O[·nÍ‚[£›Çï~÷»q›Stâ~jmZ•÷uèСìg¢KTþùœ„¼ÊîÕÀÀ@K×W+”´oß¾qA"Âat§óq«ôtÜçTEtšªu›]g3³^Õ«2ôÅs®Úsï¿ø…€E$`ÑlRå­üsqZ©¤x¬Ë¿öüóÏÏÈãóvåÇé¸Ï©ŠjY³ë¬ü§òœ‹ÓlÕfãš]g£Ï—F¾Vù¦¢‘瞀EQ5™ß"löÅ=Håoç•§ëm0voªÍo5ºÖZZ?óâ‹/Žû|üVZ¹Z¹¾ZŸ¯¼VÙL?nÓyŸSùÙÊù¸,ÈdÖÙÊ€¥ƒE$`Ñu¦Êß~Šk`U^+†Ð§òâ^ jwÜqGÖ](vŒ™›Øüj]s«^—æ¶Ûn«yEù’âZ[Á2ÑZ+SùL­Ÿ©ö›—1G4ë«õùʸòS•µ~¦•ëšÎûœL þʵD·±4ÛÕì:[°*g°âR¡xãa‹HÀ¢Y¦¸VUe÷(f¨Â•Ý‘ZïÍOä.Wå¥^yå•ìóqбòò 1ã]‰è Äo2–~­¾Ö:b3Ë-Tʸ|ÄD|ñTœè>”fÐZ±¾‰>_y½«»îº+»ï¸®õ3­\×tÞg3¢žã7E'»ÎV¬ÊÓ–ñÛƒ¡Éþádÿ2‘€E4ÍŠ¿EXyÚb¢¿EXí¡¼ Wþ?µNEƯËWªÖ|Xh×®]ã‚`3s?Õüío»a¾¿üå/ã§cƒ.ÍcMu}}>:3q±Ìf7ÚV­kºïs*á"êR”'³ÎV¬zÏåÉ\KÀ"‹(§ª¼@å7ÞxÕLJœÆ‹Ï•ÏâÅ‹'°â¾¢Sá%†Úã·ôJ¿­§ã‚o¾ùfÕŸ5Åu´Jƒå¥+h—tòäÉìëq?qÔb¹¯èèD×¢ÖZ#DƵ¾â>ãjôßûÞ÷šæ«üA•óXSY_#køë_ÿš]®¡‚§ÚŒX¥Z±®™¸ÏFÃEÔ0BUœb‹+ª×ûó8®³Õ«ÔÉŠz•_É=n·òJî XD4³¬Í‡ˆHÀ""‹ˆHÀ""‹ˆˆ,¢ëI1gSn""°ˆˆˆˆ,""""°ˆˆˆˆ,""""‹ˆˆˆˆ,""""‹ˆˆˆHÀ"""""‹ˆˆˆHÀú·xà«nwvv¦;ï¼sZ½víÚiÿ?0`À`ý0`È'CWW×õ°î»ï¾ôâ‹/N«Ož<9íÿ ¬ ùdøîw¿+`M‡;Vø'  Ô V®333_¿°$| 0¨ ,3XγcÀ`ý0`0ƒ¥ƒ%ácÀ`ý0`ÐÁ°˜™™™,, 0X? t°Ì`9GƒõcÀ€Á VŽÖÈÈHÚºukš7o^š?~zâ‰'t°0`À`ý0`ÐÁšŠž|òÉtôèÑìß.\H?þxÚ¹s§,fff6ƒ5YEת\ÃÃÃiéÒ¥:X0`°~ 0è`MVsæÌI£££W}®½½Ý  9tÿ[éî»GÒúõçÒ£ž,¤·o?_ص‡ûú†ROÏ¥´eË c3XµÕÓÓ“öîÝ›ÍbEÐzíµ×ª¬V%—«··7{pK 6>¶úöÐÐдÞÿLÜ,ôúãcÉE]ÜŽ:yý+Wþ3-\˜8îê:ïxv<Ûßfh+dÀŠS‚}}}Y¨ŠA÷M›6¥Ûo¿Ý s«ØÜW¯~·Ð] ";‚UÔ :Yž“Ìf°Ö‘#GÒc=f †:6øØÜ㣨レ‚¬7Þx#ÝqÇéÌ™3f°0`°¹«Á,XŽg f°f@qz0ÜÝÝNœ8á:X0ØÜÕ@ ,‹ùúðlÙÜÕ€™,, lîj ƒ…ƒ–¿Eè<;ó?j Žg f°, ,5P ,3XÌ6w5ðx0›ÁÒÁò.ƒî‰¨ãƒ–€å5 f°Ô@0`°$| lîj 0Xf°˜mî¬Ìf°t°$| º':Xj t°,ç¨1`°¹«:`À `é`aÀ`sW, t°Ì`1›ÿa5`6ƒ%`Iø0è`©:`À `™Á€Áæ®f°0`0ƒ¥ƒå] Ý5PÇ3,‹™Íÿ¨3 X:X0ØÜÕ@0`°Ì`9ÏŽÁü¨ãƒ,KÂÇ€AK Ôƒ–€ÅÌ6w5`fKË» 6w5ÐÁ€A«µ:sæLêëëK™7lØNŸ>m ›»˜Á€Á ÖduÇw¤ƒ¦ÑÑÑÌûöíKË–-ÓÁ€Áæ®:X0è`MVíííY°*×ܹsÍ`1ÛÜY ˜Í`MVû÷ïO;wîLÏ=÷\zûí·Ó /¼y䑪ÁªärõööfíÁR‚­¾=444­÷?· ½þøXrQ×·£E^ÿÖ­CÙæ‹ú|*úñ¼}ûù±€åxv<Ûßff+dÀŠPÕÙÙ™…ª…ï¿jÄ<Ö;ï¼c Ý50ƒ…ƒ¬ÉjÍš5éÍ7ß»½wïÞÔÕÕe ›»˜Á€Á ÖdUmÞÊ ³ÍÕ€Ù Ö—eˆÓ„¡vß±cGšt°0`°¹« :X“Ôððpšâ· Ã1‹e ›»˜Á€Á –+¹KøtOÔ@ Ït°,f6ÿ£Ì,`é`aÀ`sW, t°fOÀrŽ3Xj Ì` X> 6w5P ,3XÌ6wVf3X:X>Ý5PÇ3,Ë9j lîj 0X:X0ØÜÕ@ ,3XÌæX ˜Í` X> :Xj 0Xf°0`°¹«, Ì`é`y—‚A÷D ÔÀñŒAKÀbfó?jÀÌ–„ƒÍ] ÔË –óìlîj`ËñŒÁ ––„ƒ–¨,‹™mîjÀÌ– 6w5ÐÁ€AË –sÔÌÿ¨8ž1˜Á°$| t°Ô@0`°Ì`1ÛÜY ˜Í`5©¶¶¶ªÖÁ€Áæ®:X0è`µHgÏžMË—/7ƒ…ƒÍ] Ì`aÀ`«UÚ´iSzþùçs°~úÓciëÖ¡ìŬÈ.:C_ßPºçžwÓ–-ƒÞiÙÜÕ@ ¬ftúôé´bÅŠšÁªärõööféµôÇÇVÞŽ`/fœGÈšÎz»ÝØñ=׿ööíçÇ–ÇÃm·gæváÖÃ?œ>œ«!w¬|¸«ëÿ6•èdy§¥{¢:X0è`5¨H‰«V­r, æNÔA  0˜Áj•úúúÒÑ£G] ƒwíê Ž t°Z¡7ß|3­[·Îu°ØµÔÕ€Ùu°Z¥îîîô‡?üÁ•Ü1x×®jàXÀ€AËß"tŽÚ¦¢j ^W1˜Á°$|›Š:è`©:`À `™Ábs'êÀjÀlKË»›Š:¨x]Å ƒ%`9GmSQ3Xj 0X:XÞµ«ƒ:¨c,3XlSQVf3X–„oSQ,5P ,ç¨Í¨ƒ:¨:`À `é`y—â]»:¨c,3XlSQuPf3X–„ï]»:¨ƒ¨ –, æNÔA  0˜ÁÒÁ’ðm*ê jàuƒ–€Å6V5`fKË»vïÚÕA  0è`™ÁržÝ¦¢j ^W1˜Á°$|›Š:è`©:`À `™Á2wÂê ÌlKË»ïÚÕA ÔÀë*,Ë9j›Š:˜ÁRuÀ`KÀ’ð½kWuPuÀ€AÀ 8q"mܸ1uttd6ƒÅæNÔÕ€Ù Ö444”–/_žŽ?®ƒ…Á»vuPÇ :X­ÐæÍ›ÓË/¿l C]¯_.ÛTV¯~7ÛXŠêíÛÏzý]]ç³:ôõ 9,¯«Ì`å9`-Y²$ YóæÍËNF`®¬J.Woooöà–l|lõíè²MçýÏÄíÁÁÁB¯åÊf› çÃ÷ÜónaŸOE?ž#¤—VQçøXrQ×·ãuµÈë·¿5~»kΜ9i`` ŽŽ¦‘‘‘´sçÎÔÓÓc‹¯rÿ[iÅŠKY'«È ¢;:WÑÅÚ²eÐóR‹Ù VÞV¹"hµ··›Á€ƒõ›Á€Á ÖdµlÙ²töìÙ«>wÓM7™Á€ƒõ›Á€Á ÖdµgÏž´uëÖ±¹«£G¦'žxB  Ö¯ƒ…ƒÖTôä“OfCîqj0ÂÖ•+WÌ`13›Áb6ƒåJî> Ö/`90è` XÎQcÀ`KÀR ,, 0è`é`aÀ ƒ5K3³€ÅlKÀ’ð1`À ƒ¥0Xf°0`À`ýf°0`0ƒ¥ƒ%ácÀ`ý–cƒ–€ÅÌl‹™,, 0X¿ :Xf°œgÇ€Áú,Ç3X–„ƒ–€¥0Xf°˜Ù –,f3X:XÞ¥`À`ý–cƒ–€å5 f°,uÀ€AÀ’ð1`À ƒ¥0Xf°˜™Í`1›ÁÒÁ’ð1`ÐÁ°Ôƒ–€å5 Ì`© – ¬_ ,3XÌ̳,KÂÇ€AKÀR fÀjkk«j3X0`°~3X0˜ÁšBÀÒÁò.ë×Á€Aë: XÌ̳¬Â¬¹sçf^øþ«Æ¶mÛÒåË—u°0`À`ýU¼~ý¹,`­^ýn²Šê­[‡ ½~ ùp_ßPºçžwÓ–-ƒV=={6 XýýýUƒUÉåêííÍο–^4ãc«oŸ?~Zï&n zýÕ>‘'êPäõφã¡èë¿ûî‘,`1ó¿ÝÕu~Z¿Yñ[„£££©½½]  Ö_Åýýo¥•+ÿ™u²tN0\ï ¬"`E'Kk]¼x1Ý|óÍf°˜™™93‰… XÝÝÝi`` ë\E¸zì±ÇÒöíÛu°0`À`ý0`°&«gŸ}6uuu¥9sæ¤[o½5íØ±Ãß"Ä€ƒõcÀ€áú Xp%wïR0`°~ 0X­ Xqy…U«Veé×ß"dfff«kéÒ¥YÈŠS{13UïÚU:XÞ¥`À`ý0`°¼œÂsÏ=—-Z”­%K–¤×^{mV,çÙ1`0ƒ…kF‡ÜGFFÒÞ½{Ó-·ÜÒÔmÖÁò.ëÇ€ƒ€UCW®\I{öìI ,˜µ‹™™™¬ Xq‰…øû¥S„q=+3XÞ¥`À`ý0`°&©eË– ¹?õÔSY'k¶¹;ÏŽƒ, 0X.Ó ácÀ€A 0`ÐÁ*bÀ:xðàus¡Qfff° ýÇžu°0`À`ý0`°®ƒ€å<; f°0`À ` X> Ô,Ë 333 X:XÞ¥`À`ý0`°,çÙ1`À 0˜Á°t°0`À`ý0`°Ì`133³€%`y—‚ƒ ,Ë  Ô ––w)0X? ,‹™™™¬Y°:”ÚÚÚt°0`À`ý0`°Z¡(ò½÷ޛˀå<; f°0`À `.`:u*uuu¥÷Þ{O  ÖkªNkÖ¬IçÎËnç1`ñµ÷[ýýéÒŠéÜúõéä£ò5òP__:ÿþ›¡Á-[ø`:sæŒ –`À€AÀš¬º»»ÓÀÀ@M###é™gžI«V­2ƒÅ,`13 X­Ôܹsu°0`°Ô V«tôèѬ«e †üùÜúõًٻ«Wg/hEôùíÛ »v 0T¬¿«+{Mêë°êéðáÃiåÊ•Ug°"X•\®ÞÞÞlÓ*¥ðøØêÛCCCÓzÿ3q{pp°Ðë%uýq;êPäõÿóýã3^̘™óäZÓùúW耵cÇŽ¬5<<ì:XÌ9õ[ýýéÒŠY'«èïÜ™¹øŽÎU„«Á-[t°ª)Òa º» ¬ yc(dÀ:räÈ„]+3X0`°~ 0\+†B¬¶¶¶ªÖÁ€ƒõcÀ€AËß"dffæYhKÂÇ€ƒ`À€AÀò·gÇ€Áú1`À`KKÂÇ€Áú1`À ƒ%`1333 X:X0`°~ 0è`™ÁrŽëÇ€ƒ,KÂÇ€A  ,3XÌÌÌlKË» ¬ :X–sÔ0˜Á€K  j€Ë 333›Á°$| t°0`À ƒ%`9G5À€ƒ€¥ƒå]  Ö,3XÌÌÌlKÀ’ð1`À 0`°Ì`aÀ€Áú1`À`KË» ¬ :X333³€¥ƒ…5À€ƒVãŠs¨óçÏ7ƒ…ëÇ€ƒ¬V¨­­mÌ:X0`°~ 0è`µ8h™Ábfff3X×AÀ’ð1`ÐÁ€ƒÖ¬ X¬J.WooovþµôÇÇVß>þü´ÞÿLÜ*ôú«},"OÔ¡ÈëŸ ÇƒãÙñìxv<4{[KÂÇ€ƒ`À€AË 333›ÁÒÁ’ð1`°~ 0è`í2 ]®Áu°0`À 0`p,Wr÷.ëÇ€ƒ–€ÅÌÌÌ,`Iø0`P 0XÅ XÎQcÀ`  f°,  j€Ë 333›ÁÒÁ’ð1`°~ 0è` XÎQcÀ€A 0`À `é`aÀ€Áú1`À ƒe‹™™™Í` X> :X0`À `™Á€ƒ`À€Á ––w)0X? t°,ffffKÂÇ€ƒ`À€AÀ2ƒå<; Ö3X:X> :X0`ÐÁ°˜™™™,, 0X? t°Ì`9GƒõcÀ€Á –€%ácÀ ƒ¥0`°Ì`133³¬&5::š{ì±ôàƒfá)<<<¬ƒ…ëÇ€ƒÖdµk×®´wïÞ±Û{öìI›7o6ƒ…ëÇ€ƒ¬Éê®»îJgÏž»}æÌ™´dÉ, 0X? t°&«ŽŽŽq§ +?g‹™™™Í`5¡¶¶¶†>WšÏª X]]]©¯¯/õööfa+>ºí¶Ûn»í¶Ûn·êvdYßÁ""""ʳr°Ö¬Y“Î;wÕ Ö¢E‹T‡ˆˆˆ¬Éj÷îÝÙo–ÿÞ´i“ê‘€5Y5{¬J•Ïf1333O‡ °Š fX 0X? ®_Ë  ,"""¢|KÀ""""°ˆˆˆˆ,""""ëzUü5îùóç×ýž©^R" ñgˆªùZkÿþýÙŸ"hooOóæÍËã¸èl‘êÐ C^ë°oß¾´nݺŒ!þ¢BOOO:uêT¡êÐ C^ëP®C‡Õ]SÞ_—aÈkšYW^ëÐ Cž‡'N¤7fÇt½¿ö’—:XUžTõ´k×®´wïÞ±ÛqQÔÍ›7Š!o›GIÝÝÝi`` ;8FFFÒ3Ï<“V­ZU¨:4ÃP„:„#4.]º´°u˜ˆ!¯u(騱céÞ{ï­»Î<¿.5Ê×:4³®¼Ö¡†¼Öahh(-_¾<?~¼0u°š|rÝu×]éìÙ³c·£;±dÉ’B1ä}C)×ܹs [‡‰ÔA&RtÝ¢#úÞ{ïÕ]gžëÐ(ÃlXy­ÃlX^~ùåBÕAÀjòÉU„?LÝHÀŠÍ&¼pá´mÛ¶tùòåÜÕâèÑ£Y'¢¨u˜ˆ¡uˆ.ܲÓmE­ÃD y­CœÒ(ÿ;­õŽë¼Ö¡†¼Ö¡™uåµÍ0äµ"dÅèE<¦÷Ýw_ÍÓ~y©ƒ€Õ‚îOÞ3뉔P®>œV®\Yw~)ïu˜ˆ!ïu(nŽù… .²0äµëׯÏfNy\óZ‡fŠðº4ѺŠðºÔÌc›§:Ì™3çªñ‹;wf³•y®ƒ€ÕdfC«RÁÃÀyÑŽ;ê¾;)BaÈ{BW®\ÉÆ;;; {ùd6äÝè[qú?Ïu°ˆˆˆˆ,""""‹ˆˆˆHÀ"""""‹ˆˆˆHÀ""""°ˆˆˆˆHÀ""""°ˆˆˆˆ,""""°ˆèºÒ/ùËìÕÆŸÖ¨¦ø3:ñõø>""‹ˆ¨AÅß0‹¿Uö³ŸýìªÏÇ…Ïo۶̓DDQ³Š?øaê•W^Én¿ð Ùíø<‘€ED4 ]¹r%uvv¦ŽŽŽtàÀìãÚµkÓåË—=8D$`MV.\HK–,É:W‹/NÃÃÃ"°ˆˆ¦¢T XDD-Réa{{{6ìî! XDDSÔý÷ߟu®~õ«_e·>œÝŽÏ XDDMªt™†¸,C¹¢“Ÿ¯ XDD ªt¡Ñ¸ h5ÅH]h”ˆ,""""‹ˆˆˆHÀ"""""‹ˆˆˆHÀ""""°ˆˆˆˆHÀ""""°ˆˆˆˆ,""""°ˆˆˆˆ,""""‹ˆZv€ÞpsM‘€ED“ XDžDÙDÉsƒÈ1ê! ²‰’ç XD6QòÜðÜ °ˆÈ&JžDÙDÉsƒˆ,¢ër]¸°¶ëèwÞI?øÁÒ¼yóR{{{êììL»wïnz}mmm-å=yòdš?þ¤~` ¥îî”þó?ÿï㫯Nü3y{,öïߟººº²µÄš|ðÁtæÌ‹HÀ"¢¼¬7¦]»v¥‘‘‘4::š^ýõìs×RPJžŒþçRZ²äßø°î¾{â•·Ç¢ûý…¼Ÿc-±¦gžy&­ZµJÀ"°ˆhZÖo¤´wïÕ®°*¿7~þÿ5wîÜl¯¥;w¦þþþôýï?=ðÀixxx,½÷Þ{iñâÅc·'ú™—_~ùýå,Ì:2ñõF‚ÖDªöPDתò!X³&¥+ê>¹~,Ê×(` XD4k¢@5‘ãçÿ_ëÖ­KO=õT:~üø¸pÝœ§Ÿ~zìöK/½”y䑱qìØ±qa¨ÞÏ,X° :u*ûöíÛ×’€Õ‡"×EèèÑ£YWKÀ"°ˆh:V ;XÑQ‰Mÿ¦›nʺ#===éôéÓÙ×-Z”.^¼8ö½JsQ•á§t»ÞÏÜvÛmé7¿ùMÃÌ“í`ýÇŒn¿}âVž‹Ã‡§•+WšÁ"°ˆhÚV5Mr«\ÑQ‰9ŸØÈKA¡šë…Šz?óÆûifÆ é–[nIÏ=÷\KV5ý÷þë¿tÏãc±cÇŽtß}÷^°ˆ,"*PÀ*)æ‚BÑe©5“T+TÔû™’Þ~ûíÔÑÑ1m+Ý©åË£‹”RÌ…7®òôXÄ©ÇtŸÒsƒˆ,"šÙM4~K®4s޹¡è–„b@{Ïž=Ùo°…âÒ åsGÕBE½Ÿ‰y¤Ï-l øµúÒE{,Ž9R³k%` XD”ã€uðàÁl¸;fŽ¢[×Z*ßÔ#dÄ,Q|}íÚµÙ©³z¡¢ÞÏ<ôÐCÙÿW^y¥n°ªvZmº•·Ç¢Þ)F‹HÀ"¢,òÜ "‹È&Jä¹A$`‘M”<7ˆ,"²‰’ç XD6QòÜ "‹ˆ&µ‰2×2 XDDDD×þîÍ?bXŒèIEND®B`‚YIntervalRendererSample.png000066400000000000000000000412651463604235500350640ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/javadoc/org/jfree/chart/renderer/xy/doc-files‰PNG  IHDRXr5˜B|IDATxÚílWvÿWªªªªªªRUUUUU•ªª­ªªªTUUÕZ–©ÊvW±ð®’xƒ)^¬ ‚]Åñnø¥;¸ Jv!^Ǹ!ÄbCƒ(t ’`™°!lÓ‚›D¬‰½6%„µ×÷ç3åÎoÞ¼;oæ½7ï½¹ó>_éÈ~çý±Ïœy3Ÿ9÷Ü;ŸS!„B(V}ŽM€B!`!„BX!„BB!„°B ?Ø|îsfëß@! ¡ûzà2N¼ôG”õš?øƒ?ÈxÍêÕ«ó:™sr/ üüèG?R6lP¿÷{¿§~á~AýüÏÿ¼úå_þeõÅ/~QíÙ³GÝ»w/5€eÚ$æ_ù•_Qõõõêé§ŸVSSSìLX%Cÿõ_ÿ¥~îç~.ãÄuàÀ÷ùáááŒçäµòžJVÚ€­xž{¼ùí7~ã7¹Í ù_Âö)1ËÑÑQ¾ÔX%C_ýêW3NTRùÙÏ~æ<÷û¿ÿûÏmÞ¼¹è“!€U\<;wîŒÞϪÀÒd!`!”ÍÌÌ8WÿÞ“” 1ù«WR™ŸŸ¯8U3`]»vÍ󾾦¦F½óÎ;ß¾}Û°_ÿõ_O5`i}ðÁε÷¹_ýÕ_uöi„€…PÅõì³Ïfœ¤~ë·~+«zµk×®XO¤þç夸~ýzõK¿ôKŽuvvæUÉÐzï½÷Ô£>ª~û·ÛéKúÅ_üE§·ìÉ'Ÿt$×ç ¤|ó›ßT¿ó;¿ã ÁýùŸÿyÆóÿøÿè¾W~÷>÷gögŽ¿­­Mýõ_ÿµ­ò÷ˆd{®Y³Æ¡b€Cbð¾öOþäOÔÂÂBÖë¤Iÿ?…nëbãðo˨ù+d_ú«¿ú«Œç¥'Ë«¨û„ÿïH›|Öïþîï:ï“êî?ýÓ?9¯•ød¨V¾'òœ¼FàÖ¯×_Ý@½å§<?BB)—œ¤åtòûã?þãØ+þçåDì÷ùO”a'èï~÷»9{“ä¤:;;øyþµ8îoÿöoÝ÷®Zµ*ëµaÿ£Ø‘#G ¬¿üË¿,h8,îm%ÿ¶,%` ¬øÿ¶V>û„é9Ó{ä‚DÖôœTµ¶lÙ’3fy! ¡”kdd$ðDpòäÉ’VÐÉ/êç^¸p!ëä%U†À“ZØßÿøã3NÎò» “ J5Bûåw=,%0*ð![* Ò·æ¯: XRyñWX ÉE”m]lqöƒ…½Gþ?ïó² Ù'òéõ ÛŽ~蓊¡ümùéõÿË¿ü `!”výÅ_üEÖ C–r(èK”'`]¼xQ½õÖ[>—¨ŸÛÜÜl„9©yýR© ú,é;ó#ú{|d–åáÇ#/]áÿûþ˜ò/ÔåÓKUè¶.&Ó¶,`ù_£ÿ·|÷ ÿßsü‡Šüœ ¥Šþîïþ.k›‹Þÿý¼–=AÀB(Ï:‘„-Ë`™N|Qß'’ææ(/D9qûO œ=ôP†O^£%ýOÒ{$'XŠóWü§¬(ÛºTq”£‚%}N…ìA'×¶ zÎ?qDÏÌõ¿^ÿ¯X¥}çiÆY¡ TèûÂÖ…*t¸J†eAKïÉø×~í×2f­é“§4{_¥ß(Ÿííïû‰Ú(oü¥Œ£€%ÃlÞçkkk Ú' Ïô\®jc>•C„,„¬Š–¿b`š]Wh¬²bzЉyãÆîëü³Ÿzê)uçÎØKfûy_û§ú§Æ8¥Lâ ÝÖ¥Œ£€åo¨ÿö·¿]Ð>'`QÁBÀBÈ*ÀòW%´ä1A+Òk $ȺQùÆêïWòš «jU-â,¹=ŽÿõϹsçœçïÞ½ëÌbûÍßüÍH?È_Ê8‚òWȾ$ë`ùó.ëµéÞ¯|÷‰8Ë߃%KE˜rH°° z¾'÷°¿¯áAÛÙ³g¿ kyý²Ø¦ÌŒ”ª…T d&¤žv_H¬¦e,üËWø{~LpVÌö–åâ ÚÏgAù+fŸòÚ±cÇÜ÷å»OÄ XþaK™=(b!°° XAk ‰†††²V:kÉØè¡(-ÿRq÷`iù5™4¦º­KG®üX…þµ¹òÝ'â¬\±²°°XR}¡ Ý$,+lk}øá‡ÎóÒG$3ßdHJN°Rm’Õɽ«ç«|®ÿÆ×2ÓÎÿ¿IŸ”>¡Ëÿè¯dÄX"™ö/ñÈJâò÷äÿÈa1©Ò螟B¶u)ãË_Ô}JÞ'ñÊ›¬¨žëö8Q÷‰¸KW²dæ©w%wyÌJîÀB!„BB!„€…B!`!„B! !„BÀB!„°B!„€…B!`!„BX!„BÀB!„°B!„,„B!+ÝúÆ7¾‘ñ¸££C=ú裩° 6±ûq q”(–M›6XQkûöíê?øA*ìÃ?$b!ö-â â(Q,_ÿú׬¨€õä“O¦fGz÷Ýw‰ƒXˆƒ}‹8ˆ…8J €•`mÛ¶-5;†a†a¥3«KÊy ¾¥¥%Õß߯º»»@›››sŸŸœœT[¶lQõõõŽQÁ"b!ö-â rBë¾jjj\ójhhHŽŽºGFFT__ŸóûÔÔ”Z»v­ºxñ"=XŒÿ q°o±=X¹@Ë«¶¶6533ã>žžžVMMMÎïZgΜ¡‹«'b!ö-â â ‚•`ù‡ýdÈPû´²V®\éø¤§Ê;|H†a†a–°ü½¾ÚÚZ5>>î@×ââ¢Ú¿¿êêê2‚•6ÿ¡”5åÊO[k³=öMC>äñ•+WR±Iäƒï;ßw¾ïÕ¾UUKËÿ\]]=XÄA,ÄÁ¾EÄBN¨`å¬ööv5;;›ÑƒÕØØèüÞÚښџ%Zµj=XÄA,ÄÁ¾EÄsï>¬~¼}{–M>ÿ<=X6Öðð°3sÐ;‹°··×ý}``Àí»š˜˜Pƒƒƒô`a†aXÜÀò½ï)õàƒY6·i=X¶,Óà]®!l¬Ý»w;Mî24(°µ°°@‹8ˆ…8Ø·ˆƒXbŽÃ6À¢‚Ž7güŸ8ˆƒœKâã° °èÁâ^„\=qEKÄANˆ… ,îEˆa†aô`у`QÁ"b!ö-â –Èq¼uì˜T^»´oŸúïçžSÿ±{·VòSì‡d|Æ™7Þ0Î6¼ÖÓC‹,Æÿ‰ƒXˆƒXˆ£zsV­’߃>ãÜ‘#Æ÷Þ]·Ž,*X\=±±Gõæ$*`=zÔ­di»Ú×—À¢‚E†a†YÕo¥+èuI,z°¨`qõÄ-q9!++XI,*Xô`1þOOq9!+{°ò¬{=”Õ8?qð ±zf2z°¨`qõDä„8ˆ…œXÇø+¯80%¦ÁJìú3ÏD¬;mm³ óéË 4*Xô`a†aXjÌ7a€å¯tå3³0.À¢‹ WO\ÑqbIl¹ëÇÝu®Ä§Ÿ|þùŠ,z°ÿ§'ƒ8ˆƒœKbãÈXQ}•,z°¨`qõÄ-q9!–ŠÙÛccîV×p¿ðòËV,z°0 Ã0¬b¦›Ñýöãû£žžžVMMM±,â â â¨öXd¶ i-«­,*X!€U__Ÿ5d¨}òÚ+V8öàòFݳgºwï=XÄA,ÄÁ¾EÄR€•£ß*.À’×iÓïó.û@V`™ªT&ŸT¹°ž~úi#Xió–$À½{øòO[k³=ž©©©TäC_¹r%û—ÄA>ø¾ó}¯Žý˽Žçyñù_/>ÿûõë¢~ÞG.–ÿód}®™W^q×éÒsý?qï_UUÁòKž«««£ Ã0 £·*¤·Ê¦ VØp`9îU˜:ÀjooW³³³=XÆ÷Þ¹sG=ðÀô`±ûqT},I¬°ÅG‹,z°BkxxØ™9èEØÛÛëüÞÙÙ©ÆÇÇÊ•À•,ç°wï^z°ˆƒXˆƒ}‹8ª>– Àú¸«+kõó3o¼QvÀÊÇW`уeX¦Á»\C®u°Ž,ï›–i·¶¶V}á _P/¾ø"³‰ƒXˆƒ}‹8ˆ%`UzQÑr,îEˆa†aVÌ€E÷"äJ+Zâ rR!û÷ïßxÃàS'OZË¥ï|Ç]¿JÛ­ŽŽÔ–7.ïZ]T°¸!½ ôdqKãÐk(ùM ˦X>|î9câ÷Hî;Xì= £¾–,*X\ÑRe â '–V9c‘ÿÍÿ¾©û•‹ =X†aXj+ Õª(€õþ¾}ÆÕϯõô¤°èÁ¢‚Å-Uâ r’0À’¥ ü=Mz)ƒJÇR `%œ¨`уE/qâ –*èÁÊ·ªUÎX,z°¨`qEK•8ˆƒX Š#ì6*q™Ì´ó™-ÉÝBò¬$T°f¶lq{¬ä§Øø+¯PÁ°èÁÂ0 ÃÊ X¦“tÒû²¢Î´¦èÁ¢‚Å-qâ K+XqV9s"•)Ó=üÄ`QÁ¢‹ž údˆƒ8ÈI,€å_²@ìöúõ.xh»×Úšå“› X•ÊIšaŠ,*X\Ñ9!b©`+jÃwXóºw)Ýäû™>ùû¿Ww~Øí×Ò6qð`l9‘YЦUåO?`QÁ¢ Ã0 +oVÜ€e:qëá8¿Iµ,®˜°X¨ú°èÁ¢‚Å-UrBÄ[a'Ï$V9 ,™( ì^ÓÀ¢‚E=ôÉqKAqxOž¦~«$V9 ¬j1H,®h‰ƒœ±”©‚U,LÙXÁ°¨`у…a†•t¦\pø—,Y„Þ —üüÏ^Èò‰½yâD¢{°,z°¨`qEKä„8ˆ¥"¬8Nȹ|²”ƒžÕ§«^bÎòÍɃƒY÷:üŸ¯|ÅYJ»ªüBs3€E«xÉxiCCC†oiiIõ÷÷«îînÄæææ²Þ{ìØ1USSCq q°oY‡,Oà‡ ±k==‰¬0èúèÂ…,è*fÆ þÕ¼j;=X1IàH›WCCCjttÔ}<22¢úúú2^#”ºuëÖ¼‹ q qKyMÖtÒkL];pÀùyUŽé9–dH*`3l°ä³ôÌA VÌ"¤‚U0hyÕÖÖ¦fffÜÇÓÓÓª©©É}|ýúuµiy'ÿôÓOó,z°0 Ã’±¾UTÀ2Ý"F,­€UÍk^уUbÀª¯¯Ï2Ô>*looW³³³Æ÷RÁ"b!ö­tV)OȬª,4iß×¾ö5599™óµ¬´yõÔSO9c´: òÓÖÇþŸ¶ÆsëÖ­TäCOMM¥bÿ’8Èß÷¸_>u*2`-´µ9ïs Å÷yÎp™ïóõëü¿˜÷»Cu†Ï»ÛÕXaÛã'ÝÝÆ÷ʰi\ÿ_Üñ&áóòyÜ瓪ª`yû¶‚z¸¨`±û¬¤U°.¼ürÖýÅÞ£ZE«<€åÔ=X‘ÞK†a˜€õÓ––,Ø›.'°nnÝêÞôY¡÷Ð…¬ó…¬²Öðð°3sÐ;‹°··7À¢‚EÄBÄR^ó.É 'Å\K2”û„\Š¿´æ•˜T±'*Xe]¦Á;Ôu¬B‹u°ˆƒXˆƒX’±B{NÈ¥6¤¡u°XÉ+Zâ â  À¢‚`q/B ðJÛø+¯d¬K¥íú3ÏX=XT°¸¢%b!b)èºeÓM˜,‹ V•=XÄA,ÄQ­±È ==ëMÛOzH-|ùËY~Ý€`Xô`XT°ˆƒXˆƒ}+‡é%¢Ø#\ŒX¹ëÚ·¾ÅšWT°èÁªǜ— ƶÁÒnå¬ùµ¯¼vöèÑÔ>z°,*XÄqÿKi2rBiŠåÊÎÙÃ--e¬\U- ,z°è/Ii¥,rBI‰åÃçž‹ ?…Ö[ÇŽ¹`}»; =XT°¸:§‚E‹8k—¾ó”ĦÜßey„RÖ­Ž·oHì¿—?ïv{{à’ î ~Cú²,|T°èÁÂRj¥, ‹Óô­e‚f¢Xõô¸÷”Ÿçv€Íë»Ú×WðЀ…,*X\SÁ¢‚EUXâ;aåÓ `ᣂ`уU…q˜n¶z¯µÕVíê 'Ä`XºMõâÓ¿ßܺÀÂG¬p“þÿ ±ÿþ÷¹:OP¹ªU¥(/“âH `Iº^"Aý‰‰¿”€eÛ ,+a=XúêÌor£wÄŽ~+ú®°4V1'7 =XVÅ*XI,ª T°Ø·ªg¡Þ‹:‹°˜“Û…—_v‡Ôõ’ 2Äw½§'cI1ý¿Xø¨`у•÷öNUÖ´`_9‹>™ä–Í99}ü¸;ŒôÑ… îïgÞxƒ}+!ÕØ8ßË =XVÅ+Xù¬C‹ –­yò/8) ]IñšT[Òºo%¡bk•ÿÃtû'ýÿqBÆG« Kh³¡¡!÷´´¤úûûUww·HbsssÎs‡Rª®®NÕ×׫®®.uýúõ’ö`%°°èÕGïÍVMw³÷/žçÌ´VÔץɒXÅ̰ðу•BÀª©©qÍ«¡¡!5::ê>Q}²ðݲ:;;Õøø¸ab¯¾úªj‘ẠT°L3tNÑââ¢zíµ×œ!ÃJT°¢–̼1 ==z´j*Xa+;SÁŠÏ¼kyM¯}D+¹€å1è5ÿ,½|î1`ᣂ`å|ô1¨ùùy#Xió–®N‚ü {|{Çõ“înwʲ~¬Ÿw¦,¼ÿÞ£|—OŠü÷mÁùgk½¹|RõÆ"ÛÆ4«Ë_Iš|þyãí|Þ9|¸°!±^œbzvLy÷®Í”æY„¦ýFßnÉo’çR,*VÁºûÈ#Y·€2Mè â VW°ÚÛÛÕììlFVcccàûeFa¹îE˜$Àªt¿OÔ¡¢0ÀJB–‚ôIRîY˜ïìQKÔ“oÜ'érV¹gñ$í;’TÀªÖá@‹|Òƒ•` ;3½³{{{ß7oÞ¬.^¼èTµ>ûì3§K–^HBëÊÎYÕœ…/9µ¬¸+Ž8d‘LÓ‚°ïïÛWð9ŽsåSÇRm€%•9SÅ.ßÇ“PÁ »(°,|T°¬Z¦Á»\C®u°Ž-Èe©™9¸zõjgù†;wî”í^„¹’\È ¹ÖM ¬;mmY@ôà ú_ôrYÃ&Ë9)'`å Ni¬0ˆŽÃdÖq–uÍ«J–@«i}·$Þ²ÀÂÇ:XU´’ûSó7n‚Ìð+`éžïý»tÏN5T°ÊqBN `Uº‚%UTù¿õÿ®¿í€–cÛ+Xa÷äD `‘O*X‰¬¯ÿáºÂ¥ÆF÷¤mñK_26X¹nWQ =X¶–wÈWŸð†|+݃õê.j$l"CÒ«ïH!ß㸡‹“*€…,«+K"`%iaÔÙdqœåæÃ~þ¸«+vÀ2ù¼ëšygkéuÍtN®?óŒñuþž$5Në¤Ó¨®}a]*À’øýkÇÍËlá˜+X…T), ,+倕Ëdš¿©_%Žw®ÿÂË/+€ ÆvBªd”°â˜8fä]ëéɪÀŠåjì·° ÉqœýV€…,+A€eRÓk•³‚Ö0[*À*Ç 9è䫇wõß)çÙ”oõËDºI;ßý#jStØêîq–TæL;©äU° ­`E©bK.DL³^ß㤠`ᣂ`•ªâGVÒK*l‚¼@$ o–«ßªfSN¢ž|KXÅLëšÏ¸÷ó ÿ¬©) ârMV ëǨ`EÍ'Z =XV–wAÉJV\«wÛ\ÁŠzB.åp`¹+XVi'-„}GL£Ÿ¶´X>*XV\€å=ˆë¹þýj__VñJâØqJ9sÌfÀòVµÂš×“XqMëO"`…Mdˆ°d¨Ò?Kñ£žžXËÿÅÙoULÅÀ°ðу•jÀªÔ—6.ÀJ[+Ÿª¢Tü œïªíI,™`ªpJߎ­+R­º÷ÐCYëÇåÛ¯æýŽ”{øÀ°ðQÁ°,¬\cÍ2 ij:.t¥t™}h𕍇5ulÒ¤kZv@ÃA\€Gµ*©€tò¨ô6ÇöðÃê?_x!« +· J`Ébº¦æzÿLÖ8×0Ëõ°8!㣠À°b]+Ž2j\½8Õ X¦ai½ª`zB[ݽTȨ3E‹,ÄùWÖO`y«”ÞIQ.DðXä“ €UÁ,+y€õóâ¬JùÊXaà”DÀâ `ᣠÀ*ÁŽ×êÝÕRÁÒ'䤘¬ä–÷;âígóVJ½ ™Ü€ÀÂG À*ty-té±fïMb½–ë&±I,‰Õžºo¬ZÌÕXÞY„Þ™ŠQ‡0sVP?F9·€`ᣠÀªà—¶aCMê…4Ì&°80?˜µÊ·Îã­ŽŽÔV±7j Àℌ €`ýÿ©ê­­·V)fFR.“Ê—¿’v½§G}²aCÖ¬D½ò:æÒúäצyÿŒÁ$ýϲŸššës­¼7`%¡ï Àâ{Œ,+€•ëÀœoËNbz(Ñ-±&`Ý$ÌÕqÐŒãóL·ÐÜìì¯SÃæ'd|T°,+°ôXs\ NXÐÒ2£. 3,¾ÇøèÁª¨dc444dø–––T¿êîîvIlnnÎyîÕW_U›–XuuujåÊ•Îk¦§§« °.ïÝë ]>uÊùt;‹3€UyÀÂÇ÷¬²«¦¦Æ5¯†††Ôèè¨ûxddDõõõ9¿wvvªññqÂÕK/½¤üqëË{hÝ<.ýWQ¡kfË–¬YXþY„f‹íÏ =XU4D謶¶6533ã>– USSSàûW¬Xa=`[ÕŠ2› ÀâÀœô™…>¾Çø¨`•°êë볆 ý>­‰‰ §ª•FÀ2ÝZ%êp`1€%ëj™d?_d|>¾ÇøèÁ²°üƒ|§OŸV=ö˜±K÷n™K )W~: §žÇò¼wïë )ðýúuÅ|ÞOº»'ŽkžÅ8ƒ>OIÞ{Çœäñǯ¿žõ÷’o¹?/íñUúó®Œ÷ßÛO<ëÿ§gŠïÖÞ½îcòYŸÇöªÞ|NMMžÏ y\•¬_|QmÛ¶Ím~Oc+ŸU¾ãXp’«@®ÓRÁ"Ç|Ùô`QÁ2V{{»šÍèÁjllt UJ£{š–iˆê»ÖÓc\÷çý}û,Ì9ÆÇv  À ¬ááagæ waoo¯óû¹sçB«Vi¬¸}r¿7ÿJñbQïÇ€…ï1>z°¼Lƒw¹†\ë`™Þcêϰ¢ùªyÆ fÖøÈ1>*X¬äžò•Ü,ÌøÈ1>rŒ, ÀâÀŒã#Çø¨`X€ÅÛ㣠À°,ÌøÈ1>rL>©`XöùÞ:v,kµx1ñóEÆGŽñ‘c|ô`X>ÌøÈ1>¶, ÀÂÇ9ÆGŽñу`±³óEÆGŽñ‘c|T°, 9ÆÇv ÇøèÁ°,|˜ñ‘c|ä, f|ä9ÆG€Å—9&ÇøÈ1>*X€…3>rŒ“Oz°,|˜ñ‘c|ä,‹/->¶9ÆGŽñу`Xø80ã#ÇøÈ1ù¤‚`áãÀŒã#ÇøèÁ*ƒ¤œ×ÐÐá[ZZRýýýª»»Û$±¹¹¹Ð÷Xø80ã#ÇøØT°ª°jjj\ójhhHŽŽºGFFT___èû,|˜ñ‘c|lz°¨`y€É«¶¶6533ã>žžžVMMM¡ï°ðq`ÆGŽñ±¨`X T__Ÿ5dè÷Xø80ã#ÇøÈ1ù¤+À2STŸ¬´ùKJˆšrå§$ÉûXž×‰Óõóâ/ôýúu|^r?í•þÏc{¥ÿóØ^՛ϩ©)ãû }L‹ >®|ñ‘c|lz°lîÁzíµ×JXíííjvv6£«±±ÀÂÇ9ÆGŽÉg:{°jüñÿ-–°†‡‡™ƒÞY„½½½>ÌøÈ1>rL>ÓÙƒÕÒÒâ€Mmm­Ú»w¯ºwï^,Ë4x—][+è}>ÌøÈ1>¶,+Kàçèѣ΀,ŸpþüyVrÇÇ9ÆGŽñуU¬Å@?ÿùÏçUQ°ðq`ÆGŽñ‘c|T°´°°àôF­^½ÀÂÇ9ÆGŽñуU,´9rd9®Ý!Âññq†ñq`ÆGŽñ‘c|T° Ukk«Ûä¾oß>§’•dXøÈ1>¶9ÆGVU.Ó`áãÀŒã#Çøªº‚566¦l€…ãc;c|ô`YÓä`áãÀŒã#Çø¨`X>rŒí@Žñу`Xø80ã#ÇøÈ1ù¤‚`áãÀŒã#ÇøèÁ°øÒâc;c|ä, ÀÂÇ9ÆGŽÉ'=X>ÌøÈ1>rŒ €Å—–/2>rŒã£ À°ðq`ÆGŽñ±¨`QÁ°ðq`ÆGŽñ‘c|ô`Xìì|‘ñ‘c|ä,KKh³¡¡!÷´´¤úûûUww·Hbsss¡ÏXø80ã#ÇøØô`U=`ÕÔÔ¸æÕÐÐuŒŒ¨¾¾¾Ðç,|˜ñ‘c|l*XT°< åU[[›š™™qOOO«¦¦¦Ðç,|˜ñ‘c|lz°¬Àª¯¯Ï2Ô¾\ÏùÁJ›°$šrå§$ÉûXž×‰Óõóâ/ôýúu|^r?í•þÏc{¥ÿóØ^՛ϩ©)ãû }œ:Àò?öúr=G W¾øÈ1>¶=XT°JXÁ°ð‘c|ä˜ã£ Àò¨½½]ÍÎÎfôY566†>`áãÀŒãc;Äç›8xPÝ]·Î1ñéßonÝJ–€5<<ìÌôÎìíí }ÀÂÇ9ÆÇvˆÏ÷î÷¾çüî·¹M›¨`Ù²Lƒw¹ÖÁÂÇ9ÆGŽ,z°XÉf|ä9°¨`X|iñ‘crŒãK`у`áãÀŒã#Ç,‹/f|ä9N³ïÜ‘#êÃçžsÌYÄsùçÿý?ÿG]íësžóÚÙ£G³Þk1™qH€Å—Ûã#ÇT«ªU¹¶sR‹ €…3>rŒ[ Xï>¬~¼}»cò¼üü¸«+€E€…3>rŒ[ XA¯£‚`ñ¥ÅGŽÉ1>rŒ/…€E€…3>rŒ§°š›3šæÅ®ìÜ™õ¿ø‡µQÁ°ðq`ÆGŽñ‘ãÔÌ"ôÚìÞ °ü –Oã{ Ñƒ`áãÀŒã#ǩچA–dÀ¢‚`áãÀŒã#ÇV–¬%U-¿ýðÀŠ=X>ÌøÈ1>rl%`E5*X_Z|lrŒW•ïÂË/Ë?´°èÁ°ðq`ÆGŽñ‘ãŠùdHÏ4Þ™{ÅÚ[ÇŽ›æM³MCŽÞæzýZïçxg&RÁ°ðq`ÆGŽñ‘㪬0ËghR¿6¬ÒE€…3>rŒX1¬-..ªµråJÕÐР#=`áãÀŒãc;$°Î¼ñ†±÷ëZOO,€EVˆv/'~bbÂù}~~^íÚµKíß¿?ô9 f|äÛ!¹€ÖøN«Ä’Ê”Wsssª¥¥%ô9 f|äÛ!Ü÷öؘ±©\fÚ Xô`…¨¶¶V---eøêêêBŸ°ðq`ÆGŽñ±*»vT)Ë4+‘Y„y¨««KŽŽ:ýVSçÏŸw!*×s~°Òæ,£ÕIŸ’8ïcyÞ½ùåýÇúygZhïׯãó’ûyl¯ôÛ+ýŸWÍÛëú{ï©+ccîíl¦Nœp¿søpàçMMMÏw¥xüÑ… €åýí'žPw—ÏùòS^£çúü[·nÅúÿ¦°dØoûöí8I3{oo¯jnn}Ž >®|ñ‘c|ôV÷VÙTÁ*d8 Vž:wîœêïïÏû9 9ÆGŽÉ1€•°Êi§7ö~½¿o_,€EVºté’Z·L¶ÓÓÓy=`á#ÇøÈ19°þ×nut¸ÏùÚËYÁÊw,*X% ŠuvvªÉÉÉÈÏXø80ã#ÇäÀ 7]ÕŠ{í¨Jë`±’;>ÌøÈ1>rœÀ¢‚`Xø80ã#ÇøŠÎ±ôûèa2ï™ôÙ³¾áqVÃø#¨¹M›\»ÓÖVñÛâb2Òdô`Xø8ùâ#Çø˜c™±f™á–Àò® ¥ñ=©¬‚ Œ €…“/>rŒÀŠê“Ê^TSCÔ­M›b¬¤ö`bô`Xø8ùâ#Çø¬¢«UQKf ê›*{íƒÁA*X€…“/>rŒ¯´€•dè*°0 ÀÂÇÉ9®bß›'N¸ÌÎ-]îÿ>qð €UBÀ¢‚`Xø8ùâ#Ç)ö…ÝF%®_ëéÉ.[üÒ—¬¬8ªUô`X>N¾øÈ1€k޵%½/«”€E À°ðqòÅGެÀÏ“÷kÓp412’å;uò¤U€åEèµr¯ E€`á#ÇøÈqŠË¿dA>=J^p ¬ ÷Í gÒ+Vé}8ÉU*Xf|ä˜ã³°ò©(€e²¹€u¦tÕ(Ž˜e Óªòo• °èÁ°,|œ|ñ‘ãûÎ=šq‹m7·nM-`Iƒ½éodQÁ¢‚`áãä‹ã+ù0X5–Ü?Pÿ} šº0z°,|œ|ñ‘c|EV±ýV6V9¥‚`Xø8ùâ#ÇUXù€“ôkyígkÖdùÄþýûß°èÁ°,|œ|ñ‘cË~‹óä*½_&8Ëwey*XT°,ÌÔÙäŸ5€Ué“y¾ñÉ òÒtµí§--N¥Ì»ªüíõë¹Ç =X¥Ñââ¢P+W®T jppÐ}nzzZm_ÞëëëÛ¼y³ºq々“/>rl‘O†èü°!v«£#Ñ€õÃŒË*È Èbg z{Ψ`QÁ*‰v/ïlÎïóóój×®]jÿþýÎãuË_À±±1µ´´äØ¡C‡Tkk+€…“/>rlÁò zVÜüÆ9×¼J*`Ó—°¤_ßèÚkQóéÁ¢+§¤jåÕÜÜœjiiq~¯««sÀÊ«+VXø8ùâ#Ç–- °L½V•.+`Ù^õ¡‚•`ÕÖÖfA”€•èÕW_uªYG—¯ˆnÞ¼©Nž<©vìØa+m~ÀÂÕIŸÎ—ÃóXžwïÿtÿ±~Þ)Óø~ý:>/¹ŸÇöJÿç±½Êÿy—OŠ X mmY/IïvuVØöúIw·ñ½×Hl¼Õþ8u€Õµ¼ŽŽ:½XZçÏŸwK jãÆT=¸¼cJ?Ö'Ÿ|B Õ |ä8¥¬$,*XV– 8 TI£{oo¯jnnvžkooWW¯^u_+ ¶Ivx '_|äÀJ0`yã ÷†Ñ^;}üxªú–èÁ²HçÎSýýýýVô`áãä‹ÛXŸ=üplDY´’6ùüóîr 2ä§çðá‚—¡H[Õ‡ –%ºté’3sP–gɲ 2L(’áÃ_|QmÛ¶ ÀÂÇÉ9N¨ïÍ'Œ³âô‚¶›iéYn"‰Ã€å ŠuvvªÉÉÉŒáC&ý¼ôbу…“/>rl‡/Õ’ aC[‹ +¹³’;>N¾øÈ±å¾4öûØXô`X>N¾øÈq‚|2Ìg‚*XT°¨`X˜ñ±ÈqÕ¬¨®ûŸôÌ·î߯. ¹èºÙ\f¼•°Òh¶`Xø8ùâ#ÇEøtv“™qT°¨`QÁ°,|œ|ñ‘c +ý>Þ¥¼¦—n¨¶¾%z°,ÌUêÓ÷1Ów§÷ß׌í`¥eí)ÿpàµo}«,€u÷‘G²¾cºrCµ„8¨`XVJ}A'¶ €•&ŸœB­ÀÊUÕÂ0z°, ÀÂ`XžÅBu¼·^ç3*!ÆTKˆƒ €`Xø¬Šú®õô8÷æÓ÷é{ß¾‚K†ïv´ýÏW¾¢š›ÝÏ»÷ÐCÎmltó¶ü¼½~}¤ÊT`ÑïCô`X€…Àª¨OªH¹f¢XÞ~à ˧>_À¢ZBT°, ÀÂ`X1†Ñƒ`å<0ËßMý§NžäD– ŸixE†C¼³™E`¥°ä5Þ¡@mQÖa*°ä8hú~1‹8¨`X‘Ìú¤í79¸p"³§ZE•ÀJ+`cÅý>ÄA€`X€`åX‹_úRF3¼ØÍ­[©–, À°,Ëžÿ[î¨Û¼vúøñ’–÷ž…^³eer °,,oÿ‚.‰ÿlÍ ÀJ•oü•WŒ«Ü_úÎwª°dY©êèÊŽþ=_Ð)Ægº2/`Q-¡‚E,VY+ŸÅù,ËV_аÁI ÓäY³)­€•„е©·D¿N XßÿNçLLÃ1ý>ÄBN,G‹‹‹j``@­\¹R544¨ÁÁA÷¹šš£U°ü½b2»ÐIŽÏ4¼¢OŠþ÷TûÒ Q+êë¬òU°ŠY’j ±“*¬ÝËð‰‰ ç÷ùùyµk×.µÿ~ãkgffÔÚµk+XTAì Ë€`%°rÅa`a=X”T­¼š››S---Æ×öööªãÇ'°X/ëê­cÇÜ&YñéߥçÀ°¬ü*XùÜc ±+Cµµµjii)ÃWWW—õº7n¨Gy¤b=XQ‹Ù†á+;Xñ6ª›š±£À,€•?`]Þ»7ëÂA.( ù“Ï?Ÿ5™Fì‡dô–ØXôû=XTWW—uz±´ÎŸ?o¬gŸ}V>}:¬´ùK )W~:GÏcyÞ…ƒåÇS'N¨+ccÎÁS~êÇÞ×½ÿÞ£ş̌N¹×|ÿýzý:ïû“øy²½‚«\ÿ_Øã ”äo¡­Íí¹“Ç×—ß“Ôí?50`ŒCüÞÏ zÝÿ<ñ„ºµ ³{ö8'gyÝǯ¿žñwg–a-°lØ yÿÇßþ¶ßOº»Õí;œŸ÷Z[ÍÛzy/$¾ 5¯d{¿{Ÿ“üD,É_”ý¿œ§¦¦õÿóøÊ•+Vÿÿú±Ä‘†|”bÿJ`Éàöåƒ@•4ºË0`sssÆkd#<þøãe_+ŸŠHÚ+Xremºâ–+ñ´T°LöÑ3Ïd-eà¯ZÈ60ÍêÒëé×É"¦ysX/N1=;ú½²4i‬ÙT ë`y—o1mCÙßK±¨¨{…»™-[ÜŠ¥¿r‰a,£Î;§úûû3|`ºÀJvÏŽ €e² ÜEÉgÔ“oÜ'é¸+êšK6Nð{­À­©gðÍ'* Xa=X¶4´ÓïCô`%D—.]Rë–OtÓÓÓ®ïêÕ«ª£££"+¹ç2©FTÓ‚¤I,YyÚTMË÷6Ös¡k.™Ì{ƒ_oe/ß[º$°Â.Š*Xa=X¶ý>ÄAV…%Ãbjrr2ã9ñ]¾|9q€t`þlíÚT®—°d˜Â»¾Ø'6¨Ï~8 ˆ®?óLAÿ‹ž¥iZ« À*}«-ð&ˆ»²sgUÕb!*X‰_ÉÝm`~衬a€\ÇBOȦ…-«iZ9NÈVöŠÞ^óß¬JVXŽË Xr1`ºpø`p° ™…ÒŸgºÿŸTgé…Á0z°¬¬| Àª>Àò®a¦Mª‘I¬Bú£r]†Md(U>e8ÿúqFFb,Ó=*kîåù=Ž»ªEµ„Xˆƒ €UÅ€%WÖ¦+nÿL¹¸KÀÇ?óN†ã,“Ép”šòš¿B!`âŸÑ%¦«Þmhš‘§ª£V<¼Õ’\¨û²îcƒèrsÜ€UH•²R€E¿±=XVK†„Lý*¥Z=][Ø€ã8!rò-—åÚ6^˜ò®êïŸ÷´þbª%Õ Xó7f ÿEª£‚Eµ„8¨`X%,ïôn¯z-Ævò-`•ã„tòÕ=u^ÓëI%°ÂN¾¥¬°ÕÝsYÔ|zgz­Ðý­Ò€¥ZU `…­ÚŽa=XV NÈÕXÞûêUòM÷'8õƒ]XɺÜÕ*[«†ö|+XqïçA€µÐÜœqÅÌ"¬`R}¤ZB,ÄA+Ñ€µÝsbð㬸Wï¶°ÂÆÌm4U.‹Y˜2I€•oV¹+WŽÃþ†ÿ{eº|Í4³PÌáP`ÑïC,ÄAV¢ëÉ'Ÿ¬øPÊ™c6–¾âP‰Ú¼ž$ÀŠkݤ$V°Â&2ÄX¦Eb¥j±©)ký¸[VÐ~cZÖBßö§TN*XTKˆƒ VêkÛ¶mVÂ+¬/˸xë2xùgÞéÅ[m,™`Íÿ|á…Œùkßú–ºõÕ¯fUKü}€•„~«|Ë;4ì5#~ÐÉ·Ðr1¬bL*S¦æz €,b!*X)íÁJ3`ÅÝW/N®1ó´VT‹°ÂVw/UCÔ™¢Å– âL•Ú$V!«¶ÓïC,ÄA,« +Ž ²Ìóƒsçj¬J]– °rSKª¿¦IzqZª%ÄBT°èÁŠÉŠY½;€õ„œTØ3Í+ôrI¬rä³Ü€eªåš`P® * ÃèÁ¢‚UB“…õÐwfS®ÛÎhŸ©)ZLVU·©‚%€iO›nvÇÕSµU°¼k²HlTÀJjÅšj ±,z°*p–Eƒ†â¾eJ¥{°ÿÿ_“ ~ȼ±ü%¿ûðÃY ò¹ ºÒq²Z¡}MÕXôû qЃE+ƃp©V…›è¯¦ÍlÞ¬>Þº5kVb®Fi®žª;©¾ššë¥Z `QÁ"â ‚EVjËNÞÙdúó’°n–~‹ ~Lw¯µ5ë>•êå£ ÃèÁJŒÕÀÀ€Z¹r¥jhhPƒƒƒÏONNª-[¶¨úúzǨ`ų¢w¥‹«§êŒ£Ô÷ø¬ô’ ÚÂnŸÃ¾E,ÄA«lÚ½ Îïóóój×®]jÿþýÎã©©)µvíZuñâŪèÁ2ÙÔr Þ?o¯_o5`1þ_qT `±oñ}'z°#©Zy577§ZZZœßûúúÔ™3gR׃%«[{I»ÓÖº®.oc´¬ªN‹+Á$Ŷº;€Å¾E,ÄA«„ª­­UKKK¾ºº:çgSS“Y2|(ÃÒS%f{VÔ¡é-)Õ-SèÁÂ*½6€…a=X%TWW—uz±´ÎŸ?ï–À×øø¸ã—çeèP^o+mþ!B)!º«‡/ÿ´éñOº»'Žk÷+¹ÞN·öîužwú»–Þ8{Öù<6ù9óÊ+ÎOgEêÄ'þ¶æÃÿøÊ•+Vÿÿú±ÄQ’í36fÜo?ñD¬/°ÑâýK›íûß÷êù¾§aÿJ`IEJ@H J*U½½½ª¹¹Ù,¯´4|Ù܃Uì"”Q*XHþaH±\7€füŸž +Xz͸)ß"¦ä„8ˆ…œ°LƒGçÎSýýýÎï­­­jff&ãùU«VY߃UÀªdÓ1ãÿÄQNÀbß"b!'Vˆ.]º¤Ö­[§¦§§Ç###κïJfú—qHK–ÉdÍ*ÿbŸbùNùÖ&«Âë¦x}ƒb±|oD‹aI, Ã0Ë'öëììtÖ½ò/ã C‡ò¼ÀÖÂÂBÕT°â&õ$4´sõT=q„­îN>Ø·ˆ…8¨`q/ÂTŒ5'°ÿ'â 'ÄBô`q/B*X\=qEKÄBNÈ ,+M=X±Ó>k^a†a€E‹ W‚ÄÁ¾EÄBT°èÁJôXó•;ÕÝuë²LüŒÿÓ“AÄBä„,‹ q q°o±‹, Ã0 ÃèÁ¢‚ÅUqâ â 'T°,z°ˆƒXˆƒXˆƒXÈ €E‹«'b!â 'ÄBT°èÁÂ0 Ã0Œ,*X:q q q 9°èÁbüŸXˆƒ}‹8ˆ…8èÁ¢‚ÅUWOÄAÄBNˆƒ =X†a†aVA€µiÓ&²0 Ã0 Ãr™0€…B!TAX!„BB!„€•XIO†a†aX°ò,b!b!b!b!'T°Ø‘ør q°o±€…B!T]°B!„,„B! !„BÀJ³–––T¿êîîv§]ÎÍÍYƒÜ!¼¡¡Áê¸^}õUç¶uuujåÊ•Îÿ===m],‡RNõõõª««K]¿~Ýú}íØ±cª¦¦ÆÊýKþo“Ù˜“ÉÉIµeËgß³õûž–œÈ1jûöín>6oÞ¬nܸaeNÕÀÀ€sü•óÉàà uûW¾çÂrÇUu€544¤FGGÝÇ###ª¯¯Ïšÿßp²5®ÎÎN5>>îìðòE饗Ôã?n],Þ8Ä[ZZ¬Þ×äô[·nÍØÇlŠÅÿݰõ{255¥Ö®]«.^¼˜ºãØÌÌŒ›m±¬[·N¹ßw¹Àjmmµ2'»wïVÎïóóój×®]jÿþýÖÄRȹ°ÜqU`µµµ9_nïISS“uqøwª4ĵbÅŠTÄbsR}“Êâ§Ÿ~š±ÙK.À²)9ðŸ9s&•DZÞÞ^uüøqëb‘Jµ€U¾ïþÊTrôÅ¡Íß÷\ÿ{¹ãª:Àò–ÙEòeñûl,Ûã’+)©Ù‹Tâ^{í5gÈÐÆ8äÛÞÞ®fgg³ö1›b‘ÿ[Nzb>ø Ú³gºwïžuqÈ_ K†päܶm›;œaó÷]†Ôyä+÷-©PK•çèÑ£êæÍ›êäÉ“jÇŽV椶¶6  mü¾G=–;®ª,ÓÕm®+^[â°9®Ó§O«Ç{ÌíÁ²1]ª–1})·ÛÇ×¾ö5§çÇô¿ÚºÉÕªÖÓO?m]rô£Ë‰]zülÿ¾?ûì³ÎwÞÆ}K jãÆT ¼K?Ö'Ÿ|beNd_’á2Ù·d;þ¼ X6ŒϹ°ÜqQÁ¢‚UQ½øâ‹Wæ6Dz°°àôdÈØÆ8r5"Ûü½‘ÿÕÆ+s¬4Äá•4%ë^K¿ïRá½zõªûXE†Ôm̉sõD#¶mnn¶.*X û‚è!‘TM­,ã’fj¹BO[ŽôIÐö8¼û˜Í±Ü¹sG=ðÀÖÅ!ÍÓÞ~ѪU«¬Î‡œÐucµû–î·2ùlÿ¾Ÿ;wΙag[,ùœ ËWÕÖðð°3s@K~r·°l‹K¾ÌAÓcmŠE¦iË,/¹úì³Ïœ,©È¥a_óîc6ÅâÙ)p%'½{÷Z‡üo2^OLôTz÷-©üèþD›¿ï2L¨«ºoû÷ýÒ¥KÎ IݦaS,ùœ Ëë`Y¶VZÖ÷I˺8²^”œÐå*võêÕNS²œÔÓ²¯Ùø½9räˆ3l#Cl_øÂœ“ ­ß™J/Ã7RØ’ah[÷-ùž\¾|Ùêc²ü_òÿI>ĤK÷`Ù˜‡äÆÛiC,…œ?X !„BÈrX!„BB!„€…B!`!„B! !„BÀB!„°B!„€…B!`!„BX!d¯þõ_ÿÕ¹´Ü†Æ$¹õ‘÷¹ª3„°B%,âE! !p/BÀBÄ‹°Bñ"„€…8ˆ!`!„âEX¡´Ç7¾ñ@óë“O>Qÿðÿ V®\©êêêÔÆÕððpÞ¿¦¦&Öx>üðCÕÐÐ`!„,„PrK=ø`–™kË–-jhhH-..ª¥¥%õÞ{ï9¾JJ`M€…°BÖÖŠ+° ÒþýûÕÓO?­zzzœ÷ÏÍ͹ôé§Ÿª5kÖ¸ÃÞsæÌ™åãA§R&ÏG- !`!„¬¬ŽŽµoß>uñâÅ,Ð’ÊÖw¿û]÷ñ›o¾©vìØáÂϻロC¹Þ³zõjuýúuçï:tÀBX¡t–T—€V­ZåT³ºººÔ7œçÕ;wÜ× é¾(?üèǹÞóÅ/~Q½ýöÛ‘c°BBÈJÀòJªK/½ô’zì±Ç\À1Y.ÀÊõžK—.©Í›7«ÏþóêèÑ£BÀBÙXQgš$=R"©8õgV®÷hݼySÕ××X! !d`å#™1¨û¯Ä¤‡jÛ¶mÎsÒ¬>22âÌ0ÉÒ Þ, åzôfIc¼ø¤ÙÀBX¡TÖØØ˜Óè.ýWR¹êîîvgý‰¸¤¯Jžß°aƒ3Œ˜ °r½ç›ßü¦ó7¤ÊuöìÙœ`eb°BBÈ À"^„€…B€…°Bñ"„,„ÀA¼! !„,„°B%Žj3„°B!„*¨ÿ(O¨¥9MÚIEND®B`‚jfree-jfreechart-cb8ff67/src/main/javadoc/overview.html000066400000000000000000000023421463604235500233160ustar00rootroot00000000000000 JFreeChart is a chart library for the Java platform that can generate a wide variety of charts for use in both client and server-side applications. It has built-in support for Swing, plus JavaFX support is provided via an extension library called JFreeChart-FX (https://github.com/jfree/jfreechart-fx). JFreeChart requires Java 8 or later.

The JFreeChart project is hosted at GitHub: https://github.com/jfree/jfreechart.

Here are some sample charts created with JFreeChart and rendered to SVG using JFreeSVG:

BarChartDemo1.svg NormalDistributionDemo2.svg FlowPlotDemo2.svg PieChartDemo1.svg jfree-jfreechart-cb8ff67/src/main/resources/000077500000000000000000000000001463604235500211645ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/resources/org/000077500000000000000000000000001463604235500217535ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/000077500000000000000000000000001463604235500230465ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/000077500000000000000000000000001463604235500241475ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/LocalizationBundle.properties000066400000000000000000000015261463604235500320530ustar00rootroot00000000000000# org.jfree.chart.ChartPanel ResourceBundle properties file # # Changes (from 31-Aug-2003) # -------------------------- # 31-Aug-2003 : Initial version (AL); # 27-Nov-2013 : Added PNG..., PDF..., Save_as and SVG... (DG) # 29-Aug-2014 : Applied patch from Simon04 for bug 1129; # Auto_Range=Auto Range All_Axes=Both Axes Chart=Chart Chart_Properties=Chart Properties Copy=Copy Domain_Axis=Domain Axis FILE_EXISTS_CONFIRM_OVERWRITE=The file already exists, are you sure you want to overwrite it? PNG...=PNG... PNG_Image_Files=PNG Image Files PDF...=PDF... PDF_Files=PDF Files Print...=Print... Properties...=Properties... Range_Axis=Range Axis Save_as=Save as Save_as...=Save as... Save_as_PDF=Save as PDF Save_as_PNG=Save as PNG Save_as_SVG=Save as SVG SVG...=SVG... SVG_Files=SVG Files Zoom_In=Zoom In Zoom_Out=Zoom Out jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/LocalizationBundle_ca.properties000077500000000000000000000007671463604235500325270ustar00rootroot00000000000000# org.jfree.chart.ChartPanel ResourceBundle properties file # # Changes (from 31-Aug-2003) # -------------------------- # 31-Aug-2003 : Initial version (AL); # Auto_Range=Auto Range All_Axes=Els dos eixos Chart=Gr\u00c3\u00a0fic Chart_Properties=Propietats del gr\u00c3\u00a0fic Copy=Copia Domain_Axis=Eix de domini PNG_Image_Files=Arxius d'imatge PNG Print...=Imprimeix... Properties...=Propietats... Save_as=Guarda com Save_as...=Guarda com... Range_Axis=Eix de rang Zoom_In=Apropa Zoom_Out=Allunya jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/LocalizationBundle_cs.properties000066400000000000000000000006371463604235500325420ustar00rootroot00000000000000# org.jfree.chart.ChartPanel ResourceBundle properties file # Auto_Range=Auto rozm\u011br All_Axes=Ob\u011b osy Chart=Chart Chart_Properties=Vlastnosti grafu Copy=Kop\u00edrovat Domain_Axis=Osa oblasti PNG_Image_Files=Obr\u00e1zky PNG Print...=Tisk... Properties...=Vlastnosti... Save_as=Ulo\u017eit jako Save_as...=Ulo\u017eit jako... Range_Axis=Osa rozsahu Zoom_In=P\u0159ibl\u00ed\u017eit Zoom_Out=Odd\u00e1lit jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/LocalizationBundle_de.properties000066400000000000000000000020151463604235500325150ustar00rootroot00000000000000# org.jfree.chart.ChartPanel ResourceBundle properties file - german version # # Changes (from 31-Aug-2003) # -------------------------- # 31-Aug-2003 : Initial version (AL); # 15-Mar-2004 : Revised version (Christian W. Zuckschwerdt); # 29-Aug-2014 : Applied patch from Simon04 for bug 1129; # Auto_Range=Autojustage All_Axes=Beide Achsen Chart=Diagramm Chart_Properties=Diagramm-Eigenschaften Copy=Kopieren Domain_Axis=Horizontale Achse PNG_Image_Files=PNG-Datei (Portable Network Graphics) (*.png) Print...=Drucken... Properties...=Eigenschaften... Save_as=Speichern unter Save_as...=Speichern unter... Range_Axis=Vertikale Achse Zoom_In=Hineinzoomen Zoom_Out=Herauszoomen FILE_EXISTS_CONFIRM_OVERWRITE=Die Datei existiert bereit. Soll die Datei wirklich \u00c3\u00bcberschrieben werden? PNG...=PNG... PDF...=PDF... PDF_Files=PDF-Datei (Portable Document Format) (*.pdf) Save_as_PDF=Speichern als PDF Save_as_PNG=Speichern als PNG Save_as_SVG=Speichern als SVG SVG...=SVG... SVG_Files=SVG-Dateien (Scalable Vector Graphics) (*.svg) jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/LocalizationBundle_es.properties000066400000000000000000000011501463604235500325330ustar00rootroot00000000000000# org.jfree.chart.ChartPanel ResourceBundle properties file - spanish version # # Changes (from 16-Dec-2003) # -------------------------- # 16-Dec-2003 : Initial Version: Complejo Hospitalario Universitario Juan Canalejo # Auto_Range=Escala autom\u00e1tica All_Axes=Todos los ejes Chart=Chart Chart_Properties=Propiedades del gr\u00e1fico Copy=Copiar Domain_Axis=Eje horizontal PNG_Image_Files=Formato PNG (Portable Network Graphics) (*.png) Print...=Imprimir... Properties...=Propiedades... Save_as=Grabar como Save_as...=Grabar como... Range_Axis=Eje vertical Zoom_In=Acercar Zoom_Out=Alejar jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/LocalizationBundle_fr.properties000066400000000000000000000016231463604235500325400ustar00rootroot00000000000000# org.jfree.chart.ChartPanel ResourceBundle properties file - french version # # Changes (from 31-Aug-2003) # -------------------------- # 31-Aug-2003 : Initial version (AL); # Auto_Range=\u00c9chelle automatique All_Axes=Les deux axes Chart=Graphique Chart_Properties=Propri\u00e9t\u00e9s du graphique Copy=Copier Domain_Axis=Axe horizontal FILE_EXISTS_CONFIRM_OVERWRITE=Le fichier existe d\u00e9j\u00e0, voulez-vous le remplacer ? PNG_Image_Files=Format PNG (Portable Network Graphics) (*.png) PDF_Files=Format PDF (Portable Document Format) (*.pdf) Print...=Imprimer... Properties...=Propri\u00e9t\u00e9s... Save_as=Enregistrer sous Save_as...=Enregistrer sous... Save_as_PDF=Enregistrer en PDF Save_as_PNG=Enregistrer en PNG Save_as_SVG=Enregistrer en SVG Range_Axis=Axe vertical SVG_Files=Format SVG (Scalable Vector Graphics) (*.svg) Zoom_In=Zoom avant Zoom_Out=Zoom arri\u00e8re jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/LocalizationBundle_it.properties000066400000000000000000000007001463604235500325400ustar00rootroot00000000000000# org.jfree.chart.ChartPanel ResourceBundle properties file - italian version Auto_Range=Dimensiona Automaticamente All_Axes=Entrambi gli Assi Chart=Chart Chart_Properties=Propriet\u00e0 del Grafico Copy=Copia Domain_Axis=Asse Orizzontale PNG_Image_Files=Immagine PNG Print...=Stampa... Properties...=Propriet\u00e0... Save_as=Salva Come Save_as...=Salva Come... Range_Axis=Asse Verticale Zoom_In=Ingrandisci Zoom_Out=Rimpicciolisci jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/LocalizationBundle_ja.properties000066400000000000000000000011011463604235500325120ustar00rootroot00000000000000 All_Axes=\u4e21\u8ef8 Auto_Range=\u81ea\u52d5\u30b5\u30a4\u30b8\u30f3\u30b0 Chart=\u30c1\u30e3\u30fc\u30c8 Chart_Properties=\u30c1\u30e3\u30fc\u30c8 \u30d7\u30ed\u30d1\u30c6\u30a3 Copy=\u30b3\u30d4\u30fc Domain_Axis=\u5206\u985e\u8ef8 PNG_Image_Files=PNG \u30a4\u30e1\u30fc\u30b8\u30d5\u30a1\u30a4\u30eb Print...=\u5370\u5237... Properties...=\u30d7\u30ed\u30d1\u30c6\u30a3... Range_Axis=\u6570\u5024\u8ef8 Save_as=\u5225\u540d\u3067\u4fdd\u5b58 Save_as...=\u5225\u540d\u3067\u4fdd\u5b58... Zoom_In=\u30ba\u30fc\u30e0\u30a4\u30f3 Zoom_Out=\u30ba\u30fc\u30e0\u30a2\u30a6\u30c8 jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/LocalizationBundle_nl.properties000066400000000000000000000010501463604235500325340ustar00rootroot00000000000000# org.jfree.chart.ChartPanel ResourceBundle properties file # # Changes (from 31-Aug-2003) # -------------------------- # 24-Mar-2003 : Translated into Dutch # 31-Aug-2003 : Initial version (AL); # Auto_Range=Automatisch bereik All_Axes=Beide assen Chart=Chart Chart_Properties=Eigenschappen Copy=Kopi\u00ebren Domain_Axis=Horizontale As PNG_Image_Files=PNG afbeelding Print...=Afdrukken... Properties...=Eigenschappen... Save_as=Opslaan als Save_as...=Opslaan als... Range_Axis=Verticale As Zoom_In=Inzoomen Zoom_Out=Uitzoomen jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/LocalizationBundle_pl.properties000066400000000000000000000010561463604235500325440ustar00rootroot00000000000000# org.jfree.chart.ChartPanel ResourceBundle properties file # # Changes (from 15-Mar-2004) # -------------------------- # 15-Mar-2004 : Initial version (Kuba Duda); # Auto_Range=Automatyczny zakres All_Axes=Obie osie Chart=Chart Chart_Properties=W\u0142a\u015bciwo\u015bci wykresu Copy=Kopiuj Domain_Axis=O\u015b pozioma PNG_Image_Files=Pliki graficzne PNG Print...=Drukuj... Properties...=W\u0142a\u015bciwo\u015bci... Save_as=Zapisz jako Save_as...=Zapisz jako... Range_Axis=O\u015b pionowa Zoom_In=Powi\u0119ksz Zoom_Out=Pomniejsz jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/LocalizationBundle_pt_BR.properties000066400000000000000000000011261463604235500331350ustar00rootroot00000000000000# org.jfree.chart.ChartPanel ResourceBundle properties file - portuguese version # # Changes (from 24-May-2007) # -------------------------- # 24-May-2007 : Initial version (Leonardo Alves Machado); # Auto_Range=Escala autom\u00e1tica All_Axes=Todos os eixos Chart=Chart Chart_Properties=Propriedades do gr\u00e1fico Copy=Copiar Domain_Axis=Eixo horizontal PNG_Image_Files=Formato PNG (Portable Network Graphics) (*.png) Print...=Imprimir... Properties...=Propriedades... Save_as=Salvar como Save_as...=Salvar como... Range_Axis=Eixo vertical Zoom_In=Ampliar Zoom_Out=Reduzir jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/LocalizationBundle_pt_PT.properties000066400000000000000000000011051463604235500331520ustar00rootroot00000000000000# org.jfree.chart.ChartPanel ResourceBundle properties file - portuguese version # # Changes (from 09-Set-2003) # -------------------------- # 09-Set-2003 : Initial version (ER); # Auto_Range=Escala autom\u00e1tica All_Axes=Todos os eixos Chart=Chart Chart_Properties=Propriedades do gr\u00e1fico Copy=Copiar Domain_Axis=Eixo horizontal PNG_Image_Files=Formato PNG (Portable Network Graphics) (*.png) Print...=Imprimir... Properties...=Propriedades... Save_as=Gravar como Save_as...=Gravar como... Range_Axis=Eixo vertical Zoom_In=Aproximar Zoom_Out=Afastar jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/LocalizationBundle_ru.properties000066400000000000000000000022331463604235500325550ustar00rootroot00000000000000# org.jfree.chart.ChartPanel ResourceBundle properties file # Auto_Range=\u0410\u0432\u0442\u043e\u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 All_Axes=\u041f\u043e \u0432\u0441\u0435\u043c \u043e\u0441\u044f\u043c Chart=Chart Chart_Properties=\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0433\u0440\u0430\u0444\u0438\u043a\u0430 Copy=\u041a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c Domain_Axis=\u041f\u043e \u0433\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u044c\u043d\u043e\u0439 \u043e\u0441\u0438 PNG_Image_Files=PNG \u0444\u0430\u0439\u043b Print...=\u041f\u0435\u0447\u0430\u0442\u044c... Properties...=\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438... Save_as=\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043a\u0430\u043a Save_as...=\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043a\u0430\u043a... Range_Axis=\u041f\u043e \u0432\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u044c\u043d\u043e\u0439 \u043e\u0441\u0438 Zoom_In=\u041f\u0440\u0438\u0431\u043b\u0438\u0437\u0438\u0442\u044c Zoom_Out=\u041e\u0442\u0434\u0430\u043b\u0438\u0442\u044c jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/LocalizationBundle_zh_CN.properties000066400000000000000000000011211463604235500331230ustar00rootroot00000000000000# org.jfree.chart.ChartPanel ResourceBundle properties file # # Changes # ------- # 29-Jun-2005 : Initial version, see: http://www.jfree.org/phpBB2/viewtopic.php?t=13495; # Auto_Range=\u81ea\u52a8\u8c03\u6574 All_Axes=\u6240\u6709\u8f74 Chart=Chart Chart_Properties=\u56fe\u8868\u5c5e\u6027 Copy=\u590d\u5236 Domain_Axis=\u6c34\u5e73\u8f74 PNG_Image_Files=PNG \u683c\u5f0f\u7684\u56fe\u50cf Print...=\u6253\u5370 Properties...=\u5c5e\u6027 Save_as=\u53e6\u5b58\u4e3a Save_as...=\u53e6\u5b58\u4e3a Range_Axis=\u5782\u76f4\u8f74 Zoom_In=\u653e\u5927 Zoom_Out=\u7f29\u5c0f jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/LocalizationBundle_zh_TW.properties000066400000000000000000000010611463604235500331600ustar00rootroot00000000000000# org.jfree.chart.ChartPanel ResourceBundle properties file # # Changes (from 31-Aug-2003) # -------------------------- # 31-Aug-2003 : Initial version (AL); # Auto_Range=\u81ea\u52d5\u8abf\u6574 All_Axes=\u6240\u6709\u8ef8 Chart=Chart Chart_Properties=\u5716\u8868\u5167\u5bb9 Copy=\u8907\u88fd Domain_Axis=\u6a6b\u8ef8 PNG_Image_Files=PNG\u5716\u6a94 Print...=\u5217\u5370 Properties...=\u5167\u5bb9 Save_as=\u53e6\u5b58\u65b0\u6a94 Save_as...=\u53e6\u5b58\u65b0\u6a94 Range_Axis=\u7e31\u8ef8 Zoom_In=\u653e\u5927 Zoom_Out=\u7e2e\u5c0f jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/editor/000077500000000000000000000000001463604235500254355ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/editor/LocalizationBundle.properties000066400000000000000000000036371463604235500333460ustar00rootroot00000000000000# org.jfree.chart.ui.ui ResourceBundle properties file # # Changes (from 31-Aug-2003) # -------------------------- # 31-Aug-2003 : Initial version (AL); # \:=: AngleOffset=Angle Offset Appearance=Appearance Auto-adjust_range=Auto-adjust range: Auto-TickUnit_Selection=Auto-Selection of TickUnit Background=Background: Background_Color=Background Color Background_paint=Background paint: Color=Color: Color_Bar=Color Bar Domain_Axis=Domain Axis Draw_anti-aliased=Draw anti-aliased Draw_lines=Draw Lines: Draw_shapes=Draw Shapes: Edit...=Edit... Edit_Insets=Edit Insets Font=Font: Font_Selection=Font Selection General=General: General1=General Grid_Color=Grid Color Insets=Insets: Invert_Palette=Invert Palette: Label=Label: Label_Color=Label Color Label_Insets=Label Insets: Legend=Legend Manual_TickUnit_value=TickUnit value Maximum_range_value=Maximum range value: Minimum_range_value=Minimum range value: No_editor_implemented=No editor implemented Orientation=Orientation: Other=Other Outline=Outline: Outline_Color=Outline Color Outline_stroke=Outline stroke: Outline_Paint=Outline paint: Paint=Paint: Palette_Selection=Palette Selection Palette=Palette: Pen_Stroke_Selection=Pen/Stroke Selection Plot=Plot Range=Range Range_Axis=Range Axis Select...=Select... Series_Label_Color=Series Label Color Series_label_font=Series label font: Series_label_paint=Series label paint: Series_Outline_Paint=Series Outline Paint: Series_Outline_Stroke=Series Outline Stroke: Series_Paint=Series Paint: Series_Stroke=Series Stroke: Show_Legend=Show Legend: Show_tick_labels=Show tick labels Show_tick_marks=Show tick marks Show_Title=Show Title: Set_palette...=Set palette... Step_Palette=Step Palette: Stroke_Selection=Stroke Selection Text=Text: Ticks=Ticks Tick_label_font=Tick label font: Tick_Label_Insets=Tick Label Insets: TickUnit=TickUnit Title=Title Title_Color=Title Color jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/editor/LocalizationBundle_ca.properties000077500000000000000000000036651463604235500340150ustar00rootroot00000000000000# org.jfree.chart.ui.ui ResourceBundle properties file # # Changes (from 31-Aug-2003) # -------------------------- # 31-Aug-2003 : Initial version (AL); # \:=: Appearance=Aparença Auto-adjust_range=Autoajustar el rang: Background=Fons: Background_Color=Color de fons Background_paint=Pintura de fons: Color=Color: Color_Bar=Barra de colors Domain_Axis=Eix de Domini Draw_anti-aliased=Dibuxa anti-alias Draw_lines=Dibuixa Línies: Draw_shapes=Dibuixa Formes: Edit...=Edita... Edit_Insets=Edita Insercions Font=Font: Font_Selection=Selecció de Font General=General: Grid_Color=Color de Graella Insets=Insercions: Invert_Palette=Invertir la Paleta: Label=Etiqueta: Label_Color=Color d'etiqueta Label_Insets=Inserir Etiquetes: Legend=Llegenda Maximum_range_value=Màxim valor del rang: Minimum_range_value=Mínim valor del rang: No_editor_implemented=No hi ha editor implementat Orientation=Orientació: Other=Altre Outline=Esbós: Outline_Color=Color de l'esbós Outline_stroke=Traç d'esbós: Outline_Paint=Pintura d'esbós: Paint=Pinta: Palette_Selection=Selecciona Paleta Palette=Paleta: Pen_Stroke_Selection=Selecció de Llapis/Traç Plot=Diagrama Range=Rang Range_Axis=Eix del Rang Select...=Selecciona... Series_Label_Color=Color de l'etiqueta de la Sèrie: Series_label_font=Font de l'etiqueta de la Sèrie: Series_label_paint=Pintura de l'etiqueta de la Sèrie: Series_Outline_Paint=Esquema de la Pintura de la Sèrie: Series_Outline_Stroke=Esquema del Traç de la Sèrie: Series_Paint=Series de Pintura: Series_Stroke=Series de Traç: Show_Legend=Mostra la Llegenda: Show_tick_labels=Mostra marcadors d'etiquetes Show_tick_marks=Mostra marcadors de marques Show_Title=Mostra el Títol: Set_palette...=Conjunt de paletes... Step_Palette=Paleta de Passos: Stroke_Selection=Selecció de traços Text=Text: Ticks=Marcadors Tick_label_font=Font de l'etiqueta del marcador: Tick_Label_Insets=Insercions de Marques d'Etiquetes: Title=Títol Title_Color=Color del Títol jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/editor/LocalizationBundle_cs.properties000066400000000000000000000041261463604235500340250ustar00rootroot00000000000000# org.jfree.chart.ui.ui ResourceBundle properties file # # Changes (from 31-Aug-2003) # -------------------------- # 31-Aug-2003 : Initial version (AL); # 20-Mar-2010 : Czech translation; # \:=: Appearance=Vzhled Auto-adjust_range=Automatick\u00fd rozsah: Background=Pozad\u00ed: Background_Color=Barva pozad\u00ed Background_paint=V\u00fdpl\u0148 pozad\u00ed: Color=Barva: Color_Bar=Barevn\u00fd pruh Domain_Axis=Osa oblasti Draw_anti-aliased=Pou\u017e\u00edt vyhlazov\u00e1n\u00ed Draw_lines=Kreslit \u010d\u00e1ry: Draw_shapes=Kreslit tvary: Edit...=Upravit... Edit_Insets=Upravit okraje Font=P\u00edsmo: Font_Selection=V\u00fdb\u011br p\u00edsma General=Obecn\u00e9: Grid_Color=Barva m\u0159\u00ed\u017eky Insets=Okraje: Invert_Palette=Invertovat paletu: Label=Popisek: Label_Color=Barva popisku Label_Insets=Okraje popisku: Legend=Legenda Maximum_range_value=Maximum rozsahu: Minimum_range_value=Minimum rozsahu: No_editor_implemented=Editor nen\u00ed implementovan\u00fd Orientation=Orientatace: Other=Ostatn\u00ed Outline=R\u00e1me\u010dek: Outline_Color=Barva r\u00e1me\u010dku Outline_stroke=Tlou\u0161\u0165ka \u010d\u00e1ry: Outline_Paint=V\u00fdpl\u0148 r\u00e1me\u010dku: Paint=V\u00fdpl\u0148: Palette_Selection=V\u00fdb\u011br z palety Palette=Paleta: Pen_Stroke_Selection=V\u00fdb\u011br pera/\u010d\u00e1ry Plot=Graf Range=Rozsah Range_Axis=Osa rozsahu Select...=V\u00fdb\u011br... Series_Label_Color=Barva popisku dat Series_label_font=P\u00edsmo popisku dat: Series_label_paint=V\u00fdpl\u0148 popisku dat: Series_Outline_Paint=V\u00fdpl\u0148 r\u00e1me\u010dku dat: Series_Outline_Stroke=V\u00fdpl\u0148 r\u00e1me\u010dku dat: Series_Paint=V\u00fdpl\u0148 dat: Series_Stroke=\u010c\u00e1ra dat: Show_Legend=Zobrazit legendu: Show_tick_labels=Zobrazit popisky \u010dasu Show_tick_marks=Zobrazit zna\u010dky \u010dasu Show_Title=Zobrazit nadpis: Set_palette...=Nastavit paletu... Step_Palette=Krok palety: Stroke_Selection=V\u00fdber \u010d\u00e1ry Text=Text: Ticks=\u010cas Tick_label_font=P\u00edsmo popisk\u016f \u010dasu: Tick_Label_Insets=Okraje popisk\u016f \u010dasu: Title=Nadpis Title_Color=Barva nadpisu jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/editor/LocalizationBundle_de.properties000066400000000000000000000043601463604235500340100ustar00rootroot00000000000000# org.jfree.chart.ui.ui ResourceBundle properties file - german version # # Changes (from 31-Aug-2003) # -------------------------- # 31-Aug-2003 : Initial version (AL); # 15-Mar-2004 : Revised version (Christian W. Zuckschwerdt); # \:=: AngleOffset=Winkel Offset Appearance=Darstellung Auto-adjust_range=Autojustage des Wertebereichs: Auto-TickUnit_Selection=Automatische Auswahl der Schrittweite Background=Hintergrund: Background_Color=Hintergrundfarbe Background_paint=Hintergrundfarbe: Color=Farbe: Color_Bar=Farbleiste Domain_Axis=Rubrikenachse Draw_anti-aliased=Zeichnung gl\u00e4tten Draw_lines=Zeichne Linien: Draw_shapes=Zeichne Formen: Edit...=Bearbeiten... Edit_Insets=Seitenr\u00e4nder bearbeiten Font=Schrift: Font_Selection=Schriftwahl General=Allgemein: General1=Allgemein Grid_Color=Gitterfarbe Insets=R\u00e4nder: Invert_Palette=Palette invertieren: Label=Beschriftung: Label_Color=Beschriftungsfarbe Label_Insets=Beschriftungsr\u00e4nder: Legend=Legende Manual_TickUnit_value=manuelle Schrittweite Maximum_range_value=Maximalwert: Minimum_range_value=Minimalwert: No_editor_implemented=Kein Editor verf\u00fcgbar Orientation=Orientierung: Other=Sonstiges Outline=Rahmenlinien: Outline_Color=Rahmenfarbe Outline_stroke=Rahmenart: Outline_Paint=Rahmenfarbe: Paint=Farbe: Palette_Selection=Farbauswahl Palette=Palette: Pen_Stroke_Selection=Linienst\u00e4rke w\u00e4hlen Plot=Diagramm Range=Wertebereich Range_Axis=Werteachse Select...=Ausw\u00e4hlen... Series_Label_Color=Farbe der Datenreihenbeschriftung Series_label_font=Schrift der Datenreihenbeschriftung: Series_label_paint=Farbe der Datenreihenbeschriftung: Series_Outline_Paint=Farbe der Datenreihenrahmen: Series_Outline_Stroke=Art der Datenreihenrahmen: Series_Paint=Datenreihenfarbe: Series_Stroke=Datenreihenrahmen: Show_Legend=Legende anzeigen: Show_tick_labels=Beschriftungen zeichnen Show_tick_marks=Markierungen zeichnen Show_Title=\u00dcberschrift anzeigen: Set_palette...=Palette w\u00e4hlen... Step_Palette=Step Palette: Stroke_Selection=Linienauswahl Text=Text: Ticks=Markierungen TickUnit=Schrittweite Tick_label_font=Markierungsschift: Tick_Label_Insets=Markierungsr\u00e4nder: Title=\u00dcberschrift Title_Color=Titelfarbe jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/editor/LocalizationBundle_es.properties000066400000000000000000000052641463604235500340330ustar00rootroot00000000000000# org.jfree.chart.ui.ui ResourceBundle properties file - spanish version # # Changes (from 16-Dec-2003) # -------------------------- # 16-Dec-2003 : Initial Version: Complejo Hospitalario Universitario Juan Canalejo # 14-Oct-2004 : Revised by: Leopoldo Federico P\u00ef\u00bf\u00bdrtile (Grupo de Procesamiento Digital de Im\u00ef\u00bf\u00bdgenes) # Universidad Tecnol\u00ef\u00bf\u00bdgica Nacional - Facultad Regional Resistencia, Argentina \:=\ : AngleOffset=Angle Offset Appearance=Apariencia Auto-adjust_range=Ejes auto-ajustables : Auto-TickUnit_Selection=Auto-Selection of TickUnit Background=Fondo : Background_Color=Color de fondo Background_paint=Color de fondo : Color=Color : Color_Bar=Color de la barra Domain_Axis=Eje de las abcisas Draw_anti-aliased=Anti-aliasing Draw_lines=Dibujar l\u00edneas: Draw_shapes=Draw figuras: Edit...=Editar... Edit_Insets=Regular la posici\u00f3n Font=Fuente : Font_Selection=Selecci\u00f3n de fuente General=General : General1=General Grid_Color=Color de la rejilla Insets=Posici\u00f3n : Invert_Palette=Invertir la paleta : Label=Etiqueta : Label_Color=Color de la etiqueta Label_Insets=Posici\u00f3n de la etiqueta : Legend=Leyenda Manual_TickUnit_value=TickUnit value Maximum_range_value=Valor m\u00e3ximo del eje : Minimum_range_value=Valor m\u00ednimo del eje : No_editor_implemented=Editor no disponble Orientation=Orientaci\u00f3n: Other=Otro Outline=Borde : Outline_Paint=Color del borde : Outline_Color=Color del borde Outline_stroke=Estilo del trazo del borde : Paint=Color : Palette=Paleta : Palette_Selection=Selecci\u00f3n de la paleta Pen_Stroke_Selection=Selecci\u00f3n del trazo Plot=Trazo Range=Intervalo Range_Axis=Eje de ordenadas Select...=Seleccionar... Series_Label_Color=Color de la etiqueta de las series Series_label_font=Fuente de la etiqueta de las series : Series_label_paint=Color de la etiqueta de las series : Series_Outline_Paint=Color del borde de las series : Series_Outline_Stroke=Estilo del trazo del borde : Series_Paint=Color de las series : Series_Stroke=Estilo del trazo de las series : Show_Legend=Mostrar leyenda: Show_tick_labels=Visualizar etiquetas de graduaci\u00f3n de escala Show_tick_marks=Visualizar marcas de graduaci\u00f3n de escala Show_Title=Mostrar t\u00edtulo Set_palette...=Escoja la paleta... Step_Palette=Paso de paleta : Stroke_Selection=Selecci\u00f3n del trazo Text=Texto : Tick_label_font=Fuente de las etiquetas de graduaci\u00f3n de escala : Tick_Label_Insets=Posici\u00f3n de las etiquetas de graduaci\u00f3n de escala : Ticks=Etiquetas de graduaci\u00f3n de escala TickUnit=TickUnit Title=T\u00edtulo Title_Color=Color del t\u00edtulo jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/editor/LocalizationBundle_fr.properties000066400000000000000000000045051463604235500340300ustar00rootroot00000000000000# org.jfree.chart.ui.ui ResourceBundle properties file - french version # # Changes (from 31-Aug-2003) # -------------------------- # 31-Aug-2003 : Initial version (AL); # \:=\ : AngleOffset=Angle Offset Appearance=G\u00E9n\u00E9ral Auto-adjust_range=R\u00E9glage automatique de l'\u00E9chelle : Auto-TickUnit_Selection=Auto-Selection of TickUnit Background=Fond : Background_Color=Couleur du fond Background_paint=Couleur du fond : Color=Couleur : Color_Bar=Couleur de la barre Domain_Axis=Axe des abcisses Draw_anti-aliased=Anti-aliasing Edit...=Edition... Edit_Insets=R\u00E9gler la position Font=Police : Font_Selection=S\u00E9lection de la police General=G\u00E9n\u00E9ral : General=G\u00E9n\u00E9ral Grid_Color=Couleur de la grille Insets=Position : Invert_Palette=Inversion de la palette : Label=Etiquette : Label_Color=Couleur de l'\u00E9tiquette Label_Insets=Position de l'\u00E9tiquette : Legend=L\u00E9gende Manual_TickUnit_value=TickUnit value Maximum_range_value=Valeur maximum de l'axe : Minimum_range_value=Valeur minimum de l'axe : No_editor_implemented=Aucun \u00E9diteur disponible Other=Autre Outline=Bordure : Outline_Paint=Couleur de la bordure : Outline_Color=Outline Color Outline_stroke=Bordure : Paint=Couleur : Palette=Palette : Palette_Selection=Selection de la palette Pen_Stroke_Selection=S\u00E9lection du style de trait Plot=Trac\u00E9 Range=Echelle Range_Axis=Axe des ordonn\u00E9es Select...=S\u00E9lectionner... Series_Label_Color=S\u00E9lection de la couleur de l'\u00E9tiquette Series_label_font=Police de l'\u00E9tiquette de s\u00E9rie : Series_label_paint=Couleur de l'\u00E9tiquette de s\u00E9rie : Series_Outline_Paint=Couleur de bordure de la s\u00E9rie : Series_Outline_Stroke=Style du trait de bordure de la s\u00E9rie : Series_Paint=Couleur de la s\u00E9rie : Series_Stroke=Style du trait de la s\u00E9rie : Show_tick_labels=Afficher les \u00E9tiquettes de graduation Show_tick_marks=Afficher les rep\u00E8res de graduation Set_palette...=Choix de palette... Step_Palette=Changement de palette : Stroke_Selection=S\u00E9lection du style de trait Text=Texte : Tick_label_font=Police des \u00E9tiquettes de graduation : Tick_Label_Insets=Position des \u00E9tiquettes de graduation : Ticks=Etiquettes de graduation TickUnit=TickUnit Title_Color=Couleur du titre jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/editor/LocalizationBundle_ja.properties000066400000000000000000000056151463604235500340160ustar00rootroot00000000000000 \:=: Appearance=\u5916\u89b3 Auto-adjust_range=\u81ea\u52d5\u8abf\u6574\u7bc4\u56f2: Background=\u80cc\u666f: Background_Color=\u80cc\u666f\u8272 Background_paint=\u80cc\u666f\u306e\u5857\u88c5: Color=\u8272: Color_Bar=\u30ab\u30e9\u30fc\u30d0\u30fc Domain_Axis=\u5206\u985e\u8ef8 Draw_anti-aliased=\u30c9\u30ed\u30fc\u30a2\u30f3\u30c1\u30a8\u30a4\u30ea\u30a2\u30b9 Draw_lines=\u30c9\u30ed\u30fc\u30e9\u30a4\u30f3: Draw_shapes=\u63cf\u753b\u56f3\u5f62: Edit...=\u7de8\u96c6... Edit_Insets=\u30a4\u30f3\u30bb\u30c3\u30c8\u306e\u7de8\u96c6 Font=\u30d5\u30a9\u30f3\u30c8: Font_Selection=\u30d5\u30a9\u30f3\u30c8\u306e\u9078\u629e General=\u4e00\u822c: Grid_Color=\u30b0\u30ea\u30c3\u30c9\u306e\u8272 Insets=\u633f\u5165 Invert_Palette=\u30d1\u30ec\u30c3\u30c8\u3092\u53cd\u8ee2: Label=\u30e9\u30d9\u30eb: Label_Color=\u30e9\u30d9\u30eb\u306e\u8272 Label_Insets=\u30e9\u30d9\u30eb\u306e\u30a4\u30f3\u30bb\u30c3\u30c8: Legend=\u51e1\u4f8b Maximum_range_value=\u7bc4\u56f2\u306e\u6700\u5927\u5024: Minimum_range_value=\u7bc4\u56f2\u306e\u6700\u5c0f\u5024: No_editor_implemented=\u30a8\u30c7\u30a3\u30bf\u304c\u5b9f\u88c5\u3055\u308c\u3066\u3044\u307e\u305b\u3093 Orientation=\u65b9\u5411: Other=\u305d\u306e\u4ed6 Outline=\u6982\u8981: Outline_Color=\u30a2\u30a6\u30c8\u30e9\u30a4\u30f3\u306e\u8272 Outline_Paint=\u30da\u30a4\u30f3\u30c8\u306e\u30a2\u30a6\u30c8\u30e9\u30a4\u30f3: Outline_stroke=\u5de5\u7a0b\u306e\u6982\u8981: Paint=\u30da\u30a4\u30f3\u30c8: Palette=\u30d1\u30ec\u30c3\u30c8: Palette_Selection=\u30d1\u30ec\u30c3\u30c8\u306e\u9078\u629e Pen_Stroke_Selection=\u30da\u30f3/\u5de5\u7a0b\u306e\u9078\u629e Plot=\u4f5c\u56f3 Range=\u7bc4\u56f2 Range_Axis=\u6570\u5024\u8ef8 Select...=\u9078\u629e... Series_Label_Color=\u30b7\u30ea\u30fc\u30ba\u306e\u30e9\u30d9\u30eb\u8272 Series_Outline_Paint=\u30b7\u30ea\u30fc\u30ba\u306e\u6982\u8981\u30da\u30a4\u30f3\u30c8: Series_Outline_Stroke=\u30b7\u30ea\u30fc\u30ba\u306e\u6982\u8981\u5de5\u7a0b: Series_Paint=\u8a69\u30ea\u30fc\u30b9\u30da\u30a4\u30f3\u30c8: Series_Stroke=\u30b7\u30ea\u30fc\u30ba\u306e\u5de5\u7a0b: Series_label_font=\u30b7\u30ea\u30fc\u30ba\u306e\u30e9\u30d9\u30eb\u30d5\u30a9\u30f3\u30c8: Series_label_paint=\u30b7\u30ea\u30fc\u30ba\u306e\u30e9\u30d9\u30eb\u30da\u30a4\u30f3\u30c8: Set_palette...=\u30d1\u30ec\u30c3\u30c8\u306e\u8a2d\u5b9a... Show_Legend=\u51e1\u4f8b: Show_Title=\u30bf\u30a4\u30c8\u30eb\u306e\u8868\u793a: Show_tick_labels=\u76ee\u76db\u30e9\u30d9\u30eb\u3092\u8868\u793a Show_tick_marks=\u76ee\u76db\u3092\u8868\u793a Step_Palette=\u30d1\u30ec\u30c3\u30c8\u306e\u30b9\u30c6\u30c3\u30d7: Stroke_Selection=\u6982\u8981\u306e\u9078\u629e Text=\u30c6\u30ad\u30b9\u30c8: Tick_Label_Insets=\u30e9\u30d9\u30eb\u306e\u30a4\u30f3\u30bb\u30c3\u30c8\u3092\u30c1\u30a7\u30c3\u30af: Tick_label_font=\u30e9\u30d9\u30eb\u306e\u30d5\u30a9\u30f3\u30c8\u3092\u30c1\u30a7\u30c3\u30af: Ticks=\u523b\u307f Title=\u30bf\u30a4\u30c8\u30eb Title_Color=\u30bf\u30a4\u30c8\u30eb\u306e\u8272 jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/editor/LocalizationBundle_nl.properties000066400000000000000000000040321463604235500340250ustar00rootroot00000000000000# org.jfree.chart.ui.ui ResourceBundle properties file # # Changes (from 31-Aug-2003) # -------------------------- # 24-Mar-2003 : Translated into Dutch # 31-Aug-2003 : Initial version (AL); # \:=: AngleOffset=Angle Offset Appearance=Uiterlijk Auto-adjust_range=Bereik automatisch bepalen Auto-TickUnit_Selection=Auto-Selection of TickUnit Background=Achtergrond Background_Color=Achtergrondkleur # "Paint" translated sounds a bit silly IMHO. Since at present only single, # opaque colors are selectable anyway, lets just call it that -- color. Background_paint=Achtergrondkleur: Color=Kleur: Color_Bar=Kleurbalk Domain_Axis=Domein as Draw_anti-aliased=Kartelrandjes maskeren (anti-aliasing) Edit...=Wijzig... Edit_Insets=Wijzig marges Font=Lettertype: Font_Selection=Lettertype keuze General=Algemeen: General1=Algemeen Grid_Color=Rasterkleur Insets=Marges: Invert_Palette=Palet inverteren: Label=Label: Label_Color=Labelkleur Label_Insets=Labelmarges: Legend=Legenda Manual_TickUnit_value=TickUnit value Maximum_range_value=Hoogste waarde: Minimum_range_value=Laagste waarde: No_editor_implemented=Geen editor beschikbaar Other=Overig Outline=Rand: Outline_Color=Randkleur Outline_stroke=Rand stijl: Outline_Paint=Randkleur: Paint=Kleur: Palette_Selection=Paletkleuze Palette=Palet: Pen_Stroke_Selection=Pen/Stijl Keuze Plot=Diagram Range=Bereik Range_Axis=Gegevens as Select...=Selecteer... Series_Label_Color=Kleur serienaam Series_label_font=Lettertype serienaam: Series_label_paint=Kleur serienaam: Series_Outline_Paint=Kleur serie-rand: Series_Outline_Stroke=Stijl serie-rand: Series_Paint=Kleur v.d. serie: Series_Stroke=Stijl v.d. serie: Show_tick_labels=Toon waarde-labels Show_tick_marks=Toon waarde-streepjes Show_Title=Toon Titel: Set_palette...=Kies palet... Step_Palette=Stap palet: Stroke_Selection=Stijlkeuze Text=Tekst: Ticks=Waarde-streepjes TickUnit=TickUnit Tick_label_font=Lettertype waarde-labes: Tick_Label_Insets=Waarde-label marges: Title=Titel Title_Color=Kleur titel jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/editor/LocalizationBundle_pl.properties000066400000000000000000000040211463604235500340250ustar00rootroot00000000000000# org.jfree.chart.ui.ui ResourceBundle properties file # # Changes (from 15-Mar-2004) # -------------------------- # 15-Mar-2004 : Initial version (Kuba Duda); # \:=: AngleOffset=Angle Offset Appearance=Wygl\u0105d Auto-adjust_range=Automatycznie dostosuj zakres: Auto-TickUnit_Selection=Auto-Selection of TickUnit Background=T\u0142o: Background_Color=Kolor t\u0142a Background_paint=Wype\u0142nienie t\u0142a: Color=Kolor: Color_Bar=Pasek kolor\u00f3w Domain_Axis=O\u015b domeny Draw_anti-aliased=Wyg\u0142adzaj kraw\u0119dzie Edit...=Edytuj... Edit_Insets=Edytuj wstawki Font=Czcionka: Font_Selection=Wyb\u00f3r czcionki General=Og\u00f3lne: Grid_Color=Kolor siatki Insets=Wstawki: Invert_Palette=Odwr\u00f3\u0107 palet\u0119: Label=Etykieta: Label_Color=Kolor etykiet Label_Insets=Wstawki etykiet: Legend=Legenda Manual_TickUnit_value=TickUnit value Maximum_range_value=Maksymalna warto\u015b\u0107 zakresu: Minimum_range_value=Minimalna warto\u015b\u0107 zakresu: No_editor_implemented=Edytor nie zaimplementowany Other=Inne Outline=Obrys: Outline_Color=Kolor konturu Outline_stroke=Obrys konturu: Outline_Paint=wype\u0142nienie konturu: Paint=Wype\u0142nienie: Palette_Selection=Wyb\u00f3r palety Palette=Paleta: Pen_Stroke_Selection=Wyb\u00f3r pi\u00f3ra/obrysu Plot=Wykres Range=Zakres Range_Axis=O\u015b zakresu Select...=Wybierz... Series_Label_Color=Kolor etykiety serii Series_label_font=Czcionka etykiety serii: Series_label_paint=Wype\u0142nienie etykiety serii: Series_Outline_Paint=Wype\u0142nienie obrysu serii: Series_Outline_Stroke=Kolor obrysu serii: Series_Paint=Wype\u0142nienie serii: Series_Stroke=Kolor serii: Show_tick_labels=Pokazuj etykiety skali Show_tick_marks=Pokazuj znaczniki skali Set_palette...=Wybierz palet\u0119... Step_Palette=Paleta krokowa: Stroke_Selection=Wyb\u00f3r obrysu Text=Tekst: Ticks=Podzia\u0142ka TickUnit=TickUnit Tick_label_font=Czcionka etykiet podzia\u0142ki: Tick_Label_Insets=Wstawki etykiet podzia\u0142ki: Title_Color=Kolor tytu\u0142u LocalizationBundle_pt_BR.properties000066400000000000000000000045021463604235500343450ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/editor# org.jfree.chart.ChartPanel ResourceBundle properties file - portuguese version # # Changes (from 24-May-2007) # -------------------------- # 24-May-2007 : Initial version (Leonardo Alves Machado) # \:=\ : AngleOffset=Angle Offset Appearance=Geral Auto-adjust_range=Eixos auto-ajust\u00E1veis : Auto-TickUnit_Selection=Auto-Selection of TickUnit Background=Fundo : Background_Color=Cor de fundo Background_paint=Cor de fundo : Color=Cor : Color_Bar=Cor da barra Domain_Axis=Eixo das abcissas Draw_anti-aliased=Anti-aliasing Edit...=Editar... Edit_Insets=Regular a posi\u00E7\u00E3o Font=Fonte : Font_Selection=Sele\u00E7\u00E3o da fonte General=Geral : General1=Geral Grid_Color=Cor da grade Insets=Posi\u00E7\u00E3o : Invert_Palette=Inverter o palete : Label=R\u00F3tulo : Label_Color=Cor do r\u00F3tulo Label_Insets=Posi\u00E7\u00E3o do r\u00F3tulo : Legend=Legenda Manual_TickUnit_value=TickUnit value Maximum_range_value=Valor m\u00E1ximo do eixo : Minimum_range_value=Valor m\u00EDnimo do eixo : No_editor_implemented=Editor n\u00E3o dispon\u00EDvel Other=Outro Outline=Borda : Outline_Paint=Cor da borda : Outline_Color=Cor da borda Outline_stroke=Estilo do tra\u00E7o da borda : Paint=Cor : Palette=Palete : Palette_Selection=Sele\u00E7\u00E3o do palete Pen_Stroke_Selection=Sele\u00E7\u00E3o de tra\u00E7o Plot=Tra\u00E7o Range=Intervalo Range_Axis=Eixo das ordenadas Select...=Selecionar... Series_Label_Color=Cor do r\u00F3tulo das s\u00E9ries Series_label_font=Fonte do r\u00F3tulo das s\u00E9ries : Series_label_paint=Cor do r\u00F3tulo das s\u00E9ries : Series_Outline_Paint=Cor da borda das s\u00E9ries : Series_Outline_Stroke=Estilo do tra\u00E7o da borda : Series_Paint=Cor da s\u00E9ries : Series_Stroke=Estilo do tra\u00E7o das s\u00E9ries : Show_tick_labels=Visualizar r\u00F3tulos de gradua\u00E7\u00E3o de escala Show_tick_marks=Visualizar marcas de gradua\u00E7\u00E3o de escala Set_palette...=Escolha do palete... Step_Palette=Espaçamento do palete : Stroke_Selection=Sele\u00E7\u00E3o do tra\u00E7o Text=Texto : Tick_label_font=Fonte dos r\u00F3tulos de gradua\u00E7\u00E3o de escala : Tick_Label_Insets=Posi\u00E7\u00E3o dos r\u00F3tulos de gradua\u00E7\u00E3o de escala : Ticks=R\u00F3tulos de gradua\u00E7\u00E3o de escala TickUnit=TickUnit Title_Color=Cor do t\u00EDtulo LocalizationBundle_pt_PT.properties000066400000000000000000000044431463604235500343710ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/editor# org.jfree.chart.ChartPanel ResourceBundle properties file - portuguese version # # Changes (from 09-Set-2003) # -------------------------- # 09-Set-2003 : Initial version (Eduardo Ramalho); # \:=\ : Appearance=Geral AngleOffset=Angle Offset Auto-adjust_range=Eixos auto-ajust\u00E1veis : Auto-TickUnit_Selection=Auto-Selection of TickUnit Background=Fundo : Background_Color=Cor de fundo Background_paint=Cor de fundo : Color=Cor : Color_Bar=Cor da barra Domain_Axis=Eixo das abcissas Draw_anti-aliased=Anti-aliasing Edit...=Editar... Edit_Insets=Regular a posi\u00E7\u00E3o Font=Fonte : Font_Selection=Selec\u00E7\u00E3o da fonte General=Geral : General1=Geral Grid_Color=Cor da grelha Insets=Posi\u00E7\u00E3o : Invert_Palette=Inverter a paleta : Label=Etiqueta : Label_Color=Cor da etiqueta Label_Insets=Posi\u00E7\u00E3o da etiqueta : Legend=Legenda Manual_TickUnit_value=TickUnit value Maximum_range_value=Valor m\u00E1ximo do eixo : Minimum_range_value=Valor m\u00EDnimo do eixo : No_editor_implemented=Editor n\u00E3o dispon\u00EDvel Other=Outro Outline=Borda : Outline_Paint=Cor da borda : Outline_Color=Cor da borda Outline_stroke=Estilo do tra\u00E7o da borda : Paint=Cor : Palette=Palete : Palette_Selection=Selec\u00E7\u00E3o da palete Pen_Stroke_Selection=Selec\u00E7\u00E3o de tra\u00E7o Plot=Tra\u00E7o Range=Intervalo Range_Axis=Eixo das ordenadas Select...=Seleccionar... Series_Label_Color=Cor da etiqueta das s\u00E9ries Series_label_font=Fonte da etiqueta das s\u00E9ries : Series_label_paint=Cor da etiqueta das s\u00E9ries : Series_Outline_Paint=Cor da borda das s\u00E9ries : Series_Outline_Stroke=Estilo do tra\u00E7o da borda : Series_Paint=Cor da s\u00E9ries : Series_Stroke=Estilo do tra\u00E7o das s\u00E9ries : Show_tick_labels=Visualizar etiquetas de gradua\u00E7\u00E3o de escala Show_tick_marks=Visualizar marcas de gradua\u00E7\u00E3o de escala Set_palette...=Escolha de palete... Step_Palette=Changement de palette : Stroke_Selection=Selec\u00E7\u00E3o do tra\u00E7o Text=Texto : Tick_label_font=Fonte das etiquetas de gradua\u00E7\u00E3o de escala : Tick_Label_Insets=Posi\u00E7\u00E3o das etiquetas de gradua\u00E7\u00E3o de escala : Ticks=Etiquetas de gradua\u00E7\u00E3o de escala TickUnit=TickUnit Title_Color=Cor do t\u00EDtulo jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/editor/LocalizationBundle_ru.properties000066400000000000000000000116701463604235500340500ustar00rootroot00000000000000# org.jfree.chart.ui.ui ResourceBundle properties file # # Changes (from 10-Nov-2003) # -------------------------- # 10-Nov-2003 : Initial version (AL); # \:=: Appearance=\u0412\u0438\u0434 AngleOffset=Angle Offset Auto-adjust_range=\u0410\u0432\u0442\u0440\u043e\u0440\u0435\u0433\u0443\u043b\u0438\u0440\u043e\u0432\u043a\u0430: Auto-TickUnit_Selection=Auto-Selection of TickUnit Background=\u0424\u043e\u043d: Background_Color=\u0426\u0432\u0435\u0442 \u0444\u043e\u043d\u0430 Background_paint=\u0424\u043e\u043d\u043e\u0432\u044b\u0439 \u0440\u0438\u0441\u0443\u043d\u043e\u043a: Color=\u0426\u0432\u0435\u0442: Color_Bar=\u0426\u0432\u0435\u0442\u043e\u0432\u043e\u0439 \u0433\u0440\u0443\u0433 Domain_Axis=\u041e\u0441\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 Draw_anti-aliased=\u0410\u043d\u0442\u0438-\u0430\u043b\u0438\u0430\u0441\u0438\u043d\u0433 Edit...=\u0420\u0435\u0434\u0430\u043a\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c... Edit_Insets=\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043e\u0442\u0441\u0442\u0443\u043f\u043e\u0432 Font=\u0428\u0440\u0438\u0444\u0442: Font_Selection=\u0412\u044b\u0431\u043e\u0440 \u0448\u0440\u0438\u0444\u0442\u0430 General=\u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435: General1=\u041e\u0441\u043d\u043e\u0432\u043d\u044b\u0435 Grid_Color=\u0426\u0432\u0435\u0442 \u0441\u0435\u0442\u043a\u0438 Insets=\u041e\u0442\u0441\u0442\u0443\u043f\u044b: Invert_Palette=\u0418\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0430\u043b\u0438\u0442\u0440\u0443: Label=\u041c\u0435\u0442\u043a\u0430: Label_Color=\u0426\u0432\u0435\u0442 \u043c\u0435\u0442\u043a\u0438 Label_Insets=\u041e\u0442\u0441\u0442\u0443\u043f\u044b \u043c\u0435\u0442\u043a\u0438: Legend=\u041b\u0435\u0433\u0435\u043d\u0434\u0430 Manual_TickUnit_value=TickUnit value Maximum_range_value=\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435: Minimum_range_value=\u041c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435: No_editor_implemented=\u041d\u0435 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d \u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440 Other=\u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e Outline=\u0420\u0430\u043c\u043a\u0430: Outline_Color=\u0426\u0432\u0435\u0442 \u0440\u0430\u043c\u043a\u0438 Outline_stroke=\u0421\u0442\u0438\u043b\u044c \u0440\u0430\u043c\u043a\u0438: Outline_Paint=\u041e\u043a\u0440\u0430\u0441\u043a\u0430 \u0440\u0430\u043c\u043a\u0438: Paint=\u041e\u043a\u0440\u0430\u0441\u043a\u0430: Palette_Selection=\u0412\u044b\u0431\u043e\u0440 \u043f\u0430\u043b\u0438\u0442\u0440\u044b Palette=\u041f\u0430\u043b\u0438\u0442\u0440\u0430: Pen_Stroke_Selection=\u0412\u044b\u0431\u043e\u0440 \u041a\u0430\u0440\u0430\u043d\u0434\u0430\u0448/\u0428\u0442\u0440\u0438\u0445 Plot=\u0413\u0440\u0430\u0444\u0438\u043a Range=\u041e\u0431\u043b\u0430\u0441\u0442\u044c Range_Axis=\u041e\u0441\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 Select...=\u0412\u044b\u0431\u043e\u0440... Series_Label_Color=\u0426\u0432\u0435\u0442 \u043c\u0435\u0442\u043a\u0438 \u0441\u0435\u0440\u0438\u0438 Series_label_font=\u0428\u0440\u0438\u0444\u0442 \u043c\u0435\u0442\u043a\u0438 \u0441\u0435\u0440\u0438\u0438: Series_label_paint=\u041e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u043c\u0435\u0442\u043a\u0438 \u0441\u0435\u0440\u0438\u0438: Series_Outline_Paint=\u041e\u043a\u0440\u0430\u0441\u043a\u0430 \u0440\u0430\u043c\u043a\u0438 \u0441\u0435\u0440\u0438\u0438: Series_Outline_Stroke=\u0428\u0442\u0440\u0438\u0445\u043e\u0432\u0430\u044f \u0440\u0430\u043c\u043a\u0430 \u0441\u0435\u0440\u0438\u0438: Series_Paint=\u041e\u043a\u0440\u0430\u0441\u043a\u0430 \u0441\u0435\u0440\u0438\u0438: Series_Stroke=\u0428\u0442\u0440\u0438\u0445\u0438 \u0441\u0435\u0440\u0438\u0438: Show_tick_labels=\u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u043e\u0441\u0435\u0432\u044b\u0435 \u043e\u0442\u043c\u0435\u0442\u043a\u0438 Show_tick_marks=\u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u043e\u0441\u0435\u0432\u044b\u0435 \u0448\u0442\u0440\u0438\u0445\u0438 Set_palette...=\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u043f\u0430\u043b\u0438\u0442\u0440\u0443... Step_Palette=\u0428\u0430\u0433 \u043f\u0430\u043b\u0438\u0442\u0440\u044b: Stroke_Selection=\u0412\u044b\u0431\u043e\u0440 \u0448\u0442\u0440\u0438\u0445\u0430 Text=\u0422\u0435\u043a\u0441\u0442: Ticks=\u041e\u0442\u043c\u0435\u0442\u043a\u0438 TickUnit=TickUnit Tick_label_font=\u0428\u0440\u0438\u0444\u0442 \u043e\u0442\u043c\u0435\u0442\u043a\u0438: Tick_Label_Insets=\u041e\u0442\u0441\u0442\u0443\u043f\u044b \u043e\u0442\u043c\u0435\u0442\u043e\u043a: Title_Color=\u0426\u0432\u0435\u0442 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430 LocalizationBundle_zh_CN.properties000066400000000000000000000050521463604235500343410ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/editor# org.jfree.chart.ui.ui ResourceBundle properties file # # Changes # ------- # 29-Jun-2005 : Initial version, see: http://www.jfree.org/phpBB2/viewtopic.php?t=13495; # \:=: Appearance=\u5916\u89c2 AngleOffset=Angle Offset Auto-adjust_range=\u81ea\u52a8\u8c03\u8282\u8303\u56f4: Auto-TickUnit_Selection=Auto-Selection of TickUnit Background=\u80cc\u666f: Background_Color=\u80cc\u666f\u8272 Background_paint=\u80cc\u666f\u586b\u5145: Color=\u989c\u8272: Color_Bar=\u8272\u5f69\u6761 Domain_Axis=\u9879\u76ee\u8f74 Draw_anti-aliased=\u7ed8\u5236\u53cd\u6df7\u6dc6 Draw_lines=\u753b\u7ebf: Draw_shapes=\u753b\u5f62\u72b6: Edit...=\u7f16\u8f91... Edit_Insets=\u7f16\u8f91\u5d4c\u5165 Font=\u5b57\u4f53: Font_Selection=\u5b57\u4f53\u9009\u62e9 General=\u7efc\u5408: General1=\u7efc\u5408 Grid_Color=\u7f51\u683c\u989c\u8272 Insets=\u63d2\u56fe: Invert_Palette=\u53cd\u8f6c\u8c03\u8272\u677f: Label=\u6807\u7b7e: Label_Color=\u6807\u7b7e\u989c\u8272 Label_Insets=\u6807\u7b7e\u63d2\u56fe: Legend=\u56fe\u4f8b Maximum_range_value=\u6700\u5927\u503c: Manual_TickUnit_value=TickUnit value Minimum_range_value=\u6700\u5c0f\u503c: No_editor_implemented=\u6ca1\u6709\u53ef\u6267\u884c\u7684\u7f16\u8f91\u5668 Orientation=\u65b9\u5411: Other=\u5176\u4ed6 Outline=\u8f6e\u5ed3: Outline_Color=\u8f6e\u5ed3\u989c\u8272 Outline_stroke=\u8f6e\u5ed3\u7ebf\u6761: Outline_Paint=\u8f6e\u5ed3\u586b\u5145: Paint=\u586b\u5145: Palette_Selection=\u8c03\u8272\u677f\u9009\u62e9 Palette=\u8c03\u8272\u677f: Pen_Stroke_Selection=\u753b\u7b14/\u7ebf\u578b \u9009\u62e9 Plot=\u56fe Range=\u8303\u56f4 Range_Axis=\u8303\u56f4\u8f74 Select...=\u9009\u62e9... Series_Label_Color=\u5e8f\u5217\u6807\u7b7e\u8272 Series_label_font=\u5e8f\u5217\u6807\u7b7e\u5b57\u4f53: Series_label_paint=\u5e8f\u5217\u6807\u7b7e\u586b\u5145: Series_Outline_Paint=\u5e8f\u5217\u8f6e\u5ed3\u586b\u5145: Series_Outline_Stroke=\u5e8f\u5217\u8f6e\u5ed3\u7ebf\u578b: Series_Paint=\u5e8f\u5217\u586b\u5145: Series_Stroke=\u5e8f\u5217\u7ebf\u578b: Show_Legend=\u663e\u793a\u56fe\u4f8b: Show_tick_labels=\u663e\u793a\u523b\u5ea6\u6807\u7b7e Show_tick_marks=\u663e\u793a\u6807\u7b7e\u8bb0\u53f7 Show_Title=\u663e\u793a\u6807\u9898: Set_palette...=\u8bbe\u7f6e\u8c03\u8272\u677f... Step_Palette=Step \u8c03\u8272\u677f: Stroke_Selection=\u7ebf\u578b\u9009\u62e9 Text=\u6b63\u6587: Ticks=\u523b\u5ea6\u7ebf TickUnit=TickUnit Tick_label_font=\u523b\u5ea6\u6807\u7b7e\u5b57\u4f53: Tick_Label_Insets=\u523b\u5ea6\u6807\u7b7e\u63d2\u56fe: Title=\u6807\u9898 Title_Color=\u6807\u9898\u989c\u8272 jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/plot/000077500000000000000000000000001463604235500251255ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/plot/LocalizationBundle.properties000066400000000000000000000012751463604235500330320ustar00rootroot00000000000000# org.jfree.chart.plot.plot ResourceBundle properties file # # Changes (from 31-Aug-2003) # -------------------------- # 31-Aug-2003 : Initial version (AL); # 19-Jan-2004 : Added Polar_Plot (DG); # Category_Plot=Category Plot Combined_Domain_XYPlot=Combined Domain XYPlot Combined_Range_XYPlot=Combined Range XYPlot Compass_Plot=Compass Plot Contour_Plot=Contour Plot Fast_Scatter_Plot=Fast Scatter Plot Meter_Plot=Meter Plot Period_Marker_Plot=Period Marker Plot Pie_Plot=Pie Plot Thermometer_Plot=Thermometer Plot XY_Plot=XY Plot Polar_Plot=Polar Plot Too_many_elements=Too many elements #Secteurs 3D Pie_3D_Plot=Pie 3D Plot # points of the compass N=N E=E S=S W=W jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/plot/LocalizationBundle_ca.properties000077500000000000000000000014121463604235500334710ustar00rootroot00000000000000# org.jfree.chart.plot.plot ResourceBundle properties file # # Changes (from 31-Aug-2003) # -------------------------- # 31-Aug-2003 : Initial version (AL); # 19-Jan-2004 : Added Polar_Plot (DG); # Category_Plot=Diagrama de categories Combined_Domain_XYPlot=Diagrama de Domini Combinat XY Combined_Range_XYPlot=Diagrama de Rang Combinat XY Compass_Plot=Diagrama de compas Contour_Plot=Diagrama de Contorn Fast_Scatter_Plot=Diagrama de Dispersi� R�pida Meter_Plot=Diagrama de Metres Period_Marker_Plot=Diagrama de Marcadors de Per�ode Pie_Plot=Diagrama de Tarta Thermometer_Plot=Diagrama de Term�metre XY_Plot=Diagrama XY Polar_Plot=Diagrama Polar Too_many_elements=Massa elements #Secteurs 3D Pie_3D_Plot=Diagrama de Tarta 3D # points of the compass N=N E=E S=S W=O jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/plot/LocalizationBundle_cs.properties000066400000000000000000000014451463604235500335160ustar00rootroot00000000000000# org.jfree.chart.plot.plot ResourceBundle properties file # # Changes (from 31-Aug-2003) # -------------------------- # 31-Aug-2003 : Initial version (AL); # 19-Jan-2004 : Added Polar_Plot (DG); # 20-Mar-2010 : Czech translation; # Category_Plot=Graf kategori\u00ed Combined_Domain_XYPlot=XY graf kombinovan\u00e9 oblasti Combined_Range_XYPlot=XY graf kombinovan\u00e9ho rozsahu Compass_Plot=Kompasov\u00fd graf Contour_Plot=Obrysov\u00fd graf Fast_Scatter_Plot=Graf s rychl\u00fdm rozptylem Meter_Plot=Graf s m\u011b\u0159i\u010dem Period_Marker_Plot=Period Marker Plot Pie_Plot=Kol\u00e1\u010dov\u00fd graf Thermometer_Plot=Teplom\u011brov\u00fd graf XY_Plot=XY graf Polar_Plot=Pol\u00e1rn\u00ed graf #Secteurs 3D Pie_3D_Plot=3D kol\u00e1\u010dov\u00fd graf # points of the compass N=N E=E S=S W=W jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/plot/LocalizationBundle_de.properties000066400000000000000000000013711463604235500334770ustar00rootroot00000000000000# org.jfree.chart.plot.plot ResourceBundle properties file - german version # # Changes (from 31-Aug-2003) # -------------------------- # 31-Aug-2003 : Initial version (AL); # 15-Mar-2004 : Revised version (Christian W. Zuckschwerdt); # Category_Plot=Kategoriediagramm Combined_Domain_XYPlot=Rubriken-kombiniertes XY-Diagramm Combined_Range_XYPlot=Werte-kombiniertes XY-Diagramm Compass_Plot=Kompass-Diagramm Contour_Plot=Liniendiagramm Fast_Scatter_Plot=Streudiagramm Meter_Plot=Tachometer-Diagramm Period_Marker_Plot=Zeitreihe Pie_Plot=Kreisdiagramm Thermometer_Plot=Thermometer-Diagramm XY_Plot=XY-Diagramm Too_many_elements=Zu viele Elemente #Secteurs 3D Pie_3D_Plot=3D-Kreisdiagramm # points of the compass N=N E=O S=S W=W jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/plot/LocalizationBundle_es.properties000066400000000000000000000013471463604235500335210ustar00rootroot00000000000000# org.jfree.chart.plot.plot ResourceBundle properties file - spanish version # # Changes (from 16-Dec-2003) # -------------------------- # 16-Dec-2003 : Initial Version: Complejo Hospitalario Universitario Juan Canalejo # Category_Plot=Barras Combined_Domain_XYPlot=Curvas combinadas por la abcisa Combined_Range_XYPlot=Curvas combinadas por la ordenada Compass_Plot=Diagrama del comp\u00a3s Contour_Plot=Contorno del comp\u00a3s Fast_Scatter_Plot=Dispersi\u00f3n Meter_Plot=Diagrama del metro Period_Marker_Plot=Diagrama del marcador del per�odo Pie_Plot=Sectores Thermometer_Plot=Term\u00f3metro XY_Plot=Curvas Pie_3D_Plot=Sectores 3D Too_many_elements=Too many elements # points of the compass N=N E=E S=S W=W jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/plot/LocalizationBundle_fr.properties000066400000000000000000000012141463604235500335120ustar00rootroot00000000000000# org.jfree.chart.plot.plot ResourceBundle properties file - French version # # Changes (from 31-Aug-2003) # -------------------------- # 31-Aug-2003 : Initial version (AL); # Category_Plot=Barres Combined_Domain_XYPlot=Courbes combin\u00E9es sur l'abcisse Combined_Range_XYPlot=Courbes combin\u00E9es sur l'ordonn\u00E9e Compass_Plot=Compas Contour_Plot=Contours Fast_Scatter_Plot=Nuage de points Meter_Plot=Niveaux Period_Marker_Plot=Period Marker Plot Pie_Plot=Secteurs Thermometer_Plot=Thermom\u00E8tre XY_Plot=Courbes Pie_3D_Plot=Secteurs 3D Too_many_elements=Too many elements # points of the compass N=N E=E S=S W=W jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/plot/LocalizationBundle_ja.properties000066400000000000000000000015371463604235500335050ustar00rootroot00000000000000 Category_Plot=\u30ab\u30c6\u30b4\u30ea\u306e\u30d7\u30ed\u30c3\u30c8 Combined_Domain_XYPlot=\u5206\u985e\u306e\u7d44\u307f\u5408\u308f\u305b XY\u30d7\u30ed\u30c3\u30c8 Combined_Range_XYPlot=\u6570\u5024\u306e\u7d44\u307f\u5408\u308f\u305b XY\u30d7\u30ed\u30c3\u30c8 Compass_Plot=\u30b3\u30f3\u30d1\u30b9\u30d7\u30ed\u30c3\u30c8 Contour_Plot=\u7b49\u9ad8\u7dda\u56f3 Fast_Scatter_Plot=\u9ad8\u901f\u6563\u5e03\u56f3 Meter_Plot=\u30e1\u30fc\u30bf\u30fc\u306e\u30d7\u30ed\u30c3\u30c8 Period_Marker_Plot=\u671f\u9593\u30de\u30fc\u30ab\u30fc\u306e\u30d7\u30ed\u30c3\u30c8 Pie_3D_Plot=3D\u5186\u30b0\u30e9\u30d5\u306e\u30d7\u30ed\u30c3\u30c8 Pie_Plot=\u5186\u30b0\u30e9\u30d5\u306e\u30d7\u30ed\u30c3\u30c8 Polar_Plot=\u6975\u5ea7\u6a19\u306e\u30d7\u30ed\u30c3\u30c8 Thermometer_Plot=\u6e29\u5ea6\u8a08\u306e\u30d7\u30ed\u30c3\u30c8 XY_Plot=XY \u30d7\u30ed\u30c3\u30c8 jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/plot/LocalizationBundle_nl.properties000066400000000000000000000013411463604235500335150ustar00rootroot00000000000000# org.jfree.chart.plot.plot ResourceBundle properties file # # Changes (from 31-Aug-2003) # -------------------------- # 24-Mar-2003 : Translated into Dutch # 31-Aug-2003 : Initial version (AL); # Category_Plot=Categorie diagram Combined_Domain_XYPlot=Diagrammen met gecombineerd domein Combined_Range_XYPlot=Diagrammen met gecombineerd bereik Compass_Plot=Kompas diagram Contour_Plot=Contour diagram Fast_Scatter_Plot=Spreidingsdiagram Meter_Plot=Wijzerplaat-diagram Period_Marker_Plot=Periodemarker-diagram Pie_Plot=Taartdiagram Thermometer_Plot=Thermometer-diagram XY_Plot=XY Diagram Too_many_elements=Too many elements #Secteurs 3D Pie_3D_Plot=3D Taartdiagram # points of the compass N=N E=E S=S W=W jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/plot/LocalizationBundle_pl.properties000066400000000000000000000015371463604235500335260ustar00rootroot00000000000000# org.jfree.chart.plot.plot ResourceBundle properties file # # Changes (from 15-Mar-2004) # -------------------------- # 15-Mar-2004 : Initial version (Kuba Duda); # Category_Plot=Wykres kategorii Combined_Domain_XYPlot=Wykres kombinowany z dzielon\u0105 osi\u0105 domeny Combined_Range_XYPlot=Wykres kombinowany z dzielon\u0105 osi\u0105 zasi\u0119gu Compass_Plot=wykres kompasowy Contour_Plot=Wykres konturowy Fast_Scatter_Plot=Szybki wykres rozproszony punktowy Meter_Plot=Wykres miernikowy Period_Marker_Plot=Wykres zaznaczaj\u0105cy okresy czasu Pie_Plot=Wykres ko\u0142owy Thermometer_Plot=Wykres temperatury XY_Plot=Wykres XY Polar_Plot=Wykres dwuwymiarowy o wsp\u00f3\u0142rz\u0119dnych biegunowych Too_many_elements=Too many elements #Secteurs 3D Pie_3D_Plot=Wykres ko\u0142owy 3D # points of the compass N=N E=E S=S W=W jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/plot/LocalizationBundle_pt_PT.properties000066400000000000000000000012171463604235500341340ustar00rootroot00000000000000# org.jfree.chart.ChartPanel ResourceBundle properties file - portuguese version # # Changes (from 09-Set-2003) # -------------------------- # 09-Set-2003 : Initial version (Eduardo Ramalho); # Category_Plot=Barras Combined_Domain_XYPlot=Curvas combinadas pela abcissa Combined_Range_XYPlot=Curvas combinadas pela ordenada Compass_Plot=Compasso Contour_Plot=Contours Fast_Scatter_Plot=Dispers\u00E3o Meter_Plot=N\u00EDvel Period_Marker_Plot=Period Marker Plot Pie_Plot=Sectores Thermometer_Plot=Term\u00a2metro XY_Plot=Curvas Pie_3D_Plot=Sectores 3D Too_many_elements=Too many elements # points of the compass N=N E=E S=S W=W jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/plot/LocalizationBundle_ru.properties000066400000000000000000000022301463604235500335300ustar00rootroot00000000000000# org.jfree.chart.plot.plot ResourceBundle properties file # # Changes (from 10-Nov-2003) # -------------------------- # 10-Nov-2003 : Initial version (AL); # Category_Plot=\u041a\u0430\u0442\u0435\u0433\u043e\u0440\u0438\u044f Combined_Domain_XYPlot=\u0421\u043e\u0432\u043c\u0435\u0449\u0435\u043d\u0438\u0435 \u043f\u043e \u043e\u0441\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 Combined_Range_XYPlot=\u0421\u043e\u0432\u043c\u0435\u0449\u0435\u043d\u0438\u0435 \u043f\u043e \u043e\u0441\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 Compass_Plot=\u041a\u043e\u043c\u043f\u0430\u0441 Contour_Plot=\u041a\u043e\u043d\u0442\u0443\u0440 Fast_Scatter_Plot=\u0420\u043e\u0441\u0441\u044b\u043f\u044c Meter_Plot=\u041c\u0435\u0442\u0440\u0438\u043a\u0430 Period_Marker_Plot=\u041f\u0435\u0440\u0438\u043e\u0434 Pie_Plot=\u041a\u0440\u0443\u0433 Thermometer_Plot=\u0422\u0435\u0440\u043c\u043e\u043c\u0435\u0442\u0440 XY_Plot=XY Too_many_elements=Too many elements #Secteurs 3D Pie_3D_Plot=\u0422\u0440\u0435\u0445\u043c\u0435\u0440\u043d\u044b\u0439 \u043a\u0440\u0443\u0433 # points of the compass N=N E=E S=S W=W jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/plot/LocalizationBundle_zh_CN.properties000066400000000000000000000014421463604235500341070ustar00rootroot00000000000000# org.jfree.chart.plot.plot ResourceBundle properties file # # Changes # ------- # 29-Jun-2005 : Initial version, see: http://www.jfree.org/phpBB2/viewtopic.php?t=13495; # Category_Plot=\u7c7b\u6bd4\u56fe Combined_Domain_XYPlot=\u7ec4\u5408\u7684\u9879\u76ee XY \u56fe Combined_Range_XYPlot=\u7ec4\u5408\u7684\u8303\u56f4 XY \u56fe Compass_Plot=\u7f57\u76d8\u56fe Contour_Plot=\u8f6e\u5ed3\u56fe Fast_Scatter_Plot=\u5feb\u901f\u5206\u6563\u56fe Meter_Plot=\u4eea\u8868\u56fe Period_Marker_Plot=\u5468\u671f\u6807\u8bb0\u56fe Pie_Plot=\u997c\u56fe Thermometer_Plot=\u6e29\u5ea6\u8ba1\u56fe XY_Plot=XY \u56fe Polar_Plot=\u6781\u5750\u6807\u56fe Too_many_elements=Too many elements #Secteurs 3D Pie_3D_Plot=\u4e09\u7ef4\u997c\u56fe # points of the compass N=N E=E S=S W=W jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/ui/000077500000000000000000000000001463604235500245645ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/ui/LocalizationBundle.properties000066400000000000000000000006101463604235500324610ustar00rootroot00000000000000# org.jfree.ui.ui ResourceBundle properties file # # Changes (from 31-Aug-2003) # -------------------------- # 31-Aug-2003 : Initial version (AL); # Attributes=Attributes: B=B: Bold=Bold Bottom=Bottom Font=Font: Insets=Insets: Italic=Italic L=L: Left=Left No_Font_Selected=No font selected. R=R: Right=Right Size=Size: T=T: Top=Top Help=Help OK=OK Cancel=Cancel jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/ui/LocalizationBundle_de.properties000066400000000000000000000007221463604235500331350ustar00rootroot00000000000000# org.jfree.ui.ui ResourceBundle properties file - german version # # Changes (from 31-Aug-2003) # -------------------------- # 15-Mar-2004 : Initial version (Christian W. Zuckschwerdt); # Attributes=Attribute: B=U: Bold=Fett Bottom=Unten Font=Schrift: Insets=R\u00e4nder: Italic=Kursiv L=L: Left=Links No_Font_Selected=Keine Schrift gew\u00e4hlt. R=R: Right=Rechts Size=Gr\u00f6\u00dfe: T=O: Top=Oben Help=Hilfe OK=OK Cancel=Abbrechen jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/ui/LocalizationBundle_es.properties000066400000000000000000000011441463604235500331530ustar00rootroot00000000000000# org.jfree.ui.ui ResourceBundle properties file spanish # # Changes (from 31-Aug-2003) # -------------------------- # 14-Oct-2004 : Initial version: Leopoldo Federico Pértile (Grupo de Procesamiento Digital de Imágenes) # Universidad Tecnológica Nacional - Facultad Regional Resistencia, Argentina Attributes=Atributos: B=Inf: Bold=Negrita Bottom=Inferior Font=Fuente: Insets=Márgenes: Italic=Cursiva L=Izq: Left=Izquierda No_Font_Selected=No se seleccion\00F3 ninguna fuente. R=Der: Right=Derecha Size=Tama\u00f1o: T=Sup: Top=Superior Help=Ayuda OK=Aceptar Cancel=Cancelar jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/ui/LocalizationBundle_fr.properties000066400000000000000000000006571463604235500331630ustar00rootroot00000000000000# org.jfree.ui.ui ResourceBundle properties file - french version # # Changes (from 31-Aug-2003) # -------------------------- # 31-Aug-2003 : Initial version (AL); # Attributes=Attributs : B=B : Bold=Gras Bottom=Bas Font=Police : Insets=Position : Italic=Italique L=G : Left=Gauche No_Font_Selected=Aucune police n'a \u00E9t\u00E9 s\u00E9lectionn\u00E9e. R=D : Right=Droite Size=Taille : T=H : Top=Haut jfree-jfreechart-cb8ff67/src/main/resources/org/jfree/chart/ui/LocalizationBundle_pt_PT.properties000066400000000000000000000007021463604235500335710ustar00rootroot00000000000000# org.jfree.chart.ChartPanel ResourceBundle properties file - portuguese version # # Changes (from 09-Set-2003) # -------------------------- # 09-Set-2003 : Initial version (Eduardo Ramalho); # Attributes=Atributos: B=B: Bold=Negrito Bottom=Fundo Font=Fonte: Insets=Posi\u00e7\u00e3o: Italic=It\u00e1lico L=E: Left=Esquerda No_Font_Selected=Nenhuma fonte est\u00e1 seleccionada. R=D: Right=Direita Size=Tamanho: T=T: Top=Topo jfree-jfreechart-cb8ff67/src/test/000077500000000000000000000000001463604235500172055ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/000077500000000000000000000000001463604235500201265ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/000077500000000000000000000000001463604235500207155ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/000077500000000000000000000000001463604235500220105ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/000077500000000000000000000000001463604235500231115ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/AreaChartTest.java000066400000000000000000000141301463604235500264450ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * AreaChartTest.java * ------------------ * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.fail; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.ChartChangeEvent; import org.jfree.chart.event.ChartChangeListener; import org.jfree.chart.labels.CategoryToolTipGenerator; import org.jfree.chart.labels.StandardCategoryToolTipGenerator; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.renderer.category.CategoryItemRenderer; import org.jfree.chart.urls.CategoryURLGenerator; import org.jfree.chart.urls.StandardCategoryURLGenerator; import org.jfree.data.Range; import org.jfree.data.category.CategoryDataset; import org.jfree.data.general.DatasetUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * Tests for an area chart. */ class AreaChartTest { /** A chart. */ private JFreeChart chart; /** * Common test setup. */ @BeforeEach public void setUp() { this.chart = createAreaChart(); } /** * Check that setting a tool tip generator for a series does override the * default generator. */ @Test void testSetSeriesToolTipGenerator() { CategoryPlot plot = (CategoryPlot) this.chart.getPlot(); CategoryItemRenderer renderer = plot.getRenderer(); StandardCategoryToolTipGenerator tt = new StandardCategoryToolTipGenerator(); renderer.setSeriesToolTipGenerator(0, tt); CategoryToolTipGenerator tt2 = renderer.getToolTipGenerator(0, 0); assertSame(tt2, tt); } /** * Check that setting a URL generator for a series does override the * default generator. */ @Test void testSetSeriesURLGenerator() { CategoryPlot plot = (CategoryPlot) this.chart.getPlot(); CategoryItemRenderer renderer = plot.getRenderer(); StandardCategoryURLGenerator url1 = new StandardCategoryURLGenerator(); renderer.setSeriesItemURLGenerator(0, url1); CategoryURLGenerator url2 = renderer.getItemURLGenerator(0, 0); assertSame(url2, url1); } /** * Draws the chart with a null info object to make sure that no exceptions * are thrown (a problem that was occurring at one point). */ @Test void testDrawWithNullInfo() { try { BufferedImage image = new BufferedImage(200 , 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); this.chart.draw(g2, new Rectangle2D.Double(0, 0, 200, 100), null, null); g2.dispose(); } catch (Exception e) { fail("There should be no exception."); } } /** * Replaces the chart's dataset and then checks that the new dataset is OK. */ @Test void testReplaceDataset() { Number[][] data = new Integer[][] {{-30, -20}, {-10, 10}, {20, 30}}; CategoryDataset newData = DatasetUtils.createCategoryDataset( "S", "C", data); LocalListener l = new LocalListener(); this.chart.addChangeListener(l); CategoryPlot plot = (CategoryPlot) this.chart.getPlot(); plot.setDataset(newData); assertTrue(l.flag); ValueAxis axis = plot.getRangeAxis(); Range range = axis.getRange(); assertTrue(range.getLowerBound() <= -30, "Expecting the lower bound of the range to be around -30: " + range.getLowerBound()); assertTrue(range.getUpperBound() >= 30, "Expecting the upper bound of the range to be around 30: " + range.getUpperBound()); } /** * Create an area chart with sample data in the range -3 to +3. * * @return The chart. */ private static JFreeChart createAreaChart() { Number[][] data = new Integer[][] {{-3, -2}, {-1, 1}, {2, 3}}; CategoryDataset dataset = DatasetUtils.createCategoryDataset("S", "C", data); return ChartFactory.createAreaChart("Area Chart", "Domain", "Range", dataset, PlotOrientation.HORIZONTAL, true, true, true); } /** * A chart change listener. */ static class LocalListener implements ChartChangeListener { /** A flag. */ private boolean flag; /** * Event handler. * * @param event the event. */ @Override public void chartChanged(ChartChangeEvent event) { this.flag = true; } } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/BarChartTest.java000066400000000000000000000143121463604235500263030ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * BarChartTest.java * ----------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.ChartChangeEvent; import org.jfree.chart.event.ChartChangeListener; import org.jfree.chart.labels.CategoryToolTipGenerator; import org.jfree.chart.labels.StandardCategoryToolTipGenerator; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.renderer.category.CategoryItemRenderer; import org.jfree.chart.urls.CategoryURLGenerator; import org.jfree.chart.urls.StandardCategoryURLGenerator; import org.jfree.data.Range; import org.jfree.data.category.CategoryDataset; import org.jfree.data.general.DatasetUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for a bar chart. */ public class BarChartTest { /** A chart. */ private JFreeChart chart; /** * Common test setup. */ @BeforeEach public void setUp() { this.chart = createBarChart(); } /** * Draws the chart with a null info object to make sure that no exceptions * are thrown (a problem that was occurring at one point). */ @Test public void testDrawWithNullInfo() { try { BufferedImage image = new BufferedImage(200 , 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); this.chart.draw(g2, new Rectangle2D.Double(0, 0, 200, 100), null, null); g2.dispose(); } catch (Exception e) { fail("There should be no exception."); } } /** * Replaces the chart's dataset and then checks that the new dataset is OK. */ @Test public void testReplaceDataset() { // create a dataset... Number[][] data = new Integer[][] {{-30, -20}, {-10, 10}, {20, 30}}; CategoryDataset newData = DatasetUtils.createCategoryDataset("S", "C", data); LocalListener l = new LocalListener(); this.chart.addChangeListener(l); CategoryPlot plot = (CategoryPlot) this.chart.getPlot(); plot.setDataset(newData); assertTrue(l.flag); ValueAxis axis = plot.getRangeAxis(); Range range = axis.getRange(); assertTrue(range.getLowerBound() <= -30, "Expecting the lower bound of the range to be around -30: " + range.getLowerBound()); assertTrue(range.getUpperBound() >= 30, "Expecting the upper bound of the range to be around 30: " + range.getUpperBound()); } /** * Check that setting a tool tip generator for a series does override the * default generator. */ @Test public void testSetSeriesToolTipGenerator() { CategoryPlot plot = (CategoryPlot) this.chart.getPlot(); CategoryItemRenderer renderer = plot.getRenderer(); StandardCategoryToolTipGenerator tt = new StandardCategoryToolTipGenerator(); renderer.setSeriesToolTipGenerator(0, tt); CategoryToolTipGenerator tt2 = renderer.getToolTipGenerator(0, 0); assertSame(tt2, tt); } /** * Check that setting a URL generator for a series does override the * default generator. */ @Test public void testSetSeriesURLGenerator() { CategoryPlot plot = (CategoryPlot) this.chart.getPlot(); CategoryItemRenderer renderer = plot.getRenderer(); StandardCategoryURLGenerator url1 = new StandardCategoryURLGenerator(); renderer.setSeriesItemURLGenerator(0, url1); CategoryURLGenerator url2 = renderer.getItemURLGenerator(0, 0); assertSame(url2, url1); } /** * Create a bar chart with sample data in the range -3 to +3. * * @return The chart. */ private static JFreeChart createBarChart() { // create a dataset... Number[][] data = new Integer[][] {{-3, -2}, {-1, 1}, {2, 3}}; CategoryDataset dataset = DatasetUtils.createCategoryDataset("S", "C", data); // create the chart... return ChartFactory.createBarChart( "Bar Chart", "Domain", "Range", dataset, PlotOrientation.HORIZONTAL, true, // include legend true, true ); } /** * A chart change listener. * */ static class LocalListener implements ChartChangeListener { /** A flag. */ private boolean flag = false; /** * Event handler. * * @param event the event. */ @Override public void chartChanged(ChartChangeEvent event) { this.flag = true; } } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/ChartPanelTest.java000066400000000000000000000277661463604235500266570ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * ChartPanelTest.java * ------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.EventListener; import java.util.List; import javax.swing.event.CaretListener; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.event.ChartChangeEvent; import org.jfree.chart.event.ChartChangeListener; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.XYPlot; import org.jfree.data.xy.DefaultXYDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link ChartPanel} class. */ public class ChartPanelTest implements ChartChangeListener, ChartMouseListener { private final List chartChangeEvents = new ArrayList<>(); /** * Receives a chart change event and stores it in a list for later * inspection. * * @param event the event. */ @Override public void chartChanged(ChartChangeEvent event) { this.chartChangeEvents.add(event); } /** * Test that the constructor will accept a null chart. */ @Test public void testConstructor1() { ChartPanel panel = new ChartPanel(null); assertNull(panel.getChart()); } /** * Test that it is possible to set the panel's chart to null. */ @Test public void testSetChart() { JFreeChart chart = new JFreeChart(new XYPlot()); ChartPanel panel = new ChartPanel(chart); panel.setChart(null); assertNull(panel.getChart()); } /** * Check the behaviour of the getListeners() method. */ @Test public void testGetListeners() { ChartPanel p = new ChartPanel(null); p.addChartMouseListener(this); EventListener[] listeners = p.getListeners(ChartMouseListener.class); assertEquals(1, listeners.length); assertEquals(this, listeners[0]); // try a listener type that isn't registered listeners = p.getListeners(CaretListener.class); assertEquals(0, listeners.length); p.removeChartMouseListener(this); listeners = p.getListeners(ChartMouseListener.class); assertEquals(0, listeners.length); // try a null argument boolean pass = false; try { listeners = p.getListeners((Class) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); // try a class that isn't a listener pass = false; try { listeners = p.getListeners(Integer.class); } catch (ClassCastException e) { pass = true; } assertTrue(pass); } /** * Ignores a mouse click event. * * @param event the event. */ @Override public void chartMouseClicked(ChartMouseEvent event) { // ignore } /** * Ignores a mouse move event. * * @param event the event. */ @Override public void chartMouseMoved(ChartMouseEvent event) { // ignore } /** * Checks that a call to the zoom() method generates just one * ChartChangeEvent. */ @Test public void test2502355_zoom() { DefaultXYDataset dataset = new DefaultXYDataset(); JFreeChart chart = ChartFactory.createXYLineChart("TestChart", "X", "Y", dataset, PlotOrientation.VERTICAL, false, false, false); ChartPanel panel = new ChartPanel(chart); chart.addChangeListener(this); this.chartChangeEvents.clear(); panel.zoom(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0)); assertEquals(1, this.chartChangeEvents.size()); } /** * Checks that a call to the zoomInBoth() method generates just one * ChartChangeEvent. */ @Test public void test2502355_zoomInBoth() { DefaultXYDataset dataset = new DefaultXYDataset(); JFreeChart chart = ChartFactory.createXYLineChart("TestChart", "X", "Y", dataset, PlotOrientation.VERTICAL, false, false, false); ChartPanel panel = new ChartPanel(chart); chart.addChangeListener(this); this.chartChangeEvents.clear(); panel.zoomInBoth(1.0, 2.0); assertEquals(1, this.chartChangeEvents.size()); } /** * Checks that a call to the zoomOutBoth() method generates just one * ChartChangeEvent. */ @Test public void test2502355_zoomOutBoth() { DefaultXYDataset dataset = new DefaultXYDataset(); JFreeChart chart = ChartFactory.createXYLineChart("TestChart", "X", "Y", dataset, PlotOrientation.VERTICAL, false, false, false); ChartPanel panel = new ChartPanel(chart); chart.addChangeListener(this); this.chartChangeEvents.clear(); panel.zoomOutBoth(1.0, 2.0); assertEquals(1, this.chartChangeEvents.size()); } /** * Checks that a call to the restoreAutoBounds() method generates just one * ChartChangeEvent. */ @Test public void test2502355_restoreAutoBounds() { DefaultXYDataset dataset = new DefaultXYDataset(); JFreeChart chart = ChartFactory.createXYLineChart("TestChart", "X", "Y", dataset, PlotOrientation.VERTICAL, false, false, false); ChartPanel panel = new ChartPanel(chart); chart.addChangeListener(this); this.chartChangeEvents.clear(); panel.restoreAutoBounds(); assertEquals(1, this.chartChangeEvents.size()); } /** * Checks that a call to the zoomInDomain() method, for a plot with more * than one domain axis, generates just one ChartChangeEvent. */ @Test public void test2502355_zoomInDomain() { DefaultXYDataset dataset = new DefaultXYDataset(); JFreeChart chart = ChartFactory.createXYLineChart("TestChart", "X", "Y", dataset, PlotOrientation.VERTICAL, false, false, false); XYPlot plot = (XYPlot) chart.getPlot(); plot.setDomainAxis(1, new NumberAxis("X2")); ChartPanel panel = new ChartPanel(chart); chart.addChangeListener(this); this.chartChangeEvents.clear(); panel.zoomInDomain(1.0, 2.0); assertEquals(1, this.chartChangeEvents.size()); } /** * Checks that a call to the zoomInRange() method, for a plot with more * than one range axis, generates just one ChartChangeEvent. */ @Test public void test2502355_zoomInRange() { DefaultXYDataset dataset = new DefaultXYDataset(); JFreeChart chart = ChartFactory.createXYLineChart("TestChart", "X", "Y", dataset, PlotOrientation.VERTICAL, false, false, false); XYPlot plot = (XYPlot) chart.getPlot(); plot.setRangeAxis(1, new NumberAxis("X2")); ChartPanel panel = new ChartPanel(chart); chart.addChangeListener(this); this.chartChangeEvents.clear(); panel.zoomInRange(1.0, 2.0); assertEquals(1, this.chartChangeEvents.size()); } /** * Checks that a call to the zoomOutDomain() method, for a plot with more * than one domain axis, generates just one ChartChangeEvent. */ @Test public void test2502355_zoomOutDomain() { DefaultXYDataset dataset = new DefaultXYDataset(); JFreeChart chart = ChartFactory.createXYLineChart("TestChart", "X", "Y", dataset, PlotOrientation.VERTICAL, false, false, false); XYPlot plot = (XYPlot) chart.getPlot(); plot.setDomainAxis(1, new NumberAxis("X2")); ChartPanel panel = new ChartPanel(chart); chart.addChangeListener(this); this.chartChangeEvents.clear(); panel.zoomOutDomain(1.0, 2.0); assertEquals(1, this.chartChangeEvents.size()); } /** * Checks that a call to the zoomOutRange() method, for a plot with more * than one range axis, generates just one ChartChangeEvent. */ @Test public void test2502355_zoomOutRange() { DefaultXYDataset dataset = new DefaultXYDataset(); JFreeChart chart = ChartFactory.createXYLineChart("TestChart", "X", "Y", dataset, PlotOrientation.VERTICAL, false, false, false); XYPlot plot = (XYPlot) chart.getPlot(); plot.setRangeAxis(1, new NumberAxis("X2")); ChartPanel panel = new ChartPanel(chart); chart.addChangeListener(this); this.chartChangeEvents.clear(); panel.zoomOutRange(1.0, 2.0); assertEquals(1, this.chartChangeEvents.size()); } /** * Checks that a call to the restoreAutoDomainBounds() method, for a plot * with more than one range axis, generates just one ChartChangeEvent. */ @Test public void test2502355_restoreAutoDomainBounds() { DefaultXYDataset dataset = new DefaultXYDataset(); JFreeChart chart = ChartFactory.createXYLineChart("TestChart", "X", "Y", dataset, PlotOrientation.VERTICAL, false, false, false); XYPlot plot = (XYPlot) chart.getPlot(); plot.setDomainAxis(1, new NumberAxis("X2")); ChartPanel panel = new ChartPanel(chart); chart.addChangeListener(this); this.chartChangeEvents.clear(); panel.restoreAutoDomainBounds(); assertEquals(1, this.chartChangeEvents.size()); } /** * Checks that a call to the restoreAutoRangeBounds() method, for a plot * with more than one range axis, generates just one ChartChangeEvent. */ @Test public void test2502355_restoreAutoRangeBounds() { DefaultXYDataset dataset = new DefaultXYDataset(); JFreeChart chart = ChartFactory.createXYLineChart("TestChart", "X", "Y", dataset, PlotOrientation.VERTICAL, false, false, false); XYPlot plot = (XYPlot) chart.getPlot(); plot.setRangeAxis(1, new NumberAxis("X2")); ChartPanel panel = new ChartPanel(chart); chart.addChangeListener(this); this.chartChangeEvents.clear(); panel.restoreAutoRangeBounds(); assertEquals(1, this.chartChangeEvents.size()); } /** * In version 1.0.13 there is a bug where enabling the mouse wheel handler * twice would in fact disable it. */ @Test public void testSetMouseWheelEnabled() { DefaultXYDataset dataset = new DefaultXYDataset(); JFreeChart chart = ChartFactory.createXYLineChart("TestChart", "X", "Y", dataset, PlotOrientation.VERTICAL, false, false, false); ChartPanel panel = new ChartPanel(chart); panel.setMouseWheelEnabled(true); assertTrue(panel.isMouseWheelEnabled()); panel.setMouseWheelEnabled(true); assertTrue(panel.isMouseWheelEnabled()); panel.setMouseWheelEnabled(false); assertFalse(panel.isMouseWheelEnabled()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/ChartRenderingInfoTest.java000066400000000000000000000107331463604235500303330ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * ChartRenderingInfoTest.java * --------------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart; import java.awt.Rectangle; import java.awt.geom.Rectangle2D; import org.jfree.chart.entity.ChartEntity; import org.jfree.chart.entity.StandardEntityCollection; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link ChartRenderingInfo} class. */ public class ChartRenderingInfoTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { ChartRenderingInfo i1 = new ChartRenderingInfo(); ChartRenderingInfo i2 = new ChartRenderingInfo(); assertEquals(i1, i2); i1.setChartArea(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0)); assertNotEquals(i1, i2); i2.setChartArea(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0)); assertEquals(i1, i2); i1.getPlotInfo().setDataArea(new Rectangle(1, 2, 3, 4)); assertNotEquals(i1, i2); i2.getPlotInfo().setDataArea(new Rectangle(1, 2, 3, 4)); assertEquals(i1, i2); StandardEntityCollection e1 = new StandardEntityCollection(); e1.add(new ChartEntity(new Rectangle(1, 2, 3, 4))); i1.setEntityCollection(e1); assertNotEquals(i1, i2); StandardEntityCollection e2 = new StandardEntityCollection(); e2.add(new ChartEntity(new Rectangle(1, 2, 3, 4))); i2.setEntityCollection(e2); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException if there is a problem cloning. */ @Test public void testCloning() throws CloneNotSupportedException { ChartRenderingInfo i1 = new ChartRenderingInfo(); ChartRenderingInfo i2 = i1.clone(); assertNotSame(i1, i2); assertSame(i1.getClass(), i2.getClass()); assertEquals(i1, i2); // check independence i1.getChartArea().setRect(4.0, 3.0, 2.0, 1.0); assertNotEquals(i1, i2); i2.getChartArea().setRect(4.0, 3.0, 2.0, 1.0); assertEquals(i1, i2); i1.getEntityCollection().add(new ChartEntity(new Rectangle(1, 2, 2, 1))); assertNotEquals(i1, i2); i2.getEntityCollection().add(new ChartEntity(new Rectangle(1, 2, 2, 1))); assertEquals(i1, i2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { ChartRenderingInfo i1 = new ChartRenderingInfo(); i1.setChartArea(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0)); ChartRenderingInfo i2 = TestUtils.serialised(i1); assertEquals(i1, i2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization2() { ChartRenderingInfo i1 = new ChartRenderingInfo(); i1.getPlotInfo().setDataArea(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0)); ChartRenderingInfo i2 = TestUtils.serialised(i1); assertEquals(i1, i2); assertEquals(i2, i2.getPlotInfo().getOwner()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/FakePlot.java000066400000000000000000000032711463604235500254640ustar00rootroot00000000000000package org.jfree.chart; import java.awt.Graphics2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.Objects; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.plot.PlotState; /** * Dummy extension of Plot for JUnit testing using the EqualsVerifier library */ public class FakePlot extends Plot { private String fakeName = ""; @Override public String getPlotType() { return "Fake"; } @Override public void draw(Graphics2D g2, Rectangle2D area, Point2D anchor, PlotState parentState, PlotRenderingInfo info) { } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (! (obj instanceof FakePlot)) { return false; } FakePlot that = (FakePlot) obj; if (!that.canEqual(this)) { return false; } if (!Objects.equals(this.fakeName, that.fakeName)) { return false; } return super.equals(obj); } /** * Ensures symmetry between super/subclass implementations of equals. For * more detail, see http://jqno.nl/equalsverifier/manual/inheritance. * * @param other Object * * @return true ONLY if the parameter is THIS class type */ @Override public boolean canEqual(Object other) { // Solves Problem: equals not symmetric return (other instanceof FakePlot); } @Override public int hashCode() { int hash = super.hashCode(); hash = 89 * hash + Objects.hashCode(this.fakeName); return hash; } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/FakeValueAxis.java000066400000000000000000000025041463604235500264450ustar00rootroot00000000000000package org.jfree.chart; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.List; import org.jfree.chart.axis.AxisState; import org.jfree.chart.axis.TickUnitSource; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.plot.PlotRenderingInfo; import org.jfree.chart.ui.RectangleEdge; /** * Dummy extension of ValueAxis for JUnit testing using the EqualsVerifier * library. */ public class FakeValueAxis extends ValueAxis { public FakeValueAxis(String label, TickUnitSource standardTickUnits) { super(label, standardTickUnits); } @Override public double valueToJava2D(double value, Rectangle2D area, RectangleEdge edge) { return 0; } @Override public double java2DToValue(double java2DValue, Rectangle2D area, RectangleEdge edge) { return 0; } @Override protected void autoAdjustRange() { } @Override public void configure() { } @Override public AxisState draw(Graphics2D g2, double cursor, Rectangle2D plotArea, Rectangle2D dataArea, RectangleEdge edge, PlotRenderingInfo plotState) { return new AxisState(); } @Override public List refreshTicks(Graphics2D g2, AxisState state, Rectangle2D dataArea, RectangleEdge edge) { return new ArrayList<>(); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/GanttChartTest.java000066400000000000000000000257621463604235500266670ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * GanttChartTest.java * ------------------- * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.util.Calendar; import java.util.Date; import org.jfree.chart.event.ChartChangeEvent; import org.jfree.chart.event.ChartChangeListener; import org.jfree.chart.labels.CategoryToolTipGenerator; import org.jfree.chart.labels.StandardCategoryToolTipGenerator; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.renderer.category.CategoryItemRenderer; import org.jfree.chart.urls.CategoryURLGenerator; import org.jfree.chart.urls.StandardCategoryURLGenerator; import org.jfree.data.category.IntervalCategoryDataset; import org.jfree.data.gantt.Task; import org.jfree.data.gantt.TaskSeries; import org.jfree.data.gantt.TaskSeriesCollection; import org.jfree.data.time.SimpleTimePeriod; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeEach; import static org.junit.jupiter.api.Assertions.*; /** * Some tests for a Gantt chart. */ public class GanttChartTest { /** A chart. */ private JFreeChart chart; /** * Common test setup. */ @BeforeEach public void setUp() { this.chart = createGanttChart(); } /** * Draws the chart with a {@code null} info object to make sure that * no exceptions are thrown (a problem that was occurring at one point). */ @Test public void testDrawWithNullInfo() { try { BufferedImage image = new BufferedImage(200 , 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); this.chart.draw(g2, new Rectangle2D.Double(0, 0, 200, 100), null, null); g2.dispose(); } catch (Exception e) { fail("There should be no exception."); } } /** * Draws the chart with a {@code null} info object to make sure that * no exceptions are thrown (a problem that was occurring at one point). */ @Test public void testDrawWithNullInfo2() { JFreeChart chart = createGanttChart(); CategoryPlot plot = (CategoryPlot) chart.getPlot(); plot.setDataset(createDataset()); /* BufferedImage img =*/ chart.createBufferedImage(300, 200, null); //FIXME we should really assert a value } /** * Replaces the chart's dataset and then checks that the new dataset is OK. */ @Test public void testReplaceDataset() { LocalListener l = new LocalListener(); this.chart.addChangeListener(l); CategoryPlot plot = (CategoryPlot) this.chart.getPlot(); plot.setDataset(null); assertTrue(l.flag); } /** * Check that setting a tool tip generator for a series does override the * default generator. */ @Test public void testSetSeriesToolTipGenerator() { CategoryPlot plot = (CategoryPlot) this.chart.getPlot(); CategoryItemRenderer renderer = plot.getRenderer(); StandardCategoryToolTipGenerator tt = new StandardCategoryToolTipGenerator(); renderer.setSeriesToolTipGenerator(0, tt); CategoryToolTipGenerator tt2 = renderer.getToolTipGenerator(0, 0); assertSame(tt2, tt); } /** * Check that setting a URL generator for a series does override the * default generator. */ @Test public void testSetSeriesURLGenerator() { CategoryPlot plot = (CategoryPlot) this.chart.getPlot(); CategoryItemRenderer renderer = plot.getRenderer(); StandardCategoryURLGenerator url1 = new StandardCategoryURLGenerator(); renderer.setSeriesItemURLGenerator(0, url1); CategoryURLGenerator url2 = renderer.getItemURLGenerator(0, 0); assertSame(url2, url1); } /** * Create a Gantt chart. * * @return The chart. */ private static JFreeChart createGanttChart() { return ChartFactory.createGanttChart( "Gantt Chart", "Domain", "Range", null, true, // include legend true, true ); } /** * Creates a sample dataset for a Gantt chart. * * @return The dataset. */ public static IntervalCategoryDataset createDataset() { TaskSeries s1 = new TaskSeries("Scheduled"); s1.add(new Task("Write Proposal", new SimpleTimePeriod(date(1, Calendar.APRIL, 2001), date(5, Calendar.APRIL, 2001)))); s1.add(new Task("Obtain Approval", new SimpleTimePeriod(date(9, Calendar.APRIL, 2001), date(9, Calendar.APRIL, 2001)))); s1.add(new Task("Requirements Analysis", new SimpleTimePeriod(date(10, Calendar.APRIL, 2001), date(5, Calendar.MAY, 2001)))); s1.add(new Task("Design Phase", new SimpleTimePeriod(date(6, Calendar.MAY, 2001), date(30, Calendar.MAY, 2001)))); s1.add(new Task("Design Signoff", new SimpleTimePeriod(date(2, Calendar.JUNE, 2001), date(2, Calendar.JUNE, 2001)))); s1.add(new Task("Alpha Implementation", new SimpleTimePeriod(date(3, Calendar.JUNE, 2001), date(31, Calendar.JULY, 2001)))); s1.add(new Task("Design Review", new SimpleTimePeriod(date(1, Calendar.AUGUST, 2001), date(8, Calendar.AUGUST, 2001)))); s1.add(new Task("Revised Design Signoff", new SimpleTimePeriod(date(10, Calendar.AUGUST, 2001), date(10, Calendar.AUGUST, 2001)))); s1.add(new Task("Beta Implementation", new SimpleTimePeriod(date(12, Calendar.AUGUST, 2001), date(12, Calendar.SEPTEMBER, 2001)))); s1.add(new Task("Testing", new SimpleTimePeriod(date(13, Calendar.SEPTEMBER, 2001), date(31, Calendar.OCTOBER, 2001)))); s1.add(new Task("Final Implementation", new SimpleTimePeriod(date(1, Calendar.NOVEMBER, 2001), date(15, Calendar.NOVEMBER, 2001)))); s1.add(new Task("Signoff", new SimpleTimePeriod(date(28, Calendar.NOVEMBER, 2001), date(30, Calendar.NOVEMBER, 2001)))); TaskSeries s2 = new TaskSeries("Actual"); s2.add(new Task("Write Proposal", new SimpleTimePeriod(date(1, Calendar.APRIL, 2001), date(5, Calendar.APRIL, 2001)))); s2.add(new Task("Obtain Approval", new SimpleTimePeriod(date(9, Calendar.APRIL, 2001), date(9, Calendar.APRIL, 2001)))); s2.add(new Task("Requirements Analysis", new SimpleTimePeriod(date(10, Calendar.APRIL, 2001), date(15, Calendar.MAY, 2001)))); s2.add(new Task("Design Phase", new SimpleTimePeriod(date(15, Calendar.MAY, 2001), date(17, Calendar.JUNE, 2001)))); s2.add(new Task("Design Signoff", new SimpleTimePeriod(date(30, Calendar.JUNE, 2001), date(30, Calendar.JUNE, 2001)))); s2.add(new Task("Alpha Implementation", new SimpleTimePeriod(date(1, Calendar.JULY, 2001), date(12, Calendar.SEPTEMBER, 2001)))); s2.add(new Task("Design Review", new SimpleTimePeriod(date(12, Calendar.SEPTEMBER, 2001), date(22, Calendar.SEPTEMBER, 2001)))); s2.add(new Task("Revised Design Signoff", new SimpleTimePeriod(date(25, Calendar.SEPTEMBER, 2001), date(27, Calendar.SEPTEMBER, 2001)))); s2.add(new Task("Beta Implementation", new SimpleTimePeriod(date(27, Calendar.SEPTEMBER, 2001), date(30, Calendar.OCTOBER, 2001)))); s2.add(new Task("Testing", new SimpleTimePeriod(date(31, Calendar.OCTOBER, 2001), date(17, Calendar.NOVEMBER, 2001)))); s2.add(new Task("Final Implementation", new SimpleTimePeriod(date(18, Calendar.NOVEMBER, 2001), date(5, Calendar.DECEMBER, 2001)))); s2.add(new Task("Signoff", new SimpleTimePeriod(date(10, Calendar.DECEMBER, 2001), date(11, Calendar.DECEMBER, 2001)))); TaskSeriesCollection collection = new TaskSeriesCollection(); collection.add(s1); collection.add(s2); return collection; } /** * Utility method for creating {@code Date} objects. * * @param day the date. * @param month the month. * @param year the year. * * @return a date. */ private static Date date(int day, int month, int year) { Calendar calendar = Calendar.getInstance(); calendar.set(year, month, day); Date result = calendar.getTime(); return result; } /** * A chart change listener. * */ static class LocalListener implements ChartChangeListener { /** A flag. */ private boolean flag; /** * Event handler. * * @param event the event. */ @Override public void chartChanged(ChartChangeEvent event) { this.flag = true; } } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/HashUtilsTest.java000066400000000000000000000041121463604235500265160ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * HashUtilsTest.java * ------------------ * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link HashUtils} class. */ public class HashUtilsTest { /** * Some sanity checks for the hashCodeForDoubleArray() method. */ @Test public void testHashCodeForDoubleArray() { double[] a1 = new double[] {1.0}; double[] a2 = new double[] {1.0}; int h1 = HashUtils.hashCodeForDoubleArray(a1); int h2 = HashUtils.hashCodeForDoubleArray(a2); assertEquals(h1, h2); double[] a3 = new double[] {0.5, 1.0}; int h3 = HashUtils.hashCodeForDoubleArray(a3); assertNotEquals(h1, h3); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/JFreeChartTest.java000066400000000000000000000424371463604235500266030ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * JFreeChartTest.java * ------------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.event.ChartChangeEvent; import org.jfree.chart.event.ChartChangeListener; import org.jfree.chart.plot.PiePlot; import org.jfree.chart.plot.Plot; import org.jfree.chart.plot.RingPlot; import org.jfree.chart.title.LegendTitle; import org.jfree.chart.title.TextTitle; import org.jfree.chart.title.Title; import org.jfree.chart.ui.Align; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.RectangleInsets; import org.jfree.data.category.DefaultCategoryDataset; import org.jfree.data.general.DefaultPieDataset; import org.jfree.data.time.Day; import org.jfree.data.time.RegularTimePeriod; import org.jfree.data.time.TimeSeries; import org.jfree.data.time.TimeSeriesCollection; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import javax.swing.event.EventListenerList; import java.awt.*; import java.util.List; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link JFreeChart} class. */ class JFreeChartTest implements ChartChangeListener { /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test void testEqualsHashCode() { EqualsVerifier.forClass(JFreeChart.class) .withPrefabValues(TextTitle.class, new TextTitle("tee"), new TextTitle("vee")) .withPrefabValues(Plot.class, TestUtils.createPlot(true), TestUtils.createPlot(false)) .withPrefabValues(RenderingHints.class, new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF), new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON)) .withPrefabValues(EventListenerList.class, new EventListenerList(), new EventListenerList()) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** A pie chart. */ private JFreeChart pieChart; /** * Common test setup. */ @BeforeEach public void setUp() { DefaultPieDataset data = new DefaultPieDataset<>(); data.setValue("Java", 43.2); data.setValue("Visual Basic", 0.0); data.setValue("C/C++", 17.5); this.pieChart = ChartFactory.createPieChart("Pie Chart", data); } /** * Check that the equals() method can distinguish all fields. */ @Test void testEquals() { JFreeChart chart1 = new JFreeChart("Title", new Font("SansSerif", Font.PLAIN, 12), new PiePlot(), true); JFreeChart chart2 = new JFreeChart("Title", new Font("SansSerif", Font.PLAIN, 12), new PiePlot(), true); assertEquals(chart1, chart2); assertEquals(chart2, chart1); // renderingHints chart1.setRenderingHints(new RenderingHints( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON)); assertNotEquals(chart1, chart2); chart2.setRenderingHints(new RenderingHints( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON)); assertEquals(chart1, chart2); // borderVisible chart1.setBorderVisible(true); assertNotEquals(chart1, chart2); chart2.setBorderVisible(true); assertEquals(chart1, chart2); // borderStroke BasicStroke s = new BasicStroke(2.0f); chart1.setBorderStroke(s); assertNotEquals(chart1, chart2); chart2.setBorderStroke(s); assertEquals(chart1, chart2); // borderPaint chart1.setBorderPaint(Color.RED); assertNotEquals(chart1, chart2); chart2.setBorderPaint(Color.RED); assertEquals(chart1, chart2); // padding chart1.setPadding(new RectangleInsets(1, 2, 3, 4)); assertNotEquals(chart1, chart2); chart2.setPadding(new RectangleInsets(1, 2, 3, 4)); assertEquals(chart1, chart2); // title chart1.setTitle("XYZ"); assertNotEquals(chart1, chart2); chart2.setTitle("XYZ"); assertEquals(chart1, chart2); // subtitles chart1.addSubtitle(new TextTitle("Subtitle")); assertNotEquals(chart1, chart2); chart2.addSubtitle(new TextTitle("Subtitle")); assertEquals(chart1, chart2); // plot chart1 = new JFreeChart("Title", new Font("SansSerif", Font.PLAIN, 12), new RingPlot(), false); chart2 = new JFreeChart("Title", new Font("SansSerif", Font.PLAIN, 12), new PiePlot(), false); assertNotEquals(chart1, chart2); chart2 = new JFreeChart("Title", new Font("SansSerif", Font.PLAIN, 12), new RingPlot(), false); assertEquals(chart1, chart2); // backgroundPaint chart1.setBackgroundPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); assertNotEquals(chart1, chart2); chart2.setBackgroundPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); assertEquals(chart1, chart2); // // backgroundImage // chart1.setBackgroundImage(JFreeChart.INFO.getLogo()); // assertFalse(chart1.equals(chart2)); // chart2.setBackgroundImage(JFreeChart.INFO.getLogo()); // assertEquals(chart1, chart2); // backgroundImageAlignment chart1.setBackgroundImageAlignment(Align.BOTTOM_LEFT); assertNotEquals(chart1, chart2); chart2.setBackgroundImageAlignment(Align.BOTTOM_LEFT); assertEquals(chart1, chart2); // backgroundImageAlpha chart1.setBackgroundImageAlpha(0.1f); assertNotEquals(chart1, chart2); chart2.setBackgroundImageAlpha(0.1f); assertEquals(chart1, chart2); } /** * A test to make sure that the legend is being picked up in the * equals() testing. */ @Test void testEquals2() { JFreeChart chart1 = new JFreeChart("Title", new Font("SansSerif", Font.PLAIN, 12), new PiePlot(), true); JFreeChart chart2 = new JFreeChart("Title", new Font("SansSerif", Font.PLAIN, 12), new PiePlot(), false); assertNotEquals(chart1, chart2); assertNotEquals(chart2, chart1); } /** * Checks the subtitle count - should be 1 (the legend). */ @Test void testSubtitleCount() { int count = this.pieChart.getSubtitleCount(); assertEquals(1, count); } /** * Some checks for the getSubtitle() method. */ @Test void testGetSubtitle() { DefaultPieDataset dataset = new DefaultPieDataset<>(); JFreeChart chart = ChartFactory.createPieChart("title", dataset); Title t = chart.getSubtitle(0); assertTrue(t instanceof LegendTitle); try { chart.getSubtitle(-1); fail("Should have thrown an IllegalArgumentException on negative number"); } catch (IllegalArgumentException e) { assertEquals("Index out of range.", e.getMessage()); } try { chart.getSubtitle(1); fail("Should have thrown an IllegalArgumentException on excesive number"); } catch (IllegalArgumentException e) { assertEquals("Index out of range.", e.getMessage()); } try { chart.getSubtitle(2); fail("Should have thrown an IllegalArgumentException on number being out of range"); } catch (IllegalArgumentException e) { assertEquals("Index out of range.", e.getMessage()); } } /** * Serialize a pie chart, restore it, and check for equality. */ @Test void testSerialization1() { DefaultPieDataset data = new DefaultPieDataset<>(); data.setValue("Type 1", 54.5); data.setValue("Type 2", 23.9); data.setValue("Type 3", 45.8); JFreeChart c1 = ChartFactory.createPieChart("Test", data); JFreeChart c2 = TestUtils.serialised(c1); assertEquals(c1, c2); LegendTitle lt2 = c2.getLegend(); assertSame(lt2.getSources()[0], c2.getPlot()); } /** * Serialize a 3D pie chart, restore it, and check for equality. */ @Test void testSerialization2() { DefaultPieDataset data = new DefaultPieDataset<>(); data.setValue("Type 1", 54.5); data.setValue("Type 2", 23.9); data.setValue("Type 3", 45.8); JFreeChart c1 = ChartFactory.createPieChart("Test", data); JFreeChart c2 = TestUtils.serialised(c1); assertEquals(c1, c2); } /** * Serialize a bar chart, restore it, and check for equality. */ @Test void testSerialization3() { // row keys... String series1 = "First"; String series2 = "Second"; String series3 = "Third"; // column keys... String category1 = "Category 1"; String category2 = "Category 2"; String category3 = "Category 3"; String category4 = "Category 4"; String category5 = "Category 5"; String category6 = "Category 6"; String category7 = "Category 7"; String category8 = "Category 8"; // create the dataset... DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.addValue(1.0, series1, category1); dataset.addValue(4.0, series1, category2); dataset.addValue(3.0, series1, category3); dataset.addValue(5.0, series1, category4); dataset.addValue(5.0, series1, category5); dataset.addValue(7.0, series1, category6); dataset.addValue(7.0, series1, category7); dataset.addValue(8.0, series1, category8); dataset.addValue(5.0, series2, category1); dataset.addValue(7.0, series2, category2); dataset.addValue(6.0, series2, category3); dataset.addValue(8.0, series2, category4); dataset.addValue(4.0, series2, category5); dataset.addValue(4.0, series2, category6); dataset.addValue(2.0, series2, category7); dataset.addValue(1.0, series2, category8); dataset.addValue(4.0, series3, category1); dataset.addValue(3.0, series3, category2); dataset.addValue(2.0, series3, category3); dataset.addValue(3.0, series3, category4); dataset.addValue(6.0, series3, category5); dataset.addValue(3.0, series3, category6); dataset.addValue(4.0, series3, category7); dataset.addValue(3.0, series3, category8); // create the chart... JFreeChart c1 = ChartFactory.createBarChart("Vertical Bar Chart", "Category", "Value", dataset); JFreeChart c2 = TestUtils.serialised(c1); assertEquals(c1, c2); } /** * Serialize a time seroes chart, restore it, and check for equality. */ @Test void testSerialization4() { RegularTimePeriod t = new Day(); TimeSeries series = new TimeSeries("Series 1"); series.add(t, 36.4); t = t.next(); series.add(t, 63.5); TimeSeriesCollection dataset = new TimeSeriesCollection(); dataset.addSeries(series); JFreeChart c1 = ChartFactory.createTimeSeriesChart("Test", "Date", "Value", dataset); JFreeChart c2 = TestUtils.serialised(c1); assertEquals(c1, c2); } /** * Some checks for the addSubtitle() methods. */ @Test void testAddSubtitle() { DefaultPieDataset dataset = new DefaultPieDataset<>(); JFreeChart chart = ChartFactory.createPieChart("title", dataset); TextTitle t0 = new TextTitle("T0"); chart.addSubtitle(0, t0); assertEquals(t0, chart.getSubtitle(0)); TextTitle t1 = new TextTitle("T1"); chart.addSubtitle(t1); assertEquals(t1, chart.getSubtitle(2)); // subtitle 1 is the legend try { chart.addSubtitle(null); fail("Should have thrown an IllegalArgumentException on index out of range"); } catch (IllegalArgumentException e) { assertEquals("Null 'subtitle' argument.", e.getMessage()); } try { chart.addSubtitle(-1, t0); fail("Should have thrown an IllegalArgumentException on index out of range"); } catch (IllegalArgumentException e) { assertEquals("The 'index' argument is out of range.", e.getMessage()); } try { chart.addSubtitle(4, t0); fail("Should have thrown an IllegalArgumentException on index out of range"); } catch (IllegalArgumentException e) { assertEquals("The 'index' argument is out of range.", e.getMessage()); } } /** * Some checks for the getSubtitles() method. */ @Test void testGetSubtitles() { DefaultPieDataset dataset = new DefaultPieDataset<>(); JFreeChart chart = ChartFactory.createPieChart("title", dataset); List subtitles = chart.getSubtitles(); assertEquals(1, chart.getSubtitleCount()); // adding something to the returned list should NOT change the chart subtitles.add(new TextTitle("T")); assertEquals(1, chart.getSubtitleCount()); } /** * Some checks for the default legend firing change events. */ @Test void testLegendEvents() { DefaultPieDataset dataset = new DefaultPieDataset<>(); JFreeChart chart = ChartFactory.createPieChart("title", dataset); chart.addChangeListener(this); this.lastChartChangeEvent = null; LegendTitle legend = chart.getLegend(); legend.setPosition(RectangleEdge.TOP); assertNotNull(this.lastChartChangeEvent); } /** * Some checks for title changes and event notification. */ @Test void testTitleChangeEvent() { DefaultPieDataset dataset = new DefaultPieDataset(); JFreeChart chart = ChartFactory.createPieChart("title", dataset); chart.addChangeListener(this); this.lastChartChangeEvent = null; TextTitle t = chart.getTitle(); t.setFont(new Font("Dialog", Font.BOLD, 9)); assertNotNull(this.lastChartChangeEvent); this.lastChartChangeEvent = null; // now create a new title and replace the existing title, several // things should happen: // (1) Adding the new title should trigger an immediate // ChartChangeEvent; // (2) Modifying the new title should trigger a ChartChangeEvent; // (3) Modifying the old title should NOT trigger a ChartChangeEvent TextTitle t2 = new TextTitle("T2"); chart.setTitle(t2); assertNotNull(this.lastChartChangeEvent); this.lastChartChangeEvent = null; t2.setFont(new Font("Dialog", Font.BOLD, 9)); assertNotNull(this.lastChartChangeEvent); this.lastChartChangeEvent = null; t.setFont(new Font("Dialog", Font.BOLD, 9)); assertNull(this.lastChartChangeEvent); this.lastChartChangeEvent = null; } @Test void testBug942() { final String title = "Pie Chart Demo 1\n\n\ntestnew line"; assertEquals(title, ChartFactory.createPieChart(title, new DefaultPieDataset()).getTitle().getText()); } /** The last ChartChangeEvent received. */ private ChartChangeEvent lastChartChangeEvent; /** * Records the last chart change event. * * @param event the event. */ @Override public void chartChanged(ChartChangeEvent event) { this.lastChartChangeEvent = event; } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/LegendItemCollectionTest.java000066400000000000000000000106621463604235500306520ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------- * LegendItemCollectionTest.java * ----------------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart; import java.awt.BasicStroke; import java.awt.Color; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link LegendItemCollection} class. */ public class LegendItemCollectionTest { /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(LegendItemCollection.class) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { LegendItemCollection c1 = new LegendItemCollection(); LegendItemCollection c2 = new LegendItemCollection(); assertEquals(c1, c2); assertEquals(c2, c1); LegendItem item1 = new LegendItem("Label", "Description", "ToolTip", "URL", true, new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), true, Color.RED, true, Color.BLUE, new BasicStroke(1.2f), true, new Line2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(2.1f), Color.GREEN); LegendItem item2 = new LegendItem("Label", "Description", "ToolTip", "URL", true, new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), true, Color.RED, true, Color.BLUE, new BasicStroke(1.2f), true, new Line2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(2.1f), Color.GREEN); c1.add(item1); assertNotEquals(c1, c2); c2.add(item2); assertEquals(c1, c2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { LegendItemCollection c1 = new LegendItemCollection(); c1.add(new LegendItem("Item", "Description", "ToolTip", "URL", new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), Color.RED)); LegendItemCollection c2 = TestUtils.serialised(c1); assertEquals(c1, c2); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { LegendItemCollection c1 = new LegendItemCollection(); LegendItem item1 = new LegendItem("Item 1"); c1.add(item1); LegendItemCollection c2 = (LegendItemCollection) c1.clone(); assertNotSame(c1, c2); assertSame(c1.getClass(), c2.getClass()); assertEquals(c1, c2); Rectangle2D item1Shape = (Rectangle2D) item1.getShape(); item1Shape.setRect(1.0, 2.0, 3.0, 4.0); assertNotEquals(c1, c2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/LegendItemTest.java000066400000000000000000000407601463604235500266400ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * LegendItemTest.java * ------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.GradientPaint; import java.awt.font.TextAttribute; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.text.AttributedString; import org.jfree.chart.ui.GradientPaintTransformType; import org.jfree.chart.ui.StandardGradientPaintTransformer; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link LegendItem} class. */ public class LegendItemTest { /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(LegendItem.class) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .withPrefabValues(AttributedString.class, TestUtils.createAS(true), TestUtils.createAS(false)) .verify(); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { LegendItem item1 = new LegendItem("Label", "Description", "ToolTip", "URL", true, new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), true, Color.RED, true, Color.BLUE, new BasicStroke(1.2f), true, new Line2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(2.1f), Color.GREEN); LegendItem item2 = new LegendItem("Label", "Description", "ToolTip", "URL", true, new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), true, Color.RED, true, Color.BLUE, new BasicStroke(1.2f), true, new Line2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(2.1f), Color.GREEN); assertEquals(item1, item2); assertEquals(item2, item1); item1 = new LegendItem("Label2", "Description", "ToolTip", "URL", true, new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), true, Color.RED, true, Color.BLUE, new BasicStroke(1.2f), true, new Line2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(2.1f), Color.GREEN); assertNotEquals(item1, item2); item2 = new LegendItem("Label2", "Description", "ToolTip", "URL", true, new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), true, Color.RED, true, Color.BLUE, new BasicStroke(1.2f), true, new Line2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(2.1f), Color.GREEN); assertEquals(item1, item2); item1 = new LegendItem("Label2", "Description2", "ToolTip", "URL", true, new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), true, Color.RED, true, Color.BLUE, new BasicStroke(1.2f), true, new Line2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(2.1f), Color.GREEN); assertNotEquals(item1, item2); item2 = new LegendItem("Label2", "Description2", "ToolTip", "URL", true, new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), true, Color.RED, true, Color.BLUE, new BasicStroke(1.2f), true, new Line2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(2.1f), Color.GREEN); assertEquals(item1, item2); item1 = new LegendItem("Label2", "Description2", "ToolTip", "URL", false, new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), true, Color.RED, true, Color.BLUE, new BasicStroke(1.2f), true, new Line2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(2.1f), Color.GREEN); assertNotEquals(item1, item2); item2 = new LegendItem("Label2", "Description2", "ToolTip", "URL", false, new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), true, Color.RED, true, Color.BLUE, new BasicStroke(1.2f), true, new Line2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(2.1f), Color.GREEN); assertEquals(item1, item2); item1 = new LegendItem("Label2", "Description2", "ToolTip", "URL", false, new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0), true, Color.RED, true, Color.BLUE, new BasicStroke(1.2f), true, new Line2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(2.1f), Color.GREEN); assertNotEquals(item1, item2); item2 = new LegendItem("Label2", "Description2", "ToolTip", "URL", false, new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0), true, Color.RED, true, Color.BLUE, new BasicStroke(1.2f), true, new Line2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(2.1f), Color.GREEN); assertEquals(item1, item2); item1 = new LegendItem("Label2", "Description2", "ToolTip", "URL", false, new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0), false, Color.RED, true, Color.BLUE, new BasicStroke(1.2f), true, new Line2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(2.1f), Color.GREEN); assertNotEquals(item1, item2); item2 = new LegendItem("Label2", "Description2", "ToolTip", "URL", false, new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0), false, Color.RED, true, Color.BLUE, new BasicStroke(1.2f), true, new Line2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(2.1f), Color.GREEN); assertEquals(item1, item2); item1 = new LegendItem("Label2", "Description2", "ToolTip", "URL", false, new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0), false, Color.BLACK, true, Color.BLUE, new BasicStroke(1.2f), true, new Line2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(2.1f), Color.GREEN); assertNotEquals(item1, item2); item2 = new LegendItem("Label2", "Description2", "ToolTip", "URL", false, new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0), false, Color.BLACK, true, Color.BLUE, new BasicStroke(1.2f), true, new Line2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(2.1f), Color.GREEN); assertEquals(item1, item2); item1 = new LegendItem("Label2", "Description2", "ToolTip", "URL", false, new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0), false, Color.BLACK, false, Color.BLUE, new BasicStroke(1.2f), true, new Line2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(2.1f), Color.GREEN); assertNotEquals(item1, item2); item2 = new LegendItem("Label2", "Description2", "ToolTip", "URL", false, new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0), false, Color.BLACK, false, Color.BLUE, new BasicStroke(1.2f), true, new Line2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(2.1f), Color.GREEN); assertEquals(item1, item2); item1 = new LegendItem("Label2", "Description2", "ToolTip", "URL", false, new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0), false, Color.BLACK, false, Color.YELLOW, new BasicStroke(1.2f), true, new Line2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(2.1f), Color.GREEN); assertNotEquals(item1, item2); item2 = new LegendItem("Label2", "Description2", "ToolTip", "URL", false, new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0), false, Color.BLACK, false, Color.YELLOW, new BasicStroke(1.2f), true, new Line2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(2.1f), Color.GREEN); assertEquals(item1, item2); item1 = new LegendItem("Label2", "Description2", "ToolTip", "URL", false, new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0), false, Color.BLACK, false, Color.YELLOW, new BasicStroke(2.1f), true, new Line2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(2.1f), Color.GREEN); assertNotEquals(item1, item2); item2 = new LegendItem("Label2", "Description2", "ToolTip", "URL", false, new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0), false, Color.BLACK, false, Color.YELLOW, new BasicStroke(2.1f), true, new Line2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(2.1f), Color.GREEN); assertEquals(item1, item2); item1 = new LegendItem("Label2", "Description2", "ToolTip", "URL", false, new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0), false, Color.BLACK, false, Color.YELLOW, new BasicStroke(2.1f), false, new Line2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(2.1f), Color.GREEN); assertNotEquals(item1, item2); item2 = new LegendItem("Label2", "Description2", "ToolTip", "URL", false, new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0), false, Color.BLACK, false, Color.YELLOW, new BasicStroke(2.1f), false, new Line2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(2.1f), Color.GREEN); assertEquals(item1, item2); item1 = new LegendItem("Label2", "Description2", "ToolTip", "URL", false, new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0), false, Color.BLACK, false, Color.YELLOW, new BasicStroke(2.1f), false, new Line2D.Double(4.0, 3.0, 2.0, 1.0), new BasicStroke(2.1f), Color.GREEN); assertNotEquals(item1, item2); item2 = new LegendItem("Label2", "Description2", "ToolTip", "URL", false, new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0), false, Color.BLACK, false, Color.YELLOW, new BasicStroke(2.1f), false, new Line2D.Double(4.0, 3.0, 2.0, 1.0), new BasicStroke(2.1f), Color.GREEN); assertEquals(item1, item2); item1 = new LegendItem("Label2", "Description2", "ToolTip", "URL", false, new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0), false, Color.BLACK, false, Color.YELLOW, new BasicStroke(2.1f), false, new Line2D.Double(4.0, 3.0, 2.0, 1.0), new BasicStroke(3.3f), Color.GREEN); assertNotEquals(item1, item2); item2 = new LegendItem("Label2", "Description2", "ToolTip", "URL", false, new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0), false, Color.BLACK, false, Color.YELLOW, new BasicStroke(2.1f), false, new Line2D.Double(4.0, 3.0, 2.0, 1.0), new BasicStroke(3.3f), Color.GREEN); assertEquals(item1, item2); item1 = new LegendItem("Label2", "Description2", "ToolTip", "URL", false, new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0), false, Color.BLACK, false, Color.YELLOW, new BasicStroke(2.1f), false, new Line2D.Double(4.0, 3.0, 2.0, 1.0), new BasicStroke(3.3f), Color.WHITE ); assertNotEquals(item1, item2); item2 = new LegendItem("Label2", "Description2", "ToolTip", "URL", false, new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0), false, Color.BLACK, false, Color.YELLOW, new BasicStroke(2.1f), false, new Line2D.Double(4.0, 3.0, 2.0, 1.0), new BasicStroke(3.3f), Color.WHITE); assertEquals(item1, item2); // fillPaintTransformer item1.setFillPaintTransformer(new StandardGradientPaintTransformer( GradientPaintTransformType.CENTER_VERTICAL)); assertNotEquals(item1, item2); item2.setFillPaintTransformer(new StandardGradientPaintTransformer( GradientPaintTransformType.CENTER_VERTICAL)); assertEquals(item1, item2); // labelFont item1.setLabelFont(new Font("Dialog", Font.PLAIN, 13)); assertNotEquals(item1, item2); item2.setLabelFont(new Font("Dialog", Font.PLAIN, 13)); assertEquals(item1, item2); // labelPaint item1.setLabelPaint(Color.RED); assertNotEquals(item1, item2); item2.setLabelPaint(Color.RED); assertEquals(item1, item2); // fillPaint item1.setFillPaint(new GradientPaint(1.0f, 2.0f, Color.GREEN, 3.0f, 4.0f, Color.BLUE)); assertNotEquals(item1, item2); item2.setFillPaint(new GradientPaint(1.0f, 2.0f, Color.GREEN, 3.0f, 4.0f, Color.BLUE)); assertEquals(item1, item2); // outlinePaint item1.setOutlinePaint(new GradientPaint(1.1f, 2.2f, Color.GREEN, 3.3f, 4.4f, Color.BLUE)); assertNotEquals(item1, item2); item2.setOutlinePaint(new GradientPaint(1.1f, 2.2f, Color.GREEN, 3.3f, 4.4f, Color.BLUE)); assertEquals(item1, item2); // linePaint item1.setLinePaint(new GradientPaint(0.1f, 0.2f, Color.GREEN, 0.3f, 0.4f, Color.BLUE)); assertNotEquals(item1, item2); item2.setLinePaint(new GradientPaint(0.1f, 0.2f, Color.GREEN, 0.3f, 0.4f, Color.BLUE)); assertEquals(item1, item2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { LegendItem item1 = new LegendItem("Item", "Description", "ToolTip", "URL", new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), new GradientPaint( 5.0f, 6.0f, Color.BLUE, 7.0f, 8.0f, Color.GRAY)); item1.setLabelPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW)); item1.setOutlinePaint(new GradientPaint(4.0f, 3.0f, Color.GREEN, 2.0f, 1.0f, Color.RED)); item1.setLinePaint(new GradientPaint(1.0f, 2.0f, Color.WHITE, 3.0f, 4.0f, Color.RED)); LegendItem item2; item2 = TestUtils.serialised(item1); assertEquals(item1, item2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization2() { AttributedString as = new AttributedString("Test String"); as.addAttribute(TextAttribute.FONT, new Font("Dialog", Font.PLAIN, 12)); LegendItem item1 = new LegendItem(as, "Description", "ToolTip", "URL", new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), Color.RED); LegendItem item2 = TestUtils.serialised(item1); assertEquals(item1, item2); } /** * Basic checks for cloning. * * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { LegendItem item1 = new LegendItem("Item"); LegendItem item2 = (LegendItem) item1.clone(); assertNotSame(item1, item2); assertSame(item1.getClass(), item2.getClass()); assertEquals(item1, item2); // the clone references the same dataset as the original assertSame(item1.getDataset(), item2.getDataset()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/LineChartTest.java000066400000000000000000000137041463604235500264720ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * LineChartTest.java * ------------------ * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.ChartChangeEvent; import org.jfree.chart.event.ChartChangeListener; import org.jfree.chart.labels.CategoryToolTipGenerator; import org.jfree.chart.labels.StandardCategoryToolTipGenerator; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.renderer.category.CategoryItemRenderer; import org.jfree.chart.urls.CategoryURLGenerator; import org.jfree.chart.urls.StandardCategoryURLGenerator; import org.jfree.data.Range; import org.jfree.data.category.CategoryDataset; import org.jfree.data.general.DatasetUtils; import org.junit.jupiter.api.Test; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.BeforeEach; /** * Some tests for a line chart. */ public class LineChartTest { /** A chart. */ private JFreeChart chart; /** * Common test setup. */ @BeforeEach public void setUp() { this.chart = createLineChart(); } /** * Draws the chart with a null info object to make sure that no exceptions * are thrown (a problem that was occurring at one point). */ @Test public void testDrawWithNullInfo() { BufferedImage image = new BufferedImage(200 , 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); this.chart.draw(g2, new Rectangle2D.Double(0, 0, 200, 100), null, null); g2.dispose(); //FIXME we should really assert a value here } /** * Replaces the chart's dataset and then checks that the new dataset is OK. */ @Test public void testReplaceDataset() { // create a dataset... Number[][] data = new Integer[][] {{-30, -20}, {-10, 10}, {20, 30}}; CategoryDataset newData = DatasetUtils.createCategoryDataset("S", "C", data); LocalListener l = new LocalListener(); this.chart.addChangeListener(l); CategoryPlot plot = (CategoryPlot) this.chart.getPlot(); plot.setDataset(newData); assertTrue(l.flag); ValueAxis axis = plot.getRangeAxis(); Range range = axis.getRange(); assertTrue(range.getLowerBound() <= -30, "Expecting the lower bound of the range to be around -30: " + range.getLowerBound()); assertTrue(range.getUpperBound() >= 30, "Expecting the upper bound of the range to be around 30: " + range.getUpperBound()); } /** * Check that setting a tool tip generator for a series does override the * default generator. */ @Test public void testSetSeriesToolTipGenerator() { CategoryPlot plot = (CategoryPlot) this.chart.getPlot(); CategoryItemRenderer renderer = plot.getRenderer(); StandardCategoryToolTipGenerator tt = new StandardCategoryToolTipGenerator(); renderer.setSeriesToolTipGenerator(0, tt); CategoryToolTipGenerator tt2 = renderer.getToolTipGenerator(0, 0); assertSame(tt2, tt); } /** * Check that setting a URL generator for a series does override the * default generator. */ @Test public void testSetSeriesURLGenerator() { CategoryPlot plot = (CategoryPlot) this.chart.getPlot(); CategoryItemRenderer renderer = plot.getRenderer(); StandardCategoryURLGenerator url1 = new StandardCategoryURLGenerator(); renderer.setSeriesItemURLGenerator(0, url1); CategoryURLGenerator url2 = renderer.getItemURLGenerator(0, 0); assertSame(url2, url1); } /** * Create a line chart with sample data in the range -3 to +3. * * @return The chart. */ private static JFreeChart createLineChart() { Number[][] data = new Integer[][] {{-3, -2}, {-1, 1}, {2, 3}}; CategoryDataset dataset = DatasetUtils.createCategoryDataset("S", "C", data); return ChartFactory.createLineChart("Line Chart", "Domain", "Range", dataset); } /** * A chart change listener. * */ static class LocalListener implements ChartChangeListener { /** A flag. */ private boolean flag; /** * Event handler. * * @param event the event. */ @Override public void chartChanged(ChartChangeEvent event) { this.flag = true; } } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/MeterChartTest.java000066400000000000000000000047071463604235500266620ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * MeterChartTest.java * ------------------- * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import org.jfree.chart.plot.MeterInterval; import org.jfree.chart.plot.MeterPlot; import org.jfree.data.Range; import org.jfree.data.general.DefaultValueDataset; import org.junit.jupiter.api.Test; /** * Miscellaneous checks for meter charts. */ public class MeterChartTest { /** * Draws the chart with a single range. At one point, this caused a null * pointer exception (fixed now). */ @Test public void testDrawWithNullInfo() { MeterPlot plot = new MeterPlot(new DefaultValueDataset(60.0)); plot.addInterval(new MeterInterval("Normal", new Range(0.0, 80.0))); JFreeChart chart = new JFreeChart(plot); BufferedImage image = new BufferedImage(200, 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); chart.draw(g2, new Rectangle2D.Double(0, 0, 200, 100), null, null); g2.dispose(); //FIXME we should really assert a value here } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/PaintMapTest.java000066400000000000000000000120741463604235500263310ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * PaintMapTest.java * ----------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart; import org.junit.jupiter.api.Test; import java.awt.Color; import java.awt.GradientPaint; import static org.junit.jupiter.api.Assertions.*; /** * Some tests for the {@link PaintMap} class. */ public class PaintMapTest { /** * Some checks for the getPaint() method. */ @Test public void testGetPaint() { PaintMap m1 = new PaintMap(); assertNull(m1.getPaint("A")); m1.put("A", Color.RED); assertEquals(Color.RED, m1.getPaint("A")); m1.put("A", null); assertNull(m1.getPaint("A")); // a null key should throw an IllegalArgumentException try { m1.getPaint(null); fail("IllegalArgumentException should have been thrown on passing null value"); } catch (IllegalArgumentException e) { assertEquals("Null 'key' argument.", e.getMessage()); } } /** * Some checks for the put() method. */ @Test public void testPut() { PaintMap m1 = new PaintMap(); m1.put("A", Color.RED); assertEquals(Color.RED, m1.getPaint("A")); // a null key should throw an IllegalArgumentException try { m1.put(null, Color.BLUE); fail("IllegalArgumentException should have been thrown on null key"); } catch (IllegalArgumentException e) { assertEquals("Null 'key' argument.", e.getMessage()); } } /** * Some checks for the equals() method. */ @Test public void testEquals() { PaintMap m1 = new PaintMap(); PaintMap m2 = new PaintMap(); assertEquals(m1, m1); assertEquals(m1, m2); assertNotEquals(null, m1); assertNotEquals("ABC", m1); m1.put("K1", Color.RED); assertNotEquals(m1, m2); m2.put("K1", Color.RED); assertEquals(m1, m2); m1.put("K2", new GradientPaint(1.0f, 2.0f, Color.GREEN, 3.0f, 4.0f, Color.YELLOW)); assertNotEquals(m1, m2); m2.put("K2", new GradientPaint(1.0f, 2.0f, Color.GREEN, 3.0f, 4.0f, Color.YELLOW)); assertEquals(m1, m2); m1.put("K2", null); assertNotEquals(m1, m2); m2.put("K2", null); assertEquals(m1, m2); } /** * Some checks for cloning. * * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { PaintMap m1 = new PaintMap(); PaintMap m2 = (PaintMap) m1.clone(); assertEquals(m1, m2); m1.put("K1", Color.RED); m1.put("K2", new GradientPaint(1.0f, 2.0f, Color.GREEN, 3.0f, 4.0f, Color.YELLOW)); m2 = (PaintMap) m1.clone(); assertEquals(m1, m2); } /** * A check for serialization. */ @Test public void testSerialization1() { PaintMap m1 = new PaintMap(); PaintMap m2 = TestUtils.serialised(m1); assertEquals(m1, m2); } /** * A check for serialization. */ @Test public void testSerialization2() { PaintMap m1 = new PaintMap(); m1.put("K1", Color.RED); m1.put("K2", new GradientPaint(1.0f, 2.0f, Color.GREEN, 3.0f, 4.0f, Color.YELLOW)); PaintMap m2 = TestUtils.serialised(m1); assertEquals(m1, m2); } /** * This test covers a bug reported in the forum: * * http://www.jfree.org/phpBB2/viewtopic.php?t=19980 */ @Test public void testKeysOfDifferentClasses() { PaintMap m = new PaintMap(); m.put("ABC", Color.RED); m.put(99, Color.BLUE); assertEquals(Color.BLUE, m.getPaint(99)); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/PieChart3DTest.java000066400000000000000000000102751463604235500265070ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * PieChart3DTest.java * ------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import org.jfree.chart.event.ChartChangeEvent; import org.jfree.chart.event.ChartChangeListener; import org.jfree.chart.plot.PiePlot; import org.jfree.data.general.DefaultPieDataset; import org.jfree.data.general.PieDataset; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for a pie chart with a 3D effect. */ public class PieChart3DTest { /** A chart. */ private JFreeChart pieChart; /** * Common test setup. */ @BeforeEach public void setUp() { // create a dataset... DefaultPieDataset dataset = new DefaultPieDataset(); dataset.setValue("Java", 43.2); dataset.setValue("Visual Basic", 0.0); dataset.setValue("C/C++", 17.5); this.pieChart = createPieChart3D(dataset); } /** * Using a regular pie chart, we replace the dataset with null. Expect to * receive notification of a chart change event, and (of course) the * dataset should be null. */ @Test public void testReplaceDatasetOnPieChart() { LocalListener l = new LocalListener(); this.pieChart.addChangeListener(l); PiePlot plot = (PiePlot) this.pieChart.getPlot(); plot.setDataset(null); assertTrue(l.flag); assertNull(plot.getDataset()); } /** * Tests that no exceptions are thrown when there is a {@code null} * value in the dataset. */ @Test public void testNullValueInDataset() { DefaultPieDataset dataset = new DefaultPieDataset(); dataset.setValue("Section 1", 10.0); dataset.setValue("Section 2", 11.0); dataset.setValue("Section 3", null); JFreeChart chart = createPieChart3D(dataset); BufferedImage image = new BufferedImage(200 , 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); chart.draw(g2, new Rectangle2D.Double(0, 0, 200, 100), null, null); g2.dispose(); //FIXME we should really assert a value here } /** * Creates a pie chart. * * @param dataset the dataset. * * @return The pie chart. */ private static JFreeChart createPieChart3D(PieDataset dataset) { return ChartFactory.createPieChart3D("Pie Chart", dataset); } /** * A chart change listener. */ static class LocalListener implements ChartChangeListener { /** A flag. */ private boolean flag; /** * Event handler. * * @param event the event. */ @Override public void chartChanged(ChartChangeEvent event) { this.flag = true; } } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/PieChartTest.java000066400000000000000000000063051463604235500263170ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * PieChartTest.java * ----------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart; import org.jfree.chart.event.ChartChangeEvent; import org.jfree.chart.event.ChartChangeListener; import org.jfree.chart.plot.PiePlot; import org.jfree.data.general.DefaultPieDataset; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeEach; import static org.junit.jupiter.api.Assertions.*; /** * Tests for a pie chart. */ public class PieChartTest { /** A chart. */ private JFreeChart pieChart; /** * Common test setup. */ @BeforeEach public void setUp() { this.pieChart = createPieChart(); } /** * Using a regular pie chart, we replace the dataset with null. Expect to * receive notification of a chart change event, and (of course) the * dataset should be null. */ @Test public void testReplaceDatasetOnPieChart() { LocalListener l = new LocalListener(); this.pieChart.addChangeListener(l); PiePlot plot = (PiePlot) this.pieChart.getPlot(); plot.setDataset(null); assertTrue(l.flag); assertNull(plot.getDataset()); } /** * Creates a pie chart. * * @return The pie chart. */ private static JFreeChart createPieChart() { DefaultPieDataset data = new DefaultPieDataset(); data.setValue("Java", 43.2); data.setValue("Visual Basic", 0.0); data.setValue("C/C++", 17.5); return ChartFactory.createPieChart("Pie Chart", data); } /** * A chart change listener. */ static class LocalListener implements ChartChangeListener { /** A flag. */ private boolean flag; /** * Event handler. * * @param event the event. */ @Override public void chartChanged(ChartChangeEvent event) { this.flag = true; } } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/ScatterPlotTest.java000066400000000000000000000124761463604235500270720ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * ScatterPlotTest.java * -------------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.ChartChangeEvent; import org.jfree.chart.event.ChartChangeListener; import org.jfree.chart.labels.StandardXYToolTipGenerator; import org.jfree.chart.labels.XYToolTipGenerator; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.xy.XYItemRenderer; import org.jfree.data.Range; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for a scatter plot. */ public class ScatterPlotTest { /** A chart. */ private JFreeChart chart; /** * Common test setup. */ @BeforeEach public void setUp() { this.chart = createChart(); } /** * Draws the chart with a null info object to make sure that no exceptions * are thrown (a problem that was occurring at one point). */ @Test public void testDrawWithNullInfo() { try { BufferedImage image = new BufferedImage(200 , 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); this.chart.draw(g2, new Rectangle2D.Double(0, 0, 200, 100), null, null); g2.dispose(); } catch (Exception e) { fail("No exception should be thrown."); } } /** * Replaces the dataset and checks that it has changed as expected. */ @Test public void testReplaceDataset() { // create a dataset... XYSeries series1 = new XYSeries("Series 1"); series1.add(10.0, 10.0); series1.add(20.0, 20.0); series1.add(30.0, 30.0); XYDataset dataset = new XYSeriesCollection(series1); LocalListener l = new LocalListener(); this.chart.addChangeListener(l); XYPlot plot = (XYPlot) this.chart.getPlot(); plot.setDataset(dataset); assertTrue(l.flag); ValueAxis axis = plot.getRangeAxis(); Range range = axis.getRange(); assertTrue(range.getLowerBound() <= 10, "Expecting the lower bound of the range to be around 10: " + range.getLowerBound()); assertTrue(range.getUpperBound() >= 30, "Expecting the upper bound of the range to be around 30: " + range.getUpperBound()); } /** * Check that setting a tool tip generator for a series does override the * default generator. */ @Test public void testSetSeriesToolTipGenerator() { XYPlot plot = (XYPlot) this.chart.getPlot(); XYItemRenderer renderer = plot.getRenderer(); StandardXYToolTipGenerator tt = new StandardXYToolTipGenerator(); renderer.setSeriesToolTipGenerator(0, tt); XYToolTipGenerator tt2 = renderer.getToolTipGenerator(0, 0); assertSame(tt2, tt); } /** * Create a horizontal bar chart with sample data in the range -3 to +3. * * @return The chart. */ private static JFreeChart createChart() { XYSeries series1 = new XYSeries("Series 1"); series1.add(1.0, 1.0); series1.add(2.0, 2.0); series1.add(3.0, 3.0); XYDataset dataset = new XYSeriesCollection(series1); return ChartFactory.createScatterPlot("Scatter Plot", "Domain", "Range", dataset); } /** * A chart change listener. * */ static class LocalListener implements ChartChangeListener { /** A flag. */ private boolean flag = false; /** * Event handler. * * @param event the event. */ @Override public void chartChanged(ChartChangeEvent event) { this.flag = true; } } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/StackedAreaChartTest.java000066400000000000000000000142651463604235500277550ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * StackedAreaChartTest.java * ------------------------- * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.ChartChangeEvent; import org.jfree.chart.event.ChartChangeListener; import org.jfree.chart.labels.CategoryToolTipGenerator; import org.jfree.chart.labels.StandardCategoryToolTipGenerator; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.renderer.category.CategoryItemRenderer; import org.jfree.chart.urls.CategoryURLGenerator; import org.jfree.chart.urls.StandardCategoryURLGenerator; import org.jfree.data.Range; import org.jfree.data.category.CategoryDataset; import org.jfree.data.general.DatasetUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Some tests for a stacked area chart. */ public class StackedAreaChartTest { /** A chart. */ private JFreeChart chart; /** * Common test setup. */ @BeforeEach public void setUp() { this.chart = createChart(); } /** * Draws the chart with a null info object to make sure that no exceptions * are thrown (a problem that was occurring at one point). */ @Test public void testDrawWithNullInfo() { try { BufferedImage image = new BufferedImage(200 , 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); this.chart.draw(g2, new Rectangle2D.Double(0, 0, 200, 100), null, null); g2.dispose(); } catch (Exception e) { fail("There should be no exception."); } } /** * Replaces the dataset and checks that it has changed as expected. */ @Test public void testReplaceDataset() { Number[][] data = new Integer[][] {{-30, -20}, {-10, 10}, {20, 30}}; CategoryDataset newData = DatasetUtils.createCategoryDataset("S", "C", data); LocalListener l = new LocalListener(); this.chart.addChangeListener(l); CategoryPlot plot = (CategoryPlot) this.chart.getPlot(); plot.setDataset(newData); assertTrue(l.flag); ValueAxis axis = plot.getRangeAxis(); Range range = axis.getRange(); assertTrue(range.getLowerBound() <= -30, "Expecting the lower bound of the range to be around -30: " + range.getLowerBound()); assertTrue(range.getUpperBound() >= 30, "Expecting the upper bound of the range to be around 30: " + range.getUpperBound()); } /** * Check that setting a tool tip generator for a series does override the * default generator. */ @Test public void testSetSeriesToolTipGenerator() { CategoryPlot plot = (CategoryPlot) this.chart.getPlot(); CategoryItemRenderer renderer = plot.getRenderer(); StandardCategoryToolTipGenerator tt = new StandardCategoryToolTipGenerator(); renderer.setSeriesToolTipGenerator(0, tt); CategoryToolTipGenerator tt2 = renderer.getToolTipGenerator(0, 0); assertSame(tt2, tt); } /** * Check that setting a URL generator for a series does override the * default generator. */ @Test public void testSetSeriesURLGenerator() { CategoryPlot plot = (CategoryPlot) this.chart.getPlot(); CategoryItemRenderer renderer = plot.getRenderer(); StandardCategoryURLGenerator url1 = new StandardCategoryURLGenerator(); renderer.setSeriesItemURLGenerator(0, url1); CategoryURLGenerator url2 = renderer.getItemURLGenerator(0, 0); assertSame(url2, url1); } /** * Create a stacked bar chart with sample data in the range -3 to +3. * * @return The chart. */ private static JFreeChart createChart() { Number[][] data = new Integer[][] {{-3, -2}, {-1, 1}, {2, 3}}; CategoryDataset dataset = DatasetUtils.createCategoryDataset("S", "C", data); return ChartFactory.createStackedAreaChart( "Stacked Area Chart", // chart title "Domain", "Range", dataset, // data PlotOrientation.HORIZONTAL, true, // include legend true, true ); } /** * A chart change listener. */ static class LocalListener implements ChartChangeListener { /** A flag. */ private boolean flag = false; /** * Event handler. * * @param event the event. */ @Override public void chartChanged(ChartChangeEvent event) { this.flag = true; } } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/StackedBarChartTest.java000066400000000000000000000143171463604235500276070ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * StackedBarChartTest.java * ------------------------ * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.ChartChangeEvent; import org.jfree.chart.event.ChartChangeListener; import org.jfree.chart.labels.CategoryToolTipGenerator; import org.jfree.chart.labels.StandardCategoryToolTipGenerator; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.renderer.category.CategoryItemRenderer; import org.jfree.chart.urls.CategoryURLGenerator; import org.jfree.chart.urls.StandardCategoryURLGenerator; import org.jfree.data.Range; import org.jfree.data.category.CategoryDataset; import org.jfree.data.general.DatasetUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for a stacked bar chart. */ public class StackedBarChartTest { /** A chart. */ private JFreeChart chart; /** * Common test setup. */ @BeforeEach public void setUp() { this.chart = createChart(); } /** * Draws the chart with a null info object to make sure that no exceptions * are thrown (a problem that was occurring at one point). */ @Test public void testDrawWithNullInfo() { try { BufferedImage image = new BufferedImage(200 , 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); this.chart.draw(g2, new Rectangle2D.Double(0, 0, 200, 100), null, null); g2.dispose(); } catch (Exception e) { fail("There should be no exception."); } } /** * Replaces the dataset and checks that it has changed as expected. */ @Test public void testReplaceDataset() { // create a dataset... Number[][] data = new Integer[][] {{-30, -20}, {-10, 10}, {20, 30}}; CategoryDataset newData = DatasetUtils.createCategoryDataset("S", "C", data); LocalListener l = new LocalListener(); this.chart.addChangeListener(l); CategoryPlot plot = (CategoryPlot) this.chart.getPlot(); plot.setDataset(newData); assertTrue(l.flag); ValueAxis axis = plot.getRangeAxis(); Range range = axis.getRange(); assertTrue(range.getLowerBound() <= -30, "Expecting the lower bound of the range to be around -30: " + range.getLowerBound()); assertTrue(range.getUpperBound() >= 30, "Expecting the upper bound of the range to be around 30: " + range.getUpperBound()); } /** * Check that setting a tool tip generator for a series does override the * default generator. */ @Test public void testSetSeriesToolTipGenerator() { CategoryPlot plot = (CategoryPlot) this.chart.getPlot(); CategoryItemRenderer renderer = plot.getRenderer(); StandardCategoryToolTipGenerator tt = new StandardCategoryToolTipGenerator(); renderer.setSeriesToolTipGenerator(0, tt); CategoryToolTipGenerator tt2 = renderer.getToolTipGenerator(0, 0); assertSame(tt2, tt); } /** * Check that setting a URL generator for a series does override the * default generator. */ @Test public void testSetSeriesURLGenerator() { CategoryPlot plot = (CategoryPlot) this.chart.getPlot(); CategoryItemRenderer renderer = plot.getRenderer(); StandardCategoryURLGenerator url1 = new StandardCategoryURLGenerator(); renderer.setSeriesItemURLGenerator(0, url1); CategoryURLGenerator url2 = renderer.getItemURLGenerator(0, 0); assertSame(url2, url1); } /** * Create a stacked bar chart with sample data in the range -3 to +3. * * @return The chart. */ private static JFreeChart createChart() { Number[][] data = new Integer[][] {{-3, -2}, {-1, 1}, {2, 3}}; CategoryDataset dataset = DatasetUtils.createCategoryDataset("S", "C", data); return ChartFactory.createStackedBarChart( "Stacked Bar Chart", // chart title "Domain", "Range", dataset, // data PlotOrientation.HORIZONTAL, true, // include legend true, true ); } /** * A chart change listener. */ static class LocalListener implements ChartChangeListener { /** A flag. */ private boolean flag = false; /** * Event handler. * * @param event the event. */ @Override public void chartChanged(ChartChangeEvent event) { this.flag = true; } } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/StandardChartThemeTest.java000066400000000000000000000261651463604235500303330ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * StandardChartThemeTest.java * --------------------------- * (C) Copyright 2008-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.GradientPaint; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Rectangle2D; import org.jfree.chart.plot.DefaultDrawingSupplier; import org.jfree.chart.plot.PieLabelLinkStyle; import org.jfree.chart.renderer.category.StandardBarPainter; import org.jfree.chart.renderer.xy.StandardXYBarPainter; import org.jfree.chart.ui.RectangleInsets; import org.junit.jupiter.api.Test; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link StandardChartTheme} class. */ public class StandardChartThemeTest { /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(StandardChartTheme.class) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .withPrefabValues(Font.class, TestUtils.createFont(true), TestUtils.createFont(false)) .verify(); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { StandardChartTheme t1 = new StandardChartTheme("Name"); StandardChartTheme t2 = new StandardChartTheme("Name"); assertEquals(t1, t2); // name t1 = new StandardChartTheme("t1"); assertNotEquals(t1, t2); t2 = new StandardChartTheme("t1"); assertEquals(t1, t2); //extraLargeFont t1.setExtraLargeFont(new Font("Dialog", Font.PLAIN, 21)); assertNotEquals(t1, t2); t2.setExtraLargeFont(new Font("Dialog", Font.PLAIN, 21)); assertEquals(t1, t2); //largeFont t1.setLargeFont(new Font("Dialog", Font.PLAIN, 19)); assertNotEquals(t1, t2); t2.setLargeFont(new Font("Dialog", Font.PLAIN, 19)); assertEquals(t1, t2); //regularFont; t1.setRegularFont(new Font("Dialog", Font.PLAIN, 17)); assertNotEquals(t1, t2); t2.setRegularFont(new Font("Dialog", Font.PLAIN, 17)); assertEquals(t1, t2); //titlePaint; t1.setTitlePaint(new GradientPaint(0f, 1f, Color.RED, 2f, 3f, Color.BLUE)); assertNotEquals(t1, t2); t2.setTitlePaint(new GradientPaint(0f, 1f, Color.RED, 2f, 3f, Color.BLUE)); assertEquals(t1, t2); //subtitlePaint; t1.setSubtitlePaint(new GradientPaint(1f, 2f, Color.RED, 3f, 4f, Color.BLUE)); assertNotEquals(t1, t2); t2.setSubtitlePaint(new GradientPaint(1f, 2f, Color.RED, 3f, 4f, Color.BLUE)); assertEquals(t1, t2); //chartBackgroundPaint; t1.setChartBackgroundPaint(new GradientPaint(2f, 3f, Color.BLUE, 4f, 5f, Color.RED)); assertNotEquals(t1, t2); t2.setChartBackgroundPaint(new GradientPaint(2f, 3f, Color.BLUE, 4f, 5f, Color.RED)); assertEquals(t1, t2); //legendBackgroundPaint; t1.setLegendBackgroundPaint(new GradientPaint(3f, 4f, Color.GRAY, 1f, 2f, Color.RED)); assertNotEquals(t1, t2); t2.setLegendBackgroundPaint(new GradientPaint(3f, 4f, Color.GRAY, 1f, 2f, Color.RED)); assertEquals(t1, t2); //legendItemPaint; t1.setLegendItemPaint(new GradientPaint(9f, 8f, Color.RED, 7f, 6f, Color.BLUE)); assertNotEquals(t1, t2); t2.setLegendItemPaint(new GradientPaint(9f, 8f, Color.RED, 7f, 6f, Color.BLUE)); assertEquals(t1, t2); //drawingSupplier; t1.setDrawingSupplier(new DefaultDrawingSupplier( new Paint[] {Color.RED}, new Paint[] {Color.BLUE}, new Stroke[] {new BasicStroke(1.0f)}, new Stroke[] {new BasicStroke(1.0f)}, new Shape[] {new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0)})); assertNotEquals(t1, t2); t2.setDrawingSupplier(new DefaultDrawingSupplier( new Paint[] {Color.RED}, new Paint[] {Color.BLUE}, new Stroke[] {new BasicStroke(1.0f)}, new Stroke[] {new BasicStroke(1.0f)}, new Shape[] {new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0)})); assertEquals(t1, t2); //plotBackgroundPaint; t1.setPlotBackgroundPaint(new GradientPaint(4f, 3f, Color.RED, 6f, 7f, Color.BLUE)); assertNotEquals(t1, t2); t2.setPlotBackgroundPaint(new GradientPaint(4f, 3f, Color.RED, 6f, 7f, Color.BLUE)); assertEquals(t1, t2); //plotOutlinePaint; t1.setPlotOutlinePaint(new GradientPaint(5f, 2f, Color.BLUE, 6f, 7f, Color.RED)); assertNotEquals(t1, t2); t2.setPlotOutlinePaint(new GradientPaint(5f, 2f, Color.BLUE, 6f, 7f, Color.RED)); assertEquals(t1, t2); //labelLinkStyle; t1.setLabelLinkStyle(PieLabelLinkStyle.STANDARD); assertNotEquals(t1, t2); t2.setLabelLinkStyle(PieLabelLinkStyle.STANDARD); assertEquals(t1, t2); //labelLinkPaint; t1.setLabelLinkPaint(new GradientPaint(4f, 3f, Color.RED, 2f, 9f, Color.BLUE)); assertNotEquals(t1, t2); t2.setLabelLinkPaint(new GradientPaint(4f, 3f, Color.RED, 2f, 9f, Color.BLUE)); assertEquals(t1, t2); //domainGridlinePaint; t1.setDomainGridlinePaint(Color.BLUE); assertNotEquals(t1, t2); t2.setDomainGridlinePaint(Color.BLUE); assertEquals(t1, t2); //rangeGridlinePaint; t1.setRangeGridlinePaint(Color.RED); assertNotEquals(t1, t2); t2.setRangeGridlinePaint(Color.RED); assertEquals(t1, t2); //axisOffset; t1.setAxisOffset(new RectangleInsets(1, 2, 3, 4)); assertNotEquals(t1, t2); t2.setAxisOffset(new RectangleInsets(1, 2, 3, 4)); assertEquals(t1, t2); //axisLabelPaint; t1.setAxisLabelPaint(new GradientPaint(8f, 4f, Color.GRAY, 2f, 9f, Color.BLUE)); assertNotEquals(t1, t2); t2.setAxisLabelPaint(new GradientPaint(8f, 4f, Color.GRAY, 2f, 9f, Color.BLUE)); assertEquals(t1, t2); //tickLabelPaint; t1.setTickLabelPaint(new GradientPaint(3f, 4f, Color.RED, 5f, 6f, Color.YELLOW)); assertNotEquals(t1, t2); t2.setTickLabelPaint(new GradientPaint(3f, 4f, Color.RED, 5f, 6f, Color.YELLOW)); assertEquals(t1, t2); //itemLabelPaint; t1.setItemLabelPaint(new GradientPaint(2f, 5f, Color.GRAY, 1f, 2f, Color.BLUE)); assertNotEquals(t1, t2); t2.setItemLabelPaint(new GradientPaint(2f, 5f, Color.GRAY, 1f, 2f, Color.BLUE)); assertEquals(t1, t2); //shadowVisible; t1.setShadowVisible(!t1.isShadowVisible()); assertNotEquals(t1, t2); t2.setShadowVisible(t1.isShadowVisible()); assertEquals(t1, t2); //shadowPaint; t1.setShadowPaint(new GradientPaint(7f, 1f, Color.BLUE, 4f, 6f, Color.RED)); assertNotEquals(t1, t2); t2.setShadowPaint(new GradientPaint(7f, 1f, Color.BLUE, 4f, 6f, Color.RED)); assertEquals(t1, t2); //barPainter; t1.setBarPainter(new StandardBarPainter()); assertNotEquals(t1, t2); t2.setBarPainter(new StandardBarPainter()); assertEquals(t1, t2); //xyBarPainter; t1.setXYBarPainter(new StandardXYBarPainter()); assertNotEquals(t1, t2); t2.setXYBarPainter(new StandardXYBarPainter()); assertEquals(t1, t2); //thermometerPaint; t1.setThermometerPaint(new GradientPaint(9f, 7f, Color.RED, 5f, 1f, Color.BLUE)); assertNotEquals(t1, t2); t2.setThermometerPaint(new GradientPaint(9f, 7f, Color.RED, 5f, 1f, Color.BLUE)); assertEquals(t1, t2); //errorIndicatorPaint; t1.setErrorIndicatorPaint(new GradientPaint(0f, 1f, Color.WHITE, 2f, 3f, Color.BLUE)); assertNotEquals(t1, t2); t2.setErrorIndicatorPaint(new GradientPaint(0f, 1f, Color.WHITE, 2f, 3f, Color.BLUE)); assertEquals(t1, t2); //gridBandPaint t1.setGridBandPaint(new GradientPaint(1f, 2f, Color.WHITE, 4f, 8f, Color.RED)); assertNotEquals(t1, t2); t2.setGridBandPaint(new GradientPaint(1f, 2f, Color.WHITE, 4f, 8f, Color.RED)); assertEquals(t1, t2); //gridBandAlternatePaint t1.setGridBandAlternatePaint(new GradientPaint(1f, 4f, Color.GREEN, 1f, 2f, Color.RED)); assertNotEquals(t1, t2); t2.setGridBandAlternatePaint(new GradientPaint(1f, 4f, Color.GREEN, 1f, 2f, Color.RED)); assertEquals(t1, t2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { StandardChartTheme t1 = new StandardChartTheme("Name"); StandardChartTheme t2 = TestUtils.serialised(t1); assertEquals(t1, t2); } /** * Basic checks for cloning. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { StandardChartTheme t1 = new StandardChartTheme("Name"); StandardChartTheme t2 = (StandardChartTheme) t1.clone(); assertNotSame(t1, t2); assertSame(t1.getClass(), t2.getClass()); assertEquals(t1, t2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/StrokeMapTest.java000066400000000000000000000105701463604235500265240ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * StrokeMapTest.java * ------------------ * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart; import java.awt.BasicStroke; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Some tests for the {@link StrokeMap} class. */ public class StrokeMapTest { /** * Some checks for the getStroke() method. */ @Test public void testGetStroke() { StrokeMap m1 = new StrokeMap(); assertNull(m1.getStroke("A")); m1.put("A", new BasicStroke(1.1f)); assertEquals(new BasicStroke(1.1f), m1.getStroke("A")); m1.put("A", null); assertNull(m1.getStroke("A")); // a null key should throw an IllegalArgumentException boolean pass = false; try { m1.getStroke(null); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Some checks for the put() method. */ @Test public void testPut() { StrokeMap m1 = new StrokeMap(); m1.put("A", new BasicStroke(1.1f)); assertEquals(new BasicStroke(1.1f), m1.getStroke("A")); // a null key should throw an IllegalArgumentException boolean pass = false; try { m1.put(null, new BasicStroke(1.1f)); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Some checks for the equals() method. */ @Test public void testEquals() { StrokeMap m1 = new StrokeMap(); StrokeMap m2 = new StrokeMap(); assertEquals(m1, m1); assertEquals(m1, m2); assertNotEquals(null, m1); assertNotEquals("ABC", m1); m1.put("K1", new BasicStroke(1.1f)); assertNotEquals(m1, m2); m2.put("K1", new BasicStroke(1.1f)); assertEquals(m1, m2); m1.put("K2", new BasicStroke(2.2f)); assertNotEquals(m1, m2); m2.put("K2", new BasicStroke(2.2f)); assertEquals(m1, m2); m1.put("K2", null); assertNotEquals(m1, m2); m2.put("K2", null); assertEquals(m1, m2); } /** * Some checks for cloning. */ @Test public void testCloning() throws CloneNotSupportedException { StrokeMap m1 = new StrokeMap(); StrokeMap m2 = (StrokeMap) m1.clone(); assertEquals(m1, m2); m1.put("K1", new BasicStroke(1.1f)); m1.put("K2", new BasicStroke(2.2f)); m2 = (StrokeMap) m1.clone(); assertEquals(m1, m2); } /** * A check for serialization. */ @Test public void testSerialization1() { StrokeMap m1 = new StrokeMap(); StrokeMap m2 = TestUtils.serialised(m1); assertEquals(m1, m2); } /** * A check for serialization. */ @Test public void testSerialization2() { StrokeMap m1 = new StrokeMap(); m1.put("K1", new BasicStroke(1.1f)); m1.put("K2", new BasicStroke(2.2f)); StrokeMap m2 = TestUtils.serialised(m1); assertEquals(m1, m2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/TestUtils.java000066400000000000000000000125241463604235500257200ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * TestUtils.java * -------------- * (C) Copyright 2007-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart; import java.awt.Font; import java.awt.geom.Rectangle2D; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectInputStream; import java.io.ObjectOutput; import java.io.ObjectOutputStream; import java.text.AttributedString; import java.util.Collection; import java.util.Iterator; import java.util.ResourceBundle; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.plot.MeterPlot; import org.jfree.chart.plot.Plot; import org.jfree.chart.util.ResourceBundleWrapper; import org.jfree.data.general.DefaultValueDataset; /** * Some utility methods for use by the testing code. */ public class TestUtils { public static Rectangle2D createR2D(boolean isRed) { if (isRed) { return new Rectangle2D.Double(0, 0, 1, 1); } else { return new Rectangle2D.Double(1, 1, 2, 2); } } public static ResourceBundle createRB(boolean isRed) { if (isRed) { String baseName = "org.jfree.data.resources.DataPackageResources"; return ResourceBundleWrapper.getBundle(baseName); } else { String baseName = "org.jfree.chart.LocalizationBundle"; return ResourceBundleWrapper.getBundle(baseName); } } public static AttributedString createAS(boolean isRed) { if (isRed) { AttributedString as = new AttributedString("aaa"); return as; } else { AttributedString as = new AttributedString("bbb"); return as; } } public static Font createFont(boolean isRed) { if (isRed) { return new Font("SansSerif", Font.PLAIN, 12); } else { return new Font("Dialog", Font.BOLD, 10); } } public static JFreeChart createJFC(boolean isRed) { if (isRed) { return new JFreeChart("abc", new MeterPlot(new DefaultValueDataset(44))); } else { return new JFreeChart("xyz", new MeterPlot(new DefaultValueDataset(55))); } } public static Plot createPlot(boolean isRed) { FakePlot plot = new FakePlot(); plot.setNotify(isRed); return plot; } public static ValueAxis createValueAxis(boolean isRed) { if (isRed) { return new FakeValueAxis("Fake1", null); } else { return new FakeValueAxis("Fake2", null); } } /** * Returns {@code true} if the collections contains any object that * is an instance of the specified class, and {@code false} otherwise. * * @param collection the collection. * @param c the class. * * @return A boolean. */ public static boolean containsInstanceOf(Collection collection, Class c) { Iterator iterator = collection.iterator(); while (iterator.hasNext()) { Object obj = iterator.next(); if (obj != null && obj.getClass().equals(c)) { return true; } } return false; } /** * Serialises an object, deserialises it and returns the deserialised * version. * * @param original the original object. * * @return A serialised and deserialised version of the original. */ public static K serialised(K original) { K result = null; ByteArrayOutputStream buffer = new ByteArrayOutputStream(); ObjectOutput out; try { out = new ObjectOutputStream(buffer); out.writeObject(original); out.close(); ObjectInput in = new ObjectInputStream( new ByteArrayInputStream(buffer.toByteArray())); result = (K) in.readObject(); in.close(); } catch (IOException e) { throw new RuntimeException(e); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } return result; } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/TimeSeriesChartTest.java000066400000000000000000000130301463604235500276440ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * TimeSeriesChartTest.java * ------------------------ * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.ChartChangeEvent; import org.jfree.chart.event.ChartChangeListener; import org.jfree.chart.labels.StandardXYToolTipGenerator; import org.jfree.chart.labels.XYToolTipGenerator; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.xy.XYItemRenderer; import org.jfree.data.Range; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Some tests for a time series chart. */ public class TimeSeriesChartTest { /** A chart. */ private JFreeChart chart; /** * Common test setup. */ @BeforeEach public void setUp() { this.chart = createChart(); } /** * Draws the chart with a null info object to make sure that no exceptions * are thrown (a problem that was occurring at one point). */ @Test public void testDrawWithNullInfo() { try { BufferedImage image = new BufferedImage(200 , 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); this.chart.draw(g2, new Rectangle2D.Double(0, 0, 200, 100), null, null); g2.dispose(); } catch (Exception e) { fail("No exception should be triggered."); } } /** * Replaces the dataset and checks that it has changed as expected. */ @Test public void testReplaceDataset() { // create a dataset... XYSeries series1 = new XYSeries("Series 1"); series1.add(10.0, 10.0); series1.add(20.0, 20.0); series1.add(30.0, 30.0); XYDataset dataset = new XYSeriesCollection(series1); LocalListener l = new LocalListener(); this.chart.addChangeListener(l); XYPlot plot = (XYPlot) this.chart.getPlot(); plot.setDataset(dataset); assertTrue(l.flag); ValueAxis axis = plot.getRangeAxis(); Range range = axis.getRange(); assertTrue(range.getLowerBound() <= 10, "Expecting the lower bound of the range to be around 10: " + range.getLowerBound()); assertTrue(range.getUpperBound() >= 30, "Expecting the upper bound of the range to be around 30: " + range.getUpperBound()); } /** * Check that setting a tool tip generator for a series does override the * default generator. */ @Test public void testSetSeriesToolTipGenerator() { XYPlot plot = (XYPlot) this.chart.getPlot(); XYItemRenderer renderer = plot.getRenderer(); StandardXYToolTipGenerator tt = new StandardXYToolTipGenerator(); renderer.setSeriesToolTipGenerator(0, tt); XYToolTipGenerator tt2 = renderer.getToolTipGenerator(0, 0); assertSame(tt2, tt); } /** * Create a horizontal bar chart with sample data in the range -3 to +3. * * @return The chart. */ private static JFreeChart createChart() { XYSeries series1 = new XYSeries("Series 1"); series1.add(1.0, 1.0); series1.add(2.0, 2.0); series1.add(3.0, 3.0); XYDataset dataset = new XYSeriesCollection(series1); return ChartFactory.createTimeSeriesChart( "XY Line Chart", // chart title "Domain", "Range", dataset, // data true, // include legend true, // tooltips true // urls ); } /** * A chart change listener. * */ static class LocalListener implements ChartChangeListener { /** A flag. */ private boolean flag = false; /** * Event handler. * * @param event the event. */ @Override public void chartChanged(ChartChangeEvent event) { this.flag = true; } } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/WaterfallChartTest.java000066400000000000000000000111521463604235500275170ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * WaterfallChartTest.java * ----------------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import org.jfree.chart.labels.CategoryToolTipGenerator; import org.jfree.chart.labels.StandardCategoryToolTipGenerator; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.renderer.category.CategoryItemRenderer; import org.jfree.chart.urls.CategoryURLGenerator; import org.jfree.chart.urls.StandardCategoryURLGenerator; import org.jfree.data.category.CategoryDataset; import org.jfree.data.general.DatasetUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Some tests for a waterfall chart. */ public class WaterfallChartTest { /** A chart. */ private JFreeChart chart; /** * Common test setup. */ @BeforeEach public void setUp() { this.chart = createWaterfallChart(); } /** * Draws the chart with a null info object to make sure that no exceptions * are thrown (a problem that was occurring at one point). */ @Test public void testDrawWithNullInfo() { try { BufferedImage image = new BufferedImage(200 , 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); this.chart.draw(g2, new Rectangle2D.Double(0, 0, 200, 100), null, null); g2.dispose(); } catch (Exception e) { fail("There should be no exception."); } } /** * Check that setting a tool tip generator for a series does override the * default generator. */ @Test public void testSetSeriesToolTipGenerator() { CategoryPlot plot = (CategoryPlot) this.chart.getPlot(); CategoryItemRenderer renderer = plot.getRenderer(); StandardCategoryToolTipGenerator tt = new StandardCategoryToolTipGenerator(); renderer.setSeriesToolTipGenerator(0, tt); CategoryToolTipGenerator tt2 = renderer.getToolTipGenerator(0, 0); assertSame(tt2, tt); } /** * Check that setting a URL generator for a series does override the * default generator. */ @Test public void testSetSeriesURLGenerator() { CategoryPlot plot = (CategoryPlot) this.chart.getPlot(); CategoryItemRenderer renderer = plot.getRenderer(); StandardCategoryURLGenerator url1 = new StandardCategoryURLGenerator(); renderer.setSeriesItemURLGenerator(0, url1); CategoryURLGenerator url2 = renderer.getItemURLGenerator(0, 0); assertSame(url2, url1); } /** * Create a bar chart with sample data in the range -3 to +3. * * @return The chart. */ private static JFreeChart createWaterfallChart() { Number[][] data = new Integer[][] {{-3, -2}, {-1, 1}, {2, 3}}; CategoryDataset dataset = DatasetUtils.createCategoryDataset("S", "C", data); return ChartFactory.createWaterfallChart("Waterfall Chart", "Domain", "Range", dataset, PlotOrientation.HORIZONTAL, true, // include legend true, true); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/XYAreaChartTest.java000066400000000000000000000124331463604235500267320ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * XYAreaChartTest.java * -------------------- * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.ChartChangeEvent; import org.jfree.chart.event.ChartChangeListener; import org.jfree.chart.labels.StandardXYToolTipGenerator; import org.jfree.chart.labels.XYToolTipGenerator; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.xy.XYItemRenderer; import org.jfree.data.Range; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Some tests for an XY area chart. */ public class XYAreaChartTest { /** A chart. */ private JFreeChart chart; /** * Common test setup. */ @BeforeEach public void setUp() { this.chart = createChart(); } /** * Draws the chart with a null info object to make sure that no exceptions * are thrown (a problem that was occurring at one point). */ @Test public void testDrawWithNullInfo() { try { BufferedImage image = new BufferedImage(200 , 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); this.chart.draw(g2, new Rectangle2D.Double(0, 0, 200, 100), null, null); g2.dispose(); } catch (Exception e) { fail("No exception should be triggered."); } } /** * Replaces the dataset and checks that it has changed as expected. */ @Test public void testReplaceDataset() { // create a dataset... XYSeries series1 = new XYSeries("Series 1"); series1.add(10.0, 10.0); series1.add(20.0, 20.0); series1.add(30.0, 30.0); XYDataset dataset = new XYSeriesCollection(series1); LocalListener l = new LocalListener(); this.chart.addChangeListener(l); XYPlot plot = (XYPlot) this.chart.getPlot(); plot.setDataset(dataset); assertTrue(l.flag); ValueAxis axis = plot.getRangeAxis(); Range range = axis.getRange(); assertTrue(range.getLowerBound() <= 10, "Expecting the lower bound of the range to be around 10: " + range.getLowerBound()); assertTrue(range.getUpperBound() >= 30, "Expecting the upper bound of the range to be around 30: " + range.getUpperBound()); } /** * Check that setting a tool tip generator for a series does override the * default generator. */ @Test public void testSetSeriesToolTipGenerator() { XYPlot plot = (XYPlot) this.chart.getPlot(); XYItemRenderer renderer = plot.getRenderer(); StandardXYToolTipGenerator tt = new StandardXYToolTipGenerator(); renderer.setSeriesToolTipGenerator(0, tt); XYToolTipGenerator tt2 = renderer.getToolTipGenerator(0, 0); assertSame(tt2, tt); } /** * Create a chart for testing. * * @return The chart. */ private static JFreeChart createChart() { XYSeries series1 = new XYSeries("Series 1"); series1.add(1.0, 1.0); series1.add(2.0, 2.0); series1.add(3.0, 3.0); XYDataset dataset = new XYSeriesCollection(series1); return ChartFactory.createXYAreaChart("Area Chart", "Domain", "Range", dataset); } /** * A chart change listener. * */ static class LocalListener implements ChartChangeListener { /** A flag. */ private boolean flag = false; /** * Event handler. * * @param event the event. */ @Override public void chartChanged(ChartChangeEvent event) { this.flag = true; } } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/XYBarChartTest.java000066400000000000000000000127151463604235500265710ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * XYBarChartTest.java * ------------------- * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.ChartChangeEvent; import org.jfree.chart.event.ChartChangeListener; import org.jfree.chart.labels.StandardXYToolTipGenerator; import org.jfree.chart.labels.XYToolTipGenerator; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.xy.XYItemRenderer; import org.jfree.data.Range; import org.jfree.data.xy.IntervalXYDataset; import org.jfree.data.xy.XYBarDataset; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Some tests for an XY bar chart. */ public class XYBarChartTest { /** A chart. */ private JFreeChart chart; /** * Common test setup. */ @BeforeEach public void setUp() { this.chart = createChart(); } /** * Draws the chart with a null info object to make sure that no exceptions * are thrown (a problem that was occurring at one point). */ @Test public void testDrawWithNullInfo() { try { BufferedImage image = new BufferedImage(200 , 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); this.chart.draw(g2, new Rectangle2D.Double(0, 0, 200, 100), null, null); g2.dispose(); } catch (Exception e) { fail("No exception should be triggered."); } } /** * Replaces the dataset and checks that it has changed as expected. */ @Test public void testReplaceDataset() { // create a dataset... XYSeries series1 = new XYSeries("Series 1"); series1.add(10.0, 10.0); series1.add(20.0, 20.0); series1.add(30.0, 30.0); XYDataset dataset = new XYSeriesCollection(series1); LocalListener l = new LocalListener(); this.chart.addChangeListener(l); XYPlot plot = (XYPlot) this.chart.getPlot(); plot.setDataset(dataset); assertTrue(l.flag); ValueAxis axis = plot.getRangeAxis(); Range range = axis.getRange(); assertTrue(range.getLowerBound() <= 10, "Expecting the lower bound of the range to be around 10: " + range.getLowerBound()); assertTrue(range.getUpperBound() >= 30, "Expecting the upper bound of the range to be around 30: " + range.getUpperBound()); } /** * Check that setting a tool tip generator for a series does override the * default generator. */ @Test public void testSetSeriesToolTipGenerator() { XYPlot plot = (XYPlot) this.chart.getPlot(); XYItemRenderer renderer = plot.getRenderer(); StandardXYToolTipGenerator tt = new StandardXYToolTipGenerator(); renderer.setSeriesToolTipGenerator(0, tt); XYToolTipGenerator tt2 = renderer.getToolTipGenerator(0, 0); assertSame(tt2, tt); } /** * Create a horizontal bar chart with sample data in the range -3 to +3. * * @return The chart. */ private static JFreeChart createChart() { XYSeries series1 = new XYSeries("Series 1"); series1.add(1.0, 1.0); series1.add(2.0, 2.0); series1.add(3.0, 3.0); IntervalXYDataset dataset = new XYBarDataset(new XYSeriesCollection( series1), 1.0); return ChartFactory.createXYBarChart("XY Bar Chart", "Domain", false, "Range", dataset); } /** * A chart change listener. * */ static class LocalListener implements ChartChangeListener { /** A flag. */ private boolean flag = false; /** * Event handler. * * @param event the event. */ @Override public void chartChanged(ChartChangeEvent event) { this.flag = true; } } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/XYLineChartTest.java000066400000000000000000000125111463604235500267460ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * XYLineChartTest.java * -------------------- * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.ChartChangeEvent; import org.jfree.chart.event.ChartChangeListener; import org.jfree.chart.labels.StandardXYToolTipGenerator; import org.jfree.chart.labels.XYToolTipGenerator; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.xy.XYItemRenderer; import org.jfree.data.Range; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Some tests for an XY line chart. */ public class XYLineChartTest { /** A chart. */ private JFreeChart chart; /** * Common test setup. */ @BeforeEach public void setUp() { this.chart = createChart(); } /** * Draws the chart with a null info object to make sure that no exceptions * are thrown (a problem that was occurring at one point). */ @Test public void testDrawWithNullInfo() { try { BufferedImage image = new BufferedImage(200 , 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); this.chart.draw(g2, new Rectangle2D.Double(0, 0, 200, 100), null, null); g2.dispose(); } catch (Exception e) { fail("No exception should be triggered."); } } /** * Replaces the dataset and checks that it has changed as expected. */ @Test public void testReplaceDataset() { // create a dataset... XYSeries series1 = new XYSeries("Series 1"); series1.add(10.0, 10.0); series1.add(20.0, 20.0); series1.add(30.0, 30.0); XYDataset dataset = new XYSeriesCollection(series1); LocalListener l = new LocalListener(); this.chart.addChangeListener(l); XYPlot plot = (XYPlot) this.chart.getPlot(); plot.setDataset(dataset); assertTrue(l.flag); ValueAxis axis = plot.getRangeAxis(); Range range = axis.getRange(); assertTrue(range.getLowerBound() <= 10, "Expecting the lower bound of the range to be around 10: " + range.getLowerBound()); assertTrue(range.getUpperBound() >= 30, "Expecting the upper bound of the range to be around 30: " + range.getUpperBound()); } /** * Check that setting a tool tip generator for a series does override the * default generator. */ @Test public void testSetSeriesToolTipGenerator() { XYPlot plot = (XYPlot) this.chart.getPlot(); XYItemRenderer renderer = plot.getRenderer(); StandardXYToolTipGenerator tt = new StandardXYToolTipGenerator(); renderer.setSeriesToolTipGenerator(0, tt); XYToolTipGenerator tt2 = renderer.getToolTipGenerator(0, 0); assertSame(tt2, tt); } /** * Create a horizontal bar chart with sample data in the range -3 to +3. * * @return The chart. */ private static JFreeChart createChart() { XYSeries series1 = new XYSeries("Series 1"); series1.add(1.0, 1.0); series1.add(2.0, 2.0); series1.add(3.0, 3.0); XYDataset dataset = new XYSeriesCollection(series1); return ChartFactory.createXYLineChart("XY Line Chart", "Domain", "Range", dataset); } /** * A chart change listener. * */ static class LocalListener implements ChartChangeListener { /** A flag. */ private boolean flag = false; /** * Event handler. * * @param event the event. */ @Override public void chartChanged(ChartChangeEvent event) { this.flag = true; } } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/XYStepAreaChartTest.java000066400000000000000000000124571463604235500275740ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * XYStepAreaChartTest.java * ------------------------ * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.ChartChangeEvent; import org.jfree.chart.event.ChartChangeListener; import org.jfree.chart.labels.StandardXYToolTipGenerator; import org.jfree.chart.labels.XYToolTipGenerator; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.xy.XYItemRenderer; import org.jfree.data.Range; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Some tests for an XY step area chart. */ public class XYStepAreaChartTest { /** A chart. */ private JFreeChart chart; /** * Common test setup. */ @BeforeEach public void setUp() { this.chart = createChart(); } /** * Draws the chart with a null info object to make sure that no exceptions * are thrown (a problem that was occurring at one point). */ @Test public void testDrawWithNullInfo() { try { BufferedImage image = new BufferedImage(200 , 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); this.chart.draw(g2, new Rectangle2D.Double(0, 0, 200, 100), null, null); g2.dispose(); } catch (Exception e) { fail("No exception should be triggered."); } } /** * Replaces the dataset and checks that it has changed as expected. */ @Test public void testReplaceDataset() { // create a dataset... XYSeries series1 = new XYSeries("Series 1"); series1.add(10.0, 10.0); series1.add(20.0, 20.0); series1.add(30.0, 30.0); XYDataset dataset = new XYSeriesCollection(series1); LocalListener l = new LocalListener(); this.chart.addChangeListener(l); XYPlot plot = (XYPlot) this.chart.getPlot(); plot.setDataset(dataset); assertTrue(l.flag); ValueAxis axis = plot.getRangeAxis(); Range range = axis.getRange(); assertTrue(range.getLowerBound() <= 10, "Expecting the lower bound of the range to be around 10: " + range.getLowerBound()); assertTrue(range.getUpperBound() >= 30, "Expecting the upper bound of the range to be around 30: " + range.getUpperBound()); } /** * Check that setting a tool tip generator for a series does override the * default generator. */ @Test public void testSetSeriesToolTipGenerator() { XYPlot plot = (XYPlot) this.chart.getPlot(); XYItemRenderer renderer = plot.getRenderer(); StandardXYToolTipGenerator tt = new StandardXYToolTipGenerator(); renderer.setSeriesToolTipGenerator(0, tt); XYToolTipGenerator tt2 = renderer.getToolTipGenerator(0, 0); assertSame(tt2, tt); } /** * Create an area chart. * * @return The chart. */ private static JFreeChart createChart() { XYSeries series1 = new XYSeries("Series 1"); series1.add(1.0, 1.0); series1.add(2.0, 2.0); series1.add(3.0, 3.0); XYDataset dataset = new XYSeriesCollection(series1); return ChartFactory.createXYStepAreaChart("Step Chart", "Domain", "Range", dataset); } /** * A chart change listener. * */ static class LocalListener implements ChartChangeListener { /** A flag. */ private boolean flag = false; /** * Event handler. * * @param event the event. */ @Override public void chartChanged(ChartChangeEvent event) { this.flag = true; } } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/XYStepChartTest.java000066400000000000000000000132411463604235500267730ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * XYStepChartTest.java * -------------------- * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.event.ChartChangeEvent; import org.jfree.chart.event.ChartChangeListener; import org.jfree.chart.labels.StandardXYToolTipGenerator; import org.jfree.chart.labels.XYToolTipGenerator; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.xy.XYItemRenderer; import org.jfree.data.Range; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Some tests for an XY step plot. */ public class XYStepChartTest { /** A chart. */ private JFreeChart chart; /** * Common test setup. */ @BeforeEach public void setUp() { this.chart = createChart(); } /** * Draws the chart with a null info object to make sure that no exceptions * are thrown (a problem that was occurring at one point). */ @Test public void testDrawWithNullInfo() { try { BufferedImage image = new BufferedImage(200 , 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); this.chart.draw(g2, new Rectangle2D.Double(0, 0, 200, 100), null, null); g2.dispose(); } catch (Exception e) { fail("No exception should be triggered."); } } /** * Replaces the dataset and checks that it has changed as expected. */ @Test public void testReplaceDataset() { // create a dataset... XYSeries series1 = new XYSeries("Series 1"); series1.add(10.0, 10.0); series1.add(20.0, 20.0); series1.add(30.0, 30.0); XYDataset dataset = new XYSeriesCollection(series1); LocalListener l = new LocalListener(); this.chart.addChangeListener(l); XYPlot plot = (XYPlot) this.chart.getPlot(); plot.setDataset(dataset); assertTrue(l.flag); ValueAxis axis = plot.getRangeAxis(); Range range = axis.getRange(); assertTrue(range.getLowerBound() <= 10, "Expecting the lower bound of the range to be around 10: " + range.getLowerBound()); assertTrue(range.getUpperBound() >= 30, "Expecting the upper bound of the range to be around 30: " + range.getUpperBound()); } /** * Check that setting a tool tip generator for a series does override the * default generator. */ @Test public void testSetSeriesToolTipGenerator() { XYPlot plot = (XYPlot) this.chart.getPlot(); XYItemRenderer renderer = plot.getRenderer(); StandardXYToolTipGenerator tt = new StandardXYToolTipGenerator(); renderer.setSeriesToolTipGenerator(0, tt); XYToolTipGenerator tt2 = renderer.getToolTipGenerator(0, 0); assertSame(tt2, tt); } /** * Create a horizontal bar chart with sample data in the range -3 to +3. * * @return The chart. */ private static JFreeChart createChart() { // create a dataset... XYSeries series1 = new XYSeries("Series 1"); series1.add(1.0, 1.0); series1.add(2.0, 2.0); series1.add(3.0, 3.0); XYDataset dataset = new XYSeriesCollection(series1); // create the chart... return ChartFactory.createXYStepChart( "Step Chart", // chart title "Domain", "Range", dataset, // data PlotOrientation.VERTICAL, true, // include legend true, // tooltips true // urls ); } /** * A chart change listener. * */ static class LocalListener implements ChartChangeListener { /** A flag. */ private boolean flag = false; /** * Event handler. * * @param event the event. */ @Override public void chartChanged(ChartChangeEvent event) { this.flag = true; } } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/annotations/000077500000000000000000000000001463604235500254465ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/annotations/CategoryLineAnnotationTest.java000066400000000000000000000162351463604235500336000ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------- * CategoryLineAnnotationTest.java * ------------------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.annotations; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Stroke; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import javax.swing.event.EventListenerList; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link CategoryLineAnnotation} class. */ public class CategoryLineAnnotationTest { @Test public void testConstructorExceptions() { Stroke stroke = new BasicStroke(2.0f); assertThrows(IllegalArgumentException.class, () -> { CategoryLineAnnotation a1 = new CategoryLineAnnotation(null, 20.0, "Cat2", 200.0, Color.BLUE, stroke); }); assertThrows(IllegalArgumentException.class, () -> { CategoryLineAnnotation a1 = new CategoryLineAnnotation("Cat1", Double.NaN, "Cat2", 200.0, Color.BLUE, stroke); }); assertThrows(IllegalArgumentException.class, () -> { CategoryLineAnnotation a1 = new CategoryLineAnnotation("Cat1", 20.0, null, 200.0, Color.BLUE, stroke); }); assertThrows(IllegalArgumentException.class, () -> { CategoryLineAnnotation a1 = new CategoryLineAnnotation("Cat1", 20.0, "Cat2", Double.NaN, Color.BLUE, stroke); }); assertThrows(IllegalArgumentException.class, () -> { CategoryLineAnnotation a1 = new CategoryLineAnnotation("Cat1", 20.0, "Cat2", 200.0, null, stroke); }); assertThrows(IllegalArgumentException.class, () -> { CategoryLineAnnotation a1 = new CategoryLineAnnotation("Cat1", 20.0, "Cat2", 200.0, Color.BLUE, null); }); } /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashcode() { EqualsVerifier.forClass(CategoryLineAnnotation.class) .withRedefinedSuperclass() // superclass also defines equals/hashCode .withPrefabValues(EventListenerList.class, new EventListenerList(), new EventListenerList()) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { BasicStroke s1 = new BasicStroke(1.0f); BasicStroke s2 = new BasicStroke(2.0f); CategoryLineAnnotation a1 = new CategoryLineAnnotation("Category 1", 1.0, "Category 2", 2.0, Color.RED, s1); CategoryLineAnnotation a2 = new CategoryLineAnnotation("Category 1", 1.0, "Category 2", 2.0, Color.RED, s1); assertEquals(a1, a2); assertEquals(a2, a1); // category 1 a1.setCategory1("Category A"); assertNotEquals(a1, a2); a2.setCategory1("Category A"); assertEquals(a1, a2); // value 1 a1.setValue1(0.15); assertNotEquals(a1, a2); a2.setValue1(0.15); assertEquals(a1, a2); // category 2 a1.setCategory2("Category B"); assertNotEquals(a1, a2); a2.setCategory2("Category B"); assertEquals(a1, a2); // value 2 a1.setValue2(0.25); assertNotEquals(a1, a2); a2.setValue2(0.25); assertEquals(a1, a2); // paint a1.setPaint(Color.YELLOW); assertNotEquals(a1, a2); a2.setPaint(Color.YELLOW); assertEquals(a1, a2); // stroke a1.setStroke(s2); assertNotEquals(a1, a2); a2.setStroke(s2); assertEquals(a1, a2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { CategoryLineAnnotation a1 = new CategoryLineAnnotation("Category 1", 1.0, "Category 2", 2.0, Color.RED, new BasicStroke(1.0f)); CategoryLineAnnotation a2 = new CategoryLineAnnotation("Category 1", 1.0, "Category 2", 2.0, Color.RED, new BasicStroke(1.0f)); assertEquals(a1, a2); int h1 = a1.hashCode(); int h2 = a2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException if there is an issue cloning */ @Test public void testCloning() throws CloneNotSupportedException { CategoryLineAnnotation a1 = new CategoryLineAnnotation("Category 1", 1.0, "Category 2", 2.0, Color.RED, new BasicStroke(1.0f)); CategoryLineAnnotation a2 = (CategoryLineAnnotation) a1.clone(); assertNotSame(a1, a2); assertSame(a1.getClass(), a2.getClass()); assertEquals(a1, a2); } /** * Checks that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { CategoryLineAnnotation a1 = new CategoryLineAnnotation( "Category 1", 1.0, "Category 2", 2.0, Color.RED, new BasicStroke(1.0f)); assertTrue(a1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { CategoryLineAnnotation a1 = new CategoryLineAnnotation("Category 1", 1.0, "Category 2", 2.0, Color.RED, new BasicStroke(1.0f)); CategoryLineAnnotation a2 = TestUtils.serialised(a1); assertEquals(a1, a2); } } CategoryPointerAnnotationTest.java000066400000000000000000000154341463604235500342520ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/annotations/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------------- * CategoryPointerAnnotationTest.java * ---------------------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.annotations; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.Stroke; import java.awt.geom.Rectangle2D; import org.jfree.chart.TestUtils; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import static org.jfree.chart.TestUtils.createFont; import static org.jfree.chart.TestUtils.createR2D; import static org.junit.jupiter.api.Assertions.*; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import javax.swing.event.EventListenerList; /** * Tests for the {@link CategoryPointerAnnotation} class. */ public class CategoryPointerAnnotationTest { /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(CategoryPointerAnnotation.class) .withRedefinedSuperclass() // superclass also defines equals/hashCode .withPrefabValues(Rectangle2D.class, createR2D(true), createR2D(false)) .withPrefabValues(Font.class, createFont(true), createFont(false)) .withPrefabValues(EventListenerList.class, new EventListenerList(), new EventListenerList()) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { CategoryPointerAnnotation a1 = new CategoryPointerAnnotation("Label", "Key 1", 20.0, Math.PI); CategoryPointerAnnotation a2 = new CategoryPointerAnnotation("Label", "Key 1", 20.0, Math.PI); assertEquals(a1, a2); a1 = new CategoryPointerAnnotation("Label2", "Key 1", 20.0, Math.PI); assertNotEquals(a1, a2); a2 = new CategoryPointerAnnotation("Label2", "Key 1", 20.0, Math.PI); assertEquals(a1, a2); a1.setCategory("Key 2"); assertNotEquals(a1, a2); a2.setCategory("Key 2"); assertEquals(a1, a2); a1.setValue(22.0); assertNotEquals(a1, a2); a2.setValue(22.0); assertEquals(a1, a2); //private double angle; a1.setAngle(Math.PI / 4.0); assertNotEquals(a1, a2); a2.setAngle(Math.PI / 4.0); assertEquals(a1, a2); //private double tipRadius; a1.setTipRadius(20.0); assertNotEquals(a1, a2); a2.setTipRadius(20.0); assertEquals(a1, a2); //private double baseRadius; a1.setBaseRadius(5.0); assertNotEquals(a1, a2); a2.setBaseRadius(5.0); assertEquals(a1, a2); //private double arrowLength; a1.setArrowLength(33.0); assertNotEquals(a1, a2); a2.setArrowLength(33.0); assertEquals(a1, a2); //private double arrowWidth; a1.setArrowWidth(9.0); assertNotEquals(a1, a2); a2.setArrowWidth(9.0); assertEquals(a1, a2); //private Stroke arrowStroke; Stroke stroke = new BasicStroke(1.5f); a1.setArrowStroke(stroke); assertNotEquals(a1, a2); a2.setArrowStroke(stroke); assertEquals(a1, a2); //private Paint arrowPaint; a1.setArrowPaint(Color.BLUE); assertNotEquals(a1, a2); a2.setArrowPaint(Color.BLUE); assertEquals(a1, a2); //private double labelOffset; a1.setLabelOffset(10.0); assertNotEquals(a1, a2); a2.setLabelOffset(10.0); assertEquals(a1, a2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { CategoryPointerAnnotation a1 = new CategoryPointerAnnotation("Label", "A", 20.0, Math.PI); CategoryPointerAnnotation a2 = new CategoryPointerAnnotation("Label", "A", 20.0, Math.PI); assertEquals(a1, a2); int h1 = a1.hashCode(); int h2 = a2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException if there is a cloning issue. */ @Test public void testCloning() throws CloneNotSupportedException { CategoryPointerAnnotation a1 = new CategoryPointerAnnotation("Label", "A", 20.0, Math.PI); CategoryPointerAnnotation a2 = (CategoryPointerAnnotation) a1.clone(); assertNotSame(a1, a2); assertSame(a1.getClass(), a2.getClass()); assertEquals(a1, a2); } /** * Checks that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { CategoryPointerAnnotation a1 = new CategoryPointerAnnotation("Label", "A", 20.0, Math.PI); assertTrue(a1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { CategoryPointerAnnotation a1 = new CategoryPointerAnnotation("Label", "A", 20.0, Math.PI); CategoryPointerAnnotation a2 = TestUtils.serialised(a1); assertEquals(a1, a2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/annotations/CategoryTextAnnotationTest.java000066400000000000000000000123121463604235500336250ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------- * CategoryTextAnnotationTest.java * ------------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.annotations; import java.awt.Font; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import static org.jfree.chart.TestUtils.createFont; import static org.junit.jupiter.api.Assertions.*; import org.jfree.chart.axis.CategoryAnchor; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import javax.swing.event.EventListenerList; /** * Tests for the {@link CategoryTextAnnotation} class. */ public class CategoryTextAnnotationTest { /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(CategoryTextAnnotation.class) .withRedefinedSuperclass() // superclass also defines equals/hashCode // Add prefab values for Font .withPrefabValues(Font.class, createFont(true), createFont(false)) .withPrefabValues(EventListenerList.class, new EventListenerList(), new EventListenerList()) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { CategoryTextAnnotation a1 = new CategoryTextAnnotation("Test", "Category", 1.0); CategoryTextAnnotation a2 = new CategoryTextAnnotation("Test", "Category", 1.0); assertEquals(a1, a2); // category a1.setCategory("Category 2"); assertNotEquals(a1, a2); a2.setCategory("Category 2"); assertEquals(a1, a2); // categoryAnchor a1.setCategoryAnchor(CategoryAnchor.START); assertNotEquals(a1, a2); a2.setCategoryAnchor(CategoryAnchor.START); assertEquals(a1, a2); // value a1.setValue(0.15); assertNotEquals(a1, a2); a2.setValue(0.15); assertEquals(a1, a2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { CategoryTextAnnotation a1 = new CategoryTextAnnotation("Test", "Category", 1.0); CategoryTextAnnotation a2 = new CategoryTextAnnotation("Test", "Category", 1.0); assertEquals(a1, a2); int h1 = a1.hashCode(); int h2 = a2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException if there is a cloning issue. */ @Test public void testCloning() throws CloneNotSupportedException { CategoryTextAnnotation a1 = new CategoryTextAnnotation( "Test", "Category", 1.0); CategoryTextAnnotation a2 = (CategoryTextAnnotation) a1.clone(); assertNotSame(a1, a2); assertSame(a1.getClass(), a2.getClass()); assertEquals(a1, a2); } /** * Checks that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { CategoryTextAnnotation a1 = new CategoryTextAnnotation( "Test", "Category", 1.0); assertTrue(a1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { CategoryTextAnnotation a1 = new CategoryTextAnnotation("Test", "Category", 1.0); CategoryTextAnnotation a2 = TestUtils.serialised(a1); assertEquals(a1, a2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/annotations/TextAnnotationTest.java000066400000000000000000000146651463604235500321440ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * TextAnnotationTest.java * ----------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Martin Hoeller; Tracy Hiltbrand; * */ package org.jfree.chart.annotations; import java.awt.Color; import java.awt.Font; import java.awt.GradientPaint; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import static org.jfree.chart.TestUtils.createFont; import static org.junit.jupiter.api.Assertions.*; import org.jfree.chart.event.AnnotationChangeEvent; import org.jfree.chart.event.AnnotationChangeListener; import org.jfree.chart.ui.TextAnchor; import org.junit.jupiter.api.Test; import javax.swing.event.EventListenerList; /** * Tests for the {@link TextAnnotation} class. */ public class TextAnnotationTest implements AnnotationChangeListener { /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(TextAnnotation.class) .withRedefinedSuperclass() .withRedefinedSubclass(CategoryTextAnnotation.class) // Add prefab values for Font .withPrefabValues(Font.class, createFont(true), createFont(false)) .withPrefabValues(EventListenerList.class, new EventListenerList(), new EventListenerList()) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { TextAnnotation a1 = new CategoryTextAnnotation("Test", "Category", 1.0); TextAnnotation a2 = new CategoryTextAnnotation("Test", "Category", 1.0); assertEquals(a1, a2); // text a1.setText("Text"); assertNotEquals(a1, a2); a2.setText("Text"); assertEquals(a1, a2); // font a1.setFont(new Font("Serif", Font.BOLD, 18)); assertNotEquals(a1, a2); a2.setFont(new Font("Serif", Font.BOLD, 18)); assertEquals(a1, a2); // paint a1.setPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.pink)); assertNotEquals(a1, a2); a2.setPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.pink)); assertEquals(a1, a2); // textAnchor a1.setTextAnchor(TextAnchor.BOTTOM_LEFT); assertNotEquals(a1, a2); a2.setTextAnchor(TextAnchor.BOTTOM_LEFT); assertEquals(a1, a2); // rotationAnchor a1.setRotationAnchor(TextAnchor.BOTTOM_LEFT); assertNotEquals(a1, a2); a2.setRotationAnchor(TextAnchor.BOTTOM_LEFT); assertEquals(a1, a2); // rotationAngle a1.setRotationAngle(Math.PI); assertNotEquals(a1, a2); a2.setRotationAngle(Math.PI); assertEquals(a1, a2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { TextAnnotation a1 = new CategoryTextAnnotation("Test", "Category", 1.0); TextAnnotation a2 = new CategoryTextAnnotation("Test", "Category", 1.0); assertEquals(a1, a2); int h1 = a1.hashCode(); int h2 = a2.hashCode(); assertEquals(h1, h2); } /** * Test null-argument (Bug #3428870). */ @Test public void testSetRotationAnchor() { TextAnnotation a = new CategoryTextAnnotation("Test", "Category", 1.0); try { a.setRotationAnchor(null); fail("Should have thrown Exception."); } catch (IllegalArgumentException e) { // ok, exception is expected } } /** * Some tests to ensure that change events are generated as expected. */ @Test public void testChangeEvents() { TextAnnotation a = new CategoryTextAnnotation("Test", "A", 1.0); a.addChangeListener(this); this.lastEvent = null; a.setText("B"); assertNotNull(this.lastEvent); this.lastEvent = null; a.setText("B"); assertNotNull(this.lastEvent); this.lastEvent = null; a.setFont(new Font("SansSerif", Font.PLAIN, 12)); assertNotNull(this.lastEvent); this.lastEvent = null; a.setPaint(Color.BLUE); assertNotNull(this.lastEvent); this.lastEvent = null; a.setTextAnchor(TextAnchor.CENTER_LEFT); assertNotNull(this.lastEvent); this.lastEvent = null; a.setRotationAnchor(TextAnchor.CENTER_LEFT); assertNotNull(this.lastEvent); this.lastEvent = null; a.setRotationAngle(123.4); assertNotNull(this.lastEvent); } private AnnotationChangeEvent lastEvent; /** * Receives notification of a change to an annotation. * * @param event the event. */ @Override public void annotationChanged(AnnotationChangeEvent event) { this.lastEvent = event; } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/annotations/XYBoxAnnotationTest.java000066400000000000000000000214421463604235500322200ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * XYBoxAnnotationTest.java * ------------------------ * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.annotations; import java.awt.BasicStroke; import java.awt.Color; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import java.awt.GradientPaint; import java.awt.Stroke; import org.jfree.chart.JFreeChart; import org.jfree.chart.TestUtils; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.xy.DefaultTableXYDataset; import org.jfree.data.xy.XYSeries; import org.junit.jupiter.api.Test; import javax.swing.event.EventListenerList; import static org.junit.jupiter.api.Assertions.*; /** * Some tests for the {@link XYBoxAnnotation} class. */ public class XYBoxAnnotationTest { @Test public void testConstructorExceptions() { Stroke stroke = new BasicStroke(2.0f); assertThrows(IllegalArgumentException.class, () -> { XYBoxAnnotation a1 = new XYBoxAnnotation(Double.NaN, 20.0, 100.0, 200.0, stroke, Color.BLUE, Color.RED); }); assertThrows(IllegalArgumentException.class, () -> { XYBoxAnnotation a1 = new XYBoxAnnotation(10.0, Double.NaN, 100.0, 200.0, stroke, Color.BLUE, Color.RED); }); assertThrows(IllegalArgumentException.class, () -> { XYBoxAnnotation a1 = new XYBoxAnnotation(10.0, 20.0, Double.NaN, 200.0, stroke, Color.BLUE, Color.RED); }); assertThrows(IllegalArgumentException.class, () -> { XYBoxAnnotation a1 = new XYBoxAnnotation(10.0, 20.0, 100.0, Double.NaN, stroke, Color.BLUE, Color.RED); }); } /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(XYBoxAnnotation.class) .withRedefinedSuperclass() // superclass also defines equals/hashCode .withPrefabValues(EventListenerList.class, new EventListenerList(), new EventListenerList()) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { XYBoxAnnotation a1 = new XYBoxAnnotation(1.0, 2.0, 3.0, 4.0, new BasicStroke(1.2f), Color.RED, Color.BLUE); XYBoxAnnotation a2 = new XYBoxAnnotation(1.0, 2.0, 3.0, 4.0, new BasicStroke(1.2f), Color.RED, Color.BLUE); assertEquals(a1, a2); assertEquals(a2, a1); // x0 a1 = new XYBoxAnnotation(2.0, 2.0, 3.0, 4.0, new BasicStroke(1.2f), Color.RED, Color.BLUE); assertNotEquals(a1, a2); a2 = new XYBoxAnnotation(2.0, 2.0, 3.0, 4.0, new BasicStroke(1.2f), Color.RED, Color.BLUE); assertEquals(a1, a2); // stroke a1 = new XYBoxAnnotation(1.0, 2.0, 3.0, 4.0, new BasicStroke(2.3f), Color.RED, Color.BLUE); assertNotEquals(a1, a2); a2 = new XYBoxAnnotation(1.0, 2.0, 3.0, 4.0, new BasicStroke(2.3f), Color.RED, Color.BLUE); assertEquals(a1, a2); GradientPaint gp1a = new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.RED); GradientPaint gp1b = new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.RED); GradientPaint gp2a = new GradientPaint(5.0f, 6.0f, Color.pink, 7.0f, 8.0f, Color.WHITE); GradientPaint gp2b = new GradientPaint(5.0f, 6.0f, Color.pink, 7.0f, 8.0f, Color.WHITE); // outlinePaint a1 = new XYBoxAnnotation(1.0, 2.0, 3.0, 4.0, new BasicStroke(2.3f), gp1a, Color.BLUE); assertNotEquals(a1, a2); a2 = new XYBoxAnnotation(1.0, 2.0, 3.0, 4.0, new BasicStroke(2.3f), gp1b, Color.BLUE); assertEquals(a1, a2); // fillPaint a1 = new XYBoxAnnotation(1.0, 2.0, 3.0, 4.0, new BasicStroke(2.3f), gp1a, gp2a); assertNotEquals(a1, a2); a2 = new XYBoxAnnotation(1.0, 2.0, 3.0, 4.0, new BasicStroke(2.3f), gp1b, gp2b); assertEquals(a1, a2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { XYBoxAnnotation a1 = new XYBoxAnnotation(1.0, 2.0, 3.0, 4.0, new BasicStroke(1.2f), Color.RED, Color.BLUE); XYBoxAnnotation a2 = new XYBoxAnnotation(1.0, 2.0, 3.0, 4.0, new BasicStroke(1.2f), Color.RED, Color.BLUE); assertEquals(a1, a2); int h1 = a1.hashCode(); int h2 = a2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException if there is an issue cloning. */ @Test public void testCloning() throws CloneNotSupportedException { XYBoxAnnotation a1 = new XYBoxAnnotation(1.0, 2.0, 3.0, 4.0, new BasicStroke(1.2f), Color.RED, Color.BLUE); XYBoxAnnotation a2 = (XYBoxAnnotation) a1.clone(); assertNotSame(a1, a2); assertSame(a1.getClass(), a2.getClass()); assertEquals(a1, a2); } /** * Checks that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { XYBoxAnnotation a1 = new XYBoxAnnotation(1.0, 2.0, 3.0, 4.0, new BasicStroke(1.2f), Color.RED, Color.BLUE); assertTrue(a1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XYBoxAnnotation a1 = new XYBoxAnnotation(1.0, 2.0, 3.0, 4.0, new BasicStroke(1.2f), Color.RED, Color.BLUE); XYBoxAnnotation a2 = TestUtils.serialised(a1); assertEquals(a1, a2); } /** * Draws the chart with a {@code null} info object to make sure that * no exceptions are thrown. */ @Test public void testDrawWithNullInfo() { try { DefaultTableXYDataset dataset = new DefaultTableXYDataset(); XYSeries s1 = new XYSeries("Series 1", true, false); s1.add(5.0, 5.0); s1.add(10.0, 15.5); s1.add(15.0, 9.5); s1.add(20.0, 7.5); dataset.addSeries(s1); XYSeries s2 = new XYSeries("Series 2", true, false); s2.add(5.0, 5.0); s2.add(10.0, 15.5); s2.add(15.0, 9.5); s2.add(20.0, 3.5); dataset.addSeries(s2); XYPlot plot = new XYPlot(dataset, new NumberAxis("X"), new NumberAxis("Y"), new XYLineAndShapeRenderer()); plot.addAnnotation(new XYBoxAnnotation(10.0, 12.0, 3.0, 4.0, new BasicStroke(1.2f), Color.RED, Color.BLUE)); JFreeChart chart = new JFreeChart(plot); /* BufferedImage image = */ chart.createBufferedImage(300, 200, null); } catch (NullPointerException e) { fail("No exception should be triggered."); } } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/annotations/XYDrawableAnnotationTest.java000066400000000000000000000162511463604235500332130ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------- * XYDrawableAnnotationTest.java * ----------------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.annotations; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.io.Serializable; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.chart.ui.Drawable; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import javax.swing.event.EventListenerList; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XYDrawableAnnotation} class. */ public class XYDrawableAnnotationTest { static class TestDrawable implements Drawable, Cloneable, Serializable { /** * Default constructor. */ public TestDrawable() { } /** * Draws something. * @param g2 the graphics device. * @param area the area in which to draw. */ @Override public void draw(Graphics2D g2, Rectangle2D area) { // do nothing } /** * Tests this object for equality with an arbitrary object. * @param obj the object to test against ({@code null} permitted). * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } return obj instanceof TestDrawable; } /** * Returns a clone. * * @return A clone. * * @throws CloneNotSupportedException if there is a problem cloning. */ @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public int hashCode() { return 5; } } /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(XYDrawableAnnotation.class) .withRedefinedSuperclass() // superclass also defines equals/hashCode .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS).withPrefabValues(EventListenerList.class, new EventListenerList(), new EventListenerList()) .verify(); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { XYDrawableAnnotation a1 = new XYDrawableAnnotation(10.0, 20.0, 100.0, 200.0, new TestDrawable()); XYDrawableAnnotation a2 = new XYDrawableAnnotation(10.0, 20.0, 100.0, 200.0, new TestDrawable()); assertEquals(a1, a2); a1 = new XYDrawableAnnotation(11.0, 20.0, 100.0, 200.0, new TestDrawable()); assertNotEquals(a1, a2); a2 = new XYDrawableAnnotation(11.0, 20.0, 100.0, 200.0, new TestDrawable()); assertEquals(a1, a2); a1 = new XYDrawableAnnotation(11.0, 22.0, 100.0, 200.0, new TestDrawable()); assertNotEquals(a1, a2); a2 = new XYDrawableAnnotation(11.0, 22.0, 100.0, 200.0, new TestDrawable()); assertEquals(a1, a2); a1 = new XYDrawableAnnotation(11.0, 22.0, 101.0, 200.0, new TestDrawable()); assertNotEquals(a1, a2); a2 = new XYDrawableAnnotation(11.0, 22.0, 101.0, 200.0, new TestDrawable()); assertEquals(a1, a2); a1 = new XYDrawableAnnotation(11.0, 22.0, 101.0, 202.0, new TestDrawable()); assertNotEquals(a1, a2); a2 = new XYDrawableAnnotation(11.0, 22.0, 101.0, 202.0, new TestDrawable()); assertEquals(a1, a2); a1 = new XYDrawableAnnotation(11.0, 22.0, 101.0, 202.0, 2.0, new TestDrawable()); assertNotEquals(a1, a2); a2 = new XYDrawableAnnotation(11.0, 22.0, 101.0, 202.0, 2.0, new TestDrawable()); assertEquals(a1, a2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { XYDrawableAnnotation a1 = new XYDrawableAnnotation(10.0, 20.0, 100.0, 200.0, new TestDrawable()); XYDrawableAnnotation a2 = new XYDrawableAnnotation(10.0, 20.0, 100.0, 200.0, new TestDrawable()); assertEquals(a1, a2); int h1 = a1.hashCode(); int h2 = a2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException if there is an issue cloning. */ @Test public void testCloning() throws CloneNotSupportedException { XYDrawableAnnotation a1 = new XYDrawableAnnotation(10.0, 20.0, 100.0, 200.0, new TestDrawable()); XYDrawableAnnotation a2 = (XYDrawableAnnotation) a1.clone(); assertNotSame(a1, a2); assertSame(a1.getClass(), a2.getClass()); assertEquals(a1, a2); } /** * Checks that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { XYDrawableAnnotation a1 = new XYDrawableAnnotation(10.0, 20.0, 100.0, 200.0, new TestDrawable()); assertTrue(a1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XYDrawableAnnotation a1 = new XYDrawableAnnotation(10.0, 20.0, 100.0, 200.0, new TestDrawable()); XYDrawableAnnotation a2 = TestUtils.serialised(a1); assertEquals(a1, a2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/annotations/XYImageAnnotationTest.java000066400000000000000000000111221463604235500325040ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * XYImageAnnotationTest.java * -------------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.annotations; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertFalse; import java.awt.Image; import org.jfree.chart.JFreeChart; import org.jfree.chart.ui.RectangleAnchor; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; /** * Tests for the {@link XYImageAnnotation} class. */ public class XYImageAnnotationTest { // /** // * Confirm that the equals method can distinguish all the required fields. // */ // @Test // public void testEquals() { // Image image = JFreeChart.INFO.getLogo(); // XYImageAnnotation a1 = new XYImageAnnotation(10.0, 20.0, image); // XYImageAnnotation a2 = new XYImageAnnotation(10.0, 20.0, image); // assertTrue(a1.equals(a2)); // // a1 = new XYImageAnnotation(10.0, 20.0, image, RectangleAnchor.LEFT); // assertFalse(a1.equals(a2)); // a2 = new XYImageAnnotation(10.0, 20.0, image, RectangleAnchor.LEFT); // assertTrue(a1.equals(a2)); // } // /** // * Two objects that are equal are required to return the same hashCode. // */ // @Test // public void testHashCode() { // Image image = JFreeChart.INFO.getLogo(); // XYImageAnnotation a1 = new XYImageAnnotation(10.0, 20.0, image); // XYImageAnnotation a2 = new XYImageAnnotation(10.0, 20.0, image); // assertTrue(a1.equals(a2)); // int h1 = a1.hashCode(); // int h2 = a2.hashCode(); // assertEquals(h1, h2); // } // /** // * Confirm that cloning works. // */ // @Test // public void testCloning() throws CloneNotSupportedException { // XYImageAnnotation a1 = new XYImageAnnotation(10.0, 20.0, // JFreeChart.INFO.getLogo()); // XYImageAnnotation a2 = (XYImageAnnotation) a1.clone(); // assertTrue(a1 != a2); // assertTrue(a1.getClass() == a2.getClass()); // assertTrue(a1.equals(a2)); // } // /** // * Checks that this class implements PublicCloneable. // */ // @Test // public void testPublicCloneable() { // XYImageAnnotation a1 = new XYImageAnnotation(10.0, 20.0, // JFreeChart.INFO.getLogo()); // assertTrue(a1 instanceof PublicCloneable); // } // FIXME: Make this test pass // /** // * Serialize an instance, restore it, and check for equality. // */ // public void testSerialization() { // XYImageAnnotation a1 = new XYImageAnnotation(10.0, 20.0, // JFreeChart.INFO.getLogo()); // XYImageAnnotation a2 = null; // try { // ByteArrayOutputStream buffer = new ByteArrayOutputStream(); // ObjectOutput out = new ObjectOutputStream(buffer); // out.writeObject(a1); // out.close(); // // ObjectInput in = new ObjectInputStream(new ByteArrayInputStream( // buffer.toByteArray())); // a2 = (XYImageAnnotation) in.readObject(); // in.close(); // } // catch (Exception e) { // e.printStackTrace(); // } // assertEquals(a1, a2); // } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/annotations/XYLineAnnotationTest.java000066400000000000000000000203131463604235500323530ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * XYLineAnnotationTest.java * ------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.annotations; import java.awt.BasicStroke; import java.awt.Color; import java.awt.GradientPaint; import java.awt.Stroke; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import javax.swing.event.EventListenerList; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XYLineAnnotation} class. */ public class XYLineAnnotationTest { private static final double EPSILON = 0.000000001; @Test public void testConstructor() { Stroke stroke = new BasicStroke(2.0f); XYLineAnnotation a1 = new XYLineAnnotation(10.0, 20.0, 100.0, 200.0, stroke, Color.BLUE); assertEquals(10.0, a1.getX1(), EPSILON); assertEquals(20.0, a1.getY1(), EPSILON); assertEquals(100.0, a1.getX2(), EPSILON); assertEquals(200.0, a1.getY2(), EPSILON); assertEquals(stroke, a1.getStroke()); assertEquals(Color.BLUE, a1.getPaint()); } @Test public void testConstructorExceptions() { Stroke stroke = new BasicStroke(2.0f); assertThrows(IllegalArgumentException.class, () -> { XYLineAnnotation a1 = new XYLineAnnotation(Double.NaN, 20.0, 100.0, 200.0, stroke, Color.BLUE); }); assertThrows(IllegalArgumentException.class, () -> { XYLineAnnotation a1 = new XYLineAnnotation(10.0, Double.NaN, 100.0, 200.0, stroke, Color.BLUE); }); assertThrows(IllegalArgumentException.class, () -> { XYLineAnnotation a1 = new XYLineAnnotation(10.0, 20.0, Double.NaN, 200.0, stroke, Color.BLUE); }); assertThrows(IllegalArgumentException.class, () -> { XYLineAnnotation a1 = new XYLineAnnotation(10.0, 20.0, 100.0, Double.NaN, stroke, Color.BLUE); }); assertThrows(IllegalArgumentException.class, () -> { XYLineAnnotation a1 = new XYLineAnnotation(10.0, 20.0, 100.0, 200.0, null, Color.BLUE); }); assertThrows(IllegalArgumentException.class, () -> { XYLineAnnotation a1 = new XYLineAnnotation(10.0, 20.0, 100.0, 200.0, stroke, null); }); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { Stroke stroke = new BasicStroke(2.0f); XYLineAnnotation a1 = new XYLineAnnotation(10.0, 20.0, 100.0, 200.0, stroke, Color.BLUE); XYLineAnnotation a2 = new XYLineAnnotation(10.0, 20.0, 100.0, 200.0, stroke, Color.BLUE); assertEquals(a1, a2); assertEquals(a2, a1); a1 = new XYLineAnnotation(11.0, 20.0, 100.0, 200.0, stroke, Color.BLUE); assertNotEquals(a1, a2); a2 = new XYLineAnnotation(11.0, 20.0, 100.0, 200.0, stroke, Color.BLUE); assertEquals(a1, a2); a1 = new XYLineAnnotation(11.0, 21.0, 100.0, 200.0, stroke, Color.BLUE); assertNotEquals(a1, a2); a2 = new XYLineAnnotation(11.0, 21.0, 100.0, 200.0, stroke, Color.BLUE); assertEquals(a1, a2); a1 = new XYLineAnnotation(11.0, 21.0, 101.0, 200.0, stroke, Color.BLUE); assertNotEquals(a1, a2); a2 = new XYLineAnnotation(11.0, 21.0, 101.0, 200.0, stroke, Color.BLUE); assertEquals(a1, a2); a1 = new XYLineAnnotation(11.0, 21.0, 101.0, 201.0, stroke, Color.BLUE); assertNotEquals(a1, a2); a2 = new XYLineAnnotation(11.0, 21.0, 101.0, 201.0, stroke, Color.BLUE); assertEquals(a1, a2); Stroke stroke2 = new BasicStroke(0.99f); a1 = new XYLineAnnotation(11.0, 21.0, 101.0, 200.0, stroke2, Color.BLUE); assertNotEquals(a1, a2); a2 = new XYLineAnnotation(11.0, 21.0, 101.0, 200.0, stroke2, Color.BLUE); assertEquals(a1, a2); GradientPaint g1 = new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.WHITE); GradientPaint g2 = new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.WHITE); a1 = new XYLineAnnotation(11.0, 21.0, 101.0, 200.0, stroke2, g1); assertNotEquals(a1, a2); a2 = new XYLineAnnotation(11.0, 21.0, 101.0, 200.0, stroke2, g2); assertEquals(a1, a2); } /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(XYLineAnnotation.class) .withRedefinedSuperclass() // superclass also defines equals/hashCode .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .withPrefabValues(EventListenerList.class, new EventListenerList(), new EventListenerList()) .verify(); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { Stroke stroke = new BasicStroke(2.0f); XYLineAnnotation a1 = new XYLineAnnotation(10.0, 20.0, 100.0, 200.0, stroke, Color.BLUE); XYLineAnnotation a2 = new XYLineAnnotation(10.0, 20.0, 100.0, 200.0, stroke, Color.BLUE); assertEquals(a1, a2); int h1 = a1.hashCode(); int h2 = a2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException if there is an issue cloning */ @Test public void testCloning() throws CloneNotSupportedException { Stroke stroke = new BasicStroke(2.0f); XYLineAnnotation a1 = new XYLineAnnotation(10.0, 20.0, 100.0, 200.0, stroke, Color.BLUE); XYLineAnnotation a2 = (XYLineAnnotation) a1.clone(); assertNotSame(a1, a2); assertSame(a1.getClass(), a2.getClass()); assertEquals(a1, a2); } /** * Checks that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { Stroke stroke = new BasicStroke(2.0f); XYLineAnnotation a1 = new XYLineAnnotation(10.0, 20.0, 100.0, 200.0, stroke, Color.BLUE); assertTrue(a1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { Stroke stroke = new BasicStroke(2.0f); XYLineAnnotation a1 = new XYLineAnnotation(10.0, 20.0, 100.0, 200.0, stroke, Color.BLUE); XYLineAnnotation a2 = TestUtils.serialised(a1); assertEquals(a1, a2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/annotations/XYPointerAnnotationTest.java000066400000000000000000000146511463604235500331140ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------- * XYPointerAnnotationTest.java * ---------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.annotations; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.Stroke; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import static org.jfree.chart.TestUtils.createFont; import static org.junit.jupiter.api.Assertions.*; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import javax.swing.event.EventListenerList; /** * Tests for the {@link XYPointerAnnotation} class. */ public class XYPointerAnnotationTest { /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(XYPointerAnnotation.class) .withRedefinedSuperclass() // superclass also defines equals/hashCode .withPrefabValues(Font.class, createFont(true), createFont(false)) .withPrefabValues(EventListenerList.class, new EventListenerList(), new EventListenerList()) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { XYPointerAnnotation a1 = new XYPointerAnnotation("Label", 10.0, 20.0, Math.PI); XYPointerAnnotation a2 = new XYPointerAnnotation("Label", 10.0, 20.0, Math.PI); assertEquals(a1, a2); a1 = new XYPointerAnnotation("Label2", 10.0, 20.0, Math.PI); assertNotEquals(a1, a2); a2 = new XYPointerAnnotation("Label2", 10.0, 20.0, Math.PI); assertEquals(a1, a2); a1.setX(11.0); assertNotEquals(a1, a2); a2.setX(11.0); assertEquals(a1, a2); a1.setY(22.0); assertNotEquals(a1, a2); a2.setY(22.0); assertEquals(a1, a2); //private double angle; a1.setAngle(Math.PI / 4.0); assertNotEquals(a1, a2); a2.setAngle(Math.PI / 4.0); assertEquals(a1, a2); //private double tipRadius; a1.setTipRadius(20.0); assertNotEquals(a1, a2); a2.setTipRadius(20.0); assertEquals(a1, a2); //private double baseRadius; a1.setBaseRadius(5.0); assertNotEquals(a1, a2); a2.setBaseRadius(5.0); assertEquals(a1, a2); //private double arrowLength; a1.setArrowLength(33.0); assertNotEquals(a1, a2); a2.setArrowLength(33.0); assertEquals(a1, a2); //private double arrowWidth; a1.setArrowWidth(9.0); assertNotEquals(a1, a2); a2.setArrowWidth(9.0); assertEquals(a1, a2); //private Stroke arrowStroke; Stroke stroke = new BasicStroke(1.5f); a1.setArrowStroke(stroke); assertNotEquals(a1, a2); a2.setArrowStroke(stroke); assertEquals(a1, a2); //private Paint arrowPaint; a1.setArrowPaint(Color.BLUE); assertNotEquals(a1, a2); a2.setArrowPaint(Color.BLUE); assertEquals(a1, a2); //private double labelOffset; a1.setLabelOffset(10.0); assertNotEquals(a1, a2); a2.setLabelOffset(10.0); assertEquals(a1, a2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { XYPointerAnnotation a1 = new XYPointerAnnotation("Label", 10.0, 20.0, Math.PI); XYPointerAnnotation a2 = new XYPointerAnnotation("Label", 10.0, 20.0, Math.PI); assertEquals(a1, a2); int h1 = a1.hashCode(); int h2 = a2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException if there is an issue cloning */ @Test public void testCloning() throws CloneNotSupportedException { XYPointerAnnotation a1 = new XYPointerAnnotation("Label", 10.0, 20.0, Math.PI); XYPointerAnnotation a2 = (XYPointerAnnotation) a1.clone(); assertNotSame(a1, a2); assertSame(a1.getClass(), a2.getClass()); assertEquals(a1, a2); } /** * Checks that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { XYPointerAnnotation a1 = new XYPointerAnnotation("Label", 10.0, 20.0, Math.PI); assertTrue(a1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XYPointerAnnotation a1 = new XYPointerAnnotation("Label", 10.0, 20.0, Math.PI); XYPointerAnnotation a2 = TestUtils.serialised(a1); assertEquals(a1, a2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/annotations/XYPolygonAnnotationTest.java000066400000000000000000000154141463604235500331210ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------- * XYPolygonAnnotationTest.java * ---------------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.annotations; import java.awt.BasicStroke; import java.awt.Color; import java.awt.GradientPaint; import java.awt.Stroke; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import javax.swing.event.EventListenerList; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XYPolygonAnnotation} class. */ public class XYPolygonAnnotationTest { /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(XYPolygonAnnotation.class) .withRedefinedSuperclass() // superclass also defines equals/hashCode .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .withPrefabValues(EventListenerList.class, new EventListenerList(), new EventListenerList()) .verify(); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { Stroke stroke1 = new BasicStroke(2.0f); Stroke stroke2 = new BasicStroke(2.5f); XYPolygonAnnotation a1 = new XYPolygonAnnotation(new double[] {1.0, 2.0, 3.0, 4.0, 5.0, 6.0}, stroke1, Color.RED, Color.BLUE); XYPolygonAnnotation a2 = new XYPolygonAnnotation(new double[] {1.0, 2.0, 3.0, 4.0, 5.0, 6.0}, stroke1, Color.RED, Color.BLUE); assertEquals(a1, a2); assertEquals(a2, a1); a1 = new XYPolygonAnnotation(new double[] {99.0, 2.0, 3.0, 4.0, 5.0, 6.0}, stroke1, Color.RED, Color.BLUE); assertNotEquals(a1, a2); a2 = new XYPolygonAnnotation(new double[] {99.0, 2.0, 3.0, 4.0, 5.0, 6.0}, stroke1, Color.RED, Color.BLUE); assertEquals(a1, a2); a1 = new XYPolygonAnnotation(new double[] {99.0, 2.0, 3.0, 4.0, 5.0, 6.0}, stroke2, Color.RED, Color.BLUE); assertNotEquals(a1, a2); a2 = new XYPolygonAnnotation(new double[] {99.0, 2.0, 3.0, 4.0, 5.0, 6.0}, stroke2, Color.RED, Color.BLUE); assertEquals(a1, a2); GradientPaint gp1 = new GradientPaint(1.0f, 2.0f, Color.YELLOW, 3.0f, 4.0f, Color.WHITE); GradientPaint gp2 = new GradientPaint(1.0f, 2.0f, Color.YELLOW, 3.0f, 4.0f, Color.WHITE); a1 = new XYPolygonAnnotation(new double[] {99.0, 2.0, 3.0, 4.0, 5.0, 6.0}, stroke2, gp1, Color.BLUE); assertNotEquals(a1, a2); a2 = new XYPolygonAnnotation(new double[] {99.0, 2.0, 3.0, 4.0, 5.0, 6.0}, stroke2, gp2, Color.BLUE); assertEquals(a1, a2); GradientPaint gp3 = new GradientPaint(1.0f, 2.0f, Color.GREEN, 3.0f, 4.0f, Color.WHITE); GradientPaint gp4 = new GradientPaint(1.0f, 2.0f, Color.GREEN, 3.0f, 4.0f, Color.WHITE); a1 = new XYPolygonAnnotation(new double[] {99.0, 2.0, 3.0, 4.0, 5.0, 6.0}, stroke2, gp1, gp3); assertNotEquals(a1, a2); a2 = new XYPolygonAnnotation(new double[] {99.0, 2.0, 3.0, 4.0, 5.0, 6.0}, stroke2, gp2, gp4); assertEquals(a1, a2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { Stroke stroke = new BasicStroke(2.0f); XYPolygonAnnotation a1 = new XYPolygonAnnotation(new double[] {1.0, 2.0, 3.0, 4.0, 5.0, 6.0}, stroke, Color.RED, Color.BLUE); XYPolygonAnnotation a2 = new XYPolygonAnnotation(new double[] {1.0, 2.0, 3.0, 4.0, 5.0, 6.0}, stroke, Color.RED, Color.BLUE); assertEquals(a1, a2); int h1 = a1.hashCode(); int h2 = a2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { Stroke stroke1 = new BasicStroke(2.0f); XYPolygonAnnotation a1 = new XYPolygonAnnotation(new double[] {1.0, 2.0, 3.0, 4.0, 5.0, 6.0}, stroke1, Color.RED, Color.BLUE); XYPolygonAnnotation a2 = (XYPolygonAnnotation) a1.clone(); assertNotSame(a1, a2); assertSame(a1.getClass(), a2.getClass()); assertEquals(a1, a2); } /** * Checks that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { Stroke stroke1 = new BasicStroke(2.0f); XYPolygonAnnotation a1 = new XYPolygonAnnotation(new double[] {1.0, 2.0, 3.0, 4.0, 5.0, 6.0}, stroke1, Color.RED, Color.BLUE); assertTrue(a1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { Stroke stroke1 = new BasicStroke(2.0f); XYPolygonAnnotation a1 = new XYPolygonAnnotation(new double[] {1.0, 2.0, 3.0, 4.0, 5.0, 6.0}, stroke1, Color.RED, Color.BLUE); XYPolygonAnnotation a2 = TestUtils.serialised(a1); assertEquals(a1, a2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/annotations/XYShapeAnnotationTest.java000066400000000000000000000142561463604235500325350ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * XYShapeAnnotationTest.java * -------------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.annotations; import java.awt.BasicStroke; import java.awt.Color; import java.awt.GradientPaint; import java.awt.geom.Rectangle2D; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Some tests for the {@link XYShapeAnnotation} class. */ public class XYShapeAnnotationTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { XYShapeAnnotation a1 = new XYShapeAnnotation( new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(1.2f), Color.RED, Color.BLUE); XYShapeAnnotation a2 = new XYShapeAnnotation( new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(1.2f), Color.RED, Color.BLUE); assertEquals(a1, a2); assertEquals(a2, a1); // shape a1 = new XYShapeAnnotation( new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0), new BasicStroke(1.2f), Color.RED, Color.BLUE); assertNotEquals(a1, a2); a2 = new XYShapeAnnotation( new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0), new BasicStroke(1.2f), Color.RED, Color.BLUE); assertEquals(a1, a2); // stroke a1 = new XYShapeAnnotation( new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0), new BasicStroke(2.3f), Color.RED, Color.BLUE); assertNotEquals(a1, a2); a2 = new XYShapeAnnotation( new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0), new BasicStroke(2.3f), Color.RED, Color.BLUE); assertEquals(a1, a2); GradientPaint gp1a = new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.RED); GradientPaint gp1b = new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.RED); GradientPaint gp2a = new GradientPaint(5.0f, 6.0f, Color.pink, 7.0f, 8.0f, Color.WHITE); GradientPaint gp2b = new GradientPaint(5.0f, 6.0f, Color.pink, 7.0f, 8.0f, Color.WHITE); // outlinePaint a1 = new XYShapeAnnotation( new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0), new BasicStroke(2.3f), gp1a, Color.BLUE); assertNotEquals(a1, a2); a2 = new XYShapeAnnotation( new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0), new BasicStroke(2.3f), gp1b, Color.BLUE); assertEquals(a1, a2); // fillPaint a1 = new XYShapeAnnotation( new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0), new BasicStroke(2.3f), gp1a, gp2a); assertNotEquals(a1, a2); a2 = new XYShapeAnnotation( new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0), new BasicStroke(2.3f), gp1b, gp2b); assertEquals(a1, a2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { XYShapeAnnotation a1 = new XYShapeAnnotation( new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(1.2f), Color.RED, Color.BLUE); XYShapeAnnotation a2 = new XYShapeAnnotation( new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(1.2f), Color.RED, Color.BLUE); assertEquals(a1, a2); int h1 = a1.hashCode(); int h2 = a2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { XYShapeAnnotation a1 = new XYShapeAnnotation( new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(1.2f), Color.RED, Color.BLUE); XYShapeAnnotation a2 = (XYShapeAnnotation) a1.clone(); assertNotSame(a1, a2); assertSame(a1.getClass(), a2.getClass()); assertEquals(a1, a2); } /** * Checks that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { XYShapeAnnotation a1 = new XYShapeAnnotation( new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(1.2f), Color.RED, Color.BLUE); assertTrue(a1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XYShapeAnnotation a1 = new XYShapeAnnotation( new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(1.2f), Color.RED, Color.BLUE); XYShapeAnnotation a2 = TestUtils.serialised(a1); assertEquals(a1, a2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/annotations/XYTextAnnotationTest.java000066400000000000000000000154431463604235500324200ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * XYTextAnnotationTest.java * ------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.annotations; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.GradientPaint; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import static org.jfree.chart.TestUtils.createFont; import static org.junit.jupiter.api.Assertions.*; import org.jfree.chart.TestUtils; import org.jfree.chart.ui.TextAnchor; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import javax.swing.event.EventListenerList; /** * Tests for the {@link XYTextAnnotation} class. */ public class XYTextAnnotationTest { /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(XYTextAnnotation.class) .withRedefinedSuperclass() // superclass also defines equals/hashCode .withPrefabValues(Font.class, createFont(true), createFont(false)) .withPrefabValues(EventListenerList.class, new EventListenerList(), new EventListenerList()) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { XYTextAnnotation a1 = new XYTextAnnotation("Text", 10.0, 20.0); XYTextAnnotation a2 = new XYTextAnnotation("Text", 10.0, 20.0); assertEquals(a1, a2); // text a1 = new XYTextAnnotation("ABC", 10.0, 20.0); assertNotEquals(a1, a2); a2 = new XYTextAnnotation("ABC", 10.0, 20.0); assertEquals(a1, a2); // x a1 = new XYTextAnnotation("ABC", 11.0, 20.0); assertNotEquals(a1, a2); a2 = new XYTextAnnotation("ABC", 11.0, 20.0); assertEquals(a1, a2); // y a1 = new XYTextAnnotation("ABC", 11.0, 22.0); assertNotEquals(a1, a2); a2 = new XYTextAnnotation("ABC", 11.0, 22.0); assertEquals(a1, a2); // font a1.setFont(new Font("Serif", Font.PLAIN, 23)); assertNotEquals(a1, a2); a2.setFont(new Font("Serif", Font.PLAIN, 23)); assertEquals(a1, a2); // paint GradientPaint gp1 = new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW); GradientPaint gp2 = new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW); a1.setPaint(gp1); assertNotEquals(a1, a2); a2.setPaint(gp2); assertEquals(a1, a2); // rotation anchor a1.setRotationAnchor(TextAnchor.BASELINE_RIGHT); assertNotEquals(a1, a2); a2.setRotationAnchor(TextAnchor.BASELINE_RIGHT); assertEquals(a1, a2); // rotation angle a1.setRotationAngle(12.3); assertNotEquals(a1, a2); a2.setRotationAngle(12.3); assertEquals(a1, a2); // text anchor a1.setTextAnchor(TextAnchor.BASELINE_RIGHT); assertNotEquals(a1, a2); a2.setTextAnchor(TextAnchor.BASELINE_RIGHT); assertEquals(a1, a2); a1.setBackgroundPaint(gp1); assertNotEquals(a1, a2); a2.setBackgroundPaint(gp1); assertEquals(a1, a2); a1.setOutlinePaint(gp1); assertNotEquals(a1, a2); a2.setOutlinePaint(gp1); assertEquals(a1, a2); a1.setOutlineStroke(new BasicStroke(1.2f)); assertNotEquals(a1, a2); a2.setOutlineStroke(new BasicStroke(1.2f)); assertEquals(a1, a2); a1.setOutlineVisible(!a1.isOutlineVisible()); assertNotEquals(a1, a2); a2.setOutlineVisible(a1.isOutlineVisible()); assertEquals(a1, a2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { XYTextAnnotation a1 = new XYTextAnnotation("Text", 10.0, 20.0); XYTextAnnotation a2 = new XYTextAnnotation("Text", 10.0, 20.0); assertEquals(a1, a2); int h1 = a1.hashCode(); int h2 = a2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException if there is an issue cloning. */ @Test public void testCloning() throws CloneNotSupportedException { XYTextAnnotation a1 = new XYTextAnnotation("Text", 10.0, 20.0); XYTextAnnotation a2 = (XYTextAnnotation) a1.clone(); assertNotSame(a1, a2); assertSame(a1.getClass(), a2.getClass()); assertEquals(a1, a2); } /** * Checks that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { XYTextAnnotation a1 = new XYTextAnnotation("Text", 10.0, 20.0); assertTrue(a1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XYTextAnnotation a1 = new XYTextAnnotation("Text", 10.0, 20.0); a1.setOutlinePaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); XYTextAnnotation a2 = TestUtils.serialised(a1); assertEquals(a1, a2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/annotations/XYTitleAnnotationTest.java000066400000000000000000000144741463604235500325600ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * XYTitleAnnotationTest.java * -------------------------- * (C) Copyright 2007-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.annotations; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.JFreeChart; import org.jfree.chart.TestUtils; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; import org.jfree.chart.title.TextTitle; import org.jfree.chart.title.Title; import org.jfree.data.xy.DefaultTableXYDataset; import org.jfree.data.xy.XYSeries; import org.junit.jupiter.api.Test; import javax.swing.event.EventListenerList; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XYTitleAnnotation} class. */ public class XYTitleAnnotationTest { /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(XYTitleAnnotation.class) .withRedefinedSuperclass() // superclass also defines equals/hashCode .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .withPrefabValues(Title.class, new TextTitle("abc"), new TextTitle("def")) .withPrefabValues(EventListenerList.class, new EventListenerList(), new EventListenerList()) .verify(); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { TextTitle t = new TextTitle("Title"); XYTitleAnnotation a1 = new XYTitleAnnotation(1.0, 2.0, t); XYTitleAnnotation a2 = new XYTitleAnnotation(1.0, 2.0, t); assertEquals(a1, a2); a1 = new XYTitleAnnotation(1.1, 2.0, t); assertNotEquals(a1, a2); a2 = new XYTitleAnnotation(1.1, 2.0, t); assertEquals(a1, a2); a1 = new XYTitleAnnotation(1.1, 2.2, t); assertNotEquals(a1, a2); a2 = new XYTitleAnnotation(1.1, 2.2, t); assertEquals(a1, a2); TextTitle t2 = new TextTitle("Title 2"); a1 = new XYTitleAnnotation(1.1, 2.2, t2); assertNotEquals(a1, a2); a2 = new XYTitleAnnotation(1.1, 2.2, t2); assertEquals(a1, a2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { TextTitle t = new TextTitle("Title"); XYTitleAnnotation a1 = new XYTitleAnnotation(1.0, 2.0, t); XYTitleAnnotation a2 = new XYTitleAnnotation(1.0, 2.0, t); assertEquals(a1, a2); int h1 = a1.hashCode(); int h2 = a2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException if there is a cloning issue. */ @Test public void testCloning() throws CloneNotSupportedException { TextTitle t = new TextTitle("Title"); XYTitleAnnotation a1 = new XYTitleAnnotation(1.0, 2.0, t); XYTitleAnnotation a2 = (XYTitleAnnotation) a1.clone(); assertNotSame(a1, a2); assertSame(a1.getClass(), a2.getClass()); assertEquals(a1, a2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { TextTitle t = new TextTitle("Title"); XYTitleAnnotation a1 = new XYTitleAnnotation(1.0, 2.0, t); XYTitleAnnotation a2 = TestUtils.serialised(a1); assertEquals(a1, a2); } /** * Draws the chart with a {@code null} info object to make sure that * no exceptions are thrown. */ @Test public void testDrawWithNullInfo() { try { DefaultTableXYDataset dataset = new DefaultTableXYDataset(); XYSeries s1 = new XYSeries("Series 1", true, false); s1.add(5.0, 5.0); s1.add(10.0, 15.5); s1.add(15.0, 9.5); s1.add(20.0, 7.5); dataset.addSeries(s1); XYSeries s2 = new XYSeries("Series 2", true, false); s2.add(5.0, 5.0); s2.add(10.0, 15.5); s2.add(15.0, 9.5); s2.add(20.0, 3.5); dataset.addSeries(s2); XYPlot plot = new XYPlot(dataset, new NumberAxis("X"), new NumberAxis("Y"), new XYLineAndShapeRenderer()); plot.addAnnotation(new XYTitleAnnotation(5.0, 6.0, new TextTitle("Hello World!"))); JFreeChart chart = new JFreeChart(plot); /* BufferedImage image = */ chart.createBufferedImage(300, 200, null); } catch (NullPointerException e) { fail("There should be no exception."); } } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/annotations/package.html000066400000000000000000000002651463604235500277320ustar00rootroot00000000000000 Tests for the classes in the org.jfree.chart.annotations package. jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/axis/000077500000000000000000000000001463604235500240555ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/axis/AxisLocationTest.java000066400000000000000000000055621463604235500301650ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * AxisLocationTest.java * --------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; /** * Tests for the {@link AxisLocation} class. */ public class AxisLocationTest { /** * Some checks for the equals() method. */ @Test public void testEquals() { assertEquals(AxisLocation.TOP_OR_RIGHT, AxisLocation.TOP_OR_RIGHT); assertEquals(AxisLocation.BOTTOM_OR_RIGHT, AxisLocation.BOTTOM_OR_RIGHT); assertEquals(AxisLocation.TOP_OR_LEFT, AxisLocation.TOP_OR_LEFT); assertEquals(AxisLocation.BOTTOM_OR_LEFT, AxisLocation.BOTTOM_OR_LEFT); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { AxisLocation a1 = AxisLocation.TOP_OR_RIGHT; AxisLocation a2 = AxisLocation.TOP_OR_RIGHT; assertEquals(a1, a2); int h1 = a1.hashCode(); int h2 = a2.hashCode(); assertEquals(h1, h2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { AxisLocation location1 = AxisLocation.BOTTOM_OR_RIGHT; AxisLocation location2 = TestUtils.serialised(location1); assertEquals(location1, location2); boolean same = location1 == location2; assertTrue(same); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/axis/AxisSpaceTest.java000066400000000000000000000070261463604235500274450ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * AxisSpaceTest.java * ------------------ * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.axis; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link AxisSpace} class. */ public class AxisSpaceTest { /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(AxisSpace.class) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Check that the equals() method can distinguish all fields. */ @Test public void testEquals() { AxisSpace a1 = new AxisSpace(); AxisSpace a2 = new AxisSpace(); assertEquals(a1, a2); a1.setTop(1.11); assertNotEquals(a1, a2); a2.setTop(1.11); assertEquals(a1, a2); a1.setBottom(2.22); assertNotEquals(a1, a2); a2.setBottom(2.22); assertEquals(a1, a2); a1.setLeft(3.33); assertNotEquals(a1, a2); a2.setLeft(3.33); assertEquals(a1, a2); a1.setRight(4.44); assertNotEquals(a1, a2); a2.setRight(4.44); assertEquals(a1, a2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { AxisSpace s1 = new AxisSpace(); AxisSpace s2 = new AxisSpace(); assertEquals(s1, s2); int h1 = s1.hashCode(); int h2 = s2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException if there is a problem cloning. */ @Test public void testCloning() throws CloneNotSupportedException { AxisSpace s1 = new AxisSpace(); AxisSpace s2 = (AxisSpace) s1.clone(); assertNotSame(s1, s2); assertSame(s1.getClass(), s2.getClass()); assertEquals(s1, s2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/axis/AxisTest.java000066400000000000000000000216001463604235500264630ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------- * AxisTest.java * ------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertSame; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.GradientPaint; import java.awt.font.TextAttribute; import java.text.AttributedString; import org.jfree.chart.TestUtils; import org.jfree.chart.ui.RectangleInsets; import org.junit.jupiter.api.Test; /** * Tests for the {@link Axis} class. */ public class AxisTest { /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { CategoryAxis a1 = new CategoryAxis("Test"); a1.setAxisLinePaint(Color.RED); CategoryAxis a2 = (CategoryAxis) a1.clone(); assertNotSame(a1, a2); assertSame(a1.getClass(), a2.getClass()); assertEquals(a1, a2); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { Axis a1 = new CategoryAxis("Test"); Axis a2 = new CategoryAxis("Test"); assertEquals(a1, a2); // visible flag... a1.setVisible(false); assertNotEquals(a1, a2); a2.setVisible(false); assertEquals(a1, a2); // label... a1.setLabel("New Label"); assertNotEquals(a1, a2); a2.setLabel("New Label"); assertEquals(a1, a2); // label font... a1.setLabelFont(new Font("Dialog", Font.PLAIN, 8)); assertNotEquals(a1, a2); a2.setLabelFont(new Font("Dialog", Font.PLAIN, 8)); assertEquals(a1, a2); // label paint... a1.setLabelPaint(new GradientPaint(1.0f, 2.0f, Color.WHITE, 3.0f, 4.0f, Color.BLACK)); assertNotEquals(a1, a2); a2.setLabelPaint(new GradientPaint(1.0f, 2.0f, Color.WHITE, 3.0f, 4.0f, Color.BLACK)); assertEquals(a1, a2); // attributed label... a1.setAttributedLabel(new AttributedString("Hello World!")); assertNotEquals(a1, a2); a2.setAttributedLabel(new AttributedString("Hello World!")); assertEquals(a1, a2); AttributedString l1 = a1.getAttributedLabel(); l1.addAttribute(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUB, 1, 2); a1.setAttributedLabel(l1); assertNotEquals(a1, a2); AttributedString l2 = a2.getAttributedLabel(); l2.addAttribute(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUB, 1, 2); a2.setAttributedLabel(l2); assertEquals(a1, a2); // label insets... a1.setLabelInsets(new RectangleInsets(10.0, 10.0, 10.0, 10.0)); assertNotEquals(a1, a2); a2.setLabelInsets(new RectangleInsets(10.0, 10.0, 10.0, 10.0)); assertEquals(a1, a2); // label angle... a1.setLabelAngle(1.23); assertNotEquals(a1, a2); a2.setLabelAngle(1.23); assertEquals(a1, a2); // label location... a1.setLabelLocation(AxisLabelLocation.HIGH_END); assertNotEquals(a1, a2); a2.setLabelLocation(AxisLabelLocation.HIGH_END); assertEquals(a1, a2); // axis line visible... a1.setAxisLineVisible(false); assertNotEquals(a1, a2); a2.setAxisLineVisible(false); assertEquals(a1, a2); // axis line stroke... BasicStroke s = new BasicStroke(1.1f); a1.setAxisLineStroke(s); assertNotEquals(a1, a2); a2.setAxisLineStroke(s); assertEquals(a1, a2); // axis line paint... a1.setAxisLinePaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLACK)); assertNotEquals(a1, a2); a2.setAxisLinePaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLACK)); assertEquals(a1, a2); // tick labels visible flag... a1.setTickLabelsVisible(false); assertNotEquals(a1, a2); a2.setTickLabelsVisible(false); assertEquals(a1, a2); // tick label font... a1.setTickLabelFont(new Font("Dialog", Font.PLAIN, 12)); assertNotEquals(a1, a2); a2.setTickLabelFont(new Font("Dialog", Font.PLAIN, 12)); assertEquals(a1, a2); // tick label paint... a1.setTickLabelPaint(new GradientPaint(1.0f, 2.0f, Color.YELLOW, 3.0f, 4.0f, Color.BLACK)); assertNotEquals(a1, a2); a2.setTickLabelPaint(new GradientPaint(1.0f, 2.0f, Color.YELLOW, 3.0f, 4.0f, Color.BLACK)); assertEquals(a1, a2); // tick label insets... a1.setTickLabelInsets(new RectangleInsets(10.0, 10.0, 10.0, 10.0)); assertNotEquals(a1, a2); a2.setTickLabelInsets(new RectangleInsets(10.0, 10.0, 10.0, 10.0)); assertEquals(a1, a2); // tick marks visible flag... a1.setTickMarksVisible(false); assertNotEquals(a1, a2); a2.setTickMarksVisible(false); assertEquals(a1, a2); // tick mark inside length... a1.setTickMarkInsideLength(1.23f); assertNotEquals(a1, a2); a2.setTickMarkInsideLength(1.23f); assertEquals(a1, a2); // tick mark outside length... a1.setTickMarkOutsideLength(1.23f); assertNotEquals(a1, a2); a2.setTickMarkOutsideLength(1.23f); assertEquals(a1, a2); // tick mark stroke... a1.setTickMarkStroke(new BasicStroke(2.0f)); assertNotEquals(a1, a2); a2.setTickMarkStroke(new BasicStroke(2.0f)); assertEquals(a1, a2); // tick mark paint... a1.setTickMarkPaint(new GradientPaint(1.0f, 2.0f, Color.CYAN, 3.0f, 4.0f, Color.BLACK)); assertNotEquals(a1, a2); a2.setTickMarkPaint(new GradientPaint(1.0f, 2.0f, Color.CYAN, 3.0f, 4.0f, Color.BLACK)); assertEquals(a1, a2); // fixed dimension... a1.setFixedDimension(3.21f); assertNotEquals(a1, a2); a2.setFixedDimension(3.21f); assertEquals(a1, a2); a1.setMinorTickMarksVisible(true); assertNotEquals(a1, a2); a2.setMinorTickMarksVisible(true); assertEquals(a1, a2); a1.setMinorTickMarkInsideLength(1.23f); assertNotEquals(a1, a2); a2.setMinorTickMarkInsideLength(1.23f); assertEquals(a1, a2); a1.setMinorTickMarkOutsideLength(3.21f); assertNotEquals(a1, a2); a2.setMinorTickMarkOutsideLength(3.21f); assertEquals(a1, a2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { Axis a1 = new CategoryAxis("Test"); Axis a2 = new CategoryAxis("Test"); assertEquals(a1, a2); int h1 = a1.hashCode(); int h2 = a2.hashCode(); assertEquals(h1, h2); } /** * Checks that serialization works, particularly with the attributed label. */ @Test public void testSerialization() { Axis a1 = new CategoryAxis("Test"); AttributedString label = new AttributedString("Axis Label"); label.addAttribute(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUB, 1, 4); a1.setAttributedLabel(label); Axis a2 = TestUtils.serialised(a1); assertEquals(a1, a2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/axis/CategoryAnchorTest.java000066400000000000000000000053751463604235500305020ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * CategoryAnchorTest.java * ----------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link CategoryAnchor} class. */ public class CategoryAnchorTest { /** * Check that the equals() method distinguishes known instances. */ @Test public void testEquals() { assertEquals(CategoryAnchor.START, CategoryAnchor.START); assertEquals(CategoryAnchor.MIDDLE, CategoryAnchor.MIDDLE); assertEquals(CategoryAnchor.END, CategoryAnchor.END); assertNotEquals(CategoryAnchor.START, CategoryAnchor.END); assertNotEquals(CategoryAnchor.MIDDLE, CategoryAnchor.END); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { CategoryAnchor a1 = CategoryAnchor.START; CategoryAnchor a2 = CategoryAnchor.START; assertEquals(a1, a2); int h1 = a1.hashCode(); int h2 = a2.hashCode(); assertEquals(h1, h2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { CategoryAnchor a1 = CategoryAnchor.MIDDLE; CategoryAnchor a2 = TestUtils.serialised(a1); assertEquals(a1, a2); assertSame(a1, a2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/axis/CategoryAxisTest.java000066400000000000000000000151121463604235500301620ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * CategoryAxisTest.java * --------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.awt.Color; import java.awt.Font; import java.awt.GradientPaint; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link CategoryAxis} class. */ public class CategoryAxisTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { CategoryAxis a1 = new CategoryAxis("Test"); CategoryAxis a2 = new CategoryAxis("Test"); assertEquals(a1, a2); // lowerMargin a1.setLowerMargin(0.15); assertNotEquals(a1, a2); a2.setLowerMargin(0.15); assertEquals(a1, a2); // upperMargin a1.setUpperMargin(0.15); assertNotEquals(a1, a2); a2.setUpperMargin(0.15); assertEquals(a1, a2); // categoryMargin a1.setCategoryMargin(0.15); assertNotEquals(a1, a2); a2.setCategoryMargin(0.15); assertEquals(a1, a2); // maxCategoryLabelWidthRatio a1.setMaximumCategoryLabelWidthRatio(0.98f); assertNotEquals(a1, a2); a2.setMaximumCategoryLabelWidthRatio(0.98f); assertEquals(a1, a2); // categoryLabelPositionOffset a1.setCategoryLabelPositionOffset(11); assertNotEquals(a1, a2); a2.setCategoryLabelPositionOffset(11); assertEquals(a1, a2); // categoryLabelPositions a1.setCategoryLabelPositions(CategoryLabelPositions.DOWN_45); assertNotEquals(a1, a2); a2.setCategoryLabelPositions(CategoryLabelPositions.DOWN_45); assertEquals(a1, a2); // categoryLabelToolTips a1.addCategoryLabelToolTip("Test", "Check"); assertNotEquals(a1, a2); a2.addCategoryLabelToolTip("Test", "Check"); assertEquals(a1, a2); // categoryLabelURLs a1.addCategoryLabelURL("Test", "http://www.jfree.org/"); assertNotEquals(a1, a2); a2.addCategoryLabelURL("Test", "http://www.jfree.org/"); assertEquals(a1, a2); // tickLabelFont a1.setTickLabelFont("C1", new Font("Dialog", Font.PLAIN, 21)); assertNotEquals(a1, a2); a2.setTickLabelFont("C1", new Font("Dialog", Font.PLAIN, 21)); assertEquals(a1, a2); // tickLabelPaint a1.setTickLabelPaint("C1", Color.RED); assertNotEquals(a1, a2); a2.setTickLabelPaint("C1", Color.RED); assertEquals(a1, a2); // tickLabelPaint2 a1.setTickLabelPaint("C1", new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW)); assertNotEquals(a1, a2); a2.setTickLabelPaint("C1", new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW)); assertEquals(a1, a2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { CategoryAxis a1 = new CategoryAxis("Test"); CategoryAxis a2 = new CategoryAxis("Test"); assertEquals(a1, a2); int h1 = a1.hashCode(); int h2 = a2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { CategoryAxis a1 = new CategoryAxis("Test"); CategoryAxis a2 = (CategoryAxis) a1.clone(); assertNotSame(a1, a2); assertSame(a1.getClass(), a2.getClass()); assertEquals(a1, a2); } /** * Confirm that cloning works. This test customises the font and paint * per category label. */ @Test public void testCloning2() throws CloneNotSupportedException { CategoryAxis a1 = new CategoryAxis("Test"); a1.setTickLabelFont("C1", new Font("Dialog", Font.PLAIN, 15)); a1.setTickLabelPaint("C1", new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.WHITE)); CategoryAxis a2 = (CategoryAxis) a1.clone(); assertNotSame(a1, a2); assertSame(a1.getClass(), a2.getClass()); assertEquals(a1, a2); // check that changing a tick label font in a1 doesn't change a2 a1.setTickLabelFont("C1", null); assertNotEquals(a1, a2); a2.setTickLabelFont("C1", null); assertEquals(a1, a2); // check that changing a tick label paint in a1 doesn't change a2 a1.setTickLabelPaint("C1", Color.YELLOW); assertNotEquals(a1, a2); a2.setTickLabelPaint("C1", Color.YELLOW); assertEquals(a1, a2); // check that changing a category label tooltip in a1 doesn't change a2 a1.addCategoryLabelToolTip("C1", "XYZ"); assertNotEquals(a1, a2); a2.addCategoryLabelToolTip("C1", "XYZ"); assertEquals(a1, a2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { CategoryAxis a1 = new CategoryAxis("Test Axis"); a1.setTickLabelPaint("C1", new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.WHITE)); CategoryAxis a2 = TestUtils.serialised(a1); assertEquals(a1, a2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/axis/CategoryLabelPositionTest.java000066400000000000000000000144341463604235500320300ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------ * CategoryLabelPositionTest.java * ------------------------------ * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import org.jfree.chart.TestUtils; import org.jfree.chart.text.TextBlockAnchor; import org.jfree.chart.ui.RectangleAnchor; import org.jfree.chart.ui.TextAnchor; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link CategoryLabelPosition} class. */ public class CategoryLabelPositionTest { /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(CategoryLabelPosition.class) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Check that the equals() method can distinguish all fields. */ @Test public void testEquals() { CategoryLabelPosition p1 = new CategoryLabelPosition( RectangleAnchor.BOTTOM_LEFT, TextBlockAnchor.CENTER_RIGHT, TextAnchor.BASELINE_LEFT, Math.PI / 4.0, CategoryLabelWidthType.RANGE, 0.44f); CategoryLabelPosition p2 = new CategoryLabelPosition( RectangleAnchor.BOTTOM_LEFT, TextBlockAnchor.CENTER_RIGHT, TextAnchor.BASELINE_LEFT, Math.PI / 4.0, CategoryLabelWidthType.RANGE, 0.44f); assertEquals(p1, p2); assertEquals(p2, p1); p1 = new CategoryLabelPosition(RectangleAnchor.TOP, TextBlockAnchor.CENTER_RIGHT, TextAnchor.BASELINE_LEFT, Math.PI / 4.0, CategoryLabelWidthType.RANGE, 0.44f); assertNotEquals(p1, p2); p2 = new CategoryLabelPosition(RectangleAnchor.TOP, TextBlockAnchor.CENTER_RIGHT, TextAnchor.BASELINE_LEFT, Math.PI / 4.0, CategoryLabelWidthType.RANGE, 0.44f); assertEquals(p1, p2); p1 = new CategoryLabelPosition(RectangleAnchor.TOP, TextBlockAnchor.CENTER, TextAnchor.BASELINE_LEFT, Math.PI / 4.0, CategoryLabelWidthType.RANGE, 0.44f); assertNotEquals(p1, p2); p2 = new CategoryLabelPosition(RectangleAnchor.TOP, TextBlockAnchor.CENTER, TextAnchor.BASELINE_LEFT, Math.PI / 4.0, CategoryLabelWidthType.RANGE, 0.44f); assertEquals(p1, p2); p1 = new CategoryLabelPosition(RectangleAnchor.TOP, TextBlockAnchor.CENTER, TextAnchor.CENTER, Math.PI / 4.0, CategoryLabelWidthType.RANGE, 0.44f); assertNotEquals(p1, p2); p2 = new CategoryLabelPosition(RectangleAnchor.TOP, TextBlockAnchor.CENTER, TextAnchor.CENTER, Math.PI / 4.0, CategoryLabelWidthType.RANGE, 0.44f); assertEquals(p1, p2); p1 = new CategoryLabelPosition(RectangleAnchor.TOP, TextBlockAnchor.CENTER, TextAnchor.CENTER, Math.PI / 6.0, CategoryLabelWidthType.RANGE, 0.44f); assertNotEquals(p1, p2); p2 = new CategoryLabelPosition(RectangleAnchor.TOP, TextBlockAnchor.CENTER, TextAnchor.CENTER, Math.PI / 6.0, CategoryLabelWidthType.RANGE, 0.44f); assertEquals(p1, p2); p1 = new CategoryLabelPosition(RectangleAnchor.TOP, TextBlockAnchor.CENTER, TextAnchor.CENTER, Math.PI / 6.0, CategoryLabelWidthType.CATEGORY, 0.44f); assertNotEquals(p1, p2); p2 = new CategoryLabelPosition(RectangleAnchor.TOP, TextBlockAnchor.CENTER, TextAnchor.CENTER, Math.PI / 6.0, CategoryLabelWidthType.CATEGORY, 0.44f); assertEquals(p1, p2); p1 = new CategoryLabelPosition(RectangleAnchor.TOP, TextBlockAnchor.CENTER, TextAnchor.CENTER, Math.PI / 6.0, CategoryLabelWidthType.CATEGORY, 0.55f); assertNotEquals(p1, p2); p2 = new CategoryLabelPosition(RectangleAnchor.TOP, TextBlockAnchor.CENTER, TextAnchor.CENTER, Math.PI / 6.0, CategoryLabelWidthType.CATEGORY, 0.55f); assertEquals(p1, p2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { CategoryLabelPosition a1 = new CategoryLabelPosition(); CategoryLabelPosition a2 = new CategoryLabelPosition(); assertEquals(a1, a2); int h1 = a1.hashCode(); int h2 = a2.hashCode(); assertEquals(h1, h2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { CategoryLabelPosition p1 = new CategoryLabelPosition(); CategoryLabelPosition p2 = TestUtils.serialised(p1); assertEquals(p1, p2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/axis/CategoryLabelPositionsTest.java000066400000000000000000000200771463604235500322130ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------- * CategoryLabelPositionsTest.java * ------------------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.axis; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.chart.text.TextBlockAnchor; import org.jfree.chart.ui.RectangleAnchor; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link CategoryLabelPositions} class. */ public class CategoryLabelPositionsTest { private static final RectangleAnchor RA_TOP = RectangleAnchor.TOP; private static final RectangleAnchor RA_BOTTOM = RectangleAnchor.BOTTOM; /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(CategoryLabelPositions.class) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Check that the equals method distinguishes all fields. */ @Test public void testEquals() { CategoryLabelPositions p1 = new CategoryLabelPositions( new CategoryLabelPosition(RA_TOP, TextBlockAnchor.CENTER), new CategoryLabelPosition(RA_TOP, TextBlockAnchor.CENTER), new CategoryLabelPosition(RA_TOP, TextBlockAnchor.CENTER), new CategoryLabelPosition(RA_TOP, TextBlockAnchor.CENTER)); CategoryLabelPositions p2 = new CategoryLabelPositions( new CategoryLabelPosition(RA_TOP, TextBlockAnchor.CENTER), new CategoryLabelPosition(RA_TOP, TextBlockAnchor.CENTER), new CategoryLabelPosition(RA_TOP, TextBlockAnchor.CENTER), new CategoryLabelPosition(RA_TOP, TextBlockAnchor.CENTER)); assertEquals(p1, p2); p1 = new CategoryLabelPositions( new CategoryLabelPosition(RA_BOTTOM, TextBlockAnchor.TOP_CENTER), new CategoryLabelPosition(RA_TOP, TextBlockAnchor.CENTER), new CategoryLabelPosition(RA_TOP, TextBlockAnchor.CENTER), new CategoryLabelPosition(RA_TOP, TextBlockAnchor.CENTER)); assertNotEquals(p1, p2); p2 = new CategoryLabelPositions( new CategoryLabelPosition(RA_BOTTOM, TextBlockAnchor.TOP_CENTER), new CategoryLabelPosition(RA_TOP, TextBlockAnchor.CENTER), new CategoryLabelPosition(RA_TOP, TextBlockAnchor.CENTER), new CategoryLabelPosition(RA_TOP, TextBlockAnchor.CENTER)); assertEquals(p1, p2); p1 = new CategoryLabelPositions( new CategoryLabelPosition(RA_BOTTOM, TextBlockAnchor.TOP_CENTER), new CategoryLabelPosition(RA_BOTTOM, TextBlockAnchor.TOP_CENTER), new CategoryLabelPosition(RA_TOP, TextBlockAnchor.CENTER), new CategoryLabelPosition(RA_TOP, TextBlockAnchor.CENTER)); assertNotEquals(p1, p2); p2 = new CategoryLabelPositions( new CategoryLabelPosition(RA_BOTTOM, TextBlockAnchor.TOP_CENTER), new CategoryLabelPosition(RA_BOTTOM, TextBlockAnchor.TOP_CENTER), new CategoryLabelPosition(RA_TOP, TextBlockAnchor.CENTER), new CategoryLabelPosition(RA_TOP, TextBlockAnchor.CENTER)); assertEquals(p1, p2); p1 = new CategoryLabelPositions( new CategoryLabelPosition(RA_BOTTOM, TextBlockAnchor.TOP_CENTER), new CategoryLabelPosition(RA_BOTTOM, TextBlockAnchor.TOP_CENTER), new CategoryLabelPosition(RA_BOTTOM, TextBlockAnchor.TOP_CENTER), new CategoryLabelPosition(RA_TOP, TextBlockAnchor.CENTER)); assertNotEquals(p1, p2); p2 = new CategoryLabelPositions( new CategoryLabelPosition(RA_BOTTOM, TextBlockAnchor.TOP_CENTER), new CategoryLabelPosition(RA_BOTTOM, TextBlockAnchor.TOP_CENTER), new CategoryLabelPosition(RA_BOTTOM, TextBlockAnchor.TOP_CENTER), new CategoryLabelPosition(RA_TOP, TextBlockAnchor.CENTER)); assertEquals(p1, p2); p1 = new CategoryLabelPositions( new CategoryLabelPosition(RA_BOTTOM, TextBlockAnchor.TOP_CENTER), new CategoryLabelPosition(RA_BOTTOM, TextBlockAnchor.TOP_CENTER), new CategoryLabelPosition(RA_BOTTOM, TextBlockAnchor.TOP_CENTER), new CategoryLabelPosition(RA_BOTTOM, TextBlockAnchor.TOP_CENTER)); assertNotEquals(p1, p2); p2 = new CategoryLabelPositions( new CategoryLabelPosition(RA_BOTTOM, TextBlockAnchor.TOP_CENTER), new CategoryLabelPosition(RA_BOTTOM, TextBlockAnchor.TOP_CENTER), new CategoryLabelPosition(RA_BOTTOM, TextBlockAnchor.TOP_CENTER), new CategoryLabelPosition(RA_BOTTOM, TextBlockAnchor.TOP_CENTER)); assertEquals(p1, p2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { CategoryLabelPositions p1 = new CategoryLabelPositions( new CategoryLabelPosition(RA_TOP, TextBlockAnchor.CENTER), new CategoryLabelPosition(RA_TOP, TextBlockAnchor.CENTER), new CategoryLabelPosition(RA_TOP, TextBlockAnchor.CENTER), new CategoryLabelPosition(RA_TOP, TextBlockAnchor.CENTER)); CategoryLabelPositions p2 = new CategoryLabelPositions( new CategoryLabelPosition(RA_TOP, TextBlockAnchor.CENTER), new CategoryLabelPosition(RA_TOP, TextBlockAnchor.CENTER), new CategoryLabelPosition(RA_TOP, TextBlockAnchor.CENTER), new CategoryLabelPosition(RA_TOP, TextBlockAnchor.CENTER)); assertEquals(p1, p2); int h1 = p1.hashCode(); int h2 = p2.hashCode(); assertEquals(h1, h2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { CategoryLabelPositions p1 = CategoryLabelPositions.STANDARD; CategoryLabelPositions p2 = TestUtils.serialised(p1); assertEquals(p1, p2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/axis/CategoryLabelWidthTypeTest.java000066400000000000000000000053401463604235500321410ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------- * CategoryLabelWidthTypeTest.java * ------------------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link CategoryLabelWidthType} class. */ public class CategoryLabelWidthTypeTest { /** * Confirm that the equals() method distinguishes the known values. */ @Test public void testEquals() { assertEquals(CategoryLabelWidthType.CATEGORY, CategoryLabelWidthType.CATEGORY); assertEquals(CategoryLabelWidthType.RANGE, CategoryLabelWidthType.RANGE); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { CategoryLabelWidthType a1 = CategoryLabelWidthType.CATEGORY; CategoryLabelWidthType a2 = CategoryLabelWidthType.CATEGORY; assertEquals(a1, a2); int h1 = a1.hashCode(); int h2 = a2.hashCode(); assertEquals(h1, h2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { CategoryLabelWidthType w1 = CategoryLabelWidthType.RANGE; CategoryLabelWidthType w2 = TestUtils.serialised(w1); assertEquals(w1, w2); assertSame(w1, w2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/axis/CategoryTickTest.java000066400000000000000000000125671463604235500301630ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * CategoryTickTest.java * --------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.axis; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.chart.text.TextBlock; import org.jfree.chart.text.TextBlockAnchor; import org.jfree.chart.text.TextLine; import org.jfree.chart.ui.TextAnchor; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link CategoryTick} class. */ public class CategoryTickTest { /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(CategoryTick.class) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .withRedefinedSuperclass() .verify(); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { Comparable c1 = "C1"; Comparable c2 = "C2"; TextBlock tb1 = new TextBlock(); tb1.addLine(new TextLine("Block 1")); TextBlock tb2 = new TextBlock(); tb1.addLine(new TextLine("Block 2")); TextBlockAnchor tba1 = TextBlockAnchor.CENTER; TextBlockAnchor tba2 = TextBlockAnchor.BOTTOM_CENTER; TextAnchor ta1 = TextAnchor.CENTER; TextAnchor ta2 = TextAnchor.BASELINE_LEFT; CategoryTick t1 = new CategoryTick(c1, tb1, tba1, ta1, 1.0f); CategoryTick t2 = new CategoryTick(c1, tb1, tba1, ta1, 1.0f); assertEquals(t1, t2); t1 = new CategoryTick(c2, tb1, tba1, ta1, 1.0f); assertNotEquals(t1, t2); t2 = new CategoryTick(c2, tb1, tba1, ta1, 1.0f); assertEquals(t1, t2); t1 = new CategoryTick(c2, tb2, tba1, ta1, 1.0f); assertNotEquals(t1, t2); t2 = new CategoryTick(c2, tb2, tba1, ta1, 1.0f); assertEquals(t1, t2); t1 = new CategoryTick(c2, tb2, tba2, ta1, 1.0f); assertNotEquals(t1, t2); t2 = new CategoryTick(c2, tb2, tba2, ta1, 1.0f); assertEquals(t1, t2); t1 = new CategoryTick(c2, tb2, tba2, ta2, 1.0f); assertNotEquals(t1, t2); t2 = new CategoryTick(c2, tb2, tba2, ta2, 1.0f); assertEquals(t1, t2); t1 = new CategoryTick(c2, tb2, tba2, ta2, 2.0f); assertNotEquals(t1, t2); t2 = new CategoryTick(c2, tb2, tba2, ta2, 2.0f); assertEquals(t1, t2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { Comparable c1 = "C1"; TextBlock tb1 = new TextBlock(); tb1.addLine(new TextLine("Block 1")); tb1.addLine(new TextLine("Block 2")); TextBlockAnchor tba1 = TextBlockAnchor.CENTER; TextAnchor ta1 = TextAnchor.CENTER; CategoryTick t1 = new CategoryTick(c1, tb1, tba1, ta1, 1.0f); CategoryTick t2 = new CategoryTick(c1, tb1, tba1, ta1, 1.0f); assertEquals(t1, t2); int h1 = t1.hashCode(); int h2 = t2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { CategoryTick t1 = new CategoryTick("C1", new TextBlock(), TextBlockAnchor.CENTER, TextAnchor.CENTER, 1.5f); CategoryTick t2 = (CategoryTick) t1.clone(); assertNotSame(t1, t2); assertSame(t1.getClass(), t2.getClass()); assertEquals(t1, t2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { CategoryTick t1 = new CategoryTick("C1", new TextBlock(), TextBlockAnchor.CENTER, TextAnchor.CENTER, 1.5f); CategoryTick t2 = TestUtils.serialised(t1); assertEquals(t1, t2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/axis/CyclicNumberAxisTest.java000066400000000000000000000105121463604235500307630ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * CyclicAxisTest.java * ------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: Nicolas Brodu * Contributor(s): -; * */ package org.jfree.chart.axis; import java.awt.BasicStroke; import java.awt.Color; import java.awt.GradientPaint; import java.awt.Stroke; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link CyclicNumberAxis} class. */ public class CyclicNumberAxisTest { /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { CyclicNumberAxis a1 = new CyclicNumberAxis(10, 0, "Test"); CyclicNumberAxis a2 = (CyclicNumberAxis) a1.clone(); assertNotSame(a1, a2); assertSame(a1.getClass(), a2.getClass()); assertEquals(a1, a2); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { CyclicNumberAxis a1 = new CyclicNumberAxis(10, 0, "Test"); CyclicNumberAxis a2 = new CyclicNumberAxis(10, 0, "Test"); assertEquals(a1, a2); // period a1.setPeriod(5); assertNotEquals(a1, a2); a2.setPeriod(5); assertEquals(a1, a2); // offset a1.setOffset(2.0); assertNotEquals(a1, a2); a2.setOffset(2.0); assertEquals(a1, a2); // advance line Paint a1.setAdvanceLinePaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLACK)); assertNotEquals(a1, a2); a2.setAdvanceLinePaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLACK)); assertEquals(a1, a2); // advance line Stroke Stroke stroke = new BasicStroke(0.2f); a1.setAdvanceLineStroke(stroke); assertNotEquals(a1, a2); a2.setAdvanceLineStroke(stroke); assertEquals(a1, a2); // advance line Visible a1.setAdvanceLineVisible(!a1.isAdvanceLineVisible()); assertNotEquals(a1, a2); a2.setAdvanceLineVisible(a1.isAdvanceLineVisible()); assertEquals(a1, a2); // cycle bound mapping a1.setBoundMappedToLastCycle(!a1.isBoundMappedToLastCycle()); assertNotEquals(a1, a2); a2.setBoundMappedToLastCycle(a1.isBoundMappedToLastCycle()); assertEquals(a1, a2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { CyclicNumberAxis a1 = new CyclicNumberAxis(10, 0, "Test"); CyclicNumberAxis a2 = new CyclicNumberAxis(10, 0, "Test"); assertEquals(a1, a2); int h1 = a1.hashCode(); int h2 = a2.hashCode(); assertEquals(h1, h2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { CyclicNumberAxis a1 = new CyclicNumberAxis(10, 0, "Test Axis"); CyclicNumberAxis a2 = TestUtils.serialised(a1); assertEquals(a1, a2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/axis/DateAxisTest.java000066400000000000000000001330561463604235500272720ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * DateAxisTest.java * ----------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import org.junit.jupiter.api.Test; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.List; import java.util.Locale; import java.util.TimeZone; import org.jfree.chart.TestUtils; import org.jfree.chart.ui.RectangleEdge; import org.jfree.data.time.DateRange; import org.jfree.data.time.Day; import org.jfree.data.time.Hour; import org.jfree.data.time.Millisecond; import org.jfree.data.time.Minute; import org.jfree.data.time.Month; import org.jfree.data.time.Second; import org.jfree.data.time.Year; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link DateAxis} class. */ public class DateAxisTest { static class MyDateAxis extends DateAxis { /** * Creates a new instance. * * @param label the label. */ public MyDateAxis(String label) { super(label); } @Override public Date previousStandardDate(Date d, DateTickUnit unit) { return super.previousStandardDate(d, unit); } } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { DateAxis a1 = new DateAxis("Test"); DateAxis a2 = new DateAxis("Test"); assertEquals(a1, a2); assertNotEquals(null, a1); assertNotEquals("Some non-DateAxis object", a1); a1 = new DateAxis("Test", TimeZone.getTimeZone("PST"), Locale.US); assertNotEquals(a1, a2); a2 = new DateAxis("Test", TimeZone.getTimeZone("PST"), Locale.US); assertEquals(a1, a2); a1 = new DateAxis("Test", TimeZone.getTimeZone("PST"), Locale.FRANCE); assertNotEquals(a1, a2); a2 = new DateAxis("Test", TimeZone.getTimeZone("PST"), Locale.FRANCE); assertEquals(a1, a2); // tickUnit a1.setTickUnit(new DateTickUnit(DateTickUnitType.DAY, 7)); assertNotEquals(a1, a2); a2.setTickUnit(new DateTickUnit(DateTickUnitType.DAY, 7)); assertEquals(a1, a2); // dateFormatOverride a1.setDateFormatOverride(new SimpleDateFormat("yyyy")); assertNotEquals(a1, a2); a2.setDateFormatOverride(new SimpleDateFormat("yyyy")); assertEquals(a1, a2); // tickMarkPosition a1.setTickMarkPosition(DateTickMarkPosition.END); assertNotEquals(a1, a2); a2.setTickMarkPosition(DateTickMarkPosition.END); assertEquals(a1, a2); } /** * A test for bug report 1472942. The DateFormat.equals() method is not * checking the range attribute. */ @Test public void test1472942() { DateAxis a1 = new DateAxis("Test"); DateAxis a2 = new DateAxis("Test"); assertEquals(a1, a2); // range a1.setRange(new Date(1L), new Date(2L)); assertNotEquals(a1, a2); a2.setRange(new Date(1L), new Date(2L)); assertEquals(a1, a2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { DateAxis a1 = new DateAxis("Test"); DateAxis a2 = new DateAxis("Test"); assertEquals(a1, a2); int h1 = a1.hashCode(); int h2 = a2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { DateAxis a1 = new DateAxis("Test"); DateAxis a2 = (DateAxis) a1.clone(); assertNotSame(a1, a2); assertSame(a1.getClass(), a2.getClass()); assertEquals(a1, a2); } /** * Test that the setRange() method works. */ @Test public void testSetRange() { DateAxis axis = new DateAxis("Test Axis"); Calendar calendar = Calendar.getInstance(); calendar.set(1999, Calendar.JANUARY, 3); Date d1 = calendar.getTime(); calendar.set(1999, Calendar.JANUARY, 31); Date d2 = calendar.getTime(); axis.setRange(d1, d2); DateRange range = (DateRange) axis.getRange(); assertEquals(d1, range.getLowerDate()); assertEquals(d2, range.getUpperDate()); } /** * Test that the setMaximumDate() method works. */ @Test public void testSetMaximumDate() { DateAxis axis = new DateAxis("Test Axis"); Date date = new Date(); axis.setMaximumDate(date); assertEquals(date, axis.getMaximumDate()); // check that setting the max date to something on or before the // current min date works... Date d1 = new Date(); Date d2 = new Date(d1.getTime() + 1); Date d0 = new Date(d1.getTime() - 1); axis.setMaximumDate(d2); axis.setMinimumDate(d1); axis.setMaximumDate(d1); assertEquals(d0, axis.getMinimumDate()); } /** * Test that the setMinimumDate() method works. */ @Test public void testSetMinimumDate() { DateAxis axis = new DateAxis("Test Axis"); Date d1 = new Date(); Date d2 = new Date(d1.getTime() + 1); axis.setMaximumDate(d2); axis.setMinimumDate(d1); assertEquals(d1, axis.getMinimumDate()); // check that setting the min date to something on or after the // current min date works... Date d3 = new Date(d2.getTime() + 1); axis.setMinimumDate(d2); assertEquals(d3, axis.getMaximumDate()); } /** * Tests two doubles for 'near enough' equality. * * @param d1 number 1. * @param d2 number 2. * @param tolerance maximum tolerance. * * @return A boolean. */ private boolean same(double d1, double d2, double tolerance) { return (Math.abs(d1 - d2) < tolerance); } /** * Test the translation of Java2D values to data values. */ @Test public void testJava2DToValue() { DateAxis axis = new DateAxis(); axis.setRange(50.0, 100.0); Rectangle2D dataArea = new Rectangle2D.Double(10.0, 50.0, 400.0, 300.0); double y1 = axis.java2DToValue(75.0, dataArea, RectangleEdge.LEFT); assertTrue(same(y1, 95.8333333, 1.0)); double y2 = axis.java2DToValue(75.0, dataArea, RectangleEdge.RIGHT); assertTrue(same(y2, 95.8333333, 1.0)); double x1 = axis.java2DToValue(75.0, dataArea, RectangleEdge.TOP); assertTrue(same(x1, 58.125, 1.0)); double x2 = axis.java2DToValue(75.0, dataArea, RectangleEdge.BOTTOM); assertTrue(same(x2, 58.125, 1.0)); axis.setInverted(true); double y3 = axis.java2DToValue(75.0, dataArea, RectangleEdge.LEFT); assertTrue(same(y3, 54.1666667, 1.0)); double y4 = axis.java2DToValue(75.0, dataArea, RectangleEdge.RIGHT); assertTrue(same(y4, 54.1666667, 1.0)); double x3 = axis.java2DToValue(75.0, dataArea, RectangleEdge.TOP); assertTrue(same(x3, 91.875, 1.0)); double x4 = axis.java2DToValue(75.0, dataArea, RectangleEdge.BOTTOM); assertTrue(same(x4, 91.875, 1.0)); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DateAxis a1 = new DateAxis("Test Axis"); DateAxis a2 = TestUtils.serialised(a1); assertEquals(a1, a2); } /** * A basic check for the testPreviousStandardDate() method when the * tick unit is 1 year. */ @Test public void testPreviousStandardDateYearA() { MyDateAxis axis = new MyDateAxis("Year"); Year y2006 = new Year(2006); Year y2007 = new Year(2007); // five dates to check... Date d0 = new Date(y2006.getFirstMillisecond()); Date d1 = new Date(y2006.getFirstMillisecond() + 500L); Date d2 = new Date(y2006.getMiddleMillisecond()); Date d3 = new Date(y2006.getMiddleMillisecond() + 500L); Date d4 = new Date(y2006.getLastMillisecond()); Date end = new Date(y2007.getLastMillisecond()); DateTickUnit unit = new DateTickUnit(DateTickUnitType.YEAR, 1); axis.setTickUnit(unit); // START: check d0 and d1 axis.setTickMarkPosition(DateTickMarkPosition.START); axis.setRange(d0, end); Date psd = axis.previousStandardDate(d0, unit); Date nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d0.getTime()); assertTrue(nsd.getTime() >= d0.getTime()); axis.setRange(d1, end); psd = axis.previousStandardDate(d1, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d1.getTime()); assertTrue(nsd.getTime() >= d1.getTime()); // MIDDLE: check d1, d2 and d3 axis.setTickMarkPosition(DateTickMarkPosition.MIDDLE); axis.setRange(d1, end); psd = axis.previousStandardDate(d1, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d1.getTime()); assertTrue(nsd.getTime() >= d1.getTime()); axis.setRange(d2, end); psd = axis.previousStandardDate(d2, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d2.getTime()); assertTrue(nsd.getTime() >= d2.getTime()); axis.setRange(d3, end); psd = axis.previousStandardDate(d3, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d3.getTime()); assertTrue(nsd.getTime() >= d3.getTime()); // END: check d3 and d4 axis.setTickMarkPosition(DateTickMarkPosition.END); axis.setRange(d3, end); psd = axis.previousStandardDate(d3, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d3.getTime()); assertTrue(nsd.getTime() >= d3.getTime()); axis.setRange(d4, end); psd = axis.previousStandardDate(d4, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d4.getTime()); assertTrue(nsd.getTime() >= d4.getTime()); } /** * A basic check for the testPreviousStandardDate() method when the * tick unit is 10 years (just for the sake of having a multiple). */ @Test public void testPreviousStandardDateYearB() { MyDateAxis axis = new MyDateAxis("Year"); Year y2006 = new Year(2006); Year y2007 = new Year(2007); // five dates to check... Date d0 = new Date(y2006.getFirstMillisecond()); Date d1 = new Date(y2006.getFirstMillisecond() + 500L); Date d2 = new Date(y2006.getMiddleMillisecond()); Date d3 = new Date(y2006.getMiddleMillisecond() + 500L); Date d4 = new Date(y2006.getLastMillisecond()); Date end = new Date(y2007.getLastMillisecond()); DateTickUnit unit = new DateTickUnit(DateTickUnitType.YEAR, 10); axis.setTickUnit(unit); // START: check d0 and d1 axis.setTickMarkPosition(DateTickMarkPosition.START); axis.setRange(d0, end); Date psd = axis.previousStandardDate(d0, unit); Date nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d0.getTime()); assertTrue(nsd.getTime() >= d0.getTime()); axis.setRange(d1, end); psd = axis.previousStandardDate(d1, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d1.getTime()); assertTrue(nsd.getTime() >= d1.getTime()); // MIDDLE: check d1, d2 and d3 axis.setTickMarkPosition(DateTickMarkPosition.MIDDLE); axis.setRange(d1, end); psd = axis.previousStandardDate(d1, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d1.getTime()); assertTrue(nsd.getTime() >= d1.getTime()); axis.setRange(d2, end); psd = axis.previousStandardDate(d2, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d2.getTime()); assertTrue(nsd.getTime() >= d2.getTime()); axis.setRange(d3, end); psd = axis.previousStandardDate(d3, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d3.getTime()); assertTrue(nsd.getTime() >= d3.getTime()); // END: check d3 and d4 axis.setTickMarkPosition(DateTickMarkPosition.END); axis.setRange(d3, end); psd = axis.previousStandardDate(d3, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d3.getTime()); assertTrue(nsd.getTime() >= d3.getTime()); axis.setRange(d4, end); psd = axis.previousStandardDate(d4, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d4.getTime()); assertTrue(nsd.getTime() >= d4.getTime()); } /** * A basic check for the testPreviousStandardDate() method when the * tick unit is 1 month. */ @Test public void testPreviousStandardDateMonthA() { MyDateAxis axis = new MyDateAxis("Month"); Month nov2006 = new Month(11, 2006); Month dec2006 = new Month(12, 2006); // five dates to check... Date d0 = new Date(nov2006.getFirstMillisecond()); Date d1 = new Date(nov2006.getFirstMillisecond() + 500L); Date d2 = new Date(nov2006.getMiddleMillisecond()); Date d3 = new Date(nov2006.getMiddleMillisecond() + 500L); Date d4 = new Date(nov2006.getLastMillisecond()); Date end = new Date(dec2006.getLastMillisecond()); DateTickUnit unit = new DateTickUnit(DateTickUnitType.MONTH, 1); axis.setTickUnit(unit); // START: check d0 and d1 axis.setTickMarkPosition(DateTickMarkPosition.START); axis.setRange(d0, end); Date psd = axis.previousStandardDate(d0, unit); Date nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d0.getTime()); assertTrue(nsd.getTime() >= d0.getTime()); axis.setRange(d1, end); psd = axis.previousStandardDate(d1, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d1.getTime()); assertTrue(nsd.getTime() >= d1.getTime()); // MIDDLE: check d1, d2 and d3 axis.setTickMarkPosition(DateTickMarkPosition.MIDDLE); axis.setRange(d1, end); psd = axis.previousStandardDate(d1, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d1.getTime()); assertTrue(nsd.getTime() >= d1.getTime()); axis.setRange(d2, end); psd = axis.previousStandardDate(d2, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d2.getTime()); assertTrue(nsd.getTime() >= d2.getTime()); axis.setRange(d3, end); psd = axis.previousStandardDate(d3, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d3.getTime()); assertTrue(nsd.getTime() >= d3.getTime()); // END: check d3 and d4 axis.setTickMarkPosition(DateTickMarkPosition.END); axis.setRange(d3, end); psd = axis.previousStandardDate(d3, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d3.getTime()); assertTrue(nsd.getTime() >= d3.getTime()); axis.setRange(d4, end); psd = axis.previousStandardDate(d4, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d4.getTime()); assertTrue(nsd.getTime() >= d4.getTime()); } /** * A basic check for the testPreviousStandardDate() method when the * tick unit is 3 months (just for the sake of having a multiple). */ @Test public void testPreviousStandardDateMonthB() { MyDateAxis axis = new MyDateAxis("Month"); Month nov2006 = new Month(11, 2006); Month dec2006 = new Month(12, 2006); // five dates to check... Date d0 = new Date(nov2006.getFirstMillisecond()); Date d1 = new Date(nov2006.getFirstMillisecond() + 500L); Date d2 = new Date(nov2006.getMiddleMillisecond()); Date d3 = new Date(nov2006.getMiddleMillisecond() + 500L); Date d4 = new Date(nov2006.getLastMillisecond()); Date end = new Date(dec2006.getLastMillisecond()); DateTickUnit unit = new DateTickUnit(DateTickUnitType.MONTH, 3); axis.setTickUnit(unit); // START: check d0 and d1 axis.setTickMarkPosition(DateTickMarkPosition.START); axis.setRange(d0, end); Date psd = axis.previousStandardDate(d0, unit); Date nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d0.getTime()); assertTrue(nsd.getTime() >= d0.getTime()); axis.setRange(d1, end); psd = axis.previousStandardDate(d1, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d1.getTime()); assertTrue(nsd.getTime() >= d1.getTime()); // MIDDLE: check d1, d2 and d3 axis.setTickMarkPosition(DateTickMarkPosition.MIDDLE); axis.setRange(d1, end); psd = axis.previousStandardDate(d1, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d1.getTime()); assertTrue(nsd.getTime() >= d1.getTime()); axis.setRange(d2, end); psd = axis.previousStandardDate(d2, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d2.getTime()); assertTrue(nsd.getTime() >= d2.getTime()); axis.setRange(d3, end); psd = axis.previousStandardDate(d3, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d3.getTime()); assertTrue(nsd.getTime() >= d3.getTime()); // END: check d3 and d4 axis.setTickMarkPosition(DateTickMarkPosition.END); axis.setRange(d3, end); psd = axis.previousStandardDate(d3, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d3.getTime()); assertTrue(nsd.getTime() >= d3.getTime()); axis.setRange(d4, end); psd = axis.previousStandardDate(d4, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d4.getTime()); assertTrue(nsd.getTime() >= d4.getTime()); } /** * A basic check for the testPreviousStandardDate() method when the * tick unit is 1 day. */ @Test public void testPreviousStandardDateDayA() { MyDateAxis axis = new MyDateAxis("Day"); Day apr12007 = new Day(1, 4, 2007); Day apr22007 = new Day(2, 4, 2007); // five dates to check... Date d0 = new Date(apr12007.getFirstMillisecond()); Date d1 = new Date(apr12007.getFirstMillisecond() + 500L); Date d2 = new Date(apr12007.getMiddleMillisecond()); Date d3 = new Date(apr12007.getMiddleMillisecond() + 500L); Date d4 = new Date(apr12007.getLastMillisecond()); Date end = new Date(apr22007.getLastMillisecond()); DateTickUnit unit = new DateTickUnit(DateTickUnitType.DAY, 1); axis.setTickUnit(unit); // START: check d0 and d1 axis.setTickMarkPosition(DateTickMarkPosition.START); axis.setRange(d0, end); Date psd = axis.previousStandardDate(d0, unit); Date nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d0.getTime()); assertTrue(nsd.getTime() >= d0.getTime()); axis.setRange(d1, end); psd = axis.previousStandardDate(d1, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d1.getTime()); assertTrue(nsd.getTime() >= d1.getTime()); // MIDDLE: check d1, d2 and d3 axis.setTickMarkPosition(DateTickMarkPosition.MIDDLE); axis.setRange(d1, end); psd = axis.previousStandardDate(d1, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d1.getTime()); assertTrue(nsd.getTime() >= d1.getTime()); axis.setRange(d2, end); psd = axis.previousStandardDate(d2, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d2.getTime()); assertTrue(nsd.getTime() >= d2.getTime()); axis.setRange(d3, end); psd = axis.previousStandardDate(d3, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d3.getTime()); assertTrue(nsd.getTime() >= d3.getTime()); // END: check d3 and d4 axis.setTickMarkPosition(DateTickMarkPosition.END); axis.setRange(d3, end); psd = axis.previousStandardDate(d3, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d3.getTime()); assertTrue(nsd.getTime() >= d3.getTime()); axis.setRange(d4, end); psd = axis.previousStandardDate(d4, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d4.getTime()); assertTrue(nsd.getTime() >= d4.getTime()); } /** * A basic check for the testPreviousStandardDate() method when the * tick unit is 7 days (just for the sake of having a multiple). */ @Test public void testPreviousStandardDateDayB() { MyDateAxis axis = new MyDateAxis("Day"); Day apr12007 = new Day(1, 4, 2007); Day apr22007 = new Day(2, 4, 2007); // five dates to check... Date d0 = new Date(apr12007.getFirstMillisecond()); Date d1 = new Date(apr12007.getFirstMillisecond() + 500L); Date d2 = new Date(apr12007.getMiddleMillisecond()); Date d3 = new Date(apr12007.getMiddleMillisecond() + 500L); Date d4 = new Date(apr12007.getLastMillisecond()); Date end = new Date(apr22007.getLastMillisecond()); DateTickUnit unit = new DateTickUnit(DateTickUnitType.DAY, 7); axis.setTickUnit(unit); // START: check d0 and d1 axis.setTickMarkPosition(DateTickMarkPosition.START); axis.setRange(d0, end); Date psd = axis.previousStandardDate(d0, unit); Date nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d0.getTime()); assertTrue(nsd.getTime() >= d0.getTime()); axis.setRange(d1, end); psd = axis.previousStandardDate(d1, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d1.getTime()); assertTrue(nsd.getTime() >= d1.getTime()); // MIDDLE: check d1, d2 and d3 axis.setTickMarkPosition(DateTickMarkPosition.MIDDLE); axis.setRange(d1, end); psd = axis.previousStandardDate(d1, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d1.getTime()); assertTrue(nsd.getTime() >= d1.getTime()); axis.setRange(d2, end); psd = axis.previousStandardDate(d2, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d2.getTime()); assertTrue(nsd.getTime() >= d2.getTime()); axis.setRange(d3, end); psd = axis.previousStandardDate(d3, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d3.getTime()); assertTrue(nsd.getTime() >= d3.getTime()); // END: check d3 and d4 axis.setTickMarkPosition(DateTickMarkPosition.END); axis.setRange(d3, end); psd = axis.previousStandardDate(d3, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d3.getTime()); assertTrue(nsd.getTime() >= d3.getTime()); axis.setRange(d4, end); psd = axis.previousStandardDate(d4, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d4.getTime()); assertTrue(nsd.getTime() >= d4.getTime()); } /** * A basic check for the testPreviousStandardDate() method when the * tick unit is 1 hour. */ @Test public void testPreviousStandardDateHourA() { MyDateAxis axis = new MyDateAxis("Hour"); Hour h0 = new Hour(12, 1, 4, 2007); Hour h1 = new Hour(13, 1, 4, 2007); // five dates to check... Date d0 = new Date(h0.getFirstMillisecond()); Date d1 = new Date(h0.getFirstMillisecond() + 500L); Date d2 = new Date(h0.getMiddleMillisecond()); Date d3 = new Date(h0.getMiddleMillisecond() + 500L); Date d4 = new Date(h0.getLastMillisecond()); Date end = new Date(h1.getLastMillisecond()); DateTickUnit unit = new DateTickUnit(DateTickUnitType.HOUR, 1); axis.setTickUnit(unit); // START: check d0 and d1 axis.setTickMarkPosition(DateTickMarkPosition.START); axis.setRange(d0, end); Date psd = axis.previousStandardDate(d0, unit); Date nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d0.getTime()); assertTrue(nsd.getTime() >= d0.getTime()); axis.setRange(d1, end); psd = axis.previousStandardDate(d1, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d1.getTime()); assertTrue(nsd.getTime() >= d1.getTime()); // MIDDLE: check d1, d2 and d3 axis.setTickMarkPosition(DateTickMarkPosition.MIDDLE); axis.setRange(d1, end); psd = axis.previousStandardDate(d1, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d1.getTime()); assertTrue(nsd.getTime() >= d1.getTime()); axis.setRange(d2, end); psd = axis.previousStandardDate(d2, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d2.getTime()); assertTrue(nsd.getTime() >= d2.getTime()); axis.setRange(d3, end); psd = axis.previousStandardDate(d3, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d3.getTime()); assertTrue(nsd.getTime() >= d3.getTime()); // END: check d3 and d4 axis.setTickMarkPosition(DateTickMarkPosition.END); axis.setRange(d3, end); psd = axis.previousStandardDate(d3, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d3.getTime()); assertTrue(nsd.getTime() >= d3.getTime()); axis.setRange(d4, end); psd = axis.previousStandardDate(d4, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d4.getTime()); assertTrue(nsd.getTime() >= d4.getTime()); } /** * A basic check for the testPreviousStandardDate() method when the * tick unit is 6 hours (just for the sake of having a multiple). */ @Test public void testPreviousStandardDateHourB() { MyDateAxis axis = new MyDateAxis("Hour"); Hour h0 = new Hour(12, 1, 4, 2007); Hour h1 = new Hour(13, 1, 4, 2007); // five dates to check... Date d0 = new Date(h0.getFirstMillisecond()); Date d1 = new Date(h0.getFirstMillisecond() + 500L); Date d2 = new Date(h0.getMiddleMillisecond()); Date d3 = new Date(h0.getMiddleMillisecond() + 500L); Date d4 = new Date(h0.getLastMillisecond()); Date end = new Date(h1.getLastMillisecond()); DateTickUnit unit = new DateTickUnit(DateTickUnitType.HOUR, 6); axis.setTickUnit(unit); // START: check d0 and d1 axis.setTickMarkPosition(DateTickMarkPosition.START); axis.setRange(d0, end); Date psd = axis.previousStandardDate(d0, unit); Date nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d0.getTime()); assertTrue(nsd.getTime() >= d0.getTime()); axis.setRange(d1, end); psd = axis.previousStandardDate(d1, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d1.getTime()); assertTrue(nsd.getTime() >= d1.getTime()); // MIDDLE: check d1, d2 and d3 axis.setTickMarkPosition(DateTickMarkPosition.MIDDLE); axis.setRange(d1, end); psd = axis.previousStandardDate(d1, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d1.getTime()); assertTrue(nsd.getTime() >= d1.getTime()); axis.setRange(d2, end); psd = axis.previousStandardDate(d2, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d2.getTime()); assertTrue(nsd.getTime() >= d2.getTime()); axis.setRange(d3, end); psd = axis.previousStandardDate(d3, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d3.getTime()); assertTrue(nsd.getTime() >= d3.getTime()); // END: check d3 and d4 axis.setTickMarkPosition(DateTickMarkPosition.END); axis.setRange(d3, end); psd = axis.previousStandardDate(d3, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d3.getTime()); assertTrue(nsd.getTime() >= d3.getTime()); axis.setRange(d4, end); psd = axis.previousStandardDate(d4, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d4.getTime()); assertTrue(nsd.getTime() >= d4.getTime()); } /** * A basic check for the testPreviousStandardDate() method when the * tick unit is 1 second. */ @Test public void testPreviousStandardDateSecondA() { MyDateAxis axis = new MyDateAxis("Second"); Second s0 = new Second(58, 31, 12, 1, 4, 2007); Second s1 = new Second(59, 31, 12, 1, 4, 2007); // five dates to check... Date d0 = new Date(s0.getFirstMillisecond()); Date d1 = new Date(s0.getFirstMillisecond() + 50L); Date d2 = new Date(s0.getMiddleMillisecond()); Date d3 = new Date(s0.getMiddleMillisecond() + 50L); Date d4 = new Date(s0.getLastMillisecond()); Date end = new Date(s1.getLastMillisecond()); DateTickUnit unit = new DateTickUnit(DateTickUnitType.SECOND, 1); axis.setTickUnit(unit); // START: check d0 and d1 axis.setTickMarkPosition(DateTickMarkPosition.START); axis.setRange(d0, end); Date psd = axis.previousStandardDate(d0, unit); Date nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d0.getTime()); assertTrue(nsd.getTime() >= d0.getTime()); axis.setRange(d1, end); psd = axis.previousStandardDate(d1, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d1.getTime()); assertTrue(nsd.getTime() >= d1.getTime()); // MIDDLE: check d1, d2 and d3 axis.setTickMarkPosition(DateTickMarkPosition.MIDDLE); axis.setRange(d1, end); psd = axis.previousStandardDate(d1, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d1.getTime()); assertTrue(nsd.getTime() >= d1.getTime()); axis.setRange(d2, end); psd = axis.previousStandardDate(d2, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d2.getTime()); assertTrue(nsd.getTime() >= d2.getTime()); axis.setRange(d3, end); psd = axis.previousStandardDate(d3, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d3.getTime()); assertTrue(nsd.getTime() >= d3.getTime()); // END: check d3 and d4 axis.setTickMarkPosition(DateTickMarkPosition.END); axis.setRange(d3, end); psd = axis.previousStandardDate(d3, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d3.getTime()); assertTrue(nsd.getTime() >= d3.getTime()); axis.setRange(d4, end); psd = axis.previousStandardDate(d4, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d4.getTime()); assertTrue(nsd.getTime() >= d4.getTime()); } /** * A basic check for the testPreviousStandardDate() method when the * tick unit is 5 seconds (just for the sake of having a multiple). */ @Test public void testPreviousStandardDateSecondB() { MyDateAxis axis = new MyDateAxis("Second"); Second s0 = new Second(58, 31, 12, 1, 4, 2007); Second s1 = new Second(59, 31, 12, 1, 4, 2007); // five dates to check... Date d0 = new Date(s0.getFirstMillisecond()); Date d1 = new Date(s0.getFirstMillisecond() + 50L); Date d2 = new Date(s0.getMiddleMillisecond()); Date d3 = new Date(s0.getMiddleMillisecond() + 50L); Date d4 = new Date(s0.getLastMillisecond()); Date end = new Date(s1.getLastMillisecond()); DateTickUnit unit = new DateTickUnit(DateTickUnitType.SECOND, 5); axis.setTickUnit(unit); // START: check d0 and d1 axis.setTickMarkPosition(DateTickMarkPosition.START); axis.setRange(d0, end); Date psd = axis.previousStandardDate(d0, unit); Date nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d0.getTime()); assertTrue(nsd.getTime() >= d0.getTime()); axis.setRange(d1, end); psd = axis.previousStandardDate(d1, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d1.getTime()); assertTrue(nsd.getTime() >= d1.getTime()); // MIDDLE: check d1, d2 and d3 axis.setTickMarkPosition(DateTickMarkPosition.MIDDLE); axis.setRange(d1, end); psd = axis.previousStandardDate(d1, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d1.getTime()); assertTrue(nsd.getTime() >= d1.getTime()); axis.setRange(d2, end); psd = axis.previousStandardDate(d2, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d2.getTime()); assertTrue(nsd.getTime() >= d2.getTime()); axis.setRange(d3, end); psd = axis.previousStandardDate(d3, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d3.getTime()); assertTrue(nsd.getTime() >= d3.getTime()); // END: check d3 and d4 axis.setTickMarkPosition(DateTickMarkPosition.END); axis.setRange(d3, end); psd = axis.previousStandardDate(d3, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d3.getTime()); assertTrue(nsd.getTime() >= d3.getTime()); axis.setRange(d4, end); psd = axis.previousStandardDate(d4, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d4.getTime()); assertTrue(nsd.getTime() >= d4.getTime()); } /** * A basic check for the testPreviousStandardDate() method when the * tick unit is 1 millisecond. */ @Test public void testPreviousStandardDateMillisecondA() { MyDateAxis axis = new MyDateAxis("Millisecond"); Millisecond m0 = new Millisecond(458, 58, 31, 12, 1, 4, 2007); Millisecond m1 = new Millisecond(459, 58, 31, 12, 1, 4, 2007); Date d0 = new Date(m0.getFirstMillisecond()); Date end = new Date(m1.getLastMillisecond()); DateTickUnit unit = new DateTickUnit(DateTickUnitType.MILLISECOND, 1); axis.setTickUnit(unit); // START: check d0 axis.setTickMarkPosition(DateTickMarkPosition.START); axis.setRange(d0, end); Date psd = axis.previousStandardDate(d0, unit); Date nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d0.getTime()); assertTrue(nsd.getTime() >= d0.getTime()); // MIDDLE: check d0 axis.setTickMarkPosition(DateTickMarkPosition.MIDDLE); axis.setRange(d0, end); psd = axis.previousStandardDate(d0, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d0.getTime()); assertTrue(nsd.getTime() >= d0.getTime()); // END: check d0 axis.setTickMarkPosition(DateTickMarkPosition.END); axis.setRange(d0, end); psd = axis.previousStandardDate(d0, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d0.getTime()); assertTrue(nsd.getTime() >= d0.getTime()); } /** * A basic check for the testPreviousStandardDate() method when the * tick unit is 10 milliseconds (just for the sake of having a multiple). */ @Test public void testPreviousStandardDateMillisecondB() { MyDateAxis axis = new MyDateAxis("Millisecond"); Millisecond m0 = new Millisecond(458, 58, 31, 12, 1, 4, 2007); Millisecond m1 = new Millisecond(459, 58, 31, 12, 1, 4, 2007); Date d0 = new Date(m0.getFirstMillisecond()); Date end = new Date(m1.getLastMillisecond()); DateTickUnit unit = new DateTickUnit(DateTickUnitType.MILLISECOND, 10); axis.setTickUnit(unit); // START: check d0 axis.setTickMarkPosition(DateTickMarkPosition.START); axis.setRange(d0, end); Date psd = axis.previousStandardDate(d0, unit); Date nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d0.getTime()); assertTrue(nsd.getTime() >= d0.getTime()); // MIDDLE: check d0 axis.setTickMarkPosition(DateTickMarkPosition.MIDDLE); axis.setRange(d0, end); psd = axis.previousStandardDate(d0, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d0.getTime()); assertTrue(nsd.getTime() >= d0.getTime()); // END: check d0 axis.setTickMarkPosition(DateTickMarkPosition.END); axis.setRange(d0, end); psd = axis.previousStandardDate(d0, unit); nsd = unit.addToDate(psd, TimeZone.getDefault()); assertTrue(psd.getTime() < d0.getTime()); assertTrue(nsd.getTime() >= d0.getTime()); } /** * A test to reproduce bug 2201869. */ @Test public void testBug2201869() { TimeZone tz = TimeZone.getTimeZone("GMT"); GregorianCalendar c = new GregorianCalendar(tz, Locale.UK); DateAxis axis = new DateAxis("Date", tz, Locale.UK); SimpleDateFormat sdf = new SimpleDateFormat("d-MMM-yyyy", Locale.UK); sdf.setCalendar(c); axis.setTickUnit(new DateTickUnit(DateTickUnitType.MONTH, 1, sdf)); Day d1 = new Day(1, 3, 2008); d1.peg(c); Day d2 = new Day(30, 6, 2008); d2.peg(c); axis.setRange(d1.getStart(), d2.getEnd()); BufferedImage image = new BufferedImage(200, 100, BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = image.createGraphics(); Rectangle2D area = new Rectangle2D.Double(0.0, 0.0, 200, 100); axis.setTickMarkPosition(DateTickMarkPosition.END); List ticks = axis.refreshTicks(g2, new AxisState(), area, RectangleEdge.BOTTOM); assertEquals(3, ticks.size()); DateTick t1 = (DateTick) ticks.get(0); assertEquals("31-Mar-2008", t1.getText()); DateTick t2 = (DateTick) ticks.get(1); assertEquals("30-Apr-2008", t2.getText()); DateTick t3 = (DateTick) ticks.get(2); assertEquals("31-May-2008", t3.getText()); // now repeat for a vertical axis ticks = axis.refreshTicks(g2, new AxisState(), area, RectangleEdge.LEFT); assertEquals(3, ticks.size()); t1 = (DateTick) ticks.get(0); assertEquals("31-Mar-2008", t1.getText()); t2 = (DateTick) ticks.get(1); assertEquals("30-Apr-2008", t2.getText()); t3 = (DateTick) ticks.get(2); assertEquals("31-May-2008", t3.getText()); } @Test public void testBug3484403() { final long[] dates = { 1304892000000L, 1304632800000L, 1304546400000L, 1304460000000L, 1304373600000L, 1304287200000L, 1320015600000L, 1309384800000L, 1319752800000L, 1319666400000L, 1319580000000L, 1319493600000L }; Arrays.sort(dates); DateAxis axis = new DateAxis("Date"); // set start and end date Date start = new Date(dates[0]); Date end = new Date(dates[dates.length-1]); axis.setMinimumDate(start); axis.setMaximumDate(end); BufferedImage image = new BufferedImage(200, 100, BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = image.createGraphics(); Rectangle2D area = new Rectangle2D.Double(0.0, 0.0, 500, 200); // if the bug is still present, this leads to an endless loop axis.refreshTicks(g2, new AxisState(), area, RectangleEdge.BOTTOM); } /** * Test for bug #25 at Github. * * https://github.com/jfree/jfreechart/issues/25 * */ @Test public void testBug25() { TimeZone tz = TimeZone.getTimeZone("GMT"); GregorianCalendar cal = new GregorianCalendar(tz, Locale.UK); MyDateAxis axis = new MyDateAxis("25"); axis.setTimeZone(tz); // YEAR DateTickUnit ydtu = new DateTickUnit(DateTickUnitType.YEAR, 5); Year y = new Year(2015); long ymillis = y.getFirstMillisecond(cal); // 1420070400000L Date yprev = axis.previousStandardDate(new Date(ymillis), ydtu); assertEquals(new Year(2010).getFirstMillisecond(cal), yprev.getTime()); // MONTH DateTickUnit mdtu = new DateTickUnit(DateTickUnitType.MONTH, 3); Month m = new Month(12, 2016); long mmillis = m.getFirstMillisecond(cal); Date mprev = axis.previousStandardDate(new Date(mmillis), mdtu); assertEquals(new Month(9, 2016).getFirstMillisecond(cal), mprev.getTime()); // DAY DateTickUnit ddtu = new DateTickUnit(DateTickUnitType.DAY, 7); Day d = new Day(14, 1, 2016); long dmillis = d.getFirstMillisecond(cal); Date dprev = axis.previousStandardDate(new Date(dmillis), ddtu); assertEquals(new Day(7, 1, 2016).getFirstMillisecond(cal), dprev.getTime()); // HOUR DateTickUnit hdtu = new DateTickUnit(DateTickUnitType.HOUR, 6); Hour h = new Hour(18, 24, 8, 2016); long hmillis = h.getFirstMillisecond(cal); Date hprev = axis.previousStandardDate(new Date(hmillis), hdtu); assertEquals(new Hour(12, 24, 8, 2016).getFirstMillisecond(cal), hprev.getTime()); // MINUTE DateTickUnit mindtu = new DateTickUnit(DateTickUnitType.MINUTE, 5); Minute min = new Minute(10, 12, 24, 8, 2016); long minmillis = min.getFirstMillisecond(cal); // 1472040600000L GMT Date minprev = axis.previousStandardDate(new Date(minmillis), mindtu); assertEquals(1472040600000L - 5 * 60 * 1000L, minprev.getTime()); // SECOND DateTickUnit sdtu = new DateTickUnit(DateTickUnitType.SECOND, 10); Second s = new Second(50, 30, 18, 24, 8, 2016); long smillis = s.getFirstMillisecond(cal); Date sprev = axis.previousStandardDate(new Date(smillis), sdtu); assertEquals(new Second(40, 30, 18, 24, 8, 2016) .getFirstMillisecond(cal), sprev.getTime()); // MILLISECOND DateTickUnit msdtu = new DateTickUnit(DateTickUnitType.MILLISECOND, 10); Millisecond ms = new Millisecond(500, 50, 30, 18, 24, 8, 2016); long msmillis = ms.getFirstMillisecond(cal); Date msprev = axis.previousStandardDate(new Date(msmillis), msdtu); assertEquals(new Millisecond(490, 50, 30, 18, 24, 8, 2016) .getFirstMillisecond(cal), msprev.getTime()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/axis/DateTickMarkPositionTest.java000066400000000000000000000056141463604235500316160ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------- * DateTickMarkPositionTest.java * ----------------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link DateTickMarkPosition} class. */ public class DateTickMarkPositionTest { /** * Test equals() method. */ @Test public void testEquals() { assertEquals(DateTickMarkPosition.START, DateTickMarkPosition.START); assertEquals(DateTickMarkPosition.MIDDLE, DateTickMarkPosition.MIDDLE); assertEquals(DateTickMarkPosition.END, DateTickMarkPosition.END); assertNotEquals(null, DateTickMarkPosition.START); assertNotEquals(DateTickMarkPosition.START, DateTickMarkPosition.END); assertNotEquals(DateTickMarkPosition.MIDDLE, DateTickMarkPosition.END); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { DateTickMarkPosition a1 = DateTickMarkPosition.END; DateTickMarkPosition a2 = DateTickMarkPosition.END; assertEquals(a1, a2); int h1 = a1.hashCode(); int h2 = a2.hashCode(); assertEquals(h1, h2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DateTickMarkPosition p1 = DateTickMarkPosition.MIDDLE; DateTickMarkPosition p2 = TestUtils.serialised(p1); assertEquals(p1, p2); assertSame(p1, p2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/axis/DateTickTest.java000066400000000000000000000111261463604235500272510ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * DateTickTest.java * ----------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.util.Date; import org.jfree.chart.TestUtils; import org.jfree.chart.ui.TextAnchor; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link DateTick} class. */ public class DateTickTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { Date d1 = new Date(0L); Date d2 = new Date(1L); String l1 = "Label 1"; String l2 = "Label 2"; TextAnchor ta1 = TextAnchor.CENTER; TextAnchor ta2 = TextAnchor.BASELINE_LEFT; DateTick t1 = new DateTick(d1, l1, ta1, ta1, Math.PI / 2.0); DateTick t2 = new DateTick(d1, l1, ta1, ta1, Math.PI / 2.0); assertEquals(t1, t2); t1 = new DateTick(d2, l1, ta1, ta1, Math.PI / 2.0); assertNotEquals(t1, t2); t2 = new DateTick(d2, l1, ta1, ta1, Math.PI / 2.0); assertEquals(t1, t2); t1 = new DateTick(d1, l2, ta1, ta1, Math.PI / 2.0); assertNotEquals(t1, t2); t2 = new DateTick(d1, l2, ta1, ta1, Math.PI / 2.0); assertEquals(t1, t2); t1 = new DateTick(d1, l1, ta2, ta1, Math.PI / 2.0); assertNotEquals(t1, t2); t2 = new DateTick(d1, l1, ta2, ta1, Math.PI / 2.0); assertEquals(t1, t2); t1 = new DateTick(d1, l1, ta1, ta2, Math.PI / 2.0); assertNotEquals(t1, t2); t2 = new DateTick(d1, l1, ta1, ta2, Math.PI / 2.0); assertEquals(t1, t2); t1 = new DateTick(d1, l1, ta1, ta1, Math.PI / 3.0); assertNotEquals(t1, t2); t2 = new DateTick(d1, l1, ta1, ta1, Math.PI / 3.0); assertEquals(t1, t2); t1 = new DateTick(TickType.MINOR, d1, l1, ta1, ta1, Math.PI); t2 = new DateTick(TickType.MAJOR, d1, l1, ta1, ta1, Math.PI); assertNotEquals(t1, t2); t2 = new DateTick(TickType.MINOR, d1, l1, ta1, ta1, Math.PI); assertEquals(t1, t2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { Date d1 = new Date(0L); String l1 = "Label 1"; TextAnchor ta1 = TextAnchor.CENTER; DateTick t1 = new DateTick(d1, l1, ta1, ta1, Math.PI / 2.0); DateTick t2 = new DateTick(d1, l1, ta1, ta1, Math.PI / 2.0); assertEquals(t1, t2); int h1 = t1.hashCode(); int h2 = t2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { DateTick t1 = new DateTick(new Date(0L), "Label", TextAnchor.CENTER, TextAnchor.CENTER, 10.0); DateTick t2 = (DateTick) t1.clone(); assertNotSame(t1, t2); assertSame(t1.getClass(), t2.getClass()); assertEquals(t1, t2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DateTick t1 = new DateTick(new Date(0L), "Label", TextAnchor.CENTER, TextAnchor.CENTER, 10.0); DateTick t2 = TestUtils.serialised(t1); assertEquals(t1, t2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/axis/DateTickUnitTest.java000066400000000000000000000051731463604235500301160ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * DateTickUnitTest.java * --------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import static org.junit.jupiter.api.Assertions.assertEquals; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; /** * Tests for the {@link DateTickUnit} class. */ public class DateTickUnitTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { DateTickUnit t1 = new DateTickUnit(DateTickUnitType.DAY, 1); DateTickUnit t2 = new DateTickUnit(DateTickUnitType.DAY, 1); assertEquals(t1, t2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { DateTickUnit t1 = new DateTickUnit(DateTickUnitType.DAY, 1); DateTickUnit t2 = new DateTickUnit(DateTickUnitType.DAY, 1); assertEquals(t1, t2); int h1 = t1.hashCode(); int h2 = t2.hashCode(); assertEquals(h1, h2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DateTickUnit a1 = new DateTickUnit(DateTickUnitType.DAY, 7); DateTickUnit a2 = TestUtils.serialised(a1); assertEquals(a1, a2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/axis/ExtendedCategoryAxisTest.java000066400000000000000000000122411463604235500316430ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------- * ExtendedCategoryAxisTest.java * ----------------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.awt.Color; import java.awt.Font; import java.awt.GradientPaint; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link ExtendedCategoryAxis} class. */ public class ExtendedCategoryAxisTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { ExtendedCategoryAxis a1 = new ExtendedCategoryAxis("Test"); ExtendedCategoryAxis a2 = new ExtendedCategoryAxis("Test"); assertEquals(a1, a2); a1.addSubLabel("C1", "C1-sublabel"); assertNotEquals(a1, a2); a2.addSubLabel("C1", "C1-sublabel"); assertEquals(a1, a2); a1.setSubLabelFont(new Font("Dialog", Font.BOLD, 8)); assertNotEquals(a1, a2); a2.setSubLabelFont(new Font("Dialog", Font.BOLD, 8)); assertEquals(a1, a2); a1.setSubLabelPaint(Color.RED); assertNotEquals(a1, a2); a2.setSubLabelPaint(Color.RED); assertEquals(a1, a2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { ExtendedCategoryAxis a1 = new ExtendedCategoryAxis("Test"); ExtendedCategoryAxis a2 = new ExtendedCategoryAxis("Test"); assertEquals(a1, a2); int h1 = a1.hashCode(); int h2 = a2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { ExtendedCategoryAxis a1 = new ExtendedCategoryAxis("Test"); ExtendedCategoryAxis a2 = (ExtendedCategoryAxis) a1.clone(); assertNotSame(a1, a2); assertSame(a1.getClass(), a2.getClass()); assertEquals(a1, a2); // check independence a1.addSubLabel("C1", "ABC"); assertNotEquals(a1, a2); a2.addSubLabel("C1", "ABC"); assertEquals(a1, a2); } /** * Confirm that cloning works. This test customises the font and paint * per category label. */ @Test public void testCloning2() throws CloneNotSupportedException { ExtendedCategoryAxis a1 = new ExtendedCategoryAxis("Test"); a1.setTickLabelFont("C1", new Font("Dialog", Font.PLAIN, 15)); a1.setTickLabelPaint("C1", new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.WHITE)); ExtendedCategoryAxis a2 = (ExtendedCategoryAxis) a1.clone(); assertNotSame(a1, a2); assertSame(a1.getClass(), a2.getClass()); assertEquals(a1, a2); // check that changing a tick label font in a1 doesn't change a2 a1.setTickLabelFont("C1", null); assertNotEquals(a1, a2); a2.setTickLabelFont("C1", null); assertEquals(a1, a2); // check that changing a tick label paint in a1 doesn't change a2 a1.setTickLabelPaint("C1", Color.YELLOW); assertNotEquals(a1, a2); a2.setTickLabelPaint("C1", Color.YELLOW); assertEquals(a1, a2); // check that changing a category label tooltip in a1 doesn't change a2 a1.addCategoryLabelToolTip("C1", "XYZ"); assertNotEquals(a1, a2); a2.addCategoryLabelToolTip("C1", "XYZ"); assertEquals(a1, a2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { ExtendedCategoryAxis a1 = new ExtendedCategoryAxis("Test"); a1.setSubLabelPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); ExtendedCategoryAxis a2 = TestUtils.serialised(a1); assertEquals(a1, a2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/axis/LogAxisTest.java000066400000000000000000000233511463604235500271320ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * LogAxisTest.java * ---------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.TestUtils; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.RectangleEdge; import org.jfree.data.category.DefaultCategoryDataset; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link LogAxis} class. */ public class LogAxisTest { /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { LogAxis a1 = new LogAxis("Test"); LogAxis a2 = (LogAxis) a1.clone(); assertNotSame(a1, a2); assertSame(a1.getClass(), a2.getClass()); assertEquals(a1, a2); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { LogAxis a1 = new LogAxis("Test"); LogAxis a2 = new LogAxis("Test"); assertEquals(a1, a2); a1.setBase(2.0); assertNotEquals(a1, a2); a2.setBase(2.0); assertEquals(a1, a2); a1.setSmallestValue(0.1); assertNotEquals(a1, a2); a2.setSmallestValue(0.1); assertEquals(a1, a2); a1.setMinorTickCount(8); assertNotEquals(a1, a2); a2.setMinorTickCount(8); assertEquals(a1, a2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { LogAxis a1 = new LogAxis("Test"); LogAxis a2 = new LogAxis("Test"); assertEquals(a1, a2); int h1 = a1.hashCode(); int h2 = a2.hashCode(); assertEquals(h1, h2); } private static final double EPSILON = 0.0000001; /** * Test the translation of Java2D values to data values. */ @Test public void testTranslateJava2DToValue() { LogAxis axis = new LogAxis(); axis.setRange(50.0, 100.0); Rectangle2D dataArea = new Rectangle2D.Double(10.0, 50.0, 400.0, 300.0); double y1 = axis.java2DToValue(75.0, dataArea, RectangleEdge.LEFT); assertEquals(94.3874312681693, y1, EPSILON); double y2 = axis.java2DToValue(75.0, dataArea, RectangleEdge.RIGHT); assertEquals(94.3874312681693, y2, EPSILON); double x1 = axis.java2DToValue(75.0, dataArea, RectangleEdge.TOP); assertEquals(55.961246381405, x1, EPSILON); double x2 = axis.java2DToValue(75.0, dataArea, RectangleEdge.BOTTOM); assertEquals(55.961246381405, x2, EPSILON); axis.setInverted(true); double y3 = axis.java2DToValue(75.0, dataArea, RectangleEdge.LEFT); assertEquals(52.9731547179647, y3, EPSILON); double y4 = axis.java2DToValue(75.0, dataArea, RectangleEdge.RIGHT); assertEquals(52.9731547179647, y4, EPSILON); double x3 = axis.java2DToValue(75.0, dataArea, RectangleEdge.TOP); assertEquals(89.3475453695651, x3, EPSILON); double x4 = axis.java2DToValue(75.0, dataArea, RectangleEdge.BOTTOM); assertEquals(89.3475453695651, x4, EPSILON); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { LogAxis a1 = new LogAxis("Test Axis"); LogAxis a2 = TestUtils.serialised(a1); assertEquals(a1, a2); } /** * A simple test for the auto-range calculation looking at a * LogAxis used as the range axis for a CategoryPlot. */ @Test public void testAutoRange1() { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.setValue(100.0, "Row 1", "Column 1"); dataset.setValue(200.0, "Row 1", "Column 2"); JFreeChart chart = ChartFactory.createBarChart("Test", "Categories", "Value", dataset); CategoryPlot plot = (CategoryPlot) chart.getPlot(); LogAxis axis = new LogAxis("Log(Y)"); plot.setRangeAxis(axis); assertEquals(0.0, axis.getLowerBound(), EPSILON); assertEquals(2.6066426411261268E7, axis.getUpperBound(), EPSILON); } /** * A simple test for the auto-range calculation looking at a * NumberAxis used as the range axis for a CategoryPlot. In this * case, the original dataset is replaced with a new dataset. */ @Test public void testAutoRange3() { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.setValue(100.0, "Row 1", "Column 1"); dataset.setValue(200.0, "Row 1", "Column 2"); JFreeChart chart = ChartFactory.createLineChart("Test", "Categories", "Value", dataset, PlotOrientation.VERTICAL, false, false, false); CategoryPlot plot = (CategoryPlot) chart.getPlot(); LogAxis axis = new LogAxis("Log(Y)"); plot.setRangeAxis(axis); assertEquals(96.59363289248458, axis.getLowerBound(), EPSILON); assertEquals(207.0529847682752, axis.getUpperBound(), EPSILON); // now replacing the dataset should update the axis range... DefaultCategoryDataset dataset2 = new DefaultCategoryDataset(); dataset2.setValue(900.0, "Row 1", "Column 1"); dataset2.setValue(1000.0, "Row 1", "Column 2"); plot.setDataset(dataset2); assertEquals(895.2712433374774, axis.getLowerBound(), EPSILON); assertEquals(1005.2819262292991, axis.getUpperBound(), EPSILON); } /** * Checks that the auto-range for the domain axis on an XYPlot is * working as expected. */ @Test public void testXYAutoRange1() { XYSeries series = new XYSeries("Series 1"); series.add(1.0, 1.0); series.add(2.0, 2.0); series.add(3.0, 3.0); XYSeriesCollection dataset = new XYSeriesCollection(); dataset.addSeries(series); JFreeChart chart = ChartFactory.createScatterPlot("Test", "X", "Y", dataset); XYPlot plot = (XYPlot) chart.getPlot(); LogAxis axis = new LogAxis("Log(Y)"); plot.setRangeAxis(axis); assertEquals(0.9465508226401592, axis.getLowerBound(), EPSILON); assertEquals(3.1694019256486126, axis.getUpperBound(), EPSILON); } /** * Checks that the auto-range for the range axis on an XYPlot is * working as expected. */ @Test public void testXYAutoRange2() { XYSeries series = new XYSeries("Series 1"); series.add(1.0, 1.0); series.add(2.0, 2.0); series.add(3.0, 3.0); XYSeriesCollection dataset = new XYSeriesCollection(); dataset.addSeries(series); JFreeChart chart = ChartFactory.createScatterPlot("Test", "X", "Y", dataset); XYPlot plot = (XYPlot) chart.getPlot(); LogAxis axis = new LogAxis("Log(Y)"); plot.setRangeAxis(axis); assertEquals(0.9465508226401592, axis.getLowerBound(), EPSILON); assertEquals(3.1694019256486126, axis.getUpperBound(), EPSILON); } /** * Some checks for the setLowerBound() method. */ @Test public void testSetLowerBound() { LogAxis axis = new LogAxis("X"); axis.setRange(0.0, 10.0); axis.setLowerBound(5.0); assertEquals(5.0, axis.getLowerBound(), EPSILON); axis.setLowerBound(10.0); assertEquals(10.0, axis.getLowerBound(), EPSILON); assertEquals(11.0, axis.getUpperBound(), EPSILON); } /** * Checks the default value for the tickMarksVisible flag. */ @Test public void testTickMarksVisibleDefault() { LogAxis axis = new LogAxis("Log Axis"); assertTrue(axis.isTickMarksVisible()); } /** * Checks that a TickUnit with a size of 0 doesn't crash. */ @Test public void testRefreshTicksWithZeroTickUnit() { LogAxis axis = new LogAxis(); AxisState state = new AxisState(); BufferedImage image = new BufferedImage(200, 100, BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = image.createGraphics(); Rectangle2D area = new Rectangle2D.Double(0.0, 0.0, 200, 100); axis.refreshTicks(g2, state, area, RectangleEdge.TOP); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/axis/LogarithmicAxisTest.java000066400000000000000000000203571463604235500306560ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * LogarithmicAxisTest.java * ------------------------ * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import java.awt.geom.Rectangle2D; import org.jfree.chart.TestUtils; import org.jfree.chart.ui.RectangleEdge; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * Tests for the {@link LogarithmicAxis} class. */ public class LogarithmicAxisTest { static class MyLogarithmicAxis extends LogarithmicAxis { /** * Creates a new instance. * * @param label the label. */ public MyLogarithmicAxis(String label) { super(label); } /* (non-Javadoc) * @see org.jfree.chart.axis.LogarithmicAxis#switchedLog10(double) */ @Override protected double switchedLog10(double val) { return super.switchedLog10(val); } } /** Tolerance for floating point comparisons */ public static double EPSILON = 0.000001; MyLogarithmicAxis axis = null; /** * Sets up a new axis. * * @throws Exception */ @BeforeEach public void setUp() throws Exception { this.axis = new MyLogarithmicAxis("Value (log)"); this.axis.setAllowNegativesFlag(false); this.axis.setLog10TickLabelsFlag(false); this.axis.setLowerMargin(0.0); this.axis.setUpperMargin(0.0); this.axis.setLowerBound(0.2); this.axis.setUpperBound(100.0); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { LogarithmicAxis a1 = new LogarithmicAxis("Test Axis"); LogarithmicAxis a2 = TestUtils.serialised(a1); assertEquals(a1, a2); } /** * Test if adjustedLog10 and adjustedPow10 are inverses of each other */ @Test public void testAdjustedLog10() { checkLogPowRoundTrip(20); checkLogPowRoundTrip(10); checkLogPowRoundTrip(5); checkLogPowRoundTrip(2); checkLogPowRoundTrip(1); checkLogPowRoundTrip(0.5); checkLogPowRoundTrip(0.2); checkLogPowRoundTrip(0.0001); } private void checkLogPowRoundTrip(double value) { assertEquals(value, this.axis.adjustedLog10( this.axis.adjustedPow10(value)), EPSILON, "log(pow(x)) = x"); assertEquals(value, this.axis.adjustedPow10( this.axis.adjustedLog10(value)), EPSILON, "pow(log(x)) = x"); } /** * Test if switchedLog10 and switchedPow10 are inverses of each other */ @Test public void testSwitchedLog10() { assertFalse(this.axis.getAllowNegativesFlag(), "Axis should not allow negative values"); assertEquals(Math.log(0.5) / LogarithmicAxis.LOG10_VALUE, this.axis.switchedLog10(0.5), EPSILON); checkSwitchedLogPowRoundTrip(20); checkSwitchedLogPowRoundTrip(10); checkSwitchedLogPowRoundTrip(5); checkSwitchedLogPowRoundTrip(2); checkSwitchedLogPowRoundTrip(1); checkSwitchedLogPowRoundTrip(0.5); checkSwitchedLogPowRoundTrip(0.2); checkSwitchedLogPowRoundTrip(0.0001); } private void checkSwitchedLogPowRoundTrip(double value) { assertEquals(value, this.axis.switchedLog10( this.axis.switchedPow10(value)), EPSILON, "log(pow(x)) = x"); assertEquals(value, this.axis.switchedPow10( this.axis.switchedLog10(value)), EPSILON, "pow(log(x)) = x"); } /** * Test of java2DToValue method. */ @Test public void testJava2DToValue() { Rectangle2D plotArea = new Rectangle2D.Double(22, 33, 500, 500); RectangleEdge edge = RectangleEdge.BOTTOM; // set axis bounds to be both greater than 1 this.axis.setRange(10, 20); checkPointsToValue(edge, plotArea); // check for bounds interval that includes 1 this.axis.setRange(0.5, 10); checkPointsToValue(edge, plotArea); // check for bounds interval that includes 1 this.axis.setRange(0.2, 20); checkPointsToValue(edge, plotArea); // check for both bounds smaller than 1 this.axis.setRange(0.2, 0.7); checkPointsToValue(edge, plotArea); } /** * Test of valueToJava2D method. */ @Test public void testValueToJava2D() { Rectangle2D plotArea = new Rectangle2D.Double(22, 33, 500, 500); RectangleEdge edge = RectangleEdge.BOTTOM; // set axis bounds to be both greater than 1 this.axis.setRange(10, 20); checkPointsToJava2D(edge, plotArea); // check for bounds interval that includes 1 this.axis.setRange(0.5, 10); checkPointsToJava2D(edge, plotArea); // check for bounds interval that includes 1 this.axis.setRange(0.2, 20); checkPointsToJava2D(edge, plotArea); // check for both bounds smaller than 1 this.axis.setRange(0.2, 0.7); checkPointsToJava2D(edge, plotArea); } private void checkPointsToJava2D(RectangleEdge edge, Rectangle2D plotArea) { assertEquals(plotArea.getX(), this.axis.valueToJava2D( this.axis.getLowerBound(), plotArea, edge), EPSILON, "Left most point on the axis should be beginning of range."); assertEquals(plotArea.getX() + plotArea.getWidth(), this.axis.valueToJava2D(this.axis.getUpperBound(), plotArea, edge), EPSILON, "Right most point on the axis should be end of range."); assertEquals(plotArea.getX() + (plotArea.getWidth() / 2), this.axis.valueToJava2D(Math.sqrt(this.axis.getLowerBound() * this.axis.getUpperBound()), plotArea, edge), EPSILON, "Center point on the axis should geometric mean of the bounds."); } /** * Check the translation java2D to value for left, right, and center point. * * @param edge the edge. * @param plotArea the plot area. */ private void checkPointsToValue(RectangleEdge edge, Rectangle2D plotArea) { assertEquals(this.axis.getUpperBound(), this.axis.java2DToValue( plotArea.getX() + plotArea.getWidth(), plotArea, edge), EPSILON, "Right most point on the axis should be end of range."); assertEquals(this.axis.getLowerBound(), this.axis.java2DToValue(plotArea.getX(), plotArea, edge), EPSILON, "Left most point on the axis should be beginning of range."); assertEquals(Math.sqrt(this.axis.getUpperBound() * this.axis.getLowerBound()), this.axis.java2DToValue( plotArea.getX() + (plotArea.getWidth() / 2), plotArea, edge), EPSILON, "Center point on the axis should geometric mean of the bounds."); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/axis/MarkerAxisBandTest.java000066400000000000000000000075121463604235500304200ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * MarkerAxisBandTest.java * ----------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.awt.Font; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link MarkerAxisBand} class. */ public class MarkerAxisBandTest { /** * Test that the equals() method can distinguish all fields. */ @Test public void testEquals() { Font font1 = new Font("SansSerif", Font.PLAIN, 12); Font font2 = new Font("SansSerif", Font.PLAIN, 14); MarkerAxisBand a1 = new MarkerAxisBand(null, 1.0, 1.0, 1.0, 1.0, font1); MarkerAxisBand a2 = new MarkerAxisBand(null, 1.0, 1.0, 1.0, 1.0, font1); assertEquals(a1, a2); a1 = new MarkerAxisBand(null, 2.0, 1.0, 1.0, 1.0, font1); assertNotEquals(a1, a2); a2 = new MarkerAxisBand(null, 2.0, 1.0, 1.0, 1.0, font1); assertEquals(a1, a2); a1 = new MarkerAxisBand(null, 2.0, 3.0, 1.0, 1.0, font1); assertNotEquals(a1, a2); a2 = new MarkerAxisBand(null, 2.0, 3.0, 1.0, 1.0, font1); assertEquals(a1, a2); a1 = new MarkerAxisBand(null, 2.0, 3.0, 4.0, 1.0, font1); assertNotEquals(a1, a2); a2 = new MarkerAxisBand(null, 2.0, 3.0, 4.0, 1.0, font1); assertEquals(a1, a2); a1 = new MarkerAxisBand(null, 2.0, 3.0, 4.0, 5.0, font1); assertNotEquals(a1, a2); a2 = new MarkerAxisBand(null, 2.0, 3.0, 4.0, 5.0, font1); assertEquals(a1, a2); a1 = new MarkerAxisBand(null, 2.0, 3.0, 4.0, 5.0, font2); assertNotEquals(a1, a2); a2 = new MarkerAxisBand(null, 2.0, 3.0, 4.0, 5.0, font2); assertEquals(a1, a2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { Font font1 = new Font("SansSerif", Font.PLAIN, 12); MarkerAxisBand a1 = new MarkerAxisBand(null, 1.0, 1.0, 1.0, 1.0, font1); MarkerAxisBand a2 = new MarkerAxisBand(null, 1.0, 1.0, 1.0, 1.0, font1); assertEquals(a1, a2); int h1 = a1.hashCode(); int h2 = a2.hashCode(); assertEquals(h1, h2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { MarkerAxisBand a1 = new MarkerAxisBand(null, 1.0, 1.0, 1.0, 1.0, null); MarkerAxisBand a2 = TestUtils.serialised(a1); assertEquals(a1, a2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/axis/ModuloAxisTest.java000066400000000000000000000061711463604235500276510ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * ModuloAxisTest.java * ------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import org.jfree.chart.TestUtils; import org.jfree.data.Range; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link ModuloAxis} class. */ public class ModuloAxisTest { /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { ModuloAxis a1 = new ModuloAxis("Test", new Range(0.0, 1.0)); ModuloAxis a2 = (ModuloAxis) a1.clone(); assertNotSame(a1, a2); assertSame(a1.getClass(), a2.getClass()); assertEquals(a1, a2); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { ModuloAxis a1 = new ModuloAxis("Test", new Range(0.0, 1.0)); ModuloAxis a2 = new ModuloAxis("Test", new Range(0.0, 1.0)); assertEquals(a1, a2); a1.setDisplayRange(0.1, 1.1); assertNotEquals(a1, a2); a2.setDisplayRange(0.1, 1.1); assertEquals(a1, a2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { ModuloAxis a1 = new ModuloAxis("Test", new Range(0.0, 1.0)); ModuloAxis a2 = new ModuloAxis("Test", new Range(0.0, 1.0)); assertEquals(a1, a2); int h1 = a1.hashCode(); int h2 = a2.hashCode(); assertEquals(h1, h2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { ModuloAxis a1 = new ModuloAxis("Test", new Range(0.0, 1.0)); ModuloAxis a2 = TestUtils.serialised(a1); assertEquals(a1, a2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/axis/MonthDateFormatTest.java000066400000000000000000000116351463604235500306220ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * MonthDateFormatTest.java * ------------------------ * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.text.SimpleDateFormat; import java.util.Locale; import java.util.TimeZone; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Some tests for the {@link MonthDateFormat} class. */ public class MonthDateFormatTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { MonthDateFormat mf1 = new MonthDateFormat(); MonthDateFormat mf2 = new MonthDateFormat(); assertEquals(mf1, mf2); assertEquals(mf2, mf1); boolean[] showYear1 = new boolean [12]; showYear1[0] = true; boolean[] showYear2 = new boolean [12]; showYear1[1] = true; // time zone mf1 = new MonthDateFormat(TimeZone.getTimeZone("PST"), Locale.US, 1, showYear1, new SimpleDateFormat("yy")); assertNotEquals(mf1, mf2); mf2 = new MonthDateFormat(TimeZone.getTimeZone("PST"), Locale.US, 1, showYear1, new SimpleDateFormat("yy")); assertEquals(mf1, mf2); // locale mf1 = new MonthDateFormat(TimeZone.getTimeZone("PST"), Locale.FRANCE, 1, showYear1, new SimpleDateFormat("yy")); assertNotEquals(mf1, mf2); mf2 = new MonthDateFormat(TimeZone.getTimeZone("PST"), Locale.FRANCE, 1, showYear1, new SimpleDateFormat("yy")); assertEquals(mf1, mf2); // chars mf1 = new MonthDateFormat(TimeZone.getTimeZone("PST"), Locale.FRANCE, 2, showYear1, new SimpleDateFormat("yy")); assertNotEquals(mf1, mf2); mf2 = new MonthDateFormat(TimeZone.getTimeZone("PST"), Locale.FRANCE, 2, showYear1, new SimpleDateFormat("yy")); assertEquals(mf1, mf2); // showYear[] mf1 = new MonthDateFormat(TimeZone.getTimeZone("PST"), Locale.FRANCE, 2, showYear2, new SimpleDateFormat("yy")); assertNotEquals(mf1, mf2); mf2 = new MonthDateFormat(TimeZone.getTimeZone("PST"), Locale.FRANCE, 2, showYear2, new SimpleDateFormat("yy")); assertEquals(mf1, mf2); // yearFormatter mf1 = new MonthDateFormat(TimeZone.getTimeZone("PST"), Locale.FRANCE, 2, showYear2, new SimpleDateFormat("yyyy")); assertNotEquals(mf1, mf2); mf2 = new MonthDateFormat(TimeZone.getTimeZone("PST"), Locale.FRANCE, 2, showYear2, new SimpleDateFormat("yyyy")); assertEquals(mf1, mf2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { MonthDateFormat mf1 = new MonthDateFormat(); MonthDateFormat mf2 = new MonthDateFormat(); assertEquals(mf1, mf2); int h1 = mf1.hashCode(); int h2 = mf2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() { MonthDateFormat mf1 = new MonthDateFormat(); MonthDateFormat mf2 = null; mf2 = (MonthDateFormat) mf1.clone(); assertNotSame(mf1, mf2); assertSame(mf1.getClass(), mf2.getClass()); assertEquals(mf1, mf2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { MonthDateFormat mf1 = new MonthDateFormat(); MonthDateFormat mf2 = TestUtils.serialised(mf1); assertEquals(mf1, mf2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/axis/NumberAxisTest.java000066400000000000000000000344011463604235500276370ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * NumberAxisTest.java * ------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.awt.geom.Rectangle2D; import java.text.DecimalFormat; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.TestUtils; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.category.BarRenderer; import org.jfree.chart.ui.RectangleEdge; import org.jfree.data.RangeType; import org.jfree.data.category.DefaultCategoryDataset; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link NumberAxis} class. */ public class NumberAxisTest { /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { NumberAxis a1 = new NumberAxis("Test"); NumberAxis a2 = (NumberAxis) a1.clone(); assertNotSame(a1, a2); assertSame(a1.getClass(), a2.getClass()); assertEquals(a1, a2); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { NumberAxis a1 = new NumberAxis("Test"); NumberAxis a2 = new NumberAxis("Test"); assertEquals(a1, a2); //private boolean autoRangeIncludesZero; a1.setAutoRangeIncludesZero(false); assertNotEquals(a1, a2); a2.setAutoRangeIncludesZero(false); assertEquals(a1, a2); //private boolean autoRangeStickyZero; a1.setAutoRangeStickyZero(false); assertNotEquals(a1, a2); a2.setAutoRangeStickyZero(false); assertEquals(a1, a2); //private NumberTickUnit tickUnit; a1.setTickUnit(new NumberTickUnit(25.0)); assertNotEquals(a1, a2); a2.setTickUnit(new NumberTickUnit(25.0)); assertEquals(a1, a2); //private NumberFormat numberFormatOverride; a1.setNumberFormatOverride(new DecimalFormat("0.00")); assertNotEquals(a1, a2); a2.setNumberFormatOverride(new DecimalFormat("0.00")); assertEquals(a1, a2); a1.setRangeType(RangeType.POSITIVE); assertNotEquals(a1, a2); a2.setRangeType(RangeType.POSITIVE); assertEquals(a1, a2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { NumberAxis a1 = new NumberAxis("Test"); NumberAxis a2 = new NumberAxis("Test"); assertEquals(a1, a2); int h1 = a1.hashCode(); int h2 = a2.hashCode(); assertEquals(h1, h2); } private static final double EPSILON = 0.0000001; /** * Test the translation of Java2D values to data values. */ @Test public void testTranslateJava2DToValue() { NumberAxis axis = new NumberAxis(); axis.setRange(50.0, 100.0); Rectangle2D dataArea = new Rectangle2D.Double(10.0, 50.0, 400.0, 300.0); double y1 = axis.java2DToValue(75.0, dataArea, RectangleEdge.LEFT); assertEquals(y1, 95.8333333, EPSILON); double y2 = axis.java2DToValue(75.0, dataArea, RectangleEdge.RIGHT); assertEquals(y2, 95.8333333, EPSILON); double x1 = axis.java2DToValue(75.0, dataArea, RectangleEdge.TOP); assertEquals(x1, 58.125, EPSILON); double x2 = axis.java2DToValue(75.0, dataArea, RectangleEdge.BOTTOM); assertEquals(x2, 58.125, EPSILON); axis.setInverted(true); double y3 = axis.java2DToValue(75.0, dataArea, RectangleEdge.LEFT); assertEquals(y3, 54.1666667, EPSILON); double y4 = axis.java2DToValue(75.0, dataArea, RectangleEdge.RIGHT); assertEquals(y4, 54.1666667, EPSILON); double x3 = axis.java2DToValue(75.0, dataArea, RectangleEdge.TOP); assertEquals(x3, 91.875, EPSILON); double x4 = axis.java2DToValue(75.0, dataArea, RectangleEdge.BOTTOM); assertEquals(x4, 91.875, EPSILON); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { NumberAxis a1 = new NumberAxis("Test Axis"); NumberAxis a2 = TestUtils.serialised(a1); assertEquals(a1, a2); } /** * A simple test for the auto-range calculation looking at a * NumberAxis used as the range axis for a CategoryPlot. */ @Test public void testAutoRange1() { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.setValue(100.0, "Row 1", "Column 1"); dataset.setValue(200.0, "Row 1", "Column 2"); JFreeChart chart = ChartFactory.createBarChart("Test", "Categories", "Value", dataset); CategoryPlot plot = (CategoryPlot) chart.getPlot(); NumberAxis axis = (NumberAxis) plot.getRangeAxis(); assertEquals(axis.getLowerBound(), 0.0, EPSILON); assertEquals(axis.getUpperBound(), 210.0, EPSILON); } /** * A simple test for the auto-range calculation looking at a * NumberAxis used as the range axis for a CategoryPlot. In this * case, the 'autoRangeIncludesZero' flag is set to false. */ @Test public void testAutoRange2() { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.setValue(100.0, "Row 1", "Column 1"); dataset.setValue(200.0, "Row 1", "Column 2"); JFreeChart chart = ChartFactory.createLineChart("Test", "Categories", "Value", dataset, PlotOrientation.VERTICAL, false, false, false); CategoryPlot plot = (CategoryPlot) chart.getPlot(); NumberAxis axis = (NumberAxis) plot.getRangeAxis(); axis.setAutoRangeIncludesZero(false); assertEquals(axis.getLowerBound(), 95.0, EPSILON); assertEquals(axis.getUpperBound(), 205.0, EPSILON); } /** * A simple test for the auto-range calculation looking at a * NumberAxis used as the range axis for a CategoryPlot. In this * case, the 'autoRangeIncludesZero' flag is set to false AND the * original dataset is replaced with a new dataset. */ @Test public void testAutoRange3() { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.setValue(100.0, "Row 1", "Column 1"); dataset.setValue(200.0, "Row 1", "Column 2"); JFreeChart chart = ChartFactory.createLineChart("Test", "Categories", "Value", dataset, PlotOrientation.VERTICAL, false, false, false); CategoryPlot plot = (CategoryPlot) chart.getPlot(); NumberAxis axis = (NumberAxis) plot.getRangeAxis(); axis.setAutoRangeIncludesZero(false); assertEquals(axis.getLowerBound(), 95.0, EPSILON); assertEquals(axis.getUpperBound(), 205.0, EPSILON); // now replacing the dataset should update the axis range... DefaultCategoryDataset dataset2 = new DefaultCategoryDataset(); dataset2.setValue(900.0, "Row 1", "Column 1"); dataset2.setValue(1000.0, "Row 1", "Column 2"); plot.setDataset(dataset2); assertEquals(axis.getLowerBound(), 895.0, EPSILON); assertEquals(axis.getUpperBound(), 1005.0, EPSILON); } /** * A check for the interaction between the 'autoRangeIncludesZero' flag * and the base setting in the BarRenderer. */ @Test public void testAutoRange4() { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.setValue(100.0, "Row 1", "Column 1"); dataset.setValue(200.0, "Row 1", "Column 2"); JFreeChart chart = ChartFactory.createBarChart("Test", "Categories", "Value", dataset, PlotOrientation.VERTICAL, false, false, false); CategoryPlot plot = (CategoryPlot) chart.getPlot(); NumberAxis axis = (NumberAxis) plot.getRangeAxis(); axis.setAutoRangeIncludesZero(false); BarRenderer br = (BarRenderer) plot.getRenderer(); br.setIncludeBaseInRange(false); assertEquals(95.0, axis.getLowerBound(), EPSILON); assertEquals(205.0, axis.getUpperBound(), EPSILON); br.setIncludeBaseInRange(true); assertEquals(0.0, axis.getLowerBound(), EPSILON); assertEquals(210.0, axis.getUpperBound(), EPSILON); axis.setAutoRangeIncludesZero(true); assertEquals(0.0, axis.getLowerBound(), EPSILON); assertEquals(210.0, axis.getUpperBound(), EPSILON); br.setIncludeBaseInRange(true); assertEquals(0.0, axis.getLowerBound(), EPSILON); assertEquals(210.0, axis.getUpperBound(), EPSILON); // now replacing the dataset should update the axis range... DefaultCategoryDataset dataset2 = new DefaultCategoryDataset(); dataset2.setValue(900.0, "Row 1", "Column 1"); dataset2.setValue(1000.0, "Row 1", "Column 2"); plot.setDataset(dataset2); assertEquals(0.0, axis.getLowerBound(), EPSILON); assertEquals(1050.0, axis.getUpperBound(), EPSILON); br.setIncludeBaseInRange(false); assertEquals(0.0, axis.getLowerBound(), EPSILON); assertEquals(1050.0, axis.getUpperBound(), EPSILON); axis.setAutoRangeIncludesZero(false); assertEquals(895.0, axis.getLowerBound(), EPSILON); assertEquals(1005.0, axis.getUpperBound(), EPSILON); } /** * Checks that the auto-range for the domain axis on an XYPlot is * working as expected. */ @Test public void testXYAutoRange1() { XYSeries series = new XYSeries("Series 1"); series.add(1.0, 1.0); series.add(2.0, 2.0); series.add(3.0, 3.0); XYSeriesCollection dataset = new XYSeriesCollection(); dataset.addSeries(series); JFreeChart chart = ChartFactory.createScatterPlot("Test", "X", "Y", dataset); XYPlot plot = (XYPlot) chart.getPlot(); NumberAxis axis = (NumberAxis) plot.getDomainAxis(); axis.setAutoRangeIncludesZero(false); assertEquals(0.9, axis.getLowerBound(), EPSILON); assertEquals(3.1, axis.getUpperBound(), EPSILON); } /** * Checks that the auto-range for the range axis on an XYPlot is * working as expected. */ @Test public void testXYAutoRange2() { XYSeries series = new XYSeries("Series 1"); series.add(1.0, 1.0); series.add(2.0, 2.0); series.add(3.0, 3.0); XYSeriesCollection dataset = new XYSeriesCollection(); dataset.addSeries(series); JFreeChart chart = ChartFactory.createScatterPlot("Test", "X", "Y", dataset); XYPlot plot = (XYPlot) chart.getPlot(); NumberAxis axis = (NumberAxis) plot.getRangeAxis(); axis.setAutoRangeIncludesZero(false); assertEquals(0.9, axis.getLowerBound(), EPSILON); assertEquals(3.1, axis.getUpperBound(), EPSILON); } // /** // * Some checks for the setRangeType() method. // */ // public void testSetRangeType() { // // NumberAxis axis = new NumberAxis("X"); // axis.setRangeType(RangeType.POSITIVE); // assertEquals(RangeType.POSITIVE, axis.getRangeType()); // // // test a change to RangeType.POSITIVE // axis.setRangeType(RangeType.FULL); // axis.setRange(-5.0, 5.0); // axis.setRangeType(RangeType.POSITIVE); // assertEquals(new Range(0.0, 5.0), axis.getRange()); // // axis.setRangeType(RangeType.FULL); // axis.setRange(-10.0, -5.0); // axis.setRangeType(RangeType.POSITIVE); // assertEquals(new Range(0.0, axis.getAutoRangeMinimumSize()), // axis.getRange()); // // // test a change to RangeType.NEGATIVE // axis.setRangeType(RangeType.FULL); // axis.setRange(-5.0, 5.0); // axis.setRangeType(RangeType.NEGATIVE); // assertEquals(new Range(-5.0, 0.0), axis.getRange()); // // axis.setRangeType(RangeType.FULL); // axis.setRange(5.0, 10.0); // axis.setRangeType(RangeType.NEGATIVE); // assertEquals(new Range(-axis.getAutoRangeMinimumSize(), 0.0), // axis.getRange()); // // // try null // boolean pass = false; // try { // axis.setRangeType(null); // } // catch (IllegalArgumentException e) { // pass = true; // } // assertTrue(pass); // } /** * Some checks for the setLowerBound() method. */ @Test public void testSetLowerBound() { NumberAxis axis = new NumberAxis("X"); axis.setRange(0.0, 10.0); axis.setLowerBound(5.0); assertEquals(5.0, axis.getLowerBound(), EPSILON); axis.setLowerBound(10.0); assertEquals(10.0, axis.getLowerBound(), EPSILON); assertEquals(11.0, axis.getUpperBound(), EPSILON); //axis.setRangeType(RangeType.POSITIVE); //axis.setLowerBound(-5.0); //assertEquals(0.0, axis.getLowerBound(), EPSILON); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/axis/NumberTickUnitTest.java000066400000000000000000000065571463604235500305000ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * NumberTickUnitTest.java * ----------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.text.DecimalFormat; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Some tests for the {@link NumberTickUnit} class. */ public class NumberTickUnitTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { NumberTickUnit t1 = new NumberTickUnit(1.23, new DecimalFormat("0.00")); NumberTickUnit t2 = new NumberTickUnit(1.23, new DecimalFormat("0.00")); assertEquals(t1, t2); assertEquals(t2, t1); t1 = new NumberTickUnit(3.21, new DecimalFormat("0.00")); assertNotEquals(t1, t2); t2 = new NumberTickUnit(3.21, new DecimalFormat("0.00")); assertEquals(t1, t2); t1 = new NumberTickUnit(3.21, new DecimalFormat("0.000")); assertNotEquals(t1, t2); t2 = new NumberTickUnit(3.21, new DecimalFormat("0.000")); assertEquals(t1, t2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { NumberTickUnit t1 = new NumberTickUnit(1.23, new DecimalFormat("0.00")); NumberTickUnit t2 = new NumberTickUnit(1.23, new DecimalFormat("0.00")); int h1 = t1.hashCode(); int h2 = t2.hashCode(); assertEquals(h1, h2); } /** * This is an immutable class so it doesn't need to be cloneable. */ @Test public void testCloning() { NumberTickUnit t1 = new NumberTickUnit(1.23, new DecimalFormat("0.00")); assertFalse(t1 instanceof Cloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { NumberTickUnit t1 = new NumberTickUnit(1.23, new DecimalFormat("0.00")); NumberTickUnit t2 = TestUtils.serialised(t1); assertEquals(t1, t2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/axis/PeriodAxisLabelInfoTest.java000066400000000000000000000156551463604235500314170ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------- * PeriodAxisLabelInfoTest.java * ---------------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.Paint; import java.awt.Stroke; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import java.util.TimeZone; import org.jfree.chart.TestUtils; import org.jfree.chart.ui.RectangleInsets; import org.jfree.data.time.Day; import org.jfree.data.time.Month; import org.jfree.data.time.Year; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link PeriodAxisLabelInfo} class. */ public class PeriodAxisLabelInfoTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { PeriodAxisLabelInfo info1 = new PeriodAxisLabelInfo(Day.class, new SimpleDateFormat("d")); PeriodAxisLabelInfo info2 = new PeriodAxisLabelInfo(Day.class, new SimpleDateFormat("d")); assertEquals(info1, info2); assertEquals(info2, info1); Class c1 = Day.class; Class c2 = Month.class; DateFormat df1 = new SimpleDateFormat("d"); DateFormat df2 = new SimpleDateFormat("MMM"); RectangleInsets sp1 = new RectangleInsets(1, 1, 1, 1); RectangleInsets sp2 = new RectangleInsets(2, 2, 2, 2); Font lf1 = new Font("SansSerif", Font.PLAIN, 10); Font lf2 = new Font("SansSerif", Font.BOLD, 9); Paint lp1 = Color.BLACK; Paint lp2 = Color.BLUE; boolean b1 = true; boolean b2 = false; Stroke s1 = new BasicStroke(0.5f); Stroke s2 = new BasicStroke(0.25f); Paint dp1 = Color.RED; Paint dp2 = Color.GREEN; info1 = new PeriodAxisLabelInfo(c2, df1, sp1, lf1, lp1, b1, s1, dp1); info2 = new PeriodAxisLabelInfo(c1, df1, sp1, lf1, lp1, b1, s1, dp1); assertNotEquals(info1, info2); info2 = new PeriodAxisLabelInfo(c2, df1, sp1, lf1, lp1, b1, s1, dp1); assertEquals(info1, info2); info1 = new PeriodAxisLabelInfo(c2, df2, sp1, lf1, lp1, b1, s1, dp1); assertNotEquals(info1, info2); info2 = new PeriodAxisLabelInfo(c2, df2, sp1, lf1, lp1, b1, s1, dp1); assertEquals(info1, info2); info1 = new PeriodAxisLabelInfo(c2, df2, sp2, lf1, lp1, b1, s1, dp1); assertNotEquals(info1, info2); info2 = new PeriodAxisLabelInfo(c2, df2, sp2, lf1, lp1, b1, s1, dp1); assertEquals(info1, info2); info1 = new PeriodAxisLabelInfo(c2, df2, sp2, lf2, lp1, b1, s1, dp1); assertNotEquals(info1, info2); info2 = new PeriodAxisLabelInfo(c2, df2, sp2, lf2, lp1, b1, s1, dp1); assertEquals(info1, info2); info1 = new PeriodAxisLabelInfo(c2, df2, sp2, lf2, lp2, b1, s1, dp1); assertNotEquals(info1, info2); info2 = new PeriodAxisLabelInfo(c2, df2, sp2, lf2, lp2, b1, s1, dp1); assertEquals(info1, info2); info1 = new PeriodAxisLabelInfo(c2, df2, sp2, lf2, lp2, b2, s1, dp1); assertNotEquals(info1, info2); info2 = new PeriodAxisLabelInfo(c2, df2, sp2, lf2, lp2, b2, s1, dp1); assertEquals(info1, info2); info1 = new PeriodAxisLabelInfo(c2, df2, sp2, lf2, lp2, b2, s2, dp1); assertNotEquals(info1, info2); info2 = new PeriodAxisLabelInfo(c2, df2, sp2, lf2, lp2, b2, s2, dp1); assertEquals(info1, info2); info1 = new PeriodAxisLabelInfo(c2, df2, sp2, lf2, lp2, b2, s2, dp2); assertNotEquals(info1, info2); info2 = new PeriodAxisLabelInfo(c2, df2, sp2, lf2, lp2, b2, s2, dp2); assertEquals(info1, info2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { PeriodAxisLabelInfo info1 = new PeriodAxisLabelInfo(Day.class, new SimpleDateFormat("d")); PeriodAxisLabelInfo info2 = new PeriodAxisLabelInfo(Day.class, new SimpleDateFormat("d")); assertEquals(info1, info2); int h1 = info1.hashCode(); int h2 = info2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { PeriodAxisLabelInfo info1 = new PeriodAxisLabelInfo(Day.class, new SimpleDateFormat("d")); PeriodAxisLabelInfo info2 = (PeriodAxisLabelInfo) info1.clone(); assertNotSame(info1, info2); assertSame(info1.getClass(), info2.getClass()); assertEquals(info1, info2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { PeriodAxisLabelInfo info1 = new PeriodAxisLabelInfo(Day.class, new SimpleDateFormat("d")); PeriodAxisLabelInfo info2 = TestUtils.serialised(info1); assertEquals(info1, info2); } /** * A test for the createInstance() method. */ @Test public void testCreateInstance() { TimeZone zone = TimeZone.getTimeZone("GMT"); PeriodAxisLabelInfo info = new PeriodAxisLabelInfo(Day.class, new SimpleDateFormat("d")); Day d = (Day) info.createInstance(new Date(0L), zone, Locale.UK); assertEquals(new Day(1, 1, 1970), d); info = new PeriodAxisLabelInfo(Year.class, new SimpleDateFormat("yyyy")); Year y = (Year) info.createInstance(new Date(0L), zone, Locale.UK); assertEquals(new Year(1970), y); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/axis/PeriodAxisTest.java000066400000000000000000000224471463604235500276400ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * PeriodAxisTest.java * ------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Stroke; import java.text.SimpleDateFormat; import java.util.GregorianCalendar; import java.util.Locale; import java.util.SimpleTimeZone; import java.util.TimeZone; import org.jfree.chart.TestUtils; import org.jfree.chart.event.AxisChangeEvent; import org.jfree.chart.event.AxisChangeListener; import org.jfree.data.Range; import org.jfree.data.time.DateRange; import org.jfree.data.time.Day; import org.jfree.data.time.Minute; import org.jfree.data.time.Month; import org.jfree.data.time.Quarter; import org.jfree.data.time.Second; import org.jfree.data.time.Year; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link PeriodAxis} class. */ public class PeriodAxisTest implements AxisChangeListener { /** The last event received. */ private AxisChangeEvent lastEvent; /** * Receives and records an {@link AxisChangeEvent}. * * @param event the event. */ @Override public void axisChanged(AxisChangeEvent event) { this.lastEvent = event; } /** * Confirm that the equals() method can distinguish all the required fields. */ @Test public void testEquals() { PeriodAxis a1 = new PeriodAxis("Test"); PeriodAxis a2 = new PeriodAxis("Test"); assertEquals(a1, a2); assertEquals(a2, a1); a1.setFirst(new Year(2000)); assertNotEquals(a1, a2); a2.setFirst(new Year(2000)); assertEquals(a1, a2); a1.setLast(new Year(2004)); assertNotEquals(a1, a2); a2.setLast(new Year(2004)); assertEquals(a1, a2); a1.setTimeZone(TimeZone.getTimeZone("Pacific/Auckland")); assertNotEquals(a1, a2); a2.setTimeZone(TimeZone.getTimeZone("Pacific/Auckland")); assertEquals(a1, a2); a1.setAutoRangeTimePeriodClass(Quarter.class); assertNotEquals(a1, a2); a2.setAutoRangeTimePeriodClass(Quarter.class); assertEquals(a1, a2); PeriodAxisLabelInfo[] info = new PeriodAxisLabelInfo[1]; info[0] = new PeriodAxisLabelInfo(Month.class, new SimpleDateFormat("MMM")); a1.setLabelInfo(info); assertNotEquals(a1, a2); a2.setLabelInfo(info); assertEquals(a1, a2); a1.setMajorTickTimePeriodClass(Minute.class); assertNotEquals(a1, a2); a2.setMajorTickTimePeriodClass(Minute.class); assertEquals(a1, a2); a1.setMinorTickMarksVisible(!a1.isMinorTickMarksVisible()); assertNotEquals(a1, a2); a2.setMinorTickMarksVisible(a1.isMinorTickMarksVisible()); assertEquals(a1, a2); a1.setMinorTickTimePeriodClass(Minute.class); assertNotEquals(a1, a2); a2.setMinorTickTimePeriodClass(Minute.class); assertEquals(a1, a2); Stroke s = new BasicStroke(1.23f); a1.setMinorTickMarkStroke(s); assertNotEquals(a1, a2); a2.setMinorTickMarkStroke(s); assertEquals(a1, a2); a1.setMinorTickMarkPaint(Color.BLUE); assertNotEquals(a1, a2); a2.setMinorTickMarkPaint(Color.BLUE); assertEquals(a1, a2); } /** * Confirm that the equals() method can distinguish the locale field (which * is new in version 1.0.13). */ @Test public void testEqualsWithLocale() { PeriodAxis a1 = new PeriodAxis("Test", new Year(2000), new Year(2009), TimeZone.getDefault(), Locale.JAPAN); PeriodAxis a2 = new PeriodAxis("Test", new Year(2000), new Year(2009), TimeZone.getDefault(), Locale.JAPAN); assertEquals(a1, a2); assertEquals(a2, a1); a1 = new PeriodAxis("Test", new Year(2000), new Year(2009), TimeZone.getDefault(), Locale.UK); assertNotEquals(a1, a2); a2 = new PeriodAxis("Test", new Year(2000), new Year(2009), TimeZone.getDefault(), Locale.UK); assertEquals(a1, a2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { PeriodAxis a1 = new PeriodAxis("Test"); PeriodAxis a2 = new PeriodAxis("Test"); assertEquals(a1, a2); int h1 = a1.hashCode(); int h2 = a2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { PeriodAxis a1 = new PeriodAxis("Test"); PeriodAxis a2 = (PeriodAxis) a1.clone(); assertNotSame(a1, a2); assertSame(a1.getClass(), a2.getClass()); assertEquals(a1, a2); // some checks that the clone is independent of the original a1.setLabel("New Label"); assertNotEquals(a1, a2); a2.setLabel("New Label"); assertEquals(a1, a2); a1.setFirst(new Year(1920)); assertNotEquals(a1, a2); a2.setFirst(new Year(1920)); assertEquals(a1, a2); a1.setLast(new Year(2020)); assertNotEquals(a1, a2); a2.setLast(new Year(2020)); assertEquals(a1, a2); PeriodAxisLabelInfo[] info = new PeriodAxisLabelInfo[2]; info[0] = new PeriodAxisLabelInfo(Day.class, new SimpleDateFormat("d")); info[1] = new PeriodAxisLabelInfo(Year.class, new SimpleDateFormat("yyyy")); a1.setLabelInfo(info); assertNotEquals(a1, a2); a2.setLabelInfo(info); assertEquals(a1, a2); a1.setAutoRangeTimePeriodClass(Second.class); assertNotEquals(a1, a2); a2.setAutoRangeTimePeriodClass(Second.class); assertEquals(a1, a2); a1.setTimeZone(new SimpleTimeZone(123, "Bogus")); assertNotEquals(a1, a2); a2.setTimeZone(new SimpleTimeZone(123, "Bogus")); assertEquals(a1, a2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { PeriodAxis a1 = new PeriodAxis("Test Axis"); PeriodAxis a2 = TestUtils.serialised(a1); boolean b = a1.equals(a2); assertTrue(b); } /** * A test for bug 1932146. */ @Test public void test1932146() { PeriodAxis axis = new PeriodAxis("TestAxis"); axis.addChangeListener(this); this.lastEvent = null; axis.setRange(new DateRange(0L, 1000L)); assertNotNull(this.lastEvent); } private static final double EPSILON = 0.0000000001; /** * A test for the setRange() method (because the axis shows whole time * periods, the range set for the axis will most likely be wider than the * one specified). */ @Test public void test2490803() { Locale savedLocale = Locale.getDefault(); TimeZone savedTimeZone = TimeZone.getDefault(); try { Locale.setDefault(Locale.FRANCE); TimeZone.setDefault(TimeZone.getTimeZone("Europe/Paris")); GregorianCalendar c0 = new GregorianCalendar(); c0.clear(); /* c0.set(2009, Calendar.JANUARY, 16, 12, 34, 56); System.out.println(c0.getTime().getTime()); c0.clear(); c0.set(2009, Calendar.JANUARY, 17, 12, 34, 56); System.out.println(c0.getTime().getTime()); */ PeriodAxis axis = new PeriodAxis("TestAxis"); axis.setRange(new Range(1232105696000L, 1232192096000L), false, false); Range r = axis.getRange(); Day d0 = new Day(16, 1, 2009); Day d1 = new Day(17, 1, 2009); assertEquals(d0.getFirstMillisecond(), r.getLowerBound(), EPSILON); assertEquals(d1.getLastMillisecond() + 1.0, r.getUpperBound(), EPSILON); } finally { TimeZone.setDefault(savedTimeZone); Locale.setDefault(savedLocale); } } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/axis/QuarterDateFormatTest.java000066400000000000000000000105571463604235500311620ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * QuarterDateFormatTest.java * -------------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.util.TimeZone; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link QuarterDateFormat} class. */ public class QuarterDateFormatTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { QuarterDateFormat qf1 = new QuarterDateFormat(TimeZone.getTimeZone( "GMT"), new String[] {"1", "2", "3", "4"}); QuarterDateFormat qf2 = new QuarterDateFormat(TimeZone.getTimeZone( "GMT"), new String[] {"1", "2", "3", "4"}); assertEquals(qf1, qf2); assertEquals(qf2, qf1); qf1 = new QuarterDateFormat(TimeZone.getTimeZone("PST"), new String[] {"1", "2", "3", "4"}); assertNotEquals(qf1, qf2); qf2 = new QuarterDateFormat(TimeZone.getTimeZone("PST"), new String[] {"1", "2", "3", "4"}); assertEquals(qf1, qf2); qf1 = new QuarterDateFormat(TimeZone.getTimeZone("PST"), new String[] {"A", "2", "3", "4"}); assertNotEquals(qf1, qf2); qf2 = new QuarterDateFormat(TimeZone.getTimeZone("PST"), new String[] {"A", "2", "3", "4"}); assertEquals(qf1, qf2); qf1 = new QuarterDateFormat(TimeZone.getTimeZone("PST"), new String[] {"A", "2", "3", "4"}, true); assertNotEquals(qf1, qf2); qf2 = new QuarterDateFormat(TimeZone.getTimeZone("PST"), new String[] {"A", "2", "3", "4"}, true); assertEquals(qf1, qf2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { QuarterDateFormat qf1 = new QuarterDateFormat(TimeZone.getTimeZone( "GMT"), new String[] {"1", "2", "3", "4"}); QuarterDateFormat qf2 = new QuarterDateFormat(TimeZone.getTimeZone( "GMT"), new String[] {"1", "2", "3", "4"}); assertEquals(qf1, qf2); int h1 = qf1.hashCode(); int h2 = qf2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() { QuarterDateFormat qf1 = new QuarterDateFormat(TimeZone.getTimeZone( "GMT"), new String[] {"1", "2", "3", "4"}); QuarterDateFormat qf2 = null; qf2 = (QuarterDateFormat) qf1.clone(); assertNotSame(qf1, qf2); assertSame(qf1.getClass(), qf2.getClass()); assertEquals(qf1, qf2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { QuarterDateFormat qf1 = new QuarterDateFormat(TimeZone.getTimeZone( "GMT"), new String[] {"1", "2", "3", "4"}); QuarterDateFormat qf2 = TestUtils.serialised(qf1); assertEquals(qf1, qf2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/axis/StandardTickUnitSourceTest.java000066400000000000000000000044321463604235500321570ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------- * StandardTickUnitSourceTest.java * ------------------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import static org.junit.jupiter.api.Assertions.assertEquals; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; /** * Tests for the {@link StandardTickUnitSource} class. */ public class StandardTickUnitSourceTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { StandardTickUnitSource t1 = new StandardTickUnitSource(); StandardTickUnitSource t2 = new StandardTickUnitSource(); assertEquals(t1, t2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { StandardTickUnitSource t1 = new StandardTickUnitSource(); StandardTickUnitSource t2 = TestUtils.serialised(t1); assertEquals(t1, t2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/axis/SubCategoryAxisTest.java000066400000000000000000000112521463604235500306350ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * SubCategoryAxisTest.java * ------------------------ * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.TestUtils; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link SubCategoryAxis} class. */ public class SubCategoryAxisTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { SubCategoryAxis a1 = new SubCategoryAxis("Test"); SubCategoryAxis a2 = new SubCategoryAxis("Test"); assertEquals(a1, a2); assertEquals(a2, a1); // subcategories a1.addSubCategory("Sub 1"); assertNotEquals(a1, a2); a2.addSubCategory("Sub 1"); assertEquals(a1, a2); // subLabelFont a1.setSubLabelFont(new Font("Serif", Font.BOLD, 15)); assertNotEquals(a1, a2); a2.setSubLabelFont(new Font("Serif", Font.BOLD, 15)); assertEquals(a1, a2); // subLabelPaint a1.setSubLabelPaint(Color.RED); assertNotEquals(a1, a2); a2.setSubLabelPaint(Color.RED); assertEquals(a1, a2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { SubCategoryAxis a1 = new SubCategoryAxis("Test"); SubCategoryAxis a2 = new SubCategoryAxis("Test"); assertEquals(a1, a2); int h1 = a1.hashCode(); int h2 = a2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { SubCategoryAxis a1 = new SubCategoryAxis("Test"); a1.addSubCategory("SubCategoryA"); SubCategoryAxis a2 = (SubCategoryAxis) a1.clone(); assertNotSame(a1, a2); assertSame(a1.getClass(), a2.getClass()); assertEquals(a1, a2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { SubCategoryAxis a1 = new SubCategoryAxis("Test Axis"); a1.addSubCategory("SubCategoryA"); SubCategoryAxis a2 = TestUtils.serialised(a1); assertEquals(a1, a2); } /** * A check for the NullPointerException in bug 2275695. */ @Test public void test2275695() { JFreeChart chart = ChartFactory.createStackedBarChart("Test", "Category", "Value", null, PlotOrientation.VERTICAL, true, false, false); CategoryPlot plot = (CategoryPlot) chart.getPlot(); plot.setDomainAxis(new SubCategoryAxis("SubCategoryAxis")); try { BufferedImage image = new BufferedImage(200 , 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); chart.draw(g2, new Rectangle2D.Double(0, 0, 200, 100), null, null); g2.dispose(); } catch (Exception e) { fail("There should be no exception."); } } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/axis/SymbolAxisTest.java000066400000000000000000000070071463604235500276560ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * SymbolicAxisTest.java * --------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.awt.Color; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link SymbolAxis} class. */ public class SymbolAxisTest { /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { String[] tickLabels = new String[] {"One", "Two", "Three"}; SymbolAxis a1 = new SymbolAxis("Test Axis", tickLabels); SymbolAxis a2 = TestUtils.serialised(a1); assertEquals(a1, a2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { SymbolAxis a1 = new SymbolAxis("Axis", new String[] {"A", "B"}); SymbolAxis a2 = (SymbolAxis) a1.clone(); assertNotSame(a1, a2); assertSame(a1.getClass(), a2.getClass()); assertEquals(a1, a2); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { SymbolAxis a1 = new SymbolAxis("Axis", new String[] {"A", "B"}); SymbolAxis a2 = new SymbolAxis("Axis", new String[] {"A", "B"}); assertEquals(a1, a2); assertEquals(a2, a1); a1 = new SymbolAxis("Axis 2", new String[] {"A", "B"}); assertNotEquals(a1, a2); a2 = new SymbolAxis("Axis 2", new String[] {"A", "B"}); assertEquals(a1, a2); a1 = new SymbolAxis("Axis 2", new String[] {"C", "B"}); assertNotEquals(a1, a2); a2 = new SymbolAxis("Axis 2", new String[] {"C", "B"}); assertEquals(a1, a2); a1.setGridBandsVisible(false); assertNotEquals(a1, a2); a2.setGridBandsVisible(false); assertEquals(a1, a2); a1.setGridBandPaint(Color.BLACK); assertNotEquals(a1, a2); a2.setGridBandPaint(Color.BLACK); assertEquals(a1, a2); a1.setGridBandAlternatePaint(Color.RED); assertNotEquals(a1, a2); a2.setGridBandAlternatePaint(Color.RED); assertEquals(a1, a2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/axis/TickUnitsTest.java000066400000000000000000000054121463604235500274770ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * TickUnitsTest.java * ------------------ * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.text.DecimalFormat; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link TickUnits} class. */ public class TickUnitsTest { /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { TickUnits t1 = new TickUnits(); t1.add(new NumberTickUnit(10, new DecimalFormat("0.00"))); TickUnits t2 = TestUtils.serialised(t1); assertEquals(t1, t2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { TickUnits t1 = new TickUnits(); t1.add(new NumberTickUnit(10, new DecimalFormat("0.00"))); TickUnits t2 = (TickUnits) t1.clone(); assertNotSame(t1, t2); assertSame(t1.getClass(), t2.getClass()); assertEquals(t1, t2); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { TickUnits t1 = new TickUnits(); t1.add(new NumberTickUnit(10, new DecimalFormat("0.00"))); TickUnits t2 = new TickUnits(); t2.add(new NumberTickUnit(10, new DecimalFormat("0.00"))); assertEquals(t1, t2); assertEquals(t2, t1); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/axis/ValueAxisTest.java000066400000000000000000000173661463604235500274760ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * ValueAxisTest.java * ------------------ * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.axis; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Stroke; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import org.jfree.chart.ChartFactory; import org.jfree.chart.ChartRenderingInfo; import org.jfree.chart.JFreeChart; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.RectangleInsets; import org.jfree.data.Range; import org.jfree.data.category.DefaultCategoryDataset; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link ValueAxis} class. */ public class ValueAxisTest { private static final double EPSILON = 0.000000001; /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { ValueAxis a1 = new NumberAxis("Test"); ValueAxis a2 = (NumberAxis) a1.clone(); assertNotSame(a1, a2); assertSame(a1.getClass(), a2.getClass()); assertEquals(a1, a2); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { NumberAxis a1 = new NumberAxis("Test"); NumberAxis a2 = new NumberAxis("Test"); assertEquals(a1, a2); // axis line visible flag... a1.setAxisLineVisible(false); assertNotEquals(a1, a2); a2.setAxisLineVisible(false); assertEquals(a1, a2); // positiveArrowVisible; a1.setPositiveArrowVisible(true); assertNotEquals(a1, a2); a2.setPositiveArrowVisible(true); assertEquals(a1, a2); // negativeArrowVisible; a1.setNegativeArrowVisible(true); assertNotEquals(a1, a2); a2.setNegativeArrowVisible(true); assertEquals(a1, a2); //private Shape upArrow; //private Shape downArrow; //private Shape leftArrow; //private Shape rightArrow; // axisLinePaint a1.setAxisLinePaint(Color.BLUE); assertNotEquals(a1, a2); a2.setAxisLinePaint(Color.BLUE); assertEquals(a1, a2); // axisLineStroke Stroke stroke = new BasicStroke(2.0f); a1.setAxisLineStroke(stroke); assertNotEquals(a1, a2); a2.setAxisLineStroke(stroke); assertEquals(a1, a2); // inverted a1.setInverted(true); assertNotEquals(a1, a2); a2.setInverted(true); assertEquals(a1, a2); // range a1.setRange(new Range(50.0, 75.0)); assertNotEquals(a1, a2); a2.setRange(new Range(50.0, 75.0)); assertEquals(a1, a2); // autoRange a1.setAutoRange(true); assertNotEquals(a1, a2); a2.setAutoRange(true); assertEquals(a1, a2); // autoRangeMinimumSize a1.setAutoRangeMinimumSize(3.33); assertNotEquals(a1, a2); a2.setAutoRangeMinimumSize(3.33); assertEquals(a1, a2); a1.setDefaultAutoRange(new Range(1.2, 3.4)); assertNotEquals(a1, a2); a2.setDefaultAutoRange(new Range(1.2, 3.4)); assertEquals(a1, a2); // upperMargin a1.setUpperMargin(0.09); assertNotEquals(a1, a2); a2.setUpperMargin(0.09); assertEquals(a1, a2); // lowerMargin a1.setLowerMargin(0.09); assertNotEquals(a1, a2); a2.setLowerMargin(0.09); assertEquals(a1, a2); //private double fixedAutoRange; a1.setFixedAutoRange(50.0); assertNotEquals(a1, a2); a2.setFixedAutoRange(50.0); assertEquals(a1, a2); //private boolean autoTickUnitSelection; a1.setAutoTickUnitSelection(false); assertNotEquals(a1, a2); a2.setAutoTickUnitSelection(false); assertEquals(a1, a2); //private TickUnits standardTickUnits; a1.setStandardTickUnits(NumberAxis.createIntegerTickUnits()); assertNotEquals(a1, a2); a2.setStandardTickUnits(NumberAxis.createIntegerTickUnits()); assertEquals(a1, a2); // verticalTickLabels a1.setVerticalTickLabels(true); assertNotEquals(a1, a2); a2.setVerticalTickLabels(true); assertEquals(a1, a2); //private int autoTickIndex; //protected double reservedForTickLabels; //protected double reservedForAxisLabel; } /** * Tests the the lower and upper margin settings produce the expected * results. */ @Test public void testAxisMargins() { XYSeries series = new XYSeries("S1"); series.add(100.0, 1.1); series.add(200.0, 2.2); XYSeriesCollection dataset = new XYSeriesCollection(series); dataset.setIntervalWidth(0.0); JFreeChart chart = ChartFactory.createScatterPlot("Title", "X", "Y", dataset); ValueAxis domainAxis = ((XYPlot) chart.getPlot()).getDomainAxis(); Range r = domainAxis.getRange(); assertEquals(110.0, r.getLength(), EPSILON); domainAxis.setLowerMargin(0.10); domainAxis.setUpperMargin(0.10); r = domainAxis.getRange(); assertEquals(120.0, r.getLength(), EPSILON); } /** * A test for bug 3555275 (where the fixed axis space is calculated * incorrectly). */ @Test public void test3555275() { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); JFreeChart chart = ChartFactory.createLineChart("Title", "X", "Y", dataset, PlotOrientation.VERTICAL, true, false, false); CategoryPlot plot = (CategoryPlot) chart.getPlot(); plot.setInsets(RectangleInsets.ZERO_INSETS); plot.setAxisOffset(RectangleInsets.ZERO_INSETS); ValueAxis yAxis = plot.getRangeAxis(); yAxis.setFixedDimension(100.0); BufferedImage image = new BufferedImage(500, 300, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); ChartRenderingInfo info = new ChartRenderingInfo(); chart.draw(g2, new Rectangle2D.Double(0, 0, 500, 300), info); g2.dispose(); Rectangle2D rect = info.getPlotInfo().getDataArea(); double x = rect.getMinX(); assertEquals(100.0, x, 1.0); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/axis/package.html000066400000000000000000000002211463604235500263310ustar00rootroot00000000000000 Tests for the axis classes and interfaces. jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/block/000077500000000000000000000000001463604235500242035ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/block/AbstractBlockTest.java000066400000000000000000000124501463604235500304260ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * AbstractBlockTest.java * ---------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.block; import java.awt.Color; import java.awt.geom.Rectangle2D; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.chart.ui.RectangleInsets; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link AbstractBlock} class. */ public class AbstractBlockTest{ /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashcode() { EqualsVerifier.forClass(AbstractBlock.class) // Add prefab values for java.awt.geom.Rectangle2D. .withPrefabValues(Rectangle2D.class, new Rectangle2D.Double(0, 0, 1, 1), new Rectangle2D.Double(1, 1, 2, 2)) .withRedefinedSubclass(BlockContainer.class) // subclass(es) also define equals/hashCode .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Confirm that the equals() method can distinguish all the required fields. */ @Test public void testEquals() { EmptyBlock b1 = new EmptyBlock(1.0, 2.0); EmptyBlock b2 = new EmptyBlock(1.0, 2.0); assertEquals(b1, b2); assertEquals(b2, b2); b1.setID("Test"); assertNotEquals(b1, b2); b2.setID("Test"); assertEquals(b1, b2); b1.setMargin(new RectangleInsets(1.0, 2.0, 3.0, 4.0)); assertNotEquals(b1, b2); b2.setMargin(new RectangleInsets(1.0, 2.0, 3.0, 4.0)); assertEquals(b1, b2); b1.setFrame(new BlockBorder(Color.RED)); assertNotEquals(b1, b2); b2.setFrame(new BlockBorder(Color.RED)); assertEquals(b1, b2); b1.setPadding(new RectangleInsets(2.0, 4.0, 6.0, 8.0)); assertNotEquals(b1, b2); b2.setPadding(new RectangleInsets(2.0, 4.0, 6.0, 8.0)); assertEquals(b1, b2); b1.setWidth(1.23); assertNotEquals(b1, b2); b2.setWidth(1.23); assertEquals(b1, b2); b1.setHeight(4.56); assertNotEquals(b1, b2); b2.setHeight(4.56); assertEquals(b1, b2); b1.setBounds(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0)); assertNotEquals(b1, b2); b2.setBounds(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0)); assertEquals(b1, b2); b1 = new EmptyBlock(1.1, 2.0); assertNotEquals(b1, b2); b2 = new EmptyBlock(1.1, 2.0); assertEquals(b1, b2); b1 = new EmptyBlock(1.1, 2.2); assertNotEquals(b1, b2); b2 = new EmptyBlock(1.1, 2.2); assertEquals(b1, b2); } /** * Confirm that cloning works. */ @Test public void testCloning() { EmptyBlock b1 = new EmptyBlock(1.0, 2.0); Rectangle2D bounds1 = new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0); b1.setBounds(bounds1); EmptyBlock b2 = null; try { b2 = (EmptyBlock) b1.clone(); } catch (CloneNotSupportedException e) { fail(e.toString()); } assertNotSame(b1, b2); assertSame(b1.getClass(), b2.getClass()); assertEquals(b1, b2); // transient field 'bounds' not included in equals or hashCode, so test // using the 'get' method bounds1.setFrame(2.0, 4.0, 6.0, 8.0); assertNotEquals(b1.getBounds(), b2.getBounds()); b2.setBounds(new Rectangle2D.Double(2.0, 4.0, 6.0, 8.0)); assertEquals(b1.getBounds(), b2.getBounds()); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { EmptyBlock b1 = new EmptyBlock(1.0, 2.0); EmptyBlock b2 = TestUtils.serialised(b1); assertEquals(b1, b2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/block/BlockBorderTest.java000066400000000000000000000075221463604235500301040ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * BlockBorderTest.java * -------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.block; import java.awt.Color; import java.awt.GradientPaint; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.util.UnitType; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link BlockBorder} class. */ public class BlockBorderTest { /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashcode() { EqualsVerifier.forClass(BlockBorder.class) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Confirm that the equals() method can distinguish all the required fields. */ @Test public void testEquals() { BlockBorder b1 = new BlockBorder(new RectangleInsets(1.0, 2.0, 3.0, 4.0), Color.RED); BlockBorder b2 = new BlockBorder(new RectangleInsets(1.0, 2.0, 3.0, 4.0), Color.RED); assertEquals(b1, b2); assertEquals(b2, b2); // insets b1 = new BlockBorder(new RectangleInsets(UnitType.RELATIVE, 1.0, 2.0, 3.0, 4.0), Color.RED); assertNotEquals(b1, b2); b2 = new BlockBorder(new RectangleInsets(UnitType.RELATIVE, 1.0, 2.0, 3.0, 4.0), Color.RED); assertEquals(b1, b2); // paint b1 = new BlockBorder(new RectangleInsets(1.0, 2.0, 3.0, 4.0), Color.BLUE); assertNotEquals(b1, b2); b2 = new BlockBorder(new RectangleInsets(1.0, 2.0, 3.0, 4.0), Color.BLUE); assertEquals(b1, b2); } /** * Immutable - cloning not necessary. */ @Test public void testCloning() { BlockBorder b1 = new BlockBorder(); assertFalse(b1 instanceof Cloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { BlockBorder b1 = new BlockBorder(new RectangleInsets(1.0, 2.0, 3.0, 4.0), new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW)); BlockBorder b2 = TestUtils.serialised(b1); assertEquals(b1, b2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/block/BlockContainerTest.java000066400000000000000000000075441463604235500306150ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * BlockContainerTest.java * ----------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.block; import java.awt.geom.Rectangle2D; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link BlockContainer} class. */ public class BlockContainerTest { /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(BlockContainer.class) .withRedefinedSuperclass() // superclass also defines equals/hashCode .withPrefabValues(Rectangle2D.class, TestUtils.createR2D(true), TestUtils.createR2D(false)) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Confirm that the equals() method can distinguish all the required fields. */ @Test public void testEquals() { BlockContainer c1 = new BlockContainer(new FlowArrangement()); BlockContainer c2 = new BlockContainer(new FlowArrangement()); assertEquals(c1, c2); assertEquals(c2, c2); c1.setArrangement(new ColumnArrangement()); assertNotEquals(c1, c2); c2.setArrangement(new ColumnArrangement()); assertEquals(c1, c2); c1.add(new EmptyBlock(1.2, 3.4)); assertNotEquals(c1, c2); c2.add(new EmptyBlock(1.2, 3.4)); assertEquals(c1, c2); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException if there is a problem cloning. */ @Test public void testCloning() throws CloneNotSupportedException { BlockContainer c1 = new BlockContainer(new FlowArrangement()); c1.add(new EmptyBlock(1.2, 3.4)); BlockContainer c2 = (BlockContainer) c1.clone(); assertNotSame(c1, c2); assertSame(c1.getClass(), c2.getClass()); assertEquals(c1, c2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { BlockContainer c1 = new BlockContainer(); c1.add(new EmptyBlock(1.2, 3.4)); BlockContainer c2 = TestUtils.serialised(c1); assertEquals(c1, c2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/block/BorderArrangementTest.java000066400000000000000000000747421463604235500313250ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * BorderArrangementTest.java * -------------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.block; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.Size2D; import org.jfree.data.Range; import org.junit.jupiter.api.Test; /** * Tests for the {@link BorderArrangement} class. */ public class BorderArrangementTest { private static final double EPSILON = 0.0000000001; /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(BorderArrangement.class) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Immutable - cloning is not necessary. */ @Test public void testCloning() { BorderArrangement b1 = new BorderArrangement(); assertFalse(b1 instanceof Cloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { BorderArrangement b1 = new BorderArrangement(); BorderArrangement b2 = TestUtils.serialised(b1); assertEquals(b1, b2); } /** * Run some checks on sizing. */ @Test public void testSizing() { BlockContainer container = new BlockContainer(new BorderArrangement()); BufferedImage image = new BufferedImage(200, 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); // TBLRC // 00000 - no items Size2D size = container.arrange(g2); assertEquals(0.0, size.width, EPSILON); assertEquals(0.0, size.height, EPSILON); // TBLRC // 00001 - center item only container.add(new EmptyBlock(123.4, 567.8)); size = container.arrange(g2); assertEquals(123.4, size.width, EPSILON); assertEquals(567.8, size.height, EPSILON); // TBLRC // 00010 - right item only container.clear(); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.RIGHT); size = container.arrange(g2); assertEquals(12.3, size.width, EPSILON); assertEquals(45.6, size.height, EPSILON); // TBLRC // 00011 - right and center items container.clear(); container.add(new EmptyBlock(10.0, 20.0)); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.RIGHT); size = container.arrange(g2); assertEquals(22.3, size.width, EPSILON); assertEquals(45.6, size.height, EPSILON); // try case where right item is shorter than center item container.clear(); Block rb = new EmptyBlock(12.3, 15.6); container.add(new EmptyBlock(10.0, 20.0)); container.add(rb, RectangleEdge.RIGHT); size = container.arrange(g2); assertEquals(22.3, size.width, EPSILON); assertEquals(20.0, size.height, EPSILON); assertEquals(20.0, rb.getBounds().getHeight(), EPSILON); // TBLRC // 00100 - left item only container.clear(); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.LEFT); size = container.arrange(g2); assertEquals(12.3, size.width, EPSILON); assertEquals(45.6, size.height, EPSILON); // TBLRC // 00101 - left and center items container.clear(); container.add(new EmptyBlock(10.0, 20.0)); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.LEFT); size = container.arrange(g2); assertEquals(22.3, size.width, EPSILON); assertEquals(45.6, size.height, EPSILON); // try case where left item is shorter than center item container.clear(); Block lb = new EmptyBlock(12.3, 15.6); container.add(new EmptyBlock(10.0, 20.0)); container.add(lb, RectangleEdge.LEFT); size = container.arrange(g2); assertEquals(22.3, size.width, EPSILON); assertEquals(20.0, size.height, EPSILON); assertEquals(20.0, lb.getBounds().getHeight(), EPSILON); // TBLRC // 00110 - left and right items container.clear(); container.add(new EmptyBlock(10.0, 20.0), RectangleEdge.RIGHT); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.LEFT); size = container.arrange(g2); assertEquals(22.3, size.width, EPSILON); assertEquals(45.6, size.height, EPSILON); // TBLRC // 00111 - left, right and center items container.clear(); container.add(new EmptyBlock(10.0, 20.0)); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.LEFT); container.add(new EmptyBlock(5.4, 3.2), RectangleEdge.RIGHT); size = container.arrange(g2); assertEquals(27.7, size.width, EPSILON); assertEquals(45.6, size.height, EPSILON); // TBLRC // 01000 - bottom item only container.clear(); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.BOTTOM); size = container.arrange(g2); assertEquals(12.3, size.width, EPSILON); assertEquals(45.6, size.height, EPSILON); // TBLRC // 01001 - bottom and center only container.clear(); container.add(new EmptyBlock(10.0, 20.0)); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.BOTTOM); size = container.arrange(g2); assertEquals(12.3, size.width, EPSILON); assertEquals(65.6, size.height, EPSILON); // TBLRC // 01010 - bottom and right only container.clear(); container.add(new EmptyBlock(10.0, 20.0), RectangleEdge.RIGHT); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.BOTTOM); size = container.arrange(g2); assertEquals(12.3, size.width, EPSILON); assertEquals(65.6, size.height, EPSILON); // TBLRC // 01011 - bottom, right and center container.clear(); container.add(new EmptyBlock(21.0, 12.3)); container.add(new EmptyBlock(10.0, 20.0), RectangleEdge.RIGHT); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.BOTTOM); size = container.arrange(g2); assertEquals(31.0, size.width, EPSILON); assertEquals(65.6, size.height, EPSILON); // TBLRC // 01100 container.clear(); container.add(new EmptyBlock(10.0, 20.0), RectangleEdge.LEFT); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.BOTTOM); size = container.arrange(g2); assertEquals(12.3, size.width, EPSILON); assertEquals(65.6, size.height, EPSILON); // TBLRC // 01101 - bottom, left and center container.clear(); container.add(new EmptyBlock(21.0, 12.3)); container.add(new EmptyBlock(10.0, 20.0), RectangleEdge.LEFT); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.BOTTOM); size = container.arrange(g2); assertEquals(31.0, size.width, EPSILON); assertEquals(65.6, size.height, EPSILON); // TBLRC // 01110 - bottom. left and right container.clear(); container.add(new EmptyBlock(21.0, 12.3), RectangleEdge.RIGHT); container.add(new EmptyBlock(10.0, 20.0), RectangleEdge.LEFT); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.BOTTOM); size = container.arrange(g2); assertEquals(31.0, size.width, EPSILON); assertEquals(65.6, size.height, EPSILON); // TBLRC // 01111 container.clear(); container.add(new EmptyBlock(3.0, 4.0), RectangleEdge.BOTTOM); container.add(new EmptyBlock(5.0, 6.0), RectangleEdge.LEFT); container.add(new EmptyBlock(7.0, 8.0), RectangleEdge.RIGHT); container.add(new EmptyBlock(9.0, 10.0)); size = container.arrange(g2); assertEquals(21.0, size.width, EPSILON); assertEquals(14.0, size.height, EPSILON); // TBLRC // 10000 - top item only container.clear(); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.TOP); size = container.arrange(g2); assertEquals(12.3, size.width, EPSILON); assertEquals(45.6, size.height, EPSILON); // TBLRC // 10001 - top and center only container.clear(); container.add(new EmptyBlock(10.0, 20.0)); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.TOP); size = container.arrange(g2); assertEquals(12.3, size.width, EPSILON); assertEquals(65.6, size.height, EPSILON); // TBLRC // 10010 - right and top only container.clear(); container.add(new EmptyBlock(10.0, 20.0), RectangleEdge.RIGHT); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.TOP); size = container.arrange(g2); assertEquals(12.3, size.width, EPSILON); assertEquals(65.6, size.height, EPSILON); // TBLRC // 10011 - top, right and center container.clear(); container.add(new EmptyBlock(21.0, 12.3)); container.add(new EmptyBlock(10.0, 20.0), RectangleEdge.TOP); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.RIGHT); size = container.arrange(g2); assertEquals(33.3, size.width, EPSILON); assertEquals(65.6, size.height, EPSILON); // TBLRC // 10100 - top and left only container.clear(); container.add(new EmptyBlock(10.0, 20.0), RectangleEdge.LEFT); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.TOP); size = container.arrange(g2); assertEquals(12.3, size.width, EPSILON); assertEquals(65.6, size.height, EPSILON); // TBLRC // 10101 - top, left and center container.clear(); container.add(new EmptyBlock(21.0, 12.3)); container.add(new EmptyBlock(10.0, 20.0), RectangleEdge.TOP); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.LEFT); size = container.arrange(g2); assertEquals(33.3, size.width, EPSILON); assertEquals(65.6, size.height, EPSILON); // TBLRC // 10110 - top, left and right container.clear(); container.add(new EmptyBlock(21.0, 12.3), RectangleEdge.RIGHT); container.add(new EmptyBlock(10.0, 20.0), RectangleEdge.TOP); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.LEFT); size = container.arrange(g2); assertEquals(33.3, size.width, EPSILON); assertEquals(65.6, size.height, EPSILON); // TBLRC // 10111 container.clear(); container.add(new EmptyBlock(1.0, 2.0), RectangleEdge.TOP); container.add(new EmptyBlock(5.0, 6.0), RectangleEdge.LEFT); container.add(new EmptyBlock(7.0, 8.0), RectangleEdge.RIGHT); container.add(new EmptyBlock(9.0, 10.0)); size = container.arrange(g2); assertEquals(21.0, size.width, EPSILON); assertEquals(12.0, size.height, EPSILON); // TBLRC // 11000 - top and bottom only container.clear(); container.add(new EmptyBlock(10.0, 20.0), RectangleEdge.TOP); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.BOTTOM); size = container.arrange(g2); assertEquals(12.3, size.width, EPSILON); assertEquals(65.6, size.height, EPSILON); // TBLRC // 11001 container.clear(); container.add(new EmptyBlock(21.0, 12.3)); container.add(new EmptyBlock(10.0, 20.0), RectangleEdge.TOP); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.BOTTOM); size = container.arrange(g2); assertEquals(21.0, size.width, EPSILON); assertEquals(77.9, size.height, EPSILON); // TBLRC // 11010 - top, bottom and right container.clear(); container.add(new EmptyBlock(21.0, 12.3), RectangleEdge.RIGHT); container.add(new EmptyBlock(10.0, 20.0), RectangleEdge.TOP); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.BOTTOM); size = container.arrange(g2); assertEquals(21.0, size.width, EPSILON); assertEquals(77.9, size.height, EPSILON); // TBLRC // 11011 container.clear(); container.add(new EmptyBlock(1.0, 2.0), RectangleEdge.TOP); container.add(new EmptyBlock(3.0, 4.0), RectangleEdge.BOTTOM); container.add(new EmptyBlock(7.0, 8.0), RectangleEdge.RIGHT); container.add(new EmptyBlock(9.0, 10.0)); size = container.arrange(g2); assertEquals(16.0, size.width, EPSILON); assertEquals(16.0, size.height, EPSILON); // TBLRC // 11100 container.clear(); container.add(new EmptyBlock(21.0, 12.3), RectangleEdge.LEFT); container.add(new EmptyBlock(10.0, 20.0), RectangleEdge.TOP); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.BOTTOM); size = container.arrange(g2); assertEquals(21.0, size.width, EPSILON); assertEquals(77.9, size.height, EPSILON); // TBLRC // 11101 container.clear(); container.add(new EmptyBlock(1.0, 2.0), RectangleEdge.TOP); container.add(new EmptyBlock(3.0, 4.0), RectangleEdge.BOTTOM); container.add(new EmptyBlock(5.0, 6.0), RectangleEdge.LEFT); container.add(new EmptyBlock(9.0, 10.0)); size = container.arrange(g2); assertEquals(14.0, size.width, EPSILON); assertEquals(16.0, size.height, EPSILON); // TBLRC // 11110 container.clear(); container.add(new EmptyBlock(1.0, 2.0), RectangleEdge.TOP); container.add(new EmptyBlock(3.0, 4.0), RectangleEdge.BOTTOM); container.add(new EmptyBlock(5.0, 6.0), RectangleEdge.LEFT); container.add(new EmptyBlock(7.0, 8.0), RectangleEdge.RIGHT); size = container.arrange(g2); assertEquals(12.0, size.width, EPSILON); assertEquals(14.0, size.height, EPSILON); // TBLRC // 11111 - all container.clear(); container.add(new EmptyBlock(1.0, 2.0), RectangleEdge.TOP); container.add(new EmptyBlock(3.0, 4.0), RectangleEdge.BOTTOM); container.add(new EmptyBlock(5.0, 6.0), RectangleEdge.LEFT); container.add(new EmptyBlock(7.0, 8.0), RectangleEdge.RIGHT); container.add(new EmptyBlock(9.0, 10.0)); size = container.arrange(g2); assertEquals(21.0, size.width, EPSILON); assertEquals(16.0, size.height, EPSILON); } /** * Run some checks on sizing when there is a fixed width constraint. */ @Test public void testSizingWithWidthConstraint() { RectangleConstraint constraint = new RectangleConstraint( 10.0, new Range(10.0, 10.0), LengthConstraintType.FIXED, 0.0, new Range(0.0, 0.0), LengthConstraintType.NONE); BlockContainer container = new BlockContainer(new BorderArrangement()); BufferedImage image = new BufferedImage(200, 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); // TBLRC // 00001 - center item only container.add(new EmptyBlock(5.0, 6.0)); Size2D size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(6.0, size.height, EPSILON); container.clear(); container.add(new EmptyBlock(15.0, 16.0)); size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(16.0, size.height, EPSILON); // TBLRC // 00010 - right item only container.clear(); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.RIGHT); size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(45.6, size.height, EPSILON); // TBLRC // 00011 - right and center items container.clear(); container.add(new EmptyBlock(7.0, 20.0)); container.add(new EmptyBlock(8.0, 45.6), RectangleEdge.RIGHT); size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(45.6, size.height, EPSILON); // TBLRC // 00100 - left item only container.clear(); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.LEFT); size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(45.6, size.height, EPSILON); // TBLRC // 00101 - left and center items container.clear(); container.add(new EmptyBlock(10.0, 20.0)); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.LEFT); size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(45.6, size.height, EPSILON); // TBLRC // 00110 - left and right items container.clear(); container.add(new EmptyBlock(10.0, 20.0), RectangleEdge.RIGHT); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.LEFT); size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(45.6, size.height, EPSILON); // TBLRC // 00111 - left, right and center items container.clear(); container.add(new EmptyBlock(10.0, 20.0)); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.LEFT); container.add(new EmptyBlock(5.4, 3.2), RectangleEdge.RIGHT); size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(45.6, size.height, EPSILON); // TBLRC // 01000 - bottom item only container.clear(); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.BOTTOM); size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(45.6, size.height, EPSILON); // TBLRC // 01001 - bottom and center only container.clear(); container.add(new EmptyBlock(10.0, 20.0)); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.BOTTOM); size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(65.6, size.height, EPSILON); // TBLRC // 01010 - bottom and right only container.clear(); container.add(new EmptyBlock(10.0, 20.0), RectangleEdge.RIGHT); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.BOTTOM); size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(65.6, size.height, EPSILON); // TBLRC // 01011 - bottom, right and center container.clear(); container.add(new EmptyBlock(21.0, 12.3)); container.add(new EmptyBlock(10.0, 20.0), RectangleEdge.RIGHT); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.BOTTOM); size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(65.6, size.height, EPSILON); // TBLRC // 01100 container.clear(); container.add(new EmptyBlock(10.0, 20.0), RectangleEdge.LEFT); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.BOTTOM); size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(65.6, size.height, EPSILON); // TBLRC // 01101 - bottom, left and center container.clear(); container.add(new EmptyBlock(21.0, 12.3)); container.add(new EmptyBlock(10.0, 20.0), RectangleEdge.LEFT); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.BOTTOM); size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(65.6, size.height, EPSILON); // TBLRC // 01110 - bottom. left and right container.clear(); container.add(new EmptyBlock(21.0, 12.3), RectangleEdge.RIGHT); container.add(new EmptyBlock(10.0, 20.0), RectangleEdge.LEFT); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.BOTTOM); size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(65.6, size.height, EPSILON); // TBLRC // 01111 container.clear(); container.add(new EmptyBlock(3.0, 4.0), RectangleEdge.BOTTOM); container.add(new EmptyBlock(5.0, 6.0), RectangleEdge.LEFT); container.add(new EmptyBlock(7.0, 8.0), RectangleEdge.RIGHT); container.add(new EmptyBlock(9.0, 10.0)); size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(14.0, size.height, EPSILON); // TBLRC // 10000 - top item only container.clear(); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.TOP); size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(45.6, size.height, EPSILON); // TBLRC // 10001 - top and center only container.clear(); container.add(new EmptyBlock(10.0, 20.0)); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.TOP); size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(65.6, size.height, EPSILON); // TBLRC // 10010 - right and top only container.clear(); container.add(new EmptyBlock(10.0, 20.0), RectangleEdge.RIGHT); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.TOP); size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(65.6, size.height, EPSILON); // TBLRC // 10011 - top, right and center container.clear(); container.add(new EmptyBlock(21.0, 12.3)); container.add(new EmptyBlock(10.0, 20.0), RectangleEdge.TOP); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.RIGHT); size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(65.6, size.height, EPSILON); // TBLRC // 10100 - top and left only container.clear(); container.add(new EmptyBlock(10.0, 20.0), RectangleEdge.LEFT); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.TOP); size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(65.6, size.height, EPSILON); // TBLRC // 10101 - top, left and center container.clear(); container.add(new EmptyBlock(21.0, 12.3)); container.add(new EmptyBlock(10.0, 20.0), RectangleEdge.TOP); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.LEFT); size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(65.6, size.height, EPSILON); // TBLRC // 10110 - top, left and right container.clear(); container.add(new EmptyBlock(21.0, 12.3), RectangleEdge.RIGHT); container.add(new EmptyBlock(10.0, 20.0), RectangleEdge.TOP); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.LEFT); size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(65.6, size.height, EPSILON); // TBLRC // 10111 container.clear(); container.add(new EmptyBlock(1.0, 2.0), RectangleEdge.TOP); container.add(new EmptyBlock(5.0, 6.0), RectangleEdge.LEFT); container.add(new EmptyBlock(7.0, 8.0), RectangleEdge.RIGHT); container.add(new EmptyBlock(9.0, 10.0)); size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(12.0, size.height, EPSILON); // TBLRC // 11000 - top and bottom only container.clear(); container.add(new EmptyBlock(10.0, 20.0), RectangleEdge.TOP); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.BOTTOM); size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(65.6, size.height, EPSILON); // TBLRC // 11001 container.clear(); container.add(new EmptyBlock(21.0, 12.3)); container.add(new EmptyBlock(10.0, 20.0), RectangleEdge.TOP); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.BOTTOM); size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(77.9, size.height, EPSILON); // TBLRC // 11010 - top, bottom and right container.clear(); container.add(new EmptyBlock(21.0, 12.3), RectangleEdge.RIGHT); container.add(new EmptyBlock(10.0, 20.0), RectangleEdge.TOP); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.BOTTOM); size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(77.9, size.height, EPSILON); // TBLRC // 11011 container.clear(); container.add(new EmptyBlock(1.0, 2.0), RectangleEdge.TOP); container.add(new EmptyBlock(3.0, 4.0), RectangleEdge.BOTTOM); container.add(new EmptyBlock(7.0, 8.0), RectangleEdge.RIGHT); container.add(new EmptyBlock(9.0, 10.0)); size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(16.0, size.height, EPSILON); // TBLRC // 11100 container.clear(); container.add(new EmptyBlock(21.0, 12.3), RectangleEdge.LEFT); container.add(new EmptyBlock(10.0, 20.0), RectangleEdge.TOP); container.add(new EmptyBlock(12.3, 45.6), RectangleEdge.BOTTOM); size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(77.9, size.height, EPSILON); // TBLRC // 11101 container.clear(); container.add(new EmptyBlock(1.0, 2.0), RectangleEdge.TOP); container.add(new EmptyBlock(3.0, 4.0), RectangleEdge.BOTTOM); container.add(new EmptyBlock(5.0, 6.0), RectangleEdge.LEFT); container.add(new EmptyBlock(9.0, 10.0)); size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(16.0, size.height, EPSILON); // TBLRC // 11110 container.clear(); container.add(new EmptyBlock(1.0, 2.0), RectangleEdge.TOP); container.add(new EmptyBlock(3.0, 4.0), RectangleEdge.BOTTOM); container.add(new EmptyBlock(5.0, 6.0), RectangleEdge.LEFT); container.add(new EmptyBlock(7.0, 8.0), RectangleEdge.RIGHT); size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(14.0, size.height, EPSILON); // TBLRC // 11111 - all container.clear(); container.add(new EmptyBlock(1.0, 2.0), RectangleEdge.TOP); container.add(new EmptyBlock(3.0, 4.0), RectangleEdge.BOTTOM); container.add(new EmptyBlock(5.0, 6.0), RectangleEdge.LEFT); container.add(new EmptyBlock(7.0, 8.0), RectangleEdge.RIGHT); container.add(new EmptyBlock(9.0, 10.0)); size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(16.0, size.height, EPSILON); // TBLRC // 00000 - no items container.clear(); size = container.arrange(g2, constraint); assertEquals(10.0, size.width, EPSILON); assertEquals(0.0, size.height, EPSILON); } /** * This test is for a particular bug that arose just prior to the release * of JFreeChart 1.0.10. A BorderArrangement with LEFT, CENTRE and RIGHT * blocks that is too wide, by default, for the available space, wasn't * shrinking the centre block as expected. */ @Test public void testBugX() { RectangleConstraint constraint = new RectangleConstraint( new Range(0.0, 200.0), new Range(0.0, 100.0)); BlockContainer container = new BlockContainer(new BorderArrangement()); BufferedImage image = new BufferedImage(200, 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); container.add(new EmptyBlock(10.0, 6.0), RectangleEdge.LEFT); container.add(new EmptyBlock(20.0, 6.0), RectangleEdge.RIGHT); container.add(new EmptyBlock(30.0, 6.0)); Size2D size = container.arrange(g2, constraint); assertEquals(60.0, size.width, EPSILON); assertEquals(6.0, size.height, EPSILON); container.clear(); container.add(new EmptyBlock(10.0, 6.0), RectangleEdge.LEFT); container.add(new EmptyBlock(20.0, 6.0), RectangleEdge.RIGHT); container.add(new EmptyBlock(300.0, 6.0)); size = container.arrange(g2, constraint); assertEquals(200.0, size.width, EPSILON); assertEquals(6.0, size.height, EPSILON); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/block/ColorBlockTest.java000066400000000000000000000105521463604235500277420ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * ColorBlockTest.java * ------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.block; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import java.awt.*; import java.awt.geom.Rectangle2D; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link ColorBlock} class. */ public class ColorBlockTest { /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(ColorBlock.class) .withPrefabValues(Rectangle2D.class, TestUtils.createR2D(true), TestUtils.createR2D(false)) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Confirm that the equals() method can distinguish all the required fields. */ @Test public void testEquals() { ColorBlock b1 = new ColorBlock(Color.RED, 1.0, 2.0); ColorBlock b2 = new ColorBlock(Color.RED, 1.0, 2.0); assertEquals(b1, b2); assertEquals(b2, b2); b1 = new ColorBlock(Color.BLUE, 1.0, 2.0); assertNotEquals(b1, b2); b2 = new ColorBlock(Color.BLUE, 1.0, 2.0); assertEquals(b1, b2); b1 = new ColorBlock(Color.BLUE, 1.1, 2.0); assertNotEquals(b1, b2); b2 = new ColorBlock(Color.BLUE, 1.1, 2.0); assertEquals(b1, b2); b1 = new ColorBlock(Color.BLUE, 1.1, 2.2); assertNotEquals(b1, b2); b2 = new ColorBlock(Color.BLUE, 1.1, 2.2); assertEquals(b1, b2); } /** * Confirm that cloning works. */ @Test public void testCloning() { GradientPaint gp = new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE); Rectangle2D bounds1 = new Rectangle2D.Double(10.0, 20.0, 30.0, 40.0); ColorBlock b1 = new ColorBlock(gp, 1.0, 2.0); b1.setBounds(bounds1); ColorBlock b2 = null; try { b2 = (ColorBlock) b1.clone(); } catch (CloneNotSupportedException e) { fail(e.toString()); } assertNotSame(b1, b2); assertSame(b1.getClass(), b2.getClass()); assertEquals(b1, b2); // check independence bounds1.setRect(1.0, 2.0, 3.0, 4.0); assertNotEquals(b1, b2); b2.setBounds(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0)); assertEquals(b1, b2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { GradientPaint gp = new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE); ColorBlock b1 = new ColorBlock(gp, 1.0, 2.0); ColorBlock b2 = TestUtils.serialised(b1); assertEquals(b1, b2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/block/ColumnArrangementTest.java000066400000000000000000000106051463604235500313310ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * ColumnArrangementTest.java * -------------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.block; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.chart.ui.HorizontalAlignment; import org.jfree.chart.ui.VerticalAlignment; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link ColumnArrangement} class. */ public class ColumnArrangementTest { /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(ColumnArrangement.class) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Confirm that the equals() method can distinguish all the required fields. */ @Test public void testEquals() { ColumnArrangement c1 = new ColumnArrangement(HorizontalAlignment.LEFT, VerticalAlignment.TOP, 1.0, 2.0); ColumnArrangement c2 = new ColumnArrangement(HorizontalAlignment.LEFT, VerticalAlignment.TOP, 1.0, 2.0); assertEquals(c1, c2); assertEquals(c2, c1); c1 = new ColumnArrangement(HorizontalAlignment.RIGHT, VerticalAlignment.TOP, 1.0, 2.0); assertNotEquals(c1, c2); c2 = new ColumnArrangement(HorizontalAlignment.RIGHT, VerticalAlignment.TOP, 1.0, 2.0); assertEquals(c1, c2); c1 = new ColumnArrangement(HorizontalAlignment.RIGHT, VerticalAlignment.BOTTOM, 1.0, 2.0); assertNotEquals(c1, c2); c2 = new ColumnArrangement(HorizontalAlignment.RIGHT, VerticalAlignment.BOTTOM, 1.0, 2.0); assertEquals(c1, c2); c1 = new ColumnArrangement(HorizontalAlignment.RIGHT, VerticalAlignment.BOTTOM, 1.1, 2.0); assertNotEquals(c1, c2); c2 = new ColumnArrangement(HorizontalAlignment.RIGHT, VerticalAlignment.BOTTOM, 1.1, 2.0); assertEquals(c1, c2); c1 = new ColumnArrangement(HorizontalAlignment.RIGHT, VerticalAlignment.BOTTOM, 1.1, 2.2); assertNotEquals(c1, c2); c2 = new ColumnArrangement(HorizontalAlignment.RIGHT, VerticalAlignment.BOTTOM, 1.1, 2.2); assertEquals(c1, c2); } /** * Immutable - cloning is not necessary. */ @Test public void testCloning() { FlowArrangement f1 = new FlowArrangement(); assertFalse(f1 instanceof Cloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { FlowArrangement f1 = new FlowArrangement(HorizontalAlignment.LEFT, VerticalAlignment.TOP, 1.0, 2.0); FlowArrangement f2 = TestUtils.serialised(f1); assertEquals(f1, f2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/block/EmptyBlockTest.java000066400000000000000000000071171463604235500277650ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * EmptyBlockTest.java * ------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.block; import java.awt.geom.Rectangle2D; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link EmptyBlock} class. */ public class EmptyBlockTest { /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(EmptyBlock.class) .withPrefabValues(Rectangle2D.class, TestUtils.createR2D(true), TestUtils.createR2D(false)) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Confirm that the equals() method can distinguish all the required fields. */ @Test public void testEquals() { EmptyBlock b1 = new EmptyBlock(1.0, 2.0); EmptyBlock b2 = new EmptyBlock(1.0, 2.0); assertEquals(b1, b2); assertEquals(b2, b2); b1 = new EmptyBlock(1.1, 2.0); assertNotEquals(b1, b2); b2 = new EmptyBlock(1.1, 2.0); assertEquals(b1, b2); b1 = new EmptyBlock(1.1, 2.2); assertNotEquals(b1, b2); b2 = new EmptyBlock(1.1, 2.2); assertEquals(b1, b2); } /** * Confirm that cloning works. */ @Test public void testCloning() { EmptyBlock b1 = new EmptyBlock(1.0, 2.0); EmptyBlock b2 = null; try { b2 = (EmptyBlock) b1.clone(); } catch (CloneNotSupportedException e) { fail(e.toString()); } assertNotSame(b1, b2); assertSame(b1.getClass(), b2.getClass()); assertEquals(b1, b2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { EmptyBlock b1 = new EmptyBlock(1.0, 2.0); EmptyBlock b2 = TestUtils.serialised(b1); assertEquals(b1, b2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/block/FlowArrangementTest.java000066400000000000000000000105421463604235500310030ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * FlowArrangementTest.java * ------------------------ * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.block; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.chart.ui.HorizontalAlignment; import org.jfree.chart.ui.VerticalAlignment; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link FlowArrangement} class. */ public class FlowArrangementTest { /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(FlowArrangement.class) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Confirm that the equals() method can distinguish all the required fields. */ @Test public void testEquals() { FlowArrangement f1 = new FlowArrangement(HorizontalAlignment.LEFT, VerticalAlignment.TOP, 1.0, 2.0); FlowArrangement f2 = new FlowArrangement(HorizontalAlignment.LEFT, VerticalAlignment.TOP, 1.0, 2.0); assertEquals(f1, f2); assertEquals(f2, f1); f1 = new FlowArrangement(HorizontalAlignment.RIGHT, VerticalAlignment.TOP, 1.0, 2.0); assertNotEquals(f1, f2); f2 = new FlowArrangement(HorizontalAlignment.RIGHT, VerticalAlignment.TOP, 1.0, 2.0); assertEquals(f1, f2); f1 = new FlowArrangement(HorizontalAlignment.RIGHT, VerticalAlignment.BOTTOM, 1.0, 2.0); assertNotEquals(f1, f2); f2 = new FlowArrangement(HorizontalAlignment.RIGHT, VerticalAlignment.BOTTOM, 1.0, 2.0); assertEquals(f1, f2); f1 = new FlowArrangement(HorizontalAlignment.RIGHT, VerticalAlignment.BOTTOM, 1.1, 2.0); assertNotEquals(f1, f2); f2 = new FlowArrangement(HorizontalAlignment.RIGHT, VerticalAlignment.BOTTOM, 1.1, 2.0); assertEquals(f1, f2); f1 = new FlowArrangement(HorizontalAlignment.RIGHT, VerticalAlignment.BOTTOM, 1.1, 2.2); assertNotEquals(f1, f2); f2 = new FlowArrangement(HorizontalAlignment.RIGHT, VerticalAlignment.BOTTOM, 1.1, 2.2); assertEquals(f1, f2); } /** * Immutable - cloning is not necessary. */ @Test public void testCloning() { FlowArrangement f1 = new FlowArrangement(); assertFalse(f1 instanceof Cloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { FlowArrangement f1 = new FlowArrangement(HorizontalAlignment.LEFT, VerticalAlignment.TOP, 1.0, 2.0); FlowArrangement f2 = TestUtils.serialised(f1); assertEquals(f1, f2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/block/GridArrangementTest.java000066400000000000000000000230751463604235500307660ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * GridArrangementTest.java * ------------------------ * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.block; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import org.jfree.chart.TestUtils; import org.jfree.chart.ui.Size2D; import org.jfree.data.Range; import org.junit.jupiter.api.Test; /** * Tests for the {@link GridArrangement} class. */ public class GridArrangementTest { /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(GridArrangement.class) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Immutable - cloning is not necessary. */ @Test public void testCloning() { GridArrangement f1 = new GridArrangement(1, 2); assertFalse(f1 instanceof Cloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { GridArrangement f1 = new GridArrangement(33, 44); GridArrangement f2 = TestUtils.serialised(f1); assertEquals(f1, f2); } private static final double EPSILON = 0.000000001; /** * Test arrangement with no constraints. */ @Test public void testNN() { BlockContainer c = createTestContainer1(); Size2D s = c.arrange(null, RectangleConstraint.NONE); assertEquals(90.0, s.width, EPSILON); assertEquals(33.0, s.height, EPSILON); } /** * Test arrangement with a fixed width and no height constraint. */ @Test public void testFN() { BlockContainer c = createTestContainer1(); RectangleConstraint constraint = new RectangleConstraint(100.0, null, LengthConstraintType.FIXED, 0.0, null, LengthConstraintType.NONE); Size2D s = c.arrange(null, constraint); assertEquals(100.0, s.width, EPSILON); assertEquals(33.0, s.height, EPSILON); } /** * Test arrangement with a fixed height and no width constraint. */ @Test public void testNF() { BlockContainer c = createTestContainer1(); RectangleConstraint constraint = RectangleConstraint.NONE.toFixedHeight( 100.0); Size2D s = c.arrange(null, constraint); assertEquals(90.0, s.width, EPSILON); assertEquals(100.0, s.height, EPSILON); } /** * Test arrangement with a range for the width and a fixed height. */ @Test public void testRF() { BlockContainer c = createTestContainer1(); RectangleConstraint constraint = new RectangleConstraint(new Range(40.0, 60.0), 100.0); Size2D s = c.arrange(null, constraint); assertEquals(60.0, s.width, EPSILON); assertEquals(100.0, s.height, EPSILON); } /** * Test arrangement with a range for the width and height. */ @Test public void testRR() { BlockContainer c = createTestContainer1(); RectangleConstraint constraint = new RectangleConstraint(new Range(40.0, 60.0), new Range(50.0, 70.0)); Size2D s = c.arrange(null, constraint); assertEquals(60.0, s.width, EPSILON); assertEquals(50.0, s.height, EPSILON); } /** * Test arrangement with a range for the width and no height constraint. */ @Test public void testRN() { BlockContainer c = createTestContainer1(); RectangleConstraint constraint = RectangleConstraint.NONE.toRangeWidth( new Range(40.0, 60.0)); Size2D s = c.arrange(null, constraint); assertEquals(60.0, s.width, EPSILON); assertEquals(33.0, s.height, EPSILON); } /** * Test arrangement with a range for the height and no width constraint. */ @Test public void testNR() { BlockContainer c = createTestContainer1(); RectangleConstraint constraint = RectangleConstraint.NONE.toRangeHeight( new Range(40.0, 60.0)); Size2D s = c.arrange(null, constraint); assertEquals(90.0, s.width, EPSILON); assertEquals(40.0, s.height, EPSILON); } private BlockContainer createTestContainer1() { Block b1 = new EmptyBlock(10, 11); Block b2 = new EmptyBlock(20, 22); Block b3 = new EmptyBlock(30, 33); BlockContainer result = new BlockContainer(new GridArrangement(1, 3)); result.add(b1); result.add(b2); result.add(b3); return result; } /** * The arrangement should be able to handle null blocks in the layout. */ @Test public void testNullBlock_FF() { BlockContainer c = new BlockContainer(new GridArrangement(1, 1)); c.add(null); Size2D s = c.arrange(null, new RectangleConstraint(20, 10)); assertEquals(20.0, s.getWidth(), EPSILON); assertEquals(10.0, s.getHeight(), EPSILON); } /** * The arrangement should be able to handle null blocks in the layout. */ @Test public void testNullBlock_FN() { BlockContainer c = new BlockContainer(new GridArrangement(1, 1)); c.add(null); Size2D s = c.arrange(null, RectangleConstraint.NONE.toFixedWidth(10)); assertEquals(10.0, s.getWidth(), EPSILON); assertEquals(0.0, s.getHeight(), EPSILON); } /** * The arrangement should be able to handle null blocks in the layout. */ @Test public void testNullBlock_FR() { BlockContainer c = new BlockContainer(new GridArrangement(1, 1)); c.add(null); Size2D s = c.arrange(null, new RectangleConstraint(30.0, new Range(5.0, 10.0))); assertEquals(30.0, s.getWidth(), EPSILON); assertEquals(5.0, s.getHeight(), EPSILON); } /** * The arrangement should be able to handle null blocks in the layout. */ @Test public void testNullBlock_NN() { BlockContainer c = new BlockContainer(new GridArrangement(1, 1)); c.add(null); Size2D s = c.arrange(null, RectangleConstraint.NONE); assertEquals(0.0, s.getWidth(), EPSILON); assertEquals(0.0, s.getHeight(), EPSILON); } /** * The arrangement should be able to handle less blocks than grid spaces. */ @Test public void testGridNotFull_FF() { Block b1 = new EmptyBlock(5, 5); BlockContainer c = new BlockContainer(new GridArrangement(2, 3)); c.add(b1); Size2D s = c.arrange(null, new RectangleConstraint(200, 100)); assertEquals(200.0, s.getWidth(), EPSILON); assertEquals(100.0, s.getHeight(), EPSILON); } /** * The arrangement should be able to handle less blocks than grid spaces. */ @Test public void testGridNotFull_FN() { Block b1 = new EmptyBlock(5, 5); BlockContainer c = new BlockContainer(new GridArrangement(2, 3)); c.add(b1); Size2D s = c.arrange(null, RectangleConstraint.NONE.toFixedWidth(30.0)); assertEquals(30.0, s.getWidth(), EPSILON); assertEquals(10.0, s.getHeight(), EPSILON); } /** * The arrangement should be able to handle less blocks than grid spaces. */ @Test public void testGridNotFull_FR() { Block b1 = new EmptyBlock(5, 5); BlockContainer c = new BlockContainer(new GridArrangement(2, 3)); c.add(b1); Size2D s = c.arrange(null, new RectangleConstraint(30.0, new Range(5.0, 10.0))); assertEquals(30.0, s.getWidth(), EPSILON); assertEquals(10.0, s.getHeight(), EPSILON); } /** * The arrangement should be able to handle less blocks than grid spaces. */ @Test public void testGridNotFull_NN() { Block b1 = new EmptyBlock(5, 5); BlockContainer c = new BlockContainer(new GridArrangement(2, 3)); c.add(b1); Size2D s = c.arrange(null, RectangleConstraint.NONE); assertEquals(15.0, s.getWidth(), EPSILON); assertEquals(10.0, s.getHeight(), EPSILON); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/block/LabelBlockTest.java000066400000000000000000000124331463604235500277030ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * LabelBlockTest.java * ------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.block; import java.awt.Color; import java.awt.Font; import java.awt.GradientPaint; import java.awt.geom.Rectangle2D; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import static org.jfree.chart.TestUtils.createFont; import static org.jfree.chart.TestUtils.createR2D; import static org.junit.jupiter.api.Assertions.*; import org.jfree.chart.TestUtils; import org.jfree.chart.text.TextBlockAnchor; import org.jfree.chart.ui.RectangleAnchor; import org.junit.jupiter.api.Test; /** * Some tests for the {@link LabelBlock} class. */ public class LabelBlockTest { /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(LabelBlock.class) .withPrefabValues(Rectangle2D.class, createR2D(true), createR2D(false)) .withPrefabValues(Font.class, createFont(true), createFont(false)) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .withRedefinedSuperclass() .verify(); } /** * Confirm that the equals() method can distinguish all the required fields. */ @Test public void testEquals() { LabelBlock b1 = new LabelBlock("ABC", new Font("Dialog", Font.PLAIN, 12), Color.RED); LabelBlock b2 = new LabelBlock("ABC", new Font("Dialog", Font.PLAIN, 12), Color.RED); assertEquals(b1, b2); assertEquals(b2, b2); b1 = new LabelBlock("XYZ", new Font("Dialog", Font.PLAIN, 12), Color.RED); assertNotEquals(b1, b2); b2 = new LabelBlock("XYZ", new Font("Dialog", Font.PLAIN, 12), Color.RED); assertEquals(b1, b2); b1 = new LabelBlock("XYZ", new Font("Dialog", Font.BOLD, 12), Color.RED); assertNotEquals(b1, b2); b2 = new LabelBlock("XYZ", new Font("Dialog", Font.BOLD, 12), Color.RED); assertEquals(b1, b2); b1 = new LabelBlock("XYZ", new Font("Dialog", Font.BOLD, 12), Color.BLUE); assertNotEquals(b1, b2); b2 = new LabelBlock("XYZ", new Font("Dialog", Font.BOLD, 12), Color.BLUE); assertEquals(b1, b2); b1.setToolTipText("Tooltip"); assertNotEquals(b1, b2); b2.setToolTipText("Tooltip"); assertEquals(b1, b2); b1.setURLText("URL"); assertNotEquals(b1, b2); b2.setURLText("URL"); assertEquals(b1, b2); b1.setContentAlignmentPoint(TextBlockAnchor.CENTER_RIGHT); assertNotEquals(b1, b2); b2.setContentAlignmentPoint(TextBlockAnchor.CENTER_RIGHT); assertEquals(b1, b2); b1.setTextAnchor(RectangleAnchor.BOTTOM_RIGHT); assertNotEquals(b1, b2); b2.setTextAnchor(RectangleAnchor.BOTTOM_RIGHT); assertEquals(b1, b2); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { LabelBlock b1 = new LabelBlock("ABC", new Font("Dialog", Font.PLAIN, 12), Color.RED); LabelBlock b2 = (LabelBlock) b1.clone(); assertNotSame(b1, b2); assertSame(b1.getClass(), b2.getClass()); assertEquals(b1, b2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { GradientPaint gp = new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE); LabelBlock b1 = new LabelBlock("ABC", new Font("Dialog", Font.PLAIN, 12), gp); LabelBlock b2 = TestUtils.serialised(b1); assertEquals(b1, b2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/block/LineBorderTest.java000066400000000000000000000103031463604235500277300ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * LineBorderTest.java * ------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.block; import java.awt.BasicStroke; import java.awt.Color; import java.awt.GradientPaint; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.chart.ui.RectangleInsets; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link LineBorder} class. */ public class LineBorderTest { /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(LineBorder.class) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Confirm that the equals() method can distinguish all the required fields. */ @Test public void testEquals() { LineBorder b1 = new LineBorder(Color.RED, new BasicStroke(1.0f), new RectangleInsets(1.0, 1.0, 1.0, 1.0)); LineBorder b2 = new LineBorder(Color.RED, new BasicStroke(1.0f), new RectangleInsets(1.0, 1.0, 1.0, 1.0)); assertEquals(b1, b2); assertEquals(b2, b2); b1 = new LineBorder(Color.BLUE, new BasicStroke(1.0f), new RectangleInsets(1.0, 1.0, 1.0, 1.0)); assertNotEquals(b1, b2); b2 = new LineBorder(Color.BLUE, new BasicStroke(1.0f), new RectangleInsets(1.0, 1.0, 1.0, 1.0)); assertEquals(b1, b2); b1 = new LineBorder(Color.BLUE, new BasicStroke(1.1f), new RectangleInsets(1.0, 1.0, 1.0, 1.0)); assertNotEquals(b1, b2); b2 = new LineBorder(Color.BLUE, new BasicStroke(1.1f), new RectangleInsets(1.0, 1.0, 1.0, 1.0)); assertEquals(b1, b2); b1 = new LineBorder(Color.BLUE, new BasicStroke(1.1f), new RectangleInsets(1.0, 2.0, 3.0, 4.0)); assertNotEquals(b1, b2); b2 = new LineBorder(Color.BLUE, new BasicStroke(1.1f), new RectangleInsets(1.0, 2.0, 3.0, 4.0)); assertEquals(b1, b2); } /** * Immutable - cloning not necessary. */ @Test public void testCloning() { LineBorder b1 = new LineBorder(); assertFalse(b1 instanceof Cloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { LineBorder b1 = new LineBorder(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW), new BasicStroke(1.0f), new RectangleInsets(1.0, 1.0, 1.0, 1.0)); LineBorder b2 = TestUtils.serialised(b1); assertEquals(b1, b2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/block/RectangleConstraintTest.java000066400000000000000000000116051463604235500316620ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------- * RectangleConstraintTest.java * ---------------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.block; import org.jfree.chart.ui.Size2D; import static org.junit.jupiter.api.Assertions.assertEquals; import org.jfree.data.Range; import org.junit.jupiter.api.Test; /** * Tests for the {@link RectangleConstraint} class. */ public class RectangleConstraintTest { private static final double EPSILON = 0.0000000001; /** * Run some checks on the constrained size calculation. */ @Test public void testCalculateConstrainedSize() { Size2D s; // NONE / NONE RectangleConstraint c1 = RectangleConstraint.NONE; s = c1.calculateConstrainedSize(new Size2D(1.2, 3.4)); assertEquals(s.width, 1.2, EPSILON); assertEquals(s.height, 3.4, EPSILON); // NONE / RANGE RectangleConstraint c2 = new RectangleConstraint( 0.0, new Range(0.0, 0.0), LengthConstraintType.NONE, 0.0, new Range(2.0, 3.0), LengthConstraintType.RANGE ); s = c2.calculateConstrainedSize(new Size2D(1.2, 3.4)); assertEquals(s.width, 1.2, EPSILON); assertEquals(s.height, 3.0, EPSILON); // NONE / FIXED RectangleConstraint c3 = new RectangleConstraint( 0.0, null, LengthConstraintType.NONE, 9.9, null, LengthConstraintType.FIXED ); s = c3.calculateConstrainedSize(new Size2D(1.2, 3.4)); assertEquals(s.width, 1.2, EPSILON); assertEquals(s.height, 9.9, EPSILON); // RANGE / NONE RectangleConstraint c4 = new RectangleConstraint( 0.0, new Range(2.0, 3.0), LengthConstraintType.RANGE, 0.0, new Range(0.0, 0.0), LengthConstraintType.NONE ); s = c4.calculateConstrainedSize(new Size2D(1.2, 3.4)); assertEquals(s.width, 2.0, EPSILON); assertEquals(s.height, 3.4, EPSILON); // RANGE / RANGE RectangleConstraint c5 = new RectangleConstraint( 0.0, new Range(2.0, 3.0), LengthConstraintType.RANGE, 0.0, new Range(2.0, 3.0), LengthConstraintType.RANGE ); s = c5.calculateConstrainedSize(new Size2D(1.2, 3.4)); assertEquals(s.width, 2.0, EPSILON); assertEquals(s.height, 3.0, EPSILON); // RANGE / FIXED RectangleConstraint c6 = new RectangleConstraint( 0.0, null, LengthConstraintType.NONE, 9.9, null, LengthConstraintType.FIXED ); s = c6.calculateConstrainedSize(new Size2D(1.2, 3.4)); assertEquals(s.width, 1.2, EPSILON); assertEquals(s.height, 9.9, EPSILON); // FIXED / NONE RectangleConstraint c7 = RectangleConstraint.NONE; s = c7.calculateConstrainedSize(new Size2D(1.2, 3.4)); assertEquals(s.width, 1.2, EPSILON); assertEquals(s.height, 3.4, EPSILON); // FIXED / RANGE RectangleConstraint c8 = new RectangleConstraint( 0.0, new Range(0.0, 0.0), LengthConstraintType.NONE, 0.0, new Range(2.0, 3.0), LengthConstraintType.RANGE ); s = c8.calculateConstrainedSize(new Size2D(1.2, 3.4)); assertEquals(s.width, 1.2, EPSILON); assertEquals(s.height, 3.0, EPSILON); // FIXED / FIXED RectangleConstraint c9 = new RectangleConstraint( 0.0, null, LengthConstraintType.NONE, 9.9, null, LengthConstraintType.FIXED ); s = c9.calculateConstrainedSize(new Size2D(1.2, 3.4)); assertEquals(s.width, 1.2, EPSILON); assertEquals(s.height, 9.9, EPSILON); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/entity/000077500000000000000000000000001463604235500244255ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/entity/CategoryItemEntityTest.java000066400000000000000000000114251463604235500317240ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * CategoryItemEntityTest.java * --------------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.entity; import java.awt.geom.Rectangle2D; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.data.category.DefaultCategoryDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link CategoryItemEntity} class. */ public class CategoryItemEntityTest { /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashcode() { EqualsVerifier.forClass(CategoryItemEntity.class) .withRedefinedSuperclass() // superclass also defines equals/hashCode .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { DefaultCategoryDataset d = new DefaultCategoryDataset(); d.addValue(1.0, "R1", "C1"); d.addValue(2.0, "R1", "C2"); d.addValue(3.0, "R2", "C1"); d.addValue(4.0, "R2", "C2"); CategoryItemEntity e1 = new CategoryItemEntity(new Rectangle2D.Double( 1.0, 2.0, 3.0, 4.0), "ToolTip", "URL", d, "R2", "C2"); CategoryItemEntity e2 = new CategoryItemEntity(new Rectangle2D.Double( 1.0, 2.0, 3.0, 4.0), "ToolTip", "URL", d, "R2", "C2"); assertEquals(e1, e2); e1.setArea(new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0)); assertNotEquals(e1, e2); e2.setArea(new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0)); assertEquals(e1, e2); e1.setToolTipText("New ToolTip"); assertNotEquals(e1, e2); e2.setToolTipText("New ToolTip"); assertEquals(e1, e2); e1.setURLText("New URL"); assertNotEquals(e1, e2); e2.setURLText("New URL"); assertEquals(e1, e2); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException if there is a problem cloning. */ @Test public void testCloning() throws CloneNotSupportedException { DefaultCategoryDataset d = new DefaultCategoryDataset(); d.addValue(1.0, "R1", "C1"); d.addValue(2.0, "R1", "C2"); d.addValue(3.0, "R2", "C1"); d.addValue(4.0, "R2", "C2"); CategoryItemEntity e1 = new CategoryItemEntity(new Rectangle2D.Double( 1.0, 2.0, 3.0, 4.0), "ToolTip", "URL", d, "R2", "C2"); CategoryItemEntity e2 = (CategoryItemEntity) e1.clone(); assertNotSame(e1, e2); assertSame(e1.getClass(), e2.getClass()); assertEquals(e1, e2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DefaultCategoryDataset d = new DefaultCategoryDataset(); d.addValue(1.0, "R1", "C1"); d.addValue(2.0, "R1", "C2"); d.addValue(3.0, "R2", "C1"); d.addValue(4.0, "R2", "C2"); CategoryItemEntity e1 = new CategoryItemEntity(new Rectangle2D.Double( 1.0, 2.0, 3.0, 4.0), "ToolTip", "URL", d, "R2", "C2"); CategoryItemEntity e2 = TestUtils.serialised(e1); assertEquals(e1, e2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/entity/CategoryLabelEntityTest.java000066400000000000000000000107261463604235500320500ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------- * CategoryLabelEntityTest.java * ---------------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.entity; import java.awt.geom.Rectangle2D; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link CategoryLabelEntity} class. */ public class CategoryLabelEntityTest { /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashcode() { EqualsVerifier.forClass(CategoryLabelEntity.class) .withRedefinedSuperclass() // superclass also defines equals/hashCode .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { CategoryLabelEntity e1 = new CategoryLabelEntity("A", new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), "ToolTip", "URL"); CategoryLabelEntity e2 = new CategoryLabelEntity("A", new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), "ToolTip", "URL"); assertEquals(e1, e2); e1 = new CategoryLabelEntity("B", new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), "ToolTip", "URL"); assertNotEquals(e1, e2); e2 = new CategoryLabelEntity("B", new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), "ToolTip", "URL"); assertEquals(e1, e2); e1.setArea(new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0)); assertNotEquals(e1, e2); e2.setArea(new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0)); assertEquals(e1, e2); e1.setToolTipText("New ToolTip"); assertNotEquals(e1, e2); e2.setToolTipText("New ToolTip"); assertEquals(e1, e2); e1.setURLText("New URL"); assertNotEquals(e1, e2); e2.setURLText("New URL"); assertEquals(e1, e2); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException if there is a problem cloning. */ @Test public void testCloning() throws CloneNotSupportedException { CategoryLabelEntity e1 = new CategoryLabelEntity("A", new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), "ToolTip", "URL"); CategoryLabelEntity e2 = (CategoryLabelEntity) e1.clone(); assertNotSame(e1, e2); assertSame(e1.getClass(), e2.getClass()); assertEquals(e1, e2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { CategoryLabelEntity e1 = new CategoryLabelEntity("A", new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), "ToolTip", "URL"); CategoryLabelEntity e2 = TestUtils.serialised(e1); assertEquals(e1, e2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/entity/FlowEntityTest.java000066400000000000000000000106521463604235500302400ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * FlowEntityTest.java * ------------------- * (C) Copyright 2022, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.entity; import java.awt.Rectangle; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.data.flow.FlowKey; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Test class for the {@link FlowEntity} class. */ public class FlowEntityTest { /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(FlowEntity.class) .withRedefinedSuperclass() // superclass also defines equals/hashCode .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { FlowEntity f1 = new FlowEntity(new FlowKey<>(0, "A", "B"), new Rectangle(0, 1, 2, 3), "tt", "uu"); FlowEntity f2 = new FlowEntity(new FlowKey<>(0, "A", "B"), new Rectangle(0, 1, 2, 3), "tt", "uu"); assertEquals(f1, f2); assertEquals(f2, f1); f1 = new FlowEntity(new FlowKey<>(0, "A", "C"), new Rectangle(0, 1, 2, 3), "tt", "uu"); assertNotEquals(f1, f2); f2 = new FlowEntity(new FlowKey<>(0, "A", "C"), new Rectangle(0, 1, 2, 3), "tt", "uu"); assertEquals(f1, f2); f1 = new FlowEntity(new FlowKey<>(0, "A", "C"), new Rectangle(4, 1, 2, 3), "tt", "uu"); assertNotEquals(f1, f2); f2 = new FlowEntity(new FlowKey<>(0, "A", "C"), new Rectangle(4, 1, 2, 3), "tt", "uu"); assertEquals(f1, f2); f1 = new FlowEntity(new FlowKey<>(0, "A", "C"), new Rectangle(4, 1, 2, 3), "TT", "uu"); assertNotEquals(f1, f2); f2 = new FlowEntity(new FlowKey<>(0, "A", "C"), new Rectangle(4, 1, 2, 3), "TT", "uu"); assertEquals(f1, f2); f1 = new FlowEntity(new FlowKey<>(0, "A", "C"), new Rectangle(4, 1, 2, 3), "TT", "UU"); assertNotEquals(f1, f2); f2 = new FlowEntity(new FlowKey<>(0, "A", "C"), new Rectangle(4, 1, 2, 3), "TT", "UU"); assertEquals(f1, f2); } /** * Confirm that cloning works. * * @throws CloneNotSupportedException if cloning is not supported. */ @Test public void testCloning() throws CloneNotSupportedException { FlowEntity f1 = new FlowEntity(new FlowKey<>(0, "A", "B"), new Rectangle(0, 1, 2, 3), "tt", "uu"); FlowEntity f2 = (FlowEntity) f1.clone(); assertNotSame(f1, f2); assertSame(f1.getClass(), f2.getClass()); assertEquals(f1, f2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { FlowEntity f1 = new FlowEntity(new FlowKey<>(0, "A", "B"), new Rectangle(0, 1, 2, 3), "tt", "uu"); FlowEntity f2 = TestUtils.serialised(f1); assertEquals(f1, f2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/entity/LegendItemEntityTest.java000066400000000000000000000103721463604235500313450ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * LegendItemEntityTest.java * ------------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.entity; import java.awt.geom.Rectangle2D; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.data.category.DefaultCategoryDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link LegendItemEntity} class. */ public class LegendItemEntityTest { /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(LegendItemEntity.class) .withRedefinedSuperclass() // superclass also defines equals/hashCode .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Confirm that the equals() method can distinguish all the required fields. */ @Test public void testEquals() { LegendItemEntity e1 = new LegendItemEntity(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0)); LegendItemEntity e2 = new LegendItemEntity(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0)); assertEquals(e1, e2); e1.setArea(new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0)); assertNotEquals(e1, e2); e2.setArea(new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0)); assertEquals(e1, e2); e1.setToolTipText("New ToolTip"); assertNotEquals(e1, e2); e2.setToolTipText("New ToolTip"); assertEquals(e1, e2); e1.setURLText("New URL"); assertNotEquals(e1, e2); e2.setURLText("New URL"); assertEquals(e1, e2); e1.setDataset(new DefaultCategoryDataset()); assertNotEquals(e1, e2); e2.setDataset(new DefaultCategoryDataset()); assertEquals(e1, e2); e1.setSeriesKey("A"); assertNotEquals(e1, e2); e2.setSeriesKey("A"); assertEquals(e1, e2); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { LegendItemEntity e1 = new LegendItemEntity(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0)); LegendItemEntity e2 = (LegendItemEntity) e1.clone(); assertNotSame(e1, e2); assertSame(e1.getClass(), e2.getClass()); assertEquals(e1, e2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { LegendItemEntity e1 = new LegendItemEntity(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0)); LegendItemEntity e2 = TestUtils.serialised(e1); assertEquals(e1, e2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/entity/PieSectionEntityTest.java000066400000000000000000000113401463604235500313660ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * PieSectionEntityTest.java * ------------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.entity; import java.awt.geom.Rectangle2D; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.data.general.DefaultPieDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link PieSectionEntity} class. */ public class PieSectionEntityTest { /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(PieSectionEntity.class) .withRedefinedSuperclass() // superclass also defines equals/hashCode .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { PieSectionEntity e1 = new PieSectionEntity(new Rectangle2D.Double( 1.0, 2.0, 3.0, 4.0), new DefaultPieDataset(), 1, 2, "Key", "ToolTip", "URL"); PieSectionEntity e2 = new PieSectionEntity(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), new DefaultPieDataset(), 1, 2, "Key", "ToolTip", "URL"); assertEquals(e1, e2); e1.setArea(new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0)); assertNotEquals(e1, e2); e2.setArea(new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0)); assertEquals(e1, e2); e1.setToolTipText("New ToolTip"); assertNotEquals(e1, e2); e2.setToolTipText("New ToolTip"); assertEquals(e1, e2); e1.setURLText("New URL"); assertNotEquals(e1, e2); e2.setURLText("New URL"); assertEquals(e1, e2); e1.setDataset(null); assertNotEquals(e1, e2); e2.setDataset(null); assertEquals(e1, e2); e1.setPieIndex(99); assertNotEquals(e1, e2); e2.setPieIndex(99); assertEquals(e1, e2); e1.setSectionIndex(66); assertNotEquals(e1, e2); e2.setSectionIndex(66); assertEquals(e1, e2); e1.setSectionKey("ABC"); assertNotEquals(e1, e2); e2.setSectionKey("ABC"); assertEquals(e1, e2); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { PieSectionEntity e1 = new PieSectionEntity(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), new DefaultPieDataset(), 1, 2, "Key", "ToolTip", "URL"); PieSectionEntity e2 = (PieSectionEntity) e1.clone(); assertNotSame(e1, e2); assertSame(e1.getClass(), e2.getClass()); assertEquals(e1, e2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { PieSectionEntity e1 = new PieSectionEntity(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), new DefaultPieDataset(), 1, 2, "Key", "ToolTip", "URL"); PieSectionEntity e2 = TestUtils.serialised(e1); assertEquals(e1, e2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/entity/StandardEntityCollectionTest.java000066400000000000000000000073741463604235500331140ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------------- * StandardEntityCollectionTest.java * --------------------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.entity; import java.awt.geom.Rectangle2D; import org.jfree.chart.TestUtils; import org.jfree.data.general.DefaultPieDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link StandardEntityCollection} class. */ public class StandardEntityCollectionTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { StandardEntityCollection c1 = new StandardEntityCollection(); StandardEntityCollection c2 = new StandardEntityCollection(); assertEquals(c1, c2); PieSectionEntity e1 = new PieSectionEntity(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), new DefaultPieDataset(), 0, 1, "Key", "ToolTip", "URL"); c1.add(e1); assertNotEquals(c1, c2); PieSectionEntity e2 = new PieSectionEntity(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), new DefaultPieDataset(), 0, 1, "Key", "ToolTip", "URL"); c2.add(e2); assertEquals(c1, c2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { PieSectionEntity e1 = new PieSectionEntity(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), new DefaultPieDataset(), 0, 1, "Key", "ToolTip", "URL"); StandardEntityCollection c1 = new StandardEntityCollection(); c1.add(e1); StandardEntityCollection c2 = (StandardEntityCollection) c1.clone(); assertNotSame(c1, c2); assertSame(c1.getClass(), c2.getClass()); assertEquals(c1, c2); // check independence c1.clear(); assertNotEquals(c1, c2); c2.clear(); assertEquals(c1, c2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { PieSectionEntity e1 = new PieSectionEntity(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), new DefaultPieDataset(), 0, 1, "Key", "ToolTip", "URL"); StandardEntityCollection c1 = new StandardEntityCollection(); c1.add(e1); StandardEntityCollection c2 = TestUtils.serialised(c1); assertEquals(c1, c2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/entity/TickLabelEntityTest.java000066400000000000000000000067311463604235500311660ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * TickLabelEntityTest.java * ------------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.entity; import java.awt.geom.Rectangle2D; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link TickLabelEntity} class. */ public class TickLabelEntityTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { TickLabelEntity e1 = new TickLabelEntity(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), "ToolTip", "URL"); TickLabelEntity e2 = new TickLabelEntity(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), "ToolTip", "URL"); assertEquals(e1, e2); // Area is transient, not part of equals or hashCode - compare using // get method e1.setArea(new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0)); assertNotEquals(e1.getArea(), e2.getArea()); e2.setArea(new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0)); assertEquals(e1.getArea(), e2.getArea()); e1.setToolTipText("New ToolTip"); assertNotEquals(e1, e2); e2.setToolTipText("New ToolTip"); assertEquals(e1, e2); e1.setURLText("New URL"); assertNotEquals(e1, e2); e2.setURLText("New URL"); assertEquals(e1, e2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { TickLabelEntity e1 = new TickLabelEntity(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), "ToolTip", "URL"); TickLabelEntity e2 = (TickLabelEntity) e1.clone(); assertNotSame(e1, e2); assertSame(e1.getClass(), e2.getClass()); assertEquals(e1, e2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { TickLabelEntity e1 = new TickLabelEntity(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), "ToolTip", "URL"); TickLabelEntity e2 = TestUtils.serialised(e1); assertEquals(e1, e2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/entity/XYItemEntityTest.java000066400000000000000000000105321463604235500305050ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * XYItemEntityTest.java * --------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.entity; import java.awt.geom.Rectangle2D; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.data.time.TimeSeriesCollection; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XYItemEntity} class. */ public class XYItemEntityTest { /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(XYItemEntity.class) .withRedefinedSuperclass() // superclass also defines equals/hashCode .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { XYItemEntity e1 = new XYItemEntity(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), new TimeSeriesCollection(), 1, 9, "ToolTip", "URL"); XYItemEntity e2 = new XYItemEntity(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), new TimeSeriesCollection(), 1, 9, "ToolTip", "URL"); assertEquals(e1, e2); e1.setArea(new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0)); assertNotEquals(e1, e2); e2.setArea(new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0)); assertEquals(e1, e2); e1.setToolTipText("New ToolTip"); assertNotEquals(e1, e2); e2.setToolTipText("New ToolTip"); assertEquals(e1, e2); e1.setURLText("New URL"); assertNotEquals(e1, e2); e2.setURLText("New URL"); assertEquals(e1, e2); e1.setSeriesIndex(88); assertNotEquals(e1, e2); e2.setSeriesIndex(88); assertEquals(e1, e2); e1.setItem(88); assertNotEquals(e1, e2); e2.setItem(88); assertEquals(e1, e2); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { XYItemEntity e1 = new XYItemEntity(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), new TimeSeriesCollection(), 1, 9, "ToolTip", "URL"); XYItemEntity e2 = (XYItemEntity) e1.clone(); assertNotSame(e1, e2); assertSame(e1.getClass(), e2.getClass()); assertEquals(e1, e2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XYItemEntity e1 = new XYItemEntity(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), new TimeSeriesCollection(), 1, 9, "ToolTip", "URL"); XYItemEntity e2 = TestUtils.serialised(e1); assertEquals(e1, e2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/imagemap/000077500000000000000000000000001463604235500246715ustar00rootroot00000000000000DynamicDriveToolTipTagFragmentGeneratorTest.java000066400000000000000000000045211463604235500361770ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/imagemap/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------------------------ * DynamicDriveToolTipTagFragmentGeneratorTest.java * ------------------------------------------------ * (C) Copyright 2009-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.imagemap; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; /** * Tests for the {@link DynamicDriveToolTipTagFragmentGenerator} class. */ public class DynamicDriveToolTipTagFragmentGeneratorTest { /** * Some checks for the generateURLFragment() method. */ @Test public void testGenerateURLFragment() { OverLIBToolTipTagFragmentGenerator g = new OverLIBToolTipTagFragmentGenerator(); assertEquals(" onMouseOver=\"return overlib('abc');\"" + " onMouseOut=\"return nd();\"", g.generateToolTipFragment("abc")); assertEquals(" onMouseOver=\"return overlib(" + "'It\\'s \\\"A\\\", 100.0');\" onMouseOut=\"return nd();\"", g.generateToolTipFragment("It's \"A\", 100.0")); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/imagemap/ImageMapUtilsTest.java000066400000000000000000000053571463604235500311070ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * ImageMapUtilsTest.java * ---------------------- * (C) Copyright 2009-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.imagemap; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; /** * Tests for the {@link ImageMapUtils} class. */ public class ImageMapUtilsTest { /** * Some checks for the htmlEscape() method. */ @Test public void testHTMLEscape() { assertEquals("", ImageMapUtils.htmlEscape("")); assertEquals("abc", ImageMapUtils.htmlEscape("abc")); assertEquals("&", ImageMapUtils.htmlEscape("&")); assertEquals(""", ImageMapUtils.htmlEscape("\"")); assertEquals("<", ImageMapUtils.htmlEscape("<")); assertEquals(">", ImageMapUtils.htmlEscape(">")); assertEquals("'", ImageMapUtils.htmlEscape("'")); assertEquals("\abc", ImageMapUtils.htmlEscape("\\abc")); assertEquals("abc\n", ImageMapUtils.htmlEscape("abc\n")); } /** * Some checks for the javascriptEscape() method. */ @Test public void testJavascriptEscape() { assertEquals("", ImageMapUtils.javascriptEscape("")); assertEquals("abc", ImageMapUtils.javascriptEscape("abc")); assertEquals("\\\'", ImageMapUtils.javascriptEscape("\'")); assertEquals("\\\"", ImageMapUtils.javascriptEscape("\"")); assertEquals("\\\\", ImageMapUtils.javascriptEscape("\\")); } } OverLIBToolTipTagFragmentGeneratorTest.java000066400000000000000000000044701463604235500350660ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/imagemap/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------------------- * OverLIBToolTipTagFragmentGeneratorTest.java * ------------------------------------------- * (C) Copyright 2009-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.imagemap; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; /** * Tests for the {@link OverLIBToolTipTagFragmentGenerator} class. */ public class OverLIBToolTipTagFragmentGeneratorTest { /** * Some checks for the generateURLFragment() method. */ @Test public void testGenerateURLFragment() { OverLIBToolTipTagFragmentGenerator g = new OverLIBToolTipTagFragmentGenerator(); assertEquals(" onMouseOver=\"return overlib('abc');\"" + " onMouseOut=\"return nd();\"", g.generateToolTipFragment("abc")); assertEquals(" onMouseOver=\"return overlib(" + "'It\\'s \\\"A\\\", 100.0');\" onMouseOut=\"return nd();\"", g.generateToolTipFragment("It's \"A\", 100.0")); } } StandardToolTipTagFragmentGeneratorTest.java000066400000000000000000000043031463604235500353570ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/imagemap/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------------------------- * StandardToolTipTagFragmentGeneratorTest.java * -------------------------------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.imagemap; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; /** * Tests for the {@link StandardToolTipTagFragmentGenerator} class. */ public class StandardToolTipTagFragmentGeneratorTest { /** * Some checks for the generateURLFragment() method. */ @Test public void testGenerateURLFragment() { StandardToolTipTagFragmentGenerator g = new StandardToolTipTagFragmentGenerator(); assertEquals(" title=\"abc\" alt=\"\"", g.generateToolTipFragment("abc")); assertEquals(" title=\"Series "A", 100.0\" alt=\"\"", g.generateToolTipFragment("Series \"A\", 100.0")); } } StandardURLTagFragmentGeneratorTest.java000066400000000000000000000043761463604235500344410ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/imagemap/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------------------- * StandardURLTagFragmentGeneratorTest.java * ---------------------------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.imagemap; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; /** * Tests for the {@link StandardURLTagFragmentGenerator} class. */ public class StandardURLTagFragmentGeneratorTest { /** * Some checks for the generateURLFragment() method. */ @Test public void testGenerateURLFragment() { StandardURLTagFragmentGenerator g = new StandardURLTagFragmentGenerator(); assertEquals(" href=\"abc\"", g.generateURLFragment("abc")); assertEquals(" href=\"images/abc.png\"", g.generateURLFragment("images/abc.png")); assertEquals(" href=\"http://www.jfree.org/images/abc.png\"", g.generateURLFragment("http://www.jfree.org/images/abc.png")); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/labels/000077500000000000000000000000001463604235500243535ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/labels/BoxAndWhiskerToolTipGeneratorTest.java000066400000000000000000000102121463604235500337440ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------------------- * BoxAndWhiskerToolTipGeneratorTest.java * -------------------------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import java.text.DecimalFormat; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link BoxAndWhiskerToolTipGenerator} class. */ public class BoxAndWhiskerToolTipGeneratorTest { /** * A series of tests for the equals() method. */ @Test public void testEquals() { // standard test BoxAndWhiskerToolTipGenerator g1 = new BoxAndWhiskerToolTipGenerator(); BoxAndWhiskerToolTipGenerator g2 = new BoxAndWhiskerToolTipGenerator(); assertEquals(g1, g2); assertEquals(g2, g1); // tooltip format g1 = new BoxAndWhiskerToolTipGenerator("{0} --> {1} {2}", new DecimalFormat("0.0")); g2 = new BoxAndWhiskerToolTipGenerator("{1} {2}", new DecimalFormat("0.0")); assertNotEquals(g1, g2); g2 = new BoxAndWhiskerToolTipGenerator("{0} --> {1} {2}", new DecimalFormat("0.0")); assertEquals(g1, g2); // Y format g1 = new BoxAndWhiskerToolTipGenerator("{0} --> {1} {2}", new DecimalFormat("0.0")); g2 = new BoxAndWhiskerToolTipGenerator("{0} --> {1} {2}", new DecimalFormat("0.00")); assertNotEquals(g1, g2); } /** * Simple check that hashCode is implemented. */ @Test public void testHashCode() { BoxAndWhiskerToolTipGenerator g1 = new BoxAndWhiskerToolTipGenerator(); BoxAndWhiskerToolTipGenerator g2 = new BoxAndWhiskerToolTipGenerator(); assertEquals(g1, g2); assertEquals(g1.hashCode(), g2.hashCode()); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { BoxAndWhiskerToolTipGenerator g1 = new BoxAndWhiskerToolTipGenerator(); BoxAndWhiskerToolTipGenerator g2 = (BoxAndWhiskerToolTipGenerator) g1.clone(); assertNotSame(g1, g2); assertSame(g1.getClass(), g2.getClass()); assertEquals(g1, g2); } /** * Check to ensure that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { BoxAndWhiskerToolTipGenerator g1 = new BoxAndWhiskerToolTipGenerator(); assertTrue(g1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { BoxAndWhiskerToolTipGenerator g1 = new BoxAndWhiskerToolTipGenerator(); BoxAndWhiskerToolTipGenerator g2 = TestUtils.serialised(g1); assertEquals(g1, g2); } } BoxAndWhiskerXYToolTipGeneratorTest.java000066400000000000000000000122471463604235500341600ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/labels/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------------------- * BoxAndWhiskerXYToolTipGeneratorTest.java * ---------------------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link BoxAndWhiskerXYToolTipGenerator} class. */ public class BoxAndWhiskerXYToolTipGeneratorTest { /** * A series of tests for the equals() method. */ @Test public void testEquals() { // standard test BoxAndWhiskerXYToolTipGenerator g1 = new BoxAndWhiskerXYToolTipGenerator(); BoxAndWhiskerXYToolTipGenerator g2 = new BoxAndWhiskerXYToolTipGenerator(); assertEquals(g1, g2); assertEquals(g2, g1); // tooltip format g1 = new BoxAndWhiskerXYToolTipGenerator("{0} --> {1} {2}", new SimpleDateFormat("yyyy"), new DecimalFormat("0.0")); g2 = new BoxAndWhiskerXYToolTipGenerator("{1} {2}", new SimpleDateFormat("yyyy"), new DecimalFormat("0.0")); assertNotEquals(g1, g2); g2 = new BoxAndWhiskerXYToolTipGenerator("{0} --> {1} {2}", new SimpleDateFormat("yyyy"), new DecimalFormat("0.0")); assertEquals(g1, g2); // date format g1 = new BoxAndWhiskerXYToolTipGenerator("{0} --> {1} {2}", new SimpleDateFormat("yyyy"), new DecimalFormat("0.0")); g2 = new BoxAndWhiskerXYToolTipGenerator("{0} --> {1} {2}", new SimpleDateFormat("MMM-yyyy"), new DecimalFormat("0.0")); assertNotEquals(g1, g2); g2 = new BoxAndWhiskerXYToolTipGenerator("{0} --> {1} {2}", new SimpleDateFormat("yyyy"), new DecimalFormat("0.0")); assertEquals(g1, g2); // Y format g1 = new BoxAndWhiskerXYToolTipGenerator("{0} --> {1} {2}", new SimpleDateFormat("yyyy"), new DecimalFormat("0.0")); g2 = new BoxAndWhiskerXYToolTipGenerator("{0} --> {1} {2}", new SimpleDateFormat("yyyy"), new DecimalFormat("0.00")); assertNotEquals(g1, g2); g2 = new BoxAndWhiskerXYToolTipGenerator("{0} --> {1} {2}", new SimpleDateFormat("yyyy"), new DecimalFormat("0.0")); assertEquals(g1, g2); } /** * Simple check that hashCode is implemented. */ @Test public void testHashCode() { BoxAndWhiskerXYToolTipGenerator g1 = new BoxAndWhiskerXYToolTipGenerator(); BoxAndWhiskerXYToolTipGenerator g2 = new BoxAndWhiskerXYToolTipGenerator(); assertEquals(g1, g2); assertEquals(g1.hashCode(), g2.hashCode()); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { BoxAndWhiskerXYToolTipGenerator g1 = new BoxAndWhiskerXYToolTipGenerator(); BoxAndWhiskerXYToolTipGenerator g2 = (BoxAndWhiskerXYToolTipGenerator) g1.clone(); assertNotSame(g1, g2); assertSame(g1.getClass(), g2.getClass()); assertEquals(g1, g2); } /** * Check to ensure that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { BoxAndWhiskerXYToolTipGenerator g1 = new BoxAndWhiskerXYToolTipGenerator(); assertTrue(g1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { BoxAndWhiskerXYToolTipGenerator g1 = new BoxAndWhiskerXYToolTipGenerator(); BoxAndWhiskerXYToolTipGenerator g2 = TestUtils.serialised(g1); assertEquals(g1, g2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/labels/BubbleXYItemLabelGeneratorTest.java000066400000000000000000000150261463604235500331640ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------------- * BubbleXYItemLabelGeneratorTest.java * ----------------------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import java.text.DateFormat; import java.text.DecimalFormat; import java.text.NumberFormat; import java.text.SimpleDateFormat; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link BubbleXYItemLabelGenerator} class. */ public class BubbleXYItemLabelGeneratorTest { /** * A series of tests for the equals() method. */ @Test public void testEquals() { // some setup... String f1 = "{1}"; String f2 = "{2}"; NumberFormat xnf1 = new DecimalFormat("0.00"); NumberFormat xnf2 = new DecimalFormat("0.000"); NumberFormat ynf1 = new DecimalFormat("0.00"); NumberFormat ynf2 = new DecimalFormat("0.000"); NumberFormat znf1 = new DecimalFormat("0.00"); NumberFormat znf2 = new DecimalFormat("0.000"); BubbleXYItemLabelGenerator g1 = null; BubbleXYItemLabelGenerator g2 = null; g1 = new BubbleXYItemLabelGenerator(f1, xnf1, ynf1, znf1); g2 = new BubbleXYItemLabelGenerator(f1, xnf1, ynf1, znf1); assertEquals(g1, g2); assertEquals(g2, g1); g1 = new BubbleXYItemLabelGenerator(f2, xnf1, ynf1, znf1); assertNotEquals(g1, g2); g2 = new BubbleXYItemLabelGenerator(f2, xnf1, ynf1, znf1); assertEquals(g1, g2); g1 = new BubbleXYItemLabelGenerator(f2, xnf2, ynf1, znf1); assertNotEquals(g1, g2); g2 = new BubbleXYItemLabelGenerator(f2, xnf2, ynf1, znf1); assertEquals(g1, g2); g1 = new BubbleXYItemLabelGenerator(f2, xnf2, ynf2, znf1); assertNotEquals(g1, g2); g2 = new BubbleXYItemLabelGenerator(f2, xnf2, ynf2, znf1); assertEquals(g1, g2); g1 = new BubbleXYItemLabelGenerator(f2, xnf2, ynf2, znf2); assertNotEquals(g1, g2); g2 = new BubbleXYItemLabelGenerator(f2, xnf2, ynf2, znf2); assertEquals(g1, g2); DateFormat xdf1 = new SimpleDateFormat("d-MMM"); DateFormat xdf2 = new SimpleDateFormat("d-MMM-yyyy"); DateFormat ydf1 = new SimpleDateFormat("d-MMM"); DateFormat ydf2 = new SimpleDateFormat("d-MMM-yyyy"); DateFormat zdf1 = new SimpleDateFormat("d-MMM"); DateFormat zdf2 = new SimpleDateFormat("d-MMM-yyyy"); g1 = new BubbleXYItemLabelGenerator(f1, xdf1, ydf1, zdf1); g2 = new BubbleXYItemLabelGenerator(f1, xdf1, ydf1, zdf1); assertEquals(g1, g2); assertEquals(g2, g1); g1 = new BubbleXYItemLabelGenerator(f1, xdf2, ydf1, zdf1); assertNotEquals(g1, g2); g2 = new BubbleXYItemLabelGenerator(f1, xdf2, ydf1, zdf1); assertEquals(g1, g2); g1 = new BubbleXYItemLabelGenerator(f1, xdf2, ydf2, zdf1); assertNotEquals(g1, g2); g2 = new BubbleXYItemLabelGenerator(f1, xdf2, ydf2, zdf1); assertEquals(g1, g2); g1 = new BubbleXYItemLabelGenerator(f1, xdf2, ydf2, zdf2); assertNotEquals(g1, g2); g2 = new BubbleXYItemLabelGenerator(f1, xdf2, ydf2, zdf2); assertEquals(g1, g2); } /** * Simple check that hashCode is implemented. */ @Test public void testHashCode() { BubbleXYItemLabelGenerator g1 = new BubbleXYItemLabelGenerator(); BubbleXYItemLabelGenerator g2 = new BubbleXYItemLabelGenerator(); assertEquals(g1, g2); assertEquals(g1.hashCode(), g2.hashCode()); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { BubbleXYItemLabelGenerator g1 = new BubbleXYItemLabelGenerator(); BubbleXYItemLabelGenerator g2 = (BubbleXYItemLabelGenerator) g1.clone(); assertNotSame(g1, g2); assertSame(g1.getClass(), g2.getClass()); assertEquals(g1, g2); } /** * Check to ensure that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { BubbleXYItemLabelGenerator g1 = new BubbleXYItemLabelGenerator(); assertTrue(g1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { BubbleXYItemLabelGenerator g1 = new BubbleXYItemLabelGenerator(); BubbleXYItemLabelGenerator g2 = TestUtils.serialised(g1); assertEquals(g1, g2); } /** * Some checks for the testGenerateLabel() method. */ @Test public void testGenerateLabel() { // check handling when the dataset is a regular XYDataset, not an // XYZDataset... XYSeries s1 = new XYSeries("S1"); s1.add(1.0, 2.0); s1.add(2.2, 3.3); XYSeriesCollection dataset = new XYSeriesCollection(s1); BubbleXYItemLabelGenerator g = new BubbleXYItemLabelGenerator(); assertEquals("{3}", g.generateLabel(dataset, 0, 0)); assertEquals("{3}", g.generateLabel(dataset, 0, 1)); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/labels/CustomXYItemLabelGeneratorTest.java000066400000000000000000000061661463604235500332500ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------------- * CustomXYItemLabelGeneratorTest.java * ----------------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import java.util.ArrayList; import java.util.List; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link CustomXYToolTipGenerator} class. */ public class CustomXYItemLabelGeneratorTest { /** * Confirm that cloning works. * * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { CustomXYToolTipGenerator g1 = new CustomXYToolTipGenerator(); CustomXYToolTipGenerator g2 = (CustomXYToolTipGenerator) g1.clone(); assertNotSame(g1, g2); assertSame(g1.getClass(), g2.getClass()); assertEquals(g1, g2); } /** * Check to ensure that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { CustomXYToolTipGenerator g1 = new CustomXYToolTipGenerator(); assertTrue(g1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { List t1 = new ArrayList<>(); t1.add("Tooltip A1"); t1.add("Tooltip A2"); t1.add("Tooltip A3"); List t2 = new ArrayList<>(); t2.add("Tooltip B1"); t2.add("Tooltip B2"); t2.add("Tooltip B3"); CustomXYToolTipGenerator g1 = new CustomXYToolTipGenerator(); g1.addToolTipSeries(t1); g1.addToolTipSeries(t2); CustomXYToolTipGenerator g2 = TestUtils.serialised(g1); assertEquals(g1, g2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/labels/HighLowItemLabelGeneratorTest.java000066400000000000000000000100561463604235500330470ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------------- * HighLowItemLabelGeneratorTest.java * ---------------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import java.text.DecimalFormat; import java.text.NumberFormat; import java.text.SimpleDateFormat; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link HighLowItemLabelGenerator} class. */ public class HighLowItemLabelGeneratorTest { /** * Tests that the equals method can distinguish all fields. */ @Test public void testEquals() { HighLowItemLabelGenerator g1 = new HighLowItemLabelGenerator(); HighLowItemLabelGenerator g2 = new HighLowItemLabelGenerator(); assertEquals(g1, g2); assertEquals(g2, g1); g1 = new HighLowItemLabelGenerator(new SimpleDateFormat("d-MMM-yyyy"), NumberFormat.getInstance()); assertNotEquals(g1, g2); g2 = new HighLowItemLabelGenerator(new SimpleDateFormat("d-MMM-yyyy"), NumberFormat.getInstance()); assertEquals(g1, g2); g1 = new HighLowItemLabelGenerator(new SimpleDateFormat("d-MMM-yyyy"), new DecimalFormat("0.000")); assertNotEquals(g1, g2); g2 = new HighLowItemLabelGenerator(new SimpleDateFormat("d-MMM-yyyy"), new DecimalFormat("0.000")); assertEquals(g1, g2); } /** * Simple check that hashCode is implemented. */ @Test public void testHashCode() { HighLowItemLabelGenerator g1 = new HighLowItemLabelGenerator(); HighLowItemLabelGenerator g2 = new HighLowItemLabelGenerator(); assertEquals(g1, g2); assertEquals(g1.hashCode(), g2.hashCode()); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { HighLowItemLabelGenerator g1 = new HighLowItemLabelGenerator(); HighLowItemLabelGenerator g2 = (HighLowItemLabelGenerator) g1.clone(); assertNotSame(g1, g2); assertSame(g1.getClass(), g2.getClass()); assertEquals(g1, g2); } /** * Check to ensure that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { HighLowItemLabelGenerator g1 = new HighLowItemLabelGenerator(); assertTrue(g1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { HighLowItemLabelGenerator g1 = new HighLowItemLabelGenerator(); HighLowItemLabelGenerator g2 = TestUtils.serialised(g1); assertEquals(g1, g2); } } IntervalCategoryItemLabelGeneratorTest.java000066400000000000000000000105301463604235500347060ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/labels/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------------------- * IntervalCategoryItemLabelGeneratorTest.java * ------------------------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import java.text.DateFormat; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link IntervalCategoryItemLabelGenerator} class. */ public class IntervalCategoryItemLabelGeneratorTest { /** * Tests the equals() method. */ @Test public void testEquals() { IntervalCategoryItemLabelGenerator g1 = new IntervalCategoryItemLabelGenerator(); IntervalCategoryItemLabelGenerator g2 = new IntervalCategoryItemLabelGenerator(); assertEquals(g1, g2); assertEquals(g2, g1); g1 = new IntervalCategoryItemLabelGenerator("{3} - {4}", new DecimalFormat("0.000")); assertNotEquals(g1, g2); g2 = new IntervalCategoryItemLabelGenerator("{3} - {4}", new DecimalFormat("0.000")); assertEquals(g1, g2); g1 = new IntervalCategoryItemLabelGenerator("{3} - {4}", new SimpleDateFormat("d-MMM")); assertNotEquals(g1, g2); g2 = new IntervalCategoryItemLabelGenerator("{3} - {4}", new SimpleDateFormat("d-MMM")); assertEquals(g1, g2); } /** * Simple check that hashCode is implemented. */ @Test public void testHashCode() { IntervalCategoryItemLabelGenerator g1 = new IntervalCategoryItemLabelGenerator(); IntervalCategoryItemLabelGenerator g2 = new IntervalCategoryItemLabelGenerator(); assertEquals(g1, g2); assertEquals(g1.hashCode(), g2.hashCode()); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { IntervalCategoryItemLabelGenerator g1 = new IntervalCategoryItemLabelGenerator(); IntervalCategoryItemLabelGenerator g2 = (IntervalCategoryItemLabelGenerator) g1.clone(); assertNotSame(g1, g2); assertSame(g1.getClass(), g2.getClass()); assertEquals(g1, g2); } /** * Check to ensure that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { IntervalCategoryItemLabelGenerator g1 = new IntervalCategoryItemLabelGenerator(); assertTrue(g1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { IntervalCategoryItemLabelGenerator g1 = new IntervalCategoryItemLabelGenerator("{3} - {4}", DateFormat.getInstance()); IntervalCategoryItemLabelGenerator g2 = TestUtils.serialised(g1); assertEquals(g1, g2); } } IntervalCategoryToolTipGeneratorTest.java000066400000000000000000000117151463604235500344500ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/labels/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------------------- * IntervalCategoryToolTipGeneratorTest.java * ----------------------------------------- * (C) Copyright 2008-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import java.text.DateFormat; import java.text.DecimalFormat; import java.text.NumberFormat; import java.text.SimpleDateFormat; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link IntervalCategoryToolTipGenerator} class. */ public class IntervalCategoryToolTipGeneratorTest { /** * Tests the equals() method. */ @Test public void testEquals() { IntervalCategoryToolTipGenerator g1 = new IntervalCategoryToolTipGenerator(); IntervalCategoryToolTipGenerator g2 = new IntervalCategoryToolTipGenerator(); assertEquals(g1, g2); assertEquals(g2, g1); g1 = new IntervalCategoryToolTipGenerator("{3} - {4}", new DecimalFormat("0.000")); assertNotEquals(g1, g2); g2 = new IntervalCategoryToolTipGenerator("{3} - {4}", new DecimalFormat("0.000")); assertEquals(g1, g2); g1 = new IntervalCategoryToolTipGenerator("{3} - {4}", new SimpleDateFormat("d-MMM")); assertNotEquals(g1, g2); g2 = new IntervalCategoryToolTipGenerator("{3} - {4}", new SimpleDateFormat("d-MMM")); assertEquals(g1, g2); } /** * Check that the subclass is not equal to an instance of the superclass. */ @Test public void testEquals2() { IntervalCategoryToolTipGenerator g1 = new IntervalCategoryToolTipGenerator(); StandardCategoryToolTipGenerator g2 = new StandardCategoryToolTipGenerator( IntervalCategoryToolTipGenerator.DEFAULT_TOOL_TIP_FORMAT_STRING, NumberFormat.getInstance()); assertNotEquals(g1, g2); } /** * Simple check that hashCode is implemented. */ @Test public void testHashCode() { IntervalCategoryToolTipGenerator g1 = new IntervalCategoryToolTipGenerator(); IntervalCategoryToolTipGenerator g2 = new IntervalCategoryToolTipGenerator(); assertEquals(g1, g2); assertEquals(g1.hashCode(), g2.hashCode()); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { IntervalCategoryToolTipGenerator g1 = new IntervalCategoryToolTipGenerator(); IntervalCategoryToolTipGenerator g2 = (IntervalCategoryToolTipGenerator) g1.clone(); assertNotSame(g1, g2); assertSame(g1.getClass(), g2.getClass()); assertEquals(g1, g2); } /** * Check to ensure that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { IntervalCategoryToolTipGenerator g1 = new IntervalCategoryToolTipGenerator(); assertTrue(g1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { IntervalCategoryToolTipGenerator g1 = new IntervalCategoryToolTipGenerator("{3} - {4}", DateFormat.getInstance()); IntervalCategoryToolTipGenerator g2 = TestUtils.serialised(g1); assertEquals(g1, g2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/labels/ItemLabelAnchorTest.java000066400000000000000000000042341463604235500310520ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * ItemLabelAnchorTest.java * ------------------------ * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link ItemLabelAnchor} class. */ public class ItemLabelAnchorTest { /** * Test the equals() method. */ @Test public void testEquals() { assertEquals(ItemLabelAnchor.INSIDE1, ItemLabelAnchor.INSIDE1); assertNotEquals(ItemLabelAnchor.INSIDE1, ItemLabelAnchor.INSIDE2); } /** * Serialize an instance, restore it, and check for identity. */ @Test public void testSerialization() { ItemLabelAnchor a1 = ItemLabelAnchor.INSIDE1; ItemLabelAnchor a2 = TestUtils.serialised(a1); assertSame(a1, a2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/labels/ItemLabelPositionTest.java000066400000000000000000000053071463604235500314460ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * ItemLabelPositionTest.java * -------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.labels; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import static org.junit.jupiter.api.Assertions.assertEquals; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; /** * Tests for the {@link ItemLabelPosition} class. */ public class ItemLabelPositionTest { /** * Use EqualsVerifier to ensure correct implementation of equals and * hashCode. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(ItemLabelPosition.class) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { ItemLabelPosition p1 = new ItemLabelPosition(); ItemLabelPosition p2 = new ItemLabelPosition(); assertEquals(p1, p2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { ItemLabelPosition p1 = new ItemLabelPosition(); ItemLabelPosition p2 = TestUtils.serialised(p1); assertEquals(p1, p2); } } MultipleXYSeriesLabelGeneratorTest.java000066400000000000000000000102631463604235500340370ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/labels/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------------------- * MultipleXYSeriesLabelGeneratorTest.java * --------------------------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link MultipleXYSeriesLabelGenerator} class. */ public class MultipleXYSeriesLabelGeneratorTest { /** * A series of tests for the equals() method. */ @Test public void testEquals() { MultipleXYSeriesLabelGenerator g1 = new MultipleXYSeriesLabelGenerator(); MultipleXYSeriesLabelGenerator g2 = new MultipleXYSeriesLabelGenerator(); assertEquals(g1, g2); assertEquals(g2, g1); g1 = new MultipleXYSeriesLabelGenerator("Series {0}"); assertNotEquals(g1, g2); g2 = new MultipleXYSeriesLabelGenerator("Series {0}"); assertEquals(g1, g2); g1.addSeriesLabel(1, "Additional 1"); assertNotEquals(g1, g2); g2.addSeriesLabel(1, "Additional 1"); assertEquals(g1, g2); } /** * Simple check that hashCode is implemented. */ @Test public void testHashCode() { MultipleXYSeriesLabelGenerator g1 = new MultipleXYSeriesLabelGenerator(); MultipleXYSeriesLabelGenerator g2 = new MultipleXYSeriesLabelGenerator(); assertEquals(g1, g2); assertEquals(g1.hashCode(), g2.hashCode()); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { MultipleXYSeriesLabelGenerator g1 = new MultipleXYSeriesLabelGenerator(); MultipleXYSeriesLabelGenerator g2 = (MultipleXYSeriesLabelGenerator) g1.clone(); assertNotSame(g1, g2); assertSame(g1.getClass(), g2.getClass()); assertEquals(g1, g2); // check independence g1.addSeriesLabel(3, "Add3"); assertNotEquals(g1, g2); g2.addSeriesLabel(3, "Add3"); assertEquals(g1, g2); } /** * Check to ensure that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { MultipleXYSeriesLabelGenerator g1 = new MultipleXYSeriesLabelGenerator(); assertTrue(g1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { MultipleXYSeriesLabelGenerator g1 = new MultipleXYSeriesLabelGenerator(); g1.addSeriesLabel(0, "Add0"); g1.addSeriesLabel(0, "Add0b"); g1.addSeriesLabel(1, "Add1"); MultipleXYSeriesLabelGenerator g2 = TestUtils.serialised(g1); assertEquals(g1, g2); } } StandardCategoryItemLabelGeneratorTest.java000066400000000000000000000146301463604235500346670ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/labels/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------------------- * StandardCategoryItemLabelGeneratorTest.java * ------------------------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.labels; import java.text.DateFormat; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.category.DefaultCategoryDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link StandardCategoryItemLabelGenerator} class. */ public class StandardCategoryItemLabelGeneratorTest { /** * Use EqualsVerifier to ensure correct implementation of equals and * hashCode. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(StandardCategoryItemLabelGenerator.class) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .withRedefinedSuperclass() .verify(); } /** * Some checks for the generalLabel() method. */ @Test public void testGenerateLabel() { StandardCategoryItemLabelGenerator g = new StandardCategoryItemLabelGenerator("{2}", new DecimalFormat("0.000")); DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.addValue(1.0, "R0", "C0"); dataset.addValue(2.0, "R0", "C1"); dataset.addValue(3.0, "R1", "C0"); dataset.addValue(null, "R1", "C1"); String s = g.generateLabel(dataset, 0, 0); assertTrue(s.startsWith("1")); assertTrue(s.endsWith("000")); // try a null value s = g.generateLabel(dataset, 1, 1); assertEquals("-", s); } /** * Some checks for the equals() method. */ @Test public void testEquals() { StandardCategoryItemLabelGenerator g1 = new StandardCategoryItemLabelGenerator(); StandardCategoryItemLabelGenerator g2 = new StandardCategoryItemLabelGenerator(); assertEquals(g1, g2); assertEquals(g2, g1); g1 = new StandardCategoryItemLabelGenerator("{0}", new DecimalFormat("0.000")); assertNotEquals(g1, g2); g2 = new StandardCategoryItemLabelGenerator("{0}", new DecimalFormat("0.000")); assertEquals(g1, g2); g1 = new StandardCategoryItemLabelGenerator("{1}", new DecimalFormat("0.000")); assertNotEquals(g1, g2); g2 = new StandardCategoryItemLabelGenerator("{1}", new DecimalFormat("0.000")); assertEquals(g1, g2); g1 = new StandardCategoryItemLabelGenerator("{2}", new SimpleDateFormat("d-MMM")); assertNotEquals(g1, g2); g2 = new StandardCategoryItemLabelGenerator("{2}", new SimpleDateFormat("d-MMM")); assertEquals(g1, g2); } /** * Simple check that hashCode is implemented. */ @Test public void testHashCode() { StandardCategoryItemLabelGenerator g1 = new StandardCategoryItemLabelGenerator(); StandardCategoryItemLabelGenerator g2 = new StandardCategoryItemLabelGenerator(); assertEquals(g1, g2); assertEquals(g1.hashCode(), g2.hashCode()); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { StandardCategoryItemLabelGenerator g1 = new StandardCategoryItemLabelGenerator(); StandardCategoryItemLabelGenerator g2 = (StandardCategoryItemLabelGenerator) g1.clone(); assertNotSame(g1, g2); assertSame(g1.getClass(), g2.getClass()); assertEquals(g1, g2); } /** * Check to ensure that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { StandardCategoryItemLabelGenerator g1 = new StandardCategoryItemLabelGenerator(); assertTrue(g1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { StandardCategoryItemLabelGenerator g1 = new StandardCategoryItemLabelGenerator("{2}", DateFormat.getInstance()); StandardCategoryItemLabelGenerator g2 = TestUtils.serialised(g1); assertEquals(g1, g2); } /** * A test for bug 1481087. */ @Test public void testEquals1481087() { StandardCategoryItemLabelGenerator g1 = new StandardCategoryItemLabelGenerator("{0}", new DecimalFormat("0.00")); StandardCategoryToolTipGenerator g2 = new StandardCategoryToolTipGenerator("{0}", new DecimalFormat("0.00")); assertNotEquals(g1, g2); } } StandardCategorySeriesLabelGeneratorTest.java000066400000000000000000000120471463604235500352230ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/labels/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------------------------- * StandardCategorySeriesLabelGeneratorTest.java * --------------------------------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.labels; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.category.DefaultCategoryDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link StandardCategorySeriesLabelGenerator} class. */ public class StandardCategorySeriesLabelGeneratorTest { /** * Use EqualsVerifier to ensure correct implementation of equals and * hashCode. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(StandardCategorySeriesLabelGenerator.class) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Some checks for the generalLabel() method. */ @Test public void testGenerateLabel() { StandardCategorySeriesLabelGenerator g = new StandardCategorySeriesLabelGenerator("{0}"); DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.addValue(1.0, "R0", "C0"); dataset.addValue(2.0, "R0", "C1"); dataset.addValue(3.0, "R1", "C0"); dataset.addValue(null, "R1", "C1"); String s = g.generateLabel(dataset, 0); assertEquals("R0", s); } /** * Some checks for the equals() method. */ @Test public void testEquals() { StandardCategorySeriesLabelGenerator g1 = new StandardCategorySeriesLabelGenerator(); StandardCategorySeriesLabelGenerator g2 = new StandardCategorySeriesLabelGenerator(); assertEquals(g1, g2); assertEquals(g2, g1); g1 = new StandardCategorySeriesLabelGenerator("{1}"); assertNotEquals(g1, g2); g2 = new StandardCategorySeriesLabelGenerator("{1}"); assertEquals(g1, g2); } /** * Simple check that hashCode is implemented. */ @Test public void testHashCode() { StandardCategorySeriesLabelGenerator g1 = new StandardCategorySeriesLabelGenerator(); StandardCategorySeriesLabelGenerator g2 = new StandardCategorySeriesLabelGenerator(); assertEquals(g1, g2); assertEquals(g1.hashCode(), g2.hashCode()); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { StandardCategorySeriesLabelGenerator g1 = new StandardCategorySeriesLabelGenerator("{1}"); StandardCategorySeriesLabelGenerator g2 = (StandardCategorySeriesLabelGenerator) g1.clone(); assertNotSame(g1, g2); assertSame(g1.getClass(), g2.getClass()); assertEquals(g1, g2); } /** * Check to ensure that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { StandardCategorySeriesLabelGenerator g1 = new StandardCategorySeriesLabelGenerator("{1}"); assertTrue(g1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { StandardCategorySeriesLabelGenerator g1 = new StandardCategorySeriesLabelGenerator("{2}"); StandardCategorySeriesLabelGenerator g2 = TestUtils.serialised(g1); assertEquals(g1, g2); } } StandardCategoryToolTipGeneratorTest.java000066400000000000000000000130571463604235500344250ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/labels/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------------------- * StandardCategoryToolTipGeneratorTest.java * ----------------------------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.labels; import java.text.DateFormat; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link StandardCategoryToolTipGenerator} class. */ public class StandardCategoryToolTipGeneratorTest { /** * Use EqualsVerifier to ensure correct implementation of equals and * hashCode. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(StandardCategoryToolTipGenerator.class) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .withRedefinedSuperclass() .verify(); } /** * Tests the equals() method. */ @Test public void testEquals() { StandardCategoryToolTipGenerator g1 = new StandardCategoryToolTipGenerator(); StandardCategoryToolTipGenerator g2 = new StandardCategoryToolTipGenerator(); assertEquals(g1, g2); assertEquals(g2, g1); g1 = new StandardCategoryToolTipGenerator("{0}", new DecimalFormat("0.000")); assertNotEquals(g1, g2); g2 = new StandardCategoryToolTipGenerator("{0}", new DecimalFormat("0.000")); assertEquals(g1, g2); g1 = new StandardCategoryToolTipGenerator("{1}", new DecimalFormat("0.000")); assertNotEquals(g1, g2); g2 = new StandardCategoryToolTipGenerator("{1}", new DecimalFormat("0.000")); assertEquals(g1, g2); g1 = new StandardCategoryToolTipGenerator("{2}", new SimpleDateFormat("d-MMM")); assertNotEquals(g1, g2); g2 = new StandardCategoryToolTipGenerator("{2}", new SimpleDateFormat("d-MMM")); assertEquals(g1, g2); } /** * Simple check that hashCode is implemented. */ @Test public void testHashCode() { StandardCategoryToolTipGenerator g1 = new StandardCategoryToolTipGenerator(); StandardCategoryToolTipGenerator g2 = new StandardCategoryToolTipGenerator(); assertEquals(g1, g2); assertEquals(g1.hashCode(), g2.hashCode()); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { StandardCategoryToolTipGenerator g1 = new StandardCategoryToolTipGenerator(); StandardCategoryToolTipGenerator g2 = (StandardCategoryToolTipGenerator) g1.clone(); assertNotSame(g1, g2); assertSame(g1.getClass(), g2.getClass()); assertEquals(g1, g2); } /** * Check to ensure that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { StandardCategoryToolTipGenerator g1 = new StandardCategoryToolTipGenerator(); assertTrue(g1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { StandardCategoryToolTipGenerator g1 = new StandardCategoryToolTipGenerator("{2}", DateFormat.getInstance()); StandardCategoryToolTipGenerator g2 = TestUtils.serialised(g1); assertEquals(g1, g2); } /** * A test for bug 1481087. */ @Test public void testEquals1481087() { StandardCategoryToolTipGenerator g1 = new StandardCategoryToolTipGenerator("{0}", new DecimalFormat("0.00")); StandardCategoryItemLabelGenerator g2 = new StandardCategoryItemLabelGenerator("{0}", new DecimalFormat("0.00")); assertNotEquals(g1, g2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/labels/StandardFlowLabelGeneratorTest.java000066400000000000000000000045311463604235500332600ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------------- * StandardFlowLabelGeneratorTest.java * ----------------------------------- * (C) Copyright 2022, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import org.jfree.chart.TestUtils; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; /** * Tests for the {@link StandardFlowLabelGenerator} class. */ public class StandardFlowLabelGeneratorTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { StandardFlowLabelGenerator g1 = new StandardFlowLabelGenerator(); StandardFlowLabelGenerator g2 = new StandardFlowLabelGenerator(); assertEquals(g1, g2); assertEquals(g2, g1); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { StandardFlowLabelGenerator g1 = new StandardFlowLabelGenerator(); StandardFlowLabelGenerator g2 = TestUtils.serialised(g1); assertEquals(g1, g2); } } StandardPieSectionLabelGeneratorTest.java000066400000000000000000000124771463604235500343440ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/labels/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------------------- * StandardPieSectionLabelGeneratorTest.java * ----------------------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import java.text.AttributedString; import java.text.DecimalFormat; import java.text.NumberFormat; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link StandardPieSectionLabelGenerator} class. */ public class StandardPieSectionLabelGeneratorTest { /** * Test that the equals() method distinguishes all fields. */ @Test public void testEquals() { StandardPieSectionLabelGenerator g1 = new StandardPieSectionLabelGenerator(); StandardPieSectionLabelGenerator g2 = new StandardPieSectionLabelGenerator(); assertEquals(g1, g2); assertEquals(g2, g1); g1 = new StandardPieSectionLabelGenerator("{0}", new DecimalFormat("#,##0.00"), NumberFormat.getPercentInstance()); assertNotEquals(g1, g2); g2 = new StandardPieSectionLabelGenerator("{0}", new DecimalFormat("#,##0.00"), NumberFormat.getPercentInstance()); assertEquals(g1, g2); g1 = new StandardPieSectionLabelGenerator("{0} {1}", new DecimalFormat("#,##0.00"), NumberFormat.getPercentInstance()); assertNotEquals(g1, g2); g2 = new StandardPieSectionLabelGenerator("{0} {1}", new DecimalFormat("#,##0.00"), NumberFormat.getPercentInstance()); assertEquals(g1, g2); g1 = new StandardPieSectionLabelGenerator("{0} {1}", new DecimalFormat("#,##0"), NumberFormat.getPercentInstance()); assertNotEquals(g1, g2); g2 = new StandardPieSectionLabelGenerator("{0} {1}", new DecimalFormat("#,##0"), NumberFormat.getPercentInstance()); assertEquals(g1, g2); g1 = new StandardPieSectionLabelGenerator("{0} {1}", new DecimalFormat("#,##0"), new DecimalFormat("0.000%")); assertNotEquals(g1, g2); g2 = new StandardPieSectionLabelGenerator("{0} {1}", new DecimalFormat("#,##0"), new DecimalFormat("0.000%")); assertEquals(g1, g2); AttributedString as = new AttributedString("XYZ"); g1.setAttributedLabel(0, as); assertNotEquals(g1, g2); g2.setAttributedLabel(0, as); assertEquals(g1, g2); } /** * Simple check that hashCode is implemented. */ @Test public void testHashCode() { StandardPieSectionLabelGenerator g1 = new StandardPieSectionLabelGenerator(); StandardPieSectionLabelGenerator g2 = new StandardPieSectionLabelGenerator(); assertEquals(g1, g2); assertEquals(g1.hashCode(), g2.hashCode()); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { StandardPieSectionLabelGenerator g1 = new StandardPieSectionLabelGenerator(); StandardPieSectionLabelGenerator g2 = (StandardPieSectionLabelGenerator) g1.clone(); assertNotSame(g1, g2); assertSame(g1.getClass(), g2.getClass()); assertEquals(g1, g2); } /** * Check to ensure that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { StandardPieSectionLabelGenerator g1 = new StandardPieSectionLabelGenerator(); assertTrue(g1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { StandardPieSectionLabelGenerator g1 = new StandardPieSectionLabelGenerator(); StandardPieSectionLabelGenerator g2 = TestUtils.serialised(g1); assertEquals(g1, g2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/labels/StandardPieToolTipGeneratorTest.java000066400000000000000000000117641463604235500334470ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------------ * StandardPieToolTipGeneratorTest.java * ------------------------------------ * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import java.text.DecimalFormat; import java.text.NumberFormat; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link StandardPieToolTipGenerator} class. */ public class StandardPieToolTipGeneratorTest { /** * Test that the equals() method distinguishes all fields. */ @Test public void testEquals() { StandardPieToolTipGenerator g1 = new StandardPieToolTipGenerator(); StandardPieToolTipGenerator g2 = new StandardPieToolTipGenerator(); assertEquals(g1, g2); assertEquals(g2, g1); g1 = new StandardPieToolTipGenerator("{0}", new DecimalFormat("#,##0.00"), NumberFormat.getPercentInstance()); assertNotEquals(g1, g2); g2 = new StandardPieToolTipGenerator("{0}", new DecimalFormat("#,##0.00"), NumberFormat.getPercentInstance()); assertEquals(g1, g2); g1 = new StandardPieToolTipGenerator("{0} {1}", new DecimalFormat("#,##0.00"), NumberFormat.getPercentInstance()); assertNotEquals(g1, g2); g2 = new StandardPieToolTipGenerator("{0} {1}", new DecimalFormat("#,##0.00"), NumberFormat.getPercentInstance()); assertEquals(g1, g2); g1 = new StandardPieToolTipGenerator("{0} {1}", new DecimalFormat("#,##0"), NumberFormat.getPercentInstance()); assertNotEquals(g1, g2); g2 = new StandardPieToolTipGenerator("{0} {1}", new DecimalFormat("#,##0"), NumberFormat.getPercentInstance()); assertEquals(g1, g2); g1 = new StandardPieToolTipGenerator("{0} {1}", new DecimalFormat("#,##0"), new DecimalFormat("0.000%")); assertNotEquals(g1, g2); g2 = new StandardPieToolTipGenerator("{0} {1}", new DecimalFormat("#,##0"), new DecimalFormat("0.000%")); assertEquals(g1, g2); } /** * Simple check that hashCode is implemented. */ @Test public void testHashCode() { StandardPieToolTipGenerator g1 = new StandardPieToolTipGenerator(); StandardPieToolTipGenerator g2 = new StandardPieToolTipGenerator(); assertEquals(g1, g2); assertEquals(g1.hashCode(), g2.hashCode()); } /** * Some checks for cloning. */ @Test public void testCloning() throws CloneNotSupportedException { StandardPieToolTipGenerator g1 = new StandardPieToolTipGenerator(); StandardPieToolTipGenerator g2 = (StandardPieToolTipGenerator) g1.clone(); assertNotSame(g1, g2); assertSame(g1.getClass(), g2.getClass()); assertEquals(g1, g2); assertNotSame(g1.getNumberFormat(), g2.getNumberFormat()); assertNotSame(g1.getPercentFormat(), g2.getPercentFormat()); } /** * Check to ensure that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { StandardPieToolTipGenerator g1 = new StandardPieToolTipGenerator(); assertTrue(g1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { StandardPieToolTipGenerator g1 = new StandardPieToolTipGenerator(); StandardPieToolTipGenerator g2 = TestUtils.serialised(g1); assertEquals(g1, g2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/labels/StandardXYItemLabelGeneratorTest.java000066400000000000000000000146111463604235500335300ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------------- * StandardXYItemLabelGeneratorTest.java * ------------------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import java.text.DateFormat; import java.text.DecimalFormat; import java.text.NumberFormat; import java.text.SimpleDateFormat; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link StandardXYItemLabelGenerator} class. */ public class StandardXYItemLabelGeneratorTest { /** * A series of tests for the equals() method. */ @Test public void testEquals() { // some setup... String f1 = "{1}"; String f2 = "{2}"; NumberFormat xnf1 = new DecimalFormat("0.00"); NumberFormat xnf2 = new DecimalFormat("0.000"); NumberFormat ynf1 = new DecimalFormat("0.00"); NumberFormat ynf2 = new DecimalFormat("0.000"); StandardXYItemLabelGenerator g1 = null; StandardXYItemLabelGenerator g2 = null; g1 = new StandardXYItemLabelGenerator(f1, xnf1, ynf1); g2 = new StandardXYItemLabelGenerator(f1, xnf1, ynf1); assertEquals(g1, g2); assertEquals(g2, g1); g1 = new StandardXYItemLabelGenerator(f2, xnf1, ynf1); assertNotEquals(g1, g2); g2 = new StandardXYItemLabelGenerator(f2, xnf1, ynf1); assertEquals(g1, g2); g1 = new StandardXYItemLabelGenerator(f2, xnf2, ynf1); assertNotEquals(g1, g2); g2 = new StandardXYItemLabelGenerator(f2, xnf2, ynf1); assertEquals(g1, g2); g1 = new StandardXYItemLabelGenerator(f2, xnf2, ynf2); assertNotEquals(g1, g2); g2 = new StandardXYItemLabelGenerator(f2, xnf2, ynf2); assertEquals(g1, g2); DateFormat xdf1 = new SimpleDateFormat("d-MMM"); DateFormat xdf2 = new SimpleDateFormat("d-MMM-yyyy"); DateFormat ydf1 = new SimpleDateFormat("d-MMM"); DateFormat ydf2 = new SimpleDateFormat("d-MMM-yyyy"); g1 = new StandardXYItemLabelGenerator(f1, xdf1, ydf1); g2 = new StandardXYItemLabelGenerator(f1, xdf1, ydf1); assertEquals(g1, g2); assertEquals(g2, g1); g1 = new StandardXYItemLabelGenerator(f1, xdf2, ydf1); assertNotEquals(g1, g2); g2 = new StandardXYItemLabelGenerator(f1, xdf2, ydf1); assertEquals(g1, g2); g1 = new StandardXYItemLabelGenerator(f1, xdf2, ydf2); assertNotEquals(g1, g2); g2 = new StandardXYItemLabelGenerator(f1, xdf2, ydf2); assertEquals(g1, g2); } /** * Simple check that hashCode is implemented. */ @Test public void testHashCode() { StandardXYItemLabelGenerator g1 = new StandardXYItemLabelGenerator(); StandardXYItemLabelGenerator g2 = new StandardXYItemLabelGenerator(); assertEquals(g1, g2); assertEquals(g1.hashCode(), g2.hashCode()); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { StandardXYItemLabelGenerator g1 = new StandardXYItemLabelGenerator(); StandardXYItemLabelGenerator g2 = (StandardXYItemLabelGenerator) g1.clone(); assertNotSame(g1, g2); assertSame(g1.getClass(), g2.getClass()); assertEquals(g1, g2); // check independence g1.getXFormat().setMinimumIntegerDigits(2); assertNotEquals(g1, g2); g2.getXFormat().setMinimumIntegerDigits(2); assertEquals(g1, g2); g1.getYFormat().setMinimumIntegerDigits(2); assertNotEquals(g1, g2); g2.getYFormat().setMinimumIntegerDigits(2); assertEquals(g1, g2); // another test... g1 = new StandardXYItemLabelGenerator("{0} {1} {2}", DateFormat.getInstance(), DateFormat.getInstance()); g2 = (StandardXYItemLabelGenerator) g1.clone(); assertNotSame(g1, g2); assertSame(g1.getClass(), g2.getClass()); assertEquals(g1, g2); // check independence g1.getXDateFormat().setNumberFormat(new DecimalFormat("0.000")); assertNotEquals(g1, g2); g2.getXDateFormat().setNumberFormat(new DecimalFormat("0.000")); assertEquals(g1, g2); g1.getYDateFormat().setNumberFormat(new DecimalFormat("0.000")); assertNotEquals(g1, g2); g2.getYDateFormat().setNumberFormat(new DecimalFormat("0.000")); assertEquals(g1, g2); } /** * Check to ensure that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { StandardXYItemLabelGenerator g1 = new StandardXYItemLabelGenerator(); assertTrue(g1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { StandardXYItemLabelGenerator g1 = new StandardXYItemLabelGenerator(); StandardXYItemLabelGenerator g2 = TestUtils.serialised(g1); assertEquals(g1, g2); } } StandardXYSeriesLabelGeneratorTest.java000066400000000000000000000105601463604235500340040ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/labels/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------------------- * StandardXYSeriesLabelGeneratorTest.java * --------------------------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link StandardXYSeriesLabelGenerator} class. */ public class StandardXYSeriesLabelGeneratorTest { /** * Some checks for the generalLabel() method. */ @Test public void testGenerateLabel() { StandardXYSeriesLabelGenerator g = new StandardXYSeriesLabelGenerator("Series {0}"); XYSeriesCollection dataset = new XYSeriesCollection(); dataset.addSeries(new XYSeries("1")); dataset.addSeries(new XYSeries("2")); assertEquals("Series 1", g.generateLabel(dataset, 0)); assertEquals("Series 2", g.generateLabel(dataset, 1)); } /** * Some checks for the equals() method. */ @Test public void testEquals() { StandardXYSeriesLabelGenerator g1 = new StandardXYSeriesLabelGenerator("Series {0}"); StandardXYSeriesLabelGenerator g2 = new StandardXYSeriesLabelGenerator("Series {0}"); assertEquals(g1, g2); assertEquals(g2, g1); g1 = new StandardXYSeriesLabelGenerator("{1}"); assertNotEquals(g1, g2); g2 = new StandardXYSeriesLabelGenerator("{1}"); assertEquals(g1, g2); } /** * Simple check that hashCode is implemented. */ @Test public void testHashCode() { StandardXYSeriesLabelGenerator g1 = new StandardXYSeriesLabelGenerator(); StandardXYSeriesLabelGenerator g2 = new StandardXYSeriesLabelGenerator(); assertEquals(g1, g2); assertEquals(g1.hashCode(), g2.hashCode()); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { StandardXYSeriesLabelGenerator g1 = new StandardXYSeriesLabelGenerator("Series {0}"); StandardXYSeriesLabelGenerator g2 = (StandardXYSeriesLabelGenerator) g1.clone(); assertNotSame(g1, g2); assertSame(g1.getClass(), g2.getClass()); assertEquals(g1, g2); } /** * Check to ensure that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { StandardXYSeriesLabelGenerator g1 = new StandardXYSeriesLabelGenerator("Series {0}"); assertTrue(g1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { StandardXYSeriesLabelGenerator g1 = new StandardXYSeriesLabelGenerator("Series {0}"); StandardXYSeriesLabelGenerator g2 = TestUtils.serialised(g1); assertEquals(g1, g2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/labels/StandardXYToolTipGeneratorTest.java000066400000000000000000000122561463604235500332670ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------------- * StandardXYToolTipGeneratorTest.java * ----------------------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import java.text.DateFormat; import java.text.DecimalFormat; import java.text.NumberFormat; import java.text.SimpleDateFormat; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link StandardXYToolTipGenerator} class. */ public class StandardXYToolTipGeneratorTest { /** * Tests the equals() method. */ @Test public void testEquals() { // some setup... String f1 = "{1}"; String f2 = "{2}"; NumberFormat xnf1 = new DecimalFormat("0.00"); NumberFormat xnf2 = new DecimalFormat("0.000"); NumberFormat ynf1 = new DecimalFormat("0.00"); NumberFormat ynf2 = new DecimalFormat("0.000"); StandardXYToolTipGenerator g1 = null; StandardXYToolTipGenerator g2 = null; g1 = new StandardXYToolTipGenerator(f1, xnf1, ynf1); g2 = new StandardXYToolTipGenerator(f1, xnf1, ynf1); assertEquals(g1, g2); assertEquals(g2, g1); g1 = new StandardXYToolTipGenerator(f2, xnf1, ynf1); assertNotEquals(g1, g2); g2 = new StandardXYToolTipGenerator(f2, xnf1, ynf1); assertEquals(g1, g2); g1 = new StandardXYToolTipGenerator(f2, xnf2, ynf1); assertNotEquals(g1, g2); g2 = new StandardXYToolTipGenerator(f2, xnf2, ynf1); assertEquals(g1, g2); g1 = new StandardXYToolTipGenerator(f2, xnf2, ynf2); assertNotEquals(g1, g2); g2 = new StandardXYToolTipGenerator(f2, xnf2, ynf2); assertEquals(g1, g2); DateFormat xdf1 = new SimpleDateFormat("d-MMM"); DateFormat xdf2 = new SimpleDateFormat("d-MMM-yyyy"); DateFormat ydf1 = new SimpleDateFormat("d-MMM"); DateFormat ydf2 = new SimpleDateFormat("d-MMM-yyyy"); g1 = new StandardXYToolTipGenerator(f1, xdf1, ydf1); g2 = new StandardXYToolTipGenerator(f1, xdf1, ydf1); assertEquals(g1, g2); assertEquals(g2, g1); g1 = new StandardXYToolTipGenerator(f1, xdf2, ydf1); assertNotEquals(g1, g2); g2 = new StandardXYToolTipGenerator(f1, xdf2, ydf1); assertEquals(g1, g2); g1 = new StandardXYToolTipGenerator(f1, xdf2, ydf2); assertNotEquals(g1, g2); g2 = new StandardXYToolTipGenerator(f1, xdf2, ydf2); assertEquals(g1, g2); } /** * Simple check that hashCode is implemented. */ @Test public void testHashCode() { StandardXYToolTipGenerator g1 = new StandardXYToolTipGenerator(); StandardXYToolTipGenerator g2 = new StandardXYToolTipGenerator(); assertEquals(g1, g2); assertEquals(g1.hashCode(), g2.hashCode()); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { StandardXYToolTipGenerator g1 = new StandardXYToolTipGenerator(); StandardXYToolTipGenerator g2 = (StandardXYToolTipGenerator) g1.clone(); assertNotSame(g1, g2); assertSame(g1.getClass(), g2.getClass()); assertEquals(g1, g2); } /** * Check to ensure that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { StandardXYToolTipGenerator g1 = new StandardXYToolTipGenerator(); assertTrue(g1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { StandardXYToolTipGenerator g1 = new StandardXYToolTipGenerator(); StandardXYToolTipGenerator g2 = TestUtils.serialised(g1); assertEquals(g1, g2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/labels/StandardXYZToolTipGeneratorTest.java000066400000000000000000000141071463604235500334160ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------------ * StandardXYZToolTipGeneratorTest.java * ------------------------------------ * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import java.text.DateFormat; import java.text.DecimalFormat; import java.text.NumberFormat; import java.text.SimpleDateFormat; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link StandardXYZToolTipGenerator} class. */ public class StandardXYZToolTipGeneratorTest { /** * Tests that the equals() method can distinguish all fields. */ @Test public void testEquals() { // some setup... String f1 = "{1}"; String f2 = "{2}"; NumberFormat xnf1 = new DecimalFormat("0.00"); NumberFormat xnf2 = new DecimalFormat("0.000"); NumberFormat ynf1 = new DecimalFormat("0.00"); NumberFormat ynf2 = new DecimalFormat("0.000"); NumberFormat znf1 = new DecimalFormat("0.00"); NumberFormat znf2 = new DecimalFormat("0.000"); DateFormat xdf1 = new SimpleDateFormat("d-MMM"); DateFormat xdf2 = new SimpleDateFormat("d-MMM-yyyy"); DateFormat ydf1 = new SimpleDateFormat("d-MMM"); DateFormat ydf2 = new SimpleDateFormat("d-MMM-yyyy"); DateFormat zdf1 = new SimpleDateFormat("d-MMM"); DateFormat zdf2 = new SimpleDateFormat("d-MMM-yyyy"); StandardXYZToolTipGenerator g1 = null; StandardXYZToolTipGenerator g2 = null; g1 = new StandardXYZToolTipGenerator(f1, xnf1, ynf1, znf1); g2 = new StandardXYZToolTipGenerator(f1, xnf1, ynf1, znf1); assertEquals(g1, g2); // format string... g1 = new StandardXYZToolTipGenerator(f2, xnf1, ynf1, znf1); assertNotEquals(g1, g2); g2 = new StandardXYZToolTipGenerator(f2, xnf1, ynf1, znf1); assertEquals(g1, g2); // x number format g1 = new StandardXYZToolTipGenerator(f2, xnf2, ynf1, znf1); assertNotEquals(g1, g2); g2 = new StandardXYZToolTipGenerator(f2, xnf2, ynf1, znf1); assertEquals(g1, g2); // y number format g1 = new StandardXYZToolTipGenerator(f2, xnf2, ynf2, znf1); assertNotEquals(g1, g2); g2 = new StandardXYZToolTipGenerator(f2, xnf2, ynf2, znf1); assertEquals(g1, g2); // z number format g1 = new StandardXYZToolTipGenerator(f2, xnf2, ynf2, znf2); assertNotEquals(g1, g2); g2 = new StandardXYZToolTipGenerator(f2, xnf2, ynf2, znf2); assertEquals(g1, g2); g1 = new StandardXYZToolTipGenerator(f2, xdf1, ydf1, zdf1); g2 = new StandardXYZToolTipGenerator(f2, xdf1, ydf1, zdf1); assertEquals(g1, g2); // x date format g1 = new StandardXYZToolTipGenerator(f2, xdf2, ydf1, zdf1); assertNotEquals(g1, g2); g2 = new StandardXYZToolTipGenerator(f2, xdf2, ydf1, zdf1); assertEquals(g1, g2); // y date format g1 = new StandardXYZToolTipGenerator(f2, xdf2, ydf2, zdf1); assertNotEquals(g1, g2); g2 = new StandardXYZToolTipGenerator(f2, xdf2, ydf2, zdf1); assertEquals(g1, g2); // z date format g1 = new StandardXYZToolTipGenerator(f2, xdf2, ydf2, zdf2); assertNotEquals(g1, g2); g2 = new StandardXYZToolTipGenerator(f2, xdf2, ydf2, zdf2); assertEquals(g1, g2); } /** * Simple check that hashCode is implemented. */ @Test public void testHashCode() { StandardXYZToolTipGenerator g1 = new StandardXYZToolTipGenerator(); StandardXYZToolTipGenerator g2 = new StandardXYZToolTipGenerator(); assertEquals(g1, g2); assertEquals(g1.hashCode(), g2.hashCode()); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { StandardXYZToolTipGenerator g1 = new StandardXYZToolTipGenerator(); StandardXYZToolTipGenerator g2 = (StandardXYZToolTipGenerator) g1.clone(); assertNotSame(g1, g2); assertSame(g1.getClass(), g2.getClass()); assertEquals(g1, g2); } /** * Check to ensure that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { StandardXYZToolTipGenerator g1 = new StandardXYZToolTipGenerator(); assertTrue(g1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { StandardXYZToolTipGenerator g1 = new StandardXYZToolTipGenerator(); StandardXYZToolTipGenerator g2 = TestUtils.serialised(g1); assertEquals(g1, g2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/labels/SymbolicXYItemLabelGeneratorTest.java000066400000000000000000000066561463604235500335630ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------------- * SymbolicXYItemLabelGeneratorTest.java * ------------------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.labels; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link SymbolicXYItemLabelGenerator} class. */ public class SymbolicXYItemLabelGeneratorTest { /** * Tests the equals method. */ @Test public void testEquals() { SymbolicXYItemLabelGenerator g1 = new SymbolicXYItemLabelGenerator(); SymbolicXYItemLabelGenerator g2 = new SymbolicXYItemLabelGenerator(); assertEquals(g1, g2); assertEquals(g2, g1); } /** * Simple check that hashCode is implemented. */ @Test public void testHashCode() { SymbolicXYItemLabelGenerator g1 = new SymbolicXYItemLabelGenerator(); SymbolicXYItemLabelGenerator g2 = new SymbolicXYItemLabelGenerator(); assertEquals(g1, g2); assertEquals(g1.hashCode(), g2.hashCode()); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { SymbolicXYItemLabelGenerator g1 = new SymbolicXYItemLabelGenerator(); SymbolicXYItemLabelGenerator g2 = (SymbolicXYItemLabelGenerator) g1.clone(); assertNotSame(g1, g2); assertSame(g1.getClass(), g2.getClass()); assertEquals(g1, g2); } /** * Check to ensure that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { SymbolicXYItemLabelGenerator g1 = new SymbolicXYItemLabelGenerator(); assertTrue(g1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { SymbolicXYItemLabelGenerator g1 = new SymbolicXYItemLabelGenerator(); SymbolicXYItemLabelGenerator g2 = TestUtils.serialised(g1); assertEquals(g1, g2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/labels/package.html000066400000000000000000000001631463604235500266340ustar00rootroot00000000000000 JUnit tests. jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/needle/000077500000000000000000000000001463604235500243455ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/needle/ArrowNeedleTest.java000066400000000000000000000052101463604235500302550ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * ArrowNeedleTest.java * -------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.needle; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link ArrowNeedle} class. */ public class ArrowNeedleTest { /** * Check that the equals() method can distinguish all fields. */ @Test public void testEquals() { ArrowNeedle n1 = new ArrowNeedle(false); ArrowNeedle n2 = new ArrowNeedle(false); assertEquals(n1, n2); assertEquals(n2, n1); n1 = new ArrowNeedle(true); assertNotEquals(n1, n2); n2 = new ArrowNeedle(true); assertEquals(n1, n2); } /** * Check that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { ArrowNeedle n1 = new ArrowNeedle(false); ArrowNeedle n2 = (ArrowNeedle) n1.clone(); assertNotSame(n1, n2); assertSame(n1.getClass(), n2.getClass()); assertEquals(n1, n2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { ArrowNeedle n1 = new ArrowNeedle(false); ArrowNeedle n2 = TestUtils.serialised(n1); assertEquals(n1, n2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/needle/LineNeedleTest.java000066400000000000000000000047361463604235500300660ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * LineNeedleTest.java * ------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.needle; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link LineNeedle} class. */ public class LineNeedleTest { /** * Check that the equals() method can distinguish all fields. */ @Test public void testEquals() { LineNeedle n1 = new LineNeedle(); LineNeedle n2 = new LineNeedle(); assertEquals(n1, n2); assertEquals(n2, n1); } /** * Check that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { LineNeedle n1 = new LineNeedle(); LineNeedle n2 = (LineNeedle) n1.clone(); assertNotSame(n1, n2); assertSame(n1.getClass(), n2.getClass()); assertEquals(n1, n2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { LineNeedle n1 = new LineNeedle(); LineNeedle n2 = TestUtils.serialised(n1); assertEquals(n1, n2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/needle/LongNeedleTest.java000066400000000000000000000047361463604235500300760ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * LongNeedleTest.java * ------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.needle; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link LongNeedle} class. */ public class LongNeedleTest { /** * Check that the equals() method can distinguish all fields. */ @Test public void testEquals() { LongNeedle n1 = new LongNeedle(); LongNeedle n2 = new LongNeedle(); assertEquals(n1, n2); assertEquals(n2, n1); } /** * Check that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { LongNeedle n1 = new LongNeedle(); LongNeedle n2 = (LongNeedle) n1.clone(); assertNotSame(n1, n2); assertSame(n1.getClass(), n2.getClass()); assertEquals(n1, n2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { LongNeedle n1 = new LongNeedle(); LongNeedle n2 = TestUtils.serialised(n1); assertEquals(n1, n2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/needle/MeterNeedleTest.java000066400000000000000000000063031463604235500302430ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * MeterNeedleTest.java * -------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.needle; import java.awt.BasicStroke; import java.awt.Color; import java.awt.GradientPaint; import java.awt.Stroke; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link MeterNeedle} class. */ public class MeterNeedleTest { /** * Check that the equals() method can distinguish all fields. */ @Test public void testEquals() { MeterNeedle n1 = new LineNeedle(); MeterNeedle n2 = new LineNeedle(); assertEquals(n1, n2); n1.setFillPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); assertNotEquals(n1, n2); n2.setFillPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); assertEquals(n1, n2); n1.setOutlinePaint(new GradientPaint(5.0f, 6.0f, Color.RED, 7.0f, 8.0f, Color.BLUE)); assertNotEquals(n1, n2); n2.setOutlinePaint(new GradientPaint(5.0f, 6.0f, Color.RED, 7.0f, 8.0f, Color.BLUE)); assertEquals(n1, n2); n1.setHighlightPaint(new GradientPaint(9.0f, 0.0f, Color.RED, 1.0f, 2.0f, Color.BLUE)); assertNotEquals(n1, n2); n2.setHighlightPaint(new GradientPaint(9.0f, 0.0f, Color.RED, 1.0f, 2.0f, Color.BLUE)); assertEquals(n1, n2); Stroke s = new BasicStroke(1.23f); n1.setOutlineStroke(s); assertNotEquals(n1, n2); n2.setOutlineStroke(s); assertEquals(n1, n2); n1.setRotateX(1.23); assertNotEquals(n1, n2); n2.setRotateX(1.23); assertEquals(n1, n2); n1.setRotateY(4.56); assertNotEquals(n1, n2); n2.setRotateY(4.56); assertEquals(n1, n2); n1.setSize(11); assertNotEquals(n1, n2); n2.setSize(11); assertEquals(n1, n2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/needle/MiddlePinNeedleTest.java000066400000000000000000000050561463604235500310400ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * MiddlePinNeedleTest.java * ------------------------ * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.needle; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link MiddlePinNeedle} class. */ public class MiddlePinNeedleTest { /** * Check that the equals() method can distinguish all fields. */ @Test public void testEquals() { MiddlePinNeedle n1 = new MiddlePinNeedle(); MiddlePinNeedle n2 = new MiddlePinNeedle(); assertEquals(n1, n2); assertEquals(n2, n1); } /** * Check that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { MiddlePinNeedle n1 = new MiddlePinNeedle(); MiddlePinNeedle n2 = (MiddlePinNeedle) n1.clone(); assertNotSame(n1, n2); assertSame(n1.getClass(), n2.getClass()); assertEquals(n1, n2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { MiddlePinNeedle n1 = new MiddlePinNeedle(); MiddlePinNeedle n2 = TestUtils.serialised(n1); assertEquals(n1, n2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/needle/PinNeedleTest.java000066400000000000000000000047161463604235500277230ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * PinNeedleTest.java * ------------------ * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.needle; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link PinNeedle} class. */ public class PinNeedleTest { /** * Check that the equals() method can distinguish all fields. */ @Test public void testEquals() { PinNeedle n1 = new PinNeedle(); PinNeedle n2 = new PinNeedle(); assertEquals(n1, n2); assertEquals(n2, n1); } /** * Check that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { PinNeedle n1 = new PinNeedle(); PinNeedle n2 = (PinNeedle) n1.clone(); assertNotSame(n1, n2); assertSame(n1.getClass(), n2.getClass()); assertEquals(n1, n2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { PinNeedle n1 = new PinNeedle(); PinNeedle n2 = TestUtils.serialised(n1); assertEquals(n1, n2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/needle/PlumNeedleTest.java000066400000000000000000000047361463604235500301140ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * PlumNeedleTest.java * ------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.needle; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link PlumNeedle} class. */ public class PlumNeedleTest { /** * Check that the equals() method can distinguish all fields. */ @Test public void testEquals() { PlumNeedle n1 = new PlumNeedle(); PlumNeedle n2 = new PlumNeedle(); assertEquals(n1, n2); assertEquals(n2, n1); } /** * Check that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { PlumNeedle n1 = new PlumNeedle(); PlumNeedle n2 = (PlumNeedle) n1.clone(); assertNotSame(n1, n2); assertSame(n1.getClass(), n2.getClass()); assertEquals(n1, n2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { PlumNeedle n1 = new PlumNeedle(); PlumNeedle n2 = TestUtils.serialised(n1); assertEquals(n1, n2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/needle/PointerNeedleTest.java000066400000000000000000000050161463604235500306070ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * PointerNeedleTest.java * ---------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.needle; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link PointerNeedle} class. */ public class PointerNeedleTest { /** * Check that the equals() method can distinguish all fields. */ @Test public void testEquals() { PointerNeedle n1 = new PointerNeedle(); PointerNeedle n2 = new PointerNeedle(); assertEquals(n1, n2); assertEquals(n2, n1); } /** * Check that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { PointerNeedle n1 = new PointerNeedle(); PointerNeedle n2 = (PointerNeedle) n1.clone(); assertNotSame(n1, n2); assertSame(n1.getClass(), n2.getClass()); assertEquals(n1, n2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { PointerNeedle n1 = new PointerNeedle(); PointerNeedle n2 = TestUtils.serialised(n1); assertEquals(n1, n2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/needle/ShipNeedleTest.java000066400000000000000000000047361463604235500301020ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * ShipNeedleTest.java * ------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.needle; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link ShipNeedle} class. */ public class ShipNeedleTest { /** * Check that the equals() method can distinguish all fields. */ @Test public void testEquals() { ShipNeedle n1 = new ShipNeedle(); ShipNeedle n2 = new ShipNeedle(); assertEquals(n1, n2); assertEquals(n2, n1); } /** * Check that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { ShipNeedle n1 = new ShipNeedle(); ShipNeedle n2 = (ShipNeedle) n1.clone(); assertNotSame(n1, n2); assertSame(n1.getClass(), n2.getClass()); assertEquals(n1, n2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { ShipNeedle n1 = new ShipNeedle(); ShipNeedle n2 = TestUtils.serialised(n1); assertEquals(n1, n2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/needle/WindNeedleTest.java000066400000000000000000000047361463604235500301000ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * WindNeedleTest.java * ------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.needle; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link WindNeedle} class. */ public class WindNeedleTest { /** * Check that the equals() method can distinguish all fields. */ @Test public void testEquals() { WindNeedle n1 = new WindNeedle(); WindNeedle n2 = new WindNeedle(); assertEquals(n1, n2); assertEquals(n2, n1); } /** * Check that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { WindNeedle n1 = new WindNeedle(); WindNeedle n2 = (WindNeedle) n1.clone(); assertNotSame(n1, n2); assertSame(n1.getClass(), n2.getClass()); assertEquals(n1, n2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { WindNeedle n1 = new WindNeedle(); WindNeedle n2 = TestUtils.serialised(n1); assertEquals(n1, n2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/package.html000066400000000000000000000002611463604235500253710ustar00rootroot00000000000000 Test cases for the JFreeChart class library, based on the JUnit framework. jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/panel/000077500000000000000000000000001463604235500242105ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/panel/CrosshairOverlayTest.java000066400000000000000000000063231463604235500312160ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * CrosshairOverlayTest.java * ------------------------- * (C) Copyright 2009-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.panel; import java.awt.BasicStroke; import java.awt.Color; import java.awt.GradientPaint; import org.jfree.chart.TestUtils; import org.jfree.chart.plot.Crosshair; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link CrosshairOverlay} class. */ public class CrosshairOverlayTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { CrosshairOverlay o1 = new CrosshairOverlay(); CrosshairOverlay o2 = new CrosshairOverlay(); assertEquals(o1, o2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { CrosshairOverlay o1 = new CrosshairOverlay(); o1.addDomainCrosshair(new Crosshair(99.9)); o1.addRangeCrosshair(new Crosshair(1.23, new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE), new BasicStroke(1.1f))); CrosshairOverlay o2 = TestUtils.serialised(o1); assertEquals(o1, o2); } /** * Basic checks for cloning. */ @Test public void testCloning() throws CloneNotSupportedException { CrosshairOverlay o1 = new CrosshairOverlay(); o1.addDomainCrosshair(new Crosshair(99.9)); o1.addRangeCrosshair(new Crosshair(1.23, new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE), new BasicStroke(1.1f))); CrosshairOverlay o2 = (CrosshairOverlay) o1.clone(); assertNotSame(o1, o2); assertSame(o1.getClass(), o2.getClass()); assertEquals(o1, o2); o1.addDomainCrosshair(new Crosshair(3.21)); o1.addRangeCrosshair(new Crosshair(4.32)); assertNotEquals(o1, o2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/000077500000000000000000000000001463604235500240675ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/CategoryMarkerTest.java000066400000000000000000000171461463604235500305220ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * CategoryMarkerTest.java * ----------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.plot; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.GradientPaint; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.chart.event.MarkerChangeEvent; import org.jfree.chart.event.MarkerChangeListener; import org.junit.jupiter.api.Test; import javax.swing.event.EventListenerList; import static org.junit.jupiter.api.Assertions.*; /** * Some tests for the {@link CategoryMarker} class. */ public class CategoryMarkerTest implements MarkerChangeListener { MarkerChangeEvent lastEvent; /** * Records the last event. * * @param event the last event. */ @Override public void markerChanged(MarkerChangeEvent event) { this.lastEvent = event; } /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(CategoryMarker.class) .withRedefinedSuperclass() // superclass also defines equals/hashCode .withPrefabValues(Font.class, new Font("SansSerif", Font.PLAIN, 10), new Font("Tahoma", Font.BOLD, 12)) .withPrefabValues(EventListenerList.class, new EventListenerList(), new EventListenerList()) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { CategoryMarker m1 = new CategoryMarker("A"); CategoryMarker m2 = new CategoryMarker("A"); assertEquals(m1, m2); assertEquals(m2, m1); //key m1 = new CategoryMarker("B"); assertNotEquals(m1, m2); m2 = new CategoryMarker("B"); assertEquals(m1, m2); //paint m1 = new CategoryMarker("A", new GradientPaint(1.0f, 2.0f, Color.WHITE, 3.0f, 4.0f, Color.YELLOW), new BasicStroke(1.1f)); assertNotEquals(m1, m2); m2 = new CategoryMarker("A", new GradientPaint(1.0f, 2.0f, Color.WHITE, 3.0f, 4.0f, Color.YELLOW), new BasicStroke(1.1f)); assertEquals(m1, m2); //stroke m1 = new CategoryMarker("A", new GradientPaint(1.0f, 2.0f, Color.WHITE, 3.0f, 4.0f, Color.YELLOW), new BasicStroke(2.2f)); assertNotEquals(m1, m2); m2 = new CategoryMarker("A", new GradientPaint(1.0f, 2.0f, Color.WHITE, 3.0f, 4.0f, Color.YELLOW), new BasicStroke(2.2f)); assertEquals(m1, m2); //outlinePaint m1 = new CategoryMarker("A", new GradientPaint(1.0f, 2.0f, Color.WHITE, 3.0f, 4.0f, Color.YELLOW), new BasicStroke(2.2f), Color.RED, new BasicStroke(1.0f), 1.0f); assertNotEquals(m1, m2); m2 = new CategoryMarker("A", new GradientPaint(1.0f, 2.0f, Color.WHITE, 3.0f, 4.0f, Color.YELLOW), new BasicStroke(2.2f), Color.RED, new BasicStroke(1.0f), 1.0f); assertEquals(m1, m2); //outlineStroke m1 = new CategoryMarker("A", new GradientPaint(1.0f, 2.0f, Color.WHITE, 3.0f, 4.0f, Color.YELLOW), new BasicStroke(2.2f), Color.RED, new BasicStroke(3.3f), 1.0f); assertNotEquals(m1, m2); m2 = new CategoryMarker("A", new GradientPaint(1.0f, 2.0f, Color.WHITE, 3.0f, 4.0f, Color.YELLOW), new BasicStroke(2.2f), Color.RED, new BasicStroke(3.3f), 1.0f); assertEquals(m1, m2); //alpha m1 = new CategoryMarker("A", new GradientPaint(1.0f, 2.0f, Color.WHITE, 3.0f, 4.0f, Color.YELLOW), new BasicStroke(2.2f), Color.RED, new BasicStroke(1.0f), 0.5f); assertNotEquals(m1, m2); m2 = new CategoryMarker("A", new GradientPaint(1.0f, 2.0f, Color.WHITE, 3.0f, 4.0f, Color.YELLOW), new BasicStroke(2.2f), Color.RED, new BasicStroke(1.0f), 0.5f); assertEquals(m1, m2); } /** * Check cloning. * @throws java.lang.CloneNotSupportedException if there is a cloning issue. */ @Test public void testCloning() throws CloneNotSupportedException { CategoryMarker m1 = new CategoryMarker("A", new GradientPaint(1.0f, 2.0f, Color.WHITE, 3.0f, 4.0f, Color.YELLOW), new BasicStroke(1.1f)); CategoryMarker m2 = (CategoryMarker) m1.clone(); assertNotSame(m1, m2); assertSame(m1.getClass(), m2.getClass()); assertEquals(m1, m2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { CategoryMarker m1 = new CategoryMarker("A", new GradientPaint(1.0f, 2.0f, Color.WHITE, 3.0f, 4.0f, Color.YELLOW), new BasicStroke(1.1f)); CategoryMarker m2 = TestUtils.serialised(m1); assertEquals(m1, m2); } /** * Some checks for the getKey() and setKey() methods. */ @Test public void testGetSetKey() { CategoryMarker m = new CategoryMarker("X"); m.addChangeListener(this); this.lastEvent = null; assertEquals("X", m.getKey()); m.setKey("Y"); assertEquals("Y", m.getKey()); assertEquals(m, this.lastEvent.getMarker()); // check null argument... try { m.setKey(null); fail("Expected an IllegalArgumentException for null."); } catch (IllegalArgumentException e) { assertTrue(true); } } /** * Some checks for the getDrawAsLine() and setDrawAsLine() methods. */ @Test public void testGetSetDrawAsLine() { CategoryMarker m = new CategoryMarker("X"); m.addChangeListener(this); this.lastEvent = null; assertFalse(m.getDrawAsLine()); m.setDrawAsLine(true); assertTrue(m.getDrawAsLine()); assertEquals(m, this.lastEvent.getMarker()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/CategoryPlotTest.java000066400000000000000000001355371463604235500302240ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * CategoryPlotTest.java * --------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.*; import org.jfree.chart.annotations.CategoryLineAnnotation; import org.jfree.chart.annotations.CategoryTextAnnotation; import org.jfree.chart.axis.*; import org.jfree.chart.event.MarkerChangeListener; import org.jfree.chart.renderer.category.*; import org.jfree.chart.ui.Layer; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.util.DefaultShadowGenerator; import org.jfree.chart.util.SortOrder; import org.jfree.data.Range; import org.jfree.data.category.CategoryDataset; import org.jfree.data.category.DefaultCategoryDataset; import org.junit.jupiter.api.Test; import javax.swing.event.EventListenerList; import java.awt.*; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.util.Arrays; import java.util.EventListener; import java.util.List; import java.util.ResourceBundle; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link CategoryPlot} class. */ public class CategoryPlotTest { /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(CategoryPlot.class) .withRedefinedSuperclass() // superclass also defines equals/hashCode .withRedefinedSubclass(CombinedRangeCategoryPlot.class) .withRedefinedSubclass(CombinedDomainCategoryPlot.class) // .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .withPrefabValues(CategoryAxis.class, new CategoryAxis("red"), new CategoryAxis("black")) .withPrefabValues(ValueAxis.class, new NumberAxis("red"), new NumberAxis("black")) .withPrefabValues(Plot.class, TestUtils.createPlot(true), TestUtils.createPlot(false)) .withPrefabValues(JFreeChart.class, TestUtils.createJFC(true), TestUtils.createJFC(false)) .withPrefabValues(ResourceBundle.class, TestUtils.createRB(true), TestUtils.createRB(false)) .withPrefabValues(EventListenerList.class, new EventListenerList(), new EventListenerList()) .withIgnoredFields("chart", "parent") .verify(); } /** * Some checks for the constructor. */ @Test public void testConstructor() { CategoryPlot plot = new CategoryPlot(); assertEquals(RectangleInsets.ZERO_INSETS, plot.getAxisOffset()); } /** * A test for a bug reported in the forum. */ @Test public void testAxisRange() { DefaultCategoryDataset datasetA = new DefaultCategoryDataset(); DefaultCategoryDataset datasetB = new DefaultCategoryDataset(); datasetB.addValue(50.0, "R1", "C1"); datasetB.addValue(80.0, "R1", "C1"); CategoryPlot plot = new CategoryPlot(datasetA, new CategoryAxis(null), new NumberAxis(null), new LineAndShapeRenderer()); plot.setDataset(1, datasetB); plot.setRenderer(1, new LineAndShapeRenderer()); Range r = plot.getRangeAxis().getRange(); assertEquals(84.0, r.getUpperBound(), 0.00001); } /** * Test that the equals() method differentiates all the required fields. */ @Test public void testEquals() { CategoryPlot plot1 = new CategoryPlot(); CategoryPlot plot2 = new CategoryPlot(); assertEquals(plot1, plot2); assertEquals(plot2, plot1); // orientation... plot1.setOrientation(PlotOrientation.HORIZONTAL); assertNotEquals(plot1, plot2); plot2.setOrientation(PlotOrientation.HORIZONTAL); assertEquals(plot1, plot2); // axisOffset... plot1.setAxisOffset(new RectangleInsets(0.05, 0.05, 0.05, 0.05)); assertNotEquals(plot1, plot2); plot2.setAxisOffset(new RectangleInsets(0.05, 0.05, 0.05, 0.05)); assertEquals(plot1, plot2); // domainAxis - no longer a separate field but test anyway... plot1.setDomainAxis(new CategoryAxis("Category Axis")); assertNotEquals(plot1, plot2); plot2.setDomainAxis(new CategoryAxis("Category Axis")); assertEquals(plot1, plot2); // domainAxes... plot1.setDomainAxis(11, new CategoryAxis("Secondary Axis")); assertNotEquals(plot1, plot2); plot2.setDomainAxis(11, new CategoryAxis("Secondary Axis")); assertEquals(plot1, plot2); // domainAxisLocation - no longer a separate field but test anyway... plot1.setDomainAxisLocation(AxisLocation.TOP_OR_RIGHT); assertNotEquals(plot1, plot2); plot2.setDomainAxisLocation(AxisLocation.TOP_OR_RIGHT); assertEquals(plot1, plot2); // domainAxisLocations... plot1.setDomainAxisLocation(11, AxisLocation.TOP_OR_RIGHT); assertNotEquals(plot1, plot2); plot2.setDomainAxisLocation(11, AxisLocation.TOP_OR_RIGHT); assertEquals(plot1, plot2); // draw shared domain axis... plot1.setDrawSharedDomainAxis(!plot1.getDrawSharedDomainAxis()); assertNotEquals(plot1, plot2); plot2.setDrawSharedDomainAxis(!plot2.getDrawSharedDomainAxis()); assertEquals(plot1, plot2); // rangeAxis - no longer a separate field but test anyway... plot1.setRangeAxis(new NumberAxis("Range Axis")); assertNotEquals(plot1, plot2); plot2.setRangeAxis(new NumberAxis("Range Axis")); assertEquals(plot1, plot2); // rangeAxes... plot1.setRangeAxis(11, new NumberAxis("Secondary Range Axis")); assertNotEquals(plot1, plot2); plot2.setRangeAxis(11, new NumberAxis("Secondary Range Axis")); assertEquals(plot1, plot2); // rangeAxisLocation - no longer a separate field but test anyway... plot1.setRangeAxisLocation(AxisLocation.TOP_OR_RIGHT); assertNotEquals(plot1, plot2); plot2.setRangeAxisLocation(AxisLocation.TOP_OR_RIGHT); assertEquals(plot1, plot2); // rangeAxisLocations... plot1.setRangeAxisLocation(11, AxisLocation.TOP_OR_RIGHT); assertNotEquals(plot1, plot2); plot2.setRangeAxisLocation(11, AxisLocation.TOP_OR_RIGHT); assertEquals(plot1, plot2); // datasetToDomainAxisMap... plot1.mapDatasetToDomainAxis(11, 11); assertNotEquals(plot1, plot2); plot2.mapDatasetToDomainAxis(11, 11); assertEquals(plot1, plot2); // datasetToRangeAxisMap... plot1.mapDatasetToRangeAxis(11, 11); assertNotEquals(plot1, plot2); plot2.mapDatasetToRangeAxis(11, 11); assertEquals(plot1, plot2); // renderer - no longer a separate field but test anyway... plot1.setRenderer(new AreaRenderer()); assertNotEquals(plot1, plot2); plot2.setRenderer(new AreaRenderer()); assertEquals(plot1, plot2); // renderers... plot1.setRenderer(11, new AreaRenderer()); assertNotEquals(plot1, plot2); plot2.setRenderer(11, new AreaRenderer()); assertEquals(plot1, plot2); // rendering order... plot1.setDatasetRenderingOrder(DatasetRenderingOrder.FORWARD); assertNotEquals(plot1, plot2); plot2.setDatasetRenderingOrder(DatasetRenderingOrder.FORWARD); assertEquals(plot1, plot2); // columnRenderingOrder... plot1.setColumnRenderingOrder(SortOrder.DESCENDING); assertNotEquals(plot1, plot2); plot2.setColumnRenderingOrder(SortOrder.DESCENDING); assertEquals(plot1, plot2); // rowRenderingOrder... plot1.setRowRenderingOrder(SortOrder.DESCENDING); assertNotEquals(plot1, plot2); plot2.setRowRenderingOrder(SortOrder.DESCENDING); assertEquals(plot1, plot2); // domainGridlinesVisible plot1.setDomainGridlinesVisible(true); assertNotEquals(plot1, plot2); plot2.setDomainGridlinesVisible(true); assertEquals(plot1, plot2); // domainGridlinePosition plot1.setDomainGridlinePosition(CategoryAnchor.END); assertNotEquals(plot1, plot2); plot2.setDomainGridlinePosition(CategoryAnchor.END); assertEquals(plot1, plot2); // domainGridlineStroke Stroke stroke = new BasicStroke(2.0f); plot1.setDomainGridlineStroke(stroke); assertNotEquals(plot1, plot2); plot2.setDomainGridlineStroke(stroke); assertEquals(plot1, plot2); // domainGridlinePaint plot1.setDomainGridlinePaint(new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.YELLOW)); assertNotEquals(plot1, plot2); plot2.setDomainGridlinePaint(new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.YELLOW)); assertEquals(plot1, plot2); // rangeGridlinesVisible plot1.setRangeGridlinesVisible(false); assertNotEquals(plot1, plot2); plot2.setRangeGridlinesVisible(false); assertEquals(plot1, plot2); // rangeGridlineStroke plot1.setRangeGridlineStroke(stroke); assertNotEquals(plot1, plot2); plot2.setRangeGridlineStroke(stroke); assertEquals(plot1, plot2); // rangeGridlinePaint plot1.setRangeGridlinePaint(new GradientPaint(1.0f, 2.0f, Color.GREEN, 3.0f, 4.0f, Color.YELLOW)); assertNotEquals(plot1, plot2); plot2.setRangeGridlinePaint(new GradientPaint(1.0f, 2.0f, Color.GREEN, 3.0f, 4.0f, Color.YELLOW)); assertEquals(plot1, plot2); // anchorValue plot1.setAnchorValue(100.0); assertNotEquals(plot1, plot2); plot2.setAnchorValue(100.0); assertEquals(plot1, plot2); // rangeCrosshairVisible plot1.setRangeCrosshairVisible(true); assertNotEquals(plot1, plot2); plot2.setRangeCrosshairVisible(true); assertEquals(plot1, plot2); // rangeCrosshairValue plot1.setRangeCrosshairValue(100.0); assertNotEquals(plot1, plot2); plot2.setRangeCrosshairValue(100.0); assertEquals(plot1, plot2); // rangeCrosshairStroke plot1.setRangeCrosshairStroke(stroke); assertNotEquals(plot1, plot2); plot2.setRangeCrosshairStroke(stroke); assertEquals(plot1, plot2); // rangeCrosshairPaint plot1.setRangeCrosshairPaint(new GradientPaint(1.0f, 2.0f, Color.WHITE, 3.0f, 4.0f, Color.YELLOW)); assertNotEquals(plot1, plot2); plot2.setRangeCrosshairPaint(new GradientPaint(1.0f, 2.0f, Color.WHITE, 3.0f, 4.0f, Color.YELLOW)); assertEquals(plot1, plot2); // rangeCrosshairLockedOnData plot1.setRangeCrosshairLockedOnData(false); assertNotEquals(plot1, plot2); plot2.setRangeCrosshairLockedOnData(false); assertEquals(plot1, plot2); // foreground domain markers plot1.addDomainMarker(new CategoryMarker("C1"), Layer.FOREGROUND); assertNotEquals(plot1, plot2); plot2.addDomainMarker(new CategoryMarker("C1"), Layer.FOREGROUND); assertEquals(plot1, plot2); // background domain markers plot1.addDomainMarker(new CategoryMarker("C2"), Layer.BACKGROUND); assertNotEquals(plot1, plot2); plot2.addDomainMarker(new CategoryMarker("C2"), Layer.BACKGROUND); assertEquals(plot1, plot2); // range markers - no longer separate fields but test anyway... plot1.addRangeMarker(new ValueMarker(4.0), Layer.FOREGROUND); assertNotEquals(plot1, plot2); plot2.addRangeMarker(new ValueMarker(4.0), Layer.FOREGROUND); assertEquals(plot1, plot2); plot1.addRangeMarker(new ValueMarker(5.0), Layer.BACKGROUND); assertNotEquals(plot1, plot2); plot2.addRangeMarker(new ValueMarker(5.0), Layer.BACKGROUND); assertEquals(plot1, plot2); // foreground range markers... plot1.addRangeMarker(1, new ValueMarker(4.0), Layer.FOREGROUND); assertNotEquals(plot1, plot2); plot2.addRangeMarker(1, new ValueMarker(4.0), Layer.FOREGROUND); assertEquals(plot1, plot2); // background range markers... plot1.addRangeMarker(1, new ValueMarker(5.0), Layer.BACKGROUND); assertNotEquals(plot1, plot2); plot2.addRangeMarker(1, new ValueMarker(5.0), Layer.BACKGROUND); assertEquals(plot1, plot2); // annotations plot1.addAnnotation(new CategoryTextAnnotation("Text", "Category", 43.0)); assertNotEquals(plot1, plot2); plot2.addAnnotation(new CategoryTextAnnotation("Text", "Category", 43.0)); assertEquals(plot1, plot2); // weight plot1.setWeight(3); assertNotEquals(plot1, plot2); plot2.setWeight(3); assertEquals(plot1, plot2); // fixed domain axis space... plot1.setFixedDomainAxisSpace(new AxisSpace()); assertNotEquals(plot1, plot2); plot2.setFixedDomainAxisSpace(new AxisSpace()); assertEquals(plot1, plot2); // fixed range axis space... plot1.setFixedRangeAxisSpace(new AxisSpace()); assertNotEquals(plot1, plot2); plot2.setFixedRangeAxisSpace(new AxisSpace()); assertEquals(plot1, plot2); // fixed legend items plot1.setFixedLegendItems(new LegendItemCollection()); assertNotEquals(plot1, plot2); plot2.setFixedLegendItems(new LegendItemCollection()); assertEquals(plot1, plot2); // crosshairDatasetIndex plot1.setCrosshairDatasetIndex(99); assertNotEquals(plot1, plot2); plot2.setCrosshairDatasetIndex(99); assertEquals(plot1, plot2); // domainCrosshairColumnKey plot1.setDomainCrosshairColumnKey("A"); assertNotEquals(plot1, plot2); plot2.setDomainCrosshairColumnKey("A"); assertEquals(plot1, plot2); // domainCrosshairRowKey plot1.setDomainCrosshairRowKey("B"); assertNotEquals(plot1, plot2); plot2.setDomainCrosshairRowKey("B"); assertEquals(plot1, plot2); // domainCrosshairVisible plot1.setDomainCrosshairVisible(true); assertNotEquals(plot1, plot2); plot2.setDomainCrosshairVisible(true); assertEquals(plot1, plot2); // domainCrosshairPaint plot1.setDomainCrosshairPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); assertNotEquals(plot1, plot2); plot2.setDomainCrosshairPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); assertEquals(plot1, plot2); // domainCrosshairStroke plot1.setDomainCrosshairStroke(new BasicStroke(1.23f)); assertNotEquals(plot1, plot2); plot2.setDomainCrosshairStroke(new BasicStroke(1.23f)); assertEquals(plot1, plot2); plot1.setRangeMinorGridlinesVisible(true); assertNotEquals(plot1, plot2); plot2.setRangeMinorGridlinesVisible(true); assertEquals(plot1, plot2); plot1.setRangeMinorGridlinePaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); assertNotEquals(plot1, plot2); plot2.setRangeMinorGridlinePaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); assertEquals(plot1, plot2); plot1.setRangeMinorGridlineStroke(new BasicStroke(1.23f)); assertNotEquals(plot1, plot2); plot2.setRangeMinorGridlineStroke(new BasicStroke(1.23f)); assertEquals(plot1, plot2); plot1.setRangeZeroBaselineVisible(!plot1.isRangeZeroBaselineVisible()); assertNotEquals(plot1, plot2); plot2.setRangeZeroBaselineVisible(!plot2.isRangeZeroBaselineVisible()); assertEquals(plot1, plot2); plot1.setRangeZeroBaselinePaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); assertNotEquals(plot1, plot2); plot2.setRangeZeroBaselinePaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); assertEquals(plot1, plot2); plot1.setRangeZeroBaselineStroke(new BasicStroke(1.23f)); assertNotEquals(plot1, plot2); plot2.setRangeZeroBaselineStroke(new BasicStroke(1.23f)); assertEquals(plot1, plot2); // shadowGenerator plot1.setShadowGenerator(new DefaultShadowGenerator(5, Color.GRAY, 0.6f, 4, -Math.PI / 4)); assertNotEquals(plot1, plot2); plot2.setShadowGenerator(new DefaultShadowGenerator(5, Color.GRAY, 0.6f, 4, -Math.PI / 4)); assertEquals(plot1, plot2); plot1.setShadowGenerator(null); assertNotEquals(plot1, plot2); plot2.setShadowGenerator(null); assertEquals(plot1, plot2); } /** * This test covers a flaw in the ObjectList equals() method. */ @Test public void testEquals_ObjectList() { CategoryPlot p1 = new CategoryPlot(); p1.setDomainAxis(new CategoryAxis("A")); CategoryPlot p2 = new CategoryPlot(); p2.setDomainAxis(new CategoryAxis("A")); assertEquals(p1, p2); p2.setDomainAxis(1, new CategoryAxis("B")); assertNotEquals(p1, p2); } /** * This test covers a flaw in the ObjectList equals() method. */ @Test public void testEquals_ObjectList2() { CategoryPlot p1 = new CategoryPlot(); p1.setDomainAxisLocation(AxisLocation.BOTTOM_OR_RIGHT); CategoryPlot p2 = new CategoryPlot(); p2.setDomainAxisLocation(AxisLocation.BOTTOM_OR_RIGHT); assertEquals(p1, p2); p2.setDomainAxisLocation(1, AxisLocation.TOP_OR_LEFT); assertNotEquals(p1, p2); } /** * This test covers a flaw in the ObjectList equals() method. */ @Test public void testEquals_ObjectList3() { CategoryPlot p1 = new CategoryPlot(); p1.setRangeAxis(new NumberAxis("A")); CategoryPlot p2 = new CategoryPlot(); p2.setRangeAxis(new NumberAxis("A")); assertEquals(p1, p2); p2.setRangeAxis(1, new NumberAxis("B")); assertNotEquals(p1, p2); } /** * This test covers a flaw in the ObjectList equals() method. */ @Test public void testEquals_ObjectList4() { CategoryPlot p1 = new CategoryPlot(); p1.setRangeAxisLocation(AxisLocation.BOTTOM_OR_RIGHT); CategoryPlot p2 = new CategoryPlot(); p2.setRangeAxisLocation(AxisLocation.BOTTOM_OR_RIGHT); assertEquals(p1, p2); p2.setRangeAxisLocation(1, AxisLocation.TOP_OR_LEFT); assertNotEquals(p1, p2); } /** * This test covers a flaw in the ObjectList equals() method. */ @Test public void testEquals_ObjectList5() { CategoryPlot p1 = new CategoryPlot(); p1.setRenderer(new BarRenderer()); CategoryPlot p2 = new CategoryPlot(); p2.setRenderer(new BarRenderer()); assertEquals(p1, p2); p2.setRenderer(1, new LineAndShapeRenderer()); assertNotEquals(p1, p2); } /** * Confirm that cloning works. */ @Test public void testCloning() { CategoryPlot p1 = new CategoryPlot(); p1.setRangeCrosshairPaint(new GradientPaint(1.0f, 2.0f, Color.WHITE, 3.0f, 4.0f, Color.YELLOW)); p1.setRangeMinorGridlinePaint(new GradientPaint(2.0f, 3.0f, Color.WHITE, 4.0f, 5.0f, Color.RED)); p1.setRangeZeroBaselinePaint(new GradientPaint(3.0f, 4.0f, Color.RED, 5.0f, 6.0f, Color.WHITE)); CategoryPlot p2; try { p2 = (CategoryPlot) p1.clone(); } catch (CloneNotSupportedException e) { fail("Cloning failed."); return; } assertNotSame(p1, p2); assertSame(p1.getClass(), p2.getClass()); assertEquals(p1, p2); // check independence p1.addAnnotation(new CategoryLineAnnotation("C1", 1.0, "C2", 2.0, Color.RED, new BasicStroke(1.0f))); assertNotEquals(p1, p2); p2.addAnnotation(new CategoryLineAnnotation("C1", 1.0, "C2", 2.0, Color.RED, new BasicStroke(1.0f))); assertEquals(p1, p2); p1.addDomainMarker(new CategoryMarker("C1"), Layer.FOREGROUND); assertNotEquals(p1, p2); p2.addDomainMarker(new CategoryMarker("C1"), Layer.FOREGROUND); assertEquals(p1, p2); p1.addDomainMarker(new CategoryMarker("C2"), Layer.BACKGROUND); assertNotEquals(p1, p2); p2.addDomainMarker(new CategoryMarker("C2"), Layer.BACKGROUND); assertEquals(p1, p2); p1.addRangeMarker(new ValueMarker(1.0), Layer.FOREGROUND); assertNotEquals(p1, p2); p2.addRangeMarker(new ValueMarker(1.0), Layer.FOREGROUND); assertEquals(p1, p2); p1.addRangeMarker(new ValueMarker(2.0), Layer.BACKGROUND); assertNotEquals(p1, p2); p2.addRangeMarker(new ValueMarker(2.0), Layer.BACKGROUND); assertEquals(p1, p2); } /** * Some more cloning checks. */ @Test public void testCloning2() { AxisSpace da1 = new AxisSpace(); AxisSpace ra1 = new AxisSpace(); CategoryPlot p1 = new CategoryPlot(); p1.setFixedDomainAxisSpace(da1); p1.setFixedRangeAxisSpace(ra1); CategoryPlot p2; try { p2 = (CategoryPlot) p1.clone(); } catch (CloneNotSupportedException e) { fail("Cloning failed."); return; } assertNotSame(p1, p2); assertSame(p1.getClass(), p2.getClass()); assertEquals(p1, p2); da1.setBottom(99.0); assertNotEquals(p1, p2); p2.getFixedDomainAxisSpace().setBottom(99.0); assertEquals(p1, p2); ra1.setBottom(11.0); assertNotEquals(p1, p2); p2.getFixedRangeAxisSpace().setBottom(11.0); assertEquals(p1, p2); } /** * Some more cloning checks. */ @Test public void testCloning3() { LegendItemCollection c1 = new LegendItemCollection(); CategoryPlot p1 = new CategoryPlot(); p1.setFixedLegendItems(c1); CategoryPlot p2; try { p2 = (CategoryPlot) p1.clone(); } catch (CloneNotSupportedException e) { fail("Cloning failed."); return; } assertNotSame(p1, p2); assertSame(p1.getClass(), p2.getClass()); assertEquals(p1, p2); c1.add(new LegendItem("X", "XX", "tt", "url", true, new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), true, Color.RED, true, Color.YELLOW, new BasicStroke(1.0f), true, new Line2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(1.0f), Color.GREEN)); assertNotEquals(p1, p2); p2.getFixedLegendItems().add(new LegendItem("X", "XX", "tt", "url", true, new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), true, Color.RED, true, Color.YELLOW, new BasicStroke(1.0f), true, new Line2D.Double(1.0, 2.0, 3.0, 4.0), new BasicStroke(1.0f), Color.GREEN)); assertEquals(p1, p2); } /** * Renderers that belong to the plot are being cloned but they are * retaining a reference to the original plot. */ @Test public void testBug2817504() { CategoryPlot p1 = new CategoryPlot(); LineAndShapeRenderer r1 = new LineAndShapeRenderer(); p1.setRenderer(r1); CategoryPlot p2; try { p2 = (CategoryPlot) p1.clone(); } catch (CloneNotSupportedException e) { fail("Cloning failed."); return; } assertNotSame(p1, p2); assertSame(p1.getClass(), p2.getClass()); assertEquals(p1, p2); // check for independence LineAndShapeRenderer r2 = (LineAndShapeRenderer) p2.getRenderer(); assertSame(r2.getPlot(), p2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); CategoryAxis domainAxis = new CategoryAxis("Domain"); NumberAxis rangeAxis = new NumberAxis("Range"); BarRenderer renderer = new BarRenderer(); CategoryPlot p1 = new CategoryPlot(dataset, domainAxis, rangeAxis, renderer); p1.setOrientation(PlotOrientation.HORIZONTAL); CategoryPlot p2 = TestUtils.serialised(p1); assertEquals(p1, p2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization2() { DefaultCategoryDataset data = new DefaultCategoryDataset(); CategoryAxis domainAxis = new CategoryAxis("Domain"); NumberAxis rangeAxis = new NumberAxis("Range"); BarRenderer renderer = new BarRenderer(); CategoryPlot p1 = new CategoryPlot(data, domainAxis, rangeAxis, renderer); p1.setOrientation(PlotOrientation.VERTICAL); CategoryPlot p2 = TestUtils.serialised(p1); assertEquals(p1, p2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization3() { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); JFreeChart chart = ChartFactory.createBarChart( "Test Chart", "Category Axis", "Value Axis", dataset, PlotOrientation.VERTICAL, true, true, false); JFreeChart chart2 = TestUtils.serialised(chart); // now check that the chart is usable... try { chart2.createBufferedImage(300, 200); } catch (Exception e) { fail("No exception should be thrown."); } } /** * This test ensures that a plot with markers is serialized correctly. */ @Test public void testSerialization4() { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); JFreeChart chart = ChartFactory.createBarChart( "Test Chart", "Category Axis", "Value Axis", dataset, PlotOrientation.VERTICAL, true, true, false); CategoryPlot plot = (CategoryPlot) chart.getPlot(); plot.addRangeMarker(new ValueMarker(1.1), Layer.FOREGROUND); plot.addRangeMarker(new IntervalMarker(2.2, 3.3), Layer.BACKGROUND); JFreeChart chart2 = TestUtils.serialised(chart); assertEquals(chart, chart2); // now check that the chart is usable... try { chart2.createBufferedImage(300, 200); } catch (Exception e) { fail("No exception should be thrown."); } } /** * Tests a bug where the plot is no longer registered as a listener * with the dataset(s) and axes after deserialization. See patch 1209475 * at SourceForge. */ @Test public void testSerialization5() { DefaultCategoryDataset dataset1 = new DefaultCategoryDataset(); CategoryAxis domainAxis1 = new CategoryAxis("Domain 1"); NumberAxis rangeAxis1 = new NumberAxis("Range 1"); BarRenderer renderer1 = new BarRenderer(); CategoryPlot p1 = new CategoryPlot(dataset1, domainAxis1, rangeAxis1, renderer1); CategoryAxis domainAxis2 = new CategoryAxis("Domain 2"); NumberAxis rangeAxis2 = new NumberAxis("Range 2"); BarRenderer renderer2 = new BarRenderer(); DefaultCategoryDataset dataset2 = new DefaultCategoryDataset(); p1.setDataset(1, dataset2); p1.setDomainAxis(1, domainAxis2); p1.setRangeAxis(1, rangeAxis2); p1.setRenderer(1, renderer2); CategoryPlot p2 = TestUtils.serialised(p1); assertEquals(p1, p2); // now check that all datasets, renderers and axes are being listened // too... CategoryAxis domainAxisA = p2.getDomainAxis(0); NumberAxis rangeAxisA = (NumberAxis) p2.getRangeAxis(0); DefaultCategoryDataset datasetA = (DefaultCategoryDataset) p2.getDataset(0); BarRenderer rendererA = (BarRenderer) p2.getRenderer(0); CategoryAxis domainAxisB = p2.getDomainAxis(1); NumberAxis rangeAxisB = (NumberAxis) p2.getRangeAxis(1); DefaultCategoryDataset datasetB = (DefaultCategoryDataset) p2.getDataset(1); BarRenderer rendererB = (BarRenderer) p2.getRenderer(1); assertTrue(datasetA.hasListener(p2)); assertTrue(domainAxisA.hasListener(p2)); assertTrue(rangeAxisA.hasListener(p2)); assertTrue(rendererA.hasListener(p2)); assertTrue(datasetB.hasListener(p2)); assertTrue(domainAxisB.hasListener(p2)); assertTrue(rangeAxisB.hasListener(p2)); assertTrue(rendererB.hasListener(p2)); } /** * A test for a bug where setting the renderer doesn't register the plot * as a RendererChangeListener. */ @Test public void testSetRenderer() { CategoryPlot plot = new CategoryPlot(); CategoryItemRenderer renderer = new LineAndShapeRenderer(); plot.setRenderer(renderer); // now make a change to the renderer and see if it triggers a plot // change event... MyPlotChangeListener listener = new MyPlotChangeListener(); plot.addChangeListener(listener); renderer.setSeriesPaint(0, Color.BLACK); assertNotNull(listener.getEvent()); } /** * A test for bug report 1169972. */ @Test public void test1169972() { CategoryPlot plot = new CategoryPlot(null, null, null, null); plot.setDomainAxis(new CategoryAxis("C")); plot.setRangeAxis(new NumberAxis("Y")); plot.setRenderer(new BarRenderer()); plot.setDataset(new DefaultCategoryDataset()); assertTrue(true); // we didn't get an exception so all is good } /** * Some tests for the addDomainMarker() method(s). */ @Test public void testAddDomainMarker() { CategoryPlot plot = new CategoryPlot(); CategoryMarker m = new CategoryMarker("C1"); plot.addDomainMarker(m); List listeners = Arrays.asList(m.getListeners( MarkerChangeListener.class)); assertTrue(listeners.contains(plot)); plot.clearDomainMarkers(); listeners = Arrays.asList(m.getListeners(MarkerChangeListener.class)); assertFalse(listeners.contains(plot)); } /** * Some tests for the addRangeMarker() method(s). */ @Test public void testAddRangeMarker() { CategoryPlot plot = new CategoryPlot(); Marker m = new ValueMarker(1.0); plot.addRangeMarker(m); List listeners = Arrays.asList(m.getListeners( MarkerChangeListener.class)); assertTrue(listeners.contains(plot)); plot.clearRangeMarkers(); listeners = Arrays.asList(m.getListeners(MarkerChangeListener.class)); assertFalse(listeners.contains(plot)); } /** * A test for bug 1654215 (where a renderer is added to the plot without * a corresponding dataset and it throws an exception at drawing time). */ @Test public void test1654215() { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); JFreeChart chart = ChartFactory.createLineChart("Title", "X", "Y", dataset, PlotOrientation.VERTICAL, true, false, false); CategoryPlot plot = (CategoryPlot) chart.getPlot(); plot.setRenderer(1, new LineAndShapeRenderer()); try { BufferedImage image = new BufferedImage(200 , 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); chart.draw(g2, new Rectangle2D.Double(0, 0, 200, 100), null, null); g2.dispose(); } catch (Exception e) { fail("No exception should be thrown."); } } /** * Some checks for the getDomainAxisIndex() method. */ @Test public void testGetDomainAxisIndex() { CategoryAxis domainAxis1 = new CategoryAxis("X1"); CategoryAxis domainAxis2 = new CategoryAxis("X2"); NumberAxis rangeAxis1 = new NumberAxis("Y1"); CategoryPlot plot = new CategoryPlot(null, domainAxis1, rangeAxis1, null); assertEquals(0, plot.getDomainAxisIndex(domainAxis1)); assertEquals(-1, plot.getDomainAxisIndex(domainAxis2)); plot.setDomainAxis(1, domainAxis2); assertEquals(1, plot.getDomainAxisIndex(domainAxis2)); assertEquals(-1, plot.getDomainAxisIndex(new CategoryAxis("X2"))); boolean pass = false; try { plot.getDomainAxisIndex(null); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getRangeAxisIndex() method. */ @Test public void testGetRangeAxisIndex() { CategoryAxis domainAxis1 = new CategoryAxis("X1"); NumberAxis rangeAxis1 = new NumberAxis("Y1"); NumberAxis rangeAxis2 = new NumberAxis("Y2"); CategoryPlot plot = new CategoryPlot(null, domainAxis1, rangeAxis1, null); assertEquals(0, plot.getRangeAxisIndex(rangeAxis1)); assertEquals(-1, plot.getRangeAxisIndex(rangeAxis2)); plot.setRangeAxis(1, rangeAxis2); assertEquals(1, plot.getRangeAxisIndex(rangeAxis2)); assertEquals(-1, plot.getRangeAxisIndex(new NumberAxis("Y2"))); boolean pass = false; try { plot.getRangeAxisIndex(null); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Check that removing a marker that isn't assigned to the plot returns * false. */ @Test public void testRemoveDomainMarker() { CategoryPlot plot = new CategoryPlot(); assertFalse(plot.removeDomainMarker(new CategoryMarker("Category 1"))); } /** * Check that removing a marker that isn't assigned to the plot returns * false. */ @Test public void testRemoveRangeMarker() { CategoryPlot plot = new CategoryPlot(); assertFalse(plot.removeRangeMarker(new ValueMarker(0.5))); } /** * Some tests for the getDomainAxisForDataset() method. */ @Test public void testGetDomainAxisForDataset() { CategoryDataset dataset = new DefaultCategoryDataset(); CategoryAxis xAxis = new CategoryAxis("X"); NumberAxis yAxis = new NumberAxis("Y"); CategoryItemRenderer renderer = new BarRenderer(); CategoryPlot plot = new CategoryPlot(dataset, xAxis, yAxis, renderer); assertEquals(xAxis, plot.getDomainAxisForDataset(0)); // should get IllegalArgumentException for negative index boolean pass = false; try { plot.getDomainAxisForDataset(-1); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); // if multiple axes are mapped, the first in the list should be // returned... CategoryAxis xAxis2 = new CategoryAxis("X2"); plot.setDomainAxis(1, xAxis2); assertEquals(xAxis, plot.getDomainAxisForDataset(0)); plot.mapDatasetToDomainAxis(0, 1); assertEquals(xAxis2, plot.getDomainAxisForDataset(0)); List axisIndices = Arrays.asList(0, 1); plot.mapDatasetToDomainAxes(0, axisIndices); assertEquals(xAxis, plot.getDomainAxisForDataset(0)); axisIndices = Arrays.asList(1, 2); plot.mapDatasetToDomainAxes(0, axisIndices); assertEquals(xAxis2, plot.getDomainAxisForDataset(0)); } /** * Some tests for the getRangeAxisForDataset() method. */ @Test public void testGetRangeAxisForDataset() { CategoryDataset dataset = new DefaultCategoryDataset(); CategoryAxis xAxis = new CategoryAxis("X"); NumberAxis yAxis = new NumberAxis("Y"); CategoryItemRenderer renderer = new DefaultCategoryItemRenderer(); CategoryPlot plot = new CategoryPlot(dataset, xAxis, yAxis, renderer); assertEquals(yAxis, plot.getRangeAxisForDataset(0)); // should get IllegalArgumentException for negative index boolean pass = false; try { plot.getRangeAxisForDataset(-1); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); // if multiple axes are mapped, the first in the list should be // returned... NumberAxis yAxis2 = new NumberAxis("Y2"); plot.setRangeAxis(1, yAxis2); assertEquals(yAxis, plot.getRangeAxisForDataset(0)); plot.mapDatasetToRangeAxis(0, 1); assertEquals(yAxis2, plot.getRangeAxisForDataset(0)); List axisIndices = Arrays.asList(0, 1); plot.mapDatasetToRangeAxes(0, axisIndices); assertEquals(yAxis, plot.getRangeAxisForDataset(0)); axisIndices = Arrays.asList(1, 2); plot.mapDatasetToRangeAxes(0, axisIndices); assertEquals(yAxis2, plot.getRangeAxisForDataset(0)); } /** * Datasets are now stored in a Map, and it should be possible to assign * them an arbitrary key (index). */ @Test public void testDatasetIndices() { CategoryDataset dataset = new DefaultCategoryDataset(); CategoryAxis xAxis = new CategoryAxis("X"); NumberAxis yAxis = new NumberAxis("Y"); CategoryItemRenderer renderer = new BarRenderer(); CategoryPlot plot = new CategoryPlot(dataset, xAxis, yAxis, renderer); assertEquals(dataset, plot.getDataset(0)); DefaultCategoryDataset dataset2 = new DefaultCategoryDataset(); dataset2.setValue(1, "R1", "C1"); // we should be able to give a dataset an arbitrary index plot.setDataset(99, dataset2); assertEquals(2, plot.getDatasetCount()); assertEquals(dataset2, plot.getDataset(99)); assertEquals(0, plot.indexOf(dataset)); assertEquals(99, plot.indexOf(dataset2)); } @Test public void testAxisIndices() { CategoryDataset dataset = new DefaultCategoryDataset(); CategoryAxis xAxis = new CategoryAxis("X"); NumberAxis yAxis = new NumberAxis("Y"); CategoryItemRenderer renderer = new BarRenderer(); CategoryPlot plot = new CategoryPlot(dataset, xAxis, yAxis, renderer); assertEquals(xAxis, plot.getDomainAxis(0)); assertEquals(yAxis, plot.getRangeAxis(0)); CategoryAxis xAxis2 = new CategoryAxis("X2"); plot.setDomainAxis(99, xAxis2); assertEquals(xAxis2, plot.getDomainAxis(99)); NumberAxis yAxis2 = new NumberAxis("Y2"); plot.setRangeAxis(99, yAxis2); assertEquals(yAxis2, plot.getRangeAxis(99)); } @Test public void testAxisLocationIndices() { CategoryDataset dataset = new DefaultCategoryDataset(); CategoryAxis xAxis = new CategoryAxis("X"); NumberAxis yAxis = new NumberAxis("Y"); CategoryItemRenderer renderer = new BarRenderer(); CategoryPlot plot = new CategoryPlot(dataset, xAxis, yAxis, renderer); CategoryAxis xAxis2 = new CategoryAxis("X2"); NumberAxis yAxis2 = new NumberAxis("Y2"); plot.setDomainAxis(99, xAxis2); plot.setRangeAxis(99, yAxis2); plot.setDomainAxisLocation(99, AxisLocation.BOTTOM_OR_RIGHT); assertEquals(AxisLocation.BOTTOM_OR_RIGHT, plot.getDomainAxisLocation(99)); plot.setRangeAxisLocation(99, AxisLocation.BOTTOM_OR_LEFT); assertEquals(AxisLocation.BOTTOM_OR_LEFT, plot.getRangeAxisLocation(99)); } @Test public void testRendererIndices() { CategoryDataset dataset = new DefaultCategoryDataset(); CategoryAxis xAxis = new CategoryAxis("X"); NumberAxis yAxis = new NumberAxis("Y"); CategoryItemRenderer renderer = new BarRenderer(); CategoryPlot plot = new CategoryPlot(dataset, xAxis, yAxis, renderer); assertEquals(renderer, plot.getRenderer(0)); // we should be able to give a renderer an arbitrary index CategoryItemRenderer renderer2 = new LineAndShapeRenderer(); plot.setRenderer(20, renderer2); assertEquals(2, plot.getRendererCount()); assertEquals(renderer2, plot.getRenderer(20)); assertEquals(20, plot.getIndexOf(renderer2)); } @Test public void testGetRendererForDataset2() { CategoryDataset dataset = new DefaultCategoryDataset(); CategoryAxis xAxis = new CategoryAxis("X"); NumberAxis yAxis = new NumberAxis("Y"); CategoryItemRenderer renderer = new BarRenderer(); CategoryPlot plot = new CategoryPlot(dataset, xAxis, yAxis, renderer); // add a second dataset DefaultCategoryDataset dataset2 = new DefaultCategoryDataset(); dataset2.setValue(1, "R1", "C1"); plot.setDataset(99, dataset2); // by default, the renderer with index 0 is used assertEquals(renderer, plot.getRendererForDataset(dataset2)); // add a second renderer with the same index as dataset2, now it will // be used CategoryItemRenderer renderer2 = new LineAndShapeRenderer(); plot.setRenderer(99, renderer2); assertEquals(renderer2, plot.getRendererForDataset(dataset2)); } @Test public void testMapDatasetToDomainAxis() { CategoryDataset dataset = new DefaultCategoryDataset(); CategoryAxis xAxis = new CategoryAxis("X"); NumberAxis yAxis = new NumberAxis("Y"); CategoryItemRenderer renderer = new BarRenderer(); CategoryPlot plot = new CategoryPlot(dataset, xAxis, yAxis, renderer); CategoryAxis xAxis2 = new CategoryAxis("X2"); plot.setDomainAxis(11, xAxis2); // add a second dataset DefaultCategoryDataset dataset2 = new DefaultCategoryDataset(); dataset2.setValue(1, "R1", "C1"); plot.setDataset(99, dataset); assertEquals(xAxis, plot.getDomainAxisForDataset(99)); // now map the dataset to the second xAxis plot.mapDatasetToDomainAxis(99, 11); assertEquals(xAxis2, plot.getDomainAxisForDataset(99)); } @Test public void testMapDatasetToRangeAxis() { CategoryDataset dataset = new DefaultCategoryDataset(); CategoryAxis xAxis = new CategoryAxis("X"); NumberAxis yAxis = new NumberAxis("Y"); CategoryItemRenderer renderer = new BarRenderer(); CategoryPlot plot = new CategoryPlot(dataset, xAxis, yAxis, renderer); NumberAxis yAxis2 = new NumberAxis("Y2"); plot.setRangeAxis(22, yAxis2); // add a second dataset DefaultCategoryDataset dataset2 = new DefaultCategoryDataset(); dataset2.setValue(1, "R1", "C1"); plot.setDataset(99, dataset); assertEquals(yAxis, plot.getRangeAxisForDataset(99)); // now map the dataset to the second xAxis plot.mapDatasetToRangeAxis(99, 22); assertEquals(yAxis2, plot.getRangeAxisForDataset(99)); } @Test public void testDomainMarkerIndices() { CategoryDataset dataset = new DefaultCategoryDataset(); CategoryAxis xAxis = new CategoryAxis("X"); NumberAxis yAxis = new NumberAxis("Y"); CategoryItemRenderer renderer = new BarRenderer(); CategoryPlot plot = new CategoryPlot(dataset, xAxis, yAxis, renderer); // add a second dataset, plotted against a second x axis DefaultCategoryDataset dataset2 = new DefaultCategoryDataset(); dataset2.setValue(1, "R1", "C1"); plot.setDataset(99, dataset); CategoryAxis xAxis2 = new CategoryAxis("X2"); plot.setDomainAxis(1, xAxis2); LineAndShapeRenderer renderer2 = new LineAndShapeRenderer(); plot.setRenderer(99, renderer2); plot.mapDatasetToDomainAxis(99, 1); CategoryMarker xMarker1 = new CategoryMarker(123); plot.addDomainMarker(99, xMarker1, Layer.FOREGROUND); assertTrue(plot.getDomainMarkers(99, Layer.FOREGROUND).contains( xMarker1)); } @Test public void testRangeMarkerIndices() { CategoryDataset dataset = new DefaultCategoryDataset(); CategoryAxis xAxis = new CategoryAxis("X"); NumberAxis yAxis = new NumberAxis("Y"); CategoryItemRenderer renderer = new BarRenderer(); CategoryPlot plot = new CategoryPlot(dataset, xAxis, yAxis, renderer); // add a second dataset, plotted against a second axis DefaultCategoryDataset dataset2 = new DefaultCategoryDataset(); dataset2.setValue(1, "R1", "C1"); plot.setDataset(99, dataset); NumberAxis yAxis2 = new NumberAxis("Y2"); plot.setRangeAxis(1, yAxis2); LineAndShapeRenderer renderer2 = new LineAndShapeRenderer(); plot.setRenderer(99, renderer2); plot.mapDatasetToRangeAxis(99, 1); ValueMarker yMarker1 = new ValueMarker(123); plot.addRangeMarker(99, yMarker1, Layer.FOREGROUND); assertTrue(plot.getRangeMarkers(99, Layer.FOREGROUND).contains( yMarker1)); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/CombinedDomainCategoryPlotTest.java000066400000000000000000000251261463604235500330050ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------------- * CombinedDomainCategoryPlotTest.java * ----------------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.text.AttributedString; import java.util.List; import java.util.ResourceBundle; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.JFreeChart; import org.jfree.chart.TestUtils; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.event.ChartChangeEvent; import org.jfree.chart.event.ChartChangeListener; import org.jfree.chart.labels.StandardCategoryToolTipGenerator; import org.jfree.chart.renderer.category.BarRenderer; import org.jfree.chart.renderer.category.LineAndShapeRenderer; import org.jfree.data.category.CategoryDataset; import org.jfree.data.category.DefaultCategoryDataset; import org.junit.jupiter.api.Test; import javax.swing.event.EventListenerList; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link CombinedDomainCategoryPlot} class. */ public class CombinedDomainCategoryPlotTest implements ChartChangeListener { /** A list of the events received. */ private final List events = new java.util.ArrayList<>(); /** * Receives a chart change event. * * @param event the event. */ @Override public void chartChanged(ChartChangeEvent event) { this.events.add(event); } /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(CombinedDomainCategoryPlot.class) .withRedefinedSuperclass() // superclass also defines equals/hashCode .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .withPrefabValues(Plot.class, TestUtils.createPlot(true), TestUtils.createPlot(false)) .withPrefabValues(JFreeChart.class, TestUtils.createJFC(true), TestUtils.createJFC(false)) .withPrefabValues(ResourceBundle.class, TestUtils.createRB(true), TestUtils.createRB(false)) .withPrefabValues(EventListenerList.class, new EventListenerList(), new EventListenerList()) .withPrefabValues(String.class, "A", "B") .withPrefabValues(AttributedString.class, TestUtils.createAS(true), TestUtils.createAS(false)) .withIgnoredFields("chart", "parent") // superclass .verify(); } /** * This is a test to replicate the bug report 987080. */ @Test public void testRemoveSubplot() { CombinedDomainCategoryPlot plot = new CombinedDomainCategoryPlot(); CategoryPlot plot1 = new CategoryPlot(); CategoryPlot plot2 = new CategoryPlot(); plot.add(plot1); plot.add(plot2); // remove plot2, but plot1 is removed instead plot.remove(plot2); List plots = plot.getSubplots(); assertSame(plots.get(0), plot1); assertEquals(1, plots.size()); } /** * Some checks for the equals() method. */ @Test public void testEquals() { CombinedDomainCategoryPlot plot1 = createPlot(); CombinedDomainCategoryPlot plot2 = createPlot(); assertEquals(plot1, plot2); } /** * Some checks for cloning. */ @Test public void testCloning() throws CloneNotSupportedException { CombinedDomainCategoryPlot plot1 = createPlot(); CombinedDomainCategoryPlot plot2 = (CombinedDomainCategoryPlot) plot1.clone(); assertNotSame(plot1, plot2); assertSame(plot1.getClass(), plot2.getClass()); assertEquals(plot1, plot2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { CombinedDomainCategoryPlot plot1 = createPlot(); CombinedDomainCategoryPlot plot2 = TestUtils.serialised(plot1); assertEquals(plot1, plot2); } /** * Check that only one chart change event is generated by a change to a * subplot. */ @Test public void testNotification() { CombinedDomainCategoryPlot plot = createPlot(); JFreeChart chart = new JFreeChart(plot); chart.addChangeListener(this); CategoryPlot subplot1 = (CategoryPlot) plot.getSubplots().get(0); NumberAxis yAxis = (NumberAxis) subplot1.getRangeAxis(); yAxis.setAutoRangeIncludesZero(!yAxis.getAutoRangeIncludesZero()); assertEquals(1, this.events.size()); // a redraw should NOT trigger another change event BufferedImage image = new BufferedImage(200, 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); this.events.clear(); chart.draw(g2, new Rectangle2D.Double(0.0, 0.0, 200.0, 100.0)); assertTrue(this.events.isEmpty()); } /** * Creates a dataset. * * @return A dataset. */ public CategoryDataset createDataset1() { DefaultCategoryDataset result = new DefaultCategoryDataset(); // row keys... String series1 = "First"; String series2 = "Second"; // column keys... String type1 = "Type 1"; String type2 = "Type 2"; String type3 = "Type 3"; String type4 = "Type 4"; String type5 = "Type 5"; String type6 = "Type 6"; String type7 = "Type 7"; String type8 = "Type 8"; result.addValue(1.0, series1, type1); result.addValue(4.0, series1, type2); result.addValue(3.0, series1, type3); result.addValue(5.0, series1, type4); result.addValue(5.0, series1, type5); result.addValue(7.0, series1, type6); result.addValue(7.0, series1, type7); result.addValue(8.0, series1, type8); result.addValue(5.0, series2, type1); result.addValue(7.0, series2, type2); result.addValue(6.0, series2, type3); result.addValue(8.0, series2, type4); result.addValue(4.0, series2, type5); result.addValue(4.0, series2, type6); result.addValue(2.0, series2, type7); result.addValue(1.0, series2, type8); return result; } /** * Creates a dataset. * * @return A dataset. */ public CategoryDataset createDataset2() { DefaultCategoryDataset result = new DefaultCategoryDataset(); // row keys... String series1 = "Third"; String series2 = "Fourth"; // column keys... String type1 = "Type 1"; String type2 = "Type 2"; String type3 = "Type 3"; String type4 = "Type 4"; String type5 = "Type 5"; String type6 = "Type 6"; String type7 = "Type 7"; String type8 = "Type 8"; result.addValue(11.0, series1, type1); result.addValue(14.0, series1, type2); result.addValue(13.0, series1, type3); result.addValue(15.0, series1, type4); result.addValue(15.0, series1, type5); result.addValue(17.0, series1, type6); result.addValue(17.0, series1, type7); result.addValue(18.0, series1, type8); result.addValue(15.0, series2, type1); result.addValue(17.0, series2, type2); result.addValue(16.0, series2, type3); result.addValue(18.0, series2, type4); result.addValue(14.0, series2, type5); result.addValue(14.0, series2, type6); result.addValue(12.0, series2, type7); result.addValue(11.0, series2, type8); return result; } /** * Creates a sample plot. * * @return A sample plot. */ private CombinedDomainCategoryPlot createPlot() { CategoryDataset dataset1 = createDataset1(); NumberAxis rangeAxis1 = new NumberAxis("Value"); rangeAxis1.setStandardTickUnits(NumberAxis.createIntegerTickUnits()); LineAndShapeRenderer renderer1 = new LineAndShapeRenderer(); renderer1.setDefaultToolTipGenerator( new StandardCategoryToolTipGenerator() ); CategoryPlot subplot1 = new CategoryPlot( dataset1, null, rangeAxis1, renderer1 ); subplot1.setDomainGridlinesVisible(true); CategoryDataset dataset2 = createDataset2(); NumberAxis rangeAxis2 = new NumberAxis("Value"); rangeAxis2.setStandardTickUnits(NumberAxis.createIntegerTickUnits()); BarRenderer renderer2 = new BarRenderer(); renderer2.setDefaultToolTipGenerator( new StandardCategoryToolTipGenerator() ); CategoryPlot subplot2 = new CategoryPlot( dataset2, null, rangeAxis2, renderer2 ); subplot2.setDomainGridlinesVisible(true); CategoryAxis domainAxis = new CategoryAxis("Category"); CombinedDomainCategoryPlot plot = new CombinedDomainCategoryPlot(domainAxis); plot.add(subplot1, 2); plot.add(subplot2, 1); return plot; } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/CombinedDomainXYPlotTest.java000066400000000000000000000216311463604235500315650ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------- * CombinedDomainXYPlotTest.java * ----------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import java.awt.Font; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.List; import org.jfree.chart.JFreeChart; import org.jfree.chart.TestUtils; import org.jfree.chart.annotations.XYTextAnnotation; import org.jfree.chart.axis.AxisLocation; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.event.ChartChangeEvent; import org.jfree.chart.event.ChartChangeListener; import org.jfree.chart.renderer.xy.StandardXYItemRenderer; import org.jfree.chart.renderer.xy.XYItemRenderer; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link CombinedDomainXYPlot} class. */ public class CombinedDomainXYPlotTest implements ChartChangeListener { /** A list of the events received. */ private List events = new ArrayList<>(); /** * Receives a chart change event. * * @param event the event. */ @Override public void chartChanged(ChartChangeEvent event) { this.events.add(event); } /** * Confirm that the constructor will accept a null axis. */ @Test public void testConstructor1() { CombinedDomainXYPlot plot = new CombinedDomainXYPlot(null); assertNull(plot.getDomainAxis()); } /** * This is a test to replicate the bug report 987080. */ @Test public void testRemoveSubplot() { CombinedDomainXYPlot plot = new CombinedDomainXYPlot(); XYPlot plot1 = new XYPlot(); XYPlot plot2 = new XYPlot(); plot.add(plot1); plot.add(plot2); // remove plot2, but plot1 is removed instead plot.remove(plot2); List plots = plot.getSubplots(); assertSame(plots.get(0), plot1); } /** * Tests the equals() method. */ @Test public void testEquals() { CombinedDomainXYPlot plot1 = createPlot(); CombinedDomainXYPlot plot2 = createPlot(); assertEquals(plot1, plot2); assertEquals(plot2, plot1); } /** * Confirm that cloning works. * * @throws java.lang.CloneNotSupportedException if there is a problem cloning. */ @Test public void testCloning() throws CloneNotSupportedException { CombinedDomainXYPlot plot1 = createPlot(); CombinedDomainXYPlot plot2 = (CombinedDomainXYPlot) plot1.clone(); assertNotSame(plot1, plot2); assertSame(plot1.getClass(), plot2.getClass()); assertEquals(plot1, plot2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { CombinedDomainXYPlot plot1 = createPlot(); CombinedDomainXYPlot plot2 = TestUtils.serialised(plot1); assertEquals(plot1, plot2); } /** * Check that only one chart change event is generated by a change to a * subplot. */ @Test public void testNotification() { CombinedDomainXYPlot plot = createPlot(); JFreeChart chart = new JFreeChart(plot); chart.addChangeListener(this); XYPlot subplot1 = plot.getSubplots().get(0); NumberAxis yAxis = (NumberAxis) subplot1.getRangeAxis(); yAxis.setAutoRangeIncludesZero(!yAxis.getAutoRangeIncludesZero()); assertEquals(1, this.events.size()); // a redraw should NOT trigger another change event BufferedImage image = new BufferedImage(200, 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); this.events.clear(); chart.draw(g2, new Rectangle2D.Double(0.0, 0.0, 200.0, 100.0)); assertTrue(this.events.isEmpty()); } /** * Creates a sample dataset. * * @return Series 1. */ private XYDataset createDataset1() { // create dataset 1... XYSeries series1 = new XYSeries("Series 1"); series1.add(10.0, 12353.3); series1.add(20.0, 13734.4); series1.add(30.0, 14525.3); series1.add(40.0, 13984.3); series1.add(50.0, 12999.4); series1.add(60.0, 14274.3); series1.add(70.0, 15943.5); series1.add(80.0, 14845.3); series1.add(90.0, 14645.4); series1.add(100.0, 16234.6); series1.add(110.0, 17232.3); series1.add(120.0, 14232.2); series1.add(130.0, 13102.2); series1.add(140.0, 14230.2); series1.add(150.0, 11235.2); XYSeries series2 = new XYSeries("Series 2"); series2.add(10.0, 15000.3); series2.add(20.0, 11000.4); series2.add(30.0, 17000.3); series2.add(40.0, 15000.3); series2.add(50.0, 14000.4); series2.add(60.0, 12000.3); series2.add(70.0, 11000.5); series2.add(80.0, 12000.3); series2.add(90.0, 13000.4); series2.add(100.0, 12000.6); series2.add(110.0, 13000.3); series2.add(120.0, 17000.2); series2.add(130.0, 18000.2); series2.add(140.0, 16000.2); series2.add(150.0, 17000.2); XYSeriesCollection collection = new XYSeriesCollection(); collection.addSeries(series1); collection.addSeries(series2); return collection; } /** * Creates a sample dataset. * * @return Series 2. */ private XYDataset createDataset2() { XYSeries series2 = new XYSeries("Series 3"); series2.add(10.0, 16853.2); series2.add(20.0, 19642.3); series2.add(30.0, 18253.5); series2.add(40.0, 15352.3); series2.add(50.0, 13532.0); series2.add(100.0, 12635.3); series2.add(110.0, 13998.2); series2.add(120.0, 11943.2); series2.add(130.0, 16943.9); series2.add(140.0, 17843.2); series2.add(150.0, 16495.3); series2.add(160.0, 17943.6); series2.add(170.0, 18500.7); series2.add(180.0, 19595.9); return new XYSeriesCollection(series2); } /** * Creates a sample plot. * * @return A sample plot. */ private CombinedDomainXYPlot createPlot() { // create subplot 1... XYDataset data1 = createDataset1(); XYItemRenderer renderer1 = new StandardXYItemRenderer(); NumberAxis rangeAxis1 = new NumberAxis("Range 1"); XYPlot subplot1 = new XYPlot(data1, null, rangeAxis1, renderer1); subplot1.setRangeAxisLocation(AxisLocation.BOTTOM_OR_LEFT); XYTextAnnotation annotation = new XYTextAnnotation("Hello!", 50.0, 10000.0); annotation.setFont(new Font("SansSerif", Font.PLAIN, 9)); annotation.setRotationAngle(Math.PI / 4.0); subplot1.addAnnotation(annotation); // create subplot 2... XYDataset data2 = createDataset2(); XYItemRenderer renderer2 = new StandardXYItemRenderer(); NumberAxis rangeAxis2 = new NumberAxis("Range 2"); rangeAxis2.setAutoRangeIncludesZero(false); XYPlot subplot2 = new XYPlot(data2, null, rangeAxis2, renderer2); subplot2.setRangeAxisLocation(AxisLocation.TOP_OR_LEFT); // parent plot... CombinedDomainXYPlot plot = new CombinedDomainXYPlot( new NumberAxis("Domain")); plot.setGap(10.0); // add the subplots... plot.add(subplot1, 1); plot.add(subplot2, 1); plot.setOrientation(PlotOrientation.VERTICAL); return plot; } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/CombinedRangeCategoryPlotTest.java000066400000000000000000000246421463604235500326340ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------------- * CombinedRangeCategoryPlotTest.java * ----------------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.text.AttributedString; import java.util.List; import java.util.ResourceBundle; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.JFreeChart; import org.jfree.chart.TestUtils; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.event.ChartChangeEvent; import org.jfree.chart.event.ChartChangeListener; import org.jfree.chart.labels.StandardCategoryToolTipGenerator; import org.jfree.chart.renderer.category.BarRenderer; import org.jfree.chart.renderer.category.LineAndShapeRenderer; import org.jfree.data.category.CategoryDataset; import org.jfree.data.category.DefaultCategoryDataset; import org.junit.jupiter.api.Test; import javax.swing.event.EventListenerList; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link CombinedRangeCategoryPlot} class. */ public class CombinedRangeCategoryPlotTest implements ChartChangeListener { /** A list of the events received. */ private final List events = new java.util.ArrayList<>(); /** * Receives a chart change event. * * @param event the event. */ @Override public void chartChanged(ChartChangeEvent event) { this.events.add(event); } /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(CombinedRangeCategoryPlot.class) .withRedefinedSuperclass() // superclass also defines equals/hashCode .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .withPrefabValues(Plot.class, TestUtils.createPlot(true), TestUtils.createPlot(false)) .withPrefabValues(JFreeChart.class, TestUtils.createJFC(true), TestUtils.createJFC(false)) .withPrefabValues(ResourceBundle.class, TestUtils.createRB(true), TestUtils.createRB(false)) .withPrefabValues(EventListenerList.class, new EventListenerList(), new EventListenerList()) .withPrefabValues(String.class, "A", "B") .withPrefabValues(AttributedString.class, TestUtils.createAS(true), TestUtils.createAS(false)) .withIgnoredFields("subplotArea") .withIgnoredFields("chart", "parent") // superclass .verify(); } /** * Test the equals() method. */ @Test public void testEquals() { CombinedRangeCategoryPlot plot1 = createPlot(); CombinedRangeCategoryPlot plot2 = createPlot(); assertEquals(plot1, plot2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { CombinedRangeCategoryPlot plot1 = createPlot(); CombinedRangeCategoryPlot plot2 = (CombinedRangeCategoryPlot) plot1.clone(); assertNotSame(plot1, plot2); assertSame(plot1.getClass(), plot2.getClass()); assertEquals(plot1, plot2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { CombinedRangeCategoryPlot plot1 = createPlot(); CombinedRangeCategoryPlot plot2 = TestUtils.serialised(plot1); assertEquals(plot1, plot2); } /** * This is a test to replicate the bug report 1121172. */ @Test public void testRemoveSubplot() { CombinedRangeCategoryPlot plot = new CombinedRangeCategoryPlot(); CategoryPlot plot1 = new CategoryPlot(); CategoryPlot plot2 = new CategoryPlot(); CategoryPlot plot3 = new CategoryPlot(); plot.add(plot1); plot.add(plot2); plot.add(plot3); plot.remove(plot2); List plots = plot.getSubplots(); assertEquals(2, plots.size()); } /** * Check that only one chart change event is generated by a change to a * subplot. */ @Test public void testNotification() { CombinedRangeCategoryPlot plot = createPlot(); JFreeChart chart = new JFreeChart(plot); chart.addChangeListener(this); CategoryPlot subplot1 = (CategoryPlot) plot.getSubplots().get(0); NumberAxis yAxis = (NumberAxis) subplot1.getRangeAxis(); yAxis.setAutoRangeIncludesZero(!yAxis.getAutoRangeIncludesZero()); assertEquals(1, this.events.size()); // a redraw should NOT trigger another change event BufferedImage image = new BufferedImage(200, 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); this.events.clear(); chart.draw(g2, new Rectangle2D.Double(0.0, 0.0, 200.0, 100.0)); assertTrue(this.events.isEmpty()); } /** * Creates a dataset. * * @return A dataset. */ public CategoryDataset createDataset1() { DefaultCategoryDataset result = new DefaultCategoryDataset(); // row keys... String series1 = "First"; String series2 = "Second"; // column keys... String type1 = "Type 1"; String type2 = "Type 2"; String type3 = "Type 3"; String type4 = "Type 4"; String type5 = "Type 5"; String type6 = "Type 6"; String type7 = "Type 7"; String type8 = "Type 8"; result.addValue(1.0, series1, type1); result.addValue(4.0, series1, type2); result.addValue(3.0, series1, type3); result.addValue(5.0, series1, type4); result.addValue(5.0, series1, type5); result.addValue(7.0, series1, type6); result.addValue(7.0, series1, type7); result.addValue(8.0, series1, type8); result.addValue(5.0, series2, type1); result.addValue(7.0, series2, type2); result.addValue(6.0, series2, type3); result.addValue(8.0, series2, type4); result.addValue(4.0, series2, type5); result.addValue(4.0, series2, type6); result.addValue(2.0, series2, type7); result.addValue(1.0, series2, type8); return result; } /** * Creates a dataset. * * @return A dataset. */ public CategoryDataset createDataset2() { DefaultCategoryDataset result = new DefaultCategoryDataset(); // row keys... String series1 = "Third"; String series2 = "Fourth"; // column keys... String type1 = "Type 1"; String type2 = "Type 2"; String type3 = "Type 3"; String type4 = "Type 4"; String type5 = "Type 5"; String type6 = "Type 6"; String type7 = "Type 7"; String type8 = "Type 8"; result.addValue(11.0, series1, type1); result.addValue(14.0, series1, type2); result.addValue(13.0, series1, type3); result.addValue(15.0, series1, type4); result.addValue(15.0, series1, type5); result.addValue(17.0, series1, type6); result.addValue(17.0, series1, type7); result.addValue(18.0, series1, type8); result.addValue(15.0, series2, type1); result.addValue(17.0, series2, type2); result.addValue(16.0, series2, type3); result.addValue(18.0, series2, type4); result.addValue(14.0, series2, type5); result.addValue(14.0, series2, type6); result.addValue(12.0, series2, type7); result.addValue(11.0, series2, type8); return result; } /** * Creates a sample plot. * * @return A plot. */ private CombinedRangeCategoryPlot createPlot() { CategoryDataset dataset1 = createDataset1(); CategoryAxis catAxis1 = new CategoryAxis("Category"); LineAndShapeRenderer renderer1 = new LineAndShapeRenderer(); renderer1.setDefaultToolTipGenerator( new StandardCategoryToolTipGenerator()); CategoryPlot subplot1 = new CategoryPlot(dataset1, catAxis1, null, renderer1); subplot1.setDomainGridlinesVisible(true); CategoryDataset dataset2 = createDataset2(); CategoryAxis catAxis2 = new CategoryAxis("Category"); BarRenderer renderer2 = new BarRenderer(); renderer2.setDefaultToolTipGenerator( new StandardCategoryToolTipGenerator()); CategoryPlot subplot2 = new CategoryPlot(dataset2, catAxis2, null, renderer2); subplot2.setDomainGridlinesVisible(true); NumberAxis rangeAxis = new NumberAxis("Value"); CombinedRangeCategoryPlot plot = new CombinedRangeCategoryPlot( rangeAxis); plot.add(subplot1, 2); plot.add(subplot2, 1); return plot; } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/CombinedRangeXYPlotTest.java000066400000000000000000000207651463604235500314210ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------- * CombinedRangeXYPlotTest.java * ---------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import java.awt.Font; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.util.List; import org.jfree.chart.JFreeChart; import org.jfree.chart.TestUtils; import org.jfree.chart.annotations.XYTextAnnotation; import org.jfree.chart.axis.AxisLocation; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.event.ChartChangeEvent; import org.jfree.chart.event.ChartChangeListener; import org.jfree.chart.renderer.xy.StandardXYItemRenderer; import org.jfree.chart.renderer.xy.XYItemRenderer; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link CombinedRangeXYPlot} class. */ public class CombinedRangeXYPlotTest implements ChartChangeListener { /** A list of the events received. */ private List events = new java.util.ArrayList(); /** * Receives a chart change event. * * @param event the event. */ @Override public void chartChanged(ChartChangeEvent event) { this.events.add(event); } /** * Test the equals method. */ @Test public void testEquals() { CombinedRangeXYPlot plot1 = createPlot(); CombinedRangeXYPlot plot2 = createPlot(); assertEquals(plot1, plot2); assertEquals(plot2, plot1); } /** * This is a test to replicate the bug report 987080. */ @Test public void testRemoveSubplot() { CombinedRangeXYPlot plot = new CombinedRangeXYPlot(); XYPlot plot1 = new XYPlot(); XYPlot plot2 = new XYPlot(); plot.add(plot1); plot.add(plot2); // remove plot2, but plot1 is removed instead plot.remove(plot2); List plots = plot.getSubplots(); assertSame(plots.get(0), plot1); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { CombinedRangeXYPlot plot1 = createPlot(); CombinedRangeXYPlot plot2 = (CombinedRangeXYPlot) plot1.clone(); assertNotSame(plot1, plot2); assertSame(plot1.getClass(), plot2.getClass()); assertEquals(plot1, plot2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { CombinedRangeXYPlot plot1 = createPlot(); CombinedRangeXYPlot plot2 = TestUtils.serialised(plot1); assertEquals(plot1, plot2); } /** * Check that only one chart change event is generated by a change to a * subplot. */ @Test public void testNotification() { CombinedRangeXYPlot plot = createPlot(); JFreeChart chart = new JFreeChart(plot); chart.addChangeListener(this); XYPlot subplot1 = plot.getSubplots().get(0); NumberAxis xAxis = (NumberAxis) subplot1.getDomainAxis(); xAxis.setAutoRangeIncludesZero(!xAxis.getAutoRangeIncludesZero()); assertEquals(1, this.events.size()); // a redraw should NOT trigger another change event BufferedImage image = new BufferedImage(200, 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); this.events.clear(); chart.draw(g2, new Rectangle2D.Double(0.0, 0.0, 200.0, 100.0)); assertTrue(this.events.isEmpty()); } /** * Creates a sample dataset. * * @return Series 1. */ private XYDataset createDataset1() { XYSeries series1 = new XYSeries("Series 1"); series1.add(10.0, 12353.3); series1.add(20.0, 13734.4); series1.add(30.0, 14525.3); series1.add(40.0, 13984.3); series1.add(50.0, 12999.4); series1.add(60.0, 14274.3); series1.add(70.0, 15943.5); series1.add(80.0, 14845.3); series1.add(90.0, 14645.4); series1.add(100.0, 16234.6); series1.add(110.0, 17232.3); series1.add(120.0, 14232.2); series1.add(130.0, 13102.2); series1.add(140.0, 14230.2); series1.add(150.0, 11235.2); XYSeries series2 = new XYSeries("Series 2"); series2.add(10.0, 15000.3); series2.add(20.0, 11000.4); series2.add(30.0, 17000.3); series2.add(40.0, 15000.3); series2.add(50.0, 14000.4); series2.add(60.0, 12000.3); series2.add(70.0, 11000.5); series2.add(80.0, 12000.3); series2.add(90.0, 13000.4); series2.add(100.0, 12000.6); series2.add(110.0, 13000.3); series2.add(120.0, 17000.2); series2.add(130.0, 18000.2); series2.add(140.0, 16000.2); series2.add(150.0, 17000.2); XYSeriesCollection collection = new XYSeriesCollection(); collection.addSeries(series1); collection.addSeries(series2); return collection; } /** * Creates a sample dataset. * * @return Series 2. */ private XYDataset createDataset2() { // create dataset 2... XYSeries series2 = new XYSeries("Series 3"); series2.add(10.0, 16853.2); series2.add(20.0, 19642.3); series2.add(30.0, 18253.5); series2.add(40.0, 15352.3); series2.add(50.0, 13532.0); series2.add(100.0, 12635.3); series2.add(110.0, 13998.2); series2.add(120.0, 11943.2); series2.add(130.0, 16943.9); series2.add(140.0, 17843.2); series2.add(150.0, 16495.3); series2.add(160.0, 17943.6); series2.add(170.0, 18500.7); series2.add(180.0, 19595.9); return new XYSeriesCollection(series2); } /** * Creates a sample plot. * * @return A sample plot. */ private CombinedRangeXYPlot createPlot() { // create subplot 1... XYDataset data1 = createDataset1(); XYItemRenderer renderer1 = new StandardXYItemRenderer(); NumberAxis xAxis1 = new NumberAxis("X1"); XYPlot subplot1 = new XYPlot(data1, xAxis1, null, renderer1); subplot1.setRangeAxisLocation(AxisLocation.BOTTOM_OR_LEFT); XYTextAnnotation annotation = new XYTextAnnotation("Hello!", 50.0, 10000.0); annotation.setFont(new Font("SansSerif", Font.PLAIN, 9)); annotation.setRotationAngle(Math.PI / 4.0); subplot1.addAnnotation(annotation); // create subplot 2... XYDataset data2 = createDataset2(); XYItemRenderer renderer2 = new StandardXYItemRenderer(); NumberAxis xAxis2 = new NumberAxis("X2"); xAxis2.setAutoRangeIncludesZero(false); XYPlot subplot2 = new XYPlot(data2, xAxis2, null, renderer2); subplot2.setRangeAxisLocation(AxisLocation.TOP_OR_LEFT); // parent plot... CombinedRangeXYPlot plot = new CombinedRangeXYPlot(new NumberAxis( "Range")); plot.setGap(10.0); // add the subplots... plot.add(subplot1, 1); plot.add(subplot2, 1); plot.setOrientation(PlotOrientation.VERTICAL); return plot; } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/CompassPlotTest.java000066400000000000000000000114311463604235500300360ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * CompassPlotTest.java * -------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import java.awt.Color; import java.awt.Font; import java.awt.GradientPaint; import org.jfree.chart.TestUtils; import org.jfree.chart.needle.PointerNeedle; import org.jfree.data.general.DefaultValueDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link CompassPlot} class. */ public class CompassPlotTest { /** * Test the equals() method. */ @Test public void testEquals() { CompassPlot plot1 = new CompassPlot(); CompassPlot plot2 = new CompassPlot(); assertEquals(plot1, plot2); // labelType... plot1.setLabelType(CompassPlot.VALUE_LABELS); assertNotEquals(plot1, plot2); plot2.setLabelType(CompassPlot.VALUE_LABELS); assertEquals(plot1, plot2); // labelFont plot1.setLabelFont(new Font("Serif", Font.PLAIN, 10)); assertNotEquals(plot1, plot2); plot2.setLabelFont(new Font("Serif", Font.PLAIN, 10)); assertEquals(plot1, plot2); // drawBorder plot1.setDrawBorder(true); assertNotEquals(plot1, plot2); plot2.setDrawBorder(true); assertEquals(plot1, plot2); // rosePaint plot1.setRosePaint(new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.YELLOW)); assertNotEquals(plot1, plot2); plot2.setRosePaint(new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.YELLOW)); assertEquals(plot1, plot2); // roseCenterPaint plot1.setRoseCenterPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW)); assertNotEquals(plot1, plot2); plot2.setRoseCenterPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW)); assertEquals(plot1, plot2); // roseHighlightPaint plot1.setRoseHighlightPaint(new GradientPaint(1.0f, 2.0f, Color.GREEN, 3.0f, 4.0f, Color.YELLOW)); assertNotEquals(plot1, plot2); plot2.setRoseHighlightPaint(new GradientPaint(1.0f, 2.0f, Color.GREEN, 3.0f, 4.0f, Color.YELLOW)); assertEquals(plot1, plot2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { CompassPlot p1 = new CompassPlot(null); p1.setRosePaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); p1.setRoseCenterPaint(new GradientPaint(4.0f, 3.0f, Color.RED, 2.0f, 1.0f, Color.GREEN)); p1.setRoseHighlightPaint(new GradientPaint(4.0f, 3.0f, Color.RED, 2.0f, 1.0f, Color.GREEN)); CompassPlot p2 = TestUtils.serialised(p1); assertEquals(p1, p2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { CompassPlot p1 = new CompassPlot(new DefaultValueDataset(15.0)); CompassPlot p2 = (CompassPlot) p1.clone(); assertNotSame(p1, p2); assertSame(p1.getClass(), p2.getClass()); assertEquals(p1, p2); } /** * Test faulty array bounds; CVE-2024-23077. */ @Test public void testArrayBounds() { CompassPlot p = new CompassPlot(new DefaultValueDataset(0)); p.setSeriesNeedle(-1, new PointerNeedle()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/CrosshairTest.java000066400000000000000000000135241463604235500275340ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * CrosshairTest.java * ------------------ * (C) Copyright 2009-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.GradientPaint; import java.text.NumberFormat; import org.jfree.chart.TestUtils; import org.jfree.chart.labels.StandardCrosshairLabelGenerator; import org.jfree.chart.ui.RectangleAnchor; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link Crosshair} class. */ public class CrosshairTest { /** * Some checks for the equals() method. */ @Test public void testEquals() { Crosshair c1 = new Crosshair(1.0, Color.BLUE, new BasicStroke(1.0f)); Crosshair c2 = new Crosshair(1.0, Color.BLUE, new BasicStroke(1.0f)); assertEquals(c1, c1); assertEquals(c2, c1); c1.setVisible(false); assertNotEquals(c1, c2); c2.setVisible(false); assertEquals(c1, c2); c1.setValue(2.0); assertNotEquals(c1, c2); c2.setValue(2.0); assertEquals(c1, c2); c1.setPaint(Color.RED); assertNotEquals(c1, c2); c2.setPaint(Color.RED); assertEquals(c1, c2); c1.setStroke(new BasicStroke(1.1f)); assertNotEquals(c1, c2); c2.setStroke(new BasicStroke(1.1f)); assertEquals(c1, c2); c1.setLabelVisible(true); assertNotEquals(c1, c2); c2.setLabelVisible(true); assertEquals(c1, c2); c1.setLabelAnchor(RectangleAnchor.TOP_LEFT); assertNotEquals(c1, c2); c2.setLabelAnchor(RectangleAnchor.TOP_LEFT); assertEquals(c1, c2); c1.setLabelGenerator(new StandardCrosshairLabelGenerator("Value = {0}", NumberFormat.getNumberInstance())); assertNotEquals(c1, c2); c2.setLabelGenerator(new StandardCrosshairLabelGenerator("Value = {0}", NumberFormat.getNumberInstance())); assertEquals(c1, c2); c1.setLabelXOffset(11); assertNotEquals(c1, c2); c2.setLabelXOffset(11); assertEquals(c1, c2); c1.setLabelYOffset(22); assertNotEquals(c1, c2); c2.setLabelYOffset(22); assertEquals(c1, c2); c1.setLabelFont(new Font("Dialog", Font.PLAIN, 8)); assertNotEquals(c1, c2); c2.setLabelFont(new Font("Dialog", Font.PLAIN, 8)); assertEquals(c1, c2); c1.setLabelPaint(Color.RED); assertNotEquals(c1, c2); c2.setLabelPaint(Color.RED); assertEquals(c1, c2); c1.setLabelBackgroundPaint(Color.YELLOW); assertNotEquals(c1, c2); c2.setLabelBackgroundPaint(Color.YELLOW); assertEquals(c1, c2); c1.setLabelOutlineVisible(false); assertNotEquals(c1, c2); c2.setLabelOutlineVisible(false); assertEquals(c1, c2); c1.setLabelOutlineStroke(new BasicStroke(2.0f)); assertNotEquals(c1, c2); c2.setLabelOutlineStroke(new BasicStroke(2.0f)); assertEquals(c1, c2); c1.setLabelOutlinePaint(Color.darkGray); assertNotEquals(c1, c2); c2.setLabelOutlinePaint(Color.darkGray); assertEquals(c1, c2); } /** * Simple check that hashCode is implemented. */ @Test public void testHashCode() { Crosshair c1 = new Crosshair(1.0); Crosshair c2 = new Crosshair(1.0); assertEquals(c1, c2); assertEquals(c1.hashCode(), c2.hashCode()); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { Crosshair c1 = new Crosshair(1.0, new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE), new BasicStroke(1.0f)); Crosshair c2 = (Crosshair) c1.clone(); assertNotSame(c1, c2); assertSame(c1.getClass(), c2.getClass()); assertEquals(c1, c2); } /** * Check to ensure that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { Crosshair c1 = new Crosshair(1.0); assertTrue(c1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { Crosshair c1 = new Crosshair(1.0, new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE), new BasicStroke(1.0f)); Crosshair c2 = TestUtils.serialised(c1); assertEquals(c1, c2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/DefaultDrawingSupplierTest.java000066400000000000000000000153051463604235500322220ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------- * DefaultDrawingSupplierTest.java * ------------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Paint; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Rectangle2D; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link DefaultDrawingSupplier} class. */ public class DefaultDrawingSupplierTest { /** * Check that the equals() method can distinguish all required fields. */ @Test public void testEquals() { DefaultDrawingSupplier r1 = new DefaultDrawingSupplier(); DefaultDrawingSupplier r2 = new DefaultDrawingSupplier(); assertEquals(r1, r2); assertEquals(r2, r1); // set up some objects... Paint[] ps1A = new Paint[] {Color.RED, Color.BLUE}; Paint[] ps2A = new Paint[] {Color.GREEN, Color.YELLOW, Color.WHITE}; Paint[] ops1A = new Paint[] {Color.LIGHT_GRAY, Color.BLUE}; Paint[] ops2A = new Paint[] {Color.BLACK, Color.YELLOW, Color.CYAN}; Stroke[] ss1A = new Stroke[] {new BasicStroke(1.1f)}; Stroke[] ss2A = new Stroke[] {new BasicStroke(2.2f), new BasicStroke(3.3f)}; Stroke[] oss1A = new Stroke[] {new BasicStroke(4.4f)}; Stroke[] oss2A = new Stroke[] {new BasicStroke(5.5f), new BasicStroke(6.6f)}; Shape[] shapes1A = new Shape[] { new Rectangle2D.Double(1.0, 1.0, 1.0, 1.0) }; Shape[] shapes2A = new Shape[] { new Rectangle2D.Double(2.0, 2.0, 2.0, 2.0), new Rectangle2D.Double(2.0, 2.0, 2.0, 2.0) }; Paint[] ps1B = new Paint[] {Color.RED, Color.BLUE}; Paint[] ps2B = new Paint[] {Color.GREEN, Color.YELLOW, Color.WHITE}; Paint[] ops1B = new Paint[] {Color.LIGHT_GRAY, Color.BLUE}; Paint[] ops2B = new Paint[] {Color.BLACK, Color.YELLOW, Color.CYAN}; Stroke[] ss1B = new Stroke[] {new BasicStroke(1.1f)}; Stroke[] ss2B = new Stroke[] {new BasicStroke(2.2f), new BasicStroke(3.3f)}; Stroke[] oss1B = new Stroke[] {new BasicStroke(4.4f)}; Stroke[] oss2B = new Stroke[] {new BasicStroke(5.5f), new BasicStroke(6.6f)}; Shape[] shapes1B = new Shape[] { new Rectangle2D.Double(1.0, 1.0, 1.0, 1.0) }; Shape[] shapes2B = new Shape[] { new Rectangle2D.Double(2.0, 2.0, 2.0, 2.0), new Rectangle2D.Double(2.0, 2.0, 2.0, 2.0) }; r1 = new DefaultDrawingSupplier(ps1A, ops1A, ss1A, oss1A, shapes1A); r2 = new DefaultDrawingSupplier(ps1B, ops1B, ss1B, oss1B, shapes1B); assertEquals(r1, r2); // paint sequence r1 = new DefaultDrawingSupplier(ps2A, ops1A, ss1A, oss1A, shapes1A); assertNotEquals(r1, r2); r2 = new DefaultDrawingSupplier(ps2B, ops1B, ss1B, oss1B, shapes1B); assertEquals(r1, r2); // outline paint sequence r1 = new DefaultDrawingSupplier(ps2A, ops2A, ss1A, oss1A, shapes1A); assertNotEquals(r1, r2); r2 = new DefaultDrawingSupplier(ps2B, ops2B, ss1B, oss1B, shapes1B); assertEquals(r1, r2); // stroke sequence r1 = new DefaultDrawingSupplier(ps2A, ops2A, ss2A, oss1A, shapes1A); assertNotEquals(r1, r2); r2 = new DefaultDrawingSupplier(ps2B, ops2B, ss2B, oss1B, shapes1B); assertEquals(r1, r2); // outline stroke sequence r1 = new DefaultDrawingSupplier(ps2A, ops2A, ss2A, oss2A, shapes1A); assertNotEquals(r1, r2); r2 = new DefaultDrawingSupplier(ps2B, ops2B, ss2B, oss2B, shapes1B); assertEquals(r1, r2); // shape sequence r1 = new DefaultDrawingSupplier(ps2A, ops2A, ss2A, oss2A, shapes2A); assertNotEquals(r1, r2); r2 = new DefaultDrawingSupplier(ps2B, ops2B, ss2B, oss2B, shapes2B); assertEquals(r1, r2); // paint index r1.getNextPaint(); assertNotEquals(r1, r2); r2.getNextPaint(); assertEquals(r1, r2); // outline paint index r1.getNextOutlinePaint(); assertNotEquals(r1, r2); r2.getNextOutlinePaint(); assertEquals(r1, r2); // stroke index r1.getNextStroke(); assertNotEquals(r1, r2); r2.getNextStroke(); assertEquals(r1, r2); // outline stroke index r1.getNextOutlineStroke(); assertNotEquals(r1, r2); r2.getNextOutlineStroke(); assertEquals(r1, r2); // shape index r1.getNextShape(); assertNotEquals(r1, r2); r2.getNextShape(); assertEquals(r1, r2); } /** * Some basic checks for the clone() method. */ @Test public void testCloning() throws CloneNotSupportedException { DefaultDrawingSupplier r1 = new DefaultDrawingSupplier(); DefaultDrawingSupplier r2 = (DefaultDrawingSupplier) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DefaultDrawingSupplier r1 = new DefaultDrawingSupplier(); DefaultDrawingSupplier r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/FastScatterPlotTest.java000066400000000000000000000163721463604235500306650ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * FastScatterPlotTest.java * ------------------------ * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import java.awt.BasicStroke; import java.awt.Color; import java.awt.GradientPaint; import java.awt.Stroke; import org.jfree.chart.JFreeChart; import org.jfree.chart.TestUtils; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.axis.ValueAxis; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link FastScatterPlot} class. */ public class FastScatterPlotTest { /** * Some checks for the equals() method. */ @Test public void testEquals() { FastScatterPlot plot1 = new FastScatterPlot(); FastScatterPlot plot2 = new FastScatterPlot(); assertEquals(plot1, plot2); assertEquals(plot2, plot1); plot1.setPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW)); assertNotEquals(plot1, plot2); plot2.setPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW)); assertEquals(plot1, plot2); plot1.setDomainGridlinesVisible(false); assertNotEquals(plot1, plot2); plot2.setDomainGridlinesVisible(false); assertEquals(plot1, plot2); plot1.setDomainGridlinePaint(new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.YELLOW)); assertNotEquals(plot1, plot2); plot2.setDomainGridlinePaint(new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.YELLOW)); assertEquals(plot1, plot2); Stroke s = new BasicStroke(1.5f); plot1.setDomainGridlineStroke(s); assertNotEquals(plot1, plot2); plot2.setDomainGridlineStroke(s); assertEquals(plot1, plot2); plot1.setRangeGridlinesVisible(false); assertNotEquals(plot1, plot2); plot2.setRangeGridlinesVisible(false); assertEquals(plot1, plot2); plot1.setRangeGridlinePaint(new GradientPaint(1.0f, 2.0f, Color.GREEN, 3.0f, 4.0f, Color.YELLOW)); assertNotEquals(plot1, plot2); plot2.setRangeGridlinePaint(new GradientPaint(1.0f, 2.0f, Color.GREEN, 3.0f, 4.0f, Color.YELLOW)); assertEquals(plot1, plot2); Stroke s2 = new BasicStroke(1.5f); plot1.setRangeGridlineStroke(s2); assertNotEquals(plot1, plot2); plot2.setRangeGridlineStroke(s2); assertEquals(plot1, plot2); plot1.setDomainPannable(true); assertNotEquals(plot1, plot2); plot2.setDomainPannable(true); assertEquals(plot1, plot2); plot1.setRangePannable(true); assertNotEquals(plot1, plot2); plot2.setRangePannable(true); assertEquals(plot1, plot2); } /** * Some tests for the data array equality in the equals() method. */ @Test public void testEquals2() { FastScatterPlot plot1 = new FastScatterPlot(); FastScatterPlot plot2 = new FastScatterPlot(); assertEquals(plot1, plot2); assertEquals(plot2, plot1); float[][] a = new float[2][]; float[][] b = new float[2][]; plot1.setData(a); assertNotEquals(plot1, plot2); plot2.setData(b); assertEquals(plot1, plot2); a[0] = new float[6]; assertNotEquals(plot1, plot2); b[0] = new float[6]; assertEquals(plot1, plot2); a[0][0] = 1.0f; assertNotEquals(plot1, plot2); b[0][0] = 1.0f; assertEquals(plot1, plot2); a[0][1] = Float.NaN; assertNotEquals(plot1, plot2); b[0][1] = Float.NaN; assertEquals(plot1, plot2); a[0][2] = Float.POSITIVE_INFINITY; assertNotEquals(plot1, plot2); b[0][2] = Float.POSITIVE_INFINITY; assertEquals(plot1, plot2); a[0][3] = Float.NEGATIVE_INFINITY; assertNotEquals(plot1, plot2); b[0][3] = Float.NEGATIVE_INFINITY; assertEquals(plot1, plot2); } /** * Confirm that cloning works. * * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { FastScatterPlot p1 = new FastScatterPlot(); FastScatterPlot p2 = (FastScatterPlot) p1.clone(); assertNotSame(p1, p2); assertSame(p1.getClass(), p2.getClass()); assertEquals(p1, p2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { float[][] data = createData(); ValueAxis domainAxis = new NumberAxis("X"); ValueAxis rangeAxis = new NumberAxis("Y"); FastScatterPlot p1 = new FastScatterPlot(data, domainAxis, rangeAxis); FastScatterPlot p2 = TestUtils.serialised(p1); assertEquals(p1, p2); } /** * Draws the chart with a {@code null} info object to make sure that * no exceptions are thrown. */ @Test public void testDrawWithNullInfo() { try { float[][] data = createData(); ValueAxis domainAxis = new NumberAxis("X"); ValueAxis rangeAxis = new NumberAxis("Y"); FastScatterPlot plot = new FastScatterPlot(data, domainAxis, rangeAxis); JFreeChart chart = new JFreeChart(plot); /* BufferedImage image = */ chart.createBufferedImage(300, 200, null); } catch (NullPointerException e) { fail("No exception should be thrown."); } } /** * Populates the data array with random values. * * @return Random data. */ private float[][] createData() { float[][] result = new float[2][1000]; for (int i = 0; i < result[0].length; i++) { float x = (float) i + 100; result[0][i] = x; result[1][i] = 100 + (float) Math.random() * 1000; } return result; } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/IntervalMarkerTest.java000066400000000000000000000132711463604235500305240ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * IntervalMarkerTest.java * ----------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.plot; import java.awt.Font; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.chart.event.MarkerChangeEvent; import org.jfree.chart.event.MarkerChangeListener; import org.jfree.chart.ui.GradientPaintTransformType; import org.jfree.chart.ui.GradientPaintTransformer; import org.jfree.chart.ui.StandardGradientPaintTransformer; import org.junit.jupiter.api.Test; import javax.swing.event.EventListenerList; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link IntervalMarker} class. */ public class IntervalMarkerTest implements MarkerChangeListener { MarkerChangeEvent lastEvent; /** * Records the last event. * * @param event the last event. */ @Override public void markerChanged(MarkerChangeEvent event) { this.lastEvent = event; } /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(IntervalMarker.class) .withRedefinedSuperclass() // superclass also defines equals/hashCode .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .withPrefabValues(Font.class, new Font("SansSerif", Font.PLAIN, 10), new Font("Tahoma", Font.BOLD, 12)) .withPrefabValues(EventListenerList.class, new EventListenerList(), new EventListenerList()) .verify(); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { IntervalMarker m1 = new IntervalMarker(45.0, 50.0); IntervalMarker m2 = new IntervalMarker(45.0, 50.0); assertEquals(m1, m2); assertEquals(m2, m1); m1 = new IntervalMarker(44.0, 50.0); assertNotEquals(m1, m2); m2 = new IntervalMarker(44.0, 50.0); assertEquals(m1, m2); m1 = new IntervalMarker(44.0, 55.0); assertNotEquals(m1, m2); m2 = new IntervalMarker(44.0, 55.0); assertEquals(m1, m2); GradientPaintTransformer t = new StandardGradientPaintTransformer( GradientPaintTransformType.HORIZONTAL); m1.setGradientPaintTransformer(t); assertNotEquals(m1, m2); m2.setGradientPaintTransformer(t); assertEquals(m1, m2); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException if there is a cloning issue */ @Test public void testCloning() throws CloneNotSupportedException { IntervalMarker m1 = new IntervalMarker(45.0, 50.0); IntervalMarker m2 = (IntervalMarker) m1.clone(); assertNotSame(m1, m2); assertSame(m1.getClass(), m2.getClass()); assertEquals(m1, m2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { IntervalMarker m1 = new IntervalMarker(45.0, 50.0); IntervalMarker m2 = TestUtils.serialised(m1); assertEquals(m1, m2); } private static final double EPSILON = 0.0000000001; /** * Some checks for the getStartValue() and setStartValue() methods. */ @Test public void testGetSetStartValue() { IntervalMarker m = new IntervalMarker(1.0, 2.0); m.addChangeListener(this); this.lastEvent = null; assertEquals(1.0, m.getStartValue(), EPSILON); m.setStartValue(0.5); assertEquals(0.5, m.getStartValue(), EPSILON); assertEquals(m, this.lastEvent.getMarker()); } /** * Some checks for the getEndValue() and setEndValue() methods. */ @Test public void testGetSetEndValue() { IntervalMarker m = new IntervalMarker(1.0, 2.0); m.addChangeListener(this); this.lastEvent = null; assertEquals(2.0, m.getEndValue(), EPSILON); m.setEndValue(0.5); assertEquals(0.5, m.getEndValue(), EPSILON); assertEquals(m, this.lastEvent.getMarker()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/MarkerTest.java000066400000000000000000000351341463604235500270210ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * MarkerTest.java * --------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.plot; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.util.Arrays; import java.util.EventListener; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.event.MarkerChangeEvent; import org.jfree.chart.event.MarkerChangeListener; import org.jfree.chart.ui.LengthAdjustmentType; import org.jfree.chart.ui.RectangleAnchor; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.ui.TextAnchor; import org.junit.jupiter.api.Test; import javax.swing.event.EventListenerList; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link Marker} class. */ public class MarkerTest implements MarkerChangeListener { MarkerChangeEvent lastEvent; /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(Marker.class) .withRedefinedSubclass(IntervalMarker.class) // subclass also defines equals/hashCode .withRedefinedSubclass(CategoryMarker.class) .withRedefinedSubclass(ValueMarker.class) .withPrefabValues(Font.class, new Font("SansSerif", Font.PLAIN, 10), new Font("Tahoma", Font.BOLD, 12)) .withPrefabValues(Marker.class, new ValueMarker(44.5), new ValueMarker(33.3)) .withPrefabValues(EventListenerList.class, new EventListenerList(), new EventListenerList()) // .suppress(Warning.STRICT_INHERITANCE) // no need for this because we already told it not to worry about inheritance .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Some checks for the getPaint() and setPaint() methods. */ @Test public void testGetSetPaint() { // we use ValueMarker for the tests, because we need a concrete // subclass... ValueMarker m = new ValueMarker(1.1); m.addChangeListener(this); this.lastEvent = null; assertEquals(Color.GRAY, m.getPaint()); m.setPaint(Color.BLUE); assertEquals(Color.BLUE, m.getPaint()); assertEquals(m, this.lastEvent.getMarker()); // check null argument... try { m.setPaint(null); fail("Expected an IllegalArgumentException for null."); } catch (IllegalArgumentException e) { assertTrue(true); } } /** * Some checks for the getStroke() and setStroke() methods. */ @Test public void testGetSetStroke() { // we use ValueMarker for the tests, because we need a concrete // subclass... ValueMarker m = new ValueMarker(1.1); m.addChangeListener(this); this.lastEvent = null; assertEquals(new BasicStroke(0.5f), m.getStroke()); m.setStroke(new BasicStroke(1.1f)); assertEquals(new BasicStroke(1.1f), m.getStroke()); assertEquals(m, this.lastEvent.getMarker()); // check null argument... try { m.setStroke(null); fail("Expected an IllegalArgumentException for null."); } catch (IllegalArgumentException e) { assertTrue(true); } } /** * Some checks for the getOutlinePaint() and setOutlinePaint() methods. */ @Test public void testGetSetOutlinePaint() { // we use ValueMarker for the tests, because we need a concrete // subclass... ValueMarker m = new ValueMarker(1.1); m.addChangeListener(this); this.lastEvent = null; assertEquals(Color.GRAY, m.getOutlinePaint()); m.setOutlinePaint(Color.YELLOW); assertEquals(Color.YELLOW, m.getOutlinePaint()); assertEquals(m, this.lastEvent.getMarker()); // check null argument... m.setOutlinePaint(null); assertNull(m.getOutlinePaint()); } /** * Some checks for the getOutlineStroke() and setOutlineStroke() methods. */ @Test public void testGetSetOutlineStroke() { // we use ValueMarker for the tests, because we need a concrete // subclass... ValueMarker m = new ValueMarker(1.1); m.addChangeListener(this); this.lastEvent = null; assertEquals(new BasicStroke(0.5f), m.getOutlineStroke()); m.setOutlineStroke(new BasicStroke(1.1f)); assertEquals(new BasicStroke(1.1f), m.getOutlineStroke()); assertEquals(m, this.lastEvent.getMarker()); // check null argument... m.setOutlineStroke(null); assertNull(m.getOutlineStroke()); } private static final float EPSILON = 0.000000001f; /** * Some checks for the getAlpha() and setAlpha() methods. */ @Test public void testGetSetAlpha() { // we use ValueMarker for the tests, because we need a concrete // subclass... ValueMarker m = new ValueMarker(1.1); m.addChangeListener(this); this.lastEvent = null; assertEquals(0.8f, m.getAlpha(), EPSILON); m.setAlpha(0.5f); assertEquals(0.5f, m.getAlpha(), EPSILON); assertEquals(m, this.lastEvent.getMarker()); } /** * Some checks for the getLabel() and setLabel() methods. */ @Test public void testGetSetLabel() { // we use ValueMarker for the tests, because we need a concrete // subclass... ValueMarker m = new ValueMarker(1.1); m.addChangeListener(this); this.lastEvent = null; assertNull(m.getLabel()); m.setLabel("XYZ"); assertEquals("XYZ", m.getLabel()); assertEquals(m, this.lastEvent.getMarker()); // check null argument... m.setLabel(null); assertNull(m.getLabel()); } /** * Some checks for the getLabelFont() and setLabelFont() methods. */ @Test public void testGetSetLabelFont() { // we use ValueMarker for the tests, because we need a concrete // subclass... ValueMarker m = new ValueMarker(1.1); m.addChangeListener(this); this.lastEvent = null; assertEquals(new Font("SansSerif", Font.PLAIN, 9), m.getLabelFont()); m.setLabelFont(new Font("SansSerif", Font.BOLD, 10)); assertEquals(new Font("SansSerif", Font.BOLD, 10), m.getLabelFont()); assertEquals(m, this.lastEvent.getMarker()); // check null argument... try { m.setLabelFont(null); fail("Expected an IllegalArgumentException for null."); } catch (IllegalArgumentException e) { assertTrue(true); } } /** * Some checks for the getLabelPaint() and setLabelPaint() methods. */ @Test public void testGetSetLabelPaint() { // we use ValueMarker for the tests, because we need a concrete // subclass... ValueMarker m = new ValueMarker(1.1); m.addChangeListener(this); this.lastEvent = null; assertEquals(Color.BLACK, m.getLabelPaint()); m.setLabelPaint(Color.RED); assertEquals(Color.RED, m.getLabelPaint()); assertEquals(m, this.lastEvent.getMarker()); // check null argument... try { m.setLabelPaint(null); fail("Expected an IllegalArgumentException for null."); } catch (IllegalArgumentException e) { assertTrue(true); } } /** * Some checks for the getLabelAnchor() and setLabelAnchor() methods. */ @Test public void testGetSetLabelAnchor() { // we use ValueMarker for the tests, because we need a concrete // subclass... ValueMarker m = new ValueMarker(1.1); m.addChangeListener(this); this.lastEvent = null; assertEquals(RectangleAnchor.TOP_LEFT, m.getLabelAnchor()); m.setLabelAnchor(RectangleAnchor.TOP); assertEquals(RectangleAnchor.TOP, m.getLabelAnchor()); assertEquals(m, this.lastEvent.getMarker()); // check null argument... try { m.setLabelAnchor(null); fail("Expected an IllegalArgumentException for null."); } catch (IllegalArgumentException e) { assertTrue(true); } } /** * Some checks for the getLabelOffset() and setLabelOffset() methods. */ @Test public void testGetSetLabelOffset() { // we use ValueMarker for the tests, because we need a concrete // subclass... ValueMarker m = new ValueMarker(1.1); m.addChangeListener(this); this.lastEvent = null; assertEquals(new RectangleInsets(3, 3, 3, 3), m.getLabelOffset()); m.setLabelOffset(new RectangleInsets(1, 2, 3, 4)); assertEquals(new RectangleInsets(1, 2, 3, 4), m.getLabelOffset()); assertEquals(m, this.lastEvent.getMarker()); // check null argument... try { m.setLabelOffset(null); fail("Expected an IllegalArgumentException for null."); } catch (IllegalArgumentException e) { assertTrue(true); } } /** * Some checks for the getLabelOffsetType() and setLabelOffsetType() * methods. */ @Test public void testGetSetLabelOffsetType() { // we use ValueMarker for the tests, because we need a concrete // subclass... ValueMarker m = new ValueMarker(1.1); m.addChangeListener(this); this.lastEvent = null; assertEquals(LengthAdjustmentType.CONTRACT, m.getLabelOffsetType()); m.setLabelOffsetType(LengthAdjustmentType.EXPAND); assertEquals(LengthAdjustmentType.EXPAND, m.getLabelOffsetType()); assertEquals(m, this.lastEvent.getMarker()); // check null argument... try { m.setLabelOffsetType(null); fail("Expected an IllegalArgumentException for null."); } catch (IllegalArgumentException e) { assertTrue(true); } } /** * Some checks for the getLabelTextAnchor() and setLabelTextAnchor() * methods. */ @Test public void testGetSetLabelTextAnchor() { // we use ValueMarker for the tests, because we need a concrete // subclass... ValueMarker m = new ValueMarker(1.1); m.addChangeListener(this); this.lastEvent = null; assertEquals(TextAnchor.CENTER, m.getLabelTextAnchor()); m.setLabelTextAnchor(TextAnchor.BASELINE_LEFT); assertEquals(TextAnchor.BASELINE_LEFT, m.getLabelTextAnchor()); assertEquals(m, this.lastEvent.getMarker()); // check null argument... try { m.setLabelTextAnchor(null); fail("Expected an IllegalArgumentException for null."); } catch (IllegalArgumentException e) { assertTrue(true); } } /** * Checks that a CategoryPlot deregisters listeners when clearing markers. */ @Test public void testListenersWithCategoryPlot() { CategoryPlot plot = new CategoryPlot(); CategoryMarker marker1 = new CategoryMarker("X"); ValueMarker marker2 = new ValueMarker(1.0); plot.addDomainMarker(marker1); plot.addRangeMarker(marker2); EventListener[] listeners1 = marker1.getListeners( MarkerChangeListener.class); assertTrue(Arrays.asList(listeners1).contains(plot)); EventListener[] listeners2 = marker1.getListeners( MarkerChangeListener.class); assertTrue(Arrays.asList(listeners2).contains(plot)); plot.clearDomainMarkers(); plot.clearRangeMarkers(); listeners1 = marker1.getListeners(MarkerChangeListener.class); assertFalse(Arrays.asList(listeners1).contains(plot)); listeners2 = marker1.getListeners(MarkerChangeListener.class); assertFalse(Arrays.asList(listeners2).contains(plot)); } /** * Checks that an XYPlot deregisters listeners when clearing markers. */ @Test public void testListenersWithXYPlot() { XYPlot plot = new XYPlot(); ValueMarker marker1 = new ValueMarker(1.0); ValueMarker marker2 = new ValueMarker(2.0); plot.addDomainMarker(marker1); plot.addRangeMarker(marker2); EventListener[] listeners1 = marker1.getListeners( MarkerChangeListener.class); assertTrue(Arrays.asList(listeners1).contains(plot)); EventListener[] listeners2 = marker1.getListeners( MarkerChangeListener.class); assertTrue(Arrays.asList(listeners2).contains(plot)); plot.clearDomainMarkers(); plot.clearRangeMarkers(); listeners1 = marker1.getListeners(MarkerChangeListener.class); assertFalse(Arrays.asList(listeners1).contains(plot)); listeners2 = marker1.getListeners(MarkerChangeListener.class); assertFalse(Arrays.asList(listeners2).contains(plot)); } /** * Records the last event. * * @param event the event. */ @Override public void markerChanged(MarkerChangeEvent event) { this.lastEvent = event; } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/MeterIntervalTest.java000066400000000000000000000061321463604235500303550ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * MeterIntervalTest.java * ---------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import java.awt.BasicStroke; import java.awt.Color; import org.jfree.chart.TestUtils; import org.jfree.data.Range; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link MeterInterval} class. */ public class MeterIntervalTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { MeterInterval m1 = new MeterInterval( "Label 1", new Range(1.2, 3.4), Color.RED, new BasicStroke(1.0f), Color.BLUE ); MeterInterval m2 = new MeterInterval( "Label 1", new Range(1.2, 3.4), Color.RED, new BasicStroke(1.0f), Color.BLUE ); assertEquals(m1, m2); assertEquals(m2, m1); m1 = new MeterInterval( "Label 2", new Range(1.2, 3.4), Color.RED, new BasicStroke(1.0f), Color.BLUE ); assertNotEquals(m1, m2); m2 = new MeterInterval( "Label 2", new Range(1.2, 3.4), Color.RED, new BasicStroke(1.0f), Color.BLUE ); assertEquals(m1, m2); } /** * This class is immutable so cloning isn't required. */ @Test public void testCloning() { MeterInterval m1 = new MeterInterval("X", new Range(1.0, 2.0)); assertFalse(m1 instanceof Cloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { MeterInterval m1 = new MeterInterval("X", new Range(1.0, 2.0)); MeterInterval m2 = TestUtils.serialised(m1); assertEquals(m1, m2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/MeterPlotTest.java000066400000000000000000000207331463604235500275120ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * MeterPlotTest.java * ------------------ * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; */ package org.jfree.chart.plot; import java.awt.Color; import java.awt.Font; import java.awt.GradientPaint; import java.text.DecimalFormat; import org.jfree.chart.TestUtils; import org.jfree.data.Range; import org.jfree.data.general.DefaultValueDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link MeterPlot} class. */ public class MeterPlotTest { /** * Test the equals method to ensure that it can distinguish the required * fields. Note that the dataset is NOT considered in the equals test. */ @Test public void testEquals() { MeterPlot plot1 = new MeterPlot(); MeterPlot plot2 = new MeterPlot(); assertEquals(plot1, plot2); // units plot1.setUnits("mph"); assertNotEquals(plot1, plot2); plot2.setUnits("mph"); assertEquals(plot1, plot2); // range plot1.setRange(new Range(50.0, 70.0)); assertNotEquals(plot1, plot2); plot2.setRange(new Range(50.0, 70.0)); assertEquals(plot1, plot2); // interval plot1.addInterval(new MeterInterval("Normal", new Range(55.0, 60.0))); assertNotEquals(plot1, plot2); plot2.addInterval(new MeterInterval("Normal", new Range(55.0, 60.0))); assertEquals(plot1, plot2); // dial outline paint plot1.setDialOutlinePaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); assertNotEquals(plot1, plot2); plot2.setDialOutlinePaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); assertEquals(plot1, plot2); // dial shape plot1.setDialShape(DialShape.CHORD); assertNotEquals(plot1, plot2); plot2.setDialShape(DialShape.CHORD); assertEquals(plot1, plot2); // dial background paint plot1.setDialBackgroundPaint(new GradientPaint(9.0f, 8.0f, Color.RED, 7.0f, 6.0f, Color.BLUE)); assertNotEquals(plot1, plot2); plot2.setDialBackgroundPaint(new GradientPaint(9.0f, 8.0f, Color.RED, 7.0f, 6.0f, Color.BLUE)); assertEquals(plot1, plot2); // dial outline paint plot1.setDialOutlinePaint(new GradientPaint(1.0f, 2.0f, Color.GREEN, 3.0f, 4.0f, Color.RED)); assertNotEquals(plot1, plot2); plot2.setDialOutlinePaint(new GradientPaint(1.0f, 2.0f, Color.GREEN, 3.0f, 4.0f, Color.RED)); assertEquals(plot1, plot2); // needle paint plot1.setNeedlePaint(new GradientPaint(9.0f, 8.0f, Color.RED, 7.0f, 6.0f, Color.BLUE)); assertNotEquals(plot1, plot2); plot2.setNeedlePaint(new GradientPaint(9.0f, 8.0f, Color.RED, 7.0f, 6.0f, Color.BLUE)); assertEquals(plot1, plot2); // value visible plot1.setValueVisible(false); assertNotEquals(plot1, plot2); plot2.setValueVisible(false); assertEquals(plot1, plot2); // value font plot1.setValueFont(new Font("Serif", Font.PLAIN, 6)); assertNotEquals(plot1, plot2); plot2.setValueFont(new Font("Serif", Font.PLAIN, 6)); assertEquals(plot1, plot2); // value paint plot1.setValuePaint(new GradientPaint(1.0f, 2.0f, Color.BLACK, 3.0f, 4.0f, Color.WHITE)); assertNotEquals(plot1, plot2); plot2.setValuePaint(new GradientPaint(1.0f, 2.0f, Color.BLACK, 3.0f, 4.0f, Color.WHITE)); assertEquals(plot1, plot2); // tick labels visible plot1.setTickLabelsVisible(false); assertNotEquals(plot1, plot2); plot2.setTickLabelsVisible(false); assertEquals(plot1, plot2); // tick label font plot1.setTickLabelFont(new Font("Serif", Font.PLAIN, 6)); assertNotEquals(plot1, plot2); plot2.setTickLabelFont(new Font("Serif", Font.PLAIN, 6)); assertEquals(plot1, plot2); // tick label paint plot1.setTickLabelPaint(Color.RED); assertNotEquals(plot1, plot2); plot2.setTickLabelPaint(Color.RED); assertEquals(plot1, plot2); // tick label format plot1.setTickLabelFormat(new DecimalFormat("0")); assertNotEquals(plot1, plot2); plot2.setTickLabelFormat(new DecimalFormat("0")); assertEquals(plot1, plot2); // tick paint plot1.setTickPaint(Color.GREEN); assertNotEquals(plot1, plot2); plot2.setTickPaint(Color.GREEN); assertEquals(plot1, plot2); // tick size plot1.setTickSize(1.23); assertNotEquals(plot1, plot2); plot2.setTickSize(1.23); assertEquals(plot1, plot2); // draw border plot1.setDrawBorder(!plot1.getDrawBorder()); assertNotEquals(plot1, plot2); plot2.setDrawBorder(plot1.getDrawBorder()); assertEquals(plot1, plot2); // meter angle plot1.setMeterAngle(22); assertNotEquals(plot1, plot2); plot2.setMeterAngle(22); assertEquals(plot1, plot2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { MeterPlot p1 = new MeterPlot(); MeterPlot p2 = (MeterPlot) p1.clone(); assertNotSame(p1, p2); assertSame(p1.getClass(), p2.getClass()); assertEquals(p1, p2); // the clone and the original share a reference to the SAME dataset assertSame(p1.getDataset(), p2.getDataset()); // try a few checks to ensure that the clone is independent of the // original p1.getTickLabelFormat().setMinimumIntegerDigits(99); assertNotEquals(p1, p2); p2.getTickLabelFormat().setMinimumIntegerDigits(99); assertEquals(p1, p2); p1.addInterval(new MeterInterval("Test", new Range(1.234, 5.678))); assertNotEquals(p1, p2); p2.addInterval(new MeterInterval("Test", new Range(1.234, 5.678))); assertEquals(p1, p2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization1() { MeterPlot p1 = new MeterPlot(null); p1.setDialBackgroundPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); p1.setDialOutlinePaint(new GradientPaint(4.0f, 3.0f, Color.RED, 2.0f, 1.0f, Color.BLUE)); p1.setNeedlePaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); p1.setTickLabelPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); p1.setTickPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); MeterPlot p2 = TestUtils.serialised(p1); assertEquals(p1, p2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization2() { MeterPlot p1 = new MeterPlot(new DefaultValueDataset(1.23)); MeterPlot p2 = TestUtils.serialised(p1); assertEquals(p1, p2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/MultiplePiePlotTest.java000066400000000000000000000151521463604235500306660ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * MultiplePiePlotTest.java * ------------------------ * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import java.awt.Color; import java.awt.GradientPaint; import java.awt.geom.Rectangle2D; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.LegendItem; import org.jfree.chart.LegendItemCollection; import org.jfree.chart.TestUtils; import org.jfree.chart.event.PlotChangeEvent; import org.jfree.chart.event.PlotChangeListener; import org.jfree.chart.util.TableOrder; import org.jfree.data.category.DefaultCategoryDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Some tests for the {@link MultiplePiePlot} class. */ public class MultiplePiePlotTest implements PlotChangeListener { /** The last event received. */ PlotChangeEvent lastEvent; /** * Receives a plot change event and records it. Some tests will use this * to check that events have been generated (or not) when required. * * @param event the event. */ @Override public void plotChanged(PlotChangeEvent event) { this.lastEvent = event; } /** * Some checks for the constructors. */ @Test public void testConstructor() { MultiplePiePlot plot = new MultiplePiePlot(); assertNull(plot.getDataset()); // the following checks that the plot registers itself as a listener // with the dataset passed to the constructor - see patch 1943021 DefaultCategoryDataset dataset = new DefaultCategoryDataset(); plot = new MultiplePiePlot(dataset); assertTrue(dataset.hasListener(plot)); } /** * Check that the equals() method distinguishes the required fields. */ @Test public void testEquals() { MultiplePiePlot p1 = new MultiplePiePlot(); MultiplePiePlot p2 = new MultiplePiePlot(); assertEquals(p1, p2); assertEquals(p2, p1); p1.setDataExtractOrder(TableOrder.BY_ROW); assertNotEquals(p1, p2); p2.setDataExtractOrder(TableOrder.BY_ROW); assertEquals(p1, p2); p1.setLimit(1.23); assertNotEquals(p1, p2); p2.setLimit(1.23); assertEquals(p1, p2); p1.setAggregatedItemsKey("Aggregated Items"); assertNotEquals(p1, p2); p2.setAggregatedItemsKey("Aggregated Items"); assertEquals(p1, p2); p1.setAggregatedItemsPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW)); assertNotEquals(p1, p2); p2.setAggregatedItemsPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW)); assertEquals(p1, p2); p1.setPieChart(ChartFactory.createPieChart("Title", null, true, true, true)); assertNotEquals(p1, p2); p2.setPieChart(ChartFactory.createPieChart("Title", null, true, true, true)); assertEquals(p1, p2); p1.setLegendItemShape(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0)); assertNotEquals(p1, p2); p2.setLegendItemShape(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0)); assertEquals(p1, p2); } /** * Some basic checks for the clone() method. */ @Test public void testCloning() throws CloneNotSupportedException { MultiplePiePlot p1 = new MultiplePiePlot(); Rectangle2D rect = new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0); p1.setLegendItemShape(rect); MultiplePiePlot p2 = (MultiplePiePlot) p1.clone(); assertNotSame(p1, p2); assertSame(p1.getClass(), p2.getClass()); assertEquals(p1, p2); // check independence rect.setRect(2.0, 3.0, 4.0, 5.0); assertNotEquals(p1, p2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { MultiplePiePlot p1 = new MultiplePiePlot(null); p1.setAggregatedItemsPaint(new GradientPaint(1.0f, 2.0f, Color.YELLOW, 3.0f, 4.0f, Color.RED)); MultiplePiePlot p2 = TestUtils.serialised(p1); assertEquals(p1, p2); } /** * Fetches the legend items and checks the values. */ @Test public void testGetLegendItems() { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.addValue(35.0, "S1", "C1"); dataset.addValue(45.0, "S1", "C2"); dataset.addValue(55.0, "S2", "C1"); dataset.addValue(15.0, "S2", "C2"); MultiplePiePlot plot = new MultiplePiePlot(dataset); JFreeChart chart = new JFreeChart(plot); LegendItemCollection legendItems = plot.getLegendItems(); assertEquals(2, legendItems.getItemCount()); LegendItem item1 = legendItems.get(0); assertEquals("S1", item1.getLabel()); assertEquals("S1", item1.getSeriesKey()); assertEquals(0, item1.getSeriesIndex()); assertEquals(dataset, item1.getDataset()); assertEquals(0, item1.getDatasetIndex()); LegendItem item2 = legendItems.get(1); assertEquals("S2", item2.getLabel()); assertEquals("S2", item2.getSeriesKey()); assertEquals(1, item2.getSeriesIndex()); assertEquals(dataset, item2.getDataset()); assertEquals(0, item2.getDatasetIndex()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/MyPlotChangeListener.java000066400000000000000000000045231463604235500307760ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * MyPlotChangeListener.java * ------------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import org.jfree.chart.event.PlotChangeEvent; import org.jfree.chart.event.PlotChangeListener; /** * A utility class for testing plot events. */ public class MyPlotChangeListener implements PlotChangeListener { private PlotChangeEvent event; /** * Creates a new instance. */ public MyPlotChangeListener() { this.event = null; } /** * Returns the last event received by the listener. * * @return The event. */ public PlotChangeEvent getEvent() { return this.event; } /** * Sets the event for the listener. * * @param e the event. */ public void setEvent(PlotChangeEvent e) { this.event = e; } /** * Receives notification of a plot change event. * * @param e the event. */ @Override public void plotChanged(PlotChangeEvent e) { this.event = e; } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/PieLabelRecordTest.java000066400000000000000000000104121463604235500304040ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * PieLabelRecordTest.java * ----------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import org.jfree.chart.TestUtils; import org.jfree.chart.text.TextBox; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Some tests for the {@link PieLabelRecord} class. */ public class PieLabelRecordTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { PieLabelRecord p1 = new PieLabelRecord("A", 1.0, 2.0, new TextBox("B"), 3.0, 4.0, 5.0); PieLabelRecord p2 = new PieLabelRecord("A", 1.0, 2.0, new TextBox("B"), 3.0, 4.0, 5.0); assertEquals(p1, p2); assertEquals(p2, p1); p1 = new PieLabelRecord("B", 1.0, 2.0, new TextBox("B"), 3.0, 4.0, 5.0); assertNotEquals(p1, p2); p2 = new PieLabelRecord("B", 1.0, 2.0, new TextBox("B"), 3.0, 4.0, 5.0); assertEquals(p1, p2); p1 = new PieLabelRecord("B", 1.1, 2.0, new TextBox("B"), 3.0, 4.0, 5.0); assertNotEquals(p1, p2); p2 = new PieLabelRecord("B", 1.1, 2.0, new TextBox("B"), 3.0, 4.0, 5.0); assertEquals(p1, p2); p1 = new PieLabelRecord("B", 1.1, 2.2, new TextBox("B"), 3.0, 4.0, 5.0); assertNotEquals(p1, p2); p2 = new PieLabelRecord("B", 1.1, 2.2, new TextBox("B"), 3.0, 4.0, 5.0); assertEquals(p1, p2); p1 = new PieLabelRecord("B", 1.1, 2.2, new TextBox("C"), 3.0, 4.0, 5.0); assertNotEquals(p1, p2); p2 = new PieLabelRecord("B", 1.1, 2.2, new TextBox("C"), 3.0, 4.0, 5.0); assertEquals(p1, p2); p1 = new PieLabelRecord("B", 1.1, 2.2, new TextBox("C"), 3.3, 4.0, 5.0); assertNotEquals(p1, p2); p2 = new PieLabelRecord("B", 1.1, 2.2, new TextBox("C"), 3.3, 4.0, 5.0); assertEquals(p1, p2); p1 = new PieLabelRecord("B", 1.1, 2.2, new TextBox("C"), 3.3, 4.4, 5.0); assertNotEquals(p1, p2); p2 = new PieLabelRecord("B", 1.1, 2.2, new TextBox("C"), 3.3, 4.4, 5.0); assertEquals(p1, p2); p1 = new PieLabelRecord("B", 1.1, 2.2, new TextBox("C"), 3.3, 4.4, 5.5); assertNotEquals(p1, p2); p2 = new PieLabelRecord("B", 1.1, 2.2, new TextBox("C"), 3.3, 4.4, 5.5); assertEquals(p1, p2); } /** * Confirm that cloning is not implemented. */ @Test public void testCloning() { PieLabelRecord p1 = new PieLabelRecord("A", 1.0, 2.0, new TextBox("B"), 3.0, 4.0, 5.0); assertFalse(p1 instanceof Cloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { PieLabelRecord p1 = new PieLabelRecord("A", 1.0, 2.0, new TextBox("B"), 3.0, 4.0, 5.0); PieLabelRecord p2 = TestUtils.serialised(p1); boolean b = p1.equals(p2); assertTrue(b); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/PiePlot3DTest.java000066400000000000000000000063551463604235500273460ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * Pie3DPlotTest.java * ------------------ * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link PiePlot3D} class. */ public class PiePlot3DTest { /** * Some checks for the equals() method. */ @Test public void testEquals() { PiePlot3D p1 = new PiePlot3D(); PiePlot3D p2 = new PiePlot3D(); assertEquals(p1, p2); assertEquals(p2, p1); p1.setDepthFactor(1.23); assertNotEquals(p1, p2); p2.setDepthFactor(1.23); assertEquals(p1, p2); p1.setDarkerSides(true); assertNotEquals(p1, p2); p2.setDarkerSides(true); assertEquals(p1, p2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { PiePlot3D p1 = new PiePlot3D(null); PiePlot3D p2 = (PiePlot3D) TestUtils.serialised(p1); assertEquals(p1, p2); } /** * Draws a pie chart where the label generator returns null. */ @Test public void testDrawWithNullDataset() { JFreeChart chart = ChartFactory.createPieChart3D("Test", null, true, false, false); boolean success = false; try { BufferedImage image = new BufferedImage(200 , 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); chart.draw(g2, new Rectangle2D.Double(0, 0, 200, 100), null, null); g2.dispose(); success = true; } catch (Exception e) { success = false; } assertTrue(success); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/PiePlotTest.java000066400000000000000000000603041463604235500271510ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * PiePlotTest.java * ---------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.GradientPaint; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.Stroke; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.text.AttributedString; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.LegendItemCollection; import org.jfree.chart.TestUtils; import org.jfree.chart.labels.PieSectionLabelGenerator; import org.jfree.chart.labels.StandardPieSectionLabelGenerator; import org.jfree.chart.labels.StandardPieToolTipGenerator; import org.jfree.chart.urls.CustomPieURLGenerator; import org.jfree.chart.urls.StandardPieURLGenerator; import org.jfree.chart.util.DefaultShadowGenerator; import org.jfree.chart.util.Rotation; import org.jfree.data.general.DefaultPieDataset; import org.jfree.data.general.PieDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Some tests for the {@link PiePlot} class. */ public class PiePlotTest { /** * Test the equals() method. */ @Test public void testEquals() { PiePlot plot1 = new PiePlot(); PiePlot plot2 = new PiePlot(); assertEquals(plot1, plot2); assertEquals(plot2, plot1); // pieIndex... plot1.setPieIndex(99); assertNotEquals(plot1, plot2); plot2.setPieIndex(99); assertEquals(plot1, plot2); // interiorGap... plot1.setInteriorGap(0.15); assertNotEquals(plot1, plot2); plot2.setInteriorGap(0.15); assertEquals(plot1, plot2); // circular plot1.setCircular(!plot1.isCircular()); assertNotEquals(plot1, plot2); plot2.setCircular(false); assertEquals(plot1, plot2); // startAngle plot1.setStartAngle(Math.PI); assertNotEquals(plot1, plot2); plot2.setStartAngle(Math.PI); assertEquals(plot1, plot2); // direction plot1.setDirection(Rotation.ANTICLOCKWISE); assertNotEquals(plot1, plot2); plot2.setDirection(Rotation.ANTICLOCKWISE); assertEquals(plot1, plot2); // ignoreZeroValues plot1.setIgnoreZeroValues(true); plot2.setIgnoreZeroValues(false); assertNotEquals(plot1, plot2); plot2.setIgnoreZeroValues(true); assertEquals(plot1, plot2); // ignoreNullValues plot1.setIgnoreNullValues(true); plot2.setIgnoreNullValues(false); assertNotEquals(plot1, plot2); plot2.setIgnoreNullValues(true); assertEquals(plot1, plot2); // sectionPaintMap plot1.setSectionPaint("A", new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.WHITE)); assertNotEquals(plot1, plot2); plot2.setSectionPaint("A", new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.WHITE)); assertEquals(plot1, plot2); // defaultSectionPaint plot1.setDefaultSectionPaint(new GradientPaint(1.0f, 2.0f, Color.BLACK, 3.0f, 4.0f, Color.WHITE)); assertNotEquals(plot1, plot2); plot2.setDefaultSectionPaint(new GradientPaint(1.0f, 2.0f, Color.BLACK, 3.0f, 4.0f, Color.WHITE)); assertEquals(plot1, plot2); // sectionOutlinesVisible plot1.setSectionOutlinesVisible(false); assertNotEquals(plot1, plot2); plot2.setSectionOutlinesVisible(false); assertEquals(plot1, plot2); // sectionOutlinePaintList plot1.setSectionOutlinePaint("A", new GradientPaint(1.0f, 2.0f, Color.GREEN, 3.0f, 4.0f, Color.WHITE)); assertNotEquals(plot1, plot2); plot2.setSectionOutlinePaint("A", new GradientPaint(1.0f, 2.0f, Color.GREEN, 3.0f, 4.0f, Color.WHITE)); assertEquals(plot1, plot2); // defaultSectionOutlinePaint plot1.setDefaultSectionOutlinePaint(new GradientPaint(1.0f, 2.0f, Color.GRAY, 3.0f, 4.0f, Color.WHITE)); assertNotEquals(plot1, plot2); plot2.setDefaultSectionOutlinePaint(new GradientPaint(1.0f, 2.0f, Color.GRAY, 3.0f, 4.0f, Color.WHITE)); assertEquals(plot1, plot2); // sectionOutlineStrokeList plot1.setSectionOutlineStroke("A", new BasicStroke(1.0f)); assertNotEquals(plot1, plot2); plot2.setSectionOutlineStroke("A", new BasicStroke(1.0f)); assertEquals(plot1, plot2); // defaultSectionOutlineStroke plot1.setDefaultSectionOutlineStroke(new BasicStroke(1.0f)); assertNotEquals(plot1, plot2); plot2.setDefaultSectionOutlineStroke(new BasicStroke(1.0f)); assertEquals(plot1, plot2); // shadowPaint plot1.setShadowPaint(new GradientPaint(1.0f, 2.0f, Color.ORANGE, 3.0f, 4.0f, Color.WHITE)); assertNotEquals(plot1, plot2); plot2.setShadowPaint(new GradientPaint(1.0f, 2.0f, Color.ORANGE, 3.0f, 4.0f, Color.WHITE)); assertEquals(plot1, plot2); // shadowXOffset plot1.setShadowXOffset(4.4); assertNotEquals(plot1, plot2); plot2.setShadowXOffset(4.4); assertEquals(plot1, plot2); // shadowYOffset plot1.setShadowYOffset(4.4); assertNotEquals(plot1, plot2); plot2.setShadowYOffset(4.4); assertEquals(plot1, plot2); // labelFont plot1.setLabelFont(new Font("Serif", Font.PLAIN, 18)); assertNotEquals(plot1, plot2); plot2.setLabelFont(new Font("Serif", Font.PLAIN, 18)); assertEquals(plot1, plot2); // labelPaint plot1.setLabelPaint(new GradientPaint(1.0f, 2.0f, Color.DARK_GRAY, 3.0f, 4.0f, Color.WHITE)); assertNotEquals(plot1, plot2); plot2.setLabelPaint(new GradientPaint(1.0f, 2.0f, Color.DARK_GRAY, 3.0f, 4.0f, Color.WHITE)); assertEquals(plot1, plot2); // labelBackgroundPaint plot1.setLabelBackgroundPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.WHITE)); assertNotEquals(plot1, plot2); plot2.setLabelBackgroundPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.WHITE)); assertEquals(plot1, plot2); // labelOutlinePaint plot1.setLabelOutlinePaint(new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.WHITE)); assertNotEquals(plot1, plot2); plot2.setLabelOutlinePaint(new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.WHITE)); assertEquals(plot1, plot2); // labelOutlineStroke Stroke s = new BasicStroke(1.1f); plot1.setLabelOutlineStroke(s); assertNotEquals(plot1, plot2); plot2.setLabelOutlineStroke(s); assertEquals(plot1, plot2); // labelShadowPaint plot1.setLabelShadowPaint(new GradientPaint(1.0f, 2.0f, Color.YELLOW, 3.0f, 4.0f, Color.WHITE)); assertNotEquals(plot1, plot2); plot2.setLabelShadowPaint(new GradientPaint(1.0f, 2.0f, Color.YELLOW, 3.0f, 4.0f, Color.WHITE)); assertEquals(plot1, plot2); // explodePercentages plot1.setExplodePercent("A", 0.33); assertNotEquals(plot1, plot2); plot2.setExplodePercent("A", 0.33); assertEquals(plot1, plot2); // labelGenerator plot1.setLabelGenerator(new StandardPieSectionLabelGenerator( "{2}{1}{0}")); assertNotEquals(plot1, plot2); plot2.setLabelGenerator(new StandardPieSectionLabelGenerator( "{2}{1}{0}")); assertEquals(plot1, plot2); // labelFont Font f = new Font("SansSerif", Font.PLAIN, 20); plot1.setLabelFont(f); assertNotEquals(plot1, plot2); plot2.setLabelFont(f); assertEquals(plot1, plot2); // labelPaint plot1.setLabelPaint(new GradientPaint(1.0f, 2.0f, Color.MAGENTA, 3.0f, 4.0f, Color.WHITE)); assertNotEquals(plot1, plot2); plot2.setLabelPaint(new GradientPaint(1.0f, 2.0f, Color.MAGENTA, 3.0f, 4.0f, Color.WHITE)); assertEquals(plot1, plot2); // maximumLabelWidth plot1.setMaximumLabelWidth(0.33); assertNotEquals(plot1, plot2); plot2.setMaximumLabelWidth(0.33); assertEquals(plot1, plot2); // labelGap plot1.setLabelGap(0.11); assertNotEquals(plot1, plot2); plot2.setLabelGap(0.11); assertEquals(plot1, plot2); // links visible plot1.setLabelLinksVisible(false); assertNotEquals(plot1, plot2); plot2.setLabelLinksVisible(false); assertEquals(plot1, plot2); plot1.setLabelLinkStyle(PieLabelLinkStyle.QUAD_CURVE); assertNotEquals(plot1, plot2); plot2.setLabelLinkStyle(PieLabelLinkStyle.QUAD_CURVE); assertEquals(plot1, plot2); // linkMargin plot1.setLabelLinkMargin(0.11); assertNotEquals(plot1, plot2); plot2.setLabelLinkMargin(0.11); assertEquals(plot1, plot2); // labelLinkPaint plot1.setLabelLinkPaint(new GradientPaint(1.0f, 2.0f, Color.MAGENTA, 3.0f, 4.0f, Color.WHITE)); assertNotEquals(plot1, plot2); plot2.setLabelLinkPaint(new GradientPaint(1.0f, 2.0f, Color.MAGENTA, 3.0f, 4.0f, Color.WHITE)); assertEquals(plot1, plot2); // labelLinkStroke plot1.setLabelLinkStroke(new BasicStroke(1.0f)); assertNotEquals(plot1, plot2); plot2.setLabelLinkStroke(new BasicStroke(1.0f)); assertEquals(plot1, plot2); // toolTipGenerator plot1.setToolTipGenerator( new StandardPieToolTipGenerator("{2}{1}{0}") ); assertNotEquals(plot1, plot2); plot2.setToolTipGenerator( new StandardPieToolTipGenerator("{2}{1}{0}") ); assertEquals(plot1, plot2); // urlGenerator plot1.setURLGenerator(new StandardPieURLGenerator("xx")); assertNotEquals(plot1, plot2); plot2.setURLGenerator(new StandardPieURLGenerator("xx")); assertEquals(plot1, plot2); // minimumArcAngleToDraw plot1.setMinimumArcAngleToDraw(1.0); assertNotEquals(plot1, plot2); plot2.setMinimumArcAngleToDraw(1.0); assertEquals(plot1, plot2); // legendItemShape plot1.setLegendItemShape(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0)); assertNotEquals(plot1, plot2); plot2.setLegendItemShape(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0)); assertEquals(plot1, plot2); // legendLabelGenerator plot1.setLegendLabelGenerator(new StandardPieSectionLabelGenerator( "{0} --> {1}")); assertNotEquals(plot1, plot2); plot2.setLegendLabelGenerator(new StandardPieSectionLabelGenerator( "{0} --> {1}")); assertEquals(plot1, plot2); // legendLabelToolTipGenerator plot1.setLegendLabelToolTipGenerator( new StandardPieSectionLabelGenerator("{0} is {1}")); assertNotEquals(plot1, plot2); plot2.setLegendLabelToolTipGenerator( new StandardPieSectionLabelGenerator("{0} is {1}")); assertEquals(plot1, plot2); // legendLabelURLGenerator plot1.setLegendLabelURLGenerator(new StandardPieURLGenerator( "index.html")); assertNotEquals(plot1, plot2); plot2.setLegendLabelURLGenerator(new StandardPieURLGenerator( "index.html")); assertEquals(plot1, plot2); // autoPopulateSectionPaint plot1.setAutoPopulateSectionPaint(false); assertNotEquals(plot1, plot2); plot2.setAutoPopulateSectionPaint(false); assertEquals(plot1, plot2); // autoPopulateSectionOutlinePaint plot1.setAutoPopulateSectionOutlinePaint(true); assertNotEquals(plot1, plot2); plot2.setAutoPopulateSectionOutlinePaint(true); assertEquals(plot1, plot2); // autoPopulateSectionOutlineStroke plot1.setAutoPopulateSectionOutlineStroke(true); assertNotEquals(plot1, plot2); plot2.setAutoPopulateSectionOutlineStroke(true); assertEquals(plot1, plot2); // shadowGenerator plot1.setShadowGenerator(new DefaultShadowGenerator(5, Color.GRAY, 0.6f, 4, -Math.PI / 4)); assertNotEquals(plot1, plot2); plot2.setShadowGenerator(new DefaultShadowGenerator(5, Color.GRAY, 0.6f, 4, -Math.PI / 4)); assertEquals(plot1, plot2); plot1.setShadowGenerator(null); assertNotEquals(plot1, plot2); plot2.setShadowGenerator(null); assertEquals(plot1, plot2); } /** * Some basic checks for the clone() method. */ @Test public void testCloning() throws CloneNotSupportedException { PiePlot p1 = new PiePlot(); PiePlot p2 = (PiePlot) p1.clone(); assertNotSame(p1, p2); assertSame(p1.getClass(), p2.getClass()); assertEquals(p1, p2); } /** * Check cloning of the urlGenerator field. */ @Test public void testCloning_URLGenerator() throws CloneNotSupportedException { CustomPieURLGenerator generator = new CustomPieURLGenerator(); PiePlot p1 = new PiePlot(); p1.setURLGenerator(generator); PiePlot p2 = (PiePlot) p1.clone(); assertNotSame(p1, p2); assertSame(p1.getClass(), p2.getClass()); assertEquals(p1, p2); // check that the URL generator has been cloned assertNotSame(p1.getURLGenerator(), p2.getURLGenerator()); } /** * Check cloning of the legendItemShape field. */ @Test public void testCloning_LegendItemShape() throws CloneNotSupportedException { Rectangle shape = new Rectangle(-4, -4, 8, 8); PiePlot p1 = new PiePlot(); p1.setLegendItemShape(shape); PiePlot p2 = (PiePlot) p1.clone(); assertNotSame(p1, p2); assertSame(p1.getClass(), p2.getClass()); assertEquals(p1, p2); // change the shape and make sure it only affects p1 shape.setRect(1.0, 2.0, 3.0, 4.0); assertNotEquals(p1, p2); } /** * Check cloning of the legendLabelGenerator field. */ @Test public void testCloning_LegendLabelGenerator() throws CloneNotSupportedException { StandardPieSectionLabelGenerator generator = new StandardPieSectionLabelGenerator(); PiePlot p1 = new PiePlot(); p1.setLegendLabelGenerator(generator); PiePlot p2 = (PiePlot) p1.clone(); assertNotSame(p1, p2); assertSame(p1.getClass(), p2.getClass()); assertEquals(p1, p2); // change the generator and make sure it only affects p1 generator.getNumberFormat().setMinimumFractionDigits(2); assertNotEquals(p1, p2); } /** * Check cloning of the legendLabelToolTipGenerator field. */ @Test public void testCloning_LegendLabelToolTipGenerator() throws CloneNotSupportedException { StandardPieSectionLabelGenerator generator = new StandardPieSectionLabelGenerator(); PiePlot p1 = new PiePlot(); p1.setLegendLabelToolTipGenerator(generator); PiePlot p2 = (PiePlot) p1.clone(); assertNotSame(p1, p2); assertSame(p1.getClass(), p2.getClass()); assertEquals(p1, p2); // change the generator and make sure it only affects p1 generator.getNumberFormat().setMinimumFractionDigits(2); assertNotEquals(p1, p2); } /** * Check cloning of the legendLabelURLGenerator field. */ @Test public void testCloning_LegendLabelURLGenerator() throws CloneNotSupportedException { CustomPieURLGenerator generator = new CustomPieURLGenerator(); PiePlot p1 = new PiePlot(); p1.setLegendLabelURLGenerator(generator); PiePlot p2 = (PiePlot) p1.clone(); assertNotSame(p1, p2); assertSame(p1.getClass(), p2.getClass()); assertEquals(p1, p2); // check that the URL generator has been cloned assertNotSame(p1.getLegendLabelURLGenerator(), p2.getLegendLabelURLGenerator()); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { PiePlot p1 = new PiePlot(null); PiePlot p2 = TestUtils.serialised(p1); assertEquals(p1, p2); } /** * Some checks for the getLegendItems() method. */ @Test public void testGetLegendItems() { DefaultPieDataset dataset = new DefaultPieDataset(); dataset.setValue("Item 1", 1.0); dataset.setValue("Item 2", 2.0); dataset.setValue("Item 3", 0.0); dataset.setValue("Item 4", null); PiePlot plot = new PiePlot(dataset); plot.setIgnoreNullValues(false); plot.setIgnoreZeroValues(false); LegendItemCollection items = plot.getLegendItems(); assertEquals(4, items.getItemCount()); // check that null items are ignored if requested plot.setIgnoreNullValues(true); items = plot.getLegendItems(); assertEquals(3, items.getItemCount()); // check that zero items are ignored if requested plot.setIgnoreZeroValues(true); items = plot.getLegendItems(); assertEquals(2, items.getItemCount()); // check that negative items are always ignored dataset.setValue("Item 5", -1.0); items = plot.getLegendItems(); assertEquals(2, items.getItemCount()); } /** * Check that the default section paint is not null, and that you * can never set it to null. */ @Test public void testGetDefaultSectionPaint() { PiePlot plot = new PiePlot(); assertNotNull(plot.getDefaultSectionPaint()); boolean pass = false; try { plot.setDefaultSectionPaint(null); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } static class NullLegendLabelGenerator implements PieSectionLabelGenerator { @Override public AttributedString generateAttributedSectionLabel( PieDataset dataset, Comparable key) { return null; } @Override public String generateSectionLabel(PieDataset dataset, Comparable key) { return null; } } /** * Draws a pie chart where the label generator returns null. */ @Test public void testDrawWithNullLegendLabels() { DefaultPieDataset dataset = new DefaultPieDataset(); dataset.setValue("L1", 12.0); dataset.setValue("L2", 11.0); JFreeChart chart = ChartFactory.createPieChart("Test", dataset, true, false, false); PiePlot plot = (PiePlot) chart.getPlot(); plot.setLegendLabelGenerator(new NullLegendLabelGenerator()); boolean success = false; try { BufferedImage image = new BufferedImage(200 , 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); chart.draw(g2, new Rectangle2D.Double(0, 0, 200, 100), null, null); g2.dispose(); success = true; } catch (Exception e) { success = false; } assertTrue(success); } @Test public void testBug1126() throws CloneNotSupportedException { DefaultPieDataset dataset1 = new DefaultPieDataset(); PiePlot plot1 = new PiePlot(dataset1); plot1.setSectionPaint("A", Color.RED); plot1.setSectionPaint("B", Color.GREEN); PiePlot plot2 = (PiePlot) plot1.clone(); plot2.setSectionPaint("A", Color.BLUE); plot2.setSectionPaint("B", Color.YELLOW); assertEquals(Color.RED, plot1.getSectionPaint("A")); assertEquals(Color.GREEN, plot1.getSectionPaint("B")); assertEquals(Color.BLUE, plot2.getSectionPaint("A")); assertEquals(Color.YELLOW, plot2.getSectionPaint("B")); } @Test public void testBug1126_b() throws CloneNotSupportedException { DefaultPieDataset dataset1 = new DefaultPieDataset(); PiePlot plot1 = new PiePlot(dataset1); plot1.setSectionOutlinePaint("A", Color.RED); plot1.setSectionOutlinePaint("B", Color.GREEN); PiePlot plot2 = (PiePlot) plot1.clone(); plot2.setSectionOutlinePaint("A", Color.BLUE); plot2.setSectionOutlinePaint("B", Color.YELLOW); assertEquals(Color.RED, plot1.getSectionOutlinePaint("A")); assertEquals(Color.GREEN, plot1.getSectionOutlinePaint("B")); assertEquals(Color.BLUE, plot2.getSectionOutlinePaint("A")); assertEquals(Color.YELLOW, plot2.getSectionOutlinePaint("B")); } @Test public void testBug1126_c() throws CloneNotSupportedException { DefaultPieDataset dataset1 = new DefaultPieDataset(); PiePlot plot1 = new PiePlot(dataset1); plot1.setSectionOutlineStroke("A", new BasicStroke(5.0f)); plot1.setSectionOutlineStroke("B", new BasicStroke(6.0f)); PiePlot plot2 = (PiePlot) plot1.clone(); plot2.setSectionOutlineStroke("A", new BasicStroke(7.0f)); plot2.setSectionOutlineStroke("B", new BasicStroke(8.0f)); assertEquals(new BasicStroke(5.0f), plot1.getSectionOutlineStroke("A")); assertEquals(new BasicStroke(6.0f), plot1.getSectionOutlineStroke("B")); assertEquals(new BasicStroke(7.0f), plot2.getSectionOutlineStroke("A")); assertEquals(new BasicStroke(8.0f), plot2.getSectionOutlineStroke("B")); } @Test public void testBug1126_d() throws CloneNotSupportedException { DefaultPieDataset dataset1 = new DefaultPieDataset(); PiePlot plot1 = new PiePlot(dataset1); plot1.setExplodePercent("A", 0.1); plot1.setExplodePercent("B", 0.2); PiePlot plot2 = (PiePlot) plot1.clone(); plot2.setExplodePercent("A", 0.3); plot2.setExplodePercent("B", 0.4); assertEquals(0.1, plot1.getExplodePercent("A"), EPSILON); assertEquals(0.2, plot1.getExplodePercent("B"), EPSILON); assertEquals(0.3, plot2.getExplodePercent("A"), EPSILON); assertEquals(0.4, plot2.getExplodePercent("B"), EPSILON); } private static final double EPSILON = 0.000000001; @Test public void testBug1126_e() throws CloneNotSupportedException { DefaultPieDataset dataset1 = new DefaultPieDataset(); PiePlot plot1 = new PiePlot(dataset1); plot1.setLabelGenerator(new StandardPieSectionLabelGenerator()); PiePlot plot2 = (PiePlot) plot1.clone(); StandardPieSectionLabelGenerator g2 = (StandardPieSectionLabelGenerator) plot2.getLabelGenerator(); g2.setAttributedLabel(1, new AttributedString("TESTING")); assertNotEquals(plot1, plot2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/PlotOrientationTest.java000066400000000000000000000047211463604235500307300ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * PlotOrientationTest.java * ------------------------ * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link PlotOrientation} class. * */ public class PlotOrientationTest { /** * Some checks for the equals() method. */ @Test public void testEquals() { assertEquals(PlotOrientation.HORIZONTAL, PlotOrientation.HORIZONTAL); assertEquals(PlotOrientation.VERTICAL, PlotOrientation.VERTICAL); assertNotEquals(PlotOrientation.HORIZONTAL, PlotOrientation.VERTICAL); assertNotEquals(PlotOrientation.VERTICAL, PlotOrientation.HORIZONTAL); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { PlotOrientation orientation1 = PlotOrientation.HORIZONTAL; PlotOrientation orientation2 = TestUtils.serialised(orientation1); assertEquals(orientation1, orientation2); boolean same = orientation1 == orientation2; assertTrue(same); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/PlotRenderingInfoTest.java000066400000000000000000000076501463604235500311720ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * PlotRenderingInfoTest.java * -------------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.plot; import java.awt.Rectangle; import java.awt.geom.Rectangle2D; import org.jfree.chart.ChartRenderingInfo; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link PlotRenderingInfo} class. */ public class PlotRenderingInfoTest { /** * Test the equals() method. */ @Test public void testEquals() { PlotRenderingInfo p1 = new PlotRenderingInfo(new ChartRenderingInfo()); PlotRenderingInfo p2 = new PlotRenderingInfo(new ChartRenderingInfo()); assertEquals(p1, p2); assertEquals(p2, p1); p1.setPlotArea(new Rectangle(2, 3, 4, 5)); assertNotEquals(p1, p2); p2.setPlotArea(new Rectangle(2, 3, 4, 5)); assertEquals(p1, p2); p1.setDataArea(new Rectangle(2, 4, 6, 8)); assertNotEquals(p1, p2); p2.setDataArea(new Rectangle(2, 4, 6, 8)); assertEquals(p1, p2); p1.addSubplotInfo(new PlotRenderingInfo(null)); assertNotEquals(p1, p2); p2.addSubplotInfo(new PlotRenderingInfo(null)); assertEquals(p1, p2); p1.getSubplotInfo(0).setDataArea(new Rectangle(1, 2, 3, 4)); assertNotEquals(p1, p2); p2.getSubplotInfo(0).setDataArea(new Rectangle(1, 2, 3, 4)); assertEquals(p1, p2); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { PlotRenderingInfo p1 = new PlotRenderingInfo(new ChartRenderingInfo()); p1.setPlotArea(new Rectangle2D.Double()); PlotRenderingInfo p2 = (PlotRenderingInfo) p1.clone(); assertNotSame(p1, p2); assertSame(p1.getClass(), p2.getClass()); assertEquals(p1, p2); // check independence p1.getPlotArea().setRect(1.0, 2.0, 3.0, 4.0); assertNotEquals(p1, p2); p2.getPlotArea().setRect(1.0, 2.0, 3.0, 4.0); assertEquals(p1, p2); p1.getDataArea().setRect(4.0, 3.0, 2.0, 1.0); assertNotEquals(p1, p2); p2.getDataArea().setRect(4.0, 3.0, 2.0, 1.0); assertEquals(p1, p2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { PlotRenderingInfo p1 = new PlotRenderingInfo(new ChartRenderingInfo()); PlotRenderingInfo p2 = TestUtils.serialised(p1); assertEquals(p1, p2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/PlotTest.java000066400000000000000000000137031463604235500265140ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------- * PlotTest.java * ------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.plot; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.GradientPaint; import java.awt.Paint; import java.awt.Rectangle; import java.awt.Shape; import java.awt.Stroke; import org.jfree.chart.ui.Align; import org.jfree.chart.ui.RectangleInsets; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Some tests for the {@link Plot} class. */ public class PlotTest { /** * Check that the equals() method can distinguish all fields (note that * the dataset is NOT considered in the equals() method). */ @Test public void testEquals() { PiePlot plot1 = new PiePlot(); PiePlot plot2 = new PiePlot(); assertEquals(plot1, plot2); assertEquals(plot2, plot1); // noDataMessage plot1.setNoDataMessage("No data XYZ"); assertNotEquals(plot1, plot2); plot2.setNoDataMessage("No data XYZ"); assertEquals(plot1, plot2); // noDataMessageFont plot1.setNoDataMessageFont(new Font("SansSerif", Font.PLAIN, 13)); assertNotEquals(plot1, plot2); plot2.setNoDataMessageFont(new Font("SansSerif", Font.PLAIN, 13)); assertEquals(plot1, plot2); // noDataMessagePaint plot1.setNoDataMessagePaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); assertNotEquals(plot1, plot2); plot2.setNoDataMessagePaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); assertEquals(plot1, plot2); // insets plot1.setInsets(new RectangleInsets(1.0, 2.0, 3.0, 4.0)); assertNotEquals(plot1, plot2); plot2.setInsets(new RectangleInsets(1.0, 2.0, 3.0, 4.0)); assertEquals(plot1, plot2); // outlineVisible plot1.setOutlineVisible(false); assertNotEquals(plot1, plot2); plot2.setOutlineVisible(false); assertEquals(plot1, plot2); // outlineStroke BasicStroke s = new BasicStroke(1.23f); plot1.setOutlineStroke(s); assertNotEquals(plot1, plot2); plot2.setOutlineStroke(s); assertEquals(plot1, plot2); // outlinePaint plot1.setOutlinePaint(new GradientPaint(1.0f, 2.0f, Color.YELLOW, 3.0f, 4.0f, Color.GREEN)); assertNotEquals(plot1, plot2); plot2.setOutlinePaint(new GradientPaint(1.0f, 2.0f, Color.YELLOW, 3.0f, 4.0f, Color.GREEN)); assertEquals(plot1, plot2); // backgroundPaint plot1.setBackgroundPaint(new GradientPaint(1.0f, 2.0f, Color.cyan, 3.0f, 4.0f, Color.GREEN)); assertNotEquals(plot1, plot2); plot2.setBackgroundPaint(new GradientPaint(1.0f, 2.0f, Color.cyan, 3.0f, 4.0f, Color.GREEN)); assertEquals(plot1, plot2); // // backgroundImage // plot1.setBackgroundImage(JFreeChart.INFO.getLogo()); // assertFalse(plot1.equals(plot2)); // plot2.setBackgroundImage(JFreeChart.INFO.getLogo()); // assertTrue(plot1.equals(plot2)); // backgroundImageAlignment plot1.setBackgroundImageAlignment(Align.BOTTOM_RIGHT); assertNotEquals(plot1, plot2); plot2.setBackgroundImageAlignment(Align.BOTTOM_RIGHT); assertEquals(plot1, plot2); // backgroundImageAlpha plot1.setBackgroundImageAlpha(0.77f); assertNotEquals(plot1, plot2); plot2.setBackgroundImageAlpha(0.77f); assertEquals(plot1, plot2); // foregroundAlpha plot1.setForegroundAlpha(0.99f); assertNotEquals(plot1, plot2); plot2.setForegroundAlpha(0.99f); assertEquals(plot1, plot2); // backgroundAlpha plot1.setBackgroundAlpha(0.99f); assertNotEquals(plot1, plot2); plot2.setBackgroundAlpha(0.99f); assertEquals(plot1, plot2); // drawingSupplier plot1.setDrawingSupplier(new DefaultDrawingSupplier( new Paint[] {Color.BLUE}, new Paint[] {Color.RED}, new Stroke[] {new BasicStroke(1.1f)}, new Stroke[] {new BasicStroke(9.9f)}, new Shape[] {new Rectangle(1, 2, 3, 4)})); assertNotEquals(plot1, plot2); plot2.setDrawingSupplier(new DefaultDrawingSupplier( new Paint[] {Color.BLUE}, new Paint[] {Color.RED}, new Stroke[] {new BasicStroke(1.1f)}, new Stroke[] {new BasicStroke(9.9f)}, new Shape[] {new Rectangle(1, 2, 3, 4)})); assertEquals(plot1, plot2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/PolarPlotTest.java000066400000000000000000000321511463604235500275100ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * PolarPlotTest.java * ------------------ * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.GradientPaint; import java.awt.Point; import java.awt.Stroke; import java.awt.geom.Rectangle2D; import org.jfree.chart.LegendItem; import org.jfree.chart.LegendItemCollection; import org.jfree.chart.TestUtils; import org.jfree.chart.axis.LogAxis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.axis.NumberTickUnit; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.renderer.DefaultPolarItemRenderer; import org.jfree.data.xy.DefaultXYDataset; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Some tests for the {@link PolarPlot} class. */ public class PolarPlotTest { /** * Some checks for the getLegendItems() method. */ @Test public void testGetLegendItems() { XYSeriesCollection d = new XYSeriesCollection(); d.addSeries(new XYSeries("A")); d.addSeries(new XYSeries("B")); DefaultPolarItemRenderer r = new DefaultPolarItemRenderer(); PolarPlot plot = new PolarPlot(); plot.setDataset(d); plot.setRenderer(r); LegendItemCollection items = plot.getLegendItems(); assertEquals(2, items.getItemCount()); LegendItem item1 = items.get(0); assertEquals("A", item1.getLabel()); LegendItem item2 = items.get(1); assertEquals("B", item2.getLabel()); } /** * Some checks for the getLegendItems() method with multiple datasets. */ @Test public void testGetLegendItems2() { XYSeriesCollection d1 = new XYSeriesCollection(); d1.addSeries(new XYSeries("A")); d1.addSeries(new XYSeries("B")); XYSeriesCollection d2 = new XYSeriesCollection(); d2.addSeries(new XYSeries("C")); d2.addSeries(new XYSeries("D")); DefaultPolarItemRenderer r = new DefaultPolarItemRenderer(); PolarPlot plot = new PolarPlot(); plot.setDataset(d1); plot.setDataset(1, d2); plot.setRenderer(r); plot.setRenderer(1, new DefaultPolarItemRenderer()); LegendItemCollection items = plot.getLegendItems(); assertEquals(4, items.getItemCount()); LegendItem item1 = items.get(0); assertEquals("A", item1.getLabel()); LegendItem item2 = items.get(1); assertEquals("B", item2.getLabel()); LegendItem item3 = items.get(2); assertEquals("C", item3.getLabel()); LegendItem item4 = items.get(3); assertEquals("D", item4.getLabel()); } /** * Some checks for the equals() method. */ @Test public void testEquals() { PolarPlot plot1 = new PolarPlot(); PolarPlot plot2 = new PolarPlot(); assertEquals(plot1, plot2); assertEquals(plot2, plot1); plot1.setAngleGridlinePaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); assertNotEquals(plot1, plot2); plot2.setAngleGridlinePaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); assertEquals(plot1, plot2); Stroke s = new BasicStroke(1.23f); plot1.setAngleGridlineStroke(s); assertNotEquals(plot1, plot2); plot2.setAngleGridlineStroke(s); assertEquals(plot1, plot2); plot1.setAngleTickUnit(new NumberTickUnit(11.0)); assertNotEquals(plot1, plot2); plot2.setAngleTickUnit(new NumberTickUnit(11.0)); assertEquals(plot1, plot2); plot1.setAngleGridlinesVisible(false); assertNotEquals(plot1, plot2); plot2.setAngleGridlinesVisible(false); assertEquals(plot1, plot2); plot1.setAngleLabelFont(new Font("Serif", Font.PLAIN, 9)); assertNotEquals(plot1, plot2); plot2.setAngleLabelFont(new Font("Serif", Font.PLAIN, 9)); assertEquals(plot1, plot2); plot1.setAngleLabelPaint(new GradientPaint(9.0f, 8.0f, Color.BLUE, 7.0f, 6.0f, Color.RED)); assertNotEquals(plot1, plot2); plot2.setAngleLabelPaint(new GradientPaint(9.0f, 8.0f, Color.BLUE, 7.0f, 6.0f, Color.RED)); assertEquals(plot1, plot2); plot1.setAngleLabelsVisible(false); assertNotEquals(plot1, plot2); plot2.setAngleLabelsVisible(false); assertEquals(plot1, plot2); plot1.setAxis(new NumberAxis("Test")); assertNotEquals(plot1, plot2); plot2.setAxis(new NumberAxis("Test")); assertEquals(plot1, plot2); plot1.setRadiusGridlinePaint(new GradientPaint(1.0f, 2.0f, Color.WHITE, 3.0f, 4.0f, Color.BLACK)); assertNotEquals(plot1, plot2); plot2.setRadiusGridlinePaint(new GradientPaint(1.0f, 2.0f, Color.WHITE, 3.0f, 4.0f, Color.BLACK)); assertEquals(plot1, plot2); plot1.setRadiusGridlineStroke(s); assertNotEquals(plot1, plot2); plot2.setRadiusGridlineStroke(s); assertEquals(plot1, plot2); plot1.setRadiusGridlinesVisible(false); assertNotEquals(plot1, plot2); plot2.setRadiusGridlinesVisible(false); assertEquals(plot1, plot2); plot1.setRadiusMinorGridlinesVisible(false); assertNotEquals(plot1, plot2); plot2.setRadiusMinorGridlinesVisible(false); assertEquals(plot1, plot2); plot1.addCornerTextItem("XYZ"); assertNotEquals(plot1, plot2); plot2.addCornerTextItem("XYZ"); assertEquals(plot1, plot2); plot1.setMargin(6); assertNotEquals(plot1, plot2); plot2.setMargin(6); assertEquals(plot1, plot2); LegendItemCollection lic1 = new LegendItemCollection(); lic1.add(new LegendItem("XYZ", Color.RED)); plot1.setFixedLegendItems(lic1); assertNotEquals(plot1, plot2); LegendItemCollection lic2 = new LegendItemCollection(); lic2.add(new LegendItem("XYZ", Color.RED)); plot2.setFixedLegendItems(lic2); assertEquals(plot1, plot2); } /** * Some basic checks for the clone() method. */ @Test public void testCloning() throws CloneNotSupportedException { PolarPlot p1 = new PolarPlot(); PolarPlot p2 = (PolarPlot) p1.clone(); assertNotSame(p1, p2); assertSame(p1.getClass(), p2.getClass()); assertEquals(p1, p2); // check independence p1.addCornerTextItem("XYZ"); assertNotEquals(p1, p2); p2.addCornerTextItem("XYZ"); assertEquals(p1, p2); p1 = new PolarPlot(new DefaultXYDataset(), new NumberAxis("A1"), new DefaultPolarItemRenderer()); p2 = (PolarPlot) p1.clone(); assertNotSame(p1, p2); assertSame(p1.getClass(), p2.getClass()); assertEquals(p1, p2); // check independence p1.getAxis().setLabel("ABC"); assertNotEquals(p1, p2); p2.getAxis().setLabel("ABC"); assertEquals(p1, p2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { PolarPlot p1 = new PolarPlot(); p1.setAngleGridlinePaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); p1.setAngleLabelPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); p1.setRadiusGridlinePaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); PolarPlot p2 = TestUtils.serialised(p1); assertEquals(p1, p2); } @Test public void testTranslateToJava2D_NumberAxis() { Rectangle2D dataArea = new Rectangle2D.Double(0.0, 0.0, 100.0, 100.0); ValueAxis axis = new NumberAxis(); axis.setRange(0.0, 20.0); PolarPlot plot = new PolarPlot(null, axis, null); plot.setMargin(0); plot.setAngleOffset(0.0); Point point = plot.translateToJava2D(0.0, 10.0, axis, dataArea ); assertEquals(75.0, point.getX(), 0.5); assertEquals(50.0, point.getY(), 0.5); point = plot.translateToJava2D(90.0, 5.0, axis, dataArea ); assertEquals(50.0, point.getX(), 0.5); assertEquals(62.5, point.getY(), 0.5); point = plot.translateToJava2D(45.0, 20.0, axis, dataArea ); assertEquals(85.0, point.getX(), 0.5); assertEquals(85.0, point.getY(), 0.5); point = plot.translateToJava2D(135.0, 20.0, axis, dataArea ); assertEquals(15.0, point.getX(), 0.5); assertEquals(85.0, point.getY(), 0.5); point = plot.translateToJava2D(225.0, 15.0, axis, dataArea ); assertEquals(23.0, point.getX(), 0.5); assertEquals(23.0, point.getY(), 0.5); point = plot.translateToJava2D(315.0, 15.0, axis, dataArea ); assertEquals(77.0, point.getX(), 0.5); assertEquals(23.0, point.getY(), 0.5); point = plot.translateToJava2D(21.0, 11.5, axis, dataArea ); assertEquals(77.0, point.getX(), 0.5); assertEquals(60.0, point.getY(), 0.5); point = plot.translateToJava2D(162.0, 7.0, axis, dataArea ); assertEquals(33.0, point.getX(), 0.5); assertEquals(55.0, point.getY(), 0.5); } @Test public void testTranslateToJava2D_NumberAxisAndMargin() { Rectangle2D dataArea = new Rectangle2D.Double(10.0, 10.0, 80.0, 80.0); ValueAxis axis = new NumberAxis(); axis.setRange(-2.0, 2.0); PolarPlot plot = new PolarPlot(null, axis, null); plot.setAngleOffset(0.0); Point point = plot.translateToJava2D(0.0, 10.0, axis, dataArea ); assertEquals(110.0, point.getX(), 0.5); assertEquals(50.0, point.getY(), 0.5); point = plot.translateToJava2D(90.0, 5.0, axis, dataArea ); assertEquals(50.0, point.getX(), 0.5); assertEquals(85.0, point.getY(), 0.5); point = plot.translateToJava2D(45.0, 20.0, axis, dataArea ); assertEquals(128.0, point.getX(), 0.5); assertEquals(128.0, point.getY(), 0.5); point = plot.translateToJava2D(135.0, 20.0, axis, dataArea ); assertEquals(-28.0, point.getX(), 0.5); assertEquals(128.0, point.getY(), 0.5); point = plot.translateToJava2D(225.0, 15.0, axis, dataArea ); assertEquals(-10.0, point.getX(), 0.5); assertEquals(-10.0, point.getY(), 0.5); point = plot.translateToJava2D(315.0, 15.0, axis, dataArea ); assertEquals(110.0, point.getX(), 0.5); assertEquals(-10.0, point.getY(), 0.5); point = plot.translateToJava2D(21.0, 11.5, axis, dataArea ); assertEquals(113.0, point.getX(), 0.5); assertEquals(74.0, point.getY(), 0.5); point = plot.translateToJava2D(162.0, 7.0, axis, dataArea ); assertEquals(7.0, point.getX(), 0.5); assertEquals(64.0, point.getY(), 0.5); } @Test public void testTranslateToJava2D_LogAxis() { Rectangle2D dataArea = new Rectangle2D.Double(0.0, 0.0, 100.0, 100.0); ValueAxis axis = new LogAxis(); axis.setRange(1.0, 100.0); PolarPlot plot = new PolarPlot(null, axis, null); plot.setMargin(0); plot.setAngleOffset(0.0); Point point = plot.translateToJava2D(0.0, 10.0, axis, dataArea ); assertEquals(75.0, point.getX(), 0.5); assertEquals(50.0, point.getY(), 0.5); point = plot.translateToJava2D(90.0, 5.0, axis, dataArea ); assertEquals(50.0, point.getX(), 0.5); assertEquals(67.5, point.getY(), 0.5); point = plot.translateToJava2D(45.0, 20.0, axis, dataArea ); assertEquals(73.0, point.getX(), 0.5); assertEquals(73.0, point.getY(), 0.5); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/RingPlotTest.java000066400000000000000000000121721463604235500273330ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * RingPlotTest.java * ----------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import org.junit.jupiter.api.Test; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.GradientPaint; import java.awt.Stroke; import java.text.DecimalFormat; import org.jfree.chart.TestUtils; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link RingPlot} class. */ public class RingPlotTest { /** * Some checks for the equals() method. */ @Test public void testEquals() { RingPlot plot1 = new RingPlot(null); RingPlot plot2 = new RingPlot(null); assertEquals(plot1, plot2); assertEquals(plot2, plot1); plot1.setCenterTextMode(CenterTextMode.FIXED); assertNotEquals(plot1, plot2); plot2.setCenterTextMode(CenterTextMode.FIXED); assertEquals(plot1, plot2); plot1.setCenterText("ABC"); assertNotEquals(plot1, plot2); plot2.setCenterText("ABC"); assertEquals(plot1, plot2); plot1.setCenterTextColor(Color.RED); assertNotEquals(plot1, plot2); plot2.setCenterTextColor(Color.RED); assertEquals(plot1, plot2); plot1.setCenterTextFont(new Font(Font.SERIF, Font.PLAIN, 7)); assertNotEquals(plot1, plot2); plot2.setCenterTextFont(new Font(Font.SERIF, Font.PLAIN, 7)); assertEquals(plot1, plot2); plot1.setCenterTextFormatter(new DecimalFormat("0.000")); assertNotEquals(plot1, plot2); plot2.setCenterTextFormatter(new DecimalFormat("0.000")); assertEquals(plot1, plot2); // separatorsVisible plot1.setSeparatorsVisible(false); assertNotEquals(plot1, plot2); plot2.setSeparatorsVisible(false); assertEquals(plot1, plot2); // separatorStroke Stroke s = new BasicStroke(1.1f); plot1.setSeparatorStroke(s); assertNotEquals(plot1, plot2); plot2.setSeparatorStroke(s); assertEquals(plot1, plot2); // separatorPaint plot1.setSeparatorPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 2.0f, 1.0f, Color.BLUE)); assertNotEquals(plot1, plot2); plot2.setSeparatorPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 2.0f, 1.0f, Color.BLUE)); assertEquals(plot1, plot2); // innerSeparatorExtension plot1.setInnerSeparatorExtension(0.01); assertNotEquals(plot1, plot2); plot2.setInnerSeparatorExtension(0.01); assertEquals(plot1, plot2); // outerSeparatorExtension plot1.setOuterSeparatorExtension(0.02); assertNotEquals(plot1, plot2); plot2.setOuterSeparatorExtension(0.02); assertEquals(plot1, plot2); // sectionDepth plot1.setSectionDepth(0.12); assertNotEquals(plot1, plot2); plot2.setSectionDepth(0.12); assertEquals(plot1, plot2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { RingPlot p1 = new RingPlot(null); GradientPaint gp = new GradientPaint(1.0f, 2.0f, Color.YELLOW, 3.0f, 4.0f, Color.RED); p1.setSeparatorPaint(gp); RingPlot p2 = (RingPlot) p1.clone(); assertNotSame(p1, p2); assertSame(p1.getClass(), p2.getClass()); assertEquals(p1, p2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { RingPlot p1 = new RingPlot(null); GradientPaint gp = new GradientPaint(1.0f, 2.0f, Color.YELLOW, 3.0f, 4.0f, Color.RED); p1.setSeparatorPaint(gp); RingPlot p2 = TestUtils.serialised(p1); assertEquals(p1, p2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/SpiderWebPlotTest.java000066400000000000000000000277661463604235500303370ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * SpiderWebPlotTest.java * ---------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.GradientPaint; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.text.DecimalFormat; import org.jfree.chart.JFreeChart; import org.jfree.chart.LegendItem; import org.jfree.chart.LegendItemCollection; import org.jfree.chart.TestUtils; import org.jfree.chart.labels.StandardCategoryItemLabelGenerator; import org.jfree.chart.labels.StandardCategoryToolTipGenerator; import org.jfree.chart.urls.StandardCategoryURLGenerator; import org.jfree.chart.util.Rotation; import org.jfree.chart.util.TableOrder; import org.jfree.data.category.DefaultCategoryDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link SpiderWebPlot} class. */ public class SpiderWebPlotTest { /** * Some checks for the equals() method. */ @Test public void testEquals() { SpiderWebPlot p1 = new SpiderWebPlot(new DefaultCategoryDataset()); SpiderWebPlot p2 = new SpiderWebPlot(new DefaultCategoryDataset()); assertEquals(p1, p2); assertEquals(p2, p1); // dataExtractOrder p1.setDataExtractOrder(TableOrder.BY_COLUMN); assertNotEquals(p1, p2); p2.setDataExtractOrder(TableOrder.BY_COLUMN); assertEquals(p1, p2); // headPercent p1.setHeadPercent(0.321); assertNotEquals(p1, p2); p2.setHeadPercent(0.321); assertEquals(p1, p2); // interiorGap p1.setInteriorGap(0.123); assertNotEquals(p1, p2); p2.setInteriorGap(0.123); assertEquals(p1, p2); // startAngle p1.setStartAngle(0.456); assertNotEquals(p1, p2); p2.setStartAngle(0.456); assertEquals(p1, p2); // direction p1.setDirection(Rotation.ANTICLOCKWISE); assertNotEquals(p1, p2); p2.setDirection(Rotation.ANTICLOCKWISE); assertEquals(p1, p2); // maxValue p1.setMaxValue(123.4); assertNotEquals(p1, p2); p2.setMaxValue(123.4); assertEquals(p1, p2); // legendItemShape p1.setLegendItemShape(new Rectangle(1, 2, 3, 4)); assertNotEquals(p1, p2); p2.setLegendItemShape(new Rectangle(1, 2, 3, 4)); assertEquals(p1, p2); // seriesPaint p1.setSeriesPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.WHITE)); assertNotEquals(p1, p2); p2.setSeriesPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.WHITE)); assertEquals(p1, p2); // seriesPaintList p1.setSeriesPaint(1, new GradientPaint(1.0f, 2.0f, Color.YELLOW, 3.0f, 4.0f, Color.WHITE)); assertNotEquals(p1, p2); p2.setSeriesPaint(1, new GradientPaint(1.0f, 2.0f, Color.YELLOW, 3.0f, 4.0f, Color.WHITE)); assertEquals(p1, p2); // baseSeriesPaint p1.setBaseSeriesPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLACK)); assertNotEquals(p1, p2); p2.setBaseSeriesPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLACK)); assertEquals(p1, p2); // seriesOutlinePaint p1.setSeriesOutlinePaint(new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.BLACK)); assertNotEquals(p1, p2); p2.setSeriesOutlinePaint(new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.BLACK)); assertEquals(p1, p2); // seriesOutlinePaintList p1.setSeriesOutlinePaint(1, new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.GREEN)); assertNotEquals(p1, p2); p2.setSeriesOutlinePaint(1, new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.GREEN)); assertEquals(p1, p2); // baseSeriesOutlinePaint p1.setBaseSeriesOutlinePaint(new GradientPaint(1.0f, 2.0f, Color.CYAN, 3.0f, 4.0f, Color.GREEN)); assertNotEquals(p1, p2); p2.setBaseSeriesOutlinePaint(new GradientPaint(1.0f, 2.0f, Color.CYAN, 3.0f, 4.0f, Color.GREEN)); assertEquals(p1, p2); // seriesOutlineStroke BasicStroke s = new BasicStroke(1.23f); p1.setSeriesOutlineStroke(s); assertNotEquals(p1, p2); p2.setSeriesOutlineStroke(s); assertEquals(p1, p2); // seriesOutlineStrokeList p1.setSeriesOutlineStroke(1, s); assertNotEquals(p1, p2); p2.setSeriesOutlineStroke(1, s); assertEquals(p1, p2); // baseSeriesOutlineStroke p1.setBaseSeriesOutlineStroke(s); assertNotEquals(p1, p2); p2.setBaseSeriesOutlineStroke(s); assertEquals(p1, p2); // webFilled p1.setWebFilled(false); assertNotEquals(p1, p2); p2.setWebFilled(false); assertEquals(p1, p2); p1.setWebFillAlpha(0.5f); assertNotEquals(p1, p2); p2.setWebFillAlpha(0.5f); assertEquals(p1, p2); // axisLabelGap p1.setAxisLabelGap(0.11); assertNotEquals(p1, p2); p2.setAxisLabelGap(0.11); assertEquals(p1, p2); // labelFont p1.setLabelFont(new Font("Serif", Font.PLAIN, 9)); assertNotEquals(p1, p2); p2.setLabelFont(new Font("Serif", Font.PLAIN, 9)); assertEquals(p1, p2); // labelPaint p1.setLabelPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); assertNotEquals(p1, p2); p2.setLabelPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); assertEquals(p1, p2); // labelGenerator p1.setLabelGenerator(new StandardCategoryItemLabelGenerator("XYZ: {0}", new DecimalFormat("0.000"))); assertNotEquals(p1, p2); p2.setLabelGenerator(new StandardCategoryItemLabelGenerator("XYZ: {0}", new DecimalFormat("0.000"))); assertEquals(p1, p2); // toolTipGenerator p1.setToolTipGenerator(new StandardCategoryToolTipGenerator()); assertNotEquals(p1, p2); p2.setToolTipGenerator(new StandardCategoryToolTipGenerator()); assertEquals(p1, p2); // urlGenerator p1.setURLGenerator(new StandardCategoryURLGenerator()); assertNotEquals(p1, p2); p2.setURLGenerator(new StandardCategoryURLGenerator()); assertEquals(p1, p2); // axisLinePaint p1.setAxisLinePaint(Color.RED); assertNotEquals(p1, p2); p2.setAxisLinePaint(Color.RED); assertEquals(p1, p2); // axisLineStroke p1.setAxisLineStroke(new BasicStroke(1.1f)); assertNotEquals(p1, p2); p2.setAxisLineStroke(new BasicStroke(1.1f)); assertEquals(p1, p2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { SpiderWebPlot p1 = new SpiderWebPlot(new DefaultCategoryDataset()); Rectangle2D legendShape = new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0); p1.setLegendItemShape(legendShape); SpiderWebPlot p2 = (SpiderWebPlot) p1.clone(); assertNotSame(p1, p2); assertSame(p1.getClass(), p2.getClass()); assertEquals(p1, p2); // change the legendItemShape legendShape.setRect(4.0, 3.0, 2.0, 1.0); assertNotEquals(p1, p2); p2.setLegendItemShape(legendShape); assertEquals(p1, p2); // change a series paint p1.setSeriesPaint(1, Color.BLACK); assertNotEquals(p1, p2); p2.setSeriesPaint(1, Color.BLACK); assertEquals(p1, p2); // change a series outline paint p1.setSeriesOutlinePaint(0, Color.RED); assertNotEquals(p1, p2); p2.setSeriesOutlinePaint(0, Color.RED); assertEquals(p1, p2); // change a series outline stroke p1.setSeriesOutlineStroke(0, new BasicStroke(1.1f)); assertNotEquals(p1, p2); p2.setSeriesOutlineStroke(0, new BasicStroke(1.1f)); assertEquals(p1, p2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { SpiderWebPlot p1 = new SpiderWebPlot(new DefaultCategoryDataset()); SpiderWebPlot p2 = TestUtils.serialised(p1); assertEquals(p1, p2); } /** * Draws the chart with a null info object to make sure that no exceptions * are thrown. */ @Test public void testDrawWithNullInfo() { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.addValue(35.0, "S1", "C1"); dataset.addValue(45.0, "S1", "C2"); dataset.addValue(55.0, "S1", "C3"); dataset.addValue(15.0, "S1", "C4"); dataset.addValue(25.0, "S1", "C5"); SpiderWebPlot plot = new SpiderWebPlot(dataset); JFreeChart chart = new JFreeChart(plot); try { BufferedImage image = new BufferedImage(200 , 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); chart.draw(g2, new Rectangle2D.Double(0, 0, 200, 100), null, null); g2.dispose(); } catch (Exception e) { fail("There should be no exception."); } } /** * Fetches the legend items and checks the values. */ @Test public void testGetLegendItems() { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.addValue(35.0, "S1", "C1"); dataset.addValue(45.0, "S1", "C2"); dataset.addValue(55.0, "S2", "C1"); dataset.addValue(15.0, "S2", "C2"); SpiderWebPlot plot = new SpiderWebPlot(dataset); JFreeChart chart = new JFreeChart(plot); LegendItemCollection legendItems = plot.getLegendItems(); assertEquals(2, legendItems.getItemCount()); LegendItem item1 = legendItems.get(0); assertEquals("S1", item1.getLabel()); assertEquals("S1", item1.getSeriesKey()); assertEquals(0, item1.getSeriesIndex()); assertEquals(dataset, item1.getDataset()); assertEquals(0, item1.getDatasetIndex()); LegendItem item2 = legendItems.get(1); assertEquals("S2", item2.getLabel()); assertEquals("S2", item2.getSeriesKey()); assertEquals(1, item2.getSeriesIndex()); assertEquals(dataset, item2.getDataset()); assertEquals(0, item2.getDatasetIndex()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/ThermometerPlotTest.java000066400000000000000000000141751463604235500307340ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * ThermometerPlotTest.java * ------------------------ * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.GradientPaint; import java.text.DecimalFormat; import org.jfree.chart.TestUtils; import org.jfree.chart.ui.RectangleInsets; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link ThermometerPlot} class. */ public class ThermometerPlotTest { /** * Some checks for the equals() method. */ @Test public void testEquals() { ThermometerPlot p1 = new ThermometerPlot(); ThermometerPlot p2 = new ThermometerPlot(); assertEquals(p1, p2); assertEquals(p2, p1); // padding p1.setPadding(new RectangleInsets(1.0, 2.0, 3.0, 4.0)); assertNotEquals(p1, p2); p2.setPadding(new RectangleInsets(1.0, 2.0, 3.0, 4.0)); assertEquals(p2, p1); // thermometerStroke BasicStroke s = new BasicStroke(1.23f); p1.setThermometerStroke(s); assertNotEquals(p1, p2); p2.setThermometerStroke(s); assertEquals(p2, p1); // thermometerPaint p1.setThermometerPaint(new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.RED)); assertNotEquals(p1, p2); p2.setThermometerPaint(new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.RED)); assertEquals(p2, p1); // units p1.setUnits(ThermometerPlot.UNITS_KELVIN); assertNotEquals(p1, p2); p2.setUnits(ThermometerPlot.UNITS_KELVIN); assertEquals(p2, p1); // valueLocation p1.setValueLocation(ThermometerPlot.LEFT); assertNotEquals(p1, p2); p2.setValueLocation(ThermometerPlot.LEFT); assertEquals(p2, p1); // axisLocation p1.setAxisLocation(ThermometerPlot.RIGHT); assertNotEquals(p1, p2); p2.setAxisLocation(ThermometerPlot.RIGHT); assertEquals(p2, p1); // valueFont p1.setValueFont(new Font("Serif", Font.PLAIN, 9)); assertNotEquals(p1, p2); p2.setValueFont(new Font("Serif", Font.PLAIN, 9)); assertEquals(p2, p1); // valuePaint p1.setValuePaint(new GradientPaint(4.0f, 5.0f, Color.RED, 6.0f, 7.0f, Color.WHITE)); assertNotEquals(p1, p2); p2.setValuePaint(new GradientPaint(4.0f, 5.0f, Color.RED, 6.0f, 7.0f, Color.WHITE)); assertEquals(p2, p1); // valueFormat p1.setValueFormat(new DecimalFormat("0.0000")); assertNotEquals(p1, p2); p2.setValueFormat(new DecimalFormat("0.0000")); assertEquals(p2, p1); // mercuryPaint p1.setMercuryPaint(new GradientPaint(9.0f, 8.0f, Color.RED, 7.0f, 6.0f, Color.BLUE)); assertNotEquals(p1, p2); p2.setMercuryPaint(new GradientPaint(9.0f, 8.0f, Color.RED, 7.0f, 6.0f, Color.BLUE)); assertEquals(p2, p1); p1.setSubrange(1, 1.0, 2.0); assertNotEquals(p1, p2); p2.setSubrange(1, 1.0, 2.0); assertEquals(p2, p1); p1.setSubrangePaint(1, new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW)); assertNotEquals(p1, p2); p2.setSubrangePaint(1, new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW)); assertEquals(p2, p1); p1.setBulbRadius(9); assertNotEquals(p1, p2); p2.setBulbRadius(9); assertEquals(p2, p1); p1.setColumnRadius(8); assertNotEquals(p1, p2); p2.setColumnRadius(8); assertEquals(p2, p1); p1.setGap(7); assertNotEquals(p1, p2); p2.setGap(7); assertEquals(p2, p1); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { ThermometerPlot p1 = new ThermometerPlot(); ThermometerPlot p2 = (ThermometerPlot) p1.clone(); assertNotSame(p1, p2); assertSame(p1.getClass(), p2.getClass()); assertEquals(p1, p2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { ThermometerPlot p1 = new ThermometerPlot(); ThermometerPlot p2 = TestUtils.serialised(p1); assertEquals(p1, p2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization2() { ThermometerPlot p1 = new ThermometerPlot(); p1.setSubrangePaint(1, new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); ThermometerPlot p2 = TestUtils.serialised(p1); assertEquals(p1, p2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/ValueMarkerTest.java000066400000000000000000000202171463604235500300120ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * ValueMarkerTest.java * -------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.GradientPaint; import java.awt.Stroke; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.chart.event.MarkerChangeEvent; import org.jfree.chart.event.MarkerChangeListener; import org.jfree.chart.ui.LengthAdjustmentType; import org.jfree.chart.ui.RectangleAnchor; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.ui.TextAnchor; import org.junit.jupiter.api.Test; import javax.swing.event.EventListenerList; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link ValueMarker} class. */ public class ValueMarkerTest implements MarkerChangeListener { MarkerChangeEvent lastEvent; /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(ValueMarker.class) .withRedefinedSuperclass() // superclass also defines equals/hashCode .withPrefabValues(ValueMarker.class, new ValueMarker(44.5), new ValueMarker(33.3)) .withPrefabValues(Font.class, new Font("SansSerif", Font.PLAIN, 10), new Font("Tahoma", Font.BOLD, 12)) .withPrefabValues(EventListenerList.class, new EventListenerList(), new EventListenerList()) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { Marker m1 = new ValueMarker(45.0); Marker m2 = new ValueMarker(45.0); assertEquals(m1, m2); assertEquals(m2, m1); m1.setPaint(new GradientPaint(1.0f, 2.0f, Color.GREEN, 3.0f, 4.0f, Color.RED)); assertNotEquals(m1, m2); m2.setPaint(new GradientPaint(1.0f, 2.0f, Color.GREEN, 3.0f, 4.0f, Color.RED)); assertEquals(m1, m2); BasicStroke stroke = new BasicStroke(2.2f); m1.setStroke(stroke); assertNotEquals(m1, m2); m2.setStroke(stroke); assertEquals(m1, m2); m1.setOutlinePaint(new GradientPaint(4.0f, 3.0f, Color.YELLOW, 2.0f, 1.0f, Color.WHITE)); assertNotEquals(m1, m2); m2.setOutlinePaint(new GradientPaint(4.0f, 3.0f, Color.YELLOW, 2.0f, 1.0f, Color.WHITE)); assertEquals(m1, m2); m1.setOutlineStroke(stroke); assertNotEquals(m1, m2); m2.setOutlineStroke(stroke); assertEquals(m1, m2); m1.setAlpha(0.1f); assertNotEquals(m1, m2); m2.setAlpha(0.1f); assertEquals(m1, m2); m1.setLabel("New Label"); assertNotEquals(m1, m2); m2.setLabel("New Label"); assertEquals(m1, m2); m1.setLabelFont(new Font("SansSerif", Font.PLAIN, 10)); assertNotEquals(m1, m2); m2.setLabelFont(new Font("SansSerif", Font.PLAIN, 10)); assertEquals(m1, m2); m1.setLabelPaint(new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.YELLOW)); assertNotEquals(m1, m2); m2.setLabelPaint(new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.YELLOW)); assertEquals(m1, m2); m1.setLabelAnchor(RectangleAnchor.TOP_RIGHT); assertNotEquals(m1, m2); m2.setLabelAnchor(RectangleAnchor.TOP_RIGHT); assertEquals(m1, m2); m1.setLabelTextAnchor(TextAnchor.BASELINE_RIGHT); assertNotEquals(m1, m2); m2.setLabelTextAnchor(TextAnchor.BASELINE_RIGHT); assertEquals(m1, m2); m1.setLabelOffset(new RectangleInsets(10.0, 10.0, 10.0, 10.0)); assertNotEquals(m1, m2); m2.setLabelOffset(new RectangleInsets(10.0, 10.0, 10.0, 10.0)); assertEquals(m1, m2); m1.setLabelOffsetType(LengthAdjustmentType.EXPAND); assertNotEquals(m1, m2); m2.setLabelOffsetType(LengthAdjustmentType.EXPAND); assertEquals(m1, m2); m1 = new ValueMarker(12.3); m2 = new ValueMarker(45.6); assertNotEquals(m1, m2); m2 = new ValueMarker(12.3); assertEquals(m1, m2); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException if there is a cloning issue. */ @Test public void testCloning() throws CloneNotSupportedException { ValueMarker m1 = new ValueMarker(25.0); ValueMarker m2 = (ValueMarker) m1.clone(); assertNotSame(m1, m2); assertSame(m1.getClass(), m2.getClass()); assertEquals(m1, m2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { ValueMarker m1 = new ValueMarker(25.0); ValueMarker m2 = TestUtils.serialised(m1); assertEquals(m1, m2); } private static final double EPSILON = 0.000000001; /** * Some checks for the getValue() and setValue() methods. */ @Test public void testGetSetValue() { ValueMarker m = new ValueMarker(1.1); m.addChangeListener(this); this.lastEvent = null; assertEquals(1.1, m.getValue(), EPSILON); m.setValue(33.3); assertEquals(33.3, m.getValue(), EPSILON); assertEquals(m, this.lastEvent.getMarker()); } /** * Records the last event. * * @param event the last event. */ @Override public void markerChanged(MarkerChangeEvent event) { this.lastEvent = event; } /** * A test for bug 1802195. */ @Test public void test1802195() { ValueMarker m1 = new ValueMarker(25.0); ValueMarker m2 = TestUtils.serialised(m1); assertEquals(m1, m2); try { m2.setValue(-10.0); } catch (NullPointerException e) { fail("No exception should be thrown."); } } /** * A test for bug report 1808376. */ @Test public void test1808376() { Stroke stroke = new BasicStroke(1.0f); Stroke outlineStroke = new BasicStroke(2.0f); ValueMarker m = new ValueMarker(1.0, Color.RED, stroke, Color.BLUE, outlineStroke, 0.5f); assertEquals(1.0, m.getValue(), EPSILON); assertEquals(Color.RED, m.getPaint()); assertEquals(stroke, m.getStroke()); assertEquals(Color.BLUE, m.getOutlinePaint()); assertEquals(outlineStroke, m.getOutlineStroke()); assertEquals(0.5f, m.getAlpha(), EPSILON); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/XYPlotTest.java000066400000000000000000001502541463604235500270000ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * XYPlotTest.java * --------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot; import org.junit.jupiter.api.Test; import java.awt.BasicStroke; import java.awt.Color; import java.awt.GradientPaint; import java.awt.Graphics2D; import java.awt.Stroke; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.util.Arrays; import java.util.List; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.LegendItem; import org.jfree.chart.LegendItemCollection; import org.jfree.chart.TestUtils; import org.jfree.chart.annotations.XYTextAnnotation; import org.jfree.chart.axis.AxisLocation; import org.jfree.chart.axis.DateAxis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.date.MonthConstants; import org.jfree.chart.event.MarkerChangeListener; import org.jfree.chart.labels.StandardXYToolTipGenerator; import org.jfree.chart.renderer.xy.DefaultXYItemRenderer; import org.jfree.chart.renderer.xy.StandardXYItemRenderer; import org.jfree.chart.renderer.xy.XYBarRenderer; import org.jfree.chart.renderer.xy.XYItemRenderer; import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; import org.jfree.chart.ui.Layer; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.util.DefaultShadowGenerator; import org.jfree.data.Range; import org.jfree.data.time.Day; import org.jfree.data.time.TimeSeries; import org.jfree.data.time.TimeSeriesCollection; import org.jfree.data.xy.DefaultXYDataset; import org.jfree.data.xy.IntervalXYDataset; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XYPlot} class. */ public class XYPlotTest { /** * Added this test in response to a bug report. */ @Test public void testGetDatasetCount() { XYPlot plot = new XYPlot(); assertEquals(0, plot.getDatasetCount()); } /** * Some checks for the equals() method. */ @Test public void testEquals() { XYPlot plot1 = new XYPlot(); XYPlot plot2 = new XYPlot(); assertEquals(plot1, plot2); // orientation... plot1.setOrientation(PlotOrientation.HORIZONTAL); assertNotEquals(plot1, plot2); plot2.setOrientation(PlotOrientation.HORIZONTAL); assertEquals(plot1, plot2); // axisOffset... plot1.setAxisOffset(new RectangleInsets(0.05, 0.05, 0.05, 0.05)); assertNotEquals(plot1, plot2); plot2.setAxisOffset(new RectangleInsets(0.05, 0.05, 0.05, 0.05)); assertEquals(plot1, plot2); // domainAxis... plot1.setDomainAxis(new NumberAxis("Domain Axis")); assertNotEquals(plot1, plot2); plot2.setDomainAxis(new NumberAxis("Domain Axis")); assertEquals(plot1, plot2); // domainAxisLocation... plot1.setDomainAxisLocation(AxisLocation.TOP_OR_RIGHT); assertNotEquals(plot1, plot2); plot2.setDomainAxisLocation(AxisLocation.TOP_OR_RIGHT); assertEquals(plot1, plot2); // secondary DomainAxes... plot1.setDomainAxis(11, new NumberAxis("Secondary Domain Axis")); assertNotEquals(plot1, plot2); plot2.setDomainAxis(11, new NumberAxis("Secondary Domain Axis")); assertEquals(plot1, plot2); // secondary DomainAxisLocations... plot1.setDomainAxisLocation(11, AxisLocation.TOP_OR_RIGHT); assertNotEquals(plot1, plot2); plot2.setDomainAxisLocation(11, AxisLocation.TOP_OR_RIGHT); assertEquals(plot1, plot2); // rangeAxis... plot1.setRangeAxis(new NumberAxis("Range Axis")); assertNotEquals(plot1, plot2); plot2.setRangeAxis(new NumberAxis("Range Axis")); assertEquals(plot1, plot2); // rangeAxisLocation... plot1.setRangeAxisLocation(AxisLocation.TOP_OR_RIGHT); assertNotEquals(plot1, plot2); plot2.setRangeAxisLocation(AxisLocation.TOP_OR_RIGHT); assertEquals(plot1, plot2); // secondary RangeAxes... plot1.setRangeAxis(11, new NumberAxis("Secondary Range Axis")); assertNotEquals(plot1, plot2); plot2.setRangeAxis(11, new NumberAxis("Secondary Range Axis")); assertEquals(plot1, plot2); // secondary RangeAxisLocations... plot1.setRangeAxisLocation(11, AxisLocation.TOP_OR_RIGHT); assertNotEquals(plot1, plot2); plot2.setRangeAxisLocation(11, AxisLocation.TOP_OR_RIGHT); assertEquals(plot1, plot2); // secondary DatasetDomainAxisMap... plot1.mapDatasetToDomainAxis(11, 11); assertNotEquals(plot1, plot2); plot2.mapDatasetToDomainAxis(11, 11); assertEquals(plot1, plot2); // secondaryDatasetRangeAxisMap... plot1.mapDatasetToRangeAxis(11, 11); assertNotEquals(plot1, plot2); plot2.mapDatasetToRangeAxis(11, 11); assertEquals(plot1, plot2); // renderer plot1.setRenderer(new DefaultXYItemRenderer()); assertNotEquals(plot1, plot2); plot2.setRenderer(new DefaultXYItemRenderer()); assertEquals(plot1, plot2); // secondary renderers plot1.setRenderer(11, new DefaultXYItemRenderer()); assertNotEquals(plot1, plot2); plot2.setRenderer(11, new DefaultXYItemRenderer()); assertEquals(plot1, plot2); // domainGridlinesVisible plot1.setDomainGridlinesVisible(false); assertNotEquals(plot1, plot2); plot2.setDomainGridlinesVisible(false); assertEquals(plot1, plot2); // domainGridlineStroke Stroke stroke = new BasicStroke(2.0f); plot1.setDomainGridlineStroke(stroke); assertNotEquals(plot1, plot2); plot2.setDomainGridlineStroke(stroke); assertEquals(plot1, plot2); // domainGridlinePaint plot1.setDomainGridlinePaint(new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.RED)); assertNotEquals(plot1, plot2); plot2.setDomainGridlinePaint(new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.RED)); assertEquals(plot1, plot2); // rangeGridlinesVisible plot1.setRangeGridlinesVisible(false); assertNotEquals(plot1, plot2); plot2.setRangeGridlinesVisible(false); assertEquals(plot1, plot2); // rangeGridlineStroke plot1.setRangeGridlineStroke(stroke); assertNotEquals(plot1, plot2); plot2.setRangeGridlineStroke(stroke); assertEquals(plot1, plot2); // rangeGridlinePaint plot1.setRangeGridlinePaint(new GradientPaint(1.0f, 2.0f, Color.GREEN, 3.0f, 4.0f, Color.RED)); assertNotEquals(plot1, plot2); plot2.setRangeGridlinePaint(new GradientPaint(1.0f, 2.0f, Color.GREEN, 3.0f, 4.0f, Color.RED)); assertEquals(plot1, plot2); // rangeZeroBaselineVisible plot1.setRangeZeroBaselineVisible(true); assertNotEquals(plot1, plot2); plot2.setRangeZeroBaselineVisible(true); assertEquals(plot1, plot2); // rangeZeroBaselineStroke plot1.setRangeZeroBaselineStroke(stroke); assertNotEquals(plot1, plot2); plot2.setRangeZeroBaselineStroke(stroke); assertEquals(plot1, plot2); // rangeZeroBaselinePaint plot1.setRangeZeroBaselinePaint(new GradientPaint(1.0f, 2.0f, Color.WHITE, 3.0f, 4.0f, Color.RED)); assertNotEquals(plot1, plot2); plot2.setRangeZeroBaselinePaint(new GradientPaint(1.0f, 2.0f, Color.WHITE, 3.0f, 4.0f, Color.RED)); assertEquals(plot1, plot2); // rangeCrosshairVisible plot1.setRangeCrosshairVisible(true); assertNotEquals(plot1, plot2); plot2.setRangeCrosshairVisible(true); assertEquals(plot1, plot2); // rangeCrosshairValue plot1.setRangeCrosshairValue(100.0); assertNotEquals(plot1, plot2); plot2.setRangeCrosshairValue(100.0); assertEquals(plot1, plot2); // rangeCrosshairStroke plot1.setRangeCrosshairStroke(stroke); assertNotEquals(plot1, plot2); plot2.setRangeCrosshairStroke(stroke); assertEquals(plot1, plot2); // rangeCrosshairPaint plot1.setRangeCrosshairPaint(new GradientPaint(1.0f, 2.0f, Color.PINK, 3.0f, 4.0f, Color.RED)); assertNotEquals(plot1, plot2); plot2.setRangeCrosshairPaint(new GradientPaint(1.0f, 2.0f, Color.PINK, 3.0f, 4.0f, Color.RED)); assertEquals(plot1, plot2); // rangeCrosshairLockedOnData plot1.setRangeCrosshairLockedOnData(false); assertNotEquals(plot1, plot2); plot2.setRangeCrosshairLockedOnData(false); assertEquals(plot1, plot2); // range markers plot1.addRangeMarker(new ValueMarker(4.0)); assertNotEquals(plot1, plot2); plot2.addRangeMarker(new ValueMarker(4.0)); assertEquals(plot1, plot2); // secondary range markers plot1.addRangeMarker(1, new ValueMarker(4.0), Layer.FOREGROUND); assertNotEquals(plot1, plot2); plot2.addRangeMarker(1, new ValueMarker(4.0), Layer.FOREGROUND); assertEquals(plot1, plot2); plot1.addRangeMarker(1, new ValueMarker(99.0), Layer.BACKGROUND); assertNotEquals(plot1, plot2); plot2.addRangeMarker(1, new ValueMarker(99.0), Layer.BACKGROUND); assertEquals(plot1, plot2); // fixed legend items plot1.setFixedLegendItems(new LegendItemCollection()); assertNotEquals(plot1, plot2); plot2.setFixedLegendItems(new LegendItemCollection()); assertEquals(plot1, plot2); // weight plot1.setWeight(3); assertNotEquals(plot1, plot2); plot2.setWeight(3); assertEquals(plot1, plot2); // quadrant origin plot1.setQuadrantOrigin(new Point2D.Double(12.3, 45.6)); assertNotEquals(plot1, plot2); plot2.setQuadrantOrigin(new Point2D.Double(12.3, 45.6)); assertEquals(plot1, plot2); // quadrant paint plot1.setQuadrantPaint(0, new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); assertNotEquals(plot1, plot2); plot2.setQuadrantPaint(0, new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); assertEquals(plot1, plot2); plot1.setQuadrantPaint(1, new GradientPaint(2.0f, 3.0f, Color.RED, 4.0f, 5.0f, Color.BLUE)); assertNotEquals(plot1, plot2); plot2.setQuadrantPaint(1, new GradientPaint(2.0f, 3.0f, Color.RED, 4.0f, 5.0f, Color.BLUE)); assertEquals(plot1, plot2); plot1.setQuadrantPaint(2, new GradientPaint(3.0f, 4.0f, Color.RED, 5.0f, 6.0f, Color.BLUE)); assertNotEquals(plot1, plot2); plot2.setQuadrantPaint(2, new GradientPaint(3.0f, 4.0f, Color.RED, 5.0f, 6.0f, Color.BLUE)); assertEquals(plot1, plot2); plot1.setQuadrantPaint(3, new GradientPaint(4.0f, 5.0f, Color.RED, 6.0f, 7.0f, Color.BLUE)); assertNotEquals(plot1, plot2); plot2.setQuadrantPaint(3, new GradientPaint(4.0f, 5.0f, Color.RED, 6.0f, 7.0f, Color.BLUE)); assertEquals(plot1, plot2); plot1.setDomainTickBandPaint(Color.RED); assertNotEquals(plot1, plot2); plot2.setDomainTickBandPaint(Color.RED); assertEquals(plot1, plot2); plot1.setRangeTickBandPaint(Color.BLUE); assertNotEquals(plot1, plot2); plot2.setRangeTickBandPaint(Color.BLUE); assertEquals(plot1, plot2); plot1.setDomainMinorGridlinesVisible(true); assertNotEquals(plot1, plot2); plot2.setDomainMinorGridlinesVisible(true); assertEquals(plot1, plot2); plot1.setDomainMinorGridlinePaint(Color.RED); assertNotEquals(plot1, plot2); plot2.setDomainMinorGridlinePaint(Color.RED); assertEquals(plot1, plot2); plot1.setDomainGridlineStroke(new BasicStroke(1.1f)); assertNotEquals(plot1, plot2); plot2.setDomainGridlineStroke(new BasicStroke(1.1f)); assertEquals(plot1, plot2); plot1.setRangeMinorGridlinesVisible(true); assertNotEquals(plot1, plot2); plot2.setRangeMinorGridlinesVisible(true); assertEquals(plot1, plot2); plot1.setRangeMinorGridlinePaint(Color.BLUE); assertNotEquals(plot1, plot2); plot2.setRangeMinorGridlinePaint(Color.BLUE); assertEquals(plot1, plot2); plot1.setRangeMinorGridlineStroke(new BasicStroke(1.23f)); assertNotEquals(plot1, plot2); plot2.setRangeMinorGridlineStroke(new BasicStroke(1.23f)); assertEquals(plot1, plot2); List axisIndices = Arrays.asList(0, 1); plot1.mapDatasetToDomainAxes(0, axisIndices); assertNotEquals(plot1, plot2); plot2.mapDatasetToDomainAxes(0, axisIndices); assertEquals(plot1, plot2); plot1.mapDatasetToRangeAxes(0, axisIndices); assertNotEquals(plot1, plot2); plot2.mapDatasetToRangeAxes(0, axisIndices); assertEquals(plot1, plot2); // shadowGenerator plot1.setShadowGenerator(new DefaultShadowGenerator(5, Color.GRAY, 0.6f, 4, -Math.PI / 4)); assertNotEquals(plot1, plot2); plot2.setShadowGenerator(new DefaultShadowGenerator(5, Color.GRAY, 0.6f, 4, -Math.PI / 4)); assertEquals(plot1, plot2); plot1.setShadowGenerator(null); assertNotEquals(plot1, plot2); plot2.setShadowGenerator(null); assertEquals(plot1, plot2); LegendItemCollection lic1 = new LegendItemCollection(); lic1.add(new LegendItem("XYZ", Color.RED)); plot1.setFixedLegendItems(lic1); assertNotEquals(plot1, plot2); LegendItemCollection lic2 = new LegendItemCollection(); lic2.add(new LegendItem("XYZ", Color.RED)); plot2.setFixedLegendItems(lic2); assertEquals(plot1, plot2); } /** * This test covers a flaw in the ObjectList equals() method. */ @Test public void testEquals_ObjectList() { XYPlot p1 = new XYPlot(); p1.setDomainAxis(new NumberAxis("A")); XYPlot p2 = new XYPlot(); p2.setDomainAxis(new NumberAxis("A")); assertEquals(p1, p2); p2.setDomainAxis(1, new NumberAxis("B")); assertNotEquals(p1, p2); } /** * This test covers a flaw in the ObjectList equals() method. */ @Test public void testEquals_ObjectList2() { XYPlot p1 = new XYPlot(); p1.setDomainAxisLocation(AxisLocation.BOTTOM_OR_RIGHT); XYPlot p2 = new XYPlot(); p2.setDomainAxisLocation(AxisLocation.BOTTOM_OR_RIGHT); assertEquals(p1, p2); p2.setDomainAxisLocation(1, AxisLocation.TOP_OR_LEFT); assertNotEquals(p1, p2); } /** * This test covers a flaw in the ObjectList equals() method. */ @Test public void testEquals_ObjectList3() { XYPlot p1 = new XYPlot(); p1.setRangeAxis(new NumberAxis("A")); XYPlot p2 = new XYPlot(); p2.setRangeAxis(new NumberAxis("A")); assertEquals(p1, p2); p2.setRangeAxis(1, new NumberAxis("B")); assertNotEquals(p1, p2); } /** * This test covers a flaw in the ObjectList equals() method. */ @Test public void testEquals_ObjectList4() { XYPlot p1 = new XYPlot(); p1.setRangeAxisLocation(AxisLocation.BOTTOM_OR_RIGHT); XYPlot p2 = new XYPlot(); p2.setRangeAxisLocation(AxisLocation.BOTTOM_OR_RIGHT); assertEquals(p1, p2); p2.setRangeAxisLocation(1, AxisLocation.TOP_OR_LEFT); assertNotEquals(p1, p2); } /** * This test covers a flaw in the ObjectList equals() method. */ @Test public void testEquals_ObjectList5() { XYPlot p1 = new XYPlot(); p1.setRenderer(new XYBarRenderer()); XYPlot p2 = new XYPlot(); p2.setRenderer(new XYBarRenderer()); assertEquals(p1, p2); p2.setRenderer(1, new XYLineAndShapeRenderer()); assertNotEquals(p1, p2); } /** * Confirm that basic cloning works. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { XYPlot p1 = new XYPlot(); XYPlot p2 = (XYPlot) p1.clone(); assertNotSame(p1, p2); assertSame(p1.getClass(), p2.getClass()); assertEquals(p1, p2); } /** * Tests cloning for a more complex plot. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning2() throws CloneNotSupportedException { XYPlot p1 = new XYPlot(null, new NumberAxis("Domain Axis"), new NumberAxis("Range Axis"), new StandardXYItemRenderer()); p1.setRangeAxis(1, new NumberAxis("Range Axis 2")); List axisIndices = Arrays.asList(0, 1); p1.mapDatasetToDomainAxes(0, axisIndices); p1.mapDatasetToRangeAxes(0, axisIndices); p1.setRenderer(1, new XYBarRenderer()); XYPlot p2 = (XYPlot) p1.clone(); assertNotSame(p1, p2); assertSame(p1.getClass(), p2.getClass()); assertEquals(p1, p2); } /** * Tests cloning for a plot where the fixed legend items have been * specified. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning3() throws CloneNotSupportedException { XYPlot p1 = new XYPlot(null, new NumberAxis("Domain Axis"), new NumberAxis("Range Axis"), new StandardXYItemRenderer()); LegendItemCollection c1 = new LegendItemCollection(); p1.setFixedLegendItems(c1); XYPlot p2 = (XYPlot) p1.clone(); assertNotSame(p1, p2); assertSame(p1.getClass(), p2.getClass()); assertEquals(p1, p2); // verify independence of fixed legend item collection c1.add(new LegendItem("X")); assertNotEquals(p1, p2); } /** * Tests cloning to ensure that the cloned plot is registered as a listener * on the cloned renderer. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning4() throws CloneNotSupportedException { XYLineAndShapeRenderer r1 = new XYLineAndShapeRenderer(); XYPlot p1 = new XYPlot(null, new NumberAxis("Domain Axis"), new NumberAxis("Range Axis"), r1); XYPlot p2 = (XYPlot) p1.clone(); assertNotSame(p1, p2); assertSame(p1.getClass(), p2.getClass()); assertEquals(p1, p2); // verify that the plot is listening to the cloned renderer XYLineAndShapeRenderer r2 = (XYLineAndShapeRenderer) p2.getRenderer(); assertTrue(r2.hasListener(p2)); } /** * Confirm that cloning captures the quadrantOrigin field. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning_QuadrantOrigin() throws CloneNotSupportedException { XYPlot p1 = new XYPlot(); Point2D p = new Point2D.Double(1.2, 3.4); p1.setQuadrantOrigin(p); XYPlot p2 = (XYPlot) p1.clone(); assertNotSame(p1, p2); assertSame(p1.getClass(), p2.getClass()); assertEquals(p1, p2); assertNotSame(p2.getQuadrantOrigin(), p); } /** * Confirm that cloning captures the quadrantOrigin field. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning_QuadrantPaint() throws CloneNotSupportedException { XYPlot p1 = new XYPlot(); p1.setQuadrantPaint(3, new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); XYPlot p2 = (XYPlot) p1.clone(); assertNotSame(p1, p2); assertSame(p1.getClass(), p2.getClass()); assertEquals(p1, p2); // check for independence p1.setQuadrantPaint(1, Color.RED); assertNotEquals(p1, p2); p2.setQuadrantPaint(1, Color.RED); assertEquals(p1, p2); } /** * Renderers that belong to the plot are being cloned but they are * retaining a reference to the original plot. * @throws java.lang.CloneNotSupportedException */ @Test public void testBug2817504() throws CloneNotSupportedException { XYPlot p1 = new XYPlot(); XYLineAndShapeRenderer r1 = new XYLineAndShapeRenderer(); p1.setRenderer(r1); XYPlot p2 = (XYPlot) p1.clone(); assertNotSame(p1, p2); assertSame(p1.getClass(), p2.getClass()); assertEquals(p1, p2); // check for independence XYLineAndShapeRenderer r2 = (XYLineAndShapeRenderer) p2.getRenderer(); assertSame(r2.getPlot(), p2); } /** * Tests the independence of the clones. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloneIndependence() throws CloneNotSupportedException { XYPlot p1 = new XYPlot(null, new NumberAxis("Domain Axis"), new NumberAxis("Range Axis"), new StandardXYItemRenderer()); p1.setDomainAxis(1, new NumberAxis("Domain Axis 2")); p1.setDomainAxisLocation(1, AxisLocation.BOTTOM_OR_LEFT); p1.setRangeAxis(1, new NumberAxis("Range Axis 2")); p1.setRangeAxisLocation(1, AxisLocation.TOP_OR_RIGHT); p1.setRenderer(1, new XYBarRenderer()); XYPlot p2 = (XYPlot) p1.clone(); assertEquals(p1, p2); p1.getDomainAxis().setLabel("Label"); assertNotEquals(p1, p2); p2.getDomainAxis().setLabel("Label"); assertEquals(p1, p2); p1.getDomainAxis(1).setLabel("S1"); assertNotEquals(p1, p2); p2.getDomainAxis(1).setLabel("S1"); assertEquals(p1, p2); p1.setDomainAxisLocation(1, AxisLocation.TOP_OR_RIGHT); assertNotEquals(p1, p2); p2.setDomainAxisLocation(1, AxisLocation.TOP_OR_RIGHT); assertEquals(p1, p2); p1.mapDatasetToDomainAxis(2, 1); assertNotEquals(p1, p2); p2.mapDatasetToDomainAxis(2, 1); assertEquals(p1, p2); p1.getRangeAxis().setLabel("Label"); assertNotEquals(p1, p2); p2.getRangeAxis().setLabel("Label"); assertEquals(p1, p2); p1.getRangeAxis(1).setLabel("S1"); assertNotEquals(p1, p2); p2.getRangeAxis(1).setLabel("S1"); assertEquals(p1, p2); p1.setRangeAxisLocation(1, AxisLocation.TOP_OR_LEFT); assertNotEquals(p1, p2); p2.setRangeAxisLocation(1, AxisLocation.TOP_OR_LEFT); assertEquals(p1, p2); p1.mapDatasetToRangeAxis(2, 1); assertNotEquals(p1, p2); p2.mapDatasetToRangeAxis(2, 1); assertEquals(p1, p2); } /** * Setting a null renderer should be allowed, but is generating a null * pointer exception in 0.9.7. */ @Test public void testSetNullRenderer() { boolean failed = false; try { XYPlot plot = new XYPlot(null, new NumberAxis("X"), new NumberAxis("Y"), null); plot.setRenderer(null); } catch (Exception e) { failed = true; } assertFalse(failed); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization1() { XYDataset data = new XYSeriesCollection(); NumberAxis domainAxis = new NumberAxis("Domain"); NumberAxis rangeAxis = new NumberAxis("Range"); StandardXYItemRenderer renderer = new StandardXYItemRenderer(); XYPlot p1 = new XYPlot(data, domainAxis, rangeAxis, renderer); XYPlot p2 = TestUtils.serialised(p1); assertEquals(p1, p2); } /** * Serialize an instance, restore it, and check for equality. This test * uses a {@link DateAxis} and a {@link StandardXYToolTipGenerator}. */ @Test public void testSerialization2() { IntervalXYDataset data1 = createDataset1(); XYItemRenderer renderer1 = new XYBarRenderer(0.20); renderer1.setDefaultToolTipGenerator( StandardXYToolTipGenerator.getTimeSeriesInstance()); XYPlot p1 = new XYPlot(data1, new DateAxis("Date"), null, renderer1); XYPlot p2 = TestUtils.serialised(p1); assertEquals(p1, p2); } /** * Problem to reproduce a bug in serialization. The bug (first reported * against the {@link org.jfree.chart.plot.CategoryPlot} class) is a null * pointer exception that occurs when drawing a plot after deserialization. * It is caused by four temporary storage structures (axesAtTop, * axesAtBottom, axesAtLeft and axesAtRight - all initialized as empty * lists in the constructor) not being initialized by the readObject() * method following deserialization. This test has been written to * reproduce the bug (now fixed). */ @Test public void testSerialization3() { XYSeriesCollection dataset = new XYSeriesCollection(); JFreeChart chart = ChartFactory.createXYLineChart("Test Chart", "Domain Axis", "Range Axis", dataset); JFreeChart chart2 = TestUtils.serialised(chart); assertEquals(chart, chart2); try { chart2.createBufferedImage(300, 200); } catch (Exception e) { fail("No exception should be thrown."); } } /** * A test to reproduce a bug in serialization: the domain and/or range * markers for a plot are not being serialized. */ @Test public void testSerialization4() { XYSeriesCollection dataset = new XYSeriesCollection(); JFreeChart chart = ChartFactory.createXYLineChart("Test Chart", "Domain Axis", "Range Axis", dataset); XYPlot plot = (XYPlot) chart.getPlot(); plot.addDomainMarker(new ValueMarker(1.0), Layer.FOREGROUND); plot.addDomainMarker(new IntervalMarker(2.0, 3.0), Layer.BACKGROUND); plot.addRangeMarker(new ValueMarker(4.0), Layer.FOREGROUND); plot.addRangeMarker(new IntervalMarker(5.0, 6.0), Layer.BACKGROUND); JFreeChart chart2 = TestUtils.serialised(chart); assertEquals(chart, chart2); try { chart2.createBufferedImage(300, 200); } catch (Exception e) { fail("No exception should be thrown."); } } /** * Tests a bug where the plot is no longer registered as a listener * with the dataset(s) and axes after deserialization. See patch 1209475 * at SourceForge. */ @Test public void testSerialization5() { XYSeriesCollection dataset1 = new XYSeriesCollection(); NumberAxis domainAxis1 = new NumberAxis("Domain 1"); NumberAxis rangeAxis1 = new NumberAxis("Range 1"); StandardXYItemRenderer renderer1 = new StandardXYItemRenderer(); XYPlot p1 = new XYPlot(dataset1, domainAxis1, rangeAxis1, renderer1); NumberAxis domainAxis2 = new NumberAxis("Domain 2"); NumberAxis rangeAxis2 = new NumberAxis("Range 2"); StandardXYItemRenderer renderer2 = new StandardXYItemRenderer(); XYSeriesCollection dataset2 = new XYSeriesCollection(); p1.setDataset(1, dataset2); p1.setDomainAxis(1, domainAxis2); p1.setRangeAxis(1, rangeAxis2); p1.setRenderer(1, renderer2); XYPlot p2 = TestUtils.serialised(p1); assertEquals(p1, p2); // now check that all datasets, renderers and axes are being listened // too... NumberAxis domainAxisA = (NumberAxis) p2.getDomainAxis(0); NumberAxis rangeAxisA = (NumberAxis) p2.getRangeAxis(0); XYSeriesCollection datasetA = (XYSeriesCollection) p2.getDataset(0); StandardXYItemRenderer rendererA = (StandardXYItemRenderer) p2.getRenderer(0); NumberAxis domainAxisB = (NumberAxis) p2.getDomainAxis(1); NumberAxis rangeAxisB = (NumberAxis) p2.getRangeAxis(1); XYSeriesCollection datasetB = (XYSeriesCollection) p2.getDataset(1); StandardXYItemRenderer rendererB = (StandardXYItemRenderer) p2.getRenderer(1); assertTrue(datasetA.hasListener(p2)); assertTrue(domainAxisA.hasListener(p2)); assertTrue(rangeAxisA.hasListener(p2)); assertTrue(rendererA.hasListener(p2)); assertTrue(datasetB.hasListener(p2)); assertTrue(domainAxisB.hasListener(p2)); assertTrue(rangeAxisB.hasListener(p2)); assertTrue(rendererB.hasListener(p2)); } /** * Some checks for the getRendererForDataset() method. */ @Test public void testGetRendererForDataset() { XYDataset d0 = new XYSeriesCollection(); XYDataset d1 = new XYSeriesCollection(); XYDataset d2 = new XYSeriesCollection(); XYDataset d3 = new XYSeriesCollection(); // not used by plot XYItemRenderer r0 = new XYLineAndShapeRenderer(); XYItemRenderer r2 = new XYLineAndShapeRenderer(); XYPlot plot = new XYPlot(); plot.setDataset(0, d0); plot.setDataset(1, d1); plot.setDataset(2, d2); plot.setRenderer(0, r0); // no renderer 1 plot.setRenderer(2, r2); assertEquals(r0, plot.getRendererForDataset(d0)); assertEquals(r0, plot.getRendererForDataset(d1)); assertEquals(r2, plot.getRendererForDataset(d2)); assertNull(plot.getRendererForDataset(d3)); assertNull(plot.getRendererForDataset(null)); } /** * Some checks for the getLegendItems() method. */ @Test public void testGetLegendItems() { // check the case where there is a secondary dataset that doesn't // have a renderer (i.e. falls back to renderer 0) XYDataset d0 = createDataset1(); XYDataset d1 = createDataset2(); XYItemRenderer r0 = new XYLineAndShapeRenderer(); XYPlot plot = new XYPlot(); plot.setDataset(0, d0); plot.setDataset(1, d1); plot.setRenderer(0, r0); LegendItemCollection items = plot.getLegendItems(); assertEquals(2, items.getItemCount()); } /** * Creates a sample dataset. * * @return Series 1. */ private IntervalXYDataset createDataset1() { // create dataset 1... TimeSeries series1 = new TimeSeries("Series 1"); series1.add(new Day(1, MonthConstants.MARCH, 2002), 12353.3); series1.add(new Day(2, MonthConstants.MARCH, 2002), 13734.4); series1.add(new Day(3, MonthConstants.MARCH, 2002), 14525.3); series1.add(new Day(4, MonthConstants.MARCH, 2002), 13984.3); series1.add(new Day(5, MonthConstants.MARCH, 2002), 12999.4); series1.add(new Day(6, MonthConstants.MARCH, 2002), 14274.3); series1.add(new Day(7, MonthConstants.MARCH, 2002), 15943.5); series1.add(new Day(8, MonthConstants.MARCH, 2002), 14845.3); series1.add(new Day(9, MonthConstants.MARCH, 2002), 14645.4); series1.add(new Day(10, MonthConstants.MARCH, 2002), 16234.6); series1.add(new Day(11, MonthConstants.MARCH, 2002), 17232.3); series1.add(new Day(12, MonthConstants.MARCH, 2002), 14232.2); series1.add(new Day(13, MonthConstants.MARCH, 2002), 13102.2); series1.add(new Day(14, MonthConstants.MARCH, 2002), 14230.2); series1.add(new Day(15, MonthConstants.MARCH, 2002), 11235.2); TimeSeriesCollection collection = new TimeSeriesCollection(series1); return collection; } /** * Creates a sample dataset. * * @return A sample dataset. */ private XYDataset createDataset2() { // create dataset 1... XYSeries series = new XYSeries("Series 2"); XYSeriesCollection collection = new XYSeriesCollection(series); return collection; } /** * A test for a bug where setting the renderer doesn't register the plot * as a RendererChangeListener. */ @Test public void testSetRenderer() { XYPlot plot = new XYPlot(); XYItemRenderer renderer = new XYLineAndShapeRenderer(); plot.setRenderer(renderer); // now make a change to the renderer and see if it triggers a plot // change event... MyPlotChangeListener listener = new MyPlotChangeListener(); plot.addChangeListener(listener); renderer.setSeriesPaint(0, Color.BLACK); assertNotNull(listener.getEvent()); } /** * Some checks for the removeAnnotation() method. */ @Test public void testRemoveAnnotation() { XYPlot plot = new XYPlot(); XYTextAnnotation a1 = new XYTextAnnotation("X", 1.0, 2.0); XYTextAnnotation a2 = new XYTextAnnotation("X", 3.0, 4.0); XYTextAnnotation a3 = new XYTextAnnotation("X", 1.0, 2.0); plot.addAnnotation(a1); plot.addAnnotation(a2); plot.addAnnotation(a3); plot.removeAnnotation(a2); XYTextAnnotation x = (XYTextAnnotation) plot.getAnnotations().get(0); assertEquals(x, a1); // now remove a3, but since a3.equals(a1), this will in fact remove // a1... assertEquals(a1, a3); plot.removeAnnotation(a3); // actually removes a1 x = (XYTextAnnotation) plot.getAnnotations().get(0); assertEquals(x, a3); } /** * Some tests for the addDomainMarker() method(s). */ @Test public void testAddDomainMarker() { XYPlot plot = new XYPlot(); Marker m = new ValueMarker(1.0); plot.addDomainMarker(m); List listeners = Arrays.asList(m.getListeners( MarkerChangeListener.class)); assertTrue(listeners.contains(plot)); plot.clearDomainMarkers(); listeners = Arrays.asList(m.getListeners(MarkerChangeListener.class)); assertFalse(listeners.contains(plot)); } /** * Some tests for the addRangeMarker() method(s). */ @Test public void testAddRangeMarker() { XYPlot plot = new XYPlot(); Marker m = new ValueMarker(1.0); plot.addRangeMarker(m); List listeners = Arrays.asList(m.getListeners( MarkerChangeListener.class)); assertTrue(listeners.contains(plot)); plot.clearRangeMarkers(); listeners = Arrays.asList(m.getListeners(MarkerChangeListener.class)); assertFalse(listeners.contains(plot)); } /** * A test for bug 1654215 (where a renderer is added to the plot without * a corresponding dataset and it throws an exception at drawing time). */ @Test public void test1654215() { DefaultXYDataset dataset = new DefaultXYDataset(); JFreeChart chart = ChartFactory.createXYLineChart("Title", "X", "Y", dataset, PlotOrientation.VERTICAL, true, false, false); XYPlot plot = (XYPlot) chart.getPlot(); plot.setRenderer(1, new XYLineAndShapeRenderer()); try { BufferedImage image = new BufferedImage(200 , 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); chart.draw(g2, new Rectangle2D.Double(0, 0, 200, 100), null, null); g2.dispose(); } catch (Exception e) { fail("No exception should be thrown."); } } /** * A test for drawing range grid lines when there is no primary renderer. * In 1.0.4, this is throwing a NullPointerException. */ @Test public void testDrawRangeGridlines() { DefaultXYDataset dataset = new DefaultXYDataset(); JFreeChart chart = ChartFactory.createXYLineChart("Title", "X", "Y", dataset, PlotOrientation.VERTICAL, true, false, false); XYPlot plot = (XYPlot) chart.getPlot(); plot.setRenderer(null); try { BufferedImage image = new BufferedImage(200 , 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); chart.draw(g2, new Rectangle2D.Double(0, 0, 200, 100), null, null); g2.dispose(); } catch (Exception e) { fail("No exception should be thrown."); } } /** * A test for drawing a plot where a series has zero items. With * JFreeChart 1.0.5+cvs this was throwing an exception at one point. */ @Test public void testDrawSeriesWithZeroItems() { DefaultXYDataset dataset = new DefaultXYDataset(); dataset.addSeries("Series 1", new double[][] {{1.0, 2.0}, {3.0, 4.0}}); dataset.addSeries("Series 2", new double[][] {{}, {}}); JFreeChart chart = ChartFactory.createXYLineChart("Title", "X", "Y", dataset, PlotOrientation.VERTICAL, true, false, false); try { BufferedImage image = new BufferedImage(200 , 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); chart.draw(g2, new Rectangle2D.Double(0, 0, 200, 100), null, null); g2.dispose(); } catch (Exception e) { fail("No exception should be thrown."); } } /** * Check that removing a marker that isn't assigned to the plot returns * false. */ @Test public void testRemoveDomainMarker() { XYPlot plot = new XYPlot(); assertFalse(plot.removeDomainMarker(new ValueMarker(0.5))); } /** * Check that removing a marker that isn't assigned to the plot returns * false. */ @Test public void testRemoveRangeMarker() { XYPlot plot = new XYPlot(); assertFalse(plot.removeRangeMarker(new ValueMarker(0.5))); } /** * Some tests for the getDomainAxisForDataset() method. */ @Test public void testGetDomainAxisForDataset() { XYDataset dataset = new XYSeriesCollection(); NumberAxis xAxis = new NumberAxis("X"); NumberAxis yAxis = new NumberAxis("Y"); XYItemRenderer renderer = new DefaultXYItemRenderer(); XYPlot plot = new XYPlot(dataset, xAxis, yAxis, renderer); assertEquals(xAxis, plot.getDomainAxisForDataset(0)); // should get IllegalArgumentException for negative index boolean pass = false; try { plot.getDomainAxisForDataset(-1); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); // if multiple axes are mapped, the first in the list should be // returned... NumberAxis xAxis2 = new NumberAxis("X2"); plot.setDomainAxis(1, xAxis2); assertEquals(xAxis, plot.getDomainAxisForDataset(0)); plot.mapDatasetToDomainAxis(0, 1); assertEquals(xAxis2, plot.getDomainAxisForDataset(0)); List axisIndices = Arrays.asList(0, 1); plot.mapDatasetToDomainAxes(0, axisIndices); assertEquals(xAxis, plot.getDomainAxisForDataset(0)); axisIndices = Arrays.asList(1, 2); plot.mapDatasetToDomainAxes(0, axisIndices); assertEquals(xAxis2, plot.getDomainAxisForDataset(0)); } /** * Some tests for the getRangeAxisForDataset() method. */ @Test public void testGetRangeAxisForDataset() { XYDataset dataset = new XYSeriesCollection(); NumberAxis xAxis = new NumberAxis("X"); NumberAxis yAxis = new NumberAxis("Y"); XYItemRenderer renderer = new DefaultXYItemRenderer(); XYPlot plot = new XYPlot(dataset, xAxis, yAxis, renderer); assertEquals(yAxis, plot.getRangeAxisForDataset(0)); // should get IllegalArgumentException for negative index boolean pass = false; try { plot.getRangeAxisForDataset(-1); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); // if multiple axes are mapped, the first in the list should be // returned... NumberAxis yAxis2 = new NumberAxis("Y2"); plot.setRangeAxis(1, yAxis2); assertEquals(yAxis, plot.getRangeAxisForDataset(0)); plot.mapDatasetToRangeAxis(0, 1); assertEquals(yAxis2, plot.getRangeAxisForDataset(0)); List axisIndices = Arrays.asList(0, 1); plot.mapDatasetToRangeAxes(0, axisIndices); assertEquals(yAxis, plot.getRangeAxisForDataset(0)); axisIndices = Arrays.asList(1, 2); plot.mapDatasetToRangeAxes(0, axisIndices); assertEquals(yAxis2, plot.getRangeAxisForDataset(0)); } /** * Datasets are now stored in a Map, and it should be possible to assign * them an arbitrary key (index). */ @Test public void testDatasetIndices() { XYDataset dataset = new XYSeriesCollection(); NumberAxis xAxis = new NumberAxis("X"); NumberAxis yAxis = new NumberAxis("Y"); XYItemRenderer renderer = new DefaultXYItemRenderer(); XYPlot plot = new XYPlot(dataset, xAxis, yAxis, renderer); assertEquals(dataset, plot.getDataset(0)); XYSeriesCollection dataset2 = new XYSeriesCollection(); dataset2.addSeries(new XYSeries("Series in dataset 2")); // we should be able to give a dataset an arbitrary index plot.setDataset(99, dataset2); assertEquals(2, plot.getDatasetCount()); assertEquals(dataset2, plot.getDataset(99)); assertEquals(0, plot.indexOf(dataset)); assertEquals(99, plot.indexOf(dataset2)); } @Test public void testAxisIndices() { XYDataset dataset = new XYSeriesCollection(); NumberAxis xAxis = new NumberAxis("X"); NumberAxis yAxis = new NumberAxis("Y"); XYItemRenderer renderer = new DefaultXYItemRenderer(); XYPlot plot = new XYPlot(dataset, xAxis, yAxis, renderer); assertEquals(xAxis, plot.getDomainAxis(0)); assertEquals(yAxis, plot.getRangeAxis(0)); NumberAxis xAxis2 = new NumberAxis("X2"); plot.setDomainAxis(99, xAxis2); assertEquals(xAxis2, plot.getDomainAxis(99)); NumberAxis yAxis2 = new NumberAxis("Y2"); plot.setRangeAxis(99, yAxis2); assertEquals(yAxis2, plot.getRangeAxis(99)); } @Test public void testAxisLocationIndices() { XYDataset dataset = new XYSeriesCollection(); NumberAxis xAxis = new NumberAxis("X"); NumberAxis yAxis = new NumberAxis("Y"); XYItemRenderer renderer = new DefaultXYItemRenderer(); XYPlot plot = new XYPlot(dataset, xAxis, yAxis, renderer); NumberAxis xAxis2 = new NumberAxis("X2"); NumberAxis yAxis2 = new NumberAxis("Y2"); plot.setDomainAxis(99, xAxis2); plot.setRangeAxis(99, yAxis2); plot.setDomainAxisLocation(99, AxisLocation.BOTTOM_OR_RIGHT); assertEquals(AxisLocation.BOTTOM_OR_RIGHT, plot.getDomainAxisLocation(99)); plot.setRangeAxisLocation(99, AxisLocation.BOTTOM_OR_LEFT); assertEquals(AxisLocation.BOTTOM_OR_LEFT, plot.getRangeAxisLocation(99)); } @Test public void testRendererIndices() { XYDataset dataset = new XYSeriesCollection(); NumberAxis xAxis = new NumberAxis("X"); NumberAxis yAxis = new NumberAxis("Y"); XYItemRenderer renderer = new DefaultXYItemRenderer(); XYPlot plot = new XYPlot(dataset, xAxis, yAxis, renderer); assertEquals(renderer, plot.getRenderer(0)); // we should be able to give a renderer an arbitrary index XYLineAndShapeRenderer renderer2 = new XYLineAndShapeRenderer(); plot.setRenderer(20, renderer2); assertEquals(2, plot.getRendererCount()); assertEquals(renderer2, plot.getRenderer(20)); assertEquals(20, plot.getIndexOf(renderer2)); } @Test public void testGetRendererForDataset2() { XYDataset dataset = new XYSeriesCollection(); NumberAxis xAxis = new NumberAxis("X"); NumberAxis yAxis = new NumberAxis("Y"); XYItemRenderer renderer = new DefaultXYItemRenderer(); XYPlot plot = new XYPlot(dataset, xAxis, yAxis, renderer); // add a second dataset XYSeriesCollection dataset2 = new XYSeriesCollection(); dataset2.addSeries(new XYSeries("Series in dataset 2")); plot.setDataset(99, dataset2); // by default, the renderer with index 0 is used assertEquals(renderer, plot.getRendererForDataset(dataset2)); // add a second renderer with the same index as dataset2, now it will // be used XYLineAndShapeRenderer renderer2 = new XYLineAndShapeRenderer(); plot.setRenderer(99, renderer); assertEquals(renderer2, plot.getRendererForDataset(dataset2)); } @Test public void testMapDatasetToDomainAxis() { XYDataset dataset = new XYSeriesCollection(); NumberAxis xAxis = new NumberAxis("X"); NumberAxis yAxis = new NumberAxis("Y"); XYItemRenderer renderer = new DefaultXYItemRenderer(); XYPlot plot = new XYPlot(dataset, xAxis, yAxis, renderer); NumberAxis xAxis2 = new NumberAxis("X2"); plot.setDomainAxis(11, xAxis2); // add a second dataset XYSeriesCollection dataset2 = new XYSeriesCollection(); dataset2.addSeries(new XYSeries("Series in dataset 2")); plot.setDataset(99, dataset); assertEquals(xAxis, plot.getDomainAxisForDataset(99)); // now map the dataset to the second xAxis plot.mapDatasetToDomainAxis(99, 11); assertEquals(xAxis2, plot.getDomainAxisForDataset(99)); } @Test public void testMapDatasetToRangeAxis() { XYDataset dataset = new XYSeriesCollection(); NumberAxis xAxis = new NumberAxis("X"); NumberAxis yAxis = new NumberAxis("Y"); XYItemRenderer renderer = new DefaultXYItemRenderer(); XYPlot plot = new XYPlot(dataset, xAxis, yAxis, renderer); NumberAxis yAxis2 = new NumberAxis("Y2"); plot.setRangeAxis(22, yAxis2); // add a second dataset XYSeriesCollection dataset2 = new XYSeriesCollection(); dataset2.addSeries(new XYSeries("Series in dataset 2")); plot.setDataset(99, dataset); assertEquals(yAxis, plot.getRangeAxisForDataset(99)); // now map the dataset to the second xAxis plot.mapDatasetToRangeAxis(99, 22); assertEquals(yAxis2, plot.getRangeAxisForDataset(99)); } @Test public void testDomainMarkerIndices() { XYDataset dataset = new XYSeriesCollection(); NumberAxis xAxis = new NumberAxis("X"); NumberAxis yAxis = new NumberAxis("Y"); XYItemRenderer renderer = new DefaultXYItemRenderer(); XYPlot plot = new XYPlot(dataset, xAxis, yAxis, renderer); // add a second dataset, plotted against a second x axis XYSeriesCollection dataset2 = new XYSeriesCollection(); dataset2.addSeries(new XYSeries("Series in dataset 2")); plot.setDataset(99, dataset); NumberAxis xAxis2 = new NumberAxis("X2"); plot.setDomainAxis(1, xAxis2); XYLineAndShapeRenderer renderer2 = new XYLineAndShapeRenderer(); plot.setRenderer(99, renderer2); plot.mapDatasetToDomainAxis(99, 1); ValueMarker xMarker1 = new ValueMarker(123); plot.addDomainMarker(99, xMarker1, Layer.FOREGROUND); assertTrue(plot.getDomainMarkers(99, Layer.FOREGROUND).contains( xMarker1)); } @Test public void testRangeMarkerIndices() { XYDataset dataset = new XYSeriesCollection(); NumberAxis xAxis = new NumberAxis("X"); NumberAxis yAxis = new NumberAxis("Y"); XYItemRenderer renderer = new DefaultXYItemRenderer(); XYPlot plot = new XYPlot(dataset, xAxis, yAxis, renderer); // add a second dataset, plotted against a second axis XYSeriesCollection dataset2 = new XYSeriesCollection(); dataset2.addSeries(new XYSeries("Series in dataset 2")); plot.setDataset(99, dataset); NumberAxis yAxis2 = new NumberAxis("Y2"); plot.setRangeAxis(1, yAxis2); XYLineAndShapeRenderer renderer2 = new XYLineAndShapeRenderer(); plot.setRenderer(99, renderer2); plot.mapDatasetToRangeAxis(99, 1); ValueMarker yMarker1 = new ValueMarker(123); plot.addRangeMarker(99, yMarker1, Layer.FOREGROUND); assertTrue(plot.getRangeMarkers(99, Layer.FOREGROUND).contains(yMarker1)); } /** * Some tests for the getDataRange() method. */ @Test public void testGetDataRange() { XYSeriesCollection dataset = new XYSeriesCollection(); NumberAxis xAxis = new NumberAxis("X"); NumberAxis yAxis = new NumberAxis("Y"); XYItemRenderer renderer = new DefaultXYItemRenderer(); XYPlot plot = new XYPlot(dataset, xAxis, yAxis, renderer); assertNull(plot.getDataRange(xAxis)); assertNull(plot.getDataRange(yAxis)); XYSeries s1 = new XYSeries("S1"); s1.add(1.0, 2.0); dataset.addSeries(s1); assertEquals(new Range(1.0, 1.0), plot.getDataRange(xAxis)); assertEquals(new Range(2.0, 2.0), plot.getDataRange(yAxis)); s1.add(5.0, null); assertEquals(new Range(1.0, 5.0), plot.getDataRange(xAxis)); assertEquals(new Range(2.0, 2.0), plot.getDataRange(yAxis)); s1.add(6.0, Double.NaN); assertEquals(new Range(1.0, 6.0), plot.getDataRange(xAxis)); assertEquals(new Range(2.0, 2.0), plot.getDataRange(yAxis)); } /** * Some tests for the getDataRange() method. */ @Test public void testGetDataRangeWithMultipleDatasets() { XYSeriesCollection dataset1 = new XYSeriesCollection(); XYSeriesCollection dataset2 = new XYSeriesCollection(); NumberAxis xAxis = new NumberAxis("X"); NumberAxis yAxis = new NumberAxis("Y"); XYItemRenderer renderer = new DefaultXYItemRenderer(); XYPlot plot = new XYPlot(dataset1, xAxis, yAxis, renderer); plot.setDataset(1, dataset2); plot.mapDatasetToDomainAxis(1, 0); plot.mapDatasetToRangeAxis(1, 0); assertNull(plot.getDataRange(xAxis)); assertNull(plot.getDataRange(yAxis)); XYSeries s1 = new XYSeries("S1"); s1.add(1.0, 2.0); dataset1.addSeries(s1); assertEquals(new Range(1.0, 1.0), plot.getDataRange(xAxis)); assertEquals(new Range(2.0, 2.0), plot.getDataRange(yAxis)); XYSeries s2 = new XYSeries("S2"); s2.add(5.0, 10.0); dataset2.addSeries(s2); assertEquals(new Range(1.0, 5.0), plot.getDataRange(xAxis)); assertEquals(new Range(2.0, 10.0), plot.getDataRange(yAxis)); s2.add(6.0, Double.NaN); assertEquals(new Range(1.0, 6.0), plot.getDataRange(xAxis)); assertEquals(new Range(2.0, 10.0), plot.getDataRange(yAxis)); s2.add(Double.NaN, 0.5); assertEquals(new Range(1.0, 6.0), plot.getDataRange(xAxis)); assertEquals(new Range(2.0, 10.0), plot.getDataRange(yAxis)); // only y-values for items in the x-range } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/dial/000077500000000000000000000000001463604235500250005ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/dial/AbstractDialLayerTest.java000066400000000000000000000062251463604235500320420ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * AbstractDialLayerTest.java * -------------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot.dial; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link AbstractDialLayer} class. */ public class AbstractDialLayerTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { DialCap c1 = new DialCap(); DialCap c2 = new DialCap(); assertEquals(c1, c2); // visible c1.setVisible(false); assertNotEquals(c1, c2); c2.setVisible(false); assertEquals(c1, c2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { // test a default instance DialCap c1 = new DialCap(); DialCap c2 = (DialCap) c1.clone(); assertNotSame(c1, c2); assertSame(c1.getClass(), c2.getClass()); assertEquals(c1, c2); // check that the listener lists are independent MyDialLayerChangeListener l1 = new MyDialLayerChangeListener(); c1.addChangeListener(l1); assertTrue(c1.hasListener(l1)); assertFalse(c2.hasListener(l1)); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { // test a default instance DialCap c1 = new DialCap(); DialCap c2 = TestUtils.serialised(c1); assertEquals(c1, c2); // check that the listener lists are independent MyDialLayerChangeListener l1 = new MyDialLayerChangeListener(); c1.addChangeListener(l1); assertTrue(c1.hasListener(l1)); assertFalse(c2.hasListener(l1)); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/dial/ArcDialFrameTest.java000066400000000000000000000113031463604235500307530ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * ArcDialFrameTest.java * --------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot.dial; import java.awt.BasicStroke; import java.awt.Color; import java.awt.GradientPaint; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link ArcDialFrame} class. */ public class ArcDialFrameTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { ArcDialFrame f1 = new ArcDialFrame(); ArcDialFrame f2 = new ArcDialFrame(); assertEquals(f1, f2); // background paint f1.setBackgroundPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW)); assertNotEquals(f1, f2); f2.setBackgroundPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW)); assertEquals(f1, f2); // foreground paint f1.setForegroundPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW)); assertNotEquals(f1, f2); f2.setForegroundPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW)); assertEquals(f1, f2); // stroke f1.setStroke(new BasicStroke(1.1f)); assertNotEquals(f1, f2); f2.setStroke(new BasicStroke(1.1f)); assertEquals(f1, f2); // inner radius f1.setInnerRadius(0.11); assertNotEquals(f1, f2); f2.setInnerRadius(0.11); assertEquals(f1, f2); // outer radius f1.setOuterRadius(0.88); assertNotEquals(f1, f2); f2.setOuterRadius(0.88); assertEquals(f1, f2); // startAngle f1.setStartAngle(99); assertNotEquals(f1, f2); f2.setStartAngle(99); assertEquals(f1, f2); // extent f1.setExtent(33); assertNotEquals(f1, f2); f2.setExtent(33); assertEquals(f1, f2); // check an inherited attribute f1.setVisible(false); assertNotEquals(f1, f2); f2.setVisible(false); assertEquals(f1, f2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { ArcDialFrame f1 = new ArcDialFrame(); ArcDialFrame f2 = new ArcDialFrame(); assertEquals(f1, f2); int h1 = f1.hashCode(); int h2 = f2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { ArcDialFrame f1 = new ArcDialFrame(); ArcDialFrame f2 = (ArcDialFrame) f1.clone(); assertNotSame(f1, f2); assertSame(f1.getClass(), f2.getClass()); assertEquals(f1, f2); // check that the listener lists are independent MyDialLayerChangeListener l1 = new MyDialLayerChangeListener(); f1.addChangeListener(l1); assertTrue(f1.hasListener(l1)); assertFalse(f2.hasListener(l1)); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { ArcDialFrame f1 = new ArcDialFrame(); ArcDialFrame f2 = TestUtils.serialised(f1); assertEquals(f1, f2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/dial/DialBackgroundTest.java000066400000000000000000000120241463604235500313530ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * DialBackgroundTest.java * ----------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot.dial; import java.awt.Color; import java.awt.GradientPaint; import org.jfree.chart.TestUtils; import org.jfree.chart.ui.GradientPaintTransformType; import org.jfree.chart.ui.StandardGradientPaintTransformer; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link DialBackground} class. */ public class DialBackgroundTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { DialBackground b1 = new DialBackground(); DialBackground b2 = new DialBackground(); assertEquals(b1, b2); // paint b1.setPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW)); assertNotEquals(b1, b2); b2.setPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW)); assertEquals(b1, b2); // gradient paint transformer b1.setGradientPaintTransformer(new StandardGradientPaintTransformer( GradientPaintTransformType.CENTER_VERTICAL)); assertNotEquals(b1, b2); b2.setGradientPaintTransformer(new StandardGradientPaintTransformer( GradientPaintTransformType.CENTER_VERTICAL)); assertEquals(b1, b2); // check an inherited attribute b1.setVisible(false); assertNotEquals(b1, b2); b2.setVisible(false); assertEquals(b1, b2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { DialBackground b1 = new DialBackground(Color.RED); DialBackground b2 = new DialBackground(Color.RED); assertEquals(b1, b2); int h1 = b1.hashCode(); int h2 = b2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { // test default instance DialBackground b1 = new DialBackground(); DialBackground b2 = (DialBackground) b1.clone(); assertNotSame(b1, b2); assertSame(b1.getClass(), b2.getClass()); assertEquals(b1, b2); // test a customised instance b1 = new DialBackground(); b1.setPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.GREEN)); b1.setGradientPaintTransformer(new StandardGradientPaintTransformer( GradientPaintTransformType.CENTER_VERTICAL)); b2 = (DialBackground) b1.clone(); assertNotSame(b1, b2); assertSame(b1.getClass(), b2.getClass()); assertEquals(b1, b2); // check that the listener lists are independent MyDialLayerChangeListener l1 = new MyDialLayerChangeListener(); b1.addChangeListener(l1); assertTrue(b1.hasListener(l1)); assertFalse(b2.hasListener(l1)); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { // test a default instance DialBackground b1 = new DialBackground(); DialBackground b2 = TestUtils.serialised(b1); assertEquals(b1, b2); // test a customised instance b1 = new DialBackground(); b1.setPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.GREEN)); b1.setGradientPaintTransformer(new StandardGradientPaintTransformer( GradientPaintTransformType.CENTER_VERTICAL)); b2 = TestUtils.serialised(b1); assertEquals(b1, b2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/dial/DialCapTest.java000066400000000000000000000120761463604235500300060ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * DialCapTest.java * ---------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot.dial; import java.awt.BasicStroke; import java.awt.Color; import java.awt.GradientPaint; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link DialCap} class. */ public class DialCapTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { DialCap c1 = new DialCap(); DialCap c2 = new DialCap(); assertEquals(c1, c2); // radius c1.setRadius(0.5); assertNotEquals(c1, c2); c2.setRadius(0.5); assertEquals(c1, c2); // fill paint c1.setFillPaint(new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.GREEN)); assertNotEquals(c1, c2); c2.setFillPaint(new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.GREEN)); // outline paint c1.setOutlinePaint(new GradientPaint(1.0f, 2.0f, Color.WHITE, 3.0f, 4.0f, Color.GRAY)); assertNotEquals(c1, c2); c2.setOutlinePaint(new GradientPaint(1.0f, 2.0f, Color.WHITE, 3.0f, 4.0f, Color.GRAY)); assertEquals(c1, c2); // outline stroke c1.setOutlineStroke(new BasicStroke(1.1f)); assertNotEquals(c1, c2); c2.setOutlineStroke(new BasicStroke(1.1f)); assertEquals(c1, c2); // check an inherited attribute c1.setVisible(false); assertNotEquals(c1, c2); c2.setVisible(false); assertEquals(c1, c2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { DialCap c1 = new DialCap(); DialCap c2 = new DialCap(); assertEquals(c1, c2); int h1 = c1.hashCode(); int h2 = c2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { // test a default instance DialCap c1 = new DialCap(); DialCap c2 = (DialCap) c1.clone(); assertNotSame(c1, c2); assertSame(c1.getClass(), c2.getClass()); assertEquals(c1, c2); // test a customised instance c1 = new DialCap(); c1.setFillPaint(new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.GREEN)); c1.setOutlinePaint(new GradientPaint(1.0f, 2.0f, Color.WHITE, 3.0f, 4.0f, Color.GRAY)); c1.setOutlineStroke(new BasicStroke(2.0f)); c2 = (DialCap) c1.clone(); assertNotSame(c1, c2); assertSame(c1.getClass(), c2.getClass()); assertEquals(c1, c2); // check that the listener lists are independent MyDialLayerChangeListener l1 = new MyDialLayerChangeListener(); c1.addChangeListener(l1); assertTrue(c1.hasListener(l1)); assertFalse(c2.hasListener(l1)); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { // test a default instance DialCap c1 = new DialCap(); DialCap c2 = TestUtils.serialised(c1); assertEquals(c1, c2); // test a custom instance c1 = new DialCap(); c1.setFillPaint(new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.GREEN)); c1.setOutlinePaint(new GradientPaint(1.0f, 2.0f, Color.WHITE, 3.0f, 4.0f, Color.GRAY)); c1.setOutlineStroke(new BasicStroke(2.0f)); c2 = TestUtils.serialised(c1); assertEquals(c1, c2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/dial/DialPlotTest.java000066400000000000000000000200361463604235500302140ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * DialPlotTest.java * ----------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot.dial; import java.awt.Color; import java.awt.GradientPaint; import org.jfree.chart.TestUtils; import org.jfree.chart.event.PlotChangeEvent; import org.jfree.chart.event.PlotChangeListener; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link DialPlot} class. */ public class DialPlotTest implements PlotChangeListener { /** The last plot change event received. */ private PlotChangeEvent lastEvent; /** * Records the last plot change event received. * * @param event the event. */ @Override public void plotChanged(PlotChangeEvent event) { this.lastEvent = event; } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { DialPlot p1 = new DialPlot(); DialPlot p2 = new DialPlot(); assertEquals(p1, p2); // background p1.setBackground(new DialBackground(Color.GREEN)); assertNotEquals(p1, p2); p2.setBackground(new DialBackground(Color.GREEN)); assertEquals(p1, p2); p1.setBackground(null); assertNotEquals(p1, p2); p2.setBackground(null); assertEquals(p1, p2); // dial cap DialCap cap1 = new DialCap(); cap1.setFillPaint(Color.RED); p1.setCap(cap1); assertNotEquals(p1, p2); DialCap cap2 = new DialCap(); cap2.setFillPaint(Color.RED); p2.setCap(cap2); assertEquals(p1, p2); p1.setCap(null); assertNotEquals(p1, p2); p2.setCap(null); assertEquals(p1, p2); // frame StandardDialFrame f1 = new StandardDialFrame(); f1.setBackgroundPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.WHITE)); p1.setDialFrame(f1); assertNotEquals(p1, p2); StandardDialFrame f2 = new StandardDialFrame(); f2.setBackgroundPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.WHITE)); p2.setDialFrame(f2); assertEquals(p1, p2); // view p1.setView(0.2, 0.0, 0.8, 1.0); assertNotEquals(p1, p2); p2.setView(0.2, 0.0, 0.8, 1.0); assertEquals(p1, p2); // layer p1.addLayer(new StandardDialScale()); assertNotEquals(p1, p2); p2.addLayer(new StandardDialScale()); assertEquals(p1, p2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { DialPlot p1 = new DialPlot(); DialPlot p2 = new DialPlot(); assertEquals(p1, p2); int h1 = p1.hashCode(); int h2 = p2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { DialPlot p1 = new DialPlot(); DialPlot p2 = (DialPlot) p1.clone(); assertNotSame(p1, p2); assertSame(p1.getClass(), p2.getClass()); assertEquals(p1, p2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DialPlot p1 = new DialPlot(); DialPlot p2 = TestUtils.serialised(p1); assertEquals(p1, p2); } /** * Check the notification event mechanism for the dial background. */ @Test public void testBackgroundListener() { DialPlot p = new DialPlot(); DialBackground b1 = new DialBackground(Color.RED); p.setBackground(b1); p.addChangeListener(this); this.lastEvent = null; b1.setPaint(Color.BLUE); assertNotNull(this.lastEvent); DialBackground b2 = new DialBackground(Color.GREEN); p.setBackground(b2); this.lastEvent = null; b1.setPaint(Color.RED); assertNull(this.lastEvent); b2.setPaint(Color.RED); assertNotNull(this.lastEvent); } /** * Check the notification event mechanism for the dial cap. */ @Test public void testCapListener() { DialPlot p = new DialPlot(); DialCap c1 = new DialCap(); p.setCap(c1); p.addChangeListener(this); this.lastEvent = null; c1.setFillPaint(Color.RED); assertNotNull(this.lastEvent); DialCap c2 = new DialCap(); p.setCap(c2); this.lastEvent = null; c1.setFillPaint(Color.BLUE); assertNull(this.lastEvent); c2.setFillPaint(Color.GREEN); assertNotNull(this.lastEvent); } /** * Check the notification event mechanism for the dial frame. */ @Test public void testFrameListener() { DialPlot p = new DialPlot(); ArcDialFrame f1 = new ArcDialFrame(); p.setDialFrame(f1); p.addChangeListener(this); this.lastEvent = null; f1.setBackgroundPaint(Color.GRAY); assertNotNull(this.lastEvent); ArcDialFrame f2 = new ArcDialFrame(); p.setDialFrame(f2); this.lastEvent = null; f1.setBackgroundPaint(Color.BLUE); assertNull(this.lastEvent); f2.setBackgroundPaint(Color.GREEN); assertNotNull(this.lastEvent); } /** * Check the notification event mechanism for the dial scales. */ @Test public void testScaleListener() { DialPlot p = new DialPlot(); StandardDialScale s1 = new StandardDialScale(); p.addScale(0, s1); p.addChangeListener(this); this.lastEvent = null; s1.setStartAngle(22.0); assertNotNull(this.lastEvent); StandardDialScale s2 = new StandardDialScale(); p.addScale(0, s2); this.lastEvent = null; s1.setStartAngle(33.0); assertNull(this.lastEvent); s2.setStartAngle(33.0); assertNotNull(this.lastEvent); } /** * Check the notification event mechanism for a layer. */ @Test public void testLayerListener() { DialPlot p = new DialPlot(); DialBackground b1 = new DialBackground(Color.RED); p.addLayer(b1); p.addChangeListener(this); this.lastEvent = null; b1.setPaint(Color.BLUE); assertNotNull(this.lastEvent); DialBackground b2 = new DialBackground(Color.GREEN); p.addLayer(b2); this.lastEvent = null; b1.setPaint(Color.RED); assertNotNull(this.lastEvent); b2.setPaint(Color.GREEN); assertNotNull(this.lastEvent); p.removeLayer(b2); this.lastEvent = null; b2.setPaint(Color.RED); assertNull(this.lastEvent); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/dial/DialPointerTest.java000066400000000000000000000115641463604235500307240ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * DialPointerTest.java * -------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot.dial; import java.awt.BasicStroke; import java.awt.Color; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link DialPointer} class. */ public class DialPointerTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { DialPointer i1 = new DialPointer.Pin(1); DialPointer i2 = new DialPointer.Pin(1); assertEquals(i1, i2); // dataset index i1 = new DialPointer.Pin(2); assertNotEquals(i1, i2); i2 = new DialPointer.Pin(2); assertEquals(i1, i2); // check an inherited attribute i1.setVisible(false); assertNotEquals(i1, i2); i2.setVisible(false); assertEquals(i1, i2); } /** * Check the equals() method for the DialPointer.Pin class. */ @Test public void testEqualsPin() { DialPointer.Pin p1 = new DialPointer.Pin(); DialPointer.Pin p2 = new DialPointer.Pin(); assertEquals(p1, p2); p1.setPaint(Color.GREEN); assertNotEquals(p1, p2); p2.setPaint(Color.GREEN); assertEquals(p1, p2); BasicStroke s = new BasicStroke(4.4f); p1.setStroke(s); assertNotEquals(p1, p2); p2.setStroke(s); assertEquals(p1, p2); } /** * Check the equals() method for the DialPointer.Pointer class. */ @Test public void testEqualsPointer() { DialPointer.Pointer p1 = new DialPointer.Pointer(); DialPointer.Pointer p2 = new DialPointer.Pointer(); assertEquals(p1, p2); p1.setFillPaint(Color.GREEN); assertNotEquals(p1, p2); p2.setFillPaint(Color.GREEN); assertEquals(p1, p2); p1.setOutlinePaint(Color.GREEN); assertNotEquals(p1, p2); p2.setOutlinePaint(Color.GREEN); assertEquals(p1, p2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { DialPointer i1 = new DialPointer.Pin(1); DialPointer i2 = new DialPointer.Pin(1); assertEquals(i1, i2); int h1 = i1.hashCode(); int h2 = i2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { DialPointer i1 = new DialPointer.Pin(1); DialPointer i2 = (DialPointer) i1.clone(); assertNotSame(i1, i2); assertSame(i1.getClass(), i2.getClass()); assertEquals(i1, i2); // check that the listener lists are independent MyDialLayerChangeListener l1 = new MyDialLayerChangeListener(); i1.addChangeListener(l1); assertTrue(i1.hasListener(l1)); assertFalse(i2.hasListener(l1)); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { // test a default instance DialPointer i1 = new DialPointer.Pin(1); DialPointer i2 = TestUtils.serialised(i1); assertEquals(i1, i2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization2() { DialPointer i1 = new DialPointer.Pointer(1); DialPointer i2 = TestUtils.serialised(i1); assertEquals(i1, i2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/dial/DialTextAnnotationTest.java000066400000000000000000000107571463604235500322660ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * DialTextAnnotationTest.java * --------------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot.dial; import java.awt.Color; import java.awt.Font; import java.awt.GradientPaint; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link DialTextAnnotation} class. */ public class DialTextAnnotationTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { DialTextAnnotation a1 = new DialTextAnnotation("A1"); DialTextAnnotation a2 = new DialTextAnnotation("A1"); assertEquals(a1, a2); // angle a1.setAngle(1.1); assertNotEquals(a1, a2); a2.setAngle(1.1); assertEquals(a1, a2); // radius a1.setRadius(9.9); assertNotEquals(a1, a2); a2.setRadius(9.9); assertEquals(a1, a2); // font Font f = new Font("SansSerif", Font.PLAIN, 14); a1.setFont(f); assertNotEquals(a1, a2); a2.setFont(f); assertEquals(a1, a2); // paint a1.setPaint(Color.RED); assertNotEquals(a1, a2); a2.setPaint(Color.RED); assertEquals(a1, a2); // label a1.setLabel("ABC"); assertNotEquals(a1, a2); a2.setLabel("ABC"); assertEquals(a1, a2); // check an inherited attribute a1.setVisible(false); assertNotEquals(a1, a2); a2.setVisible(false); assertEquals(a1, a2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { DialTextAnnotation a1 = new DialTextAnnotation("A1"); DialTextAnnotation a2 = new DialTextAnnotation("A1"); assertEquals(a1, a2); int h1 = a1.hashCode(); int h2 = a2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { // test a default instance DialTextAnnotation a1 = new DialTextAnnotation("A1"); DialTextAnnotation a2 = (DialTextAnnotation) a1.clone(); assertNotSame(a1, a2); assertSame(a1.getClass(), a2.getClass()); assertEquals(a1, a2); // check that the listener lists are independent MyDialLayerChangeListener l1 = new MyDialLayerChangeListener(); a1.addChangeListener(l1); assertTrue(a1.hasListener(l1)); assertFalse(a2.hasListener(l1)); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { // test a default instance DialTextAnnotation a1 = new DialTextAnnotation("A1"); DialTextAnnotation a2 = TestUtils.serialised(a1); assertEquals(a1, a2); // test a custom instance a1 = new DialTextAnnotation("A1"); a1.setPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); a2 = TestUtils.serialised(a1); assertEquals(a1, a2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/dial/DialValueIndicatorTest.java000066400000000000000000000144161463604235500322140ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * DialValueIndicatorTest.java * --------------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot.dial; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.GradientPaint; import org.jfree.chart.TestUtils; import org.jfree.chart.ui.RectangleAnchor; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.ui.TextAnchor; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link DialValueIndicator} class. */ public class DialValueIndicatorTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { DialValueIndicator i1 = new DialValueIndicator(0); DialValueIndicator i2 = new DialValueIndicator(0); assertEquals(i1, i2); // dataset index i1.setDatasetIndex(99); assertNotEquals(i1, i2); i2.setDatasetIndex(99); assertEquals(i1, i2); // angle i1.setAngle(43); assertNotEquals(i1, i2); i2.setAngle(43); assertEquals(i1, i2); // radius i1.setRadius(0.77); assertNotEquals(i1, i2); i2.setRadius(0.77); assertEquals(i1, i2); // frameAnchor i1.setFrameAnchor(RectangleAnchor.TOP_LEFT); assertNotEquals(i1, i2); i2.setFrameAnchor(RectangleAnchor.TOP_LEFT); assertEquals(i1, i2); // templateValue i1.setTemplateValue(1.23); assertNotEquals(i1, i2); i2.setTemplateValue(1.23); assertEquals(i1, i2); // font i1.setFont(new Font("Dialog", Font.PLAIN, 7)); assertNotEquals(i1, i2); i2.setFont(new Font("Dialog", Font.PLAIN, 7)); assertEquals(i1, i2); // paint i1.setPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.GREEN)); assertNotEquals(i1, i2); i2.setPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.GREEN)); assertEquals(i1, i2); // backgroundPaint i1.setBackgroundPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.GREEN)); assertNotEquals(i1, i2); i2.setBackgroundPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.GREEN)); assertEquals(i1, i2); // outlineStroke i1.setOutlineStroke(new BasicStroke(1.1f)); assertNotEquals(i1, i2); i2.setOutlineStroke(new BasicStroke(1.1f)); assertEquals(i1, i2); // outlinePaint i1.setOutlinePaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.GREEN)); assertNotEquals(i1, i2); i2.setOutlinePaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.GREEN)); assertEquals(i1, i2); // insets i1.setInsets(new RectangleInsets(1, 2, 3, 4)); assertNotEquals(i1, i2); i2.setInsets(new RectangleInsets(1, 2, 3, 4)); assertEquals(i1, i2); // valueAnchor i1.setValueAnchor(RectangleAnchor.BOTTOM_LEFT); assertNotEquals(i1, i2); i2.setValueAnchor(RectangleAnchor.BOTTOM_LEFT); assertEquals(i1, i2); // textAnchor i1.setTextAnchor(TextAnchor.TOP_LEFT); assertNotEquals(i1, i2); i2.setTextAnchor(TextAnchor.TOP_LEFT); assertEquals(i1, i2); // check an inherited attribute i1.setVisible(false); assertNotEquals(i1, i2); i2.setVisible(false); assertEquals(i1, i2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { DialValueIndicator i1 = new DialValueIndicator(0); DialValueIndicator i2 = new DialValueIndicator(0); assertEquals(i1, i2); int h1 = i1.hashCode(); int h2 = i2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. * * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { // test a default instance DialValueIndicator i1 = new DialValueIndicator(0); DialValueIndicator i2 = (DialValueIndicator) i1.clone(); assertNotSame(i1, i2); assertSame(i1.getClass(), i2.getClass()); assertEquals(i1, i2); // check that the listener lists are independent MyDialLayerChangeListener l1 = new MyDialLayerChangeListener(); i1.addChangeListener(l1); assertTrue(i1.hasListener(l1)); assertFalse(i2.hasListener(l1)); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DialValueIndicator i1 = new DialValueIndicator(0); DialValueIndicator i2 = TestUtils.serialised(i1); assertEquals(i1, i2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/dial/MyDialLayerChangeListener.java000066400000000000000000000035261463604235500326410ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------ * MyDialLayerChangeListener.java * ------------------------------ * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot.dial; /** * A dial layer change listener. */ public class MyDialLayerChangeListener implements DialLayerChangeListener { /** * Creates a new instance. */ public MyDialLayerChangeListener() { } /** * Receives a change event. * * @param event the event. */ @Override public void dialLayerChanged(DialLayerChangeEvent event) { } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/dial/StandardDialFrameTest.java000066400000000000000000000105451463604235500320150ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * StandardDialFrameTest.java * -------------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot.dial; import java.awt.BasicStroke; import java.awt.Color; import java.awt.GradientPaint; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link StandardDialFrame} class. */ public class StandardDialFrameTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { StandardDialFrame f1 = new StandardDialFrame(); StandardDialFrame f2 = new StandardDialFrame(); assertEquals(f1, f2); // radius f1.setRadius(0.2); assertNotEquals(f1, f2); f2.setRadius(0.2); assertEquals(f1, f2); // backgroundPaint f1.setBackgroundPaint(new GradientPaint(1.0f, 2.0f, Color.WHITE, 3.0f, 4.0f, Color.YELLOW)); assertNotEquals(f1, f2); f2.setBackgroundPaint(new GradientPaint(1.0f, 2.0f, Color.WHITE, 3.0f, 4.0f, Color.YELLOW)); assertEquals(f1, f2); // foregroundPaint f1.setForegroundPaint(new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.GREEN)); assertNotEquals(f1, f2); f2.setForegroundPaint(new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.GREEN)); assertEquals(f1, f2); // stroke f1.setStroke(new BasicStroke(2.4f)); assertNotEquals(f1, f2); f2.setStroke(new BasicStroke(2.4f)); assertEquals(f1, f2); // check an inherited attribute f1.setVisible(false); assertNotEquals(f1, f2); f2.setVisible(false); assertEquals(f1, f2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { StandardDialFrame f1 = new StandardDialFrame(); StandardDialFrame f2 = new StandardDialFrame(); assertEquals(f1, f2); int h1 = f1.hashCode(); int h2 = f2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { StandardDialFrame f1 = new StandardDialFrame(); StandardDialFrame f2 = (StandardDialFrame) f1.clone(); assertNotSame(f1, f2); assertSame(f1.getClass(), f2.getClass()); assertEquals(f1, f2); // check that the listener lists are independent MyDialLayerChangeListener l1 = new MyDialLayerChangeListener(); f1.addChangeListener(l1); assertTrue(f1.hasListener(l1)); assertFalse(f2.hasListener(l1)); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { StandardDialFrame f1 = new StandardDialFrame(); StandardDialFrame f2 = TestUtils.serialised(f1); assertEquals(f1, f2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/dial/StandardDialRangeTest.java000066400000000000000000000077211463604235500320210ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * SimpleDialRangeTest.java * ------------------------ * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot.dial; import java.awt.Color; import java.awt.GradientPaint; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link StandardDialRange} class. */ public class StandardDialRangeTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { StandardDialRange r1 = new StandardDialRange(); StandardDialRange r2 = new StandardDialRange(); assertEquals(r1, r2); // lowerBound r1.setLowerBound(1.1); assertNotEquals(r1, r2); r2.setLowerBound(1.1); assertEquals(r1, r2); // upperBound r1.setUpperBound(11.1); assertNotEquals(r1, r2); r2.setUpperBound(11.1); assertEquals(r1, r2); // paint r1.setPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); assertNotEquals(r1, r2); r2.setPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); assertEquals(r1, r2); // check an inherited attribute r1.setVisible(false); assertNotEquals(r1, r2); r2.setVisible(false); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { StandardDialRange r1 = new StandardDialRange(); StandardDialRange r2 = new StandardDialRange(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { StandardDialRange r1 = new StandardDialRange(); StandardDialRange r2 = (StandardDialRange) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); // check that the listener lists are independent MyDialLayerChangeListener l1 = new MyDialLayerChangeListener(); r1.addChangeListener(l1); assertTrue(r1.hasListener(l1)); assertFalse(r2.hasListener(l1)); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { StandardDialRange r1 = new StandardDialRange(); StandardDialRange r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/dial/StandardDialScaleTest.java000066400000000000000000000213261463604235500320110ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * StandardDialScaleTest.java * -------------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot.dial; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.GradientPaint; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link StandardDialScale} class. */ public class StandardDialScaleTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { StandardDialScale s1 = new StandardDialScale(); StandardDialScale s2 = new StandardDialScale(); assertEquals(s1, s2); // lowerBound s1 = new StandardDialScale(10.0, 100.0, 0.0, 270.0, 10.0, 4); assertNotEquals(s1, s2); s2 = new StandardDialScale(10.0, 100.0, 0.0, 270.0, 10.0, 4); assertEquals(s1, s2); // upperBound s1 = new StandardDialScale(10.0, 200.0, 0.0, 270.0, 10.0, 4); assertNotEquals(s1, s2); s2 = new StandardDialScale(10.0, 200.0, 0.0, 270.0, 10.0, 4); assertEquals(s1, s2); // startAngle s1 = new StandardDialScale(10.0, 200.0, 20.0, 270.0, 10.0, 4); assertNotEquals(s1, s2); s2 = new StandardDialScale(10.0, 200.0, 20.0, 270.0, 10.0, 4); assertEquals(s1, s2); // extent s1 = new StandardDialScale(10.0, 200.0, 20.0, 99.0, 10.0, 4); assertNotEquals(s1, s2); s2 = new StandardDialScale(10.0, 200.0, 20.0, 99.0, 10.0, 4); assertEquals(s1, s2); // tickRadius s1.setTickRadius(0.99); assertNotEquals(s1, s2); s2.setTickRadius(0.99); assertEquals(s1, s2); // majorTickIncrement s1.setMajorTickIncrement(11.1); assertNotEquals(s1, s2); s2.setMajorTickIncrement(11.1); assertEquals(s1, s2); // majorTickLength s1.setMajorTickLength(0.09); assertNotEquals(s1, s2); s2.setMajorTickLength(0.09); assertEquals(s1, s2); // majorTickPaint s1.setMajorTickPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW)); assertNotEquals(s1, s2); s2.setMajorTickPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW)); assertEquals(s1, s2); // majorTickStroke s1.setMajorTickStroke(new BasicStroke(1.1f)); assertNotEquals(s1, s2); s2.setMajorTickStroke(new BasicStroke(1.1f)); assertEquals(s1, s2); // minorTickCount s1.setMinorTickCount(7); assertNotEquals(s1, s2); s2.setMinorTickCount(7); assertEquals(s1, s2); // minorTickLength s1.setMinorTickLength(0.09); assertNotEquals(s1, s2); s2.setMinorTickLength(0.09); assertEquals(s1, s2); // tickLabelOffset s1.setTickLabelOffset(0.11); assertNotEquals(s1, s2); s2.setTickLabelOffset(0.11); assertEquals(s1, s2); // tickLabelFont s1.setTickLabelFont(new Font("Dialog", Font.PLAIN, 15)); assertNotEquals(s1, s2); s2.setTickLabelFont(new Font("Dialog", Font.PLAIN, 15)); assertEquals(s1, s2); // tickLabelPaint s1.setTickLabelPaint(new GradientPaint(1.0f, 2.0f, Color.WHITE, 3.0f, 4.0f, Color.GREEN)); assertNotEquals(s1, s2); s2.setTickLabelPaint(new GradientPaint(1.0f, 2.0f, Color.WHITE, 3.0f, 4.0f, Color.GREEN)); assertEquals(s1, s2); s1.setTickLabelsVisible(false); assertNotEquals(s1, s2); s2.setTickLabelsVisible(false); assertEquals(s1, s2); // check an inherited attribute s1.setVisible(false); assertNotEquals(s1, s2); s2.setVisible(false); assertEquals(s1, s2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { StandardDialScale s1 = new StandardDialScale(); StandardDialScale s2 = new StandardDialScale(); assertEquals(s1, s2); int h1 = s1.hashCode(); int h2 = s2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { // try a default instance StandardDialScale s1 = new StandardDialScale(); StandardDialScale s2 = (StandardDialScale) s1.clone(); assertNotSame(s1, s2); assertSame(s1.getClass(), s2.getClass()); assertEquals(s1, s2); // try a customised instance s1 = new StandardDialScale(); s1.setExtent(123.4); s1.setMajorTickPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.WHITE)); s1.setMajorTickStroke(new BasicStroke(2.0f)); s2 = (StandardDialScale) s1.clone(); assertNotSame(s1, s2); assertSame(s1.getClass(), s2.getClass()); assertEquals(s1, s2); // check that the listener lists are independent MyDialLayerChangeListener l1 = new MyDialLayerChangeListener(); s1.addChangeListener(l1); assertTrue(s1.hasListener(l1)); assertFalse(s2.hasListener(l1)); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { // try a default instance StandardDialScale s1 = new StandardDialScale(); StandardDialScale s2 = TestUtils.serialised(s1); assertEquals(s1, s2); // try a customised instance s1 = new StandardDialScale(); s1.setExtent(123.4); s1.setMajorTickPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.WHITE)); s1.setMajorTickStroke(new BasicStroke(2.0f)); s2 = TestUtils.serialised(s1); assertEquals(s1, s2); } private static final double EPSILON = 0.0000000001; /** * Some checks for the valueToAngle() method. */ @Test public void testValueToAngle() { StandardDialScale s = new StandardDialScale(); assertEquals(175.0, s.valueToAngle(0.0), EPSILON); assertEquals(90.0, s.valueToAngle(50.0), EPSILON); assertEquals(5.0, s.valueToAngle(100.0), EPSILON); assertEquals(192.0, s.valueToAngle(-10.0), EPSILON); assertEquals(-12.0, s.valueToAngle(110.0), EPSILON); s = new StandardDialScale(0, 20, 180, -180.0, 10, 3); assertEquals(180.0, s.valueToAngle(0.0), EPSILON); assertEquals(90.0, s.valueToAngle(10.0), EPSILON); assertEquals(0.0, s.valueToAngle(20.0), EPSILON); } /** * Some checks for the angleToValue() method. */ @Test public void testAngleToValue() { StandardDialScale s = new StandardDialScale(); assertEquals(0.0, s.angleToValue(175.0), EPSILON); assertEquals(50.0, s.angleToValue(90.0), EPSILON); assertEquals(100.0, s.angleToValue(5.0), EPSILON); assertEquals(-10.0, s.angleToValue(192.0), EPSILON); assertEquals(110.0, s.angleToValue(-12.0), EPSILON); s = new StandardDialScale(0, 20, 180, -180.0, 10, 3); assertEquals(0.0, s.angleToValue(180.0), EPSILON); assertEquals(10.0, s.angleToValue(90.0), EPSILON); assertEquals(20.0, s.angleToValue(0.0), EPSILON); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/flow/000077500000000000000000000000001463604235500250365ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/flow/FlowPlotTest.java000066400000000000000000000207571463604235500303220ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * FlowPlotTest.java * ----------------- * (C) Copyright 2021-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.plot.flow; import java.awt.Color; import java.awt.Font; import java.util.Arrays; import java.util.List; import org.jfree.chart.TestUtils; import org.jfree.chart.event.PlotChangeEvent; import org.jfree.chart.event.PlotChangeListener; import org.jfree.chart.labels.StandardFlowLabelGenerator; import org.jfree.chart.ui.VerticalAlignment; import org.jfree.data.flow.NodeKey; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link FlowPlot} class. */ public class FlowPlotTest implements PlotChangeListener { private PlotChangeEvent lastEvent = null; /** * Receives notification of a plot change event. * * @param event the event. */ @Override public void plotChanged(PlotChangeEvent event) { this.lastEvent = event; } @Test public void setDefaultNodeColorTriggersChangeEvent() { this.lastEvent = null; FlowPlot p1 = new FlowPlot(null); p1.addChangeListener(this); p1.setDefaultNodeColor(Color.GREEN); assertNotNull(this.lastEvent); } @Test public void setDefaultNodeLabelFontTriggersChangeEvent() { this.lastEvent = null; FlowPlot p1 = new FlowPlot(null); p1.addChangeListener(this); p1.setDefaultNodeLabelFont(new Font(Font.DIALOG, Font.PLAIN, 12)); assertNotNull(this.lastEvent); } @Test public void setDefaultNodeLabelPaintTriggersChangeEvent() { this.lastEvent = null; FlowPlot p1 = new FlowPlot(null); p1.addChangeListener(this); p1.setDefaultNodeLabelPaint(Color.RED); assertNotNull(this.lastEvent); } @Test public void setDefaultNodeLabelOffsetXTriggersChangeEvent() { this.lastEvent = null; FlowPlot p1 = new FlowPlot(null); p1.addChangeListener(this); p1.setNodeLabelOffsetX(12.3); assertNotNull(this.lastEvent); } @Test public void setDefaultNodeLabelOffsetYTriggersChangeEvent() { this.lastEvent = null; FlowPlot p1 = new FlowPlot(null); p1.addChangeListener(this); p1.setNodeLabelOffsetY(12.4); assertNotNull(this.lastEvent); } @Test public void setNodeMarginTriggersChangeEvent() { this.lastEvent = null; FlowPlot p1 = new FlowPlot(null); p1.addChangeListener(this); p1.setNodeMargin(0.02); assertNotNull(this.lastEvent); } @Test public void setNodeLabelAlignmentTriggersChangeEvent() { this.lastEvent = null; FlowPlot p1 = new FlowPlot(null); p1.addChangeListener(this); p1.setNodeLabelAlignment(VerticalAlignment.CENTER); assertNotNull(this.lastEvent); } @Test public void setNodeFillColorTriggersChangeEvent() { this.lastEvent = null; FlowPlot p1 = new FlowPlot(null); p1.addChangeListener(this); p1.setNodeFillColor(new NodeKey<>(0, "A"), Color.RED); assertNotNull(this.lastEvent); } @Test public void setFlowMarginTriggersChangeEvent() { this.lastEvent = null; FlowPlot p1 = new FlowPlot(null); p1.addChangeListener(this); p1.setFlowMargin(0.1); assertNotNull(this.lastEvent); } @Test public void setToolTipGeneratorTriggersChangeEvent() { this.lastEvent = null; FlowPlot p1 = new FlowPlot(null); p1.addChangeListener(this); p1.setToolTipGenerator(null); assertNotNull(this.lastEvent); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { FlowPlot p1 = new FlowPlot(null); FlowPlot p2 = new FlowPlot(null); assertEquals(p1, p2); assertEquals(p2, p1); // test fields one by one - the independence checker does this testIndependence(p1, p2); } /** * Confirm that cloning works. * * @throws CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { FlowPlot p1 = new FlowPlot(null); p1.setNodeFillColor(new NodeKey<>(0, "A"), Color.BLUE); FlowPlot p2 = (FlowPlot) p1.clone(); assertNotSame(p1, p2); assertSame(p1.getClass(), p2.getClass()); assertEquals(p1, p2); testIndependence(p1, p2); } /** * Tests that two plot instances do not share any state. * * @param p1 plot 1. * @param p2 plot 2. */ private void testIndependence(FlowPlot p1, FlowPlot p2) { // test fields one by one p1.setFlowMargin(0.01); assertNotEquals(p1, p2); p2.setFlowMargin(0.01); assertEquals(p1, p1); p1.setDefaultNodeColor(Color.GREEN); assertNotEquals(p1, p2); p2.setDefaultNodeColor(Color.GREEN); assertEquals(p1, p1); p1.setDefaultNodeLabelFont(new Font(Font.DIALOG, Font.PLAIN, 22)); assertNotEquals(p1, p2); p2.setDefaultNodeLabelFont(new Font(Font.DIALOG, Font.PLAIN, 22)); assertEquals(p1, p1); p1.setDefaultNodeLabelPaint(Color.WHITE); assertNotEquals(p1, p2); p2.setDefaultNodeLabelPaint(Color.WHITE); assertEquals(p1, p1); p1.setNodeMargin(0.05); assertNotEquals(p1, p2); p2.setNodeMargin(0.05); assertEquals(p1, p1); p1.setNodeLabelOffsetX(99.0); assertNotEquals(p1, p2); p2.setNodeLabelOffsetX(99.0); assertEquals(p1, p1); p1.setNodeLabelOffsetY(88.0); assertNotEquals(p1, p2); p2.setNodeLabelOffsetY(88.0); assertEquals(p1, p1); p1.setNodeWidth(9.0); assertNotEquals(p1, p2); p2.setNodeWidth(9.0); assertEquals(p1, p1); p1.setNodeLabelAlignment(VerticalAlignment.BOTTOM); assertNotEquals(p1, p2); p2.setNodeLabelAlignment(VerticalAlignment.BOTTOM); assertEquals(p1, p1); p1.setToolTipGenerator(null); assertNotEquals(p1, p2); p2.setToolTipGenerator(null); assertEquals(p1, p2); p1.setToolTipGenerator(new StandardFlowLabelGenerator("%4$,.0f")); assertNotEquals(p1, p2); p2.setToolTipGenerator(new StandardFlowLabelGenerator("%4$,.0f")); assertEquals(p1, p2); p1.setNodeFillColor(new NodeKey<>(0, "A"), Color.RED); assertNotEquals(p1, p2); p2.setNodeFillColor(new NodeKey<>(0, "A"), Color.RED); assertEquals(p1, p2); List colors1 = Arrays.asList(Color.RED, Color.GREEN, Color.BLUE); p1.setNodeColorSwatch(colors1); assertNotEquals(p1, p2); List colors2 = Arrays.asList(Color.RED, Color.GREEN, Color.BLUE); p2.setNodeColorSwatch(colors2); assertEquals(p1, p2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { FlowPlot p1 = new FlowPlot(null); FlowPlot p2 = TestUtils.serialised(p1); assertEquals(p1, p2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/plot/package.html000066400000000000000000000002101463604235500263410ustar00rootroot00000000000000 JUnit tests for the plot classes. jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/000077500000000000000000000000001463604235500247175ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/AbstractRendererTest.java000066400000000000000000000670251463604235500316660ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * AbstractRendererTest.java * ------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer; import org.jfree.chart.TestUtils; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.event.RendererChangeListener; import org.jfree.chart.labels.ItemLabelAnchor; import org.jfree.chart.labels.ItemLabelPosition; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.DefaultDrawingSupplier; import org.jfree.chart.renderer.category.BarRenderer; import org.jfree.chart.renderer.category.LineAndShapeRenderer; import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; import org.jfree.chart.ui.TextAnchor; import org.junit.jupiter.api.Test; import java.awt.*; import java.awt.geom.Ellipse2D; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link AbstractRenderer} class. */ public class AbstractRendererTest { /** * Test that the equals() method distinguishes all fields. */ @SuppressWarnings("deprecation") @Test public void testEquals() { // have to use a concrete subclass... BarRenderer r1 = new BarRenderer(); BarRenderer r2 = new BarRenderer(); assertEquals(r1, r2); assertEquals(r2, r1); // seriesVisibleList r1.setSeriesVisible(2, Boolean.TRUE); assertNotEquals(r1, r2); r2.setSeriesVisible(2, Boolean.TRUE); assertEquals(r1, r2); // defaultSeriesVisible r1.setDefaultSeriesVisible(false); assertNotEquals(r1, r2); r2.setDefaultSeriesVisible(false); assertEquals(r1, r2); // seriesVisibleInLegendList r1.setSeriesVisibleInLegend(1, Boolean.TRUE); assertNotEquals(r1, r2); r2.setSeriesVisibleInLegend(1, Boolean.TRUE); assertEquals(r1, r2); // defaultSeriesVisibleInLegend r1.setDefaultSeriesVisibleInLegend(false); assertNotEquals(r1, r2); r2.setDefaultSeriesVisibleInLegend(false); assertEquals(r1, r2); // paintList r1.setSeriesPaint(0, new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.WHITE)); assertNotEquals(r1, r2); r2.setSeriesPaint(0, new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.WHITE)); assertEquals(r1, r2); // defaultPaint r1.setDefaultPaint(new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.RED)); assertNotEquals(r1, r2); r2.setDefaultPaint(new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.RED)); assertEquals(r1, r2); // fillPaintList r1.setSeriesFillPaint(0, new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.RED)); assertNotEquals(r1, r2); r2.setSeriesFillPaint(0, new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.RED)); assertEquals(r1, r2); // defaultFillPaint r1.setDefaultFillPaint(new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.RED)); assertNotEquals(r1, r2); r2.setDefaultFillPaint(new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.RED)); assertEquals(r1, r2); // outlinePaintList r1.setSeriesOutlinePaint(0, new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.RED)); assertNotEquals(r1, r2); r2.setSeriesOutlinePaint(0, new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.RED)); assertEquals(r1, r2); // defaultOutlinePaint r1.setDefaultOutlinePaint(new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.RED)); assertNotEquals(r1, r2); r2.setDefaultOutlinePaint(new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.RED)); assertEquals(r1, r2); // stroke Stroke s = new BasicStroke(3.21f); // strokeList r1.setSeriesStroke(1, s); assertNotEquals(r1, r2); r2.setSeriesStroke(1, s); assertEquals(r1, r2); // defaultStroke r1.setDefaultStroke(s); assertNotEquals(r1, r2); r2.setDefaultStroke(s); assertEquals(r1, r2); // outlineStrokeList r1.setSeriesOutlineStroke(0, s); assertNotEquals(r1, r2); r2.setSeriesOutlineStroke(0, s); assertEquals(r1, r2); // defaultOutlineStroke r1.setDefaultOutlineStroke(s); assertNotEquals(r1, r2); r2.setDefaultOutlineStroke(s); assertEquals(r1, r2); // shapeList r1.setSeriesShape(1, new Ellipse2D.Double(1, 2, 3, 4)); assertNotEquals(r1, r2); r2.setSeriesShape(1, new Ellipse2D.Double(1, 2, 3, 4)); assertEquals(r1, r2); // defaultShape r1.setDefaultShape(new Ellipse2D.Double(1, 2, 3, 4)); assertNotEquals(r1, r2); r2.setDefaultShape(new Ellipse2D.Double(1, 2, 3, 4)); assertEquals(r1, r2); // itemLabelsVisibleList r1.setSeriesItemLabelsVisible(1, Boolean.TRUE); assertNotEquals(r1, r2); r2.setSeriesItemLabelsVisible(1, Boolean.TRUE); assertEquals(r1, r2); // baseItemLabelsVisible r1.setDefaultItemLabelsVisible(true); assertNotEquals(r1, r2); r2.setDefaultItemLabelsVisible(true); assertEquals(r1, r2); // itemLabelFontList r1.setSeriesItemLabelFont(1, new Font("Serif", Font.BOLD, 9)); assertNotEquals(r1, r2); r2.setSeriesItemLabelFont(1, new Font("Serif", Font.BOLD, 9)); assertEquals(r1, r2); // defaultItemLabelFont r1.setDefaultItemLabelFont(new Font("Serif", Font.PLAIN, 10)); assertNotEquals(r1, r2); r2.setDefaultItemLabelFont(new Font("Serif", Font.PLAIN, 10)); assertEquals(r1, r2); // itemLabelPaintList r1.setSeriesItemLabelPaint(0, new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.GRAY)); assertNotEquals(r1, r2); r2.setSeriesItemLabelPaint(0, new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.GRAY)); assertEquals(r1, r2); // defaultItemLabelPaint r1.setDefaultItemLabelPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.GRAY)); assertNotEquals(r1, r2); r2.setDefaultItemLabelPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.GRAY)); assertEquals(r1, r2); // positiveItemLabelPositionList; r1.setSeriesPositiveItemLabelPosition(0, new ItemLabelPosition()); assertNotEquals(r1, r2); r2.setSeriesPositiveItemLabelPosition(0, new ItemLabelPosition()); assertEquals(r1, r2); // defaultPositiveItemLabelPosition; r1.setDefaultPositiveItemLabelPosition(new ItemLabelPosition( ItemLabelAnchor.INSIDE10, TextAnchor.BASELINE_RIGHT)); assertNotEquals(r1, r2); r2.setDefaultPositiveItemLabelPosition(new ItemLabelPosition( ItemLabelAnchor.INSIDE10, TextAnchor.BASELINE_RIGHT)); assertEquals(r1, r2); // negativeItemLabelPositionList; r1.setSeriesNegativeItemLabelPosition(1, new ItemLabelPosition( ItemLabelAnchor.INSIDE10, TextAnchor.BASELINE_RIGHT)); assertNotEquals(r1, r2); r2.setSeriesNegativeItemLabelPosition(1, new ItemLabelPosition( ItemLabelAnchor.INSIDE10, TextAnchor.BASELINE_RIGHT)); assertEquals(r1, r2); // defaultNegativeItemLabelPosition; r1.setDefaultNegativeItemLabelPosition(new ItemLabelPosition( ItemLabelAnchor.INSIDE10, TextAnchor.BASELINE_RIGHT)); assertNotEquals(r1, r2); r2.setDefaultNegativeItemLabelPosition(new ItemLabelPosition( ItemLabelAnchor.INSIDE10, TextAnchor.BASELINE_RIGHT)); assertEquals(r1, r2); // itemLabelAnchorOffset r1.setItemLabelAnchorOffset(3.0); assertNotEquals(r1, r2); r2.setItemLabelAnchorOffset(3.0); assertEquals(r1, r2); // createEntitiesList; r1.setSeriesCreateEntities(0, Boolean.TRUE); assertNotEquals(r1, r2); r2.setSeriesCreateEntities(0, Boolean.TRUE); assertEquals(r1, r2); // baseCreateEntities; r1.setDefaultCreateEntities(false); assertNotEquals(r1, r2); r2.setDefaultCreateEntities(false); assertEquals(r1, r2); // legendShape r1.setLegendShape(0, new Ellipse2D.Double(1.0, 2.0, 3.0, 4.0)); assertNotEquals(r1, r2); r2.setLegendShape(0, new Ellipse2D.Double(1.0, 2.0, 3.0, 4.0)); assertEquals(r1, r2); // baseLegendShape r1.setDefaultLegendShape(new Ellipse2D.Double(5.0, 6.0, 7.0, 8.0)); assertNotEquals(r1, r2); r2.setDefaultLegendShape(new Ellipse2D.Double(5.0, 6.0, 7.0, 8.0)); assertEquals(r1, r2); // legendTextFont r1.setLegendTextFont(0, new Font("Dialog", Font.PLAIN, 7)); assertNotEquals(r1, r2); r2.setLegendTextFont(0, new Font("Dialog", Font.PLAIN, 7)); assertEquals(r1, r2); // defaultLegendTextFont r1.setDefaultLegendTextFont(new Font("Dialog", Font.PLAIN, 7)); assertNotEquals(r1, r2); r2.setDefaultLegendTextFont(new Font("Dialog", Font.PLAIN, 7)); assertEquals(r1, r2); // legendTextPaint r1.setLegendTextPaint(0, new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.RED)); assertNotEquals(r1, r2); r2.setLegendTextPaint(0, new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.RED)); assertEquals(r1, r2); // defaultOutlinePaint r1.setDefaultLegendTextPaint(new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.RED)); assertNotEquals(r1, r2); r2.setDefaultLegendTextPaint(new GradientPaint(1.0f, 2.0f, Color.BLUE, 3.0f, 4.0f, Color.RED)); assertEquals(r1, r2); } @Test public void testEquals_ObjectList() { BarRenderer r1 = new BarRenderer(); r1.setSeriesItemLabelFont(0, new Font(Font.DIALOG, Font.BOLD, 10)); BarRenderer r2 = new BarRenderer(); r2.setSeriesItemLabelFont(0, new Font(Font.DIALOG, Font.BOLD, 10)); assertEquals(r1, r2); r2.setSeriesItemLabelFont(1, new Font(Font.DIALOG, Font.PLAIN, 5)); assertNotEquals(r1, r2); } @Test public void testEquals_ObjectList2() { BarRenderer r1 = new BarRenderer(); r1.setLegendTextFont(0, new Font(Font.DIALOG, Font.BOLD, 10)); BarRenderer r2 = new BarRenderer(); r2.setLegendTextFont(0, new Font(Font.DIALOG, Font.BOLD, 10)); assertEquals(r1, r2); r2.setLegendTextFont(1, new Font(Font.DIALOG, Font.PLAIN, 5)); assertNotEquals(r1, r2); } @Test public void testEquals_ObjectList3() { BarRenderer r1 = new BarRenderer(); r1.setSeriesPositiveItemLabelPosition(0, new ItemLabelPosition(ItemLabelAnchor.CENTER, TextAnchor.CENTER)); BarRenderer r2 = new BarRenderer(); r2.setSeriesPositiveItemLabelPosition(0, new ItemLabelPosition(ItemLabelAnchor.CENTER, TextAnchor.CENTER)); assertEquals(r1, r2); r2.setSeriesPositiveItemLabelPosition(1, new ItemLabelPosition(ItemLabelAnchor.INSIDE1, TextAnchor.CENTER)); assertNotEquals(r1, r2); } @Test public void testEquals_ObjectList4() { BarRenderer r1 = new BarRenderer(); r1.setSeriesNegativeItemLabelPosition(0, new ItemLabelPosition(ItemLabelAnchor.CENTER, TextAnchor.CENTER)); BarRenderer r2 = new BarRenderer(); r2.setSeriesNegativeItemLabelPosition(0, new ItemLabelPosition(ItemLabelAnchor.CENTER, TextAnchor.CENTER)); assertEquals(r1, r2); r2.setSeriesNegativeItemLabelPosition(1, new ItemLabelPosition(ItemLabelAnchor.INSIDE1, TextAnchor.CENTER)); assertNotEquals(r1, r2); } private static class TestRenderer extends XYLineAndShapeRenderer { @Override public void setTreatLegendShapeAsLine(boolean flag) { super.setTreatLegendShapeAsLine(flag); } } /** * Check that the treatLegendShapeAsLine flag is included in the equals() * comparison. */ @Test public void testEquals2() { TestRenderer r1 = new TestRenderer(); TestRenderer r2 = new TestRenderer(); assertEquals(r1, r2); r1.setTreatLegendShapeAsLine(true); assertNotEquals(r1, r2); r2.setTreatLegendShapeAsLine(true); assertEquals(r1, r2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { LineAndShapeRenderer r1 = new LineAndShapeRenderer(); Rectangle2D shape = new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0); Rectangle2D baseShape = new Rectangle2D.Double(11.0, 12.0, 13.0, 14.0); r1.setDefaultShape(baseShape); r1.setDefaultLegendShape(new Rectangle(4, 3, 2, 1)); r1.setDefaultLegendTextFont(new Font("Dialog", Font.PLAIN, 3)); r1.setDefaultLegendTextPaint(new Color(1, 2, 3)); r1.setSeriesItemLabelFont(0, new Font(Font.MONOSPACED, Font.BOLD, 13)); r1.setLegendTextFont(0, new Font(Font.MONOSPACED, Font.BOLD, 14)); r1.setSeriesPositiveItemLabelPosition(0, new ItemLabelPosition( ItemLabelAnchor.CENTER, TextAnchor.TOP_LEFT)); r1.setSeriesNegativeItemLabelPosition(0, new ItemLabelPosition( ItemLabelAnchor.CENTER, TextAnchor.CENTER)); LineAndShapeRenderer r2 = (LineAndShapeRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); r1.setSeriesVisible(0, Boolean.FALSE); assertNotEquals(r1, r2); r2.setSeriesVisible(0, Boolean.FALSE); assertEquals(r1, r2); r1.setSeriesVisibleInLegend(0, Boolean.FALSE); assertNotEquals(r1, r2); r2.setSeriesVisibleInLegend(0, Boolean.FALSE); assertEquals(r1, r2); r1.setSeriesPaint(0, Color.BLACK); assertNotEquals(r1, r2); r2.setSeriesPaint(0, Color.BLACK); assertEquals(r1, r2); r1.setSeriesFillPaint(0, Color.YELLOW); assertNotEquals(r1, r2); r2.setSeriesFillPaint(0, Color.YELLOW); assertEquals(r1, r2); r1.setSeriesOutlinePaint(0, Color.YELLOW); assertNotEquals(r1, r2); r2.setSeriesOutlinePaint(0, Color.YELLOW); assertEquals(r1, r2); r1.setSeriesStroke(0, new BasicStroke(2.2f)); assertNotEquals(r1, r2); r2.setSeriesStroke(0, new BasicStroke(2.2f)); assertEquals(r1, r2); r1.setSeriesOutlineStroke(0, new BasicStroke(2.2f)); assertNotEquals(r1, r2); r2.setSeriesOutlineStroke(0, new BasicStroke(2.2f)); assertEquals(r1, r2); baseShape.setRect(4.0, 3.0, 2.0, 1.0); assertNotEquals(r1, r2); r2.setDefaultShape(new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0)); assertEquals(r1, r2); r1.setSeriesShape(0, new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0)); assertNotEquals(r1, r2); r2.setSeriesShape(0, new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0)); assertEquals(r1, r2); r1.setSeriesItemLabelsVisible(0, Boolean.TRUE); assertNotEquals(r1, r2); r2.setSeriesItemLabelsVisible(0, Boolean.TRUE); assertEquals(r1, r2); r1.setSeriesItemLabelPaint(0, Color.RED); assertNotEquals(r1, r2); r2.setSeriesItemLabelPaint(0, Color.RED); assertEquals(r1, r2); r1.setSeriesPositiveItemLabelPosition(0, new ItemLabelPosition()); assertNotEquals(r1, r2); r2.setSeriesPositiveItemLabelPosition(0, new ItemLabelPosition()); assertEquals(r1, r2); r1.setSeriesNegativeItemLabelPosition(0, new ItemLabelPosition()); assertNotEquals(r1, r2); r2.setSeriesNegativeItemLabelPosition(0, new ItemLabelPosition()); assertEquals(r1, r2); r1.setSeriesCreateEntities(0, Boolean.FALSE); assertNotEquals(r1, r2); r2.setSeriesCreateEntities(0, Boolean.FALSE); assertEquals(r1, r2); r1.setLegendShape(0, new Rectangle(9, 7, 3, 4)); assertNotEquals(r1, r2); r2.setLegendShape(0, new Rectangle(9, 7, 3, 4)); assertEquals(r1, r2); r1.setDefaultLegendShape(new Rectangle(3, 4, 1, 5)); assertNotEquals(r1, r2); r2.setDefaultLegendShape(new Rectangle(3, 4, 1, 5)); assertEquals(r1, r2); r1.setLegendTextFont(1, new Font("Dialog", Font.PLAIN, 33)); assertNotEquals(r1, r2); r2.setLegendTextFont(1, new Font("Dialog", Font.PLAIN, 33)); assertEquals(r1, r2); r1.setDefaultLegendTextFont(new Font("Dialog", Font.PLAIN, 11)); assertNotEquals(r1, r2); r2.setDefaultLegendTextFont(new Font("Dialog", Font.PLAIN, 11)); assertEquals(r1, r2); r1.setLegendTextPaint(3, Color.RED); assertNotEquals(r1, r2); r2.setLegendTextPaint(3, Color.RED); assertEquals(r1, r2); r1.setDefaultLegendTextPaint(Color.GREEN); assertNotEquals(r1, r2); r2.setDefaultLegendTextPaint(Color.GREEN); assertEquals(r1, r2); } /** * A utility class for listening to changes to a renderer. */ static class MyRendererChangeListener implements RendererChangeListener { /** The last event received. */ public RendererChangeEvent lastEvent; /** * Creates a new instance. */ public MyRendererChangeListener() { this.lastEvent = null; } @Override public void rendererChanged(RendererChangeEvent event) { this.lastEvent = event; } } /** * A check for cloning. */ @Test public void testCloning2() throws CloneNotSupportedException { LineAndShapeRenderer r1 = new LineAndShapeRenderer(); r1.setDefaultPaint(Color.BLUE); r1.setDefaultLegendTextPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); LineAndShapeRenderer r2 = (LineAndShapeRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); MyRendererChangeListener listener = new MyRendererChangeListener(); r2.addChangeListener(listener); r2.setDefaultPaint(Color.RED); assertSame(listener.lastEvent.getRenderer(), r2); assertFalse(r1.hasListener(listener)); } /** * Tests each setter method to ensure that it sends an event notification. */ @Test public void testEventNotification() { RendererChangeDetector detector = new RendererChangeDetector(); BarRenderer r1 = new BarRenderer(); // have to use a subclass of // AbstractRenderer r1.addChangeListener(detector); // PAINT detector.setNotified(false); r1.setSeriesPaint(0, Color.RED); assertTrue(detector.getNotified()); detector.setNotified(false); r1.setDefaultPaint(Color.RED); assertTrue(detector.getNotified()); // OUTLINE PAINT detector.setNotified(false); r1.setSeriesOutlinePaint(0, Color.RED); assertTrue(detector.getNotified()); detector.setNotified(false); r1.setDefaultOutlinePaint(Color.RED); assertTrue(detector.getNotified()); // STROKE detector.setNotified(false); r1.setSeriesStroke(0, new BasicStroke(1.0f)); assertTrue(detector.getNotified()); detector.setNotified(false); r1.setDefaultStroke(new BasicStroke(1.0f)); assertTrue(detector.getNotified()); // OUTLINE STROKE detector.setNotified(false); r1.setSeriesOutlineStroke(0, new BasicStroke(1.0f)); assertTrue(detector.getNotified()); detector.setNotified(false); r1.setDefaultOutlineStroke(new BasicStroke(1.0f)); assertTrue(detector.getNotified()); // SHAPE detector.setNotified(false); r1.setSeriesShape(0, new Rectangle2D.Float()); assertTrue(detector.getNotified()); detector.setNotified(false); r1.setDefaultShape(new Rectangle2D.Float()); assertTrue(detector.getNotified()); // ITEM_LABELS_VISIBLE detector.setNotified(false); r1.setSeriesItemLabelsVisible(0, Boolean.TRUE); assertTrue(detector.getNotified()); detector.setNotified(false); r1.setDefaultItemLabelsVisible(Boolean.TRUE); assertTrue(detector.getNotified()); // ITEM_LABEL_FONT detector.setNotified(false); r1.setSeriesItemLabelFont(0, new Font("Serif", Font.PLAIN, 12)); assertTrue(detector.getNotified()); detector.setNotified(false); r1.setDefaultItemLabelFont(new Font("Serif", Font.PLAIN, 12)); assertTrue(detector.getNotified()); // ITEM_LABEL_PAINT detector.setNotified(false); r1.setSeriesItemLabelPaint(0, Color.BLUE); assertTrue(detector.getNotified()); detector.setNotified(false); r1.setDefaultItemLabelPaint(Color.BLUE); assertTrue(detector.getNotified()); // POSITIVE ITEM LABEL POSITION detector.setNotified(false); r1.setSeriesPositiveItemLabelPosition(0, new ItemLabelPosition( ItemLabelAnchor.CENTER, TextAnchor.CENTER)); assertTrue(detector.getNotified()); detector.setNotified(false); r1.setDefaultPositiveItemLabelPosition(new ItemLabelPosition( ItemLabelAnchor.CENTER, TextAnchor.CENTER)); assertTrue(detector.getNotified()); // NEGATIVE ITEM LABEL ANCHOR detector.setNotified(false); r1.setSeriesNegativeItemLabelPosition(0, new ItemLabelPosition( ItemLabelAnchor.CENTER, TextAnchor.CENTER)); assertTrue(detector.getNotified()); detector.setNotified(false); r1.setDefaultNegativeItemLabelPosition(new ItemLabelPosition( ItemLabelAnchor.CENTER, TextAnchor.CENTER)); assertTrue(detector.getNotified()); } /** * Serialize an instance, restore it, and check for equality. In addition, * test for a bug that was reported where the listener list is 'null' after * deserialization. */ @Test public void testSerialization() { BarRenderer r1 = new BarRenderer(); r1.setDefaultLegendTextFont(new Font("Dialog", Font.PLAIN, 4)); r1.setDefaultLegendTextPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.GREEN)); r1.setDefaultLegendShape(new Line2D.Double(1.0, 2.0, 3.0, 4.0)); BarRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); try { r2.notifyListeners(new RendererChangeEvent(r2)); } catch (NullPointerException e) { fail("No exception should be thrown."); // failed } } /** * Some checks for the autoPopulate flag default values. */ @Test public void testAutoPopulateFlagDefaults() { BarRenderer r = new BarRenderer(); assertTrue(r.getAutoPopulateSeriesPaint()); assertFalse(r.getAutoPopulateSeriesFillPaint()); assertFalse(r.getAutoPopulateSeriesOutlinePaint()); assertTrue(r.getAutoPopulateSeriesStroke()); assertFalse(r.getAutoPopulateSeriesOutlineStroke()); assertTrue(r.getAutoPopulateSeriesShape()); } /** * Some checks for the paint lookup mechanism. */ @Test public void testPaintLookup() { BarRenderer r = new BarRenderer(); assertEquals(Color.BLUE, r.getDefaultPaint()); // first check that autoPopulate==false works as expected r.setAutoPopulateSeriesPaint(false); assertEquals(Color.BLUE, r.lookupSeriesPaint(0)); assertNull(r.getSeriesPaint(0)); // now check autoPopulate==true r.setAutoPopulateSeriesPaint(true); /*CategoryPlot plot =*/ new CategoryPlot(null, new CategoryAxis( "Category"), new NumberAxis("Value"), r); assertEquals(DefaultDrawingSupplier.DEFAULT_PAINT_SEQUENCE[0], r.lookupSeriesPaint(0)); assertNotNull(r.getSeriesPaint(0)); } /** * Some checks for the fill paint lookup mechanism. */ @Test public void testFillPaintLookup() { BarRenderer r = new BarRenderer(); assertEquals(Color.WHITE, r.getDefaultFillPaint()); // first check that autoPopulate==false works as expected r.setAutoPopulateSeriesFillPaint(false); assertEquals(Color.WHITE, r.lookupSeriesFillPaint(0)); assertNull(r.getSeriesFillPaint(0)); // now check autoPopulate==true r.setAutoPopulateSeriesFillPaint(true); /*CategoryPlot plot =*/ new CategoryPlot(null, new CategoryAxis( "Category"), new NumberAxis("Value"), r); assertEquals(DefaultDrawingSupplier.DEFAULT_FILL_PAINT_SEQUENCE[0], r.lookupSeriesFillPaint(0)); assertNotNull(r.getSeriesFillPaint(0)); } /** * Some checks for the outline paint lookup mechanism. */ @Test public void testOutlinePaintLookup() { BarRenderer r = new BarRenderer(); assertEquals(Color.GRAY, r.getDefaultOutlinePaint()); // first check that autoPopulate==false works as expected r.setAutoPopulateSeriesOutlinePaint(false); assertEquals(Color.GRAY, r.lookupSeriesOutlinePaint(0)); assertNull(r.getSeriesOutlinePaint(0)); // now check autoPopulate==true r.setAutoPopulateSeriesOutlinePaint(true); /*CategoryPlot plot =*/ new CategoryPlot(null, new CategoryAxis( "Category"), new NumberAxis("Value"), r); assertEquals(DefaultDrawingSupplier.DEFAULT_OUTLINE_PAINT_SEQUENCE[0], r.lookupSeriesOutlinePaint(0)); assertNotNull(r.getSeriesOutlinePaint(0)); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/AreaRendererEndTypeTest.java000066400000000000000000000046621463604235500322620ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------- * AreaRendererEndTypeTest.java * ---------------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; /** * Tests for the {@link AreaRendererEndType} class. */ public class AreaRendererEndTypeTest { /** * A test for the equals() method. */ @Test public void testEquals() { assertEquals(AreaRendererEndType.LEVEL, AreaRendererEndType.LEVEL); assertEquals(AreaRendererEndType.TAPER, AreaRendererEndType.TAPER); assertEquals( AreaRendererEndType.TRUNCATE, AreaRendererEndType.TRUNCATE ); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { AreaRendererEndType t1 = AreaRendererEndType.TAPER; AreaRendererEndType t2 = TestUtils.serialised(t1); assertEquals(t1, t2); boolean same = t1 == t2; assertTrue(same); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/DefaultPolarItemRendererTest.java000066400000000000000000000065201463604235500333150ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------------- * DefaultPolarItemRendererTest.java * --------------------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link DefaultPolarItemRenderer} class. */ public class DefaultPolarItemRendererTest { /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { DefaultPolarItemRenderer r1 = new DefaultPolarItemRenderer(); DefaultPolarItemRenderer r2 = new DefaultPolarItemRenderer(); assertEquals(r1, r2); r1.setSeriesFilled(1, true); assertNotEquals(r1, r2); r2.setSeriesFilled(1, true); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { DefaultPolarItemRenderer r1 = new DefaultPolarItemRenderer(); DefaultPolarItemRenderer r2 = new DefaultPolarItemRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { DefaultPolarItemRenderer r1 = new DefaultPolarItemRenderer(); DefaultPolarItemRenderer r2 = (DefaultPolarItemRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); r1.setSeriesFilled(1, true); assertNotEquals(r1, r2); r2.setSeriesFilled(1, true); assertEquals(r1, r2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DefaultPolarItemRenderer r1 = new DefaultPolarItemRenderer(); DefaultPolarItemRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/GrayPaintScaleTest.java000066400000000000000000000100351463604235500312670ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * GrayPaintScaleTest.java * ----------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer; import java.awt.Color; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link GrayPaintScale} class. */ public class GrayPaintScaleTest { private static final double EPSILON = 0.000000001; /** * Simple check for the default constructor. */ @Test public void testConstructor() { GrayPaintScale gps = new GrayPaintScale(); assertEquals(0.0, gps.getLowerBound(), EPSILON); assertEquals(1.0, gps.getUpperBound(), EPSILON); assertEquals(255, gps.getAlpha()); } /** * Some checks for the getPaint() method. */ @Test public void testGetPaint() { GrayPaintScale gps = new GrayPaintScale(); Color c = (Color) gps.getPaint(0.0); assertEquals(c, Color.BLACK); c = (Color) gps.getPaint(1.0); assertEquals(c, Color.WHITE); // check lookup values that are outside the bounds - see bug report // 1767315 c = (Color) gps.getPaint(-0.5); assertEquals(c, Color.BLACK); c = (Color) gps.getPaint(1.5); assertEquals(c, Color.WHITE); } /** * A test for the equals() method. */ @Test public void testEquals() { GrayPaintScale g1 = new GrayPaintScale(); GrayPaintScale g2 = new GrayPaintScale(); assertEquals(g1, g2); assertEquals(g2, g1); g1 = new GrayPaintScale(0.0, 1.0); g2 = new GrayPaintScale(0.0, 1.0); assertEquals(g1, g2); g1 = new GrayPaintScale(0.1, 1.0); assertNotEquals(g1, g2); g2 = new GrayPaintScale(0.1, 1.0); assertEquals(g1, g2); g1 = new GrayPaintScale(0.1, 0.9); assertNotEquals(g1, g2); g2 = new GrayPaintScale(0.1, 0.9); assertEquals(g1, g2); g1 = new GrayPaintScale(0.1, 0.9, 128); assertNotEquals(g1, g2); g2 = new GrayPaintScale(0.1, 0.9, 128); assertEquals(g1, g2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { GrayPaintScale g1 = new GrayPaintScale(); GrayPaintScale g2 = (GrayPaintScale) g1.clone(); assertNotSame(g1, g2); assertSame(g1.getClass(), g2.getClass()); assertEquals(g1, g2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { GrayPaintScale g1 = new GrayPaintScale(); GrayPaintScale g2 = TestUtils.serialised(g1); assertEquals(g1, g2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/LookupPaintScaleTest.java000066400000000000000000000145211463604235500316420ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * LookupPaintScaleTest.java * ------------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer; import java.awt.Color; import java.awt.GradientPaint; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link LookupPaintScale} class. */ public class LookupPaintScaleTest { /** * A test for the equals() method. */ @Test public void testEquals() { LookupPaintScale g1 = new LookupPaintScale(); LookupPaintScale g2 = new LookupPaintScale(); assertEquals(g1, g2); assertEquals(g2, g1); g1 = new LookupPaintScale(1.0, 2.0, Color.RED); assertNotEquals(g1, g2); g2 = new LookupPaintScale(1.0, 2.0, Color.RED); assertEquals(g1, g2); g1.add(1.5, new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); assertNotEquals(g1, g2); g2.add(1.5, new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); assertEquals(g1, g2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { LookupPaintScale g1 = new LookupPaintScale(); LookupPaintScale g2 = (LookupPaintScale) g1.clone(); assertNotSame(g1, g2); assertSame(g1.getClass(), g2.getClass()); assertEquals(g1, g2); // check independence g1.add(0.5, Color.RED); assertNotEquals(g1, g2); g2.add(0.5, Color.RED); assertEquals(g1, g2); // try with gradient paint g1 = new LookupPaintScale(1.0, 2.0, new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.GREEN)); g1.add(1.5, new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); g2 = (LookupPaintScale) g1.clone(); assertNotSame(g1, g2); assertSame(g1.getClass(), g2.getClass()); assertEquals(g1, g2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { LookupPaintScale g1 = new LookupPaintScale(); LookupPaintScale g2 = TestUtils.serialised(g1); assertEquals(g1, g2); g1 = new LookupPaintScale(1.0, 2.0, new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW)); g1.add(1.5, new GradientPaint(1.1f, 2.2f, Color.RED, 3.3f, 4.4f, Color.BLUE)); g2 = TestUtils.serialised(g1); assertEquals(g1, g2); } private static final double EPSILON = 0.0000000001; /** * Some checks for the default constructor. */ @Test public void testConstructor1() { LookupPaintScale s = new LookupPaintScale(); assertEquals(0.0, s.getLowerBound(), EPSILON); assertEquals(1.0, s.getUpperBound(), EPSILON); } /** * Some checks for the other constructor. */ @Test public void testConstructor2() { LookupPaintScale s = new LookupPaintScale(1.0, 2.0, Color.RED); assertEquals(1.0, s.getLowerBound(), EPSILON); assertEquals(2.0, s.getUpperBound(), EPSILON); assertEquals(Color.RED, s.getDefaultPaint()); } /** * Some general checks for the lookup table. */ @Test public void testGeneral() { LookupPaintScale s = new LookupPaintScale(0.0, 100.0, Color.BLACK); assertEquals(Color.BLACK, s.getPaint(-1.0)); assertEquals(Color.BLACK, s.getPaint(0.0)); assertEquals(Color.BLACK, s.getPaint(50.0)); assertEquals(Color.BLACK, s.getPaint(100.0)); assertEquals(Color.BLACK, s.getPaint(101.0)); s.add(50.0, Color.BLUE); assertEquals(Color.BLACK, s.getPaint(-1.0)); assertEquals(Color.BLACK, s.getPaint(0.0)); assertEquals(Color.BLUE, s.getPaint(50.0)); assertEquals(Color.BLUE, s.getPaint(100.0)); assertEquals(Color.BLACK, s.getPaint(101.0)); s.add(50.0, Color.RED); assertEquals(Color.BLACK, s.getPaint(-1.0)); assertEquals(Color.BLACK, s.getPaint(0.0)); assertEquals(Color.RED, s.getPaint(50.0)); assertEquals(Color.RED, s.getPaint(100.0)); assertEquals(Color.BLACK, s.getPaint(101.0)); s.add(25.0, Color.GREEN); assertEquals(Color.BLACK, s.getPaint(-1.0)); assertEquals(Color.BLACK, s.getPaint(0.0)); assertEquals(Color.GREEN, s.getPaint(25.0)); assertEquals(Color.RED, s.getPaint(50.0)); assertEquals(Color.RED, s.getPaint(100.0)); assertEquals(Color.BLACK, s.getPaint(101.0)); s.add(75.0, Color.YELLOW); assertEquals(Color.BLACK, s.getPaint(-1.0)); assertEquals(Color.BLACK, s.getPaint(0.0)); assertEquals(Color.GREEN, s.getPaint(25.0)); assertEquals(Color.RED, s.getPaint(50.0)); assertEquals(Color.YELLOW, s.getPaint(75.0)); assertEquals(Color.YELLOW, s.getPaint(100.0)); assertEquals(Color.BLACK, s.getPaint(101.0)); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/OutlierTest.java000066400000000000000000000062571463604235500300570ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * OutlierTest.java * ---------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer; import java.awt.geom.Point2D; import java.io.Serializable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link Outlier} class. */ public class OutlierTest { private static final double EPSILON = 0.000000001; /** * Simple check for the default constructor. */ @Test public void testConstructor() { Outlier out = new Outlier(1.0, 2.0, 3.0); assertEquals(-2.0, out.getX(), EPSILON); assertEquals(-1.0, out.getY(), EPSILON); assertEquals(3.0, out.getRadius(), EPSILON); } /** * A test for the equals() method. */ @Test public void testEquals() { Outlier out1 = new Outlier(1.0, 2.0, 3.0); Outlier out2 = new Outlier(1.0, 2.0, 3.0); assertEquals(out1, out2); assertEquals(out2, out1); out1.setPoint(new Point2D.Double(2.0, 2.0)); assertNotEquals(out1, out2); out2.setPoint(new Point2D.Double(2.0, 2.0)); assertEquals(out1, out2); out1.setPoint(new Point2D.Double(2.0, 3.0)); assertNotEquals(out1, out2); out2.setPoint(new Point2D.Double(2.0, 3.0)); assertEquals(out1, out2); out1.setRadius(4.0); assertNotEquals(out1, out2); out2.setRadius(4.0); assertEquals(out1, out2); } /** * Confirm that cloning is not implemented. */ @Test public void testCloning() { Outlier out1 = new Outlier(1.0, 2.0, 3.0); assertFalse(out1 instanceof Cloneable); } /** * Confirm that serialization is not implemented. */ @Test public void testSerialization() { Outlier out1 = new Outlier(1.0, 2.0, 3.0); assertFalse(out1 instanceof Serializable); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/RendererChangeDetector.java000066400000000000000000000052251463604235500321340ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * RendererChangeDetector.java * --------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer; import org.jfree.chart.event.RendererChangeEvent; import org.jfree.chart.event.RendererChangeListener; /** * A simple class for detecting whether or not a renderer has generated * a {@link RendererChangeEvent}. */ public class RendererChangeDetector implements RendererChangeListener { /** A flag that records whether or not a change event has been received. */ private boolean notified; /** * Creates a new detector. */ public RendererChangeDetector() { this.notified = false; } /** * Returns the flag that indicates whether or not a change event has been * received. * * @return The flag. */ public boolean getNotified() { return this.notified; } /** * Sets the flag that indicates whether or not a change event has been * received. * * @param notified the new value of the flag. */ public void setNotified(boolean notified) { this.notified = notified; } /** * Receives a {@link RendererChangeEvent} from a renderer. * * @param event the event. */ @Override public void rendererChanged(RendererChangeEvent event) { this.notified = true; } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/RendererUtilsTest.java000066400000000000000000000477111463604235500312230ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * RendererUtilsTest.java * ---------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer; import static org.junit.jupiter.api.Assertions.assertEquals; import org.jfree.data.DomainOrder; import org.jfree.data.xy.DefaultXYDataset; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.junit.jupiter.api.Test; /** * Some checks for the {@link RendererUtils} class. */ public class RendererUtilsTest { /** * Some checks for the findLiveItemsLowerBound() method when the dataset is * unordered. */ @Test public void testFindLiveItemsLowerBound_Unordered() { DefaultXYDataset d = new DefaultXYDataset(); // check a series with no items d.addSeries("S1", new double[][] {{}, {}}); assertEquals(0, RendererUtils.findLiveItemsLowerBound(d, 0, 10.0, 11.0)); // check a series with one item d.addSeries("S2", new double[][] {{0.0}, {9.9}}); assertEquals(0, RendererUtils.findLiveItemsLowerBound(d, 1, 0.0, 1.1)); assertEquals(0, RendererUtils.findLiveItemsLowerBound(d, 1, 2.0, 3.3)); // check a series with two items d.addSeries("S3", new double[][] {{0.0, 1.0}, {9.9, 9.9}}); assertEquals(0, RendererUtils.findLiveItemsLowerBound(d, 2, 0.0, 1.1)); assertEquals(1, RendererUtils.findLiveItemsLowerBound(d, 2, 1.0, 2.2)); assertEquals(1, RendererUtils.findLiveItemsLowerBound(d, 2, 2.0, 3.3)); assertEquals(1, RendererUtils.findLiveItemsLowerBound(d, 2, 3.0, 4.4)); // check a series with three items d.addSeries("S4", new double[][] {{1.0, 2.0, 1.5}, {9.9, 9.9, 9.9}}); assertEquals(0, RendererUtils.findLiveItemsLowerBound(d, 3, 0.0, 1.1)); assertEquals(0, RendererUtils.findLiveItemsLowerBound(d, 3, 1.0, 2.2)); assertEquals(1, RendererUtils.findLiveItemsLowerBound(d, 3, 2.0, 3.3)); assertEquals(2, RendererUtils.findLiveItemsLowerBound(d, 3, 3.0, 4.4)); // check a series with four items d.addSeries("S5", new double[][] {{1.0, 2.0, 1.5, 1.8}, {9.9, 9.9, 9.9, 9.9}}); assertEquals(0, RendererUtils.findLiveItemsLowerBound(d, 4, 0.0, 1.1)); assertEquals(0, RendererUtils.findLiveItemsLowerBound(d, 4, 1.0, 2.2)); assertEquals(1, RendererUtils.findLiveItemsLowerBound(d, 4, 2.0, 3.3)); assertEquals(3, RendererUtils.findLiveItemsLowerBound(d, 4, 3.0, 4.4)); assertEquals(3, RendererUtils.findLiveItemsLowerBound(d, 4, 4.0, 5.5)); } /** * Some checks for the findLiveItemsLowerBound() method when the dataset is * ASCENDING. */ @Test public void testFindLiveItemsLowerBound_Ascending() { DefaultXYDataset d = new DefaultXYDataset() { @Override public DomainOrder getDomainOrder() { // we're doing this for testing only, and make sure that we // only add data in ascending order by x-value return DomainOrder.ASCENDING; } }; // check a series with no items d.addSeries("S1", new double[][] {{}, {}}); assertEquals(0, RendererUtils.findLiveItemsLowerBound(d, 0, 10.0, 11.1)); // check a series with one item d.addSeries("S2", new double[][] {{1.0}, {9.9}}); assertEquals(0, RendererUtils.findLiveItemsLowerBound(d, 1, 0.0, 1.1)); assertEquals(0, RendererUtils.findLiveItemsLowerBound(d, 1, 2.0, 2.2)); // check a series with two items d.addSeries("S3", new double[][] {{1.0, 2.0}, {9.9, 9.9}}); assertEquals(0, RendererUtils.findLiveItemsLowerBound(d, 2, 0.0, 1.1)); assertEquals(0, RendererUtils.findLiveItemsLowerBound(d, 2, 1.0, 2.2)); assertEquals(1, RendererUtils.findLiveItemsLowerBound(d, 2, 2.0, 3.3)); assertEquals(1, RendererUtils.findLiveItemsLowerBound(d, 2, 3.0, 4.4)); // check a series with three items d.addSeries("S4", new double[][] {{1.0, 2.0, 3.0}, {9.9, 9.9, 9.9}}); assertEquals(0, RendererUtils.findLiveItemsLowerBound(d, 3, 0.0, 1.1)); assertEquals(0, RendererUtils.findLiveItemsLowerBound(d, 3, 1.0, 2.2)); assertEquals(1, RendererUtils.findLiveItemsLowerBound(d, 3, 2.0, 3.3)); assertEquals(2, RendererUtils.findLiveItemsLowerBound(d, 3, 3.0, 4.4)); // check a series with four items d.addSeries("S5", new double[][] {{1.0, 2.0, 3.0, 4.0}, {9.9, 9.9, 9.9, 9.9}}); assertEquals(0, RendererUtils.findLiveItemsLowerBound(d, 4, 0.0, 1.1)); assertEquals(0, RendererUtils.findLiveItemsLowerBound(d, 4, 1.0, 2.2)); assertEquals(1, RendererUtils.findLiveItemsLowerBound(d, 4, 2.0, 3.3)); assertEquals(2, RendererUtils.findLiveItemsLowerBound(d, 4, 3.0, 4.4)); assertEquals(3, RendererUtils.findLiveItemsLowerBound(d, 4, 4.0, 5.5)); // check a series with repeating items d.addSeries("S5", new double[][] {{1.0, 2.0, 2.0, 2.0, 3.0}, {9.9, 9.9, 9.9, 9.9, 9.9}}); assertEquals(0, RendererUtils.findLiveItemsLowerBound(d, 4, 0.0, 4.0)); assertEquals(0, RendererUtils.findLiveItemsLowerBound(d, 4, 1.0, 4.0)); assertEquals(1, RendererUtils.findLiveItemsLowerBound(d, 4, 2.0, 4.0)); assertEquals(4, RendererUtils.findLiveItemsLowerBound(d, 4, 3.0, 4.0)); } /** * Some checks for the findLiveItemsLowerBound() method when the dataset is * DESCENDING. */ @Test public void testFindLiveItemsLowerBound_Descending() { DefaultXYDataset d = new DefaultXYDataset() { @Override public DomainOrder getDomainOrder() { // we're doing this for testing only, and make sure that we // only add data in descending order by x-value return DomainOrder.DESCENDING; } }; // check a series with no items d.addSeries("S1", new double[][] {{}, {}}); assertEquals(0, RendererUtils.findLiveItemsLowerBound(d, 0, 10.0, 11.0)); // check a series with one item d.addSeries("S2", new double[][] {{1.0}, {9.9}}); assertEquals(0, RendererUtils.findLiveItemsLowerBound(d, 1, 0.0, 1.0)); assertEquals(0, RendererUtils.findLiveItemsLowerBound(d, 1, 1.1, 2.0)); // check a series with two items d.addSeries("S3", new double[][] {{2.0, 1.0}, {9.9, 9.9}}); assertEquals(1, RendererUtils.findLiveItemsLowerBound(d, 2, 0.1, 0.5)); assertEquals(1, RendererUtils.findLiveItemsLowerBound(d, 2, 0.1, 1.0)); assertEquals(0, RendererUtils.findLiveItemsLowerBound(d, 2, 1.1, 2.0)); assertEquals(0, RendererUtils.findLiveItemsLowerBound(d, 2, 2.2, 3.0)); assertEquals(0, RendererUtils.findLiveItemsLowerBound(d, 2, 3.3, 4.0)); // check a series with three items d.addSeries("S4", new double[][] {{3.0, 2.0, 1.0}, {9.9, 9.9, 9.9}}); assertEquals(2, RendererUtils.findLiveItemsLowerBound(d, 3, 0.0, 1.0)); assertEquals(1, RendererUtils.findLiveItemsLowerBound(d, 3, 1.0, 2.0)); assertEquals(0, RendererUtils.findLiveItemsLowerBound(d, 3, 2.0, 3.0)); assertEquals(0, RendererUtils.findLiveItemsLowerBound(d, 3, 3.0, 4.0)); // check a series with four items d.addSeries("S5", new double[][] {{4.0, 3.0, 2.0, 1.0}, {9.9, 9.9, 9.9, 9.9}}); assertEquals(3, RendererUtils.findLiveItemsLowerBound(d, 4, 0.1, 0.5)); assertEquals(3, RendererUtils.findLiveItemsLowerBound(d, 4, 0.1, 1.0)); assertEquals(2, RendererUtils.findLiveItemsLowerBound(d, 4, 1.1, 2.0)); assertEquals(1, RendererUtils.findLiveItemsLowerBound(d, 4, 2.2, 3.0)); assertEquals(0, RendererUtils.findLiveItemsLowerBound(d, 4, 3.3, 4.0)); assertEquals(0, RendererUtils.findLiveItemsLowerBound(d, 4, 4.4, 5.0)); // check a series with repeating items d.addSeries("S6", new double[][] {{3.0, 2.0, 2.0, 2.0, 1.0}, {9.9, 9.9, 9.9, 9.9, 9.9}}); assertEquals(0, RendererUtils.findLiveItemsLowerBound(d, 5, 0.0, 3.0)); assertEquals(1, RendererUtils.findLiveItemsLowerBound(d, 5, 0.0, 2.0)); assertEquals(4, RendererUtils.findLiveItemsLowerBound(d, 5, 0.0, 1.0)); assertEquals(4, RendererUtils.findLiveItemsLowerBound(d, 5, 0.0, 0.5)); } /** * Some checks for the findLiveItemsUpperBound() method when the dataset is * unordered. */ @Test public void testFindLiveItemsUpperBound_Unordered() { DefaultXYDataset d = new DefaultXYDataset(); // check a series with no items d.addSeries("S1", new double[][] {{}, {}}); assertEquals(0, RendererUtils.findLiveItemsUpperBound(d, 0, 10.0, 11.0)); // check a series with one item d.addSeries("S2", new double[][] {{1.0}, {9.9}}); assertEquals(0, RendererUtils.findLiveItemsUpperBound(d, 1, 0.0, 1.1)); assertEquals(0, RendererUtils.findLiveItemsUpperBound(d, 1, 2.0, 3.3)); // check a series with two items d.addSeries("S3", new double[][] {{1.0, 2.0}, {9.9, 9.9}}); assertEquals(0, RendererUtils.findLiveItemsUpperBound(d, 2, 0.0, 1.1)); assertEquals(1, RendererUtils.findLiveItemsUpperBound(d, 2, 1.0, 2.2)); assertEquals(1, RendererUtils.findLiveItemsUpperBound(d, 2, 2.0, 3.3)); assertEquals(1, RendererUtils.findLiveItemsUpperBound(d, 2, 3.0, 4.4)); // check a series with three items d.addSeries("S4", new double[][] {{1.0, 2.0, 1.5}, {9.9, 9.9, 9.9}}); assertEquals(0, RendererUtils.findLiveItemsUpperBound(d, 3, 0.0, 1.1)); assertEquals(2, RendererUtils.findLiveItemsUpperBound(d, 3, 1.0, 2.2)); assertEquals(2, RendererUtils.findLiveItemsUpperBound(d, 3, 2.0, 3.3)); assertEquals(2, RendererUtils.findLiveItemsUpperBound(d, 3, 3.0, 4.4)); // check a series with four items d.addSeries("S5", new double[][] {{1.0, 2.0, 1.5, 1.8}, {9.9, 9.9, 9.9, 9.9}}); assertEquals(0, RendererUtils.findLiveItemsUpperBound(d, 4, 0.0, 1.1)); assertEquals(3, RendererUtils.findLiveItemsUpperBound(d, 4, 1.0, 2.2)); assertEquals(3, RendererUtils.findLiveItemsUpperBound(d, 4, 2.0, 3.3)); assertEquals(3, RendererUtils.findLiveItemsUpperBound(d, 4, 3.0, 4.4)); assertEquals(3, RendererUtils.findLiveItemsUpperBound(d, 4, 4.0, 5.5)); } /** * Some checks for the findLiveItemsUpperBound() method when the dataset is * ASCENDING. */ @Test public void testFindLiveItemsUpperBound_Ascending() { DefaultXYDataset d = new DefaultXYDataset() { @Override public DomainOrder getDomainOrder() { // we're doing this for testing only, and make sure that we // only add data in ascending order by x-value return DomainOrder.ASCENDING; } }; // check a series with no items d.addSeries("S1", new double[][] {{}, {}}); assertEquals(0, RendererUtils.findLiveItemsUpperBound(d, 0, 10.0, 11.1)); // check a series with one item d.addSeries("S2", new double[][] {{1.0}, {9.9}}); assertEquals(0, RendererUtils.findLiveItemsUpperBound(d, 1, 0.0, 1.1)); assertEquals(0, RendererUtils.findLiveItemsUpperBound(d, 1, 2.0, 2.2)); // check a series with two items d.addSeries("S3", new double[][] {{1.0, 2.0}, {9.9, 9.9}}); assertEquals(0, RendererUtils.findLiveItemsUpperBound(d, 2, 0.0, 1.0)); assertEquals(1, RendererUtils.findLiveItemsUpperBound(d, 2, 1.0, 2.2)); assertEquals(1, RendererUtils.findLiveItemsUpperBound(d, 2, 2.0, 3.3)); assertEquals(1, RendererUtils.findLiveItemsUpperBound(d, 2, 3.0, 4.4)); // check a series with three items d.addSeries("S4", new double[][] {{1.0, 2.0, 3.0}, {9.9, 9.9, 9.9}}); assertEquals(0, RendererUtils.findLiveItemsUpperBound(d, 3, 0.0, 1.1)); assertEquals(1, RendererUtils.findLiveItemsUpperBound(d, 3, 1.0, 2.2)); assertEquals(2, RendererUtils.findLiveItemsUpperBound(d, 3, 2.0, 3.3)); assertEquals(2, RendererUtils.findLiveItemsUpperBound(d, 3, 3.0, 4.4)); // check a series with four items d.addSeries("S5", new double[][] {{1.0, 2.0, 3.0, 4.0}, {9.9, 9.9, 9.9, 9.9}}); assertEquals(0, RendererUtils.findLiveItemsUpperBound(d, 4, 0.0, 1.1)); assertEquals(1, RendererUtils.findLiveItemsUpperBound(d, 4, 1.0, 2.2)); assertEquals(2, RendererUtils.findLiveItemsUpperBound(d, 4, 2.0, 3.3)); assertEquals(3, RendererUtils.findLiveItemsUpperBound(d, 4, 3.0, 4.4)); assertEquals(3, RendererUtils.findLiveItemsUpperBound(d, 4, 4.0, 5.5)); // check a series with repeating items d.addSeries("S5", new double[][] {{1.0, 2.0, 2.0, 2.0, 3.0}, {9.9, 9.9, 9.9, 9.9, 9.9}}); assertEquals(0, RendererUtils.findLiveItemsUpperBound(d, 4, 0.0, 1.0)); assertEquals(3, RendererUtils.findLiveItemsUpperBound(d, 4, 0.0, 2.0)); assertEquals(4, RendererUtils.findLiveItemsUpperBound(d, 4, 0.0, 3.0)); assertEquals(4, RendererUtils.findLiveItemsUpperBound(d, 4, 0.0, 4.0)); } /** * Some checks for the findLiveItemsUpperBound() method when the dataset is * DESCENDING. */ @Test public void testFindLiveItemsUpperBound_Descending() { DefaultXYDataset d = new DefaultXYDataset() { @Override public DomainOrder getDomainOrder() { // we're doing this for testing only, and make sure that we // only add data in descending order by x-value return DomainOrder.DESCENDING; } }; // check a series with no items d.addSeries("S1", new double[][] {{}, {}}); assertEquals(0, RendererUtils.findLiveItemsUpperBound(d, 0, 10.0, 11.0)); // check a series with one item d.addSeries("S2", new double[][] {{1.0}, {9.9}}); assertEquals(0, RendererUtils.findLiveItemsUpperBound(d, 1, 0.0, 1.0)); assertEquals(0, RendererUtils.findLiveItemsUpperBound(d, 1, 1.1, 2.0)); // check a series with two items d.addSeries("S3", new double[][] {{2.0, 1.0}, {9.9, 9.9}}); assertEquals(1, RendererUtils.findLiveItemsUpperBound(d, 2, 0.1, 0.5)); assertEquals(1, RendererUtils.findLiveItemsUpperBound(d, 2, 0.1, 1.0)); assertEquals(0, RendererUtils.findLiveItemsUpperBound(d, 2, 1.1, 2.0)); assertEquals(0, RendererUtils.findLiveItemsUpperBound(d, 2, 2.2, 3.0)); assertEquals(0, RendererUtils.findLiveItemsUpperBound(d, 2, 3.3, 4.0)); // check a series with three items d.addSeries("S4", new double[][] {{3.0, 2.0, 1.0}, {9.9, 9.9, 9.9}}); assertEquals(2, RendererUtils.findLiveItemsUpperBound(d, 3, 0.0, 1.0)); assertEquals(2, RendererUtils.findLiveItemsUpperBound(d, 3, 1.0, 2.0)); assertEquals(1, RendererUtils.findLiveItemsUpperBound(d, 3, 2.0, 3.0)); assertEquals(0, RendererUtils.findLiveItemsUpperBound(d, 3, 3.0, 4.0)); // check a series with four items d.addSeries("S5", new double[][] {{4.0, 3.0, 2.0, 1.0}, {9.9, 9.9, 9.9, 9.9}}); assertEquals(3, RendererUtils.findLiveItemsUpperBound(d, 4, 0.1, 0.5)); assertEquals(3, RendererUtils.findLiveItemsUpperBound(d, 4, 0.1, 1.0)); assertEquals(2, RendererUtils.findLiveItemsUpperBound(d, 4, 1.1, 2.0)); assertEquals(1, RendererUtils.findLiveItemsUpperBound(d, 4, 2.2, 3.0)); assertEquals(0, RendererUtils.findLiveItemsUpperBound(d, 4, 3.3, 4.0)); assertEquals(0, RendererUtils.findLiveItemsUpperBound(d, 4, 4.4, 5.0)); // check a series with repeating items d.addSeries("S6", new double[][] {{3.0, 2.0, 2.0, 2.0, 1.0}, {9.9, 9.9, 9.9, 9.9, 9.9}}); assertEquals(4, RendererUtils.findLiveItemsUpperBound(d, 5, 0.0, 5.0)); assertEquals(4, RendererUtils.findLiveItemsUpperBound(d, 5, 1.0, 5.0)); assertEquals(3, RendererUtils.findLiveItemsUpperBound(d, 5, 2.0, 5.0)); assertEquals(0, RendererUtils.findLiveItemsUpperBound(d, 5, 3.0, 5.0)); } /** * Checks the bounds calculation for a series where the x-ordering is not * known. See bug 3561093. */ @Test public void test3561093() { XYSeries s = new XYSeries("S1", false); s.add(0.0, 0.0); s.add(21.0, 0.0); s.add(2.0, 0.0); s.add(23.0, 0.0); XYSeriesCollection dataset = new XYSeriesCollection(); dataset.addSeries(s); assertEquals(1, RendererUtils.findLiveItemsLowerBound(dataset, 0, 10.0, 20.0)); assertEquals(2, RendererUtils.findLiveItemsUpperBound(dataset, 0, 10.0, 20.0)); int[] bounds = RendererUtils.findLiveItems(dataset, 0, 10.0, 20.0); assertEquals(1, bounds[0]); assertEquals(2, bounds[1]); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/category/000077500000000000000000000000001463604235500265345ustar00rootroot00000000000000AbstractCategoryItemRendererTest.java000066400000000000000000000300601463604235500357260ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/category/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------------- * AbstractCategoryItemRendererTest.java * ------------------------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.category; import org.junit.jupiter.api.Test; import java.text.DecimalFormat; import java.text.NumberFormat; import org.jfree.chart.LegendItemCollection; import org.jfree.chart.labels.IntervalCategoryItemLabelGenerator; import org.jfree.chart.labels.StandardCategoryItemLabelGenerator; import org.jfree.chart.labels.StandardCategorySeriesLabelGenerator; import org.jfree.chart.labels.StandardCategoryToolTipGenerator; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.urls.StandardCategoryURLGenerator; import org.jfree.data.Range; import org.jfree.data.category.DefaultCategoryDataset; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link AbstractCategoryItemRenderer} class. */ public class AbstractCategoryItemRendererTest { /** * Checks that all fields are distinguished. */ @Test public void testEquals() { BarRenderer r1 = new BarRenderer(); BarRenderer r2 = new BarRenderer(); assertEquals(r1, r2); // the plot field is NOT tested // toolTipGeneratorList r1.setSeriesToolTipGenerator(1, new StandardCategoryToolTipGenerator()); assertNotEquals(r1, r2); r2.setSeriesToolTipGenerator(1, new StandardCategoryToolTipGenerator()); assertEquals(r1, r2); // defaultToolTipGenerator r1.setDefaultToolTipGenerator(new StandardCategoryToolTipGenerator("{2}", NumberFormat.getInstance())); assertNotEquals(r1, r2); r2.setDefaultToolTipGenerator(new StandardCategoryToolTipGenerator("{2}", NumberFormat.getInstance())); assertEquals(r1, r2); // itemLabelGeneratorList r1.setSeriesItemLabelGenerator(1, new StandardCategoryItemLabelGenerator()); assertNotEquals(r1, r2); r2.setSeriesItemLabelGenerator(1, new StandardCategoryItemLabelGenerator()); assertEquals(r1, r2); // defaultItemLabelGenerator r1.setDefaultItemLabelGenerator(new StandardCategoryItemLabelGenerator( "{2}", NumberFormat.getInstance())); assertNotEquals(r1, r2); r2.setDefaultItemLabelGenerator(new StandardCategoryItemLabelGenerator( "{2}", NumberFormat.getInstance())); assertEquals(r1, r2); // urlGeneratorList r1.setSeriesItemURLGenerator(1, new StandardCategoryURLGenerator()); assertNotEquals(r1, r2); r2.setSeriesItemURLGenerator(1, new StandardCategoryURLGenerator()); assertEquals(r1, r2); // defaultItemURLGenerator r1.setDefaultItemURLGenerator(new StandardCategoryURLGenerator( "abc.html")); assertNotEquals(r1, r2); r2.setDefaultItemURLGenerator(new StandardCategoryURLGenerator( "abc.html")); assertEquals(r1, r2); // legendItemLabelGenerator r1.setLegendItemLabelGenerator(new StandardCategorySeriesLabelGenerator( "XYZ")); assertNotEquals(r1, r2); r2.setLegendItemLabelGenerator(new StandardCategorySeriesLabelGenerator( "XYZ")); assertEquals(r1, r2); // legendItemToolTipGenerator r1.setLegendItemToolTipGenerator( new StandardCategorySeriesLabelGenerator("ToolTip")); assertNotEquals(r1, r2); r2.setLegendItemToolTipGenerator( new StandardCategorySeriesLabelGenerator("ToolTip")); assertEquals(r1, r2); // legendItemURLGenerator r1.setLegendItemURLGenerator( new StandardCategorySeriesLabelGenerator("URL")); assertNotEquals(r1, r2); r2.setLegendItemURLGenerator( new StandardCategorySeriesLabelGenerator("URL")); assertEquals(r1, r2); } @Test public void testEquals_ObjectList() { BarRenderer r1 = new BarRenderer(); r1.setSeriesItemLabelGenerator(0, new StandardCategoryItemLabelGenerator()); BarRenderer r2 = new BarRenderer(); r2.setSeriesItemLabelGenerator(0, new StandardCategoryItemLabelGenerator()); assertEquals(r1, r2); r2.setSeriesItemLabelGenerator(1, new StandardCategoryItemLabelGenerator("X", new DecimalFormat("0.0"))); assertNotEquals(r1, r2); } @Test public void testEquals_ObjectList2() { BarRenderer r1 = new BarRenderer(); r1.setSeriesToolTipGenerator(0, new StandardCategoryToolTipGenerator()); BarRenderer r2 = new BarRenderer(); r2.setSeriesToolTipGenerator(0, new StandardCategoryToolTipGenerator()); assertEquals(r1, r2); r2.setSeriesToolTipGenerator(1, new StandardCategoryToolTipGenerator("X", new DecimalFormat("0.0"))); assertNotEquals(r1, r2); } @Test public void testEquals_ObjectList3() { BarRenderer r1 = new BarRenderer(); r1.setSeriesItemURLGenerator(0, new StandardCategoryURLGenerator()); BarRenderer r2 = new BarRenderer(); r2.setSeriesItemURLGenerator(0, new StandardCategoryURLGenerator()); assertEquals(r1, r2); r2.setSeriesItemURLGenerator(1, new StandardCategoryURLGenerator()); assertNotEquals(r1, r2); } /** * Confirm that cloning works. */ @Test public void testCloning1() throws CloneNotSupportedException { AbstractCategoryItemRenderer r1 = new BarRenderer(); AbstractCategoryItemRenderer r2 = (BarRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); r1 = new BarRenderer(); r1.setSeriesItemLabelGenerator(0, new StandardCategoryItemLabelGenerator()); r2 = (BarRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); r1 = new BarRenderer(); r1.setDefaultItemLabelGenerator(new StandardCategoryItemLabelGenerator()); r2 = (BarRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Confirm that cloning works. */ @Test public void testCloning2() throws CloneNotSupportedException { BarRenderer r1 = new BarRenderer(); r1.setDefaultItemLabelGenerator(new IntervalCategoryItemLabelGenerator()); BarRenderer r2 = (BarRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); r1 = new BarRenderer(); r1.setSeriesItemLabelGenerator(0, new IntervalCategoryItemLabelGenerator()); r2 = (BarRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); r1 = new BarRenderer(); r1.setDefaultItemLabelGenerator(new IntervalCategoryItemLabelGenerator()); r2 = (BarRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Check that the legendItemLabelGenerator is cloned. */ @Test public void testCloning_LegendItemLabelGenerator() throws CloneNotSupportedException { StandardCategorySeriesLabelGenerator generator = new StandardCategorySeriesLabelGenerator("Series {0}"); BarRenderer r1 = new BarRenderer(); r1.setLegendItemLabelGenerator(generator); BarRenderer r2 = (BarRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); // check that the generator has been cloned assertNotSame(r1.getLegendItemLabelGenerator(), r2.getLegendItemLabelGenerator()); } /** * Check that the legendItemToolTipGenerator is cloned. */ @Test public void testCloning_LegendItemToolTipGenerator() throws CloneNotSupportedException { StandardCategorySeriesLabelGenerator generator = new StandardCategorySeriesLabelGenerator("Series {0}"); BarRenderer r1 = new BarRenderer(); r1.setLegendItemToolTipGenerator(generator); BarRenderer r2 = (BarRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); // check that the generator has been cloned assertNotSame(r1.getLegendItemToolTipGenerator(), r2.getLegendItemToolTipGenerator()); } /** * Check that the legendItemURLGenerator is cloned. */ @Test public void testCloning_LegendItemURLGenerator() throws CloneNotSupportedException { StandardCategorySeriesLabelGenerator generator = new StandardCategorySeriesLabelGenerator("Series {0}"); BarRenderer r1 = new BarRenderer(); r1.setLegendItemURLGenerator(generator); BarRenderer r2 = (BarRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); // check that the generator has been cloned assertNotSame(r1.getLegendItemURLGenerator(), r2.getLegendItemURLGenerator()); } /** * Some checks for the findRangeBounds() method. */ @Test public void testFindRangeBounds() { AbstractCategoryItemRenderer r = new LineAndShapeRenderer(); assertNull(r.findRangeBounds(null)); // an empty dataset should return a null range DefaultCategoryDataset dataset = new DefaultCategoryDataset(); assertNull(r.findRangeBounds(dataset)); dataset.addValue(1.0, "R1", "C1"); assertEquals(new Range(1.0, 1.0), r.findRangeBounds(dataset)); dataset.addValue(-2.0, "R1", "C2"); assertEquals(new Range(-2.0, 1.0), r.findRangeBounds(dataset)); dataset.addValue(null, "R1", "C3"); assertEquals(new Range(-2.0, 1.0), r.findRangeBounds(dataset)); } /** * A test that reproduces the problem reported in bug 2947660. */ @Test public void test2947660() { AbstractCategoryItemRenderer r = new LineAndShapeRenderer(); assertNotNull(r.getLegendItems()); assertEquals(0, r.getLegendItems().getItemCount()); DefaultCategoryDataset dataset = new DefaultCategoryDataset(); CategoryPlot plot = new CategoryPlot(); plot.setDataset(dataset); plot.setRenderer(r); assertEquals(0, r.getLegendItems().getItemCount()); dataset.addValue(1.0, "S1", "C1"); LegendItemCollection lic = r.getLegendItems(); assertEquals(1, lic.getItemCount()); assertEquals("S1", lic.get(0).getLabel()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/category/AreaRendererTest.java000066400000000000000000000110521463604235500325750ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * AreaRendererTest.java * --------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.category; import org.jfree.chart.JFreeChart; import org.jfree.chart.LegendItem; import org.jfree.chart.TestUtils; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.renderer.AreaRendererEndType; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.category.DefaultCategoryDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link AreaRenderer} class. */ public class AreaRendererTest { /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { AreaRenderer r1 = new AreaRenderer(); AreaRenderer r2 = new AreaRenderer(); assertEquals(r1, r2); r1.setEndType(AreaRendererEndType.LEVEL); assertNotEquals(r1, r2); r2.setEndType(AreaRendererEndType.LEVEL); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { AreaRenderer r1 = new AreaRenderer(); AreaRenderer r2 = new AreaRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { AreaRenderer r1 = new AreaRenderer(); AreaRenderer r2 = (AreaRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Check that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { AreaRenderer r1 = new AreaRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { AreaRenderer r1 = new AreaRenderer(); AreaRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * A check for the datasetIndex and seriesIndex fields in the LegendItem * returned by the getLegendItem() method. */ @Test public void testGetLegendItemSeriesIndex() { DefaultCategoryDataset dataset0 = new DefaultCategoryDataset(); dataset0.addValue(21.0, "R1", "C1"); dataset0.addValue(22.0, "R2", "C1"); DefaultCategoryDataset dataset1 = new DefaultCategoryDataset(); dataset1.addValue(23.0, "R3", "C1"); dataset1.addValue(24.0, "R4", "C1"); dataset1.addValue(25.0, "R5", "C1"); AreaRenderer r = new AreaRenderer(); CategoryPlot plot = new CategoryPlot(dataset0, new CategoryAxis("x"), new NumberAxis("y"), r); plot.setDataset(1, dataset1); /*JFreeChart chart =*/ new JFreeChart(plot); LegendItem li = r.getLegendItem(1, 2); assertEquals("R5", li.getLabel()); assertEquals(1, li.getDatasetIndex()); assertEquals(2, li.getSeriesIndex()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/category/BarRendererTest.java000066400000000000000000000234061463604235500324370ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * BarRendererTest.java * -------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.category; import java.awt.Color; import org.jfree.chart.JFreeChart; import org.jfree.chart.LegendItem; import org.jfree.chart.TestUtils; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.labels.ItemLabelAnchor; import org.jfree.chart.labels.ItemLabelPosition; import org.jfree.chart.labels.StandardCategoryItemLabelGenerator; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.renderer.RendererChangeDetector; import org.jfree.chart.ui.GradientPaintTransformType; import org.jfree.chart.ui.StandardGradientPaintTransformer; import org.jfree.chart.ui.TextAnchor; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.Range; import org.jfree.data.category.DefaultCategoryDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link BarRenderer} class. */ public class BarRendererTest { /** * Test that the equals() method distinguishes all fields. */ @Test public void testEquals() { BarRenderer r1 = new BarRenderer(); BarRenderer r2 = new BarRenderer(); assertEquals(r1, r2); assertEquals(r2, r1); // base value r1.setBase(0.123); assertNotEquals(r1, r2); r2.setBase(0.123); assertEquals(r1, r2); // itemMargin r1.setItemMargin(0.22); assertNotEquals(r1, r2); r2.setItemMargin(0.22); assertEquals(r1, r2); // drawBarOutline r1.setDrawBarOutline(!r1.isDrawBarOutline()); assertNotEquals(r1, r2); r2.setDrawBarOutline(!r2.isDrawBarOutline()); assertEquals(r1, r2); // maximumBarWidth r1.setMaximumBarWidth(0.11); assertNotEquals(r1, r2); r2.setMaximumBarWidth(0.11); assertEquals(r1, r2); // minimumBarLength r1.setMinimumBarLength(0.04); assertNotEquals(r1, r2); r2.setMinimumBarLength(0.04); assertEquals(r1, r2); // gradientPaintTransformer r1.setGradientPaintTransformer(new StandardGradientPaintTransformer( GradientPaintTransformType.CENTER_VERTICAL)); assertNotEquals(r1, r2); r2.setGradientPaintTransformer(new StandardGradientPaintTransformer( GradientPaintTransformType.CENTER_VERTICAL)); assertEquals(r1, r2); // positiveItemLabelPositionFallback r1.setPositiveItemLabelPositionFallback(new ItemLabelPosition( ItemLabelAnchor.INSIDE1, TextAnchor.CENTER)); assertNotEquals(r1, r2); r2.setPositiveItemLabelPositionFallback(new ItemLabelPosition( ItemLabelAnchor.INSIDE1, TextAnchor.CENTER)); assertEquals(r1, r2); // negativeItemLabelPositionFallback r1.setNegativeItemLabelPositionFallback(new ItemLabelPosition( ItemLabelAnchor.INSIDE1, TextAnchor.CENTER)); assertNotEquals(r1, r2); r2.setNegativeItemLabelPositionFallback(new ItemLabelPosition( ItemLabelAnchor.INSIDE1, TextAnchor.CENTER)); assertEquals(r1, r2); // barPainter r1.setBarPainter(new GradientBarPainter(0.1, 0.2, 0.3)); assertNotEquals(r1, r2); r2.setBarPainter(new GradientBarPainter(0.1, 0.2, 0.3)); assertEquals(r1, r2); // shadowsVisible r1.setShadowVisible(false); assertNotEquals(r1, r2); r2.setShadowVisible(false); assertEquals(r1, r2); r1.setShadowPaint(Color.RED); assertNotEquals(r1, r2); r2.setShadowPaint(Color.RED); assertEquals(r1, r2); // shadowXOffset r1.setShadowXOffset(3.3); assertNotEquals(r1, r2); r2.setShadowXOffset(3.3); assertEquals(r1, r2); // shadowYOffset r1.setShadowYOffset(3.3); assertNotEquals(r1, r2); r2.setShadowYOffset(3.3); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { BarRenderer r1 = new BarRenderer(); BarRenderer r2 = new BarRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { BarRenderer r1 = new BarRenderer(); r1.setDefaultItemLabelGenerator(new StandardCategoryItemLabelGenerator()); r1.setBarPainter(new GradientBarPainter(0.11, 0.22, 0.33)); BarRenderer r2 = (BarRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Check that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { BarRenderer r1 = new BarRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { BarRenderer r1 = new BarRenderer(); BarRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * Tests each setter method to ensure that it sends an event notification. */ @Test public void testEventNotification() { RendererChangeDetector detector = new RendererChangeDetector(); BarRenderer r1 = new BarRenderer(); r1.addChangeListener(detector); detector.setNotified(false); r1.setDefaultPaint(Color.RED); assertTrue(detector.getNotified()); } /** * Some checks for the getLegendItem() method. */ @Test public void testGetLegendItem() { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.addValue(21.0, "R1", "C1"); BarRenderer r = new BarRenderer(); CategoryPlot plot = new CategoryPlot(dataset, new CategoryAxis("x"), new NumberAxis("y"), r); /*JFreeChart chart =*/ new JFreeChart(plot); LegendItem li = r.getLegendItem(0, 0); assertNotNull(li); r.setSeriesVisibleInLegend(0, Boolean.FALSE); li = r.getLegendItem(0, 0); assertNull(li); } /** * A check for the datasetIndex and seriesIndex fields in the LegendItem * returned by the getLegendItem() method. */ @Test public void testGetLegendItemSeriesIndex() { DefaultCategoryDataset dataset0 = new DefaultCategoryDataset(); dataset0.addValue(21.0, "R1", "C1"); dataset0.addValue(22.0, "R2", "C1"); DefaultCategoryDataset dataset1 = new DefaultCategoryDataset(); dataset1.addValue(23.0, "R3", "C1"); dataset1.addValue(24.0, "R4", "C1"); dataset1.addValue(25.0, "R5", "C1"); BarRenderer r = new BarRenderer(); CategoryPlot plot = new CategoryPlot(dataset0, new CategoryAxis("x"), new NumberAxis("y"), r); plot.setDataset(1, dataset1); /*JFreeChart chart =*/ new JFreeChart(plot); LegendItem li = r.getLegendItem(1, 2); assertEquals("R5", li.getLabel()); assertEquals(1, li.getDatasetIndex()); assertEquals(2, li.getSeriesIndex()); } /** * Some checks for the findRangeBounds() method. */ @Test public void testFindRangeBounds() { BarRenderer r = new BarRenderer(); assertNull(r.findRangeBounds(null)); // an empty dataset should return a null range DefaultCategoryDataset dataset = new DefaultCategoryDataset(); assertNull(r.findRangeBounds(dataset)); dataset.addValue(1.0, "R1", "C1"); assertEquals(new Range(0.0, 1.0), r.findRangeBounds(dataset)); r.setIncludeBaseInRange(false); assertEquals(new Range(1.0, 1.0), r.findRangeBounds(dataset)); r.setIncludeBaseInRange(true); dataset.addValue(-2.0, "R1", "C2"); assertEquals(new Range(-2.0, 1.0), r.findRangeBounds(dataset)); dataset.addValue(null, "R1", "C3"); assertEquals(new Range(-2.0, 1.0), r.findRangeBounds(dataset)); dataset.addValue(-6.0, "R2", "C1"); assertEquals(new Range(-6.0, 1.0), r.findRangeBounds(dataset)); r.setSeriesVisible(1, Boolean.FALSE); assertEquals(new Range(-2.0, 1.0), r.findRangeBounds(dataset)); } } BoxAndWhiskerRendererTest.java000066400000000000000000000445431463604235500343710ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/category/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------ * BoxAndWhiskerRendererTest.java * ------------------------------ * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.category; import java.awt.Color; import java.awt.GradientPaint; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.List; import org.jfree.chart.ChartRenderingInfo; import org.jfree.chart.JFreeChart; import org.jfree.chart.LegendItem; import org.jfree.chart.TestUtils; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.category.DefaultCategoryDataset; import org.jfree.data.statistics.BoxAndWhiskerItem; import org.jfree.data.statistics.DefaultBoxAndWhiskerCategoryDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link BoxAndWhiskerRenderer} class. */ public class BoxAndWhiskerRendererTest { /** * Test that the equals() method distinguishes all fields. */ @Test public void testEquals() { BoxAndWhiskerRenderer r1 = new BoxAndWhiskerRenderer(); BoxAndWhiskerRenderer r2 = new BoxAndWhiskerRenderer(); assertEquals(r1, r2); r1.setArtifactPaint(new GradientPaint(1.0f, 2.0f, Color.YELLOW, 3.0f, 4.0f, Color.BLUE)); assertNotEquals(r1, r2); r2.setArtifactPaint(new GradientPaint(1.0f, 2.0f, Color.YELLOW, 3.0f, 4.0f, Color.BLUE)); assertEquals(r1, r2); r1.setFillBox(!r1.getFillBox()); assertNotEquals(r1, r2); r2.setFillBox(!r2.getFillBox()); assertEquals(r1, r2); r1.setItemMargin(0.11); assertNotEquals(r1, r2); r2.setItemMargin(0.11); assertEquals(r1, r2); r1.setMaximumBarWidth(0.99); assertNotEquals(r1, r2); r2.setMaximumBarWidth(0.99); assertEquals(r1, r2); r1.setMeanVisible(false); assertNotEquals(r1, r2); r2.setMeanVisible(false); assertEquals(r1, r2); r1.setMedianVisible(false); assertNotEquals(r1, r2); r2.setMedianVisible(false); assertEquals(r1, r2); r1.setMinOutlierVisible(false); assertNotEquals(r1, r2); r2.setMinOutlierVisible(false); assertEquals(r1, r2); r1.setMaxOutlierVisible(false); assertNotEquals(r1, r2); r2.setMaxOutlierVisible(false); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { BoxAndWhiskerRenderer r1 = new BoxAndWhiskerRenderer(); BoxAndWhiskerRenderer r2 = new BoxAndWhiskerRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. * * @throws java.lang.CloneNotSupportedException if there is a problem cloning. */ @Test public void testCloning() throws CloneNotSupportedException { BoxAndWhiskerRenderer r1 = new BoxAndWhiskerRenderer(); BoxAndWhiskerRenderer r2 = (BoxAndWhiskerRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Check that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { BoxAndWhiskerRenderer r1 = new BoxAndWhiskerRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { BoxAndWhiskerRenderer r1 = new BoxAndWhiskerRenderer(); BoxAndWhiskerRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * Draws the chart with a {@code null} info object to make sure that * no exceptions are thrown (particularly by code in the renderer). */ @Test public void testDrawWithNullInfo() { try { DefaultBoxAndWhiskerCategoryDataset dataset = new DefaultBoxAndWhiskerCategoryDataset(); dataset.add(new BoxAndWhiskerItem(1.0, 2.0, 0.0, 4.0, 0.5, 4.5, -0.5, 5.5, null), "S1", "C1"); CategoryPlot plot = new CategoryPlot(dataset, new CategoryAxis("Category"), new NumberAxis("Value"), new BoxAndWhiskerRenderer()); JFreeChart chart = new JFreeChart(plot); /* BufferedImage image = */ chart.createBufferedImage(300, 200, null); } catch (NullPointerException e) { fail("No exception should be thrown."); } } /** * A check for bug 1572478 (for the vertical orientation). */ @Test public void testBug1572478Vertical() { DefaultBoxAndWhiskerCategoryDataset dataset = new DefaultBoxAndWhiskerCategoryDataset() { @Override public Number getQ1Value(int row, int column) { return null; } @Override public Number getQ1Value(Comparable rowKey, Comparable columnKey) { return null; } }; List values = new ArrayList<>(); values.add(1.0); values.add(10.0); values.add(100.0); dataset.add(values, "row", "column"); CategoryPlot plot = new CategoryPlot(dataset, new CategoryAxis("x"), new NumberAxis("y"), new BoxAndWhiskerRenderer()); JFreeChart chart = new JFreeChart(plot); boolean success; try { BufferedImage image = new BufferedImage(200 , 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); chart.draw(g2, new Rectangle2D.Double(0, 0, 200, 100), null, new ChartRenderingInfo()); g2.dispose(); success = true; } catch (Exception e) { success = false; } assertTrue(success); } /** * A check for bug 1572478 (for the horizontal orientation). */ @Test public void testBug1572478Horizontal() { DefaultBoxAndWhiskerCategoryDataset dataset = new DefaultBoxAndWhiskerCategoryDataset() { @Override public Number getQ1Value(int row, int column) { return null; } @Override public Number getQ1Value(Comparable rowKey, Comparable columnKey) { return null; } }; List values = new ArrayList<>(); values.add(1.0); values.add(10.0); values.add(100.0); dataset.add(values, "row", "column"); CategoryPlot plot = new CategoryPlot(dataset, new CategoryAxis("x"), new NumberAxis("y"), new BoxAndWhiskerRenderer()); plot.setOrientation(PlotOrientation.HORIZONTAL); JFreeChart chart = new JFreeChart(plot); boolean success; try { BufferedImage image = new BufferedImage(200 , 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); chart.draw(g2, new Rectangle2D.Double(0, 0, 200, 100), null, new ChartRenderingInfo()); g2.dispose(); success = true; } catch (Exception e) { success = false; } assertTrue(success); } /** * Some checks for the getLegendItem() method. */ @Test public void testGetLegendItem() { DefaultBoxAndWhiskerCategoryDataset dataset = new DefaultBoxAndWhiskerCategoryDataset(); List values = new ArrayList<>(); values.add(1.10); values.add(1.45); values.add(1.33); values.add(1.23); dataset.add(values, "R1", "C1"); BoxAndWhiskerRenderer r = new BoxAndWhiskerRenderer(); CategoryPlot plot = new CategoryPlot(dataset, new CategoryAxis("x"), new NumberAxis("y"), r); /*JFreeChart chart =*/ new JFreeChart(plot); LegendItem li = r.getLegendItem(0, 0); assertNotNull(li); r.setSeriesVisibleInLegend(0, Boolean.FALSE); li = r.getLegendItem(0, 0); assertNull(li); } /** * A check for the datasetIndex and seriesIndex fields in the LegendItem * returned by the getLegendItem() method. */ @Test public void testGetLegendItemSeriesIndex() { DefaultCategoryDataset dataset0 = new DefaultCategoryDataset(); dataset0.addValue(21.0, "R1", "C1"); dataset0.addValue(22.0, "R2", "C1"); DefaultCategoryDataset dataset1 = new DefaultCategoryDataset(); dataset1.addValue(23.0, "R3", "C1"); dataset1.addValue(24.0, "R4", "C1"); dataset1.addValue(25.0, "R5", "C1"); BoxAndWhiskerRenderer r = new BoxAndWhiskerRenderer(); CategoryPlot plot = new CategoryPlot(dataset0, new CategoryAxis("x"), new NumberAxis("y"), r); plot.setDataset(1, dataset1); /*JFreeChart chart =*/ new JFreeChart(plot); LegendItem li = r.getLegendItem(1, 2); assertEquals("R5", li.getLabel()); assertEquals(1, li.getDatasetIndex()); assertEquals(2, li.getSeriesIndex()); } /** * Draws a chart where the dataset contains a null mean value. */ @Test public void testDrawWithNullMean() { boolean success; try { DefaultBoxAndWhiskerCategoryDataset dataset = new DefaultBoxAndWhiskerCategoryDataset(); dataset.add(new BoxAndWhiskerItem(null, 2.0, 0.0, 4.0, 0.5, 4.5, -0.5, 5.5, null), "S1", "C1"); CategoryPlot plot = new CategoryPlot(dataset, new CategoryAxis("Category"), new NumberAxis("Value"), new BoxAndWhiskerRenderer()); ChartRenderingInfo info = new ChartRenderingInfo(); JFreeChart chart = new JFreeChart(plot); /* BufferedImage image = */ chart.createBufferedImage(300, 200, info); success = true; } catch (Exception e) { success = false; } assertTrue(success); } /** * Draws a chart where the dataset contains a null median value. */ @Test public void testDrawWithNullMedian() { boolean success; try { DefaultBoxAndWhiskerCategoryDataset dataset = new DefaultBoxAndWhiskerCategoryDataset(); dataset.add(new BoxAndWhiskerItem(1.0, null, 0.0, 4.0, 0.5, 4.5, -0.5, 5.5, null), "S1", "C1"); CategoryPlot plot = new CategoryPlot(dataset, new CategoryAxis("Category"), new NumberAxis("Value"), new BoxAndWhiskerRenderer()); ChartRenderingInfo info = new ChartRenderingInfo(); JFreeChart chart = new JFreeChart(plot); /* BufferedImage image = */ chart.createBufferedImage(300, 200, info); success = true; } catch (Exception e) { success = false; } assertTrue(success); } /** * Draws a chart where the dataset contains a null Q1 value. */ @Test public void testDrawWithNullQ1() { boolean success; try { DefaultBoxAndWhiskerCategoryDataset dataset = new DefaultBoxAndWhiskerCategoryDataset(); dataset.add(new BoxAndWhiskerItem(1.0, 2.0, null, 4.0, 0.5, 4.5, -0.5, 5.5, null), "S1", "C1"); CategoryPlot plot = new CategoryPlot(dataset, new CategoryAxis("Category"), new NumberAxis("Value"), new BoxAndWhiskerRenderer()); ChartRenderingInfo info = new ChartRenderingInfo(); JFreeChart chart = new JFreeChart(plot); /* BufferedImage image = */ chart.createBufferedImage(300, 200, info); success = true; } catch (Exception e) { success = false; } assertTrue(success); } /** * Draws a chart where the dataset contains a null Q3 value. */ @Test public void testDrawWithNullQ3() { boolean success; try { DefaultBoxAndWhiskerCategoryDataset dataset = new DefaultBoxAndWhiskerCategoryDataset(); dataset.add(new BoxAndWhiskerItem(1.0, 2.0, 3.0, null, 0.5, 4.5, -0.5, 5.5, null), "S1", "C1"); CategoryPlot plot = new CategoryPlot(dataset, new CategoryAxis("Category"), new NumberAxis("Value"), new BoxAndWhiskerRenderer()); ChartRenderingInfo info = new ChartRenderingInfo(); JFreeChart chart = new JFreeChart(plot); /* BufferedImage image = */ chart.createBufferedImage(300, 200, info); success = true; } catch (Exception e) { success = false; } assertTrue(success); } /** * Draws a chart where the dataset contains a null min regular value. */ @Test public void testDrawWithNullMinRegular() { boolean success; try { DefaultBoxAndWhiskerCategoryDataset dataset = new DefaultBoxAndWhiskerCategoryDataset(); dataset.add(new BoxAndWhiskerItem(1.0, 2.0, 3.0, 4.0, null, 4.5, -0.5, 5.5, null), "S1", "C1"); CategoryPlot plot = new CategoryPlot(dataset, new CategoryAxis("Category"), new NumberAxis("Value"), new BoxAndWhiskerRenderer()); ChartRenderingInfo info = new ChartRenderingInfo(); JFreeChart chart = new JFreeChart(plot); /* BufferedImage image = */ chart.createBufferedImage(300, 200, info); success = true; } catch (Exception e) { success = false; } assertTrue(success); } /** * Draws a chart where the dataset contains a null max regular value. */ @Test public void testDrawWithNullMaxRegular() { try { DefaultBoxAndWhiskerCategoryDataset dataset = new DefaultBoxAndWhiskerCategoryDataset(); dataset.add(new BoxAndWhiskerItem(1.0, 2.0, 3.0, 4.0, 0.5, null, -0.5, 5.5, null), "S1", "C1"); CategoryPlot plot = new CategoryPlot(dataset, new CategoryAxis("Category"), new NumberAxis("Value"), new BoxAndWhiskerRenderer()); ChartRenderingInfo info = new ChartRenderingInfo(); JFreeChart chart = new JFreeChart(plot); /* BufferedImage image = */ chart.createBufferedImage(300, 200, info); } catch (Exception e) { fail("No exception should be thrown."); } } /** * Draws a chart where the dataset contains a null min outlier value. */ @Test public void testDrawWithNullMinOutlier() { boolean success; try { DefaultBoxAndWhiskerCategoryDataset dataset = new DefaultBoxAndWhiskerCategoryDataset(); dataset.add(new BoxAndWhiskerItem(1.0, 2.0, 3.0, 4.0, 0.5, 4.5, null, 5.5, null), "S1", "C1"); CategoryPlot plot = new CategoryPlot(dataset, new CategoryAxis("Category"), new NumberAxis("Value"), new BoxAndWhiskerRenderer()); ChartRenderingInfo info = new ChartRenderingInfo(); JFreeChart chart = new JFreeChart(plot); /* BufferedImage image = */ chart.createBufferedImage(300, 200, info); success = true; } catch (Exception e) { success = false; } assertTrue(success); } /** * Draws a chart where the dataset contains a null max outlier value. */ @Test public void testDrawWithNullMaxOutlier() { boolean success; try { DefaultBoxAndWhiskerCategoryDataset dataset = new DefaultBoxAndWhiskerCategoryDataset(); dataset.add(new BoxAndWhiskerItem(1.0, 2.0, 3.0, 4.0, 0.5, 4.5, -0.5, null, new ArrayList()), "S1", "C1"); CategoryPlot plot = new CategoryPlot(dataset, new CategoryAxis("Category"), new NumberAxis("Value"), new BoxAndWhiskerRenderer()); ChartRenderingInfo info = new ChartRenderingInfo(); JFreeChart chart = new JFreeChart(plot); /* BufferedImage image = */ chart.createBufferedImage(300, 200, info); success = true; } catch (Exception e) { success = false; } assertTrue(success); } } CategoryStepRendererTest.java000066400000000000000000000104711463604235500342630ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/category/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------- * CategoryStepRendererTest.java * ----------------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.category; import org.jfree.chart.JFreeChart; import org.jfree.chart.LegendItem; import org.jfree.chart.TestUtils; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.category.DefaultCategoryDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link CategoryStepRenderer} class. */ public class CategoryStepRendererTest { /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { CategoryStepRenderer r1 = new CategoryStepRenderer(false); CategoryStepRenderer r2 = new CategoryStepRenderer(false); assertEquals(r1, r2); r1 = new CategoryStepRenderer(true); assertNotEquals(r1, r2); r2 = new CategoryStepRenderer(true); assertEquals(r1, r2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { CategoryStepRenderer r1 = new CategoryStepRenderer(false); CategoryStepRenderer r2 = (CategoryStepRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Check that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { CategoryStepRenderer r1 = new CategoryStepRenderer(false); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { CategoryStepRenderer r1 = new CategoryStepRenderer(); CategoryStepRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * A check for the datasetIndex and seriesIndex fields in the LegendItem * returned by the getLegendItem() method. */ @Test public void testGetLegendItemSeriesIndex() { DefaultCategoryDataset dataset0 = new DefaultCategoryDataset(); dataset0.addValue(21.0, "R1", "C1"); dataset0.addValue(22.0, "R2", "C1"); DefaultCategoryDataset dataset1 = new DefaultCategoryDataset(); dataset1.addValue(23.0, "R3", "C1"); dataset1.addValue(24.0, "R4", "C1"); dataset1.addValue(25.0, "R5", "C1"); CategoryStepRenderer r = new CategoryStepRenderer(); CategoryPlot plot = new CategoryPlot(dataset0, new CategoryAxis("x"), new NumberAxis("y"), r); plot.setDataset(1, dataset1); /*JFreeChart chart =*/ new JFreeChart(plot); LegendItem li = r.getLegendItem(1, 2); assertEquals("R5", li.getLabel()); assertEquals(1, li.getDatasetIndex()); assertEquals(2, li.getSeriesIndex()); } } DefaultCategoryItemRendererTest.java000066400000000000000000000066771463604235500355700ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/category/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------------ * DefaultCategoryItemRendererTest.java * ------------------------------------ * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.category; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link DefaultCategoryItemRenderer} class. */ public class DefaultCategoryItemRendererTest { /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { DefaultCategoryItemRenderer r1 = new DefaultCategoryItemRenderer(); DefaultCategoryItemRenderer r2 = new DefaultCategoryItemRenderer(); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { DefaultCategoryItemRenderer r1 = new DefaultCategoryItemRenderer(); DefaultCategoryItemRenderer r2 = new DefaultCategoryItemRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { DefaultCategoryItemRenderer r1 = new DefaultCategoryItemRenderer(); DefaultCategoryItemRenderer r2 = (DefaultCategoryItemRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Check that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { DefaultCategoryItemRenderer r1 = new DefaultCategoryItemRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DefaultCategoryItemRenderer r1 = new DefaultCategoryItemRenderer(); DefaultCategoryItemRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/category/GanttRendererTest.java000066400000000000000000000077031463604235500330120ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * GanttRendererTest.java * ---------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.category; import java.awt.Color; import java.awt.GradientPaint; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link GanttRenderer} class. */ public class GanttRendererTest { /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { GanttRenderer r1 = new GanttRenderer(); GanttRenderer r2 = new GanttRenderer(); assertEquals(r1, r2); r1.setCompletePaint(Color.YELLOW); assertNotEquals(r1, r2); r2.setCompletePaint(Color.YELLOW); assertEquals(r1, r2); r1.setIncompletePaint(Color.GREEN); assertNotEquals(r1, r2); r2.setIncompletePaint(Color.GREEN); assertEquals(r1, r2); r1.setStartPercent(0.11); assertNotEquals(r1, r2); r2.setStartPercent(0.11); assertEquals(r1, r2); r1.setEndPercent(0.88); assertNotEquals(r1, r2); r2.setEndPercent(0.88); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { GanttRenderer r1 = new GanttRenderer(); GanttRenderer r2 = new GanttRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { GanttRenderer r1 = new GanttRenderer(); GanttRenderer r2 = (GanttRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Check that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { GanttRenderer r1 = new GanttRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { GanttRenderer r1 = new GanttRenderer(); r1.setCompletePaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); r1.setIncompletePaint(new GradientPaint(4.0f, 3.0f, Color.RED, 2.0f, 1.0f, Color.BLUE)); GanttRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/category/GradientBarPainterTest.java000066400000000000000000000072371463604235500337550ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * GradientBarPainterTest.java * --------------------------- * (C) Copyright 2008-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.category; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link GradientBarPainter} class. */ public class GradientBarPainterTest { /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { GradientBarPainter p1 = new GradientBarPainter(0.1, 0.2, 0.3); GradientBarPainter p2 = new GradientBarPainter(0.1, 0.2, 0.3); assertEquals(p1, p2); p1 = new GradientBarPainter(0.11, 0.2, 0.3); assertNotEquals(p1, p2); p2 = new GradientBarPainter(0.11, 0.2, 0.3); assertEquals(p1, p2); p1 = new GradientBarPainter(0.11, 0.22, 0.3); assertNotEquals(p1, p2); p2 = new GradientBarPainter(0.11, 0.22, 0.3); assertEquals(p1, p2); p1 = new GradientBarPainter(0.11, 0.22, 0.33); assertNotEquals(p1, p2); p2 = new GradientBarPainter(0.11, 0.22, 0.33); assertEquals(p1, p2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { GradientBarPainter p1 = new GradientBarPainter(0.1, 0.2, 0.3); GradientBarPainter p2 = new GradientBarPainter(0.1, 0.2, 0.3); assertEquals(p1, p2); int h1 = p1.hashCode(); int h2 = p2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning isn't implemented (it isn't required, because * instances of the class are immutable). */ @Test public void testCloning() { GradientBarPainter p1 = new GradientBarPainter(0.1, 0.2, 0.3); assertFalse(p1 instanceof Cloneable); assertFalse(p1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { GradientBarPainter p1 = new GradientBarPainter(0.1, 0.2, 0.3); GradientBarPainter p2 = TestUtils.serialised(p1); assertEquals(p1, p2); } } GroupedStackedBarRendererTest.java000066400000000000000000000134311463604235500352020ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/category/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------------- * GroupedStackedBarRendererTest.java * ---------------------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.category; import org.jfree.chart.JFreeChart; import org.jfree.chart.TestUtils; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.KeyToGroupMap; import org.jfree.data.Range; import org.jfree.data.category.DefaultCategoryDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link GroupedStackedBarRenderer} class. */ public class GroupedStackedBarRendererTest { /** * Test that the equals() method distinguishes all fields. */ @Test public void testEquals() { GroupedStackedBarRenderer r1 = new GroupedStackedBarRenderer(); GroupedStackedBarRenderer r2 = new GroupedStackedBarRenderer(); assertEquals(r1, r2); assertEquals(r2, r1); // map KeyToGroupMap m1 = new KeyToGroupMap("G1"); m1.mapKeyToGroup("S1", "G2"); r1.setSeriesToGroupMap(m1); assertNotEquals(r1, r2); KeyToGroupMap m2 = new KeyToGroupMap("G1"); m2.mapKeyToGroup("S1", "G2"); r2.setSeriesToGroupMap(m2); assertEquals(r1, r2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { GroupedStackedBarRenderer r1 = new GroupedStackedBarRenderer(); GroupedStackedBarRenderer r2 = (GroupedStackedBarRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Check that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { GroupedStackedBarRenderer r1 = new GroupedStackedBarRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { GroupedStackedBarRenderer r1 = new GroupedStackedBarRenderer(); GroupedStackedBarRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * Draws the chart with a {@code null} info object to make sure that * no exceptions are thrown (particularly by code in the renderer). */ @Test public void testDrawWithNullInfo() { try { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.addValue(1.0, "S1", "C1"); dataset.addValue(2.0, "S1", "C2"); dataset.addValue(3.0, "S2", "C1"); dataset.addValue(4.0, "S2", "C2"); GroupedStackedBarRenderer renderer = new GroupedStackedBarRenderer(); CategoryPlot plot = new CategoryPlot(dataset, new CategoryAxis("Category"), new NumberAxis("Value"), renderer); JFreeChart chart = new JFreeChart(plot); /* BufferedImage image = */ chart.createBufferedImage(300, 200, null); } catch (NullPointerException e) { fail("No exception should be thrown."); } } /** * Some checks for the findRangeBounds() method. */ @Test public void testFindRangeBounds() { GroupedStackedBarRenderer r = new GroupedStackedBarRenderer(); assertNull(r.findRangeBounds(null)); // an empty dataset should return a null range DefaultCategoryDataset dataset = new DefaultCategoryDataset(); assertNull(r.findRangeBounds(dataset)); dataset.addValue(1.0, "R1", "C1"); assertEquals(new Range(0.0, 1.0), r.findRangeBounds(dataset)); dataset.addValue(-2.0, "R1", "C2"); assertEquals(new Range(-2.0, 1.0), r.findRangeBounds(dataset)); dataset.addValue(null, "R1", "C3"); assertEquals(new Range(-2.0, 1.0), r.findRangeBounds(dataset)); KeyToGroupMap m = new KeyToGroupMap("G1"); m.mapKeyToGroup("R1", "G1"); m.mapKeyToGroup("R2", "G1"); m.mapKeyToGroup("R3", "G2"); r.setSeriesToGroupMap(m); dataset.addValue(0.5, "R3", "C1"); assertEquals(new Range(-2.0, 1.0), r.findRangeBounds(dataset)); dataset.addValue(5.0, "R3", "C2"); assertEquals(new Range(-2.0, 5.0), r.findRangeBounds(dataset)); } } IntervalBarRendererTest.java000066400000000000000000000132631463604235500340650ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/category/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------- * IntervalBarRendererTest.java * ---------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.category; import org.jfree.chart.JFreeChart; import org.jfree.chart.TestUtils; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.Range; import org.jfree.data.category.DefaultIntervalCategoryDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link IntervalBarRenderer} class. */ public class IntervalBarRendererTest { /** * Problem that the equals() method distinguishes all fields. */ @Test public void testEquals() { IntervalBarRenderer r1 = new IntervalBarRenderer(); IntervalBarRenderer r2 = new IntervalBarRenderer(); assertEquals(r1, r2); // the renderer should not be equal to a BarRenderer BarRenderer br = new BarRenderer(); assertNotEquals(r1, br); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { IntervalBarRenderer r1 = new IntervalBarRenderer(); IntervalBarRenderer r2 = new IntervalBarRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { IntervalBarRenderer r1 = new IntervalBarRenderer(); IntervalBarRenderer r2 = (IntervalBarRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Check that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { IntervalBarRenderer r1 = new IntervalBarRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { IntervalBarRenderer r1 = new IntervalBarRenderer(); IntervalBarRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * Draws the chart with a {@code null} info object to make sure that * no exceptions are thrown (particularly by code in the renderer). */ @Test public void testDrawWithNullInfo() { try { double[][] starts = new double[][] {{0.1, 0.2, 0.3}, {0.3, 0.4, 0.5}}; double[][] ends = new double[][] {{0.5, 0.6, 0.7}, {0.7, 0.8, 0.9}}; DefaultIntervalCategoryDataset dataset = new DefaultIntervalCategoryDataset(starts, ends); IntervalBarRenderer renderer = new IntervalBarRenderer(); CategoryPlot plot = new CategoryPlot(dataset, new CategoryAxis("Category"), new NumberAxis("Value"), renderer); JFreeChart chart = new JFreeChart(plot); /* BufferedImage image = */ chart.createBufferedImage(300, 200, null); } catch (NullPointerException e) { fail("No exception should be thrown."); } } /** * Some checks for the findRangeBounds() method. */ @Test public void testFindRangeBounds() { IntervalBarRenderer r = new IntervalBarRenderer(); assertNull(r.findRangeBounds(null)); // an empty dataset should return a null range DefaultIntervalCategoryDataset dataset = new DefaultIntervalCategoryDataset(new double[0][0], new double[0][0]); assertNull(r.findRangeBounds(dataset)); double[][] starts = new double[][] {{0.1, 0.2, 0.3}, {0.3, 0.4, 0.5}}; double[][] ends = new double[][] {{0.5, 0.6, 0.7}, {0.7, 0.8, 0.9}}; dataset = new DefaultIntervalCategoryDataset(starts, ends); assertEquals(new Range(0.0, 0.9), r.findRangeBounds(dataset)); r.setIncludeBaseInRange(false); assertEquals(new Range(0.1, 0.9), r.findRangeBounds(dataset)); r.setIncludeBaseInRange(true); r.setSeriesVisible(1, Boolean.FALSE); assertEquals(new Range(0.0, 0.7), r.findRangeBounds(dataset)); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/category/LayeredBarRendererTest.java000066400000000000000000000103451463604235500337430ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * LayeredBarRendererTest.java * --------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.category; import org.jfree.chart.JFreeChart; import org.jfree.chart.TestUtils; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.category.DefaultCategoryDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link LayeredBarRenderer} class. */ public class LayeredBarRendererTest { /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { LayeredBarRenderer r1 = new LayeredBarRenderer(); LayeredBarRenderer r2 = new LayeredBarRenderer(); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { LayeredBarRenderer r1 = new LayeredBarRenderer(); LayeredBarRenderer r2 = new LayeredBarRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { LayeredBarRenderer r1 = new LayeredBarRenderer(); LayeredBarRenderer r2 = (LayeredBarRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Check that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { LayeredBarRenderer r1 = new LayeredBarRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { LayeredBarRenderer r1 = new LayeredBarRenderer(); LayeredBarRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * Draws the chart with a {@code null} info object to make sure that * no exceptions are thrown (particularly by code in the renderer). */ @Test public void testDrawWithNullInfo() { try { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.addValue(1.0, "S1", "C1"); CategoryPlot plot = new CategoryPlot(dataset, new CategoryAxis("Category"), new NumberAxis("Value"), new LayeredBarRenderer()); JFreeChart chart = new JFreeChart(plot); /* BufferedImage image = */ chart.createBufferedImage(300, 200, null); } catch (NullPointerException e) { fail("No exception should be thrown."); } } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/category/LevelRendererTest.java000066400000000000000000000141171463604235500330010ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * LevelRendererTest.java * ---------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.category; import org.jfree.chart.JFreeChart; import org.jfree.chart.LegendItem; import org.jfree.chart.TestUtils; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.category.DefaultCategoryDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link LevelRenderer} class. */ public class LevelRendererTest { /** * Test that the equals() method distinguishes all fields. */ @Test public void testEquals() { LevelRenderer r1 = new LevelRenderer(); LevelRenderer r2 = new LevelRenderer(); assertEquals(r1, r2); assertEquals(r2, r1); r1.setItemMargin(0.123); assertNotEquals(r1, r2); r2.setItemMargin(0.123); assertEquals(r1, r2); r1.setMaximumItemWidth(0.234); assertNotEquals(r1, r2); r2.setMaximumItemWidth(0.234); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { LevelRenderer r1 = new LevelRenderer(); LevelRenderer r2 = new LevelRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { LevelRenderer r1 = new LevelRenderer(); r1.setItemMargin(0.123); r1.setMaximumItemWidth(0.234); LevelRenderer r2 = (LevelRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); assertTrue(checkIndependence(r1, r2)); } /** * Check that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { LevelRenderer r1 = new LevelRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Checks that the two renderers are equal but independent of one another. * * @param r1 renderer 1. * @param r2 renderer 2. * * @return A boolean. */ private boolean checkIndependence(LevelRenderer r1, LevelRenderer r2) { // should be equal... boolean b0 = r1.equals(r2); // and independent... r1.setItemMargin(0.0); boolean b1 = !r1.equals(r2); r2.setItemMargin(0.0); boolean b2 = r1.equals(r2); return b0 && b1 && b2; } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { LevelRenderer r1 = new LevelRenderer(); LevelRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * Draws the chart with a {@code null} info object to make sure that * no exceptions are thrown (particularly by code in the renderer). */ @Test public void testDrawWithNullInfo() { try { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.addValue(1.0, "S1", "C1"); CategoryPlot plot = new CategoryPlot(dataset, new CategoryAxis("Category"), new NumberAxis("Value"), new LevelRenderer()); JFreeChart chart = new JFreeChart(plot); /* BufferedImage image = */ chart.createBufferedImage(300, 200, null); } catch (NullPointerException e) { fail("No exception should be thrown."); } } /** * A check for the datasetIndex and seriesIndex fields in the LegendItem * returned by the getLegendItem() method. */ @Test public void testGetLegendItemSeriesIndex() { DefaultCategoryDataset dataset0 = new DefaultCategoryDataset(); dataset0.addValue(21.0, "R1", "C1"); dataset0.addValue(22.0, "R2", "C1"); DefaultCategoryDataset dataset1 = new DefaultCategoryDataset(); dataset1.addValue(23.0, "R3", "C1"); dataset1.addValue(24.0, "R4", "C1"); dataset1.addValue(25.0, "R5", "C1"); LevelRenderer r = new LevelRenderer(); CategoryPlot plot = new CategoryPlot(dataset0, new CategoryAxis("x"), new NumberAxis("y"), r); plot.setDataset(1, dataset1); /*JFreeChart chart =*/ new JFreeChart(plot); LegendItem li = r.getLegendItem(1, 2); assertEquals("R5", li.getLabel()); assertEquals(1, li.getDatasetIndex()); assertEquals(2, li.getSeriesIndex()); } } LineAndShapeRendererTest.java000066400000000000000000000215331463604235500341460ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/category/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------- * LineAndShapeRendererTest.java * ----------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.category; import org.jfree.chart.JFreeChart; import org.jfree.chart.LegendItem; import org.jfree.chart.TestUtils; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.Range; import org.jfree.data.category.DefaultCategoryDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link LineAndShapeRenderer} class. */ public class LineAndShapeRendererTest { /** * Test that the equals() method distinguishes all fields. */ @Test public void testEquals() { LineAndShapeRenderer r1 = new LineAndShapeRenderer(); LineAndShapeRenderer r2 = new LineAndShapeRenderer(); assertEquals(r1, r2); r1.setDefaultLinesVisible(!r1.getDefaultLinesVisible()); assertNotEquals(r1, r2); r2.setDefaultLinesVisible(r1.getDefaultLinesVisible()); assertEquals(r1, r2); r1.setSeriesLinesVisible(1, true); assertNotEquals(r1, r2); r2.setSeriesLinesVisible(1, true); assertEquals(r1, r2); r1.setDefaultShapesVisible(!r1.getDefaultShapesVisible()); assertNotEquals(r1, r2); r2.setDefaultShapesVisible(r1.getDefaultShapesVisible()); assertEquals(r1, r2); r1.setSeriesShapesVisible(1, true); assertNotEquals(r1, r2); r2.setSeriesShapesVisible(1, true); assertEquals(r1, r2); r1.setSeriesShapesFilled(1, true); assertNotEquals(r1, r2); r2.setSeriesShapesFilled(1, true); assertEquals(r1, r2); r1.setDefaultShapesFilled(false); assertNotEquals(r1, r2); r2.setDefaultShapesFilled(false); assertEquals(r1, r2); r1.setUseOutlinePaint(true); assertNotEquals(r1, r2); r2.setUseOutlinePaint(true); assertEquals(r1, r2); r1.setUseSeriesOffset(true); assertNotEquals(r1, r2); r2.setUseSeriesOffset(true); assertEquals(r1, r2); r1.setItemMargin(0.14); assertNotEquals(r1, r2); r2.setItemMargin(0.14); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { LineAndShapeRenderer r1 = new LineAndShapeRenderer(); LineAndShapeRenderer r2 = new LineAndShapeRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { LineAndShapeRenderer r1 = new LineAndShapeRenderer(); LineAndShapeRenderer r2 = (LineAndShapeRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); assertTrue(checkIndependence(r1, r2)); } /** * Check that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { LineAndShapeRenderer r1 = new LineAndShapeRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Checks that the two renderers are equal but independent of one another. * * @param r1 renderer 1. * @param r2 renderer 2. * * @return A boolean. */ private boolean checkIndependence(LineAndShapeRenderer r1, LineAndShapeRenderer r2) { // should be equal... if (!r1.equals(r2)) { return false; } // and independent... r1.setDefaultLinesVisible(!r1.getDefaultLinesVisible()); if (r1.equals(r2)) { return false; } r2.setDefaultLinesVisible(r1.getDefaultLinesVisible()); if (!r1.equals(r2)) { return false; } r1.setSeriesLinesVisible(1, true); if (r1.equals(r2)) { return false; } r2.setSeriesLinesVisible(1, true); if (!r1.equals(r2)) { return false; } r1.setDefaultShapesVisible(!r1.getDefaultShapesVisible()); if (r1.equals(r2)) { return false; } r2.setDefaultShapesVisible(r1.getDefaultShapesVisible()); if (!r1.equals(r2)) { return false; } r1.setSeriesShapesVisible(1, true); if (r1.equals(r2)) { return false; } r2.setSeriesShapesVisible(1, true); if (!r1.equals(r2)) { return false; } r1.setSeriesShapesFilled(0, false); r2.setSeriesShapesFilled(0, true); if (r1.equals(r2)) { return false; } r2.setSeriesShapesFilled(0, false); if (!r1.equals(r2)) { return false; } r1.setDefaultShapesFilled(false); r2.setDefaultShapesFilled(true); if (r1.equals(r2)) { return false; } r2.setDefaultShapesFilled(false); if (!r1.equals(r2)) { return false; } return true; } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { LineAndShapeRenderer r1 = new LineAndShapeRenderer(); LineAndShapeRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * A check for the datasetIndex and seriesIndex fields in the LegendItem * returned by the getLegendItem() method. */ @Test public void testGetLegendItemSeriesIndex() { DefaultCategoryDataset dataset0 = new DefaultCategoryDataset(); dataset0.addValue(21.0, "R1", "C1"); dataset0.addValue(22.0, "R2", "C1"); DefaultCategoryDataset dataset1 = new DefaultCategoryDataset(); dataset1.addValue(23.0, "R3", "C1"); dataset1.addValue(24.0, "R4", "C1"); dataset1.addValue(25.0, "R5", "C1"); LineAndShapeRenderer r = new LineAndShapeRenderer(); CategoryPlot plot = new CategoryPlot(dataset0, new CategoryAxis("x"), new NumberAxis("y"), r); plot.setDataset(1, dataset1); /*JFreeChart chart =*/ new JFreeChart(plot); LegendItem li = r.getLegendItem(1, 2); assertEquals("R5", li.getLabel()); assertEquals(1, li.getDatasetIndex()); assertEquals(2, li.getSeriesIndex()); } /** * Some checks for the findRangeBounds() method. */ @Test public void testFindRangeBounds() { LineAndShapeRenderer r = new LineAndShapeRenderer(); assertNull(r.findRangeBounds(null)); // an empty dataset should return a null range DefaultCategoryDataset dataset = new DefaultCategoryDataset(); assertNull(r.findRangeBounds(dataset)); dataset.addValue(1.0, "R1", "C1"); assertEquals(new Range(1.0, 1.0), r.findRangeBounds(dataset)); dataset.addValue(-2.0, "R1", "C2"); assertEquals(new Range(-2.0, 1.0), r.findRangeBounds(dataset)); dataset.addValue(null, "R1", "C3"); assertEquals(new Range(-2.0, 1.0), r.findRangeBounds(dataset)); dataset.addValue(-6.0, "R2", "C1"); assertEquals(new Range(-6.0, 1.0), r.findRangeBounds(dataset)); r.setSeriesVisible(1, Boolean.FALSE); assertEquals(new Range(-2.0, 1.0), r.findRangeBounds(dataset)); } } MinMaxCategoryRendererTest.java000066400000000000000000000116571463604235500345500ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/category/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------- * MinMaxCategoryRendererTest.java * ------------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.category; import java.awt.BasicStroke; import java.awt.Color; import java.awt.GradientPaint; import org.jfree.chart.JFreeChart; import org.jfree.chart.TestUtils; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.category.DefaultCategoryDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link MinMaxCategoryRenderer} class. */ public class MinMaxCategoryRendererTest { /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { MinMaxCategoryRenderer r1 = new MinMaxCategoryRenderer(); MinMaxCategoryRenderer r2 = new MinMaxCategoryRenderer(); assertEquals(r1, r2); r1.setDrawLines(true); assertNotEquals(r1, r2); r2.setDrawLines(true); assertEquals(r1, r2); r1.setGroupPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW)); assertNotEquals(r1, r2); r2.setGroupPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW)); assertEquals(r1, r2); r1.setGroupStroke(new BasicStroke(1.2f)); assertNotEquals(r1, r2); r2.setGroupStroke(new BasicStroke(1.2f)); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { MinMaxCategoryRenderer r1 = new MinMaxCategoryRenderer(); MinMaxCategoryRenderer r2 = new MinMaxCategoryRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { MinMaxCategoryRenderer r1 = new MinMaxCategoryRenderer(); MinMaxCategoryRenderer r2 = (MinMaxCategoryRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Check that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { MinMaxCategoryRenderer r1 = new MinMaxCategoryRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { MinMaxCategoryRenderer r1 = new MinMaxCategoryRenderer(); MinMaxCategoryRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * Draws the chart with a {@code null} info object to make sure that * no exceptions are thrown (particularly by code in the renderer). */ @Test public void testDrawWithNullInfo() { try { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.addValue(1.0, "S1", "C1"); CategoryPlot plot = new CategoryPlot(dataset, new CategoryAxis("Category"), new NumberAxis("Value"), new MinMaxCategoryRenderer()); JFreeChart chart = new JFreeChart(plot); /* BufferedImage image = */ chart.createBufferedImage(300, 200, null); } catch (NullPointerException e) { fail(); } } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/category/ScatterRendererTest.java000066400000000000000000000145011463604235500333340ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * ScatterRendererTest.java * ------------------------ * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.category; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.Range; import org.jfree.data.statistics.DefaultMultiValueCategoryDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link ScatterRenderer} class. */ public class ScatterRendererTest { /** * Test that the equals() method distinguishes all fields. */ @Test public void testEquals() { ScatterRenderer r1 = new ScatterRenderer(); ScatterRenderer r2 = new ScatterRenderer(); assertEquals(r1, r2); r1.setSeriesShapesFilled(1, true); assertNotEquals(r1, r2); r2.setSeriesShapesFilled(1, true); assertEquals(r1, r2); r1.setBaseShapesFilled(false); assertNotEquals(r1, r2); r2.setBaseShapesFilled(false); assertEquals(r1, r2); r1.setUseFillPaint(true); assertNotEquals(r1, r2); r2.setUseFillPaint(true); assertEquals(r1, r2); r1.setDrawOutlines(true); assertNotEquals(r1, r2); r2.setDrawOutlines(true); assertEquals(r1, r2); r1.setUseOutlinePaint(true); assertNotEquals(r1, r2); r2.setUseOutlinePaint(true); assertEquals(r1, r2); r1.setUseSeriesOffset(false); assertNotEquals(r1, r2); r2.setUseSeriesOffset(false); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { ScatterRenderer r1 = new ScatterRenderer(); ScatterRenderer r2 = new ScatterRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. * * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { ScatterRenderer r1 = new ScatterRenderer(); ScatterRenderer r2 = (ScatterRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); assertTrue(checkIndependence(r1, r2)); } /** * Check that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { ScatterRenderer r1 = new ScatterRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Checks that the two renderers are equal but independent of one another. * * @param r1 renderer 1. * @param r2 renderer 2. * * @return A boolean. */ private boolean checkIndependence(ScatterRenderer r1, ScatterRenderer r2) { // should be equal... if (!r1.equals(r2)) { return false; } // and independent... r1.setSeriesShapesFilled(1, true); if (r1.equals(r2)) { return false; } r2.setSeriesShapesFilled(1, true); if (!r1.equals(r2)) { return false; } r1.setBaseShapesFilled(false); r2.setBaseShapesFilled(true); if (r1.equals(r2)) { return false; } r2.setBaseShapesFilled(false); if (!r1.equals(r2)) { return false; } return true; } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { ScatterRenderer r1 = new ScatterRenderer(); ScatterRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * Some checks for the findRangeBounds() method. */ @Test public void testFindRangeBounds() { ScatterRenderer r = new ScatterRenderer(); assertNull(r.findRangeBounds(null)); // an empty dataset should return a null range DefaultMultiValueCategoryDataset dataset = new DefaultMultiValueCategoryDataset(); assertNull(r.findRangeBounds(dataset)); List values = Collections.singletonList(1.0); dataset.add(values, "R1", "C1"); assertEquals(new Range(1.0, 1.0), r.findRangeBounds(dataset)); values = Arrays.asList(2.0, 2.2); dataset.add(values, "R1", "C2"); assertEquals(new Range(1.0, 2.2), r.findRangeBounds(dataset)); values = Arrays.asList(-3.0, -3.2); dataset.add(values, "R1", "C3"); assertEquals(new Range(-3.2, 2.2), r.findRangeBounds(dataset)); values = Collections.singletonList(6.0); dataset.add(values, "R2", "C1"); assertEquals(new Range(-3.2, 6.0), r.findRangeBounds(dataset)); r.setSeriesVisible(1, Boolean.FALSE); assertEquals(new Range(-3.2, 2.2), r.findRangeBounds(dataset)); } } StackedAreaRendererTest.java000066400000000000000000000106771463604235500340310ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/category/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------- * StackedAreaRendererTest.java * ---------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.category; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.Range; import org.jfree.data.category.DefaultCategoryDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link StackedAreaRenderer} class. */ public class StackedAreaRendererTest { /** * Some checks for the findRangeBounds() method. */ @Test public void testFindRangeBounds() { StackedAreaRenderer r = new StackedAreaRenderer(); assertNull(r.findRangeBounds(null)); // an empty dataset should return a null range DefaultCategoryDataset dataset = new DefaultCategoryDataset(); assertNull(r.findRangeBounds(dataset)); dataset.addValue(1.0, "R1", "C1"); assertEquals(new Range(0.0, 1.0), r.findRangeBounds(dataset)); dataset.addValue(-2.0, "R1", "C2"); assertEquals(new Range(-2.0, 1.0), r.findRangeBounds(dataset)); dataset.addValue(null, "R1", "C3"); assertEquals(new Range(-2.0, 1.0), r.findRangeBounds(dataset)); dataset.addValue(2.0, "R2", "C1"); assertEquals(new Range(-2.0, 3.0), r.findRangeBounds(dataset)); dataset.addValue(null, "R2", "C2"); assertEquals(new Range(-2.0, 3.0), r.findRangeBounds(dataset)); } /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { StackedAreaRenderer r1 = new StackedAreaRenderer(); StackedAreaRenderer r2 = new StackedAreaRenderer(); assertEquals(r1, r2); r1.setRenderAsPercentages(true); assertNotEquals(r1, r2); r2.setRenderAsPercentages(true); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { StackedAreaRenderer r1 = new StackedAreaRenderer(); StackedAreaRenderer r2 = new StackedAreaRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { StackedAreaRenderer r1 = new StackedAreaRenderer(); StackedAreaRenderer r2 = (StackedAreaRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Check that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { StackedAreaRenderer r1 = new StackedAreaRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { StackedAreaRenderer r1 = new StackedAreaRenderer(); StackedAreaRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/category/StackedBarRendererTest.java000066400000000000000000000107051463604235500337340ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * StackedBarRendererTest.java * --------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.category; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.Range; import org.jfree.data.category.DefaultCategoryDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link StackedBarRenderer} class. */ public class StackedBarRendererTest { /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { StackedBarRenderer r1 = new StackedBarRenderer(); StackedBarRenderer r2 = new StackedBarRenderer(); assertEquals(r1, r2); assertEquals(r2, r1); r1.setRenderAsPercentages(true); assertNotEquals(r1, r2); r2.setRenderAsPercentages(true); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { StackedBarRenderer r1 = new StackedBarRenderer(); StackedBarRenderer r2 = new StackedBarRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { StackedBarRenderer r1 = new StackedBarRenderer(); StackedBarRenderer r2 = (StackedBarRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Check that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { StackedBarRenderer r1 = new StackedBarRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { StackedBarRenderer r1 = new StackedBarRenderer(); StackedBarRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * Some checks for the findRangeBounds() method. */ @Test public void testFindRangeBounds() { StackedBarRenderer r = new StackedBarRenderer(); assertNull(r.findRangeBounds(null)); // an empty dataset should return a null range DefaultCategoryDataset dataset = new DefaultCategoryDataset(); assertNull(r.findRangeBounds(dataset)); dataset.addValue(1.0, "R1", "C1"); assertEquals(new Range(0.0, 1.0), r.findRangeBounds(dataset)); dataset.addValue(-2.0, "R1", "C2"); assertEquals(new Range(-2.0, 1.0), r.findRangeBounds(dataset)); dataset.addValue(null, "R1", "C3"); assertEquals(new Range(-2.0, 1.0), r.findRangeBounds(dataset)); dataset.addValue(2.0, "R2", "C1"); assertEquals(new Range(-2.0, 3.0), r.findRangeBounds(dataset)); dataset.addValue(null, "R2", "C2"); assertEquals(new Range(-2.0, 3.0), r.findRangeBounds(dataset)); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/category/StandardBarPainterTest.java000066400000000000000000000062051463604235500337520ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * StandardBarPainterTest.java * --------------------------- * (C) Copyright 2008-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.category; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; /** * Tests for the {@link StandardBarPainter} class. */ public class StandardBarPainterTest { /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { StandardBarPainter p1 = new StandardBarPainter(); StandardBarPainter p2 = new StandardBarPainter(); assertEquals(p1, p2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { StandardBarPainter p1 = new StandardBarPainter(); StandardBarPainter p2 = new StandardBarPainter(); assertEquals(p1, p2); int h1 = p1.hashCode(); int h2 = p2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning isn't implemented (it isn't required, because * instances of the class are immutable). */ @Test public void testCloning() { StandardBarPainter p1 = new StandardBarPainter(); assertFalse(p1 instanceof Cloneable); assertFalse(p1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { StandardBarPainter p1 = new StandardBarPainter(); StandardBarPainter p2 = TestUtils.serialised(p1); assertEquals(p1, p2); } } StatisticalBarRendererTest.java000066400000000000000000000236751463604235500345750ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/category/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------- * StatisticalBarRendererTest.java * ------------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.category; import java.awt.BasicStroke; import java.awt.Color; import org.jfree.chart.JFreeChart; import org.jfree.chart.TestUtils; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.Range; import org.jfree.data.statistics.DefaultStatisticalCategoryDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link StatisticalBarRenderer} class. */ public class StatisticalBarRendererTest { /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { StatisticalBarRenderer r1 = new StatisticalBarRenderer(); StatisticalBarRenderer r2 = new StatisticalBarRenderer(); assertEquals(r1, r2); r1.setErrorIndicatorPaint(Color.RED); assertNotEquals(r1, r2); r2.setErrorIndicatorPaint(Color.RED); assertEquals(r2, r1); r1.setErrorIndicatorStroke(new BasicStroke(1.5f)); assertNotEquals(r1, r2); r2.setErrorIndicatorStroke(new BasicStroke(1.5f)); assertEquals(r2, r1); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { StatisticalBarRenderer r1 = new StatisticalBarRenderer(); StatisticalBarRenderer r2 = new StatisticalBarRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { StatisticalBarRenderer r1 = new StatisticalBarRenderer(); StatisticalBarRenderer r2 = (StatisticalBarRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Check that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { StatisticalBarRenderer r1 = new StatisticalBarRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { StatisticalBarRenderer r1 = new StatisticalBarRenderer(); StatisticalBarRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * Draws the chart with a {@code null} info object to make sure that * no exceptions are thrown (particularly by code in the renderer). */ @Test public void testDrawWithNullInfo() { try { DefaultStatisticalCategoryDataset dataset = new DefaultStatisticalCategoryDataset(); dataset.add(1.0, 2.0, "S1", "C1"); dataset.add(3.0, 4.0, "S1", "C2"); CategoryPlot plot = new CategoryPlot(dataset, new CategoryAxis("Category"), new NumberAxis("Value"), new StatisticalBarRenderer()); JFreeChart chart = new JFreeChart(plot); /* BufferedImage image = */ chart.createBufferedImage(300, 200, null); } catch (NullPointerException e) { fail("No exception should be thrown."); } } /** * Draws the chart with a {@code null} mean value to make sure that * no exceptions are thrown (particularly by code in the renderer). See * bug report 1779941. */ @Test public void testDrawWithNullMeanVertical() { try { DefaultStatisticalCategoryDataset dataset = new DefaultStatisticalCategoryDataset(); dataset.add(1.0, 2.0, "S1", "C1"); dataset.add(null, 4.0, "S1", "C2"); CategoryPlot plot = new CategoryPlot(dataset, new CategoryAxis("Category"), new NumberAxis("Value"), new StatisticalBarRenderer()); JFreeChart chart = new JFreeChart(plot); /* BufferedImage image = */ chart.createBufferedImage(300, 200, null); } catch (NullPointerException e) { fail("No exception should be thrown."); } } /** * Draws the chart with a {@code null} mean value to make sure that * no exceptions are thrown (particularly by code in the renderer). See * bug report 1779941. */ @Test public void testDrawWithNullMeanHorizontal() { try { DefaultStatisticalCategoryDataset dataset = new DefaultStatisticalCategoryDataset(); dataset.add(1.0, 2.0, "S1", "C1"); dataset.add(null, 4.0, "S1", "C2"); CategoryPlot plot = new CategoryPlot(dataset, new CategoryAxis("Category"), new NumberAxis("Value"), new StatisticalBarRenderer()); plot.setOrientation(PlotOrientation.HORIZONTAL); JFreeChart chart = new JFreeChart(plot); /* BufferedImage image = */ chart.createBufferedImage(300, 200, null); } catch (NullPointerException e) { fail("No exception should be thrown."); } } /** * Draws the chart with a {@code null} standard deviation to make sure * that no exceptions are thrown (particularly by code in the renderer). * See bug report 1779941. */ @Test public void testDrawWithNullDeviationVertical() { try { DefaultStatisticalCategoryDataset dataset = new DefaultStatisticalCategoryDataset(); dataset.add(1.0, 2.0, "S1", "C1"); dataset.add(4.0, null, "S1", "C2"); CategoryPlot plot = new CategoryPlot(dataset, new CategoryAxis("Category"), new NumberAxis("Value"), new StatisticalBarRenderer()); JFreeChart chart = new JFreeChart(plot); /* BufferedImage image = */ chart.createBufferedImage(300, 200, null); } catch (NullPointerException e) { fail("No exception should be thrown."); } } /** * Draws the chart with a {@code null} standard deviation to make sure * that no exceptions are thrown (particularly by code in the renderer). * See bug report 1779941. */ @Test public void testDrawWithNullDeviationHorizontal() { try { DefaultStatisticalCategoryDataset dataset = new DefaultStatisticalCategoryDataset(); dataset.add(1.0, 2.0, "S1", "C1"); dataset.add(4.0, null, "S1", "C2"); CategoryPlot plot = new CategoryPlot(dataset, new CategoryAxis("Category"), new NumberAxis("Value"), new StatisticalBarRenderer()); plot.setOrientation(PlotOrientation.HORIZONTAL); JFreeChart chart = new JFreeChart(plot); /* BufferedImage image = */ chart.createBufferedImage(300, 200, null); } catch (NullPointerException e) { fail("No exception should be thrown."); } } /** * Some checks for the findRangeBounds() method. */ @Test public void testFindRangeBounds() { StatisticalBarRenderer r = new StatisticalBarRenderer(); assertNull(r.findRangeBounds(null)); // an empty dataset should return a null range DefaultStatisticalCategoryDataset dataset = new DefaultStatisticalCategoryDataset(); assertNull(r.findRangeBounds(dataset)); dataset.add(1.0, 0.5, "R1", "C1"); assertEquals(new Range(0.0, 1.5), r.findRangeBounds(dataset)); r.setIncludeBaseInRange(false); assertEquals(new Range(0.5, 1.5), r.findRangeBounds(dataset)); r.setIncludeBaseInRange(true); dataset.add(-2.0, 0.2, "R1", "C2"); assertEquals(new Range(-2.2, 1.5), r.findRangeBounds(dataset)); dataset.add(null, null, "R1", "C3"); assertEquals(new Range(-2.2, 1.5), r.findRangeBounds(dataset)); dataset.add(5.0, 1.0, "R2", "C3"); assertEquals(new Range(-2.2, 6.0), r.findRangeBounds(dataset)); // check that the series visible flag is observed r.setSeriesVisible(1, Boolean.FALSE); assertEquals(new Range(-2.2, 1.5), r.findRangeBounds(dataset)); } } StatisticalLineAndShapeRendererTest.java000066400000000000000000000150171463604235500363530ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/category/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------------------- * StatisticalLineAndShapeRendererTest.java * ---------------------------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.category; import java.awt.Color; import org.jfree.chart.JFreeChart; import org.jfree.chart.TestUtils; import org.jfree.chart.axis.CategoryAxis; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.Range; import org.jfree.data.statistics.DefaultStatisticalCategoryDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link StatisticalLineAndShapeRenderer} class. */ public class StatisticalLineAndShapeRendererTest { /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { StatisticalLineAndShapeRenderer r1 = new StatisticalLineAndShapeRenderer(); StatisticalLineAndShapeRenderer r2 = new StatisticalLineAndShapeRenderer(); assertEquals(r1, r2); assertEquals(r2, r1); r1.setErrorIndicatorPaint(Color.RED); assertNotEquals(r1, r2); r2.setErrorIndicatorPaint(Color.RED); assertEquals(r2, r1); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { StatisticalLineAndShapeRenderer r1 = new StatisticalLineAndShapeRenderer(); StatisticalLineAndShapeRenderer r2 = new StatisticalLineAndShapeRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { StatisticalLineAndShapeRenderer r1 = new StatisticalLineAndShapeRenderer(); StatisticalLineAndShapeRenderer r2 = (StatisticalLineAndShapeRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Check that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { StatisticalLineAndShapeRenderer r1 = new StatisticalLineAndShapeRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { StatisticalLineAndShapeRenderer r1 = new StatisticalLineAndShapeRenderer(); StatisticalLineAndShapeRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * Draws the chart with a {@code null} info object to make sure that * no exceptions are thrown (particularly by code in the renderer). */ @Test public void testDrawWithNullInfo() { try { DefaultStatisticalCategoryDataset dataset = new DefaultStatisticalCategoryDataset(); dataset.add(1.0, 2.0, "S1", "C1"); dataset.add(3.0, 4.0, "S1", "C2"); CategoryPlot plot = new CategoryPlot(dataset, new CategoryAxis("Category"), new NumberAxis("Value"), new StatisticalLineAndShapeRenderer()); JFreeChart chart = new JFreeChart(plot); /* BufferedImage image = */ chart.createBufferedImage(300, 200, null); } catch (NullPointerException e) { fail("No exception should be thrown."); } } /** * A simple test for bug report 1562759. */ @Test public void test1562759() { StatisticalLineAndShapeRenderer r = new StatisticalLineAndShapeRenderer(true, false); assertTrue(r.getDefaultLinesVisible()); assertFalse(r.getDefaultShapesVisible()); r = new StatisticalLineAndShapeRenderer(false, true); assertFalse(r.getDefaultLinesVisible()); assertTrue(r.getDefaultShapesVisible()); } /** * Some checks for the findRangeBounds() method. */ @Test public void testFindRangeBounds() { StatisticalLineAndShapeRenderer r = new StatisticalLineAndShapeRenderer(); assertNull(r.findRangeBounds(null)); // an empty dataset should return a null range DefaultStatisticalCategoryDataset dataset = new DefaultStatisticalCategoryDataset(); assertNull(r.findRangeBounds(dataset)); dataset.add(1.0, 0.5, "R1", "C1"); assertEquals(new Range(0.5, 1.5), r.findRangeBounds(dataset)); dataset.add(-2.0, 0.2, "R1", "C2"); assertEquals(new Range(-2.2, 1.5), r.findRangeBounds(dataset)); dataset.add(null, null, "R1", "C3"); assertEquals(new Range(-2.2, 1.5), r.findRangeBounds(dataset)); dataset.add(5.0, 1.0, "R2", "C3"); assertEquals(new Range(-2.2, 6.0), r.findRangeBounds(dataset)); // check that the series visible flag is observed r.setSeriesVisible(1, Boolean.FALSE); assertEquals(new Range(-2.2, 1.5), r.findRangeBounds(dataset)); } } WaterfallBarRendererTest.java000066400000000000000000000147401463604235500342230ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/category/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------- * WaterfallBarRendererTest.java * ----------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.category; import java.awt.Color; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link WaterfallBarRenderer} class. */ public class WaterfallBarRendererTest { /** * Some tests for the findRangeBounds() method. */ @Test public void testFindRangeBounds() { WaterfallBarRenderer r = new WaterfallBarRenderer(); assertNull(r.findRangeBounds(null)); } /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { WaterfallBarRenderer r1 = new WaterfallBarRenderer(); WaterfallBarRenderer r2 = new WaterfallBarRenderer(); assertEquals(r1, r2); // firstBarPaint; r1.setFirstBarPaint(Color.cyan); assertNotEquals(r1, r2); r2.setFirstBarPaint(Color.cyan); assertEquals(r1, r2); // lastBarPaint; r1.setLastBarPaint(Color.cyan); assertNotEquals(r1, r2); r2.setLastBarPaint(Color.cyan); assertEquals(r1, r2); // positiveBarPaint; r1.setPositiveBarPaint(Color.cyan); assertNotEquals(r1, r2); r2.setPositiveBarPaint(Color.cyan); assertEquals(r1, r2); //private Paint negativeBarPaint; r1.setNegativeBarPaint(Color.cyan); assertNotEquals(r1, r2); r2.setNegativeBarPaint(Color.cyan); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { WaterfallBarRenderer r1 = new WaterfallBarRenderer(); WaterfallBarRenderer r2 = new WaterfallBarRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { WaterfallBarRenderer r1 = new WaterfallBarRenderer(); WaterfallBarRenderer r2 = (WaterfallBarRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); // quick check for independence r1.setFirstBarPaint(Color.YELLOW); assertNotEquals(r1, r2); r2.setFirstBarPaint(Color.YELLOW); assertEquals(r1, r2); } /** * Check that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { WaterfallBarRenderer r1 = new WaterfallBarRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { WaterfallBarRenderer r1 = new WaterfallBarRenderer(); WaterfallBarRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } // /** // * Check that the paint object returned for a middle column with 0 // * difference is the positive bar paint object // */ // @Test // public void testGetSeriesPaintForDifferentValues() { //Color firstPaint = Color.cyan; //Color positivePaint = Color.GREEN; //Color negativePaint = Color.RED; //Color lastPaint = Color.BLUE; //WaterfallBarRenderer waterfallBarRenderer = new WaterfallBarRenderer(firstPaint, positivePaint, negativePaint, lastPaint); // //// Sets of tests for making sure the correct paint object is returned //// for different scenarios. // //// In the first set, the "firstPaint" object is always returned because //// this is first column (regardless of the value of the value difference). //assertSame(firstPaint, waterfallBarRenderer.getSeriesPaintObject(0, 1, 0d)); //assertSame(firstPaint, waterfallBarRenderer.getSeriesPaintObject(0, 2, 1d)); //assertSame(firstPaint, waterfallBarRenderer.getSeriesPaintObject(0, 2, -1d)); // //// In the second set, the "positivePaint" object is returned for middle //// columns which are greater than or equal to 0. //assertSame(positivePaint, waterfallBarRenderer.getSeriesPaintObject(1, 1, 1d)); //assertSame(positivePaint, waterfallBarRenderer.getSeriesPaintObject(1, 1, 0d)); //assertSame(positivePaint, waterfallBarRenderer.getSeriesPaintObject(1, 3, 0d)); // //// In the third set, the "negativePaint" object is returned for middle //// columns which are less than zero. //assertSame(negativePaint, waterfallBarRenderer.getSeriesPaintObject(1, 1, -0.5d)); //assertSame(negativePaint, waterfallBarRenderer.getSeriesPaintObject(1, 3, -0.5d)); //assertSame(negativePaint, waterfallBarRenderer.getSeriesPaintObject(1, 0, -0.5d)); // //// In the last set, the "lastPaint" object is returned because this is the //// last column (regardless of the value of the value difference). //assertSame(lastPaint, waterfallBarRenderer.getSeriesPaintObject(1, 2, 0d)); //assertSame(lastPaint, waterfallBarRenderer.getSeriesPaintObject(1, 2, 1d)); //assertSame(lastPaint, waterfallBarRenderer.getSeriesPaintObject(1, 2, -1d)); //} } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/package.html000066400000000000000000000001631463604235500272000ustar00rootroot00000000000000 JUnit tests. jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/xy/000077500000000000000000000000001463604235500253575ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/xy/AbstractXYItemRendererTest.java000066400000000000000000000144041463604235500334170ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------- * AbstractXYItemRendererTest.java * ------------------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import org.junit.jupiter.api.Test; import org.jfree.chart.labels.StandardXYSeriesLabelGenerator; import org.jfree.chart.labels.StandardXYToolTipGenerator; import org.jfree.chart.labels.StandardXYItemLabelGenerator; import org.jfree.data.Range; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link AbstractXYItemRenderer} class. */ public class AbstractXYItemRendererTest { /** * Creates a test dataset. * * @return A test dataset. */ private XYDataset createDataset1() { XYSeries series = new XYSeries("Series"); series.add(1.0, 1.0); series.add(2.0, 2.0); series.add(3.0, 3.0); XYSeriesCollection dataset = new XYSeriesCollection(); dataset.addSeries(series); return dataset; } private static final double EPSILON = 0.0000000001; /** * Some checks for the findDomainBounds() method. */ @Test public void testFindDomainBounds() { AbstractXYItemRenderer renderer = new StandardXYItemRenderer(); // check the bounds of a simple dataset XYDataset dataset = createDataset1(); Range r = renderer.findDomainBounds(dataset); assertEquals(1.0, r.getLowerBound(), EPSILON); assertEquals(3.0, r.getUpperBound(), EPSILON); // check that a null dataset returns null bounds assertNull(renderer.findDomainBounds(null)); } /** * Some checks for the findRangeBounds() method. */ @Test public void testFindRangeBounds() { AbstractXYItemRenderer renderer = new StandardXYItemRenderer(); // check that a null dataset returns null bounds assertNull(renderer.findRangeBounds(null)); } /** * Check that the legendItemLabelGenerator is cloned. */ @Test public void testCloning_LegendItemLabelGenerator() throws CloneNotSupportedException { StandardXYSeriesLabelGenerator generator = new StandardXYSeriesLabelGenerator("Series {0}"); XYBarRenderer r1 = new XYBarRenderer(); r1.setLegendItemLabelGenerator(generator); XYBarRenderer r2 = (XYBarRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); // check that the generator has been cloned assertNotSame(r1.getLegendItemLabelGenerator(), r2.getLegendItemLabelGenerator()); } /** * Check that the legendItemToolTipGenerator is cloned. */ @Test public void testCloning_LegendItemToolTipGenerator() throws CloneNotSupportedException { StandardXYSeriesLabelGenerator generator = new StandardXYSeriesLabelGenerator("Series {0}"); XYBarRenderer r1 = new XYBarRenderer(); r1.setLegendItemToolTipGenerator(generator); XYBarRenderer r2 = (XYBarRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); // check that the generator has been cloned assertNotSame(r1.getLegendItemToolTipGenerator(), r2.getLegendItemToolTipGenerator()); } /** * Check that the legendItemURLGenerator is cloned. */ @Test public void testCloning_LegendItemURLGenerator() throws CloneNotSupportedException { StandardXYSeriesLabelGenerator generator = new StandardXYSeriesLabelGenerator("Series {0}"); XYBarRenderer r1 = new XYBarRenderer(); r1.setLegendItemURLGenerator(generator); XYBarRenderer r2 = (XYBarRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); // check that the generator has been cloned assertNotSame(r1.getLegendItemURLGenerator(), r2.getLegendItemURLGenerator()); } @Test public void testEquals_ObjectList() { XYBarRenderer r1 = new XYBarRenderer(); r1.setSeriesItemLabelGenerator(0, new StandardXYItemLabelGenerator()); XYBarRenderer r2 = new XYBarRenderer(); r2.setSeriesItemLabelGenerator(0, new StandardXYItemLabelGenerator()); assertEquals(r1, r2); r2.setSeriesItemLabelGenerator(1, new StandardXYItemLabelGenerator("X")); assertNotEquals(r1, r2); } @Test public void testEquals_ObjectList2() { XYBarRenderer r1 = new XYBarRenderer(); r1.setSeriesToolTipGenerator(0, new StandardXYToolTipGenerator()); XYBarRenderer r2 = new XYBarRenderer(); r2.setSeriesToolTipGenerator(0, new StandardXYToolTipGenerator()); assertEquals(r1, r2); r2.setSeriesToolTipGenerator(1, new StandardXYToolTipGenerator()); assertNotEquals(r1, r2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/xy/CandlestickRendererTest.java000066400000000000000000000160571463604235500330060ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------- * CandlestickRendererTest.java * ---------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import java.awt.Color; import java.awt.GradientPaint; import java.util.Date; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.Range; import org.jfree.data.xy.DefaultOHLCDataset; import org.jfree.data.xy.OHLCDataItem; import org.jfree.data.xy.OHLCDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link CandlestickRenderer} class. */ public class CandlestickRendererTest { private static final double EPSILON = 0.0000000001; /** * Some checks for the constructor. */ @Test public void testConstructor() { CandlestickRenderer r1 = new CandlestickRenderer(); // check defaults assertEquals(Color.GREEN, r1.getUpPaint()); assertEquals(Color.RED, r1.getDownPaint()); assertFalse(r1.getUseOutlinePaint()); assertTrue(r1.getDrawVolume()); assertEquals(Color.GRAY, r1.getVolumePaint()); assertEquals(-1.0, r1.getCandleWidth(), EPSILON); } /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { CandlestickRenderer r1 = new CandlestickRenderer(); CandlestickRenderer r2 = new CandlestickRenderer(); assertEquals(r1, r2); // upPaint r1.setUpPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.WHITE)); assertNotEquals(r1, r2); r2.setUpPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.WHITE)); assertEquals(r1, r2); // downPaint r1.setDownPaint(new GradientPaint(5.0f, 6.0f, Color.GREEN, 7.0f, 8.0f, Color.YELLOW)); assertNotEquals(r1, r2); r2.setDownPaint(new GradientPaint(5.0f, 6.0f, Color.GREEN, 7.0f, 8.0f, Color.YELLOW)); assertEquals(r1, r2); // drawVolume r1.setDrawVolume(false); assertNotEquals(r1, r2); r2.setDrawVolume(false); assertEquals(r1, r2); // candleWidth r1.setCandleWidth(3.3); assertNotEquals(r1, r2); r2.setCandleWidth(3.3); assertEquals(r1, r2); // maxCandleWidthInMilliseconds r1.setMaxCandleWidthInMilliseconds(123); assertNotEquals(r1, r2); r2.setMaxCandleWidthInMilliseconds(123); assertEquals(r1, r2); // autoWidthMethod r1.setAutoWidthMethod(CandlestickRenderer.WIDTHMETHOD_SMALLEST); assertNotEquals(r1, r2); r2.setAutoWidthMethod(CandlestickRenderer.WIDTHMETHOD_SMALLEST); assertEquals(r1, r2); // autoWidthFactor r1.setAutoWidthFactor(0.22); assertNotEquals(r1, r2); r2.setAutoWidthFactor(0.22); assertEquals(r1, r2); // autoWidthGap r1.setAutoWidthGap(1.1); assertNotEquals(r1, r2); r2.setAutoWidthGap(1.1); assertEquals(r1, r2); r1.setUseOutlinePaint(true); assertNotEquals(r1, r2); r2.setUseOutlinePaint(true); assertEquals(r1, r2); r1.setVolumePaint(Color.BLUE); assertNotEquals(r1, r2); r2.setVolumePaint(Color.BLUE); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { CandlestickRenderer r1 = new CandlestickRenderer(); CandlestickRenderer r2 = new CandlestickRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { CandlestickRenderer r1 = new CandlestickRenderer(); CandlestickRenderer r2 = (CandlestickRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { CandlestickRenderer r1 = new CandlestickRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { CandlestickRenderer r1 = new CandlestickRenderer(); CandlestickRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * Some checks for the findRangeBounds() method. */ @Test public void testFindRangeBounds() { CandlestickRenderer renderer = new CandlestickRenderer(); OHLCDataItem item1 = new OHLCDataItem(new Date(1L), 2.0, 4.0, 1.0, 3.0, 100); OHLCDataset dataset = new DefaultOHLCDataset("S1", new OHLCDataItem[] {item1}); Range range = renderer.findRangeBounds(dataset); assertEquals(new Range(1.0, 4.0), range); OHLCDataItem item2 = new OHLCDataItem(new Date(1L), -1.0, 3.0, -1.0, 3.0, 100); dataset = new DefaultOHLCDataset("S1", new OHLCDataItem[] {item1, item2}); range = renderer.findRangeBounds(dataset); assertEquals(new Range(-1.0, 4.0), range); // try an empty dataset - should return a null range dataset = new DefaultOHLCDataset("S1", new OHLCDataItem[] {}); range = renderer.findRangeBounds(dataset); assertNull(range); // try a null dataset - should return a null range range = renderer.findRangeBounds(null); assertNull(range); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/xy/ClusteredXYBarRendererTest.java000066400000000000000000000135411463604235500334150ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------- * ClusteredXYBarRendererTest.java * ------------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.Range; import org.jfree.data.xy.DefaultIntervalXYDataset; import org.jfree.data.xy.XYDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link ClusteredXYBarRenderer} class. */ public class ClusteredXYBarRendererTest { /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { ClusteredXYBarRenderer r1 = new ClusteredXYBarRenderer(); ClusteredXYBarRenderer r2 = new ClusteredXYBarRenderer(); assertEquals(r1, r2); assertEquals(r2, r1); r1 = new ClusteredXYBarRenderer(1.2, false); assertNotEquals(r1, r2); r2 = new ClusteredXYBarRenderer(1.2, false); assertEquals(r1, r2); r1 = new ClusteredXYBarRenderer(1.2, true); assertNotEquals(r1, r2); r2 = new ClusteredXYBarRenderer(1.2, true); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { ClusteredXYBarRenderer r1 = new ClusteredXYBarRenderer(); ClusteredXYBarRenderer r2 = new ClusteredXYBarRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { ClusteredXYBarRenderer r1 = new ClusteredXYBarRenderer(); ClusteredXYBarRenderer r2 = (ClusteredXYBarRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { ClusteredXYBarRenderer r1 = new ClusteredXYBarRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { ClusteredXYBarRenderer r1 = new ClusteredXYBarRenderer(); ClusteredXYBarRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } private static final double EPSILON = 0.0000000001; /** * Some checks for the findDomainBounds() method (which requires special * handling when the centerBarAtStartValue flag is set to true). */ @Test public void testFindDomainBounds() { AbstractXYItemRenderer renderer = new ClusteredXYBarRenderer(); XYDataset dataset = createSampleDataset1(); Range r = renderer.findDomainBounds(dataset); assertEquals(0.9, r.getLowerBound(), EPSILON); assertEquals(13.1, r.getUpperBound(), EPSILON); renderer = new ClusteredXYBarRenderer(0.0, true); r = renderer.findDomainBounds(dataset); assertEquals(0.8, r.getLowerBound(), EPSILON); assertEquals(13.0, r.getUpperBound(), EPSILON); // check that a null dataset returns null bounds assertNull(renderer.findDomainBounds(null)); } /** * Creates a sample dataset for testing. * * @return A sample dataset. */ public DefaultIntervalXYDataset createSampleDataset1() { DefaultIntervalXYDataset d = new DefaultIntervalXYDataset(); double[] x1 = new double[] {1.0, 2.0, 3.0}; double[] x1Start = new double[] {0.9, 1.9, 2.9}; double[] x1End = new double[] {1.1, 2.1, 3.1}; double[] y1 = new double[] {4.0, 5.0, 6.0}; double[] y1Start = new double[] {1.09, 2.09, 3.09}; double[] y1End = new double[] {1.11, 2.11, 3.11}; double[][] data1 = new double[][] {x1, x1Start, x1End, y1, y1Start, y1End}; d.addSeries("S1", data1); double[] x2 = new double[] {11.0, 12.0, 13.0}; double[] x2Start = new double[] {10.9, 11.9, 12.9}; double[] x2End = new double[] {11.1, 12.1, 13.1}; double[] y2 = new double[] {14.0, 15.0, 16.0}; double[] y2Start = new double[] {11.09, 12.09, 13.09}; double[] y2End = new double[] {11.11, 12.11, 13.11}; double[][] data2 = new double[][] {x2, x2Start, x2End, y2, y2Start, y2End}; d.addSeries("S2", data2); return d; } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/xy/DeviationRendererTest.java000066400000000000000000000066051463604235500325020ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * DeviationRendererTest.java * -------------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link DeviationRenderer} class. */ public class DeviationRendererTest { /** * Test that the equals() method distinguishes all fields. */ @Test public void testEquals() { // default instances DeviationRenderer r1 = new DeviationRenderer(); DeviationRenderer r2 = new DeviationRenderer(); assertEquals(r1, r2); assertEquals(r2, r1); r1.setAlpha(0.1f); assertNotEquals(r1, r2); r2.setAlpha(0.1f); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { DeviationRenderer r1 = new DeviationRenderer(); DeviationRenderer r2 = new DeviationRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { DeviationRenderer r1 = new DeviationRenderer(); DeviationRenderer r2 = (DeviationRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { DeviationRenderer r1 = new DeviationRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DeviationRenderer r1 = new DeviationRenderer(); DeviationRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/xy/DeviationStepRendererTest.java000066400000000000000000000067331463604235500333400ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------ * DeviationStepRendererTest.java * ------------------------------ * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link DeviationStepRenderer} class. */ public class DeviationStepRendererTest { /** * Test that the equals() method distinguishes all fields. */ @Test public void testEquals() { // default instances DeviationStepRenderer r1 = new DeviationStepRenderer(); DeviationStepRenderer r2 = new DeviationStepRenderer(); assertEquals(r1, r2); assertEquals(r2, r1); r1.setAlpha(0.1f); assertNotEquals(r1, r2); r2.setAlpha(0.1f); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { DeviationStepRenderer r1 = new DeviationStepRenderer(); DeviationStepRenderer r2 = new DeviationStepRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { DeviationStepRenderer r1 = new DeviationStepRenderer(); DeviationStepRenderer r2 = (DeviationStepRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { DeviationStepRenderer r1 = new DeviationStepRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DeviationStepRenderer r1 = new DeviationStepRenderer(); DeviationStepRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } }jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/xy/GradientXYBarPainterTest.java000066400000000000000000000073101463604235500330510ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------- * GradientXYBarPainterTest.java * ----------------------------- * (C) Copyright 2008-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link GradientXYBarPainter} class. */ public class GradientXYBarPainterTest { /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { GradientXYBarPainter p1 = new GradientXYBarPainter(0.1, 0.2, 0.3); GradientXYBarPainter p2 = new GradientXYBarPainter(0.1, 0.2, 0.3); assertEquals(p1, p2); p1 = new GradientXYBarPainter(0.11, 0.2, 0.3); assertNotEquals(p1, p2); p2 = new GradientXYBarPainter(0.11, 0.2, 0.3); assertEquals(p1, p2); p1 = new GradientXYBarPainter(0.11, 0.22, 0.3); assertNotEquals(p1, p2); p2 = new GradientXYBarPainter(0.11, 0.22, 0.3); assertEquals(p1, p2); p1 = new GradientXYBarPainter(0.11, 0.22, 0.33); assertNotEquals(p1, p2); p2 = new GradientXYBarPainter(0.11, 0.22, 0.33); assertEquals(p1, p2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { GradientXYBarPainter p1 = new GradientXYBarPainter(0.1, 0.2, 0.3); GradientXYBarPainter p2 = new GradientXYBarPainter(0.1, 0.2, 0.3); assertEquals(p1, p2); int h1 = p1.hashCode(); int h2 = p2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning isn't implemented (it isn't required, because * instances of the class are immutable). */ @Test public void testCloning() { GradientXYBarPainter p1 = new GradientXYBarPainter(0.1, 0.2, 0.3); assertFalse(p1 instanceof Cloneable); assertFalse(p1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { GradientXYBarPainter p1 = new GradientXYBarPainter(0.1, 0.2, 0.3); GradientXYBarPainter p2 = TestUtils.serialised(p1); assertEquals(p1, p2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/xy/HighLowRendererTest.java000066400000000000000000000125651463604235500321230ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * HighLowRendererTest.java * ------------------------ * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import java.awt.Color; import java.util.Date; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.Range; import org.jfree.data.xy.DefaultOHLCDataset; import org.jfree.data.xy.OHLCDataItem; import org.jfree.data.xy.OHLCDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link HighLowRenderer} class. */ public class HighLowRendererTest { /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { HighLowRenderer r1 = new HighLowRenderer(); HighLowRenderer r2 = new HighLowRenderer(); assertEquals(r1, r2); // drawOpenTicks r1.setDrawOpenTicks(false); assertNotEquals(r1, r2); r2.setDrawOpenTicks(false); assertEquals(r1, r2); // drawCloseTicks r1.setDrawCloseTicks(false); assertNotEquals(r1, r2); r2.setDrawCloseTicks(false); assertEquals(r1, r2); // openTickPaint r1.setOpenTickPaint(Color.RED); assertNotEquals(r1, r2); r2.setOpenTickPaint(Color.RED); assertEquals(r1, r2); // closeTickPaint r1.setCloseTickPaint(Color.BLUE); assertNotEquals(r1, r2); r2.setCloseTickPaint(Color.BLUE); assertEquals(r1, r2); // tickLength r1.setTickLength(99.9); assertNotEquals(r1, r2); r2.setTickLength(99.9); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { HighLowRenderer r1 = new HighLowRenderer(); HighLowRenderer r2 = new HighLowRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { HighLowRenderer r1 = new HighLowRenderer(); r1.setCloseTickPaint(Color.GREEN); HighLowRenderer r2 = (HighLowRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { HighLowRenderer r1 = new HighLowRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { HighLowRenderer r1 = new HighLowRenderer(); r1.setCloseTickPaint(Color.GREEN); HighLowRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * Some checks for the findRangeBounds() method. */ @Test public void testFindRangeBounds() { HighLowRenderer renderer = new HighLowRenderer(); OHLCDataItem item1 = new OHLCDataItem(new Date(1L), 2.0, 4.0, 1.0, 3.0, 100); OHLCDataset dataset = new DefaultOHLCDataset("S1", new OHLCDataItem[] {item1}); Range range = renderer.findRangeBounds(dataset); assertEquals(new Range(1.0, 4.0), range); OHLCDataItem item2 = new OHLCDataItem(new Date(1L), -1.0, 3.0, -1.0, 3.0, 100); dataset = new DefaultOHLCDataset("S1", new OHLCDataItem[] {item1, item2}); range = renderer.findRangeBounds(dataset); assertEquals(new Range(-1.0, 4.0), range); // try an empty dataset - should return a null range dataset = new DefaultOHLCDataset("S1", new OHLCDataItem[] {}); range = renderer.findRangeBounds(dataset); assertNull(range); // try a null dataset - should return a null range range = renderer.findRangeBounds(null); assertNull(range); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/xy/RendererXYPackageUtils.java000066400000000000000000000057501463604235500325550ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * RendererXYPackageTests.java * --------------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import org.jfree.data.xy.DefaultTableXYDataset; import org.jfree.data.xy.TableXYDataset; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; /** * A collection of tests for the org.jfree.chart.renderer.xy package. *

* These tests can be run using JUnit (http://www.junit.org). */ public class RendererXYPackageUtils { /** * Creates and returns a sample dataset for testing purposes. * * @return A sample dataset. */ public static XYSeriesCollection createTestXYSeriesCollection() { XYSeriesCollection result = new XYSeriesCollection(); XYSeries series1 = new XYSeries("Series 1", false, false); series1.add(1.0, 2.0); series1.add(2.0, 5.0); XYSeries series2 = new XYSeries("Series 2", false, false); series2.add(1.0, 4.0); series2.add(2.0, 3.0); result.addSeries(series1); result.addSeries(series2); return result; } /** * Creates and returns a sample dataset for testing purposes. * * @return A sample dataset. */ public static TableXYDataset createTestTableXYDataset() { DefaultTableXYDataset result = new DefaultTableXYDataset(); XYSeries series1 = new XYSeries("Series 1", false, false); series1.add(1.0, 2.0); series1.add(2.0, 5.0); XYSeries series2 = new XYSeries("Series 2", false, false); series2.add(1.0, 4.0); series2.add(2.0, 3.0); result.addSeries(series1); result.addSeries(series2); return result; } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/xy/StackedXYAreaRenderer2Test.java000066400000000000000000000134471463604235500332740ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------- * StackedXYAreaRenderer2Test.java * ------------------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.TestUtils; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.Range; import org.jfree.data.xy.DefaultTableXYDataset; import org.jfree.data.xy.TableXYDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link StackedXYAreaRenderer2} class. */ public class StackedXYAreaRenderer2Test { /** * Test chart drawing with an empty dataset to ensure that this special * case doesn't cause any exceptions. */ @Test public void testDrawWithEmptyDataset() { boolean success = false; JFreeChart chart = ChartFactory.createStackedXYAreaChart("title", "x", "y", new DefaultTableXYDataset(), PlotOrientation.VERTICAL, true, false, false); XYPlot plot = (XYPlot) chart.getPlot(); plot.setRenderer(new StackedXYAreaRenderer2()); try { BufferedImage image = new BufferedImage(200 , 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); chart.draw(g2, new Rectangle2D.Double(0, 0, 200, 100), null, null); g2.dispose(); success = true; } catch (Exception e) { success = false; } assertTrue(success); } /** * Test that the equals() method distinguishes all fields. */ @Test public void testEquals() { StackedXYAreaRenderer2 r1 = new StackedXYAreaRenderer2(); StackedXYAreaRenderer2 r2 = new StackedXYAreaRenderer2(); assertEquals(r1, r2); assertEquals(r2, r1); r1.setRoundXCoordinates(!r1.getRoundXCoordinates()); assertNotEquals(r1, r2); r2.setRoundXCoordinates(r1.getRoundXCoordinates()); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { StackedXYAreaRenderer2 r1 = new StackedXYAreaRenderer2(); StackedXYAreaRenderer2 r2 = new StackedXYAreaRenderer2(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { StackedXYAreaRenderer2 r1 = new StackedXYAreaRenderer2(); StackedXYAreaRenderer2 r2 = (StackedXYAreaRenderer2) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { StackedXYAreaRenderer2 r1 = new StackedXYAreaRenderer2(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { StackedXYAreaRenderer2 r1 = new StackedXYAreaRenderer2(); StackedXYAreaRenderer2 r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * Check that the renderer is calculating the range bounds correctly. */ @Test public void testFindRangeBounds() { TableXYDataset dataset = RendererXYPackageUtils.createTestTableXYDataset(); JFreeChart chart = ChartFactory.createStackedXYAreaChart( "Test Chart", "X", "Y", dataset, PlotOrientation.VERTICAL, false, false, false); XYPlot plot = (XYPlot) chart.getPlot(); StackedXYAreaRenderer2 renderer = new StackedXYAreaRenderer2(); plot.setRenderer(renderer); NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis(); Range bounds = rangeAxis.getRange(); assertTrue(bounds.contains(6.0)); assertTrue(bounds.contains(8.0)); // try null argument assertNull(renderer.findRangeBounds(null)); // try empty dataset assertNull(renderer.findRangeBounds(new DefaultTableXYDataset())); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/xy/StackedXYAreaRendererTest.java000066400000000000000000000163601463604235500332070ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------ * StackedXYAreaRendererTest.java * ------------------------------ * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import java.awt.BasicStroke; import java.awt.Color; import java.awt.GradientPaint; import java.awt.Stroke; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.TestUtils; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.Range; import org.jfree.data.xy.DefaultTableXYDataset; import org.jfree.data.xy.TableXYDataset; import org.jfree.data.xy.XYSeries; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link StackedXYAreaRenderer} class. */ public class StackedXYAreaRendererTest { /** * Test that the equals() method distinguishes all fields. */ @Test public void testEquals() { StackedXYAreaRenderer r1 = new StackedXYAreaRenderer(); StackedXYAreaRenderer r2 = new StackedXYAreaRenderer(); assertEquals(r1, r2); assertEquals(r2, r1); r1.setShapePaint(new GradientPaint(1.0f, 2.0f, Color.YELLOW, 3.0f, 4.0f, Color.GREEN)); assertNotEquals(r1, r2); r2.setShapePaint(new GradientPaint(1.0f, 2.0f, Color.YELLOW, 3.0f, 4.0f, Color.GREEN)); assertEquals(r1, r2); Stroke s = new BasicStroke(1.23f); r1.setShapeStroke(s); assertNotEquals(r1, r2); r2.setShapeStroke(s); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { StackedXYAreaRenderer r1 = new StackedXYAreaRenderer(); StackedXYAreaRenderer r2 = new StackedXYAreaRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { StackedXYAreaRenderer r1 = new StackedXYAreaRenderer(); StackedXYAreaRenderer r2 = (StackedXYAreaRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { StackedXYAreaRenderer r1 = new StackedXYAreaRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { StackedXYAreaRenderer r1 = new StackedXYAreaRenderer(); r1.setShapePaint(Color.RED); r1.setShapeStroke(new BasicStroke(1.23f)); StackedXYAreaRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * Check that the renderer is calculating the range bounds correctly. */ @Test public void testFindRangeBounds() { TableXYDataset dataset = RendererXYPackageUtils.createTestTableXYDataset(); JFreeChart chart = ChartFactory.createStackedXYAreaChart( "Test Chart", "X", "Y", dataset, PlotOrientation.VERTICAL, false, false, false); XYPlot plot = (XYPlot) chart.getPlot(); NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis(); Range bounds = rangeAxis.getRange(); assertTrue(bounds.contains(6.0)); assertTrue(bounds.contains(8.0)); } /** * Draws the chart with a {@code null} info object to make sure that * no exceptions are thrown (particularly by code in the renderer). */ @Test public void testDrawWithNullInfo() { try { DefaultTableXYDataset dataset = new DefaultTableXYDataset(); XYSeries s1 = new XYSeries("Series 1", true, false); s1.add(5.0, 5.0); s1.add(10.0, 15.5); s1.add(15.0, 9.5); s1.add(20.0, 7.5); dataset.addSeries(s1); XYSeries s2 = new XYSeries("Series 2", true, false); s2.add(5.0, 5.0); s2.add(10.0, 15.5); s2.add(15.0, 9.5); s2.add(20.0, 3.5); dataset.addSeries(s2); XYPlot plot = new XYPlot(dataset, new NumberAxis("X"), new NumberAxis("Y"), new StackedXYAreaRenderer()); JFreeChart chart = new JFreeChart(plot); /* BufferedImage image = */ chart.createBufferedImage(300, 200, null); } catch (NullPointerException e) { fail("No exception should be thrown."); } } /** * A test for bug 1593156. */ @Test public void testBug1593156() { try { DefaultTableXYDataset dataset = new DefaultTableXYDataset(); XYSeries s1 = new XYSeries("Series 1", true, false); s1.add(5.0, 5.0); s1.add(10.0, 15.5); s1.add(15.0, 9.5); s1.add(20.0, 7.5); dataset.addSeries(s1); XYSeries s2 = new XYSeries("Series 2", true, false); s2.add(5.0, 5.0); s2.add(10.0, 15.5); s2.add(15.0, 9.5); s2.add(20.0, 3.5); dataset.addSeries(s2); StackedXYAreaRenderer renderer = new StackedXYAreaRenderer( XYAreaRenderer.LINES); XYPlot plot = new XYPlot(dataset, new NumberAxis("X"), new NumberAxis("Y"), renderer); JFreeChart chart = new JFreeChart(plot); /* BufferedImage image = */ chart.createBufferedImage(300, 200, null); } catch (NullPointerException e) { fail("No exception should be thrown."); } } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/xy/StackedXYBarRendererTest.java000066400000000000000000000130531463604235500330370ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------- * StackedXYBarRendererTest.java * ----------------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import java.awt.Color; import java.awt.GradientPaint; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.TestUtils; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.Range; import org.jfree.data.xy.TableXYDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link StackedXYBarRenderer} class. */ public class StackedXYBarRendererTest { /** * Test that the equals() method distinguishes all fields. */ @Test public void testEquals() { StackedXYBarRenderer r1 = new StackedXYBarRenderer(); StackedXYBarRenderer r2 = new StackedXYBarRenderer(); assertEquals(r1, r2); assertEquals(r2, r1); r1.setRenderAsPercentages(true); assertNotEquals(r1, r2); r2.setRenderAsPercentages(true); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { StackedXYBarRenderer r1 = new StackedXYBarRenderer(); StackedXYBarRenderer r2 = new StackedXYBarRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); r1.setRenderAsPercentages(true); h1 = r1.hashCode(); h2 = r2.hashCode(); assertNotEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { StackedXYBarRenderer r1 = new StackedXYBarRenderer(); StackedXYBarRenderer r2 = (StackedXYBarRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { StackedXYBarRenderer r1 = new StackedXYBarRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { StackedXYBarRenderer r1 = new StackedXYBarRenderer(); r1.setSeriesPaint(0, new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW)); StackedXYBarRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * Check that the renderer is calculating the domain bounds correctly. */ @Test public void testFindDomainBounds() { TableXYDataset dataset = RendererXYPackageUtils.createTestTableXYDataset(); JFreeChart chart = ChartFactory.createStackedXYAreaChart( "Test Chart", "X", "Y", dataset, PlotOrientation.VERTICAL, false, false, false); XYPlot plot = (XYPlot) chart.getPlot(); plot.setRenderer(new StackedXYBarRenderer()); NumberAxis domainAxis = (NumberAxis) plot.getDomainAxis(); domainAxis.setAutoRangeIncludesZero(false); Range bounds = domainAxis.getRange(); assertFalse(bounds.contains(0.3)); assertTrue(bounds.contains(0.5)); assertTrue(bounds.contains(2.5)); assertFalse(bounds.contains(2.8)); } /** * Check that the renderer is calculating the range bounds correctly. */ @Test public void testFindRangeBounds() { TableXYDataset dataset = RendererXYPackageUtils.createTestTableXYDataset(); JFreeChart chart = ChartFactory.createStackedXYAreaChart( "Test Chart", "X", "Y", dataset, PlotOrientation.VERTICAL, false, false, false); XYPlot plot = (XYPlot) chart.getPlot(); plot.setRenderer(new StackedXYBarRenderer()); NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis(); Range bounds = rangeAxis.getRange(); assertTrue(bounds.contains(6.0)); assertTrue(bounds.contains(8.0)); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/xy/StandardXYBarPainterTest.java000066400000000000000000000062431463604235500330600ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------- * StandardXYBarPainterTest.java * ----------------------------- * (C) Copyright 2008-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; /** * Tests for the {@link StandardXYBarPainter} class. */ public class StandardXYBarPainterTest { /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { StandardXYBarPainter p1 = new StandardXYBarPainter(); StandardXYBarPainter p2 = new StandardXYBarPainter(); assertEquals(p1, p2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { StandardXYBarPainter p1 = new StandardXYBarPainter(); StandardXYBarPainter p2 = new StandardXYBarPainter(); assertEquals(p1, p2); int h1 = p1.hashCode(); int h2 = p2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning isn't implemented (it isn't required, because * instances of the class are immutable). */ @Test public void testCloning() { StandardXYBarPainter p1 = new StandardXYBarPainter(); assertFalse(p1 instanceof Cloneable); assertFalse(p1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { StandardXYBarPainter p1 = new StandardXYBarPainter(); StandardXYBarPainter p2 = TestUtils.serialised(p1); assertEquals(p1, p2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/xy/StandardXYItemRendererTest.java000066400000000000000000000204241463604235500334130ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------- * StandardXYItemRendererTest.java * ------------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import java.awt.Graphics2D; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import org.jfree.chart.ChartFactory; import org.jfree.chart.ChartRenderingInfo; import org.jfree.chart.JFreeChart; import org.jfree.chart.LegendItem; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.entity.EntityCollection; import org.jfree.chart.entity.XYItemEntity; import org.jfree.chart.TestUtils; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.util.PublicCloneable; import org.jfree.chart.util.UnitType; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link StandardXYItemRenderer} class. */ public class StandardXYItemRendererTest { /** * Test that the equals() method distinguishes all fields. */ @Test public void testEquals() { StandardXYItemRenderer r1 = new StandardXYItemRenderer(); StandardXYItemRenderer r2 = new StandardXYItemRenderer(); assertEquals(r1, r2); r1.setBaseShapesVisible(true); assertNotEquals(r1, r2); r2.setBaseShapesVisible(true); assertEquals(r1, r2); r1.setPlotLines(false); assertNotEquals(r1, r2); r2.setPlotLines(false); assertEquals(r1, r2); r1.setPlotImages(true); assertNotEquals(r1, r2); r2.setPlotImages(true); assertEquals(r1, r2); r1.setPlotDiscontinuous(true); assertNotEquals(r1, r2); r2.setPlotDiscontinuous(true); assertEquals(r1, r2); r1.setGapThresholdType(UnitType.ABSOLUTE); assertNotEquals(r1, r2); r2.setGapThresholdType(UnitType.ABSOLUTE); assertEquals(r1, r2); r1.setGapThreshold(1.23); assertNotEquals(r1, r2); r2.setGapThreshold(1.23); assertEquals(r1, r2); r1.setLegendLine(new Line2D.Double(1.0, 2.0, 3.0, 4.0)); assertNotEquals(r1, r2); r2.setLegendLine(new Line2D.Double(1.0, 2.0, 3.0, 4.0)); assertEquals(r1, r2); r1.setSeriesShapesFilled(1, Boolean.TRUE); assertNotEquals(r1, r2); r2.setSeriesShapesFilled(1, Boolean.TRUE); assertEquals(r1, r2); r1.setBaseShapesFilled(false); assertNotEquals(r1, r2); r2.setBaseShapesFilled(false); assertEquals(r1, r2); r1.setDrawSeriesLineAsPath(true); assertNotEquals(r1, r2); r2.setDrawSeriesLineAsPath(true); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { StandardXYItemRenderer r1 = new StandardXYItemRenderer(); StandardXYItemRenderer r2 = new StandardXYItemRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { StandardXYItemRenderer r1 = new StandardXYItemRenderer(); Rectangle2D rect1 = new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0); r1.setLegendLine(rect1); StandardXYItemRenderer r2 = (StandardXYItemRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); // check independence rect1.setRect(4.0, 3.0, 2.0, 1.0); assertNotEquals(r1, r2); r2.setLegendLine(new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0)); assertEquals(r1, r2); r1.setSeriesShapesFilled(1, Boolean.TRUE); assertNotEquals(r1, r2); r2.setSeriesShapesFilled(1, Boolean.TRUE); assertEquals(r1, r2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { StandardXYItemRenderer r1 = new StandardXYItemRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { StandardXYItemRenderer r1 = new StandardXYItemRenderer(); StandardXYItemRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * A check for the datasetIndex and seriesIndex fields in the LegendItem * returned by the getLegendItem() method. */ @Test public void testGetLegendItemSeriesIndex() { XYSeriesCollection d1 = new XYSeriesCollection(); XYSeries s1 = new XYSeries("S1"); s1.add(1.0, 1.1); XYSeries s2 = new XYSeries("S2"); s2.add(1.0, 1.1); d1.addSeries(s1); d1.addSeries(s2); XYSeriesCollection d2 = new XYSeriesCollection(); XYSeries s3 = new XYSeries("S3"); s3.add(1.0, 1.1); XYSeries s4 = new XYSeries("S4"); s4.add(1.0, 1.1); XYSeries s5 = new XYSeries("S5"); s5.add(1.0, 1.1); d2.addSeries(s3); d2.addSeries(s4); d2.addSeries(s5); StandardXYItemRenderer r = new StandardXYItemRenderer(); XYPlot plot = new XYPlot(d1, new NumberAxis("x"), new NumberAxis("y"), r); plot.setDataset(1, d2); /*JFreeChart chart =*/ new JFreeChart(plot); LegendItem li = r.getLegendItem(1, 2); assertEquals("S5", li.getLabel()); assertEquals(1, li.getDatasetIndex()); assertEquals(2, li.getSeriesIndex()); } /** * A check to ensure that an item that falls outside the plot's data area * does NOT generate an item entity. */ @Test public void testNoDisplayedItem() { XYSeriesCollection dataset = new XYSeriesCollection(); XYSeries s1 = new XYSeries("S1"); s1.add(10.0, 10.0); dataset.addSeries(s1); JFreeChart chart = ChartFactory.createXYLineChart("Title", "X", "Y", dataset, PlotOrientation.VERTICAL, false, true, false); XYPlot plot = (XYPlot) chart.getPlot(); plot.setRenderer(new StandardXYItemRenderer()); NumberAxis xAxis = (NumberAxis) plot.getDomainAxis(); xAxis.setRange(0.0, 5.0); NumberAxis yAxis = (NumberAxis) plot.getRangeAxis(); yAxis.setRange(0.0, 5.0); BufferedImage image = new BufferedImage(200 , 100, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); ChartRenderingInfo info = new ChartRenderingInfo(); chart.draw(g2, new Rectangle2D.Double(0, 0, 200, 100), null, info); g2.dispose(); EntityCollection ec = info.getEntityCollection(); assertFalse(TestUtils.containsInstanceOf(ec.getEntities(), XYItemEntity.class)); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/xy/VectorRendererTest.java000066400000000000000000000067771463604235500320340ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * VectorRendererTest.java * ----------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import java.awt.Color; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link VectorRenderer} class. */ public class VectorRendererTest { /** * Test that the equals() method distinguishes all fields. */ @Test public void testEquals() { // default instances VectorRenderer r1 = new VectorRenderer(); VectorRenderer r2 = new VectorRenderer(); assertEquals(r1, r2); assertEquals(r2, r1); // check that super class fields are being looked at... r1.setSeriesFillPaint(0, Color.GREEN); assertNotEquals(r1, r2); r2.setSeriesFillPaint(0, Color.GREEN); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { VectorRenderer r1 = new VectorRenderer(); VectorRenderer r2 = new VectorRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. * * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { VectorRenderer r1 = new VectorRenderer(); VectorRenderer r2 = (VectorRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { VectorRenderer r1 = new VectorRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { VectorRenderer r1 = new VectorRenderer(); VectorRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/xy/WindItemRendererTest.java000066400000000000000000000062771463604235500323050ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * WindItemRendererTest.java * ------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link WindItemRenderer} class. */ public class WindItemRendererTest { /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { WindItemRenderer r1 = new WindItemRenderer(); WindItemRenderer r2 = new WindItemRenderer(); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { WindItemRenderer r1 = new WindItemRenderer(); WindItemRenderer r2 = new WindItemRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { WindItemRenderer r1 = new WindItemRenderer(); WindItemRenderer r2 = (WindItemRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { WindItemRenderer r1 = new WindItemRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { WindItemRenderer r1 = new WindItemRenderer(); WindItemRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/xy/XYAreaRenderer2Test.java000066400000000000000000000144171463604235500317730ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * XYAreaRenderer2Test.java * ------------------------ * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import java.awt.Rectangle; import org.jfree.chart.JFreeChart; import org.jfree.chart.LegendItem; import org.jfree.chart.TestUtils; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.xy.DefaultTableXYDataset; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XYAreaRenderer2} class. */ public class XYAreaRenderer2Test { /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { XYAreaRenderer2 r1 = new XYAreaRenderer2(); XYAreaRenderer2 r2 = new XYAreaRenderer2(); assertEquals(r1, r2); r1.setOutline(!r1.isOutline()); assertNotEquals(r1, r2); r2.setOutline(r1.isOutline()); assertEquals(r1, r2); r1.setLegendArea(new Rectangle(1, 2, 3, 4)); assertNotEquals(r1, r2); r2.setLegendArea(new Rectangle(1, 2, 3, 4)); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { XYAreaRenderer2 r1 = new XYAreaRenderer2(); XYAreaRenderer2 r2 = new XYAreaRenderer2(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { XYAreaRenderer2 r1 = new XYAreaRenderer2(); Rectangle rect = new Rectangle(1, 2, 3, 4); r1.setLegendArea(rect); XYAreaRenderer2 r2 = (XYAreaRenderer2) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); // check independence rect.setBounds(99, 99, 99, 99); assertNotEquals(r1, r2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { XYAreaRenderer2 r1 = new XYAreaRenderer2(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XYAreaRenderer2 r1 = new XYAreaRenderer2(); XYAreaRenderer2 r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * Draws the chart with a {@code null} info object to make sure that * no exceptions are thrown (particularly by code in the renderer). */ @Test public void testDrawWithNullInfo() { try { DefaultTableXYDataset dataset = new DefaultTableXYDataset(); XYSeries s1 = new XYSeries("Series 1", true, false); s1.add(5.0, 5.0); s1.add(10.0, 15.5); s1.add(15.0, 9.5); s1.add(20.0, 7.5); dataset.addSeries(s1); XYSeries s2 = new XYSeries("Series 2", true, false); s2.add(5.0, 5.0); s2.add(10.0, 15.5); s2.add(15.0, 9.5); s2.add(20.0, 3.5); dataset.addSeries(s2); XYPlot plot = new XYPlot(dataset, new NumberAxis("X"), new NumberAxis("Y"), new XYAreaRenderer2()); JFreeChart chart = new JFreeChart(plot); /* BufferedImage image = */ chart.createBufferedImage(300, 200, null); } catch (NullPointerException e) { fail("No exception should be thrown."); } } /** * A check for the datasetIndex and seriesIndex fields in the LegendItem * returned by the getLegendItem() method. */ @Test public void testGetLegendItemSeriesIndex() { XYSeriesCollection d1 = new XYSeriesCollection(); XYSeries s1 = new XYSeries("S1"); s1.add(1.0, 1.1); XYSeries s2 = new XYSeries("S2"); s2.add(1.0, 1.1); d1.addSeries(s1); d1.addSeries(s2); XYSeriesCollection d2 = new XYSeriesCollection(); XYSeries s3 = new XYSeries("S3"); s3.add(1.0, 1.1); XYSeries s4 = new XYSeries("S4"); s4.add(1.0, 1.1); XYSeries s5 = new XYSeries("S5"); s5.add(1.0, 1.1); d2.addSeries(s3); d2.addSeries(s4); d2.addSeries(s5); XYAreaRenderer2 r = new XYAreaRenderer2(); XYPlot plot = new XYPlot(d1, new NumberAxis("x"), new NumberAxis("y"), r); plot.setDataset(1, d2); /*JFreeChart chart =*/ new JFreeChart(plot); LegendItem li = r.getLegendItem(1, 2); assertEquals("S5", li.getLabel()); assertEquals(1, li.getDatasetIndex()); assertEquals(2, li.getSeriesIndex()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/xy/XYAreaRendererTest.java000066400000000000000000000177031463604235500317120ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * XYAreaRendererTest.java * ----------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import java.awt.geom.Rectangle2D; import org.jfree.chart.JFreeChart; import org.jfree.chart.LegendItem; import org.jfree.chart.TestUtils; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.GradientPaintTransformType; import org.jfree.chart.ui.StandardGradientPaintTransformer; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.xy.DefaultTableXYDataset; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XYAreaRenderer} class. */ public class XYAreaRendererTest { /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { XYAreaRenderer r1 = new XYAreaRenderer(); XYAreaRenderer r2 = new XYAreaRenderer(); assertEquals(r1, r2); r1 = new XYAreaRenderer(XYAreaRenderer.AREA_AND_SHAPES); assertNotEquals(r1, r2); r2 = new XYAreaRenderer(XYAreaRenderer.AREA_AND_SHAPES); assertEquals(r1, r2); r1 = new XYAreaRenderer(XYAreaRenderer.AREA); assertNotEquals(r1, r2); r2 = new XYAreaRenderer(XYAreaRenderer.AREA); assertEquals(r1, r2); r1 = new XYAreaRenderer(XYAreaRenderer.LINES); assertNotEquals(r1, r2); r2 = new XYAreaRenderer(XYAreaRenderer.LINES); assertEquals(r1, r2); r1 = new XYAreaRenderer(XYAreaRenderer.SHAPES); assertNotEquals(r1, r2); r2 = new XYAreaRenderer(XYAreaRenderer.SHAPES); assertEquals(r1, r2); r1 = new XYAreaRenderer(XYAreaRenderer.SHAPES_AND_LINES); assertNotEquals(r1, r2); r2 = new XYAreaRenderer(XYAreaRenderer.SHAPES_AND_LINES); assertEquals(r1, r2); r1.setOutline(true); assertNotEquals(r1, r2); r2.setOutline(true); assertEquals(r1, r2); r1.setLegendArea(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0)); assertNotEquals(r1, r2); r2.setLegendArea(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0)); assertEquals(r1, r2); r1.setUseFillPaint(true); assertNotEquals(r1, r2); r2.setUseFillPaint(true); assertEquals(r1, r2); r1.setGradientTransformer(new StandardGradientPaintTransformer( GradientPaintTransformType.CENTER_VERTICAL)); assertNotEquals(r1, r2); r2.setGradientTransformer(new StandardGradientPaintTransformer( GradientPaintTransformType.CENTER_VERTICAL)); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { XYAreaRenderer r1 = new XYAreaRenderer(); XYAreaRenderer r2 = new XYAreaRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); r2.setUseFillPaint(true); assertNotEquals(r1.hashCode(), r2.hashCode()); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { XYAreaRenderer r1 = new XYAreaRenderer(); Rectangle2D rect1 = new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0); r1.setLegendArea(rect1); XYAreaRenderer r2 = (XYAreaRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); // check independence rect1.setRect(4.0, 3.0, 2.0, 1.0); assertNotEquals(r1, r2); r2.setLegendArea(new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0)); assertEquals(r1, r2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { XYAreaRenderer r1 = new XYAreaRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XYAreaRenderer r1 = new XYAreaRenderer(); XYAreaRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * Draws the chart with a {@code null} info object to make sure that * no exceptions are thrown (particularly by code in the renderer). */ @Test public void testDrawWithNullInfo() { try { DefaultTableXYDataset dataset = new DefaultTableXYDataset(); XYSeries s1 = new XYSeries("Series 1", true, false); s1.add(5.0, 5.0); s1.add(10.0, 15.5); s1.add(15.0, 9.5); s1.add(20.0, 7.5); dataset.addSeries(s1); XYSeries s2 = new XYSeries("Series 2", true, false); s2.add(5.0, 5.0); s2.add(10.0, 15.5); s2.add(15.0, 9.5); s2.add(20.0, 3.5); dataset.addSeries(s2); XYPlot plot = new XYPlot(dataset, new NumberAxis("X"), new NumberAxis("Y"), new XYAreaRenderer()); JFreeChart chart = new JFreeChart(plot); /* BufferedImage image = */ chart.createBufferedImage(300, 200, null); } catch (NullPointerException e) { fail("No exception should be thrown."); } } /** * A check for the datasetIndex and seriesIndex fields in the LegendItem * returned by the getLegendItem() method. */ @Test public void testGetLegendItemSeriesIndex() { XYSeriesCollection d1 = new XYSeriesCollection(); XYSeries s1 = new XYSeries("S1"); s1.add(1.0, 1.1); XYSeries s2 = new XYSeries("S2"); s2.add(1.0, 1.1); d1.addSeries(s1); d1.addSeries(s2); XYSeriesCollection d2 = new XYSeriesCollection(); XYSeries s3 = new XYSeries("S3"); s3.add(1.0, 1.1); XYSeries s4 = new XYSeries("S4"); s4.add(1.0, 1.1); XYSeries s5 = new XYSeries("S5"); s5.add(1.0, 1.1); d2.addSeries(s3); d2.addSeries(s4); d2.addSeries(s5); XYAreaRenderer r = new XYAreaRenderer(); XYPlot plot = new XYPlot(d1, new NumberAxis("x"), new NumberAxis("y"), r); plot.setDataset(1, d2); /*JFreeChart chart =*/ new JFreeChart(plot); LegendItem li = r.getLegendItem(1, 2); assertEquals("S5", li.getLabel()); assertEquals(1, li.getDatasetIndex()); assertEquals(2, li.getSeriesIndex()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/xy/XYBarRendererTest.java000066400000000000000000000303471463604235500315450ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * XYBarRendererTest.java * ---------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import java.awt.geom.Rectangle2D; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.LegendItem; import org.jfree.chart.TestUtils; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.labels.ItemLabelPosition; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.GradientPaintTransformType; import org.jfree.chart.ui.StandardGradientPaintTransformer; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.Range; import org.jfree.data.xy.DefaultIntervalXYDataset; import org.jfree.data.xy.XYBarDataset; import org.jfree.data.xy.XYIntervalSeries; import org.jfree.data.xy.XYIntervalSeriesCollection; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XYBarRenderer} class. */ public class XYBarRendererTest { /** * Test that the equals() method distinguishes all fields. */ @Test public void testEquals() { // default instances XYBarRenderer r1 = new XYBarRenderer(); XYBarRenderer r2 = new XYBarRenderer(); assertEquals(r1, r2); assertEquals(r2, r1); // setBase() r1.setBase(1.0); assertNotEquals(r1, r2); r2.setBase(1.0); assertEquals(r1, r2); // setUseYInterval r1.setUseYInterval(!r1.getUseYInterval()); assertNotEquals(r1, r2); r2.setUseYInterval(!r2.getUseYInterval()); assertEquals(r1, r2); // setMargin() r1.setMargin(0.10); assertNotEquals(r1, r2); r2.setMargin(0.10); assertEquals(r1, r2); // setDrawBarOutline() r1.setDrawBarOutline(!r1.isDrawBarOutline()); assertNotEquals(r1, r2); r2.setDrawBarOutline(!r2.isDrawBarOutline()); assertEquals(r1, r2); // setGradientPaintTransformer() r1.setGradientPaintTransformer(new StandardGradientPaintTransformer( GradientPaintTransformType.CENTER_HORIZONTAL)); assertNotEquals(r1, r2); r2.setGradientPaintTransformer(new StandardGradientPaintTransformer( GradientPaintTransformType.CENTER_HORIZONTAL)); assertEquals(r1, r2); // legendBar r1.setLegendBar(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0)); assertNotEquals(r1, r2); r2.setLegendBar(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0)); assertEquals(r1, r2); // positiveItemLabelFallbackPosition r1.setPositiveItemLabelPositionFallback(new ItemLabelPosition()); assertNotEquals(r1, r2); r2.setPositiveItemLabelPositionFallback(new ItemLabelPosition()); assertEquals(r1, r2); // negativeItemLabelFallbackPosition r1.setNegativeItemLabelPositionFallback(new ItemLabelPosition()); assertNotEquals(r1, r2); r2.setNegativeItemLabelPositionFallback(new ItemLabelPosition()); assertEquals(r1, r2); // barPainter r1.setBarPainter(new GradientXYBarPainter(0.11, 0.22, 0.33)); assertNotEquals(r1, r2); r2.setBarPainter(new GradientXYBarPainter(0.11, 0.22, 0.33)); assertEquals(r1, r2); // shadowsVisible r1.setShadowVisible(false); assertNotEquals(r1, r2); r2.setShadowVisible(false); assertEquals(r1, r2); // shadowXOffset r1.setShadowXOffset(3.3); assertNotEquals(r1, r2); r2.setShadowXOffset(3.3); assertEquals(r1, r2); // shadowYOffset r1.setShadowYOffset(3.3); assertNotEquals(r1, r2); r2.setShadowYOffset(3.3); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { XYBarRenderer r1 = new XYBarRenderer(); XYBarRenderer r2 = new XYBarRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { XYBarRenderer r1 = new XYBarRenderer(); Rectangle2D rect = new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0); r1.setLegendBar(rect); XYBarRenderer r2 = (XYBarRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); // check independence rect.setRect(4.0, 3.0, 2.0, 1.0); assertNotEquals(r1, r2); r2.setLegendBar(new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0)); assertEquals(r1, r2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { XYBarRenderer r1 = new XYBarRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XYBarRenderer r1 = new XYBarRenderer(); XYBarRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization2() { XYBarRenderer r1 = new XYBarRenderer(); r1.setPositiveItemLabelPositionFallback(new ItemLabelPosition()); XYBarRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * Check that the renderer is calculating the domain bounds correctly. */ @Test public void testFindDomainBounds() { XYSeriesCollection dataset = RendererXYPackageUtils.createTestXYSeriesCollection(); JFreeChart chart = ChartFactory.createXYBarChart("Test Chart", "X", false, "Y", dataset, PlotOrientation.VERTICAL, false, false, false); XYPlot plot = (XYPlot) chart.getPlot(); NumberAxis domainAxis = (NumberAxis) plot.getDomainAxis(); domainAxis.setAutoRangeIncludesZero(false); Range bounds = domainAxis.getRange(); assertFalse(bounds.contains(0.3)); assertTrue(bounds.contains(0.5)); assertTrue(bounds.contains(2.5)); assertFalse(bounds.contains(2.8)); } /** * A test for the findDomainBounds method to ensure it correctly accounts * for the series visibility. */ @Test public void testFindDomainBounds2() { XYIntervalSeries s1 = new XYIntervalSeries("S1"); s1.add(1.0, 0.5, 1.5, 10.0, 9.5, 10.5); s1.add(2.0, 1.9, 2.1, 20.0, 19.8, 20.3); XYIntervalSeries s2 = new XYIntervalSeries("S2"); s2.add(3.0, 2.5, 3.5, 30.0, 29.5, 30.5); s2.add(4.0, 3.9, 4.1, 9.0, 9.0, 9.0); XYIntervalSeriesCollection dataset = new XYIntervalSeriesCollection(); dataset.addSeries(s1); dataset.addSeries(s2); XYBarRenderer renderer = new XYBarRenderer(); Range r = renderer.findDomainBounds(dataset); assertEquals(0.5, r.getLowerBound(), EPSILON); assertEquals(4.1, r.getUpperBound(), EPSILON); renderer.setSeriesVisible(1, Boolean.FALSE); r = renderer.findDomainBounds(dataset); assertEquals(0.5, r.getLowerBound(), EPSILON); assertEquals(2.1, r.getUpperBound(), EPSILON); } private static final double EPSILON = 0.0000000001; /** * A simple test for the findRangeBounds() method. */ @Test public void testFindRangeBounds() { DefaultIntervalXYDataset dataset = new DefaultIntervalXYDataset(); double[] x = {1.0, 2.0, 3.0, 4.0}; double[] startx = {0.9, 1.8, 2.7, 3.6}; double[] endx = {1.1, 2.2, 3.3, 4.4}; double[] y = {1.0, 2.0, 3.0, 4.0}; double[] starty = {0.9, 1.8, 2.7, 3.6}; double[] endy = {1.1, 2.2, 3.3, 4.4}; double[][] data = new double[][] {x, startx, endx, y, starty, endy}; dataset.addSeries("Series 1", data); XYBarRenderer renderer = new XYBarRenderer(); renderer.setUseYInterval(true); Range r = renderer.findRangeBounds(dataset); assertEquals(0.9, r.getLowerBound(), EPSILON); assertEquals(4.4, r.getUpperBound(), EPSILON); renderer.setUseYInterval(false); r = renderer.findRangeBounds(dataset); assertEquals(1.0, r.getLowerBound(), EPSILON); assertEquals(4.0, r.getUpperBound(), EPSILON); } /** * A test for the findRangeBounds method to ensure it correctly accounts * for the series visibility. */ @Test public void testFindRangeBounds2() { XYIntervalSeries s1 = new XYIntervalSeries("S1"); s1.add(1.0, 0.5, 1.5, 10.0, 9.5, 10.5); s1.add(2.0, 1.9, 2.1, 20.0, 19.8, 20.3); XYIntervalSeries s2 = new XYIntervalSeries("S2"); s2.add(3.0, 2.5, 3.5, 30.0, 29.5, 30.5); s2.add(4.0, 3.9, 4.1, 9.0, 9.0, 9.0); XYIntervalSeriesCollection dataset = new XYIntervalSeriesCollection(); dataset.addSeries(s1); dataset.addSeries(s2); XYBarRenderer renderer = new XYBarRenderer(); renderer.setUseYInterval(false); Range r = renderer.findRangeBounds(dataset); assertEquals(9.0, r.getLowerBound(), EPSILON); assertEquals(30.0, r.getUpperBound(), EPSILON); renderer.setSeriesVisible(1, Boolean.FALSE); r = renderer.findRangeBounds(dataset); assertEquals(10.0, r.getLowerBound(), EPSILON); assertEquals(20.0, r.getUpperBound(), EPSILON); } /** * A check for the datasetIndex and seriesIndex fields in the LegendItem * returned by the getLegendItem() method. */ @Test public void testGetLegendItemSeriesIndex() { XYSeriesCollection d1 = new XYSeriesCollection(); XYSeries s1 = new XYSeries("S1"); s1.add(1.0, 1.1); XYSeries s2 = new XYSeries("S2"); s2.add(1.0, 1.1); d1.addSeries(s1); d1.addSeries(s2); XYSeriesCollection d2 = new XYSeriesCollection(); XYSeries s3 = new XYSeries("S3"); s3.add(1.0, 1.1); XYSeries s4 = new XYSeries("S4"); s4.add(1.0, 1.1); XYSeries s5 = new XYSeries("S5"); s5.add(1.0, 1.1); d2.addSeries(s3); d2.addSeries(s4); d2.addSeries(s5); XYBarRenderer r = new XYBarRenderer(); XYPlot plot = new XYPlot(new XYBarDataset(d1, 1.0), new NumberAxis("x"), new NumberAxis("y"), r); plot.setDataset(1, new XYBarDataset(d2, 2.0)); /*JFreeChart chart =*/ new JFreeChart(plot); LegendItem li = r.getLegendItem(1, 2); assertEquals("S5", li.getLabel()); assertEquals(1, li.getDatasetIndex()); assertEquals(2, li.getSeriesIndex()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/xy/XYBezierRendererTest.java000066400000000000000000000055421463604235500322600ustar00rootroot00000000000000package org.jfree.chart.renderer.xy; import java.awt.geom.Rectangle2D; import org.jfree.chart.TestUtils; import org.jfree.chart.ui.GradientPaintTransformType; import org.jfree.chart.ui.StandardGradientPaintTransformer; import org.jfree.chart.util.CloneUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XYBezierRenderer} class. */ class XYBezierRendererTest { /** * Test that the equals() method distinguishes all fields. */ @Test void testEquals() { XYBezierRenderer r1 = new XYBezierRenderer(); XYBezierRenderer r2 = new XYBezierRenderer(); assertEquals(r1, r2); assertEquals(r2, r1); r1.setPrecision(9); assertNotEquals(r1, r2); r2.setPrecision(9); assertEquals(r1, r2); r1.setFillType(XYBezierRenderer.FillType.TO_ZERO); assertNotEquals(r1, r2); r2.setFillType(XYBezierRenderer.FillType.TO_ZERO); assertEquals(r1, r2); r1.setGradientPaintTransformer(null); assertNotEquals(r1, r2); r2.setGradientPaintTransformer(null); assertEquals(r1, r2); r1.setGradientPaintTransformer(new StandardGradientPaintTransformer( GradientPaintTransformType.HORIZONTAL)); assertNotEquals(r1, r2); r2.setGradientPaintTransformer(new StandardGradientPaintTransformer( GradientPaintTransformType.HORIZONTAL)); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test void testHashcode() { XYBezierRenderer r1 = new XYBezierRenderer(); XYBezierRenderer r2 = new XYBezierRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test void testCloning() throws CloneNotSupportedException { Rectangle2D legendShape = new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0); XYBezierRenderer r1 = new XYBezierRenderer(); r1.setLegendLine(legendShape); XYBezierRenderer r2 = (XYBezierRenderer) CloneUtils.clone(r1); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test void testPublicCloneable() { XYBezierRenderer r1 = new XYBezierRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test void testSerialization() { XYBezierRenderer r1 = new XYBezierRenderer(); XYBezierRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } }jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/xy/XYBlockRendererTest.java000066400000000000000000000144011463604235500320640ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * XYBlockRendererTest.java * ------------------------ * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import java.awt.Color; import org.jfree.chart.TestUtils; import org.jfree.chart.renderer.GrayPaintScale; import org.jfree.chart.renderer.LookupPaintScale; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.Range; import org.jfree.data.xy.DefaultXYZDataset; import org.jfree.data.xy.XYSeriesCollection; import org.jfree.data.xy.XYSeries; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XYBlockRenderer} class. */ public class XYBlockRendererTest { private static final double EPSILON = 0.0000000001; /** * Test that the equals() method distinguishes all fields. */ @Test public void testEquals() { // default instances XYBlockRenderer r1 = new XYBlockRenderer(); XYBlockRenderer r2 = new XYBlockRenderer(); assertEquals(r1, r2); assertEquals(r2, r1); // blockHeight r1.setBlockHeight(2.0); assertNotEquals(r1, r2); r2.setBlockHeight(2.0); assertEquals(r1, r2); // blockWidth r1.setBlockWidth(2.0); assertNotEquals(r1, r2); r2.setBlockWidth(2.0); assertEquals(r1, r2); // paintScale r1.setPaintScale(new GrayPaintScale(0.0, 1.0)); assertNotEquals(r1, r2); r2.setPaintScale(new GrayPaintScale(0.0, 1.0)); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { XYBlockRenderer r1 = new XYBlockRenderer(); XYBlockRenderer r2 = new XYBlockRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { XYBlockRenderer r1 = new XYBlockRenderer(); LookupPaintScale scale1 = new LookupPaintScale(); r1.setPaintScale(scale1); XYBlockRenderer r2 = (XYBlockRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); // check independence scale1.add(0.5, Color.RED); assertNotEquals(r1, r2); LookupPaintScale scale2 = (LookupPaintScale) r2.getPaintScale(); scale2.add(0.5, Color.RED); assertEquals(r1, r2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { XYBlockRenderer r1 = new XYBlockRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XYBlockRenderer r1 = new XYBlockRenderer(); XYBlockRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * A simple test for bug 1766646. */ @Test public void testBug1766646A() { XYBlockRenderer r = new XYBlockRenderer(); Range range = r.findDomainBounds(null); assertNull(range); DefaultXYZDataset emptyDataset = new DefaultXYZDataset(); range = r.findDomainBounds(emptyDataset); assertNull(range); } /** * A simple test for bug 1766646. */ @Test public void testBug1766646B() { XYBlockRenderer r = new XYBlockRenderer(); Range range = r.findRangeBounds(null); assertNull(range); DefaultXYZDataset emptyDataset = new DefaultXYZDataset(); range = r.findRangeBounds(emptyDataset); assertNull(range); } /** * Some tests for the findRangeBounds() method. */ @Test public void testFindRangeBounds() { XYBlockRenderer renderer = new XYBlockRenderer(); assertNull(renderer.findRangeBounds(null)); XYSeriesCollection dataset = new XYSeriesCollection(); XYSeries series = new XYSeries("S1"); series.add(1.0, null); dataset.addSeries(series); Range r = renderer.findRangeBounds(dataset); assertNull(r); } /** * Some tests for the findDomainBounds() method. */ @Test public void testFindDomainBounds() { XYBlockRenderer renderer = new XYBlockRenderer(); assertNull(renderer.findRangeBounds(null)); XYSeriesCollection dataset = new XYSeriesCollection(); XYSeries series = new XYSeries("S1"); series.add(1.0, null); dataset.addSeries(series); Range r = renderer.findDomainBounds(dataset); assertEquals(0.5, r.getLowerBound(), EPSILON); assertEquals(1.5, r.getUpperBound(), EPSILON); dataset.removeAllSeries(); r = renderer.findDomainBounds(dataset); assertNull(r); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/xy/XYBoxAndWhiskerRendererTest.java000066400000000000000000000126211463604235500335440ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------------- * XYBoxAndWhiskerRendererTest.java * -------------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import java.awt.Color; import java.awt.GradientPaint; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.util.Date; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.statistics.BoxAndWhiskerItem; import org.jfree.data.statistics.DefaultBoxAndWhiskerXYDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XYBoxAndWhiskerRenderer} class. */ public class XYBoxAndWhiskerRendererTest { /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { XYBoxAndWhiskerRenderer r1 = new XYBoxAndWhiskerRenderer(); XYBoxAndWhiskerRenderer r2 = new XYBoxAndWhiskerRenderer(); assertEquals(r1, r2); r1.setArtifactPaint(new GradientPaint(1.0f, 2.0f, Color.GREEN, 3.0f, 4.0f, Color.RED)); assertNotEquals(r1, r2); r2.setArtifactPaint(new GradientPaint(1.0f, 2.0f, Color.GREEN, 3.0f, 4.0f, Color.RED)); assertEquals(r1, r2); r1.setBoxWidth(0.55); assertNotEquals(r1, r2); r2.setBoxWidth(0.55); assertEquals(r1, r2); r1.setFillBox(!r1.getFillBox()); assertNotEquals(r1, r2); r2.setFillBox(!r2.getFillBox()); assertEquals(r1, r2); r1.setBoxPaint(Color.YELLOW); assertNotEquals(r1, r2); r2.setBoxPaint(Color.YELLOW); assertEquals(r1, r2); // check boxPaint null also r1.setBoxPaint(null); assertNotEquals(r1, r2); r2.setBoxPaint(null); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { XYBoxAndWhiskerRenderer r1 = new XYBoxAndWhiskerRenderer(); XYBoxAndWhiskerRenderer r2 = new XYBoxAndWhiskerRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. * * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { XYBoxAndWhiskerRenderer r1 = new XYBoxAndWhiskerRenderer(); XYBoxAndWhiskerRenderer r2 = (XYBoxAndWhiskerRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { XYBoxAndWhiskerRenderer r1 = new XYBoxAndWhiskerRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XYBoxAndWhiskerRenderer r1 = new XYBoxAndWhiskerRenderer(); XYBoxAndWhiskerRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * A test for bug report 2909215. */ @Test public void test2909215() { DefaultBoxAndWhiskerXYDataset d1 = new DefaultBoxAndWhiskerXYDataset( "Series"); d1.add(new Date(1L), new BoxAndWhiskerItem(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, null, null, null)); JFreeChart chart = ChartFactory.createBoxAndWhiskerChart("Title", "X", "Y", d1, true); try { BufferedImage image = new BufferedImage(400, 200, BufferedImage.TYPE_INT_RGB); Graphics2D g2 = image.createGraphics(); chart.draw(g2, new Rectangle2D.Double(0, 0, 400, 200), null, null); g2.dispose(); } catch (Exception e) { fail("No exception should be thrown."); } } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/xy/XYBubbleRendererTest.java000066400000000000000000000124531463604235500322320ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * XYBubbleRendererTest.java * ------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import org.jfree.chart.JFreeChart; import org.jfree.chart.LegendItem; import org.jfree.chart.TestUtils; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.xy.DefaultXYZDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XYBubbleRenderer} class. */ public class XYBubbleRendererTest { /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { XYBubbleRenderer r1 = new XYBubbleRenderer(); XYBubbleRenderer r2 = new XYBubbleRenderer(); assertEquals(r1, r2); r1 = new XYBubbleRenderer(XYBubbleRenderer.SCALE_ON_RANGE_AXIS); assertNotEquals(r1, r2); r2 = new XYBubbleRenderer(XYBubbleRenderer.SCALE_ON_RANGE_AXIS); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { XYBubbleRenderer r1 = new XYBubbleRenderer(); XYBubbleRenderer r2 = new XYBubbleRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { XYBubbleRenderer r1 = new XYBubbleRenderer(); XYBubbleRenderer r2 = (XYBubbleRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { XYBubbleRenderer r1 = new XYBubbleRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XYBubbleRenderer r1 = new XYBubbleRenderer(); XYBubbleRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * A check for the datasetIndex and seriesIndex fields in the LegendItem * returned by the getLegendItem() method. */ @Test public void testGetLegendItemSeriesIndex() { DefaultXYZDataset d1 = new DefaultXYZDataset(); double[] x = {2.1, 2.3, 2.3, 2.2, 2.2, 1.8, 1.8, 1.9, 2.3, 3.8}; double[] y = {14.1, 11.1, 10.0, 8.8, 8.7, 8.4, 5.4, 4.1, 4.1, 25}; double[] z = {2.4, 2.7, 2.7, 2.2, 2.2, 2.2, 2.1, 2.2, 1.6, 4}; double[][] s1 = new double[][] {x, y, z}; d1.addSeries("S1", s1); x = new double[] {2.1}; y = new double[] {14.1}; z = new double[] {2.4}; double[][] s2 = new double[][] {x, y, z}; d1.addSeries("S2", s2); DefaultXYZDataset d2 = new DefaultXYZDataset(); x = new double[] {2.1}; y = new double[] {14.1}; z = new double[] {2.4}; double[][] s3 = new double[][] {x, y, z}; d2.addSeries("S3", s3); x = new double[] {2.1}; y = new double[] {14.1}; z = new double[] {2.4}; double[][] s4 = new double[][] {x, y, z}; d2.addSeries("S4", s4); x = new double[] {2.1}; y = new double[] {14.1}; z = new double[] {2.4}; double[][] s5 = new double[][] {x, y, z}; d2.addSeries("S5", s5); XYBubbleRenderer r = new XYBubbleRenderer(); XYPlot plot = new XYPlot(d1, new NumberAxis("x"), new NumberAxis("y"), r); plot.setDataset(1, d2); /*JFreeChart chart =*/ new JFreeChart(plot); LegendItem li = r.getLegendItem(1, 2); assertEquals("S5", li.getLabel()); assertEquals(1, li.getDatasetIndex()); assertEquals(2, li.getSeriesIndex()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/xy/XYDifferenceRendererTest.java000066400000000000000000000151221463604235500330650ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------- * XYDifferenceRendererTest.java * ----------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import java.awt.Color; import java.awt.GradientPaint; import java.awt.Shape; import java.awt.geom.Line2D; import org.jfree.chart.JFreeChart; import org.jfree.chart.LegendItem; import org.jfree.chart.TestUtils; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XYDifferenceRenderer} class. */ public class XYDifferenceRendererTest { /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { XYDifferenceRenderer r1 = new XYDifferenceRenderer( Color.RED, Color.BLUE, false); XYDifferenceRenderer r2 = new XYDifferenceRenderer( Color.RED, Color.BLUE, false); assertEquals(r1, r2); // positive paint r1.setPositivePaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); assertNotEquals(r1, r2); r2.setPositivePaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); assertEquals(r1, r2); // negative paint r1.setNegativePaint(new GradientPaint(1.0f, 2.0f, Color.YELLOW, 3.0f, 4.0f, Color.BLUE)); assertNotEquals(r1, r2); r2.setNegativePaint(new GradientPaint(1.0f, 2.0f, Color.YELLOW, 3.0f, 4.0f, Color.BLUE)); assertEquals(r1, r2); // shapesVisible r1 = new XYDifferenceRenderer(Color.GREEN, Color.YELLOW, true); assertNotEquals(r1, r2); r2 = new XYDifferenceRenderer(Color.GREEN, Color.YELLOW, true); assertEquals(r1, r2); // legendLine r1.setLegendLine(new Line2D.Double(1.0, 2.0, 3.0, 4.0)); assertNotEquals(r1, r2); r2.setLegendLine(new Line2D.Double(1.0, 2.0, 3.0, 4.0)); assertEquals(r1, r2); // roundXCoordinates r1.setRoundXCoordinates(true); assertNotEquals(r1, r2); r2.setRoundXCoordinates(true); assertEquals(r1, r2); assertNotEquals(null, r1); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { XYDifferenceRenderer r1 = new XYDifferenceRenderer(Color.RED, Color.BLUE, false); XYDifferenceRenderer r2 = new XYDifferenceRenderer(Color.RED, Color.BLUE, false); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. * * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { XYDifferenceRenderer r1 = new XYDifferenceRenderer(Color.RED, Color.BLUE, false); XYDifferenceRenderer r2 = (XYDifferenceRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); // check independence Shape s = r1.getLegendLine(); if (s instanceof Line2D) { Line2D l = (Line2D) s; l.setLine(1.0, 2.0, 3.0, 4.0); assertNotEquals(r1, r2); } } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { XYDifferenceRenderer r1 = new XYDifferenceRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XYDifferenceRenderer r1 = new XYDifferenceRenderer(Color.RED, Color.BLUE, false); XYDifferenceRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * A check for the datasetIndex and seriesIndex fields in the LegendItem * returned by the getLegendItem() method. */ @Test public void testGetLegendItemSeriesIndex() { XYSeriesCollection d1 = new XYSeriesCollection(); XYSeries s1 = new XYSeries("S1"); s1.add(1.0, 1.1); XYSeries s2 = new XYSeries("S2"); s2.add(1.0, 1.1); d1.addSeries(s1); d1.addSeries(s2); XYSeriesCollection d2 = new XYSeriesCollection(); XYSeries s3 = new XYSeries("S3"); s3.add(1.0, 1.1); XYSeries s4 = new XYSeries("S4"); s4.add(1.0, 1.1); XYSeries s5 = new XYSeries("S5"); s5.add(1.0, 1.1); d2.addSeries(s3); d2.addSeries(s4); d2.addSeries(s5); XYDifferenceRenderer r = new XYDifferenceRenderer(); XYPlot plot = new XYPlot(d1, new NumberAxis("x"), new NumberAxis("y"), r); plot.setDataset(1, d2); /*JFreeChart chart =*/ new JFreeChart(plot); LegendItem li = r.getLegendItem(1, 2); assertEquals("S5", li.getLabel()); assertEquals(1, li.getDatasetIndex()); assertEquals(2, li.getSeriesIndex()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/xy/XYDotRendererTest.java000066400000000000000000000122501463604235500315600ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * XYDotRendererTest.java * ---------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import java.awt.geom.Rectangle2D; import org.jfree.chart.JFreeChart; import org.jfree.chart.LegendItem; import org.jfree.chart.TestUtils; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XYDotRenderer} class. */ public class XYDotRendererTest { /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { XYDotRenderer r1 = new XYDotRenderer(); XYDotRenderer r2 = new XYDotRenderer(); assertEquals(r1, r2); r1.setDotWidth(11); assertNotEquals(r1, r2); r2.setDotWidth(11); assertEquals(r1, r2); r1.setDotHeight(12); assertNotEquals(r1, r2); r2.setDotHeight(12); assertEquals(r1, r2); r1.setLegendShape(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0)); assertNotEquals(r1, r2); r2.setLegendShape(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0)); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { XYDotRenderer r1 = new XYDotRenderer(); XYDotRenderer r2 = new XYDotRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); r1.setDotHeight(12); r2.setDotHeight(12); assertEquals(r1, r2); h1 = r1.hashCode(); h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { XYDotRenderer r1 = new XYDotRenderer(); XYDotRenderer r2 = (XYDotRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { XYDotRenderer r1 = new XYDotRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XYDotRenderer r1 = new XYDotRenderer(); XYDotRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * A check for the datasetIndex and seriesIndex fields in the LegendItem * returned by the getLegendItem() method. */ @Test public void testGetLegendItemSeriesIndex() { XYSeriesCollection d1 = new XYSeriesCollection(); XYSeries s1 = new XYSeries("S1"); s1.add(1.0, 1.1); XYSeries s2 = new XYSeries("S2"); s2.add(1.0, 1.1); d1.addSeries(s1); d1.addSeries(s2); XYSeriesCollection d2 = new XYSeriesCollection(); XYSeries s3 = new XYSeries("S3"); s3.add(1.0, 1.1); XYSeries s4 = new XYSeries("S4"); s4.add(1.0, 1.1); XYSeries s5 = new XYSeries("S5"); s5.add(1.0, 1.1); d2.addSeries(s3); d2.addSeries(s4); d2.addSeries(s5); XYDotRenderer r = new XYDotRenderer(); XYPlot plot = new XYPlot(d1, new NumberAxis("x"), new NumberAxis("y"), r); plot.setDataset(1, d2); /*JFreeChart chart =*/ new JFreeChart(plot); LegendItem li = r.getLegendItem(1, 2); assertEquals("S5", li.getLabel()); assertEquals(1, li.getDatasetIndex()); assertEquals(2, li.getSeriesIndex()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/xy/XYErrorRendererTest.java000066400000000000000000000130271463604235500321260ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * XYErrorRendererTest.java * ------------------------ * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import java.awt.BasicStroke; import java.awt.Color; import java.awt.GradientPaint; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XYErrorRenderer} class. */ public class XYErrorRendererTest { /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { XYErrorRenderer r1 = new XYErrorRenderer(); XYErrorRenderer r2 = new XYErrorRenderer(); assertEquals(r1, r2); // drawXError r1.setDrawXError(false); assertNotEquals(r1, r2); r2.setDrawXError(false); assertEquals(r1, r2); // drawYError r1.setDrawYError(false); assertNotEquals(r1, r2); r2.setDrawYError(false); assertEquals(r1, r2); // capLength r1.setCapLength(9.0); assertNotEquals(r1, r2); r2.setCapLength(9.0); assertEquals(r1, r2); // errorPaint r1.setErrorPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.GREEN)); assertNotEquals(r1, r2); r2.setErrorPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.GREEN)); assertEquals(r1, r2); // errorStroke r1.setErrorStroke(new BasicStroke(1.5f)); assertNotEquals(r1, r2); r2.setErrorStroke(new BasicStroke(1.5f)); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { XYErrorRenderer r1 = new XYErrorRenderer(); XYErrorRenderer r2 = new XYErrorRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { XYErrorRenderer r1 = new XYErrorRenderer(); r1.setErrorPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.WHITE)); XYErrorRenderer r2 = (XYErrorRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * A test for cloning. */ @Test public void testCloning2() throws CloneNotSupportedException { XYErrorRenderer r1 = new XYErrorRenderer(); r1.setErrorStroke(new BasicStroke(1.5f)); XYErrorRenderer r2 = (XYErrorRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { XYErrorRenderer r1 = new XYErrorRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XYErrorRenderer r1 = new XYErrorRenderer(); r1.setErrorPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.WHITE)); XYErrorRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization2() { XYErrorRenderer r1 = new XYErrorRenderer(); r1.setErrorStroke(new BasicStroke(1.5f)); XYErrorRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * Some checks for the findDomainBounds() method. */ @Test public void testFindDomainBounds() { XYErrorRenderer r = new XYErrorRenderer(); assertNull(r.findDomainBounds(null)); } /** * Some checks for the findRangeBounds() method. */ @Test public void testFindRangeBounds() { XYErrorRenderer r = new XYErrorRenderer(); assertNull(r.findRangeBounds(null)); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/xy/XYLineAndShapeRendererTest.java000066400000000000000000000227361463604235500333370ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------- * XYLineAndShapeRendererTest.java * ------------------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.LegendItem; import org.jfree.chart.TestUtils; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.urls.TimeSeriesURLGenerator; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.Range; import org.jfree.data.xy.TableXYDataset; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XYLineAndShapeRenderer} class. */ public class XYLineAndShapeRendererTest { /** * Test that the equals() method distinguishes all fields. */ @Test public void testEquals() { XYLineAndShapeRenderer r1 = new XYLineAndShapeRenderer(); XYLineAndShapeRenderer r2 = new XYLineAndShapeRenderer(); assertEquals(r1, r2); assertEquals(r2, r1); r1.setSeriesLinesVisible(3, true); assertNotEquals(r1, r2); r2.setSeriesLinesVisible(3, true); assertEquals(r1, r2); r1.setDefaultLinesVisible(false); assertNotEquals(r1, r2); r2.setDefaultLinesVisible(false); assertEquals(r1, r2); r1.setLegendLine(new Line2D.Double(1.0, 2.0, 3.0, 4.0)); assertNotEquals(r1, r2); r2.setLegendLine(new Line2D.Double(1.0, 2.0, 3.0, 4.0)); assertEquals(r1, r2); r1.setSeriesShapesVisible(3, true); assertNotEquals(r1, r2); r2.setSeriesShapesVisible(3, true); assertEquals(r1, r2); r1.setDefaultShapesVisible(false); assertNotEquals(r1, r2); r2.setDefaultShapesVisible(false); assertEquals(r1, r2); r1.setSeriesShapesFilled(3, true); assertNotEquals(r1, r2); r2.setSeriesShapesFilled(3, true); assertEquals(r1, r2); r1.setDefaultShapesFilled(false); assertNotEquals(r1, r2); r2.setDefaultShapesFilled(false); assertEquals(r1, r2); r1.setDrawOutlines(!r1.getDrawOutlines()); assertNotEquals(r1, r2); r2.setDrawOutlines(r1.getDrawOutlines()); assertEquals(r1, r2); r1.setUseOutlinePaint(true); assertNotEquals(r1, r2); r2.setUseOutlinePaint(true); assertEquals(r1, r2); r1.setUseFillPaint(true); assertNotEquals(r1, r2); r2.setUseFillPaint(true); assertEquals(r1, r2); r1.setDrawSeriesLineAsPath(true); assertNotEquals(r1, r2); r2.setDrawSeriesLineAsPath(true); assertEquals(r1, r2); } /** * Test that the equals() method works for a TimeSeriesURLGenerator. */ @Test public void testEquals2() { XYLineAndShapeRenderer r1 = new XYLineAndShapeRenderer(); XYLineAndShapeRenderer r2 = new XYLineAndShapeRenderer(); assertEquals(r1, r2); assertEquals(r2, r1); r1.setURLGenerator(new TimeSeriesURLGenerator()); assertNotEquals(r1, r2); r2.setURLGenerator(new TimeSeriesURLGenerator()); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { XYLineAndShapeRenderer r1 = new XYLineAndShapeRenderer(); XYLineAndShapeRenderer r2 = new XYLineAndShapeRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { Rectangle2D legendShape = new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0); XYLineAndShapeRenderer r1 = new XYLineAndShapeRenderer(); r1.setLegendLine(legendShape); XYLineAndShapeRenderer r2 = (XYLineAndShapeRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); r1.setSeriesLinesVisible(0, false); assertNotEquals(r1, r2); r2.setSeriesLinesVisible(0, false); assertEquals(r1, r2); legendShape.setRect(4.0, 3.0, 2.0, 1.0); assertNotEquals(r1, r2); r2.setLegendLine(new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0)); assertEquals(r1, r2); r1.setSeriesShapesVisible(1, true); assertNotEquals(r1, r2); r2.setSeriesShapesVisible(1, true); assertEquals(r1, r2); r1.setSeriesShapesFilled(1, true); assertNotEquals(r1, r2); r2.setSeriesShapesFilled(1, true); assertEquals(r1, r2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { XYLineAndShapeRenderer r1 = new XYLineAndShapeRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XYLineAndShapeRenderer r1 = new XYLineAndShapeRenderer(); XYLineAndShapeRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * Check that the renderer is calculating the domain bounds correctly. */ @Test public void testFindDomainBounds() { XYSeriesCollection dataset = RendererXYPackageUtils.createTestXYSeriesCollection(); JFreeChart chart = ChartFactory.createXYLineChart( "Test Chart", "X", "Y", dataset, PlotOrientation.VERTICAL, false, false, false); XYPlot plot = (XYPlot) chart.getPlot(); NumberAxis domainAxis = (NumberAxis) plot.getDomainAxis(); domainAxis.setAutoRangeIncludesZero(false); Range bounds = domainAxis.getRange(); assertFalse(bounds.contains(0.9)); assertTrue(bounds.contains(1.0)); assertTrue(bounds.contains(2.0)); assertFalse(bounds.contains(2.10)); } /** * Check that the renderer is calculating the range bounds correctly. */ @Test public void testFindRangeBounds() { TableXYDataset dataset = RendererXYPackageUtils.createTestTableXYDataset(); JFreeChart chart = ChartFactory.createXYLineChart( "Test Chart", "X", "Y", dataset, PlotOrientation.VERTICAL, false, false, false); XYPlot plot = (XYPlot) chart.getPlot(); NumberAxis rangeAxis = (NumberAxis) plot.getRangeAxis(); rangeAxis.setAutoRangeIncludesZero(false); Range bounds = rangeAxis.getRange(); assertFalse(bounds.contains(1.0)); assertTrue(bounds.contains(2.0)); assertTrue(bounds.contains(5.0)); assertFalse(bounds.contains(6.0)); } /** * A check for the datasetIndex and seriesIndex fields in the LegendItem * returned by the getLegendItem() method. */ @Test public void testGetLegendItemSeriesIndex() { XYSeriesCollection d1 = new XYSeriesCollection(); XYSeries s1 = new XYSeries("S1"); s1.add(1.0, 1.1); XYSeries s2 = new XYSeries("S2"); s2.add(1.0, 1.1); d1.addSeries(s1); d1.addSeries(s2); XYSeriesCollection d2 = new XYSeriesCollection(); XYSeries s3 = new XYSeries("S3"); s3.add(1.0, 1.1); XYSeries s4 = new XYSeries("S4"); s4.add(1.0, 1.1); XYSeries s5 = new XYSeries("S5"); s5.add(1.0, 1.1); d2.addSeries(s3); d2.addSeries(s4); d2.addSeries(s5); XYLineAndShapeRenderer r = new XYLineAndShapeRenderer(); XYPlot plot = new XYPlot(d1, new NumberAxis("x"), new NumberAxis("y"), r); plot.setDataset(1, d2); /*JFreeChart chart =*/ new JFreeChart(plot); LegendItem li = r.getLegendItem(1, 2); assertEquals("S5", li.getLabel()); assertEquals(1, li.getDatasetIndex()); assertEquals(2, li.getSeriesIndex()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/xy/XYShapeRendererTest.java000066400000000000000000000133201463604235500320710ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * XYShapeRendererTest.java * ------------------------ * (C) Copyright 2010-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Martin Hoeller (patch 2952086); * */ package org.jfree.chart.renderer.xy; import java.awt.Color; import org.jfree.chart.TestUtils; import org.jfree.chart.renderer.LookupPaintScale; import org.jfree.data.Range; import org.jfree.data.xy.DefaultXYZDataset; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XYShapeRenderer} class. */ public class XYShapeRendererTest { /** * Some checks for the equals() method. */ @Test public void testEquals() { XYShapeRenderer r1 = new XYShapeRenderer(); XYShapeRenderer r2 = new XYShapeRenderer(); assertEquals(r1, r2); assertEquals(r2, r1); r1.setPaintScale(new LookupPaintScale(1.0, 2.0, Color.WHITE)); assertNotEquals(r1, r2); r2.setPaintScale(new LookupPaintScale(1.0, 2.0, Color.WHITE)); assertEquals(r1, r2); r1.setDrawOutlines(true); assertNotEquals(r1, r2); r2.setDrawOutlines(true); assertEquals(r1, r2); r1.setUseOutlinePaint(false); assertNotEquals(r1, r2); r2.setUseOutlinePaint(false); assertEquals(r1, r2); r1.setUseFillPaint(true); assertNotEquals(r1, r2); r2.setUseFillPaint(true); assertEquals(r1, r2); r1.setGuideLinesVisible(true); assertNotEquals(r1, r2); r2.setGuideLinesVisible(true); assertEquals(r1, r2); r1.setGuideLinePaint(Color.RED); assertNotEquals(r1, r2); r2.setGuideLinePaint(Color.RED); assertEquals(r1, r2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { XYShapeRenderer r1 = new XYShapeRenderer(); XYShapeRenderer r2 = (XYShapeRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XYShapeRenderer r1 = new XYShapeRenderer(); XYShapeRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } private static final double EPSILON = 0.0000000001; /** * Check if finding the bounds in z-dimension of an XYZDataset works. */ @Test public void testFindZBounds() { XYShapeRenderer r = new XYShapeRenderer(); assertNull(r.findZBounds(null)); DefaultXYZDataset dataset = new DefaultXYZDataset(); Range range; double[][] data1 = { {1,1,1}, {1,1,1}, {1,2,3} }; dataset.addSeries("series1", data1); range = r.findZBounds(dataset); assertNotNull(range); assertEquals(1d, range.getLowerBound(), EPSILON); assertEquals(3d, range.getUpperBound(), EPSILON); double[][] data2 = { {1,1,1}, {1,1,1}, {-1,-2,-3} }; dataset.removeSeries("series1"); dataset.addSeries("series2", data2); range = r.findZBounds(dataset); assertNotNull(range); assertEquals(-3d, range.getLowerBound(), EPSILON); assertEquals(-1d, range.getUpperBound(), EPSILON); double[][] data3 = { {1,1,1}, {1,1,1}, {-1.2,2.9,3.8} }; dataset.removeSeries("series2"); dataset.addSeries("series3", data3); range = r.findZBounds(dataset); assertNotNull(range); assertEquals(-1.2d, range.getLowerBound(), EPSILON); assertEquals(3.8d, range.getUpperBound(), EPSILON); } /** * Test for bug 3026341. */ @Test public void test3026341() { XYShapeRenderer renderer = new XYShapeRenderer(); assertNull(renderer.findRangeBounds(null)); XYSeriesCollection dataset = new XYSeriesCollection(); XYSeries series = new XYSeries("S1"); series.add(1.0, null); dataset.addSeries(series); Range r = renderer.findRangeBounds(dataset); assertNull(r); // test findDomainBounds as well r = renderer.findDomainBounds(dataset); assertEquals(r.getLowerBound(), 1.0, EPSILON); assertEquals(r.getUpperBound(), 1.0, EPSILON); dataset.removeAllSeries(); r = renderer.findDomainBounds(dataset); assertNull(r); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/xy/XYSplineRendererTest.java000066400000000000000000000104241463604235500322650ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * XYSplineRendererTest.java * ------------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import java.awt.geom.Rectangle2D; import org.jfree.chart.TestUtils; import org.jfree.chart.ui.GradientPaintTransformType; import org.jfree.chart.ui.StandardGradientPaintTransformer; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XYSplineRenderer} class. */ public class XYSplineRendererTest { /** * Test that the equals() method distinguishes all fields. */ @Test public void testEquals() { XYSplineRenderer r1 = new XYSplineRenderer(); XYSplineRenderer r2 = new XYSplineRenderer(); assertEquals(r1, r2); assertEquals(r2, r1); r1.setPrecision(9); assertNotEquals(r1, r2); r2.setPrecision(9); assertEquals(r1, r2); r1.setFillType(XYSplineRenderer.FillType.TO_ZERO); assertNotEquals(r1, r2); r2.setFillType(XYSplineRenderer.FillType.TO_ZERO); assertEquals(r1, r2); r1.setGradientPaintTransformer(null); assertNotEquals(r1, r2); r2.setGradientPaintTransformer(null); assertEquals(r1, r2); r1.setGradientPaintTransformer(new StandardGradientPaintTransformer( GradientPaintTransformType.HORIZONTAL)); assertNotEquals(r1, r2); r2.setGradientPaintTransformer(new StandardGradientPaintTransformer( GradientPaintTransformType.HORIZONTAL)); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { XYSplineRenderer r1 = new XYSplineRenderer(); XYSplineRenderer r2 = new XYSplineRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { Rectangle2D legendShape = new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0); XYSplineRenderer r1 = new XYSplineRenderer(); r1.setLegendLine(legendShape); XYSplineRenderer r2 = (XYSplineRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { XYSplineRenderer r1 = new XYSplineRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XYSplineRenderer r1 = new XYSplineRenderer(); XYSplineRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/xy/XYStepAreaRendererTest.java000066400000000000000000000125331463604235500325420ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * XYStepAreaRendererTest.java * --------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Matthias Rose; * */ package org.jfree.chart.renderer.xy; import org.jfree.chart.JFreeChart; import org.jfree.chart.TestUtils; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.xy.DefaultTableXYDataset; import org.jfree.data.xy.XYSeries; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XYStepAreaRenderer} class. */ public class XYStepAreaRendererTest { /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { XYStepAreaRenderer r1 = new XYStepAreaRenderer(); XYStepAreaRenderer r2 = new XYStepAreaRenderer(); assertEquals(r1, r2); r1.setOutline(true); assertNotEquals(r1, r2); r2.setOutline(true); assertEquals(r1, r2); r1.setShapesVisible(true); assertNotEquals(r1, r2); r2.setShapesVisible(true); assertEquals(r1, r2); r1.setShapesFilled(true); assertNotEquals(r1, r2); r2.setShapesFilled(true); assertEquals(r1, r2); r1.setPlotArea(false); assertNotEquals(r1, r2); r2.setPlotArea(false); assertEquals(r1, r2); r1.setRangeBase(-1.0); assertNotEquals(r1, r2); r2.setRangeBase(-1.0); assertEquals(r1, r2); r1.setStepPoint(0.33); assertNotEquals(r1, r2); r2.setStepPoint(0.33); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { XYStepAreaRenderer r1 = new XYStepAreaRenderer(); XYStepAreaRenderer r2 = new XYStepAreaRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { XYStepAreaRenderer r1 = new XYStepAreaRenderer(); XYStepAreaRenderer r2 = (XYStepAreaRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { XYStepAreaRenderer r1 = new XYStepAreaRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XYStepAreaRenderer r1 = new XYStepAreaRenderer(); XYStepAreaRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * Draws the chart with a {@code null} info object to make sure that * no exceptions are thrown (particularly by code in the renderer). */ @Test public void testDrawWithNullInfo() { try { DefaultTableXYDataset dataset = new DefaultTableXYDataset(); XYSeries s1 = new XYSeries("Series 1", true, false); s1.add(5.0, 5.0); s1.add(10.0, 15.5); s1.add(15.0, 9.5); s1.add(20.0, 7.5); dataset.addSeries(s1); XYSeries s2 = new XYSeries("Series 2", true, false); s2.add(5.0, 5.0); s2.add(10.0, 15.5); s2.add(15.0, 9.5); s2.add(20.0, 3.5); dataset.addSeries(s2); XYPlot plot = new XYPlot(dataset, new NumberAxis("X"), new NumberAxis("Y"), new XYStepAreaRenderer()); JFreeChart chart = new JFreeChart(plot); /* BufferedImage image = */ chart.createBufferedImage(300, 200, null); } catch (NullPointerException e) { fail("No exception should be thrown."); } } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/xy/XYStepRendererTest.java000066400000000000000000000140141463604235500317450ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * XYStepRendererTest.java * ----------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import org.jfree.chart.JFreeChart; import org.jfree.chart.TestUtils; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.xy.DefaultTableXYDataset; import org.jfree.data.xy.XYSeries; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XYStepRenderer} class. */ public class XYStepRendererTest { /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { XYStepRenderer r1 = new XYStepRenderer(); XYStepRenderer r2 = new XYStepRenderer(); assertEquals(r1, r2); r1.setStepPoint(0.44); assertNotEquals(r1, r2); r2.setStepPoint(0.44); assertEquals(r1, r2); // try something from the base class r1.setDefaultCreateEntities(false); assertNotEquals(r1, r2); r2.setDefaultCreateEntities(false); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { XYStepRenderer r1 = new XYStepRenderer(); r1.setStepPoint(0.123); XYStepRenderer r2 = new XYStepRenderer(); r2.setStepPoint(0.123); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { XYStepRenderer r1 = new XYStepRenderer(); XYStepRenderer r2 = (XYStepRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { XYStepRenderer r1 = new XYStepRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XYStepRenderer r1 = new XYStepRenderer(); r1.setStepPoint(0.123); XYStepRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * Draws the chart with a {@code null} info object to make sure that * no exceptions are thrown (particularly by code in the renderer). */ @Test public void testDrawWithNullInfo() { try { DefaultTableXYDataset dataset = new DefaultTableXYDataset(); XYSeries s1 = new XYSeries("Series 1", true, false); s1.add(5.0, 5.0); s1.add(10.0, 15.5); s1.add(15.0, 9.5); s1.add(20.0, 7.5); dataset.addSeries(s1); XYSeries s2 = new XYSeries("Series 2", true, false); s2.add(5.0, 5.0); s2.add(10.0, 15.5); s2.add(15.0, 9.5); s2.add(20.0, 3.5); dataset.addSeries(s2); XYPlot plot = new XYPlot(dataset, new NumberAxis("X"), new NumberAxis("Y"), new XYStepRenderer()); JFreeChart chart = new JFreeChart(plot); /* BufferedImage image = */ chart.createBufferedImage(300, 200, null); } catch (NullPointerException e) { fail("No exception should be thrown."); } } /** * Draws the chart with a {@code null} value in the dataset to make * sure that no exceptions are thrown. */ @Test public void testDrawWithNullValue() { try { DefaultTableXYDataset dataset = new DefaultTableXYDataset(); XYSeries s1 = new XYSeries("Series 1", true, false); s1.add(5.0, 5.0); s1.add(10.0, null); s1.add(15.0, 9.5); s1.add(20.0, 7.5); dataset.addSeries(s1); XYSeries s2 = new XYSeries("Series 2", true, false); s2.add(5.0, 5.0); s2.add(10.0, 15.5); s2.add(15.0, null); s2.add(20.0, null); dataset.addSeries(s2); XYPlot plot = new XYPlot(dataset, new NumberAxis("X"), new NumberAxis("Y"), new XYStepRenderer()); JFreeChart chart = new JFreeChart(plot); /* BufferedImage image = */ chart.createBufferedImage(300, 200, null); } catch (NullPointerException e) { fail("No exception should be thrown."); } } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/renderer/xy/YIntervalRendererTest.java000066400000000000000000000213161463604235500324710ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * YIntervalRendererTest.java * -------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.renderer.xy; import org.jfree.chart.JFreeChart; import org.jfree.chart.LegendItem; import org.jfree.chart.TestUtils; import org.jfree.chart.annotations.XYTextAnnotation; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.labels.IntervalXYItemLabelGenerator; import org.jfree.chart.labels.StandardXYItemLabelGenerator; import org.jfree.chart.labels.StandardXYSeriesLabelGenerator; import org.jfree.chart.labels.StandardXYToolTipGenerator; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.Layer; import org.jfree.chart.urls.StandardXYURLGenerator; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.xy.YIntervalSeries; import org.jfree.data.xy.YIntervalSeriesCollection; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link YIntervalRenderer} class. */ public class YIntervalRendererTest { /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { YIntervalRenderer r1 = new YIntervalRenderer(); YIntervalRenderer r2 = new YIntervalRenderer(); assertEquals(r1, r2); // the following fields are inherited from the AbstractXYItemRenderer r1.setSeriesItemLabelGenerator(0, new StandardXYItemLabelGenerator()); assertNotEquals(r1, r2); r2.setSeriesItemLabelGenerator(0, new StandardXYItemLabelGenerator()); assertEquals(r1, r2); r1.setDefaultItemLabelGenerator(new StandardXYItemLabelGenerator()); assertNotEquals(r1, r2); r2.setDefaultItemLabelGenerator(new StandardXYItemLabelGenerator()); assertEquals(r1, r2); r1.setSeriesToolTipGenerator(0, new StandardXYToolTipGenerator()); assertNotEquals(r1, r2); r2.setSeriesToolTipGenerator(0, new StandardXYToolTipGenerator()); assertEquals(r1, r2); r1.setDefaultToolTipGenerator(new StandardXYToolTipGenerator()); assertNotEquals(r1, r2); r2.setDefaultToolTipGenerator(new StandardXYToolTipGenerator()); assertEquals(r1, r2); r1.setURLGenerator(new StandardXYURLGenerator()); assertNotEquals(r1, r2); r2.setURLGenerator(new StandardXYURLGenerator()); assertEquals(r1, r2); r1.addAnnotation(new XYTextAnnotation("X", 1.0, 2.0), Layer.FOREGROUND); assertNotEquals(r1, r2); r2.addAnnotation(new XYTextAnnotation("X", 1.0, 2.0), Layer.FOREGROUND); assertEquals(r1, r2); r1.addAnnotation(new XYTextAnnotation("X", 1.0, 2.0), Layer.BACKGROUND); assertNotEquals(r1, r2); r2.addAnnotation(new XYTextAnnotation("X", 1.0, 2.0), Layer.BACKGROUND); assertEquals(r1, r2); r1.setDefaultEntityRadius(99); assertNotEquals(r1, r2); r2.setDefaultEntityRadius(99); assertEquals(r1, r2); r1.setLegendItemLabelGenerator(new StandardXYSeriesLabelGenerator( "{0} {1}")); assertNotEquals(r1, r2); r2.setLegendItemLabelGenerator(new StandardXYSeriesLabelGenerator( "{0} {1}")); assertEquals(r1, r2); r1.setLegendItemToolTipGenerator(new StandardXYSeriesLabelGenerator()); assertNotEquals(r1, r2); r2.setLegendItemToolTipGenerator(new StandardXYSeriesLabelGenerator()); assertEquals(r1, r2); r1.setLegendItemURLGenerator(new StandardXYSeriesLabelGenerator()); assertNotEquals(r1, r2); r2.setLegendItemURLGenerator(new StandardXYSeriesLabelGenerator()); assertEquals(r1, r2); r1.setAdditionalItemLabelGenerator(new IntervalXYItemLabelGenerator()); assertNotEquals(r1, r2); r2.setAdditionalItemLabelGenerator(new IntervalXYItemLabelGenerator()); assertEquals(r1, r2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { YIntervalRenderer r1 = new YIntervalRenderer(); YIntervalRenderer r2 = new YIntervalRenderer(); assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { YIntervalRenderer r1 = new YIntervalRenderer(); YIntervalRenderer r2 = (YIntervalRenderer) r1.clone(); assertNotSame(r1, r2); assertSame(r1.getClass(), r2.getClass()); assertEquals(r1, r2); // check independence r1.setSeriesItemLabelGenerator(0, new StandardXYItemLabelGenerator()); assertNotEquals(r1, r2); r2.setSeriesItemLabelGenerator(0, new StandardXYItemLabelGenerator()); assertEquals(r1, r2); r1.setSeriesToolTipGenerator(0, new StandardXYToolTipGenerator()); assertNotEquals(r1, r2); r2.setSeriesToolTipGenerator(0, new StandardXYToolTipGenerator()); assertEquals(r1, r2); r1.addAnnotation(new XYTextAnnotation("ABC", 1.0, 2.0), Layer.FOREGROUND); assertNotEquals(r1, r2); r2.addAnnotation(new XYTextAnnotation("ABC", 1.0, 2.0), Layer.FOREGROUND); assertEquals(r1, r2); r1.addAnnotation(new XYTextAnnotation("ABC", 1.0, 2.0), Layer.BACKGROUND); assertNotEquals(r1, r2); r2.addAnnotation(new XYTextAnnotation("ABC", 1.0, 2.0), Layer.BACKGROUND); assertEquals(r1, r2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { YIntervalRenderer r1 = new YIntervalRenderer(); assertTrue(r1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { YIntervalRenderer r1 = new YIntervalRenderer(); YIntervalRenderer r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * A check for the datasetIndex and seriesIndex fields in the LegendItem * returned by the getLegendItem() method. */ @Test public void testGetLegendItemSeriesIndex() { YIntervalSeriesCollection d1 = new YIntervalSeriesCollection(); YIntervalSeries s1 = new YIntervalSeries("S1"); s1.add(1.0, 1.1, 1.2, 1.3); YIntervalSeries s2 = new YIntervalSeries("S2"); s2.add(1.0, 1.1, 1.2, 1.3); d1.addSeries(s1); d1.addSeries(s2); YIntervalSeriesCollection d2 = new YIntervalSeriesCollection(); YIntervalSeries s3 = new YIntervalSeries("S3"); s3.add(1.0, 1.1, 1.2, 1.3); YIntervalSeries s4 = new YIntervalSeries("S4"); s4.add(1.0, 1.1, 1.2, 1.3); YIntervalSeries s5 = new YIntervalSeries("S5"); s5.add(1.0, 1.1, 1.2, 1.3); d2.addSeries(s3); d2.addSeries(s4); d2.addSeries(s5); YIntervalRenderer r = new YIntervalRenderer(); XYPlot plot = new XYPlot(d1, new NumberAxis("x"), new NumberAxis("y"), r); plot.setDataset(1, d2); /*JFreeChart chart =*/ new JFreeChart(plot); LegendItem li = r.getLegendItem(1, 2); assertEquals("S5", li.getLabel()); assertEquals(1, li.getDatasetIndex()); assertEquals(2, li.getSeriesIndex()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/title/000077500000000000000000000000001463604235500242325ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/title/CompositeTitleTest.java000066400000000000000000000140271463604235500307050ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * CompositeTitleTest.java * ----------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.title; import java.awt.Color; import java.awt.GradientPaint; import java.awt.geom.Rectangle2D; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.chart.block.BlockBorder; import org.jfree.chart.block.BlockContainer; import org.jfree.chart.ui.RectangleInsets; import org.jfree.chart.util.PaintUtils; import org.junit.jupiter.api.Test; import javax.swing.event.EventListenerList; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link CompositeTitle} class. */ public class CompositeTitleTest { @Test public void testEqualsHashCode() { EqualsVerifier.forClass(CompositeTitle.class) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .withRedefinedSuperclass() .withPrefabValues(EventListenerList.class, new EventListenerList(), new EventListenerList()) .withPrefabValues(Rectangle2D.class, TestUtils.createR2D(true), TestUtils.createR2D(false)) .verify(); } /** * Some checks for the constructor. */ @Test public void testConstructor() { CompositeTitle t = new CompositeTitle(); assertNull(t.getBackgroundPaint()); } /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { CompositeTitle t1 = new CompositeTitle(new BlockContainer()); CompositeTitle t2 = new CompositeTitle(new BlockContainer()); assertEquals(t1, t2); assertEquals(t2, t1); // margin t1.setMargin(new RectangleInsets(1.0, 2.0, 3.0, 4.0)); assertNotEquals(t1, t2); t2.setMargin(new RectangleInsets(1.0, 2.0, 3.0, 4.0)); assertEquals(t1, t2); // frame t1.setFrame(new BlockBorder(Color.RED)); assertNotEquals(t1, t2); t2.setFrame(new BlockBorder(Color.RED)); assertEquals(t1, t2); // padding t1.setPadding(new RectangleInsets(1.0, 2.0, 3.0, 4.0)); assertNotEquals(t1, t2); t2.setPadding(new RectangleInsets(1.0, 2.0, 3.0, 4.0)); assertEquals(t1, t2); // contained titles t1.getContainer().add(new TextTitle("T1")); assertNotEquals(t1, t2); t2.getContainer().add(new TextTitle("T1")); assertEquals(t1, t2); t1.setBackgroundPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW)); assertFalse(PaintUtils.equal(t1.getBackgroundPaint(), t2.getBackgroundPaint())); t2.setBackgroundPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW)); assertTrue(PaintUtils.equal(t1.getBackgroundPaint(), t2.getBackgroundPaint())); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { CompositeTitle t1 = new CompositeTitle(new BlockContainer()); t1.getContainer().add(new TextTitle("T1")); CompositeTitle t2 = new CompositeTitle(new BlockContainer()); t2.getContainer().add(new TextTitle("T1")); assertEquals(t1, t2); int h1 = t1.hashCode(); int h2 = t2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() { CompositeTitle t1 = new CompositeTitle(new BlockContainer()); t1.getContainer().add(new TextTitle("T1")); t1.setBackgroundPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW)); CompositeTitle t2 = null; try { t2 = (CompositeTitle) t1.clone(); } catch (CloneNotSupportedException e) { fail(e.toString()); } assertNotSame(t1, t2); assertSame(t1.getClass(), t2.getClass()); assertEquals(t1, t2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { CompositeTitle t1 = new CompositeTitle(new BlockContainer()); t1.getContainer().add(new TextTitle("T1")); t1.setBackgroundPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); CompositeTitle t2 = TestUtils.serialised(t1); assertEquals(t1, t2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/title/DateTitleTest.java000066400000000000000000000071301463604235500276150ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * DateTitleTest.java * ------------------ * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.title; import java.awt.Color; import java.awt.Font; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PaintUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link DateTitle} class. */ public class DateTitleTest { /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { DateTitle t1 = new DateTitle(); DateTitle t2 = new DateTitle(); assertEquals(t1, t2); t1.setText("Test 1"); assertNotEquals(t1, t2); t2.setText("Test 1"); assertEquals(t1, t2); Font f = new Font("SansSerif", Font.PLAIN, 15); t1.setFont(f); assertNotEquals(t1, t2); t2.setFont(f); assertEquals(t1, t2); t1.setPaint(Color.BLUE); assertFalse(PaintUtils.equal(t1.getPaint(), t2.getPaint())); t2.setPaint(Color.BLUE); assertTrue(PaintUtils.equal(t1.getPaint(), t2.getPaint())); t1.setBackgroundPaint(Color.BLUE); assertFalse(PaintUtils.equal(t1.getBackgroundPaint(), t2.getBackgroundPaint())); t2.setBackgroundPaint(Color.BLUE); assertTrue(PaintUtils.equal(t1.getBackgroundPaint(), t2.getBackgroundPaint())); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { DateTitle t1 = new DateTitle(); DateTitle t2 = new DateTitle(); assertEquals(t1, t2); int h1 = t1.hashCode(); int h2 = t2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { DateTitle t1 = new DateTitle(); DateTitle t2 = (DateTitle) t1.clone(); assertNotSame(t1, t2); assertSame(t1.getClass(), t2.getClass()); assertEquals(t1, t2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DateTitle t1 = new DateTitle(); DateTitle t2 = TestUtils.serialised(t1); assertEquals(t1, t2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/title/ImageTitleTest.java000066400000000000000000000112361463604235500277640ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * ImageTitleTest.java * ------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.title; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import org.jfree.chart.JFreeChart; import org.jfree.chart.ui.Size2D; import org.junit.jupiter.api.Test; /** * Tests for the {@link ImageTitle} class. */ public class ImageTitleTest { // /** // * Check that the equals() method distinguishes all fields. // */ // @Test // public void testEquals() { // ImageTitle t1 = new ImageTitle(JFreeChart.INFO.getLogo()); // ImageTitle t2 = new ImageTitle(JFreeChart.INFO.getLogo()); // assertEquals(t1, t2); // // t1.setImage(new BufferedImage(2, 1, BufferedImage.TYPE_INT_RGB)); // assertFalse(t1.equals(t2)); // t2.setImage(new BufferedImage(2, 1, BufferedImage.TYPE_INT_RGB)); // // images considered equal only if they're the SAME object // // TODO: is there a way to do a better test? // assertFalse(t1.equals(t2)); // } // /** // * Two objects that are equal are required to return the same hashCode. // */ // @Test // public void testHashcode() { // ImageTitle t1 = new ImageTitle(JFreeChart.INFO.getLogo()); // ImageTitle t2 = new ImageTitle(JFreeChart.INFO.getLogo()); // assertTrue(t1.equals(t2)); // int h1 = t1.hashCode(); // int h2 = t2.hashCode(); // assertEquals(h1, h2); // } // /** // * Confirm that cloning works. // */ // @Test // public void testCloning() throws CloneNotSupportedException { // ImageTitle t1 = new ImageTitle(JFreeChart.INFO.getLogo()); // ImageTitle t2 = (ImageTitle) t1.clone(); // assertTrue(t1 != t2); // assertTrue(t1.getClass() == t2.getClass()); // assertTrue(t1.equals(t2)); // } /** * Serialize an instance, restore it, and check for equality. */ public void testSerialization() { // TODO: add serialization support for images } private static final double EPSILON = 0.00000001; // /** // * Check the width and height. // */ // @Test // public void testWidthAndHeight() { // ImageTitle t1 = new ImageTitle(JFreeChart.INFO.getLogo()); // assertEquals(100, t1.getWidth(), EPSILON); // assertEquals(100, t1.getHeight(), EPSILON); // } // /** // * Some checks for the arrange method. // */ // @Test // public void testArrangeNN() { // BufferedImage image = new BufferedImage(100, 100, // BufferedImage.TYPE_INT_RGB); // Graphics2D g2 = image.createGraphics(); // ImageTitle t = new ImageTitle(JFreeChart.INFO.getLogo()); // Size2D s = t.arrange(g2); // assertEquals(102.0, s.getWidth(), EPSILON); // assertEquals(102.0, s.getHeight(), EPSILON); // // t.setPadding(1.0, 2.0, 3.0, 4.0); // s = t.arrange(g2); // assertEquals(106.0, s.getWidth(), EPSILON); // assertEquals(104.0, s.getHeight(), EPSILON); // // t.setMargin(5.0, 6.0, 7.0, 8.0); // s = t.arrange(g2); // assertEquals(120.0, s.getWidth(), EPSILON); // assertEquals(116.0, s.getHeight(), EPSILON); // } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/title/LegendGraphicTest.java000066400000000000000000000177711463604235500304460ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * LegendGraphicTest.java * ---------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.title; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.Rectangle; import java.awt.Stroke; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.chart.ui.GradientPaintTransformType; import org.jfree.chart.ui.RectangleAnchor; import org.jfree.chart.ui.StandardGradientPaintTransformer; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link LegendGraphic} class. */ public class LegendGraphicTest { @Test public void testEqualsHashCode() { EqualsVerifier.forClass(LegendGraphic.class) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .withRedefinedSuperclass() .withPrefabValues(Rectangle2D.class, TestUtils.createR2D(true), TestUtils.createR2D(false)) .withPrefabValues(Font.class, TestUtils.createFont(true), TestUtils.createFont(false)) .verify(); } /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { LegendGraphic g1 = new LegendGraphic(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), Color.BLACK); LegendGraphic g2 = new LegendGraphic(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), Color.BLACK); assertEquals(g1, g2); assertEquals(g2, g1); // shapeVisible g1.setShapeVisible(!g1.isShapeVisible()); assertNotEquals(g1, g2); g2.setShapeVisible(!g2.isShapeVisible()); assertEquals(g1, g2); // shape g1.setShape(new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0)); assertNotEquals(g1, g2); g2.setShape(new Rectangle2D.Double(4.0, 3.0, 2.0, 1.0)); assertEquals(g1, g2); // shapeFilled g1.setShapeFilled(!g1.isShapeFilled()); assertNotEquals(g1, g2); g2.setShapeFilled(!g2.isShapeFilled()); assertEquals(g1, g2); // fillPaint g1.setFillPaint(Color.GREEN); assertNotEquals(g1, g2); g2.setFillPaint(Color.GREEN); assertEquals(g1, g2); // shapeOutlineVisible g1.setShapeOutlineVisible(!g1.isShapeOutlineVisible()); assertNotEquals(g1, g2); g2.setShapeOutlineVisible(!g2.isShapeOutlineVisible()); assertEquals(g1, g2); // outlinePaint g1.setOutlinePaint(Color.GREEN); assertNotEquals(g1, g2); g2.setOutlinePaint(Color.GREEN); assertEquals(g1, g2); // outlineStroke g1.setOutlineStroke(new BasicStroke(1.23f)); assertNotEquals(g1, g2); g2.setOutlineStroke(new BasicStroke(1.23f)); assertEquals(g1, g2); // shapeAnchor g1.setShapeAnchor(RectangleAnchor.BOTTOM_RIGHT); assertNotEquals(g1, g2); g2.setShapeAnchor(RectangleAnchor.BOTTOM_RIGHT); assertEquals(g1, g2); // shapeLocation g1.setShapeLocation(RectangleAnchor.BOTTOM_RIGHT); assertNotEquals(g1, g2); g2.setShapeLocation(RectangleAnchor.BOTTOM_RIGHT); assertEquals(g1, g2); // lineVisible g1.setLineVisible(!g1.isLineVisible()); assertNotEquals(g1, g2); g2.setLineVisible(!g2.isLineVisible()); assertEquals(g1, g2); // line g1.setLine(new Line2D.Double(1.0, 2.0, 3.0, 4.0)); assertNotEquals(g1, g2); g2.setLine(new Line2D.Double(1.0, 2.0, 3.0, 4.0)); assertEquals(g1, g2); // linePaint g1.setLinePaint(Color.GREEN); assertNotEquals(g1, g2); g2.setLinePaint(Color.GREEN); assertEquals(g1, g2); // lineStroke g1.setLineStroke(new BasicStroke(1.23f)); assertNotEquals(g1, g2); g2.setLineStroke(new BasicStroke(1.23f)); assertEquals(g1, g2); // fillPaintTransformer g1.setFillPaintTransformer(new StandardGradientPaintTransformer( GradientPaintTransformType.CENTER_HORIZONTAL)); assertNotEquals(g1, g2); g2.setFillPaintTransformer(new StandardGradientPaintTransformer( GradientPaintTransformType.CENTER_HORIZONTAL)); assertEquals(g1, g2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { LegendGraphic g1 = new LegendGraphic(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), Color.BLACK); LegendGraphic g2 = new LegendGraphic(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), Color.BLACK); assertEquals(g1, g2); int h1 = g1.hashCode(); int h2 = g2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { Rectangle r = new Rectangle(1, 2, 3, 4); LegendGraphic g1 = new LegendGraphic(r, Color.BLACK); LegendGraphic g2 = (LegendGraphic) g1.clone(); assertNotSame(g1, g2); assertSame(g1.getClass(), g2.getClass()); assertEquals(g1, g2); // check independence r.setBounds(4, 3, 2, 1); assertNotEquals(g1, g2); } /** * A test for cloning - checks that the line shape is cloned correctly. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning2() throws CloneNotSupportedException { Rectangle r = new Rectangle(1, 2, 3, 4); LegendGraphic g1 = new LegendGraphic(r, Color.BLACK); Line2D l = new Line2D.Double(1.0, 2.0, 3.0, 4.0); g1.setLine(l); LegendGraphic g2 = (LegendGraphic) g1.clone(); assertNotSame(g1, g2); assertSame(g1.getClass(), g2.getClass()); assertEquals(g1, g2); // check independence l.setLine(4.0, 3.0, 2.0, 1.0); assertNotEquals(g1, g2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { Stroke s = new BasicStroke(1.23f); LegendGraphic g1 = new LegendGraphic(new Rectangle2D.Double(1.0, 2.0, 3.0, 4.0), Color.BLACK); g1.setOutlineStroke(s); LegendGraphic g2 = TestUtils.serialised(g1); assertEquals(g1, g2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/title/LegendTitleTest.java000066400000000000000000000137011463604235500301370ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * LegendTitleTest.java * -------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.title; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.ui.RectangleAnchor; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.util.SortOrder; import org.junit.jupiter.api.Test; import javax.swing.event.EventListenerList; import java.awt.*; import java.awt.geom.Rectangle2D; import static org.junit.jupiter.api.Assertions.*; /** * Some tests for the {@link LegendTitle} class. */ public class LegendTitleTest { /** * Use EqualsVerifier to test that the contract between equals and hashCode * is properly implemented. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(LegendTitle.class) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .withRedefinedSuperclass() .withPrefabValues(EventListenerList.class, new EventListenerList(), new EventListenerList()) .withPrefabValues(Rectangle2D.class, TestUtils.createR2D(true), TestUtils.createR2D(false)) .withPrefabValues(Font.class, TestUtils.createFont(true), TestUtils.createFont(false)) .verify(); } /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { XYPlot plot1 = new XYPlot(); LegendTitle t1 = new LegendTitle(plot1); LegendTitle t2 = new LegendTitle(plot1); assertEquals(t1, t2); t1.setBackgroundPaint( new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW) ); assertNotEquals(t1, t2); t2.setBackgroundPaint( new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW) ); assertEquals(t1, t2); t1.setLegendItemGraphicEdge(RectangleEdge.BOTTOM); assertNotEquals(t1, t2); t2.setLegendItemGraphicEdge(RectangleEdge.BOTTOM); assertEquals(t1, t2); t1.setLegendItemGraphicAnchor(RectangleAnchor.BOTTOM_LEFT); assertNotEquals(t1, t2); t2.setLegendItemGraphicAnchor(RectangleAnchor.BOTTOM_LEFT); assertEquals(t1, t2); t1.setLegendItemGraphicLocation(RectangleAnchor.TOP_LEFT); assertNotEquals(t1, t2); t2.setLegendItemGraphicLocation(RectangleAnchor.TOP_LEFT); assertEquals(t1, t2); t1.setItemFont(new Font("Dialog", Font.PLAIN, 19)); assertNotEquals(t1, t2); t2.setItemFont(new Font("Dialog", Font.PLAIN, 19)); assertEquals(t1, t2); t1.setSortOrder(SortOrder.DESCENDING); assertNotEquals(t1, t2); t2.setSortOrder(SortOrder.DESCENDING); assertEquals(t1, t2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { XYPlot plot1 = new XYPlot(); LegendTitle t1 = new LegendTitle(plot1); LegendTitle t2 = new LegendTitle(plot1); assertEquals(t1, t2); int h1 = t1.hashCode(); int h2 = t2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { XYPlot plot = new XYPlot(); Rectangle2D bounds1 = new Rectangle2D.Double(10.0, 20.0, 30.0, 40.0); LegendTitle t1 = new LegendTitle(plot); t1.setBackgroundPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.YELLOW)); t1.setBounds(bounds1); LegendTitle t2 = (LegendTitle) t1.clone(); assertNotSame(t1, t2); assertSame(t1.getClass(), t2.getClass()); assertEquals(t1, t2); // check independence bounds1.setFrame(40.0, 30.0, 20.0, 10.0); assertNotEquals(t1, t2); t2.setBounds(new Rectangle2D.Double(40.0, 30.0, 20.0, 10.0)); assertEquals(t1, t2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XYPlot plot = new XYPlot(); LegendTitle t1 = new LegendTitle(plot); LegendTitle t2 = TestUtils.serialised(t1); assertEquals(t1, t2); assertEquals(t2.getSources()[0], plot); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/title/PaintScaleLegendTest.java000066400000000000000000000161031463604235500311000ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * PaintScaleLegendTest.java * ------------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.title; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.GradientPaint; import java.awt.geom.Rectangle2D; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.chart.axis.AxisLocation; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.plot.Plot; import org.jfree.chart.renderer.GrayPaintScale; import org.jfree.chart.renderer.LookupPaintScale; import org.junit.jupiter.api.Test; import javax.swing.event.EventListenerList; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link PaintScaleLegend} class. */ public class PaintScaleLegendTest { @Test public void testEqualsHashCode() { EqualsVerifier.forClass(PaintScaleLegend.class) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .withRedefinedSuperclass() .withPrefabValues(EventListenerList.class, new EventListenerList(), new EventListenerList()) .withPrefabValues(Rectangle2D.class, TestUtils.createR2D(true), TestUtils.createR2D(false)) .withPrefabValues(Font.class, TestUtils.createFont(true), TestUtils.createFont(false)) .withPrefabValues(Plot.class, TestUtils.createPlot(true), TestUtils.createPlot(false)) .withPrefabValues(ValueAxis.class, TestUtils.createValueAxis(true), TestUtils.createValueAxis(false)) .verify(); } /** * Test that the equals() method distinguishes all fields. */ @Test public void testEquals() { // default instances PaintScaleLegend l1 = new PaintScaleLegend(new GrayPaintScale(), new NumberAxis("X")); PaintScaleLegend l2 = new PaintScaleLegend(new GrayPaintScale(), new NumberAxis("X")); assertEquals(l1, l2); assertEquals(l2, l1); // paintScale l1.setScale(new LookupPaintScale()); assertNotEquals(l1, l2); l2.setScale(new LookupPaintScale()); assertEquals(l1, l2); // axis l1.setAxis(new NumberAxis("Axis 2")); assertNotEquals(l1, l2); l2.setAxis(new NumberAxis("Axis 2")); assertEquals(l1, l2); // axisLocation l1.setAxisLocation(AxisLocation.BOTTOM_OR_RIGHT); assertNotEquals(l1, l2); l2.setAxisLocation(AxisLocation.BOTTOM_OR_RIGHT); assertEquals(l1, l2); // axisOffset l1.setAxisOffset(99.0); assertNotEquals(l1, l2); l2.setAxisOffset(99.0); assertEquals(l1, l2); // stripWidth l1.setStripWidth(99.0); assertNotEquals(l1, l2); l2.setStripWidth(99.0); assertEquals(l1, l2); // stripOutlineVisible l1.setStripOutlineVisible(!l1.isStripOutlineVisible()); assertNotEquals(l1, l2); l2.setStripOutlineVisible(l1.isStripOutlineVisible()); assertEquals(l1, l2); // stripOutlinePaint l1.setStripOutlinePaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); assertNotEquals(l1, l2); l2.setStripOutlinePaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); assertEquals(l1, l2); // stripOutlineStroke l1.setStripOutlineStroke(new BasicStroke(1.1f)); assertNotEquals(l1, l2); l2.setStripOutlineStroke(new BasicStroke(1.1f)); assertEquals(l1, l2); // backgroundPaint l1.setBackgroundPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); assertNotEquals(l1, l2); l2.setBackgroundPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); assertEquals(l1, l2); l1.setSubdivisionCount(99); assertNotEquals(l1, l2); l2.setSubdivisionCount(99); assertEquals(l1, l2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { PaintScaleLegend l1 = new PaintScaleLegend(new GrayPaintScale(), new NumberAxis("X")); PaintScaleLegend l2 = new PaintScaleLegend(new GrayPaintScale(), new NumberAxis("X")); assertEquals(l1, l2); int h1 = l1.hashCode(); int h2 = l2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException if there is a cloning issue. */ @Test public void testCloning() throws CloneNotSupportedException { PaintScaleLegend l1 = new PaintScaleLegend(new GrayPaintScale(), new NumberAxis("X")); PaintScaleLegend l2 = (PaintScaleLegend) l1.clone(); assertNotSame(l1, l2); assertSame(l1.getClass(), l2.getClass()); assertEquals(l1, l2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { PaintScaleLegend l1 = new PaintScaleLegend(new GrayPaintScale(), new NumberAxis("X")); PaintScaleLegend l2 = TestUtils.serialised(l1); assertEquals(l1, l2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/title/ShortTextTitleTest.java000066400000000000000000000061541463604235500307110ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * ShortTextTitleTest.java * ----------------------- * (C) Copyright 2008-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.title; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link ShortTextTitle} class. */ public class ShortTextTitleTest { /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { ShortTextTitle t1 = new ShortTextTitle("ABC"); ShortTextTitle t2 = new ShortTextTitle("ABC"); assertEquals(t1, t2); t1.setText("Test 1"); assertNotEquals(t1, t2); t2.setText("Test 1"); assertEquals(t1, t2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { ShortTextTitle t1 = new ShortTextTitle("ABC"); ShortTextTitle t2 = new ShortTextTitle("ABC"); assertEquals(t1, t2); int h1 = t1.hashCode(); int h2 = t2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { ShortTextTitle t1 = new ShortTextTitle("ABC"); ShortTextTitle t2 = (ShortTextTitle) t1.clone(); assertNotSame(t1, t2); assertSame(t1.getClass(), t2.getClass()); assertEquals(t1, t2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { ShortTextTitle t1 = new ShortTextTitle("ABC"); ShortTextTitle t2 = TestUtils.serialised(t1); assertEquals(t1, t2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/title/TextTitleTest.java000066400000000000000000000135061463604235500276700ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * TextTitleTest.java * ------------------ * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.title; import java.awt.Color; import java.awt.Font; import java.awt.GradientPaint; import java.awt.geom.Rectangle2D; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.chart.ui.HorizontalAlignment; import org.jfree.chart.util.PaintUtils; import org.junit.jupiter.api.Test; import javax.swing.event.EventListenerList; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link TextTitle} class. */ public class TextTitleTest { @Test public void testEqualsHashCode() { EqualsVerifier.forClass(TextTitle.class) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .withRedefinedSuperclass() .withPrefabValues(EventListenerList.class, new EventListenerList(), new EventListenerList()) .withPrefabValues(Rectangle2D.class, TestUtils.createR2D(true), TestUtils.createR2D(false)) .withPrefabValues(Font.class, TestUtils.createFont(true), TestUtils.createFont(false)) .verify(); } /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { TextTitle t1 = new TextTitle(); TextTitle t2 = new TextTitle(); assertEquals(t1, t2); t1.setText("Test 1"); assertNotEquals(t1, t2); t2.setText("Test 1"); assertEquals(t1, t2); Font f = new Font("SansSerif", Font.PLAIN, 15); t1.setFont(f); assertNotEquals(t1, t2); t2.setFont(f); assertEquals(t1, t2); t1.setTextAlignment(HorizontalAlignment.RIGHT); assertNotEquals(t1, t2); t2.setTextAlignment(HorizontalAlignment.RIGHT); assertEquals(t1, t2); // paint t1.setPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); assertFalse(PaintUtils.equal(t1.getPaint(), t2.getPaint())); t2.setPaint(new GradientPaint(1.0f, 2.0f, Color.RED, 3.0f, 4.0f, Color.BLUE)); assertTrue(PaintUtils.equal(t1.getPaint(), t2.getPaint())); // backgroundPaint t1.setBackgroundPaint(new GradientPaint(4.0f, 3.0f, Color.RED, 2.0f, 1.0f, Color.BLUE)); assertFalse(PaintUtils.equal(t1.getBackgroundPaint(), t2.getBackgroundPaint())); t2.setBackgroundPaint(new GradientPaint(4.0f, 3.0f, Color.RED, 2.0f, 1.0f, Color.BLUE)); assertTrue(PaintUtils.equal(t1.getBackgroundPaint(), t2.getBackgroundPaint())); // maximumLinesToDisplay t1.setMaximumLinesToDisplay(3); assertNotEquals(t1, t2); t2.setMaximumLinesToDisplay(3); assertEquals(t1, t2); // toolTipText t1.setToolTipText("TTT"); assertNotEquals(t1, t2); t2.setToolTipText("TTT"); assertEquals(t1, t2); // urlText t1.setURLText(("URL")); assertNotEquals(t1, t2); t2.setURLText(("URL")); assertEquals(t1, t2); // expandToFitSpace t1.setExpandToFitSpace(!t1.getExpandToFitSpace()); assertNotEquals(t1, t2); t2.setExpandToFitSpace(!t2.getExpandToFitSpace()); assertEquals(t1, t2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { TextTitle t1 = new TextTitle(); TextTitle t2 = new TextTitle(); assertEquals(t1, t2); int h1 = t1.hashCode(); int h2 = t2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { TextTitle t1 = new TextTitle(); TextTitle t2 = (TextTitle) t1.clone(); assertNotSame(t1, t2); assertSame(t1.getClass(), t2.getClass()); assertEquals(t1, t2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { TextTitle t1 = new TextTitle("Test"); TextTitle t2 = TestUtils.serialised(t1); assertEquals(t1, t2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/title/TitleTest.java000066400000000000000000000101351463604235500270160ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * TitleTest.java * -------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.chart.title; import java.awt.geom.Rectangle2D; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.chart.ui.HorizontalAlignment; import org.jfree.chart.ui.RectangleEdge; import org.jfree.chart.ui.VerticalAlignment; import org.junit.jupiter.api.Test; import javax.swing.event.EventListenerList; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the abstract {@link Title} class. */ public class TitleTest { @Test public void testEqualsHashCode() { EqualsVerifier.forClass(Title.class) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .withRedefinedSuperclass() .withRedefinedSubclass(CompositeTitle.class) .withRedefinedSubclass(DateTitle.class) .withRedefinedSubclass(ShortTextTitle.class) .withRedefinedSubclass(TextTitle.class) .withRedefinedSubclass(LegendTitle.class) .withRedefinedSubclass(PaintScaleLegend.class) .withPrefabValues(EventListenerList.class, new EventListenerList(), new EventListenerList()) .withPrefabValues(Rectangle2D.class, TestUtils.createR2D(true), TestUtils.createR2D(false)) .verify(); } /** * Some checks for the equals() method. */ @Test public void testEquals() { // use the TextTitle class because it is a concrete subclass Title t1 = new TextTitle(); Title t2 = new TextTitle(); assertEquals(t1, t2); t1.setPosition(RectangleEdge.LEFT); assertNotEquals(t1, t2); t2.setPosition(RectangleEdge.LEFT); assertEquals(t1, t2); t1.setHorizontalAlignment(HorizontalAlignment.RIGHT); assertNotEquals(t1, t2); t2.setHorizontalAlignment(HorizontalAlignment.RIGHT); assertEquals(t1, t2); t1.setVerticalAlignment(VerticalAlignment.BOTTOM); assertNotEquals(t1, t2); t2.setVerticalAlignment(VerticalAlignment.BOTTOM); assertEquals(t1, t2); t1.setVisible(false); assertNotEquals(t1, t2); t2.setVisible(false); assertEquals(t1, t2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { TextTitle t1 = new TextTitle(); TextTitle t2 = new TextTitle(); assertEquals(t1, t2); int h1 = t1.hashCode(); int h2 = t2.hashCode(); assertEquals(h1, h2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/urls/000077500000000000000000000000001463604235500240765ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/urls/CustomCategoryURLGeneratorTest.java000066400000000000000000000122011463604235500327770ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------------- * CustomCategoryURLGeneratorTest.java * ----------------------------------- * (C) Copyright 2008-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.urls; import java.util.ArrayList; import java.util.List; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link CustomCategoryURLGenerator} class. */ public class CustomCategoryURLGeneratorTest { /** * Some checks for the equals() method. */ @Test public void testEquals() { CustomCategoryURLGenerator g1 = new CustomCategoryURLGenerator(); CustomCategoryURLGenerator g2 = new CustomCategoryURLGenerator(); assertEquals(g1, g2); List u1 = new ArrayList<>(); u1.add("URL A1"); u1.add("URL A2"); u1.add("URL A3"); g1.addURLSeries(u1); assertNotEquals(g1, g2); List u2 = new ArrayList<>(); u2.add("URL A1"); u2.add("URL A2"); u2.add("URL A3"); g2.addURLSeries(u2); assertEquals(g1, g2); } /** * Confirm that cloning works. * * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { CustomCategoryURLGenerator g1 = new CustomCategoryURLGenerator(); List u1 = new ArrayList<>(); u1.add("URL A1"); u1.add("URL A2"); u1.add("URL A3"); g1.addURLSeries(u1); CustomCategoryURLGenerator g2 = (CustomCategoryURLGenerator) g1.clone(); assertNotSame(g1, g2); assertSame(g1.getClass(), g2.getClass()); assertEquals(g1, g2); // check independence List u2 = new ArrayList<>(); u2.add("URL XXX"); g1.addURLSeries(u2); assertNotEquals(g1, g2); g2.addURLSeries(new ArrayList<>(u2)); assertEquals(g1, g2); } /** * Checks that the class implements PublicCloneable. */ @Test public void testPublicCloneable() { CustomCategoryURLGenerator g1 = new CustomCategoryURLGenerator(); assertTrue(g1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { List u1 = new ArrayList<>(); u1.add("URL A1"); u1.add("URL A2"); u1.add("URL A3"); List u2 = new ArrayList<>(); u2.add("URL B1"); u2.add("URL B2"); u2.add("URL B3"); CustomCategoryURLGenerator g1 = new CustomCategoryURLGenerator(); g1.addURLSeries(u1); g1.addURLSeries(u2); CustomCategoryURLGenerator g2 = TestUtils.serialised(g1); assertEquals(g1, g2); } /** * Some checks for the addURLSeries() method. */ @Test public void testAddURLSeries() { CustomCategoryURLGenerator g1 = new CustomCategoryURLGenerator(); // you can add a null list - it would have been better if this // required EMPTY_LIST g1.addURLSeries(null); assertEquals(1, g1.getListCount()); assertEquals(0, g1.getURLCount(0)); List list1 = new ArrayList<>(); list1.add("URL1"); g1.addURLSeries(list1); assertEquals(2, g1.getListCount()); assertEquals(0, g1.getURLCount(0)); assertEquals(1, g1.getURLCount(1)); assertEquals("URL1", g1.getURL(1, 0)); // if we modify the original list, it's best if the URL generator is // not affected list1.clear(); assertEquals("URL1", g1.getURL(1, 0)); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/urls/CustomPieURLGeneratorTest.java000066400000000000000000000072101463604235500317430ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------ * CustomPieURLGeneratorTest.java * ------------------------------ * (C) Copyright 2008-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.urls; import java.util.HashMap; import java.util.Map; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link CustomPieURLGenerator} class. */ public class CustomPieURLGeneratorTest { /** * Some checks for the equals() method. */ @Test public void testEquals() { CustomPieURLGenerator g1 = new CustomPieURLGenerator(); CustomPieURLGenerator g2 = new CustomPieURLGenerator(); assertEquals(g1, g2); Map m1 = new HashMap<>(); m1.put("A", "http://www.jfree.org/"); g1.addURLs(m1); assertNotEquals(g1, g2); g2.addURLs(m1); assertEquals(g1, g2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { CustomPieURLGenerator g1 = new CustomPieURLGenerator(); Map m1 = new HashMap<>(); m1.put("A", "http://www.jfree.org/"); g1.addURLs(m1); CustomPieURLGenerator g2 = (CustomPieURLGenerator) g1.clone(); assertNotSame(g1, g2); assertSame(g1.getClass(), g2.getClass()); assertEquals(g1, g2); // check independence Map m2 = new HashMap<>(); m2.put("B", "XYZ"); g1.addURLs(m2); assertNotEquals(g1, g2); } /** * Checks that the class implements PublicCloneable. */ @Test public void testPublicCloneable() { CustomPieURLGenerator g1 = new CustomPieURLGenerator(); assertTrue(g1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { CustomPieURLGenerator g1 = new CustomPieURLGenerator(); Map m1 = new HashMap<>(); m1.put("A", "http://www.jfree.org/"); g1.addURLs(m1); CustomPieURLGenerator g2 = TestUtils.serialised(g1); assertEquals(g1, g2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/urls/CustomXYURLGeneratorTest.java000066400000000000000000000115361463604235500315740ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------- * CustomXYURLGeneratorTest.java * ----------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.urls; import java.util.ArrayList; import java.util.List; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link CustomXYURLGenerator} class. */ public class CustomXYURLGeneratorTest { /** * Some checks for the equals() method. */ @Test public void testEquals() { CustomXYURLGenerator g1 = new CustomXYURLGenerator(); CustomXYURLGenerator g2 = new CustomXYURLGenerator(); assertEquals(g1, g2); List u1 = new ArrayList<>(); u1.add("URL A1"); u1.add("URL A2"); u1.add("URL A3"); g1.addURLSeries(u1); assertNotEquals(g1, g2); List u2 = new ArrayList<>(); u2.add("URL A1"); u2.add("URL A2"); u2.add("URL A3"); g2.addURLSeries(u2); assertEquals(g1, g2); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { CustomXYURLGenerator g1 = new CustomXYURLGenerator(); List u1 = new ArrayList<>(); u1.add("URL A1"); u1.add("URL A2"); u1.add("URL A3"); g1.addURLSeries(u1); CustomXYURLGenerator g2 = (CustomXYURLGenerator) g1.clone(); assertNotSame(g1, g2); assertSame(g1.getClass(), g2.getClass()); assertEquals(g1, g2); // check independence List u2 = new ArrayList<>(); u2.add("URL XXX"); g1.addURLSeries(u2); assertNotEquals(g1, g2); g2.addURLSeries(new ArrayList<>(u2)); assertEquals(g1, g2); } /** * Checks that the class implements PublicCloneable. */ @Test public void testPublicCloneable() { CustomXYURLGenerator g1 = new CustomXYURLGenerator(); assertTrue(g1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { List u1 = new ArrayList<>(); u1.add("URL A1"); u1.add("URL A2"); u1.add("URL A3"); List u2 = new ArrayList<>(); u2.add("URL B1"); u2.add("URL B2"); u2.add("URL B3"); CustomXYURLGenerator g1 = new CustomXYURLGenerator(); g1.addURLSeries(u1); g1.addURLSeries(u2); CustomXYURLGenerator g2 = TestUtils.serialised(g1); assertEquals(g1, g2); } /** * Some checks for the addURLSeries() method. */ @Test public void testAddURLSeries() { CustomXYURLGenerator g1 = new CustomXYURLGenerator(); // you can add a null list - it would have been better if this // required EMPTY_LIST g1.addURLSeries(null); assertEquals(1, g1.getListCount()); assertEquals(0, g1.getURLCount(0)); List list1 = new ArrayList<>(); list1.add("URL1"); g1.addURLSeries(list1); assertEquals(2, g1.getListCount()); assertEquals(0, g1.getURLCount(0)); assertEquals(1, g1.getURLCount(1)); assertEquals("URL1", g1.getURL(1, 0)); // if we modify the original list, it's best if the URL generator is // not affected list1.clear(); assertEquals("URL1", g1.getURL(1, 0)); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/urls/StandardCategoryURLGeneratorTest.java000066400000000000000000000102501463604235500332670ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------------- * StandardCategoryURLGeneratorTest.java * ------------------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.urls; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.category.DefaultCategoryDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link StandardCategoryURLGenerator} class. */ public class StandardCategoryURLGeneratorTest { /** * Some tests for the generateURL() method. */ @Test public void testGenerateURL() { StandardCategoryURLGenerator g1 = new StandardCategoryURLGenerator(); DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.addValue(1.0, "R1", "C1"); dataset.addValue(2.0, "R2", "C2"); dataset.addValue(3.0, "R&", "C&"); assertEquals("index.html?series=R1&category=C1", g1.generateURL(dataset, 0, 0)); assertEquals("index.html?series=R1&category=C2", g1.generateURL(dataset, 0, 1)); assertEquals("index.html?series=R2&category=C2", g1.generateURL(dataset, 1, 1)); assertEquals("index.html?series=R%26&category=C%26", g1.generateURL(dataset, 2, 2)); } /** * Checks that the class does not implement PublicCloneable (the generator * is immutable, so cloning is not necessary). */ @Test public void testPublicCloneable() { StandardCategoryURLGenerator g1 = new StandardCategoryURLGenerator(); assertFalse(g1 instanceof PublicCloneable); } /** * Some tests for the equals() method. */ @Test public void testEquals() { StandardCategoryURLGenerator g1 = new StandardCategoryURLGenerator(); StandardCategoryURLGenerator g2 = new StandardCategoryURLGenerator(); assertEquals(g1, g2); g1 = new StandardCategoryURLGenerator("index2.html?"); assertNotEquals(g1, g2); g2 = new StandardCategoryURLGenerator("index2.html?"); assertEquals(g1, g2); g1 = new StandardCategoryURLGenerator("index2.html?", "A", "B"); assertNotEquals(g1, g2); g2 = new StandardCategoryURLGenerator("index2.html?", "A", "B"); assertEquals(g1, g2); g1 = new StandardCategoryURLGenerator("index2.html?", "A", "C"); assertNotEquals(g1, g2); g2 = new StandardCategoryURLGenerator("index2.html?", "A", "C"); assertEquals(g1, g2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { StandardCategoryURLGenerator g1 = new StandardCategoryURLGenerator( "index.html?"); StandardCategoryURLGenerator g2 = TestUtils.serialised(g1); assertEquals(g1, g2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/urls/StandardPieURLGeneratorTest.java000066400000000000000000000105321463604235500322320ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------------- * StandardPieURLGeneratorTest.java * -------------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.urls; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.general.DefaultPieDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link StandardPieURLGenerator} class. */ public class StandardPieURLGeneratorTest { /** * Some checks for the equals() method. */ @Test public void testEquals() { StandardPieURLGenerator g1 = new StandardPieURLGenerator(); StandardPieURLGenerator g2 = new StandardPieURLGenerator(); assertEquals(g1, g2); g1 = new StandardPieURLGenerator("prefix", "category", "index"); assertNotEquals(g1, g2); g2 = new StandardPieURLGenerator("prefix", "category", "index"); assertEquals(g1, g2); g1 = new StandardPieURLGenerator("prefix2", "category", "index"); assertNotEquals(g1, g2); g2 = new StandardPieURLGenerator("prefix2", "category", "index"); assertEquals(g1, g2); g1 = new StandardPieURLGenerator("prefix2", "category2", "index"); assertNotEquals(g1, g2); g2 = new StandardPieURLGenerator("prefix2", "category2", "index"); assertEquals(g1, g2); g1 = new StandardPieURLGenerator("prefix2", "category2", "index2"); assertNotEquals(g1, g2); g2 = new StandardPieURLGenerator("prefix2", "category2", "index2"); assertEquals(g1, g2); g1 = new StandardPieURLGenerator("prefix2", "category2", null); assertNotEquals(g1, g2); g2 = new StandardPieURLGenerator("prefix2", "category2", null); assertEquals(g1, g2); } /** * Checks that the class does not implement PublicCloneable (the generator * is immutable). */ @Test public void testPublicCloneable() { StandardPieURLGenerator g1 = new StandardPieURLGenerator( "index.html?", "cat"); assertFalse(g1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { StandardPieURLGenerator g1 = new StandardPieURLGenerator( "index.html?", "cat"); StandardPieURLGenerator g2 = TestUtils.serialised(g1); assertEquals(g1, g2); } /** * Test that the generated URL is as expected. */ @Test public void testURL() { DefaultPieDataset dataset = new DefaultPieDataset(); dataset.setValue("Alpha '1'", 5.0); dataset.setValue("Beta", 5.5); StandardPieURLGenerator g1 = new StandardPieURLGenerator( "chart.jsp", "category"); String url = g1.generateURL(dataset, "Beta", 0); assertEquals("chart.jsp?category=Beta&pieIndex=0", url); url = g1.generateURL(dataset, "Alpha '1'", 0); assertEquals("chart.jsp?category=Alpha+%271%27&pieIndex=0", url); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/urls/StandardXYURLGeneratorTest.java000066400000000000000000000046211463604235500320570ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------- * StandardXYURLGeneratorTest.java * ------------------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.urls; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; /** * Tests for the {@link StandardXYURLGenerator} class. */ public class StandardXYURLGeneratorTest { /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { StandardXYURLGenerator g1 = new StandardXYURLGenerator("index.html?"); StandardXYURLGenerator g2 = TestUtils.serialised(g1); assertEquals(g1, g2); } /** * Checks that the class does not implement PublicCloneable (the generator * is immutable). */ @Test public void testPublicCloneable() { StandardXYURLGenerator g1 = new StandardXYURLGenerator("index.html?"); assertFalse(g1 instanceof PublicCloneable); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/urls/TimeSeriesURLGeneratorTest.java000066400000000000000000000110641463604235500321060ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------- * TimeSeriesURLGeneratorTest.java * ------------------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.urls; import java.text.SimpleDateFormat; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.xy.DefaultXYDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link TimeSeriesURLGenerator} class. */ public class TimeSeriesURLGeneratorTest { /** * A basic check for the generateURL() method. */ @Test public void testGenerateURL() { TimeSeriesURLGenerator g = new TimeSeriesURLGenerator(); DefaultXYDataset dataset = new DefaultXYDataset(); dataset.addSeries("Series '1'", new double[][] {{1.0, 2.0}, {3.0, 4.0}}); String s = g.generateURL(dataset, 0, 0); assertTrue(s.startsWith("index.html?series=Series+%271%27&item=")); } /** * Check that the equals() method can distinguish all fields. */ @Test public void testEquals() { TimeSeriesURLGenerator g1 = new TimeSeriesURLGenerator(); TimeSeriesURLGenerator g2 = new TimeSeriesURLGenerator(); assertEquals(g1, g2); g1 = new TimeSeriesURLGenerator(new SimpleDateFormat("yyyy"), "prefix", "series", "item"); assertNotEquals(g1, g2); g2 = new TimeSeriesURLGenerator(new SimpleDateFormat("yyyy"), "prefix", "series", "item"); assertEquals(g1, g2); g1 = new TimeSeriesURLGenerator(new SimpleDateFormat("yy"), "prefix", "series", "item"); assertNotEquals(g1, g2); g2 = new TimeSeriesURLGenerator(new SimpleDateFormat("yy"), "prefix", "series", "item"); assertEquals(g1, g2); g1 = new TimeSeriesURLGenerator(new SimpleDateFormat("yy"), "prefix1", "series", "item"); assertNotEquals(g1, g2); g2 = new TimeSeriesURLGenerator(new SimpleDateFormat("yy"), "prefix1", "series", "item"); assertEquals(g1, g2); g1 = new TimeSeriesURLGenerator(new SimpleDateFormat("yy"), "prefix1", "series1", "item"); assertNotEquals(g1, g2); g2 = new TimeSeriesURLGenerator(new SimpleDateFormat("yy"), "prefix1", "series1", "item"); assertEquals(g1, g2); g1 = new TimeSeriesURLGenerator(new SimpleDateFormat("yy"), "prefix1", "series1", "item1"); assertNotEquals(g1, g2); g2 = new TimeSeriesURLGenerator(new SimpleDateFormat("yy"), "prefix1", "series1", "item1"); assertEquals(g1, g2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { TimeSeriesURLGenerator g1 = new TimeSeriesURLGenerator(); TimeSeriesURLGenerator g2 = TestUtils.serialised(g1); assertEquals(g1, g2); } /** * Checks that the class does not implement PublicCloneable (the generator * is immutable). */ @Test public void testPublicCloneable() { TimeSeriesURLGenerator g1 = new TimeSeriesURLGenerator(); assertFalse(g1 instanceof PublicCloneable); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/urls/package.html000066400000000000000000000001631463604235500263570ustar00rootroot00000000000000 JUnit tests. jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/util/000077500000000000000000000000001463604235500240665ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/util/HMSNumberFormatTest.java000066400000000000000000000040771463604235500305520ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * HMSNumberFormatTest.java * ------------------------ * (C) Copyright 2021-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.util; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; /** * Tests for the {@link HMSNumberFormat} class. */ public class HMSNumberFormatTest { @Test public void testGeneral() { HMSNumberFormat formatter = new HMSNumberFormat(); assertEquals("00:00:00", formatter.format(0)); assertEquals("00:00:59", formatter.format(59)); assertEquals("00:01:01", formatter.format(61)); assertEquals("00:59:59", formatter.format(3599)); assertEquals("01:00:00", formatter.format(3600)); assertEquals("01:00:01", formatter.format(3601)); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/util/LineUtilsTest.java000066400000000000000000000126371463604235500275120ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * LineUtilsTest.java * ------------------ * (C) Copyright 2008-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.util; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertFalse; /** * Tests for the {@link LineUtils} class. */ public class LineUtilsTest { private boolean lineEquals(Line2D line, double x1, double y1, double x2, double y2) { boolean result = true; double epsilon = 0.0000000001; if (Math.abs(line.getX1() - x1) > epsilon) { result = false; } if (Math.abs(line.getY1() - y1) > epsilon) { result = false; } if (Math.abs(line.getX2() - x2) > epsilon) { result = false; } if (Math.abs(line.getY2() - y2) > epsilon) { result = false; } return result; } @Test public void testClipLine() { Rectangle2D rect = new Rectangle2D.Double(1.0, 1.0, 1.0, 1.0); Line2D line = new Line2D.Double(); assertFalse(LineUtils.clipLine(line, rect)); assertTrue(lineEquals(line, 0.0, 0.0, 0.0, 0.0)); line.setLine(0.5, 0.5, 0.6, 0.6); assertFalse(LineUtils.clipLine(line, rect)); assertTrue(lineEquals(line, 0.5, 0.5, 0.6, 0.6)); line.setLine(0.5, 0.5, 1.6, 0.6); assertFalse(LineUtils.clipLine(line, rect)); assertTrue(lineEquals(line, 0.5, 0.5, 1.6, 0.6)); line.setLine(0.5, 0.5, 2.6, 0.6); assertFalse(LineUtils.clipLine(line, rect)); assertTrue(lineEquals(line, 0.5, 0.5, 2.6, 0.6)); line.setLine(0.5, 0.5, 0.6, 1.6); assertFalse(LineUtils.clipLine(line, rect)); assertTrue(lineEquals(line, 0.5, 0.5, 0.6, 1.6)); line.setLine(0.5, 0.5, 1.6, 1.6); assertTrue(LineUtils.clipLine(line, rect)); assertTrue(lineEquals(line, 1.0, 1.0, 1.6, 1.6)); line.setLine(0.5, 0.5, 2.6, 1.6); assertTrue(LineUtils.clipLine(line, rect)); assertTrue(lineEquals(line, 1.4545454545454546, 1.0, 2.0, 1.2857142857142858)); line.setLine(0.5, 0.5, 0.5, 2.6); assertFalse(LineUtils.clipLine(line, rect)); assertTrue(lineEquals(line, 0.5, 0.5, 0.5, 2.6)); line.setLine(0.5, 0.5, 1.5, 2.6); assertTrue(LineUtils.clipLine(line, rect)); assertTrue(lineEquals(line, 1.0, 1.55, 1.2142857142857142, 2.0)); line.setLine(0.5, 0.5, 2.5, 2.6); assertTrue(LineUtils.clipLine(line, rect)); assertTrue(lineEquals(line, 1.0, 1.025, 1.9285714285714284, 2.0)); line.setLine(0.5, 0.5, 1.5, 1.5); assertTrue(LineUtils.clipLine(line, rect)); assertTrue(lineEquals(line, 1.0, 1.0, 1.5, 1.5)); line.setLine(2.5, 1.0, 1.5, 1.5); assertTrue(LineUtils.clipLine(line, rect)); assertTrue(lineEquals(line, 2.0, 1.25, 1.5, 1.5)); line.setLine(1.5, 1.5, 2.5, 1.0); assertTrue(LineUtils.clipLine(line, rect)); assertTrue(lineEquals(line, 1.5, 1.5, 2.0, 1.25)); } /** * Tests that a line with Double.NaN coordinates is handled gracefully in * the clipLine() method (added in response to bug#223). */ @Test public void testClipLineWithNaN() { Rectangle2D rect = new Rectangle2D.Double(1.0, 1.0, 1.0, 1.0); Line2D line = new Line2D.Double(Double.NaN, 2, 3, 4); assertFalse(LineUtils.clipLine(line, rect)); assertTrue(lineEquals(line, Double.NaN, 2, 3, 4)); line = new Line2D.Double(1, Double.NaN, 3, 4); assertFalse(LineUtils.clipLine(line, rect)); assertTrue(lineEquals(line, 1, Double.NaN, 3, 4)); line = new Line2D.Double(1, 2, Double.NaN, 4); assertFalse(LineUtils.clipLine(line, rect)); assertTrue(lineEquals(line, 1, 2, Double.NaN, 4)); line = new Line2D.Double(1, 2, 3, Double.NaN); assertFalse(LineUtils.clipLine(line, rect)); assertTrue(lineEquals(line, 1, 2, 3, Double.NaN)); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/util/LogFormatTest.java000066400000000000000000000071471463604235500274740ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * LogFormatTest.java * ------------------ * (C) Copyright 2008-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.util; import java.text.DecimalFormat; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link LogFormat} class. */ public class LogFormatTest { /** * Check that the equals() method distinguishes all fields. */ @Test public void testEquals() { LogFormat f1 = new LogFormat(10.0, "10", true); LogFormat f2 = new LogFormat(10.0, "10", true); assertEquals(f1, f2); f1 = new LogFormat(11.0, "10", true); assertNotEquals(f1, f2); f2 = new LogFormat(11.0, "10", true); assertEquals(f1, f2); f1 = new LogFormat(11.0, "11", true); assertNotEquals(f1, f2); f2 = new LogFormat(11.0, "11", true); assertEquals(f1, f2); f1 = new LogFormat(11.0, "11", false); assertNotEquals(f1, f2); f2 = new LogFormat(11.0, "11", false); assertEquals(f1, f2); f1.setExponentFormat(new DecimalFormat("0.000")); assertNotEquals(f1, f2); f2.setExponentFormat(new DecimalFormat("0.000")); assertEquals(f1, f2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { LogFormat f1 = new LogFormat(10.0, "10", true); LogFormat f2 = new LogFormat(10.0, "10", true); assertEquals(f1, f2); int h1 = f1.hashCode(); int h2 = f2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() { LogFormat f1 = new LogFormat(10.0, "10", true); LogFormat f2 = (LogFormat) f1.clone(); assertNotSame(f1, f2); assertSame(f1.getClass(), f2.getClass()); assertEquals(f1, f2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { LogFormat f1 = new LogFormat(10.0, "10", true); LogFormat f2 = TestUtils.serialised(f1); assertEquals(f1, f2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/chart/util/RelativeDateFormatTest.java000066400000000000000000000154501463604235500313200ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * RelativeDateFormatTest.java * --------------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.chart.util; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.Date; import java.util.Locale; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.BeforeEach; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link RelativeDateFormat} class. */ public class RelativeDateFormatTest { private Locale savedLocale; /** * Set a known locale for the tests. */ @BeforeEach public void setUp() throws Exception { this.savedLocale = Locale.getDefault(); Locale.setDefault(Locale.UK); } /** * Restore the default locale after the tests complete. */ @AfterEach public void tearDown() throws Exception { Locale.setDefault(this.savedLocale); } /** * Some checks for the formatting. */ @Test public void testFormat() { RelativeDateFormat rdf = new RelativeDateFormat(); String s = rdf.format(new Date(2 * 60L * 60L * 1000L + 122500L)); assertEquals("2h2m2.500s", s); } /** * Test that we can configure the RelativeDateFormat to show * hh:mm:ss. */ @Test public void test2033092() { RelativeDateFormat rdf = new RelativeDateFormat(); rdf.setShowZeroDays(false); rdf.setShowZeroHours(false); rdf.setMinuteSuffix(":"); rdf.setHourSuffix(":"); rdf.setSecondSuffix(""); DecimalFormat hoursFormatter = new DecimalFormat(); hoursFormatter.setMaximumFractionDigits(0); hoursFormatter.setMaximumIntegerDigits(2); hoursFormatter.setMinimumIntegerDigits(2); rdf.setHourFormatter(hoursFormatter); DecimalFormat minsFormatter = new DecimalFormat(); minsFormatter.setMaximumFractionDigits(0); minsFormatter.setMaximumIntegerDigits(2); minsFormatter.setMinimumIntegerDigits(2); rdf.setMinuteFormatter(minsFormatter); DecimalFormat secondsFormatter = new DecimalFormat(); secondsFormatter.setMaximumFractionDigits(0); secondsFormatter.setMaximumIntegerDigits(2); secondsFormatter.setMinimumIntegerDigits(2); rdf.setSecondFormatter(secondsFormatter); String s = rdf.format(new Date(2 * 60L * 60L * 1000L + 122500L)); assertEquals("02:02:02", s); } /** * Check that the equals() method can distinguish all fields. */ @Test public void testEquals() { RelativeDateFormat df1 = new RelativeDateFormat(); RelativeDateFormat df2 = new RelativeDateFormat(); assertEquals(df1, df2); df1.setBaseMillis(123L); assertNotEquals(df1, df2); df2.setBaseMillis(123L); assertEquals(df1, df2); df1.setDayFormatter(new DecimalFormat("0%")); assertNotEquals(df1, df2); df2.setDayFormatter(new DecimalFormat("0%")); assertEquals(df1, df2); df1.setDaySuffix("D"); assertNotEquals(df1, df2); df2.setDaySuffix("D"); assertEquals(df1, df2); df1.setHourFormatter(new DecimalFormat("0%")); assertNotEquals(df1, df2); df2.setHourFormatter(new DecimalFormat("0%")); assertEquals(df1, df2); df1.setHourSuffix("H"); assertNotEquals(df1, df2); df2.setHourSuffix("H"); assertEquals(df1, df2); df1.setMinuteFormatter(new DecimalFormat("0%")); assertNotEquals(df1, df2); df2.setMinuteFormatter(new DecimalFormat("0%")); assertEquals(df1, df2); df1.setMinuteSuffix("M"); assertNotEquals(df1, df2); df2.setMinuteSuffix("M"); assertEquals(df1, df2); df1.setSecondSuffix("S"); assertNotEquals(df1, df2); df2.setSecondSuffix("S"); assertEquals(df1, df2); df1.setShowZeroDays(!df1.getShowZeroDays()); assertNotEquals(df1, df2); df2.setShowZeroDays(!df2.getShowZeroDays()); assertEquals(df1, df2); df1.setSecondFormatter(new DecimalFormat("0.0")); assertNotEquals(df1, df2); df2.setSecondFormatter(new DecimalFormat("0.0")); assertEquals(df1, df2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { RelativeDateFormat df1 = new RelativeDateFormat(123L); RelativeDateFormat df2 = new RelativeDateFormat(123L); assertEquals(df1, df2); int h1 = df1.hashCode(); int h2 = df2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() { NumberFormat nf = new DecimalFormat("0"); RelativeDateFormat df1 = new RelativeDateFormat(); df1.setSecondFormatter(nf); RelativeDateFormat df2 = null; df2 = (RelativeDateFormat) df1.clone(); assertNotSame(df1, df2); assertSame(df1.getClass(), df2.getClass()); assertEquals(df1, df2); // is the clone independent nf.setMinimumFractionDigits(2); assertNotEquals(df1, df2); } /** * Some tests for negative dates. */ @Test public void testNegative() { NumberFormat nf = new DecimalFormat("0"); RelativeDateFormat df1 = new RelativeDateFormat(); df1.setSecondFormatter(nf); assertEquals("-0h0m1s", df1.format(new Date(-1000L))); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/000077500000000000000000000000001463604235500227215ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/ComparableObjectItemTest.java000066400000000000000000000106441463604235500304440ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------- * ComparableObjectItemTest.java * ----------------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.fail; /** * Tests for the {@link ComparableObjectItem} class. */ public class ComparableObjectItemTest { /** * Some checks for the constructor. */ @Test public void testConstructor() { // check null argument 1 try { /* ComparableObjectItem item1 = */ new ComparableObjectItem(null, "XYZ"); fail("There should be an exception."); } catch (IllegalArgumentException e) { // expected } } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { ComparableObjectItem item1 = new ComparableObjectItem(1, "XYZ"); ComparableObjectItem item2 = new ComparableObjectItem(1, "XYZ"); assertEquals(item1, item2); item1 = new ComparableObjectItem(2, "XYZ"); assertNotEquals(item1, item2); item2 = new ComparableObjectItem(2, "XYZ"); assertEquals(item1, item2); item1 = new ComparableObjectItem(2, null); assertNotEquals(item1, item2); item2 = new ComparableObjectItem(2, null); assertEquals(item1, item2); } /** * Some checks for the clone() method. * * @throws java.lang.CloneNotSupportedException if there is a problem cloning. */ @Test public void testCloning() throws CloneNotSupportedException { ComparableObjectItem item1 = new ComparableObjectItem(1, "XYZ"); ComparableObjectItem item2 = (ComparableObjectItem) item1.clone(); assertNotSame(item1, item2); assertSame(item1.getClass(), item2.getClass()); assertEquals(item1, item2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { ComparableObjectItem item1 = new ComparableObjectItem(1, "XYZ"); ComparableObjectItem item2 = TestUtils.serialised(item1); assertEquals(item1, item2); } /** * Some checks for the compareTo() method. */ @Test public void testCompareTo() { ComparableObjectItem item1 = new ComparableObjectItem(1, "XYZ"); ComparableObjectItem item2 = new ComparableObjectItem(2, "XYZ"); ComparableObjectItem item3 = new ComparableObjectItem(3, "XYZ"); ComparableObjectItem item4 = new ComparableObjectItem(1, "XYZ"); assertTrue(item2.compareTo(item1) > 0); assertTrue(item3.compareTo(item1) > 0); assertEquals(0, item4.compareTo(item1)); assertTrue(item1.compareTo(item2) < 0); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/ComparableObjectSeriesTest.java000066400000000000000000000144721463604235500310030ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------- * ComparableObjectSeriesTest.java * ------------------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link ComparableObjectSeries} class. */ public class ComparableObjectSeriesTest { static class MyComparableObjectSeries extends ComparableObjectSeries { /** * Creates a new instance. * * @param key the series key. */ public MyComparableObjectSeries(Comparable key) { super(key); } /** * Creates a new instance. * * @param key the series key. * @param autoSort automatically sort by x-value? * @param allowDuplicateXValues allow duplicate values? */ public MyComparableObjectSeries(Comparable key, boolean autoSort, boolean allowDuplicateXValues) { super(key, autoSort, allowDuplicateXValues); } @Override public void add(Comparable x, Object y) { super.add(x, y); } @Override public ComparableObjectItem remove(Comparable x) { return super.remove(x); } } /** * Some checks for the constructor. */ @Test public void testConstructor1() { ComparableObjectSeries s1 = new ComparableObjectSeries("s1"); assertEquals("s1", s1.getKey()); assertNull(s1.getDescription()); assertTrue(s1.getAllowDuplicateXValues()); assertTrue(s1.getAutoSort()); assertEquals(0, s1.getItemCount()); assertEquals(Integer.MAX_VALUE, s1.getMaximumItemCount()); // try null key boolean pass = false; try { /*s1 = */new ComparableObjectSeries(null); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { MyComparableObjectSeries s1 = new MyComparableObjectSeries("A"); MyComparableObjectSeries s2 = new MyComparableObjectSeries("A"); assertEquals(s1, s2); assertEquals(s2, s1); // key s1 = new MyComparableObjectSeries("B"); assertNotEquals(s1, s2); s2 = new MyComparableObjectSeries("B"); assertEquals(s1, s2); // autoSort s1 = new MyComparableObjectSeries("B", false, true); assertNotEquals(s1, s2); s2 = new MyComparableObjectSeries("B", false, true); assertEquals(s1, s2); // allowDuplicateXValues s1 = new MyComparableObjectSeries("B", false, false); assertNotEquals(s1, s2); s2 = new MyComparableObjectSeries("B", false, false); assertEquals(s1, s2); // add a value s1.add(1, "ABC"); assertNotEquals(s1, s2); s2.add(1, "ABC"); assertEquals(s1, s2); // add another value s1.add(0, "DEF"); assertNotEquals(s1, s2); s2.add(0, "DEF"); assertEquals(s1, s2); // remove an item s1.remove(1); assertNotEquals(s1, s2); s2.remove(1); assertEquals(s1, s2); } /** * Some checks for the clone() method. * @throws java.lang.CloneNotSupportedException if there is a problem cloning. */ @Test public void testCloning() throws CloneNotSupportedException { MyComparableObjectSeries s1 = new MyComparableObjectSeries("A"); s1.add(1, "ABC"); MyComparableObjectSeries s2 = (MyComparableObjectSeries) s1.clone(); assertNotSame(s1, s2); assertSame(s1.getClass(), s2.getClass()); assertEquals(s1, s2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { MyComparableObjectSeries s1 = new MyComparableObjectSeries("A"); s1.add(1, "ABC"); MyComparableObjectSeries s2 = TestUtils.serialised(s1); assertEquals(s1, s2); } /** * Some simple checks for the hashCode() method. */ @Test public void testHashCode() { MyComparableObjectSeries s1 = new MyComparableObjectSeries("Test"); MyComparableObjectSeries s2 = new MyComparableObjectSeries("Test"); assertEquals(s1, s2); assertEquals(s1.hashCode(), s2.hashCode()); s1.add("A", "1"); s2.add("A", "1"); assertEquals(s1, s2); assertEquals(s1.hashCode(), s2.hashCode()); s1.add("B", null); s2.add("B", null); assertEquals(s1, s2); assertEquals(s1.hashCode(), s2.hashCode()); s1.add("C", "3"); s2.add("C", "3"); assertEquals(s1, s2); assertEquals(s1.hashCode(), s2.hashCode()); s1.add("D", "4"); s2.add("D", "4"); assertEquals(s1, s2); assertEquals(s1.hashCode(), s2.hashCode()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/DataUtilsTest.java000066400000000000000000000205771463604235500263310ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * DataUtilsTest.java * ------------------ * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertFalse; import org.junit.jupiter.api.Test; /** * Some tests for the {@link DataUtils} class. */ public class DataUtilsTest { /** * Tests the createNumberArray2D() method. */ @Test public void testCreateNumberArray2D() { double[][] d = new double[2][]; d[0] = new double[] {1.1, 2.2, 3.3, 4.4}; d[1] = new double[] {1.1, 2.2, 3.3, 4.4, 5.5}; Number[][] n = DataUtils.createNumberArray2D(d); assertEquals(2, n.length); assertEquals(4, n[0].length); assertEquals(5, n[1].length); } private static final double EPSILON = 0.000000001; /** * Some checks for the calculateColumnTotal() method. */ @Test public void testCalculateColumnTotal() { DefaultKeyedValues2D table = new DefaultKeyedValues2D(); table.addValue(1.0, "R0", "C0"); table.addValue(2.0, "R0", "C1"); table.addValue(3.0, "R1", "C0"); table.addValue(4.0, "R1", "C1"); assertEquals(4.0, DataUtils.calculateColumnTotal(table, 0), EPSILON); assertEquals(6.0, DataUtils.calculateColumnTotal(table, 1), EPSILON); table.setValue(null, "R1", "C1"); assertEquals(2.0, DataUtils.calculateColumnTotal(table, 1), EPSILON); } /** * Some checks for the calculateColumnTotal() method. */ @Test public void testCalculateColumnTotal2() { DefaultKeyedValues2D table = new DefaultKeyedValues2D(); table.addValue(1.0, "R0", "C0"); table.addValue(2.0, "R0", "C1"); table.addValue(3.0, "R1", "C0"); table.addValue(4.0, "R1", "C1"); assertEquals(4.0, DataUtils.calculateColumnTotal(table, 0, new int[] {0, 1}), EPSILON); assertEquals(1.0, DataUtils.calculateColumnTotal(table, 0, new int[] {0}), EPSILON); assertEquals(3.0, DataUtils.calculateColumnTotal(table, 0, new int[] {1}), EPSILON); assertEquals(0.0, DataUtils.calculateColumnTotal(table, 0, new int[] {}), EPSILON); assertEquals(6.0, DataUtils.calculateColumnTotal(table, 1, new int[] {0, 1}), EPSILON); assertEquals(2.0, DataUtils.calculateColumnTotal(table, 1, new int[] {0}), EPSILON); assertEquals(4.0, DataUtils.calculateColumnTotal(table, 1, new int[] {1}), EPSILON); table.setValue(null, "R1", "C1"); assertEquals(2.0, DataUtils.calculateColumnTotal(table, 1, new int[] {0, 1}), EPSILON); assertEquals(0.0, DataUtils.calculateColumnTotal(table, 1, new int[] {1}), EPSILON); } /** * Some checks for the calculateRowTotal() method. */ @Test public void testCalculateRowTotal() { DefaultKeyedValues2D table = new DefaultKeyedValues2D(); table.addValue(1.0, "R0", "C0"); table.addValue(2.0, "R0", "C1"); table.addValue(3.0, "R1", "C0"); table.addValue(4.0, "R1", "C1"); assertEquals(3.0, DataUtils.calculateRowTotal(table, 0), EPSILON); assertEquals(7.0, DataUtils.calculateRowTotal(table, 1), EPSILON); table.setValue(null, "R1", "C1"); assertEquals(3.0, DataUtils.calculateRowTotal(table, 1), EPSILON); } /** * Some checks for the calculateRowTotal() method. */ @Test public void testCalculateRowTotal2() { DefaultKeyedValues2D table = new DefaultKeyedValues2D(); table.addValue(1.0, "R0", "C0"); table.addValue(2.0, "R0", "C1"); table.addValue(3.0, "R1", "C0"); table.addValue(4.0, "R1", "C1"); assertEquals(3.0, DataUtils.calculateRowTotal(table, 0, new int[] {0, 1}), EPSILON); assertEquals(1.0, DataUtils.calculateRowTotal(table, 0, new int[] {0}), EPSILON); assertEquals(2.0, DataUtils.calculateRowTotal(table, 0, new int[] {1}), EPSILON); assertEquals(0.0, DataUtils.calculateRowTotal(table, 0, new int[] {}), EPSILON); assertEquals(7.0, DataUtils.calculateRowTotal(table, 1, new int[] {0, 1}), EPSILON); assertEquals(3.0, DataUtils.calculateRowTotal(table, 1, new int[] {0}), EPSILON); assertEquals(4.0, DataUtils.calculateRowTotal(table, 1, new int[] {1}), EPSILON); assertEquals(0.0, DataUtils.calculateRowTotal(table, 1, new int[] {}), EPSILON); table.setValue(null, "R1", "C1"); assertEquals(3.0, DataUtils.calculateRowTotal(table, 1, new int[] {0, 1}), EPSILON); assertEquals(0.0, DataUtils.calculateRowTotal(table, 1, new int[] {1}), EPSILON); } /** * Some tests for the equal(double[][], double[][]) method. */ @Test public void testEqual() { assertTrue(DataUtils.equal(null, null)); double[][] a = new double[5][]; double[][] b = new double[5][]; assertTrue(DataUtils.equal(a, b)); a = new double[4][]; assertFalse(DataUtils.equal(a, b)); b = new double[4][]; assertTrue(DataUtils.equal(a, b)); a[0] = new double[6]; assertFalse(DataUtils.equal(a, b)); b[0] = new double[6]; assertTrue(DataUtils.equal(a, b)); a[0][0] = 1.0; assertFalse(DataUtils.equal(a, b)); b[0][0] = 1.0; assertTrue(DataUtils.equal(a, b)); a[0][1] = Double.NaN; assertFalse(DataUtils.equal(a, b)); b[0][1] = Double.NaN; assertTrue(DataUtils.equal(a, b)); a[0][2] = Double.NEGATIVE_INFINITY; assertFalse(DataUtils.equal(a, b)); b[0][2] = Double.NEGATIVE_INFINITY; assertTrue(DataUtils.equal(a, b)); a[0][3] = Double.POSITIVE_INFINITY; assertFalse(DataUtils.equal(a, b)); b[0][3] = Double.POSITIVE_INFINITY; assertTrue(DataUtils.equal(a, b)); a[0][4] = Double.POSITIVE_INFINITY; assertFalse(DataUtils.equal(a, b)); b[0][4] = Double.NEGATIVE_INFINITY; assertFalse(DataUtils.equal(a, b)); b[0][4] = Double.POSITIVE_INFINITY; assertTrue(DataUtils.equal(a, b)); } /** * Some tests for the clone() method. */ @Test public void testClone() { double[][] a = new double[1][]; double[][] b = DataUtils.clone(a); assertTrue(DataUtils.equal(a, b)); a[0] = new double[] { 3.0, 4.0 }; assertFalse(DataUtils.equal(a, b)); b[0] = new double[] { 3.0, 4.0 }; assertTrue(DataUtils.equal(a, b)); a = new double[2][3]; a[0][0] = 1.23; a[1][1] = Double.NaN; b = DataUtils.clone(a); assertTrue(DataUtils.equal(a, b)); a[0][0] = 99.9; assertFalse(DataUtils.equal(a, b)); b[0][0] = 99.9; assertTrue(DataUtils.equal(a, b)); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/DatasetChangeConfirmation.java000066400000000000000000000035501463604235500306330ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------ * DatasetChangeConfirmation.java * ------------------------------ * (C) Copyright 2022, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data; import org.jfree.data.general.DatasetChangeEvent; import org.jfree.data.general.DatasetChangeListener; /** * Test class that records the latest event received as a {@link DatasetChangeListener}. */ public class DatasetChangeConfirmation implements DatasetChangeListener { public DatasetChangeEvent event; @Override public void datasetChanged(DatasetChangeEvent event) { this.event = event; } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/DefaultKeyedValueTest.java000066400000000000000000000101011463604235500277600ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * DefaultKeyedValueTest.java * -------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link DefaultKeyedValue} class. */ public class DefaultKeyedValueTest { /** * Simple checks for the constructor. */ @Test public void testConstructor() { DefaultKeyedValue v = new DefaultKeyedValue("A", 1); assertEquals("A", v.getKey()); assertEquals(1, v.getValue()); // try null key boolean pass = false; try { v = new DefaultKeyedValue(null, 1); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); // try a null value v = new DefaultKeyedValue("A", null); assertNull(v.getValue()); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { DefaultKeyedValue v1 = new DefaultKeyedValue("Test", 45.5); DefaultKeyedValue v2 = new DefaultKeyedValue("Test", 45.5); assertEquals(v1, v2); assertEquals(v2, v1); v1 = new DefaultKeyedValue("Test 1", 45.5); v2 = new DefaultKeyedValue("Test 2", 45.5); assertNotEquals(v1, v2); v1 = new DefaultKeyedValue("Test", 45.5); v2 = new DefaultKeyedValue("Test", 45.6); assertNotEquals(v1, v2); } /** * Confirm that the equals method works correctly for null values. */ @Test public void testEqualsForNullValues() { DefaultKeyedValue v1 = new DefaultKeyedValue<>("K1", null); DefaultKeyedValue v2 = new DefaultKeyedValue<>("K1", null); assertEquals(v1, v2); v1.setValue(1); assertNotEquals(v1, v2); assertNotEquals(v2, v1); } /** * Some checks for the clone() method. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { DefaultKeyedValue v1 = new DefaultKeyedValue("Test", 45.5); DefaultKeyedValue v2 = (DefaultKeyedValue) v1.clone(); assertNotSame(v1, v2); assertSame(v1.getClass(), v2.getClass()); assertEquals(v1, v2); // confirm that the clone is independent of the original v2.setValue(12.3); assertNotEquals(v1, v2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DefaultKeyedValue v1 = new DefaultKeyedValue("Test", 25.3); DefaultKeyedValue v2 = TestUtils.serialised(v1); assertEquals(v1, v2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/DefaultKeyedValues2DTest.java000066400000000000000000000225041463604235500303430ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------- * DefaultKeyedValues2DTest.java * ----------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link DefaultKeyedValues2D} class. */ public class DefaultKeyedValues2DTest { /** * Some checks for the getValue() method. */ @Test public void testGetValue() { DefaultKeyedValues2D d = new DefaultKeyedValues2D(); d.addValue(1.0, "R1", "C1"); assertEquals(1.0, d.getValue("R1", "C1")); boolean pass = false; try { d.getValue("XX", "C1"); } catch (UnknownKeyException e) { pass = true; } assertTrue(pass); pass = false; try { d.getValue("R1", "XX"); } catch (UnknownKeyException e) { pass = true; } assertTrue(pass); } /** * Some checks for the clone() method. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { DefaultKeyedValues2D v1 = new DefaultKeyedValues2D(); v1.setValue(1, "V1", "C1"); v1.setValue(null, "V2", "C1"); v1.setValue(3, "V3", "C2"); DefaultKeyedValues2D v2 = (DefaultKeyedValues2D) v1.clone(); assertNotSame(v1, v2); assertSame(v1.getClass(), v2.getClass()); assertEquals(v1, v2); // check that clone is independent of the original v2.setValue(2, "V2", "C1"); assertNotEquals(v1, v2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DefaultKeyedValues2D kv2D1 = new DefaultKeyedValues2D(); kv2D1.addValue(234.2, "Row1", "Col1"); kv2D1.addValue(null, "Row1", "Col2"); kv2D1.addValue(345.9, "Row2", "Col1"); kv2D1.addValue(452.7, "Row2", "Col2"); DefaultKeyedValues2D kv2D2 = TestUtils.serialised(kv2D1); assertEquals(kv2D1, kv2D2); } /** * Some checks for the equals() method. */ @Test public void testEquals() { DefaultKeyedValues2D d1 = new DefaultKeyedValues2D(); DefaultKeyedValues2D d2 = new DefaultKeyedValues2D(); assertEquals(d1, d2); assertEquals(d2, d1); d1.addValue(1.0, 2.0, "S1"); assertNotEquals(d1, d2); d2.addValue(1.0, 2.0, "S1"); assertEquals(d1, d2); } /** * Populates a data structure with sparse entries, then checks that * the unspecified entries return null. */ @Test public void testSparsePopulation() { DefaultKeyedValues2D d = new DefaultKeyedValues2D(); d.addValue(11, "R1", "C1"); d.addValue(22, "R2", "C2"); assertEquals(11, d.getValue("R1", "C1")); assertNull(d.getValue("R1", "C2")); assertEquals(22, d.getValue("R2", "C2")); assertNull(d.getValue("R2", "C1")); } /** * Some basic checks for the getRowCount() method. */ @Test public void testRowCount() { DefaultKeyedValues2D d = new DefaultKeyedValues2D(); assertEquals(0, d.getRowCount()); d.addValue(1.0, "R1", "C1"); assertEquals(1, d.getRowCount()); d.addValue(2.0, "R2", "C1"); assertEquals(2, d.getRowCount()); } /** * Some basic checks for the getColumnCount() method. */ @Test public void testColumnCount() { DefaultKeyedValues2D d = new DefaultKeyedValues2D(); assertEquals(0, d.getColumnCount()); d.addValue(1.0, "R1", "C1"); assertEquals(1, d.getColumnCount()); d.addValue(2.0, "R1", "C2"); assertEquals(2, d.getColumnCount()); } private static final double EPSILON = 0.0000000001; /** * Some basic checks for the getValue(int, int) method. */ @Test public void testGetValue2() { DefaultKeyedValues2D d = new DefaultKeyedValues2D(); boolean pass = false; try { d.getValue(0, 0); } catch (IndexOutOfBoundsException e) { pass = true; } assertTrue(pass); d.addValue(1.0, "R1", "C1"); assertEquals(1.0, d.getValue(0, 0).doubleValue(), EPSILON); d.addValue(2.0, "R2", "C2"); assertEquals(2.0, d.getValue(1, 1).doubleValue(), EPSILON); assertNull(d.getValue(1, 0)); assertNull(d.getValue(0, 1)); pass = false; try { d.getValue(2, 0); } catch (IndexOutOfBoundsException e) { pass = true; } assertTrue(pass); } /** * Some basic checks for the getRowKey() method. */ @Test public void testGetRowKey() { DefaultKeyedValues2D d = new DefaultKeyedValues2D(); boolean pass = false; try { d.getRowKey(0); } catch (IndexOutOfBoundsException e) { pass = true; } assertTrue(pass); d.addValue(1.0, "R1", "C1"); d.addValue(1.0, "R2", "C1"); assertEquals("R1", d.getRowKey(0)); assertEquals("R2", d.getRowKey(1)); // check sorted rows d = new DefaultKeyedValues2D(true); d.addValue(1.0, "R1", "C1"); assertEquals("R1", d.getRowKey(0)); d.addValue(0.0, "R0", "C1"); assertEquals("R0", d.getRowKey(0)); assertEquals("R1", d.getRowKey(1)); } /** * Some basic checks for the getColumnKey() method. */ @Test public void testGetColumnKey() { DefaultKeyedValues2D d = new DefaultKeyedValues2D(); boolean pass = false; try { d.getColumnKey(0); } catch (IndexOutOfBoundsException e) { pass = true; } assertTrue(pass); d.addValue(1.0, "R1", "C1"); d.addValue(1.0, "R1", "C2"); assertEquals("C1", d.getColumnKey(0)); assertEquals("C2", d.getColumnKey(1)); } /** * Some basic checks for the removeValue() method. */ @Test public void testRemoveValue() { DefaultKeyedValues2D d = new DefaultKeyedValues2D(); d.removeValue("R1", "C1"); d.addValue(1.0, "R1", "C1"); d.removeValue("R1", "C1"); assertEquals(0, d.getRowCount()); assertEquals(0, d.getColumnCount()); d.addValue(1.0, "R1", "C1"); d.addValue(2.0, "R2", "C1"); d.removeValue("R1", "C1"); assertEquals(2.0, d.getValue(0, 0)); } /** * A test for bug 1690654. */ @Test public void testRemoveValueBug1690654() { DefaultKeyedValues2D d = new DefaultKeyedValues2D(); d.addValue(1.0, "R1", "C1"); d.addValue(2.0, "R2", "C2"); assertEquals(2, d.getColumnCount()); assertEquals(2, d.getRowCount()); d.removeValue("R2", "C2"); assertEquals(1, d.getColumnCount()); assertEquals(1, d.getRowCount()); assertEquals(1.0, d.getValue(0, 0)); } /** * Some basic checks for the removeRow() method. */ @Test public void testRemoveRow() { DefaultKeyedValues2D d = new DefaultKeyedValues2D(); boolean pass = false; try { d.removeRow(0); } catch (IndexOutOfBoundsException e) { pass = true; } assertTrue(pass); } /** * Some basic checks for the removeColumn(Comparable) method. */ @Test public void testRemoveColumnByKey() { DefaultKeyedValues2D d = new DefaultKeyedValues2D(); d.addValue(1.0, "R1", "C1"); d.addValue(2.0, "R2", "C2"); d.removeColumn("C2"); d.addValue(3.0, "R2", "C2"); assertEquals(3.0, d.getValue("R2", "C2").doubleValue(), EPSILON); // check for unknown column boolean pass = false; try { d.removeColumn("XXX"); } catch (UnknownKeyException e) { pass = true; } assertTrue(pass); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/DefaultKeyedValuesTest.java000066400000000000000000000336451463604235500301650ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * DefaultKeyedValuesTest.java * --------------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data; import java.util.List; import org.jfree.chart.TestUtils; import org.jfree.chart.util.SortOrder; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link DefaultKeyedValues} class. */ public class DefaultKeyedValuesTest { /** * Checks that a new instance is empty. */ @Test public void testConstructor() { DefaultKeyedValues d = new DefaultKeyedValues(); assertEquals(0, d.getItemCount()); } /** * Some checks for the getItemCount() method. */ @Test public void testGetItemCount() { DefaultKeyedValues d = new DefaultKeyedValues(); assertEquals(0, d.getItemCount()); d.addValue("A", 1.0); assertEquals(1, d.getItemCount()); d.addValue("B", 2.0); assertEquals(2, d.getItemCount()); d.clear(); assertEquals(0, d.getItemCount()); } /** * Some checks for the getKeys() method. */ @Test public void testGetKeys() { DefaultKeyedValues d = new DefaultKeyedValues(); List keys = d.getKeys(); assertTrue(keys.isEmpty()); d.addValue("A", 1.0); keys = d.getKeys(); assertEquals(1, keys.size()); assertTrue(keys.contains("A")); d.addValue("B", 2.0); keys = d.getKeys(); assertEquals(2, keys.size()); assertTrue(keys.contains("A")); assertTrue(keys.contains("B")); d.clear(); keys = d.getKeys(); assertEquals(0, keys.size()); } /** * A simple test for the clear() method. */ @Test public void testClear() { DefaultKeyedValues v1 = new DefaultKeyedValues(); v1.addValue("A", 1.0); v1.addValue("B", 2.0); assertEquals(2, v1.getItemCount()); v1.clear(); assertEquals(0, v1.getItemCount()); } /** * Some checks for the getValue() methods. */ @Test public void testGetValue() { DefaultKeyedValues v1 = new DefaultKeyedValues(); try { /* Number n = */ v1.getValue(-1); fail(); } catch (IndexOutOfBoundsException e) { // expected } try { /* Number n = */ v1.getValue(0); fail(); } catch (IndexOutOfBoundsException e) { // expected } DefaultKeyedValues v2 = new DefaultKeyedValues(); v2.addValue("K1", Integer.valueOf(1)); v2.addValue("K2", Integer.valueOf(2)); v2.addValue("K3", Integer.valueOf(3)); assertEquals(3, v2.getValue(2)); boolean pass = false; try { /* Number n = */ v2.getValue("KK"); } catch (UnknownKeyException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getKey() methods. */ @Test public void testGetKey() { DefaultKeyedValues v1 = new DefaultKeyedValues(); try { /* Comparable k = */ v1.getKey(-1); fail(); } catch (IndexOutOfBoundsException e) { // expected } try { /* Comparable k = */ v1.getKey(0); fail(); } catch (IndexOutOfBoundsException e) { // expected } DefaultKeyedValues v2 = new DefaultKeyedValues(); v2.addValue("K1", 1); v2.addValue("K2", 2); v2.addValue("K3", 3); assertEquals("K2", v2.getKey(1)); } /** * Some checks for the getIndex() methods. */ @Test public void testGetIndex() { DefaultKeyedValues v1 = new DefaultKeyedValues(); assertEquals(-1, v1.getIndex("K1")); DefaultKeyedValues v2 = new DefaultKeyedValues(); v2.addValue("K1", 1); v2.addValue("K2", 2); v2.addValue("K3", 3); assertEquals(2, v2.getIndex("K3")); // try null boolean pass = false; try { v2.getIndex(null); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Another check for the getIndex(Comparable) method. */ @Test public void testGetIndex2() { DefaultKeyedValues v = new DefaultKeyedValues(); assertEquals(-1, v.getIndex("K1")); v.addValue("K1", 1.0); assertEquals(0, v.getIndex("K1")); v.removeValue("K1"); assertEquals(-1, v.getIndex("K1")); } /** * Some checks for the addValue() method. */ @Test public void testAddValue() { DefaultKeyedValues v1 = new DefaultKeyedValues(); v1.addValue("A", 1.0); assertEquals(1.0, v1.getValue("A")); v1.addValue("B", 2.0); assertEquals(2.0, v1.getValue("B")); v1.addValue("B", 3.0); assertEquals(3.0, v1.getValue("B")); assertEquals(2, v1.getItemCount()); v1.addValue("A", null); assertNull(v1.getValue("A")); assertEquals(2, v1.getItemCount()); boolean pass = false; try { v1.addValue(null, 99.9); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Some checks for the insertValue() method. */ @Test public void testInsertValue() { DefaultKeyedValues v1 = new DefaultKeyedValues(); v1.insertValue(0, "A", 1.0); assertEquals(1.0, v1.getValue(0)); v1.insertValue(0, "B", 2.0); assertEquals(2.0, v1.getValue(0)); assertEquals(1.0, v1.getValue(1)); // it's OK to use an index equal to the size of the list v1.insertValue(2, "C", 3.0); assertEquals(2.0, v1.getValue(0)); assertEquals(1.0, v1.getValue(1)); assertEquals(3.0, v1.getValue(2)); // try replacing an existing value v1.insertValue(2, "B", 4.0); assertEquals(1.0, v1.getValue(0)); assertEquals(3.0, v1.getValue(1)); assertEquals(4.0, v1.getValue(2)); } /** * Some checks for the clone() method. * * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { DefaultKeyedValues v1 = new DefaultKeyedValues(); v1.addValue("V1", 1); v1.addValue("V2", null); v1.addValue("V3", 3); DefaultKeyedValues v2 = (DefaultKeyedValues) v1.clone(); assertNotSame(v1, v2); assertSame(v1.getClass(), v2.getClass()); assertEquals(v1, v2); // confirm that the clone is independent of the original v2.setValue("V1", 44); assertNotEquals(v1, v2); } /** * Check that inserting and retrieving values works as expected. */ @Test public void testInsertAndRetrieve() { DefaultKeyedValues data = new DefaultKeyedValues(); data.addValue("A", 1.0); data.addValue("B", 2.0); data.addValue("C", 3.0); data.addValue("D", null); // check key order assertEquals(data.getKey(0), "A"); assertEquals(data.getKey(1), "B"); assertEquals(data.getKey(2), "C"); assertEquals(data.getKey(3), "D"); // check retrieve value by key assertEquals(data.getValue("A"), 1.0); assertEquals(data.getValue("B"), 2.0); assertEquals(data.getValue("C"), 3.0); assertNull(data.getValue("D")); // check retrieve value by index assertEquals(data.getValue(0), 1.0); assertEquals(data.getValue(1), 2.0); assertEquals(data.getValue(2), 3.0); assertNull(data.getValue(3)); } /** * Some tests for the removeValue() method. */ @Test public void testRemoveValue() { DefaultKeyedValues data = new DefaultKeyedValues(); data.addValue("A", 1.0); data.addValue("B", null); data.addValue("C", 3.0); data.addValue("D", 2.0); assertEquals(1, data.getIndex("B")); data.removeValue("B"); assertEquals(-1, data.getIndex("B")); boolean pass = false; try { data.removeValue("XXX"); } catch (UnknownKeyException e) { pass = true; } assertTrue(pass); } /** * Tests sorting of data by key (ascending). */ @Test public void testSortByKeyAscending() { DefaultKeyedValues data = new DefaultKeyedValues(); data.addValue("C", 1.0); data.addValue("B", null); data.addValue("D", 3.0); data.addValue("A", 2.0); data.sortByKeys(SortOrder.ASCENDING); // check key order assertEquals(data.getKey(0), "A"); assertEquals(data.getKey(1), "B"); assertEquals(data.getKey(2), "C"); assertEquals(data.getKey(3), "D"); // check retrieve value by key assertEquals(data.getValue("A"), 2.0); assertNull(data.getValue("B")); assertEquals(data.getValue("C"), 1.0); assertEquals(data.getValue("D"), 3.0); // check retrieve value by index assertEquals(data.getValue(0), 2.0); assertNull(data.getValue(1)); assertEquals(data.getValue(2), 1.0); assertEquals(data.getValue(3), 3.0); } /** * Tests sorting of data by key (descending). */ @Test public void testSortByKeyDescending() { DefaultKeyedValues data = new DefaultKeyedValues(); data.addValue("C", 1.0); data.addValue("B", null); data.addValue("D", 3.0); data.addValue("A", 2.0); data.sortByKeys(SortOrder.DESCENDING); // check key order assertEquals(data.getKey(0), "D"); assertEquals(data.getKey(1), "C"); assertEquals(data.getKey(2), "B"); assertEquals(data.getKey(3), "A"); // check retrieve value by key assertEquals(data.getValue("A"), 2.0); assertNull(data.getValue("B")); assertEquals(data.getValue("C"), 1.0); assertEquals(data.getValue("D"), 3.0); // check retrieve value by index assertEquals(data.getValue(0), 3.0); assertEquals(data.getValue(1), 1.0); assertNull(data.getValue(2)); assertEquals(data.getValue(3), 2.0); } /** * Tests sorting of data by value (ascending). */ @Test public void testSortByValueAscending() { DefaultKeyedValues data = new DefaultKeyedValues(); data.addValue("C", 1.0); data.addValue("B", null); data.addValue("D", 3.0); data.addValue("A", 2.0); data.sortByValues(SortOrder.ASCENDING); // check key order assertEquals(data.getKey(0), "C"); assertEquals(data.getKey(1), "A"); assertEquals(data.getKey(2), "D"); assertEquals(data.getKey(3), "B"); // check retrieve value by key assertEquals(data.getValue("A"), 2.0); assertNull(data.getValue("B")); assertEquals(data.getValue("C"), 1.0); assertEquals(data.getValue("D"), 3.0); // check retrieve value by index assertEquals(data.getValue(0), 1.0); assertEquals(data.getValue(1), 2.0); assertEquals(data.getValue(2), 3.0); assertNull(data.getValue(3)); } /** * Tests sorting of data by key (descending). */ @Test public void testSortByValueDescending() { DefaultKeyedValues data = new DefaultKeyedValues(); data.addValue("C", 1.0); data.addValue("B", null); data.addValue("D", 3.0); data.addValue("A", 2.0); data.sortByValues(SortOrder.DESCENDING); // check key order assertEquals(data.getKey(0), "D"); assertEquals(data.getKey(1), "A"); assertEquals(data.getKey(2), "C"); assertEquals(data.getKey(3), "B"); // check retrieve value by key assertEquals(data.getValue("A"), 2.0); assertNull(data.getValue("B")); assertEquals(data.getValue("C"), 1.0); assertEquals(data.getValue("D"), 3.0); // check retrieve value by index assertEquals(data.getValue(0), 3.0); assertEquals(data.getValue(1), 2.0); assertEquals(data.getValue(2), 1.0); assertNull(data.getValue(3)); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DefaultKeyedValues v1 = new DefaultKeyedValues(); v1.addValue("Key 1", 23); v1.addValue("Key 2", null); v1.addValue("Key 3", 42); DefaultKeyedValues v2 = TestUtils.serialised(v1); assertEquals(v1, v2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/DomainOrderTest.java000066400000000000000000000061241463604235500266320ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * DomainOrderTest.java * -------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link DomainOrder} class. */ public class DomainOrderTest { /** * Some checks for the equals() method. */ @Test public void testEquals() { assertEquals(DomainOrder.NONE, DomainOrder.NONE); assertEquals(DomainOrder.ASCENDING, DomainOrder.ASCENDING); assertEquals(DomainOrder.DESCENDING, DomainOrder.DESCENDING); assertNotEquals(DomainOrder.NONE, DomainOrder.ASCENDING); assertNotEquals(DomainOrder.NONE, DomainOrder.DESCENDING); assertNotEquals(null, DomainOrder.NONE); assertNotEquals(DomainOrder.ASCENDING, DomainOrder.NONE); assertNotEquals(DomainOrder.ASCENDING, DomainOrder.DESCENDING); assertNotEquals(null, DomainOrder.ASCENDING); assertNotEquals(DomainOrder.DESCENDING, DomainOrder.NONE); assertNotEquals(DomainOrder.DESCENDING, DomainOrder.ASCENDING); assertNotEquals(null, DomainOrder.DESCENDING); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { DomainOrder d1 = DomainOrder.ASCENDING; DomainOrder d2 = DomainOrder.ASCENDING; assertEquals(d1, d2); int h1 = d1.hashCode(); int h2 = d2.hashCode(); assertEquals(h1, h2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DomainOrder d1 = DomainOrder.ASCENDING; DomainOrder d2 = TestUtils.serialised(d1); assertSame(d1, d2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/KeyToGroupMapTest.java000066400000000000000000000172251463604235500271410ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * KeyToGroupMapTests.java * ----------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link KeyToGroupMap} class. */ public class KeyToGroupMapTest { /** * Tests the mapKeyToGroup() method. */ @Test public void testMapKeyToGroup() { KeyToGroupMap m1 = new KeyToGroupMap("G1"); // map a key to the default group m1.mapKeyToGroup("K1", "G1"); assertEquals("G1", m1.getGroup("K1")); // map a key to a new group m1.mapKeyToGroup("K2", "G2"); assertEquals("G2", m1.getGroup("K2")); // clear a mapping m1.mapKeyToGroup("K2", null); assertEquals("G1", m1.getGroup("K2")); // after clearing, reverts to // default group // check handling of null key boolean pass = false; try { m1.mapKeyToGroup(null, "G1"); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Tests that the getGroupCount() method returns the correct values under * various circumstances. */ @Test public void testGroupCount() { KeyToGroupMap m1 = new KeyToGroupMap("Default Group"); // a new map always has 1 group (the default group) assertEquals(1, m1.getGroupCount()); // if the default group is not mapped to, it should still count towards // the group count... m1.mapKeyToGroup("C1", "G1"); assertEquals(2, m1.getGroupCount()); // now when the default group is mapped to, it shouldn't increase the // group count... m1.mapKeyToGroup("C2", "Default Group"); assertEquals(2, m1.getGroupCount()); // complicate things a little... m1.mapKeyToGroup("C3", "Default Group"); m1.mapKeyToGroup("C4", "G2"); m1.mapKeyToGroup("C5", "G2"); m1.mapKeyToGroup("C6", "Default Group"); assertEquals(3, m1.getGroupCount()); // now overwrite group "G2"... m1.mapKeyToGroup("C4", "G1"); m1.mapKeyToGroup("C5", "G1"); assertEquals(2, m1.getGroupCount()); } /** * Tests that the getKeyCount() method returns the correct values under * various circumstances. */ @Test public void testKeyCount() { KeyToGroupMap m1 = new KeyToGroupMap("Default Group"); // a new map always has 1 group (the default group) assertEquals(0, m1.getKeyCount("Default Group")); // simple case m1.mapKeyToGroup("K1", "G1"); assertEquals(1, m1.getKeyCount("G1")); m1.mapKeyToGroup("K1", null); assertEquals(0, m1.getKeyCount("G1")); // if there is an explicit mapping to the default group, it is counted m1.mapKeyToGroup("K2", "Default Group"); assertEquals(1, m1.getKeyCount("Default Group")); // complicate things a little... m1.mapKeyToGroup("K3", "Default Group"); m1.mapKeyToGroup("K4", "G2"); m1.mapKeyToGroup("K5", "G2"); m1.mapKeyToGroup("K6", "Default Group"); assertEquals(3, m1.getKeyCount("Default Group")); assertEquals(2, m1.getKeyCount("G2")); // now overwrite group "G2"... m1.mapKeyToGroup("K4", "G1"); m1.mapKeyToGroup("K5", "G1"); assertEquals(2, m1.getKeyCount("G1")); assertEquals(0, m1.getKeyCount("G2")); } /** * Tests the getGroupIndex() method. */ @Test public void testGetGroupIndex() { KeyToGroupMap m1 = new KeyToGroupMap("Default Group"); // the default group is always at index 0 assertEquals(0, m1.getGroupIndex("Default Group")); // a non-existent group should return -1 assertEquals(-1, m1.getGroupIndex("G3")); // indices are assigned in the order that groups are originally mapped m1.mapKeyToGroup("K3", "G3"); m1.mapKeyToGroup("K1", "G1"); m1.mapKeyToGroup("K2", "G2"); assertEquals(1, m1.getGroupIndex("G3")); assertEquals(2, m1.getGroupIndex("G1")); assertEquals(3, m1.getGroupIndex("G2")); } /** * Tests the getGroup() method. */ @Test public void testGetGroup() { KeyToGroupMap m1 = new KeyToGroupMap("Default Group"); // a key that hasn't been mapped should return the default group assertEquals("Default Group", m1.getGroup("K1")); m1.mapKeyToGroup("K1", "G1"); assertEquals("G1", m1.getGroup("K1")); m1.mapKeyToGroup("K1", "G2"); assertEquals("G2", m1.getGroup("K1")); m1.mapKeyToGroup("K1", null); assertEquals("Default Group", m1.getGroup("K1")); // a null argument should throw an exception boolean pass = false; try { Comparable g = m1.getGroup(null); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { KeyToGroupMap m1 = new KeyToGroupMap("Default Group"); KeyToGroupMap m2 = new KeyToGroupMap("Default Group"); assertEquals(m1, m2); assertEquals(m2, m1); m1.mapKeyToGroup("K1", "G1"); assertNotEquals(m1, m2); m2.mapKeyToGroup("K1", "G1"); assertEquals(m1, m2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { KeyToGroupMap m1 = new KeyToGroupMap("Test"); m1.mapKeyToGroup("K1", "G1"); KeyToGroupMap m2 = (KeyToGroupMap) m1.clone(); assertNotSame(m1, m2); assertSame(m1.getClass(), m2.getClass()); assertEquals(m1, m2); // a small check for independence m1.mapKeyToGroup("K1", "G2"); assertNotEquals(m1, m2); m2.mapKeyToGroup("K1", "G2"); assertEquals(m1, m2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { KeyToGroupMap m1 = new KeyToGroupMap("Test"); KeyToGroupMap m2 = TestUtils.serialised(m1); assertEquals(m1, m2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/KeyedObjectTest.java000066400000000000000000000076161463604235500266260ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * KeyedObjectTest.java * -------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data; import java.util.ArrayList; import org.jfree.chart.TestUtils; import org.jfree.data.general.DefaultPieDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link KeyedObject} class. */ public class KeyedObjectTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { KeyedObject ko1 = new KeyedObject("Test", "Object"); KeyedObject ko2 = new KeyedObject("Test", "Object"); assertEquals(ko1, ko2); assertEquals(ko2, ko1); ko1 = new KeyedObject("Test 1", "Object"); ko2 = new KeyedObject("Test 2", "Object"); assertNotEquals(ko1, ko2); ko1 = new KeyedObject("Test", "Object 1"); ko2 = new KeyedObject("Test", "Object 2"); assertNotEquals(ko1, ko2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { KeyedObject ko1 = new KeyedObject("Test", "Object"); KeyedObject ko2 = (KeyedObject) ko1.clone(); assertNotSame(ko1, ko2); assertSame(ko1.getClass(), ko2.getClass()); assertEquals(ko1, ko2); } /** * Confirm special features of cloning. */ @Test public void testCloning2() throws CloneNotSupportedException { // case 1 - object is mutable but not PublicCloneable Object obj1 = new ArrayList(); KeyedObject ko1 = new KeyedObject("Test", obj1); KeyedObject ko2 = (KeyedObject) ko1.clone(); assertNotSame(ko1, ko2); assertSame(ko1.getClass(), ko2.getClass()); assertEquals(ko1, ko2); // the clone contains a reference to the original object assertSame(ko2.getObject(), obj1); // CASE 2 - object is mutable AND PublicCloneable obj1 = new DefaultPieDataset(); ko1 = new KeyedObject("Test", obj1); ko2 = (KeyedObject) ko1.clone(); assertNotSame(ko1, ko2); assertSame(ko1.getClass(), ko2.getClass()); assertEquals(ko1, ko2); // the clone contains a reference to a CLONE of the original object assertNotSame(ko2.getObject(), obj1); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { KeyedObject ko1 = new KeyedObject("Test", "Object"); KeyedObject ko2 = TestUtils.serialised(ko1); assertEquals(ko1, ko2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/KeyedObjects2DTest.java000066400000000000000000000246401463604235500271730ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * KeyedObjects2DTest.java * ----------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link KeyedObjects2D} class. */ public class KeyedObjects2DTest { /** * Some checks for the equals() method. */ @Test public void testEquals() { KeyedObjects2D k1 = new KeyedObjects2D(); KeyedObjects2D k2 = new KeyedObjects2D(); assertEquals(k1, k2); assertEquals(k2, k1); k1.addObject(99, "R1", "C1"); assertNotEquals(k1, k2); k2.addObject(99, "R1", "C1"); assertEquals(k1, k2); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { KeyedObjects2D o1 = new KeyedObjects2D(); o1.setObject(1, "V1", "C1"); o1.setObject(null, "V2", "C1"); o1.setObject(3, "V3", "C2"); KeyedObjects2D o2 = (KeyedObjects2D) o1.clone(); assertNotSame(o1, o2); assertSame(o1.getClass(), o2.getClass()); assertEquals(o1, o2); // check independence o1.addObject("XX", "R1", "C1"); assertNotEquals(o1, o2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { KeyedObjects2D ko2D1 = new KeyedObjects2D(); ko2D1.addObject(234.2, "Row1", "Col1"); ko2D1.addObject(null, "Row1", "Col2"); ko2D1.addObject(345.9, "Row2", "Col1"); ko2D1.addObject(452.7, "Row2", "Col2"); KeyedObjects2D ko2D2 = TestUtils.serialised(ko2D1); assertEquals(ko2D1, ko2D2); } /** * Some checks for the getValue(int, int) method. */ @Test public void testGetValueByIndex() { KeyedObjects2D data = new KeyedObjects2D(); data.addObject("Obj1", "R1", "C1"); data.addObject("Obj2", "R2", "C2"); assertEquals("Obj1", data.getObject(0, 0)); assertEquals("Obj2", data.getObject(1, 1)); assertNull(data.getObject(0, 1)); assertNull(data.getObject(1, 0)); // check invalid indices boolean pass = false; try { data.getObject(-1, 0); } catch (IndexOutOfBoundsException e) { pass = true; } assertTrue(pass); pass = false; try { data.getObject(0, -1); } catch (IndexOutOfBoundsException e) { pass = true; } assertTrue(pass); pass = false; try { data.getObject(2, 0); } catch (IndexOutOfBoundsException e) { pass = true; } assertTrue(pass); pass = false; try { data.getObject(0, 2); } catch (IndexOutOfBoundsException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getValue(Comparable, Comparable) method. */ @Test public void testGetValueByKey() { KeyedObjects2D data = new KeyedObjects2D(); data.addObject("Obj1", "R1", "C1"); data.addObject("Obj2", "R2", "C2"); assertEquals("Obj1", data.getObject("R1", "C1")); assertEquals("Obj2", data.getObject("R2", "C2")); assertNull(data.getObject("R1", "C2")); assertNull(data.getObject("R2", "C1")); // check invalid indices boolean pass = false; try { data.getObject("XX", "C1"); } catch (UnknownKeyException e) { pass = true; } assertTrue(pass); pass = false; try { data.getObject("R1", "XX"); } catch (UnknownKeyException e) { pass = true; } assertTrue(pass); pass = false; try { data.getObject("XX", "C1"); } catch (UnknownKeyException e) { pass = true; } assertTrue(pass); pass = false; try { data.getObject("R1", "XX"); } catch (UnknownKeyException e) { pass = true; } assertTrue(pass); } /** * Some checks for the setObject(Object, Comparable, Comparable) method. */ @Test public void testSetObject() { KeyedObjects2D data = new KeyedObjects2D(); data.setObject("Obj1", "R1", "C1"); data.setObject("Obj2", "R2", "C2"); assertEquals("Obj1", data.getObject("R1", "C1")); assertEquals("Obj2", data.getObject("R2", "C2")); assertNull(data.getObject("R1", "C2")); assertNull(data.getObject("R2", "C1")); // confirm overwriting an existing value data.setObject("ABC", "R2", "C2"); assertEquals("ABC", data.getObject("R2", "C2")); // try null keys boolean pass = false; try { data.setObject("X", null, "C1"); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); pass = false; try { data.setObject("X", "R1", null); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Some checks for the removeRow(int) method. */ @Test public void testRemoveRowByIndex() { KeyedObjects2D data = new KeyedObjects2D(); data.setObject("Obj1", "R1", "C1"); data.setObject("Obj2", "R2", "C2"); data.removeRow(0); assertEquals(1, data.getRowCount()); assertEquals("Obj2", data.getObject(0, 1)); // try negative row index boolean pass = false; try { data.removeRow(-1); } catch (IndexOutOfBoundsException e) { pass = true; } assertTrue(pass); // try row index too high pass = false; try { data.removeRow(data.getRowCount()); } catch (IndexOutOfBoundsException e) { pass = true; } assertTrue(pass); } /** * Some checks for the removeColumn(int) method. */ @Test public void testRemoveColumnByIndex() { KeyedObjects2D data = new KeyedObjects2D(); data.setObject("Obj1", "R1", "C1"); data.setObject("Obj2", "R2", "C2"); data.removeColumn(0); assertEquals(1, data.getColumnCount()); assertEquals("Obj2", data.getObject(1, 0)); // try negative column index boolean pass = false; try { data.removeColumn(-1); } catch (IndexOutOfBoundsException e) { pass = true; } assertTrue(pass); // try column index too high pass = false; try { data.removeColumn(data.getColumnCount()); } catch (IndexOutOfBoundsException e) { pass = true; } assertTrue(pass); } /** * Some checks for the removeRow(Comparable) method. */ @Test public void testRemoveRowByKey() { KeyedObjects2D data = new KeyedObjects2D(); data.setObject("Obj1", "R1", "C1"); data.setObject("Obj2", "R2", "C2"); data.removeRow("R2"); assertEquals(1, data.getRowCount()); assertEquals("Obj1", data.getObject(0, 0)); // try unknown row key boolean pass = false; try { data.removeRow("XXX"); } catch (UnknownKeyException e) { pass = true; } assertTrue(pass); // try null row key pass = false; try { data.removeRow(null); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Some checks for the removeColumn(Comparable) method. */ @Test public void testRemoveColumnByKey() { KeyedObjects2D data = new KeyedObjects2D(); data.setObject("Obj1", "R1", "C1"); data.setObject("Obj2", "R2", "C2"); data.removeColumn("C2"); assertEquals(1, data.getColumnCount()); assertEquals("Obj1", data.getObject(0, 0)); // try unknown column key boolean pass = false; try { data.removeColumn("XXX"); } catch (UnknownKeyException e) { pass = true; } assertTrue(pass); // try null column key pass = false; try { data.removeColumn(null); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * A simple check for the removeValue() method. */ @Test public void testRemoveValue() { KeyedObjects2D data = new KeyedObjects2D(); data.setObject("Obj1", "R1", "C1"); data.setObject("Obj2", "R2", "C2"); data.removeObject("R2", "C2"); assertEquals(1, data.getRowCount()); assertEquals(1, data.getColumnCount()); assertEquals("Obj1", data.getObject(0, 0)); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/KeyedObjectsTest.java000066400000000000000000000237611463604235500270100ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * KeyedObjectsTest.java * --------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data; import java.util.ArrayList; import org.jfree.chart.TestUtils; import org.jfree.data.general.DefaultPieDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link KeyedObjects} class. */ public class KeyedObjectsTest { /** * Confirm that cloning works. * * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { KeyedObjects ko1 = new KeyedObjects(); ko1.addObject("V1", 1); ko1.addObject("V2", null); ko1.addObject("V3", 3); KeyedObjects ko2 = (KeyedObjects) ko1.clone(); assertNotSame(ko1, ko2); assertSame(ko1.getClass(), ko2.getClass()); assertEquals(ko1, ko2); } /** * Confirm special features of cloning. * * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning2() throws CloneNotSupportedException { // case 1 - object is mutable but not PublicCloneable Object obj1 = new ArrayList(); KeyedObjects ko1 = new KeyedObjects(); ko1.addObject("K1", obj1); KeyedObjects ko2 = (KeyedObjects) ko1.clone(); assertNotSame(ko1, ko2); assertSame(ko1.getClass(), ko2.getClass()); assertEquals(ko1, ko2); // the clone contains a reference to the original object assertSame(ko2.getObject("K1"), obj1); // CASE 2 - object is mutable AND PublicCloneable obj1 = new DefaultPieDataset(); ko1 = new KeyedObjects(); ko1.addObject("K1", obj1); ko2 = (KeyedObjects) ko1.clone(); assertNotSame(ko1, ko2); assertSame(ko1.getClass(), ko2.getClass()); assertEquals(ko1, ko2); // the clone contains a reference to a CLONE of the original object assertNotSame(ko2.getObject("K1"), obj1); } /** * Check that inserting and retrieving values works as expected. */ @Test public void testInsertAndRetrieve() { KeyedObjects data = new KeyedObjects(); data.addObject("A", 1.0); data.addObject("B", 2.0); data.addObject("C", 3.0); data.addObject("D", null); // check key order assertEquals(data.getKey(0), "A"); assertEquals(data.getKey(1), "B"); assertEquals(data.getKey(2), "C"); assertEquals(data.getKey(3), "D"); // check retrieve value by key assertEquals(data.getObject("A"), 1.0); assertEquals(data.getObject("B"), 2.0); assertEquals(data.getObject("C"), 3.0); assertNull(data.getObject("D")); boolean pass = false; try { data.getObject("Not a key"); } catch (UnknownKeyException e) { pass = true; } assertTrue(pass); // check retrieve value by index assertEquals(data.getObject(0), 1.0); assertEquals(data.getObject(1), 2.0); assertEquals(data.getObject(2), 3.0); assertNull(data.getObject(3)); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { KeyedObjects ko1 = new KeyedObjects(); ko1.addObject("Key 1", "Object 1"); ko1.addObject("Key 2", null); ko1.addObject("Key 3", "Object 2"); KeyedObjects ko2 = TestUtils.serialised(ko1); assertEquals(ko1, ko2); } /** * Simple checks for the getObject(int) method. */ @Test public void testGetObject() { // retrieve an item KeyedObjects ko1 = new KeyedObjects(); ko1.addObject("Key 1", "Object 1"); ko1.addObject("Key 2", null); ko1.addObject("Key 3", "Object 2"); assertEquals("Object 1", ko1.getObject(0)); assertNull(ko1.getObject(1)); assertEquals("Object 2", ko1.getObject(2)); // request with a negative index boolean pass = false; try { ko1.getObject(-1); } catch (IndexOutOfBoundsException e) { pass = true; } assertTrue(pass); // request width index == itemCount pass = false; try { ko1.getObject(3); } catch (IndexOutOfBoundsException e) { pass = true; } assertTrue(pass); } /** * Simple checks for the getKey(int) method. */ @Test public void testGetKey() { // retrieve an item KeyedObjects ko1 = new KeyedObjects(); ko1.addObject("Key 1", "Object 1"); ko1.addObject("Key 2", null); ko1.addObject("Key 3", "Object 2"); assertEquals("Key 1", ko1.getKey(0)); assertEquals("Key 2", ko1.getKey(1)); assertEquals("Key 3", ko1.getKey(2)); // request with a negative index boolean pass = false; try { ko1.getKey(-1); } catch (IndexOutOfBoundsException e) { pass = true; } assertTrue(pass); // request width index == itemCount pass = false; try { ko1.getKey(3); } catch (IndexOutOfBoundsException e) { pass = true; } assertTrue(pass); } /** * Simple checks for the getIndex(Comparable) method. */ @Test public void testGetIndex() { KeyedObjects ko1 = new KeyedObjects(); ko1.addObject("Key 1", "Object 1"); ko1.addObject("Key 2", null); ko1.addObject("Key 3", "Object 2"); assertEquals(0, ko1.getIndex("Key 1")); assertEquals(1, ko1.getIndex("Key 2")); assertEquals(2, ko1.getIndex("Key 3")); // check null argument boolean pass = false; try { ko1.getIndex(null); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Some checks for the setObject(Comparable, Object) method. */ @Test public void testSetObject() { KeyedObjects ko1 = new KeyedObjects(); ko1.setObject("Key 1", "Object 1"); ko1.setObject("Key 2", null); ko1.setObject("Key 3", "Object 2"); assertEquals("Object 1", ko1.getObject("Key 1")); assertNull(ko1.getObject("Key 2")); assertEquals("Object 2", ko1.getObject("Key 3")); // replace an existing value ko1.setObject("Key 2", "AAA"); ko1.setObject("Key 3", "BBB"); assertEquals("AAA", ko1.getObject("Key 2")); assertEquals("BBB", ko1.getObject("Key 3")); // try a null key - should throw an exception boolean pass = false; try { ko1.setObject(null, "XX"); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Some checks for the removeValue() methods. */ @Test public void testRemoveValue() { KeyedObjects ko1 = new KeyedObjects(); ko1.setObject("Key 1", "Object 1"); ko1.setObject("Key 2", null); ko1.setObject("Key 3", "Object 2"); ko1.removeValue(1); assertEquals(2, ko1.getItemCount()); assertEquals(1, ko1.getIndex("Key 3")); ko1.removeValue("Key 1"); assertEquals(1, ko1.getItemCount()); assertEquals(0, ko1.getIndex("Key 3")); // try unknown key boolean pass = false; try { ko1.removeValue("UNKNOWN"); } catch (UnknownKeyException e) { pass = true; } assertTrue(pass); // try null argument pass = false; try { ko1.removeValue(null); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Some checks for the removeValue(int) method. */ @Test public void testRemoveValueInt() { KeyedObjects ko1 = new KeyedObjects(); ko1.setObject("Key 1", "Object 1"); ko1.setObject("Key 2", null); ko1.setObject("Key 3", "Object 2"); ko1.removeValue(1); assertEquals(2, ko1.getItemCount()); assertEquals(1, ko1.getIndex("Key 3")); // try negative key index boolean pass = false; try { ko1.removeValue(-1); } catch (IndexOutOfBoundsException e) { pass = true; } assertTrue(pass); // try key index == itemCount pass = false; try { ko1.removeValue(2); } catch (IndexOutOfBoundsException e) { pass = true; } assertTrue(pass); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/RangeTest.java000066400000000000000000000257561463604235500254770ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * RangeTest.java * -------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Sergei Ivanov; * Tracy Hiltbrand; * */ package org.jfree.data; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link Range} class. */ public class RangeTest { /** * Use EqualsVerifier to ensure correct implementation of equals and * hashCode. */ @Test public void testEqualsHashCode() { EqualsVerifier.forClass(Range.class) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Confirm that the constructor initializes all the required fields. */ @Test public void testConstructor() { Range r1 = new Range(0.1, 1000.0); assertEquals(r1.getLowerBound(), 0.1, 0.0d); assertEquals(r1.getUpperBound(), 1000.0, 0.0d); try { /*Range r2 =*/ new Range(10.0, 0.0); fail("Lower bound cannot be greater than the upper"); } catch (Exception e) { // expected } } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { Range r1 = new Range(0.0, 1.0); Range r2 = new Range(0.0, 1.0); assertEquals(r1, r2); assertEquals(r2, r1); r1 = new Range(0.0, 1.0); r2 = new Range(0.5, 1.0); assertNotEquals(r1, r2); r1 = new Range(0.0, 1.0); r2 = new Range(0.0, 2.0); assertNotEquals(r1, r2); // a Range object cannot be equal to a different object type assertFalse(r1.equals(0.0)); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { Range a1 = new Range(1.0, 100.0); Range a2 = new Range(1.0, 100.0); assertEquals(a1.hashCode(), a2.hashCode()); a1 = new Range(-100.0, 2.0); a2 = new Range(-100.0, 2.0); assertEquals(a1.hashCode(), a2.hashCode()); } /** * Simple tests for the contains() method. */ @Test public void testContains() { Range r1 = new Range(0.0, 1.0); assertFalse(r1.contains(Double.NaN)); assertFalse(r1.contains(Double.NEGATIVE_INFINITY)); assertFalse(r1.contains(-1.0)); assertTrue(r1.contains(0.0)); assertTrue(r1.contains(0.5)); assertTrue(r1.contains(1.0)); assertFalse(r1.contains(2.0)); assertFalse(r1.contains(Double.POSITIVE_INFINITY)); } /** * Tests the constrain() method for various values. */ @Test public void testConstrain() { Range r1 = new Range(0.0, 1.0); double d = r1.constrain(0.5); assertEquals(0.5, d, 0.0000001); d = r1.constrain(0.0); assertEquals(0.0, d, 0.0000001); d = r1.constrain(1.0); assertEquals(1.0, d, 0.0000001); d = r1.constrain(-1.0); assertEquals(0.0, d, 0.0000001); d = r1.constrain(2.0); assertEquals(1.0, d, 0.0000001); d = r1.constrain(Double.POSITIVE_INFINITY); assertEquals(1.0, d, 0.0000001); d = r1.constrain(Double.NEGATIVE_INFINITY); assertEquals(0.0, d, 0.0000001); d = r1.constrain(Double.NaN); assertTrue(Double.isNaN(d)); } /** * Simple tests for the intersects() method. */ @Test public void testIntersects() { Range r1 = new Range(0.0, 1.0); assertFalse(r1.intersects(-2.0, -1.0)); assertFalse(r1.intersects(-2.0, 0.0)); assertTrue(r1.intersects(-2.0, 0.5)); assertTrue(r1.intersects(-2.0, 1.0)); assertTrue(r1.intersects(-2.0, 1.5)); assertTrue(r1.intersects(0.0, 0.5)); assertTrue(r1.intersects(0.0, 1.0)); assertTrue(r1.intersects(0.0, 1.5)); assertTrue(r1.intersects(0.5, 0.6)); assertTrue(r1.intersects(0.5, 1.0)); assertTrue(r1.intersects(0.5, 1.5)); assertFalse(r1.intersects(1.0, 1.1)); assertFalse(r1.intersects(1.5, 2.0)); } /** * A simple test for the expand() method. */ @Test public void testExpand() { Range r1 = new Range(0.0, 100.0); Range r2 = Range.expand(r1, 0.10, 0.10); assertEquals(-10.0, r2.getLowerBound(), 0.001); assertEquals(110.0, r2.getUpperBound(), 0.001); // Expand by 0% does not change the range r2 = Range.expand(r1, 0.0, 0.0); assertEquals(r1, r2); try { Range.expand(null, 0.1, 0.1); fail("Null value is accepted"); } catch (Exception e) { } // Lower > upper: mid point is used r2 = Range.expand(r1, -0.8, -0.5); assertEquals(65.0, r2.getLowerBound(), 0.001); assertEquals(65.0, r2.getUpperBound(), 0.001); } /** * A simple test for the scale() method. */ @Test public void testShift() { Range r1 = new Range(10.0, 20.0); Range r2 = Range.shift(r1, 20.0); assertEquals(30.0, r2.getLowerBound(), 0.001); assertEquals(40.0, r2.getUpperBound(), 0.001); r1 = new Range(0.0, 100.0); r2 = Range.shift(r1, -50.0, true); assertEquals(-50.0, r2.getLowerBound(), 0.001); assertEquals(50.0, r2.getUpperBound(), 0.001); r1 = new Range(-10.0, 20.0); r2 = Range.shift(r1, 20.0, true); assertEquals(10.0, r2.getLowerBound(), 0.001); assertEquals(40.0, r2.getUpperBound(), 0.001); r1 = new Range(-10.0, 20.0); r2 = Range.shift(r1, -30.0, true); assertEquals(-40.0, r2.getLowerBound(), 0.001); assertEquals(-10.0, r2.getUpperBound(), 0.001); r1 = new Range(-10.0, 20.0); r2 = Range.shift(r1, 20.0, false); assertEquals(0.0, r2.getLowerBound(), 0.001); assertEquals(40.0, r2.getUpperBound(), 0.001); r1 = new Range(-10.0, 20.0); r2 = Range.shift(r1, -30.0, false); assertEquals(-40.0, r2.getLowerBound(), 0.001); assertEquals(0.0, r2.getUpperBound(), 0.001); // Shifting with a delta of 0 does not change the range r2 = Range.shift(r1, 0.0); assertEquals(r1, r2); try { Range.shift(null, 0.1); fail("Null value is accepted"); } catch (Exception e) { } } /** * A simple test for the scale() method. */ @Test public void testScale() { Range r1 = new Range(0.0, 100.0); Range r2 = Range.scale(r1, 0.10); assertEquals(0.0, r2.getLowerBound(), 0.001); assertEquals(10.0, r2.getUpperBound(), 0.001); r1 = new Range(-10.0, 100.0); r2 = Range.scale(r1, 2.0); assertEquals(-20.0, r2.getLowerBound(), 0.001); assertEquals(200.0, r2.getUpperBound(), 0.001); // Scaling with a factor of 1 does not change the range r2 = Range.scale(r1, 1.0); assertEquals(r1, r2); try { Range.scale(null, 0.1); fail("Null value is accepted"); } catch (Exception e) { } try { Range.scale(r1, -0.5); fail("Negative factor accepted"); } catch (Exception e) { } } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { Range r1 = new Range(25.0, 133.42); Range r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } private static final double EPSILON = 0.0000000001; /** * Some checks for the combine method. */ @Test public void testCombine() { Range r1 = new Range(1.0, 2.0); Range r2 = new Range(1.5, 2.5); assertNull(Range.combine(null, null)); assertEquals(r1, Range.combine(r1, null)); assertEquals(r2, Range.combine(null, r2)); assertEquals(new Range(1.0, 2.5), Range.combine(r1, r2)); Range r3 = new Range(Double.NaN, 1.3); Range rr = Range.combine(r1, r3); assertTrue(Double.isNaN(rr.getLowerBound())); assertEquals(2.0, rr.getUpperBound(), EPSILON); Range r4 = new Range(1.7, Double.NaN); rr = Range.combine(r4, r1); assertEquals(1.0, rr.getLowerBound(), EPSILON); assertTrue(Double.isNaN(rr.getUpperBound())); } /** * Some checks for the combineIgnoringNaN() method. */ @Test public void testCombineIgnoringNaN() { Range r1 = new Range(1.0, 2.0); Range r2 = new Range(1.5, 2.5); assertNull(Range.combineIgnoringNaN(null, null)); assertEquals(r1, Range.combineIgnoringNaN(r1, null)); assertEquals(r2, Range.combineIgnoringNaN(null, r2)); assertEquals(new Range(1.0, 2.5), Range.combineIgnoringNaN(r1, r2)); Range r3 = new Range(Double.NaN, 1.3); Range rr = Range.combineIgnoringNaN(r1, r3); assertEquals(1.0, rr.getLowerBound(), EPSILON); assertEquals(2.0, rr.getUpperBound(), EPSILON); Range r4 = new Range(1.7, Double.NaN); rr = Range.combineIgnoringNaN(r4, r1); assertEquals(1.0, rr.getLowerBound(), EPSILON); assertEquals(2.0, rr.getUpperBound(), EPSILON); } @Test public void testIsNaNRange() { assertTrue(new Range(Double.NaN, Double.NaN).isNaNRange()); assertFalse(new Range(1.0, 2.0).isNaNRange()); assertFalse(new Range(Double.NaN, 2.0).isNaNRange()); assertFalse(new Range(1.0, Double.NaN).isNaNRange()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/RangeTypeTest.java000066400000000000000000000057561463604235500263370ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * RangeTypeTest.java * ------------------ * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link RangeType} class. */ public class RangeTypeTest { /** * Some checks for the equals() method. */ @Test public void testEquals() { assertEquals(RangeType.FULL, RangeType.FULL); assertEquals(RangeType.NEGATIVE, RangeType.NEGATIVE); assertEquals(RangeType.POSITIVE, RangeType.POSITIVE); assertNotEquals(RangeType.FULL, RangeType.NEGATIVE); assertNotEquals(RangeType.FULL, RangeType.POSITIVE); assertNotEquals(null, RangeType.FULL); assertNotEquals(RangeType.NEGATIVE, RangeType.FULL); assertNotEquals(RangeType.NEGATIVE, RangeType.POSITIVE); assertNotEquals(null, RangeType.NEGATIVE); assertNotEquals(RangeType.POSITIVE, RangeType.NEGATIVE); assertNotEquals(RangeType.POSITIVE, RangeType.FULL); assertNotEquals(null, RangeType.POSITIVE); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashCode() { RangeType r1 = RangeType.FULL; RangeType r2 = RangeType.FULL; assertEquals(r1, r2); int h1 = r1.hashCode(); int h2 = r2.hashCode(); assertEquals(h1, h2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { RangeType r1 = RangeType.FULL; RangeType r2 = TestUtils.serialised(r1); assertSame(r1, r2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/category/000077500000000000000000000000001463604235500245365ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/category/CategoryToPieDatasetTest.java000066400000000000000000000161521463604235500322720ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------ * CategoryToPieDatasetTests.java * ------------------------------ * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.category; import org.jfree.chart.TestUtils; import org.jfree.chart.util.TableOrder; import org.jfree.data.general.DefaultPieDataset; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.fail; /** * Tests for the {@link CategoryToPieDataset} class. */ public class CategoryToPieDatasetTest { /** * Some tests for the constructor. */ @Test public void testConstructor() { // try a null source CategoryToPieDataset p1 = new CategoryToPieDataset(null, TableOrder.BY_COLUMN, 0); assertNull(p1.getUnderlyingDataset()); assertEquals(p1.getItemCount(), 0); assertTrue(p1.getKeys().isEmpty()); assertNull(p1.getValue("R1")); } /** * Some checks for the getValue() method. */ @Test public void testGetValue() { DefaultCategoryDataset underlying = new DefaultCategoryDataset(); underlying.addValue(1.1, "R1", "C1"); underlying.addValue(2.2, "R1", "C2"); CategoryToPieDataset d1 = new CategoryToPieDataset(underlying, TableOrder.BY_ROW, 0); assertEquals(d1.getValue("C1"), 1.1); assertEquals(d1.getValue("C2"), 2.2); // check negative index throws exception try { /* Number n = */ d1.getValue(-1); fail("Expected IndexOutOfBoundsException."); } catch (IndexOutOfBoundsException e) { // this is expected } // check index == getItemCount() throws exception try { /* Number n = */ d1.getValue(d1.getItemCount()); fail("Expected IndexOutOfBoundsException."); } catch (IndexOutOfBoundsException e) { // this is expected } // test null source CategoryToPieDataset p1 = new CategoryToPieDataset(null, TableOrder.BY_COLUMN, 0); try { /* Number n = */ p1.getValue(0); fail("Expected IndexOutOfBoundsException."); } catch (IndexOutOfBoundsException e) { // this is expected } } /** * Some checks for the getKey(int) method. */ @Test public void testGetKey() { DefaultCategoryDataset underlying = new DefaultCategoryDataset(); underlying.addValue(1.1, "R1", "C1"); underlying.addValue(2.2, "R1", "C2"); CategoryToPieDataset d1 = new CategoryToPieDataset(underlying, TableOrder.BY_ROW, 0); assertEquals(d1.getKey(0), "C1"); assertEquals(d1.getKey(1), "C2"); // check negative index throws exception try { /* Number n = */ d1.getKey(-1); fail("Expected IndexOutOfBoundsException."); } catch (IndexOutOfBoundsException e) { // this is expected } // check index == getItemCount() throws exception try { /* Number n = */ d1.getKey(d1.getItemCount()); fail("Expected IndexOutOfBoundsException."); } catch (IndexOutOfBoundsException e) { // this is expected } // test null source CategoryToPieDataset p1 = new CategoryToPieDataset(null, TableOrder.BY_COLUMN, 0); try { /* Number n = */ p1.getKey(0); fail("Expected IndexOutOfBoundsException."); } catch (IndexOutOfBoundsException e) { // this is expected } } /** * Some checks for the getIndex() method. */ @Test public void testGetIndex() { DefaultCategoryDataset underlying = new DefaultCategoryDataset(); underlying.addValue(1.1, "R1", "C1"); underlying.addValue(2.2, "R1", "C2"); CategoryToPieDataset d1 = new CategoryToPieDataset(underlying, TableOrder.BY_ROW, 0); assertEquals(0, d1.getIndex("C1")); assertEquals(1, d1.getIndex("C2")); assertEquals(-1, d1.getIndex("XX")); // try null boolean pass = false; try { d1.getIndex(null); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * For datasets, the equals() method just checks keys and values. */ @Test public void testEquals() { DefaultCategoryDataset underlying = new DefaultCategoryDataset(); underlying.addValue(1.1, "R1", "C1"); underlying.addValue(2.2, "R1", "C2"); CategoryToPieDataset d1 = new CategoryToPieDataset(underlying, TableOrder.BY_COLUMN, 1); DefaultPieDataset d2 = new DefaultPieDataset(); d2.setValue("R1", 2.2); assertEquals(d1, d2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DefaultCategoryDataset underlying = new DefaultCategoryDataset(); underlying.addValue(1.1, "R1", "C1"); underlying.addValue(2.2, "R1", "C2"); CategoryToPieDataset d1 = new CategoryToPieDataset(underlying, TableOrder.BY_COLUMN, 1); CategoryToPieDataset d2 = TestUtils.serialised(d1); assertEquals(d1, d2); // regular equality for the datasets doesn't check the fields, just // the data values...so let's check some more things... assertEquals(d1.getUnderlyingDataset(), d2.getUnderlyingDataset()); assertEquals(d1.getExtractType(), d2.getExtractType()); assertEquals(d1.getExtractIndex(), d2.getExtractIndex()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/category/DefaultCategoryDatasetTest.java000066400000000000000000000247021463604235500326360ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------------- * DefaultCategoryDatasetTests.java * -------------------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.category; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.UnknownKeyException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link DefaultCategoryDataset} class. */ public class DefaultCategoryDatasetTest { /** * Some checks for the getValue() method. */ @Test public void testGetValue() { DefaultCategoryDataset d = new DefaultCategoryDataset(); d.addValue(1.0, "R1", "C1"); assertEquals(1.0, d.getValue("R1", "C1")); boolean pass = false; try { d.getValue("XX", "C1"); } catch (UnknownKeyException e) { pass = true; } assertTrue(pass); pass = false; try { d.getValue("R1", "XX"); } catch (UnknownKeyException e) { pass = true; } assertTrue(pass); } /** * A simple check for the getValue(int, int) method. */ @Test public void testGetValue2() { DefaultCategoryDataset d = new DefaultCategoryDataset(); boolean pass = false; try { /* Number n =*/ d.getValue(0, 0); } catch (IndexOutOfBoundsException e) { pass = true; } assertTrue(pass); } /** * Some checks for the incrementValue() method. */ @Test public void testIncrementValue() { DefaultCategoryDataset d = new DefaultCategoryDataset(); d.addValue(1.0, "R1", "C1"); d.incrementValue(2.0, "R1", "C1"); assertEquals(3.0, d.getValue("R1", "C1")); // increment a null value d.addValue(null, "R2", "C1"); d.incrementValue(2.0, "R2", "C1"); assertEquals(2.0, d.getValue("R2", "C1")); // increment an unknown row boolean pass = false; try { d.incrementValue(1.0, "XX", "C1"); } catch (UnknownKeyException e) { pass = true; } assertTrue(pass); // increment an unknown column pass = false; try { d.incrementValue(1.0, "R1", "XX"); } catch (UnknownKeyException e) { pass = true; } assertTrue(pass); } /** * Some tests for the getRowCount() method. */ @Test public void testGetRowCount() { DefaultCategoryDataset d = new DefaultCategoryDataset(); assertEquals(0, d.getRowCount()); d.addValue(1.0, "R1", "C1"); assertEquals(1, d.getRowCount()); d.addValue(1.0, "R2", "C1"); assertEquals(2, d.getRowCount()); d.addValue(2.0, "R2", "C1"); assertEquals(2, d.getRowCount()); // a row of all null values is still counted... d.setValue(null, "R2", "C1"); assertEquals(2, d.getRowCount()); } /** * Some tests for the getColumnCount() method. */ @Test public void testGetColumnCount() { DefaultCategoryDataset d = new DefaultCategoryDataset(); assertEquals(0, d.getColumnCount()); d.addValue(1.0, "R1", "C1"); assertEquals(1, d.getColumnCount()); d.addValue(1.0, "R1", "C2"); assertEquals(2, d.getColumnCount()); d.addValue(2.0, "R1", "C2"); assertEquals(2, d.getColumnCount()); // a column of all null values is still counted... d.setValue(null, "R1", "C2"); assertEquals(2, d.getColumnCount()); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { DefaultCategoryDataset d1 = new DefaultCategoryDataset(); d1.setValue(23.4, "R1", "C1"); DefaultCategoryDataset d2 = new DefaultCategoryDataset(); d2.setValue(23.4, "R1", "C1"); assertEquals(d1, d2); assertEquals(d2, d1); d1.setValue(36.5, "R1", "C2"); assertNotEquals(d1, d2); d2.setValue(36.5, "R1", "C2"); assertEquals(d1, d2); d1.setValue(null, "R1", "C1"); assertNotEquals(d1, d2); d2.setValue(null, "R1", "C1"); assertEquals(d1, d2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DefaultCategoryDataset d1 = new DefaultCategoryDataset(); d1.setValue(23.4, "R1", "C1"); DefaultCategoryDataset d2 = TestUtils.serialised(d1); assertEquals(d1, d2); } /** * Some checks for the addValue() method. */ @Test public void testAddValue() { DefaultCategoryDataset d1 = new DefaultCategoryDataset(); d1.addValue(null, "R1", "C1"); assertNull(d1.getValue("R1", "C1")); d1.addValue(1.0, "R2", "C1"); assertEquals(1.0, d1.getValue("R2", "C1")); boolean pass = false; try { d1.addValue(1.1, null, "C2"); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Some basic checks for the removeValue() method. */ @Test public void testRemoveValue() { DefaultCategoryDataset d = new DefaultCategoryDataset(); d.removeValue("R1", "C1"); d.addValue(1.0, "R1", "C1"); d.removeValue("R1", "C1"); assertEquals(0, d.getRowCount()); assertEquals(0, d.getColumnCount()); d.addValue(1.0, "R1", "C1"); d.addValue(2.0, "R2", "C1"); d.removeValue("R1", "C1"); assertEquals(2.0, d.getValue(0, 0)); boolean pass = false; try { d.removeValue(null, "C1"); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); pass = false; try { d.removeValue("R1", null); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Confirm that cloning works. * * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { DefaultCategoryDataset d1 = new DefaultCategoryDataset(); DefaultCategoryDataset d2 = (DefaultCategoryDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); // try a dataset with some content... d1.addValue(1.0, "R1", "C1"); d1.addValue(2.0, "R1", "C2"); d2 = (DefaultCategoryDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); // check that the clone doesn't share the same underlying arrays. d1.addValue(3.0, "R1", "C1"); assertNotEquals(d1, d2); d2.addValue(3.0, "R1", "C1"); assertEquals(d1, d2); } /** * Check that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { DefaultCategoryDataset d = new DefaultCategoryDataset(); assertTrue(d instanceof PublicCloneable); } private static final double EPSILON = 0.0000000001; /** * A test for bug 1835955. */ @Test public void testBug1835955() { DefaultCategoryDataset d = new DefaultCategoryDataset(); d.addValue(1.0, "R1", "C1"); d.addValue(2.0, "R2", "C2"); d.removeColumn("C2"); d.addValue(3.0, "R2", "C2"); assertEquals(3.0, d.getValue("R2", "C2").doubleValue(), EPSILON); } /** * Some checks for the removeColumn(Comparable) method. */ @Test public void testRemoveColumn() { DefaultCategoryDataset d = new DefaultCategoryDataset(); d.addValue(1.0, "R1", "C1"); d.addValue(2.0, "R2", "C2"); assertEquals(2, d.getColumnCount()); d.removeColumn("C2"); assertEquals(1, d.getColumnCount()); boolean pass = false; try { d.removeColumn("XXX"); } catch (UnknownKeyException e) { pass = true; } assertTrue(pass); pass = false; try { d.removeColumn(null); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Some checks for the removeRow(Comparable) method. */ @Test public void testRemoveRow() { DefaultCategoryDataset d = new DefaultCategoryDataset(); d.addValue(1.0, "R1", "C1"); d.addValue(2.0, "R2", "C2"); assertEquals(2, d.getRowCount()); d.removeRow("R2"); assertEquals(1, d.getRowCount()); boolean pass = false; try { d.removeRow("XXX"); } catch (UnknownKeyException e) { pass = true; } assertTrue(pass); pass = false; try { d.removeRow(null); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } } DefaultIntervalCategoryDatasetTest.java000066400000000000000000000375441463604235500342740ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/category/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------------------- * DefaultIntervalCategoryDatasetTest.java * --------------------------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.category; import java.util.List; import org.jfree.chart.TestUtils; import org.jfree.data.DataUtils; import org.jfree.data.UnknownKeyException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link DefaultIntervalCategoryDataset} class. */ public class DefaultIntervalCategoryDatasetTest { /** * Some checks for the getValue() method. */ @Test public void testGetValue() { double[] starts_S1 = new double[] {0.1, 0.2, 0.3}; double[] starts_S2 = new double[] {0.3, 0.4, 0.5}; double[] ends_S1 = new double[] {0.5, 0.6, 0.7}; double[] ends_S2 = new double[] {0.7, 0.8, 0.9}; double[][] starts = new double[][] {starts_S1, starts_S2}; double[][] ends = new double[][] {ends_S1, ends_S2}; DefaultIntervalCategoryDataset d = new DefaultIntervalCategoryDataset( new Comparable[] {"Series 1", "Series 2"}, new Comparable[] {"Category 1", "Category 2", "Category 3"}, DataUtils.createNumberArray2D(starts), DataUtils.createNumberArray2D(ends)); assertEquals(0.1, d.getStartValue("Series 1", "Category 1")); assertEquals(0.2, d.getStartValue("Series 1", "Category 2")); assertEquals(0.3, d.getStartValue("Series 1", "Category 3")); assertEquals(0.3, d.getStartValue("Series 2", "Category 1")); assertEquals(0.4, d.getStartValue("Series 2", "Category 2")); assertEquals(0.5, d.getStartValue("Series 2", "Category 3")); assertEquals(0.5, d.getEndValue("Series 1", "Category 1")); assertEquals(0.6, d.getEndValue("Series 1", "Category 2")); assertEquals(0.7, d.getEndValue("Series 1", "Category 3")); assertEquals(0.7, d.getEndValue("Series 2", "Category 1")); assertEquals(0.8, d.getEndValue("Series 2", "Category 2")); assertEquals(0.9, d.getEndValue("Series 2", "Category 3")); boolean pass = false; try { d.getValue("XX", "Category 1"); } catch (UnknownKeyException e) { pass = true; } assertTrue(pass); pass = false; try { d.getValue("Series 1", "XX"); } catch (UnknownKeyException e) { pass = true; } assertTrue(pass); } /** * Some tests for the getRowCount() method. */ @Test public void testGetRowAndColumnCount() { double[] starts_S1 = new double[] {0.1, 0.2, 0.3}; double[] starts_S2 = new double[] {0.3, 0.4, 0.5}; double[] ends_S1 = new double[] {0.5, 0.6, 0.7}; double[] ends_S2 = new double[] {0.7, 0.8, 0.9}; double[][] starts = new double[][] {starts_S1, starts_S2}; double[][] ends = new double[][] {ends_S1, ends_S2}; DefaultIntervalCategoryDataset d = new DefaultIntervalCategoryDataset(starts, ends); assertEquals(2, d.getRowCount()); assertEquals(3, d.getColumnCount()); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { double[] starts_S1A = new double[] {0.1, 0.2, 0.3}; double[] starts_S2A = new double[] {0.3, 0.4, 0.5}; double[] ends_S1A = new double[] {0.5, 0.6, 0.7}; double[] ends_S2A = new double[] {0.7, 0.8, 0.9}; double[][] startsA = new double[][] {starts_S1A, starts_S2A}; double[][] endsA = new double[][] {ends_S1A, ends_S2A}; DefaultIntervalCategoryDataset dA = new DefaultIntervalCategoryDataset(startsA, endsA); double[] starts_S1B = new double[] {0.1, 0.2, 0.3}; double[] starts_S2B = new double[] {0.3, 0.4, 0.5}; double[] ends_S1B = new double[] {0.5, 0.6, 0.7}; double[] ends_S2B = new double[] {0.7, 0.8, 0.9}; double[][] startsB = new double[][] {starts_S1B, starts_S2B}; double[][] endsB = new double[][] {ends_S1B, ends_S2B}; DefaultIntervalCategoryDataset dB = new DefaultIntervalCategoryDataset(startsB, endsB); assertEquals(dA, dB); assertEquals(dB, dA); // check that two empty datasets are equal DefaultIntervalCategoryDataset empty1 = new DefaultIntervalCategoryDataset(new double[0][0], new double[0][0]); DefaultIntervalCategoryDataset empty2 = new DefaultIntervalCategoryDataset(new double[0][0], new double[0][0]); assertEquals(empty1, empty2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { double[] starts_S1 = new double[] {0.1, 0.2, 0.3}; double[] starts_S2 = new double[] {0.3, 0.4, 0.5}; double[] ends_S1 = new double[] {0.5, 0.6, 0.7}; double[] ends_S2 = new double[] {0.7, 0.8, 0.9}; double[][] starts = new double[][] {starts_S1, starts_S2}; double[][] ends = new double[][] {ends_S1, ends_S2}; DefaultIntervalCategoryDataset d1 = new DefaultIntervalCategoryDataset(starts, ends); DefaultIntervalCategoryDataset d2 = TestUtils.serialised(d1); assertEquals(d1, d2); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { double[] starts_S1 = new double[] {0.1, 0.2, 0.3}; double[] starts_S2 = new double[] {0.3, 0.4, 0.5}; double[] ends_S1 = new double[] {0.5, 0.6, 0.7}; double[] ends_S2 = new double[] {0.7, 0.8, 0.9}; double[][] starts = new double[][] {starts_S1, starts_S2}; double[][] ends = new double[][] {ends_S1, ends_S2}; DefaultIntervalCategoryDataset d1 = new DefaultIntervalCategoryDataset( new Comparable[] {"Series 1", "Series 2"}, new Comparable[] {"Category 1", "Category 2", "Category 3"}, DataUtils.createNumberArray2D(starts), DataUtils.createNumberArray2D(ends)); DefaultIntervalCategoryDataset d2 = (DefaultIntervalCategoryDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); // check that the clone doesn't share the same underlying arrays. d1.setStartValue(0, "Category 1", 0.99); assertNotEquals(d1, d2); d2.setStartValue(0, "Category 1", 0.99); assertEquals(d1, d2); } /** * A check to ensure that an empty dataset can be cloned. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning2() throws CloneNotSupportedException { DefaultIntervalCategoryDataset d1 = new DefaultIntervalCategoryDataset(new double[0][0], new double[0][0]); DefaultIntervalCategoryDataset d2 = (DefaultIntervalCategoryDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); } /** * Some basic checks for the setStartValue() method. */ @Test public void testSetStartValue() { double[] starts_S1 = new double[] {0.1, 0.2, 0.3}; double[] starts_S2 = new double[] {0.3, 0.4, 0.5}; double[] ends_S1 = new double[] {0.5, 0.6, 0.7}; double[] ends_S2 = new double[] {0.7, 0.8, 0.9}; double[][] starts = new double[][] {starts_S1, starts_S2}; double[][] ends = new double[][] {ends_S1, ends_S2}; DefaultIntervalCategoryDataset d1 = new DefaultIntervalCategoryDataset( new Comparable[] {"Series 1", "Series 2"}, new Comparable[] {"Category 1", "Category 2", "Category 3"}, DataUtils.createNumberArray2D(starts), DataUtils.createNumberArray2D(ends)); d1.setStartValue(0, "Category 2", 99.9); assertEquals(99.9, d1.getStartValue("Series 1", "Category 2")); boolean pass = false; try { d1.setStartValue(-1, "Category 2", 99.9); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); pass = false; try { d1.setStartValue(2, "Category 2", 99.9); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Some basic checks for the setEndValue() method. */ @Test public void testSetEndValue() { double[] starts_S1 = new double[] {0.1, 0.2, 0.3}; double[] starts_S2 = new double[] {0.3, 0.4, 0.5}; double[] ends_S1 = new double[] {0.5, 0.6, 0.7}; double[] ends_S2 = new double[] {0.7, 0.8, 0.9}; double[][] starts = new double[][] {starts_S1, starts_S2}; double[][] ends = new double[][] {ends_S1, ends_S2}; DefaultIntervalCategoryDataset d1 = new DefaultIntervalCategoryDataset( new Comparable[] {"Series 1", "Series 2"}, new Comparable[] {"Category 1", "Category 2", "Category 3"}, DataUtils.createNumberArray2D(starts), DataUtils.createNumberArray2D(ends)); d1.setEndValue(0, "Category 2", 99.9); assertEquals(99.9, d1.getEndValue("Series 1", "Category 2")); boolean pass = false; try { d1.setEndValue(-1, "Category 2", 99.9); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); pass = false; try { d1.setEndValue(2, "Category 2", 99.9); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getSeriesCount() method. */ @Test public void testGetSeriesCount() { // check an empty dataset DefaultIntervalCategoryDataset empty = new DefaultIntervalCategoryDataset(new double[0][0], new double[0][0]); assertEquals(0, empty.getSeriesCount()); } /** * Some checks for the getCategoryCount() method. */ @Test public void testGetCategoryCount() { // check an empty dataset DefaultIntervalCategoryDataset empty = new DefaultIntervalCategoryDataset(new double[0][0], new double[0][0]); assertEquals(0, empty.getCategoryCount()); } /** * Some checks for the getSeriesIndex() method. */ @Test public void testGetSeriesIndex() { // check an empty dataset DefaultIntervalCategoryDataset empty = new DefaultIntervalCategoryDataset(new double[0][0], new double[0][0]); assertEquals(-1, empty.getSeriesIndex("ABC")); } /** * Some checks for the getRowIndex() method. */ @Test public void testGetRowIndex() { // check an empty dataset DefaultIntervalCategoryDataset empty = new DefaultIntervalCategoryDataset(new double[0][0], new double[0][0]); assertEquals(-1, empty.getRowIndex("ABC")); } /** * Some checks for the setSeriesKeys() method. */ @Test public void testSetSeriesKeys() { // check an empty dataset DefaultIntervalCategoryDataset empty = new DefaultIntervalCategoryDataset(new double[0][0], new double[0][0]); boolean pass = true; try { empty.setSeriesKeys(new String[0]); } catch (RuntimeException e) { pass = false; } assertTrue(pass); } /** * Some checks for the getCategoryIndex() method. */ @Test public void testGetCategoryIndex() { // check an empty dataset DefaultIntervalCategoryDataset empty = new DefaultIntervalCategoryDataset(new double[0][0], new double[0][0]); assertEquals(-1, empty.getCategoryIndex("ABC")); } /** * Some checks for the getColumnIndex() method. */ @Test public void testGetColumnIndex() { // check an empty dataset DefaultIntervalCategoryDataset empty = new DefaultIntervalCategoryDataset(new double[0][0], new double[0][0]); assertEquals(-1, empty.getColumnIndex("ABC")); } /** * Some checks for the setCategoryKeys() method. */ @Test public void testSetCategoryKeys() { // check an empty dataset DefaultIntervalCategoryDataset empty = new DefaultIntervalCategoryDataset(new double[0][0], new double[0][0]); boolean pass = true; try { empty.setCategoryKeys(new String[0]); } catch (RuntimeException e) { pass = false; } assertTrue(pass); } /** * Some checks for the getColumnKeys() method. */ @Test public void testGetColumnKeys() { // check an empty dataset DefaultIntervalCategoryDataset empty = new DefaultIntervalCategoryDataset(new double[0][0], new double[0][0]); List keys = empty.getColumnKeys(); assertEquals(0, keys.size()); } /** * Some checks for the getRowKeys() method. */ @Test public void testGetRowKeys() { // check an empty dataset DefaultIntervalCategoryDataset empty = new DefaultIntervalCategoryDataset(new double[0][0], new double[0][0]); List keys = empty.getRowKeys(); assertEquals(0, keys.size()); } /** * Some checks for the getColumnCount() method. */ @Test public void testGetColumnCount() { // check an empty dataset DefaultIntervalCategoryDataset empty = new DefaultIntervalCategoryDataset(new double[0][0], new double[0][0]); assertEquals(0, empty.getColumnCount()); } /** * Some checks for the getRowCount() method. */ @Test public void testGetRowCount() { // check an empty dataset DefaultIntervalCategoryDataset empty = new DefaultIntervalCategoryDataset(new double[0][0], new double[0][0]); assertEquals(0, empty.getColumnCount()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/category/SlidingCategoryDatasetTest.java000066400000000000000000000220611463604235500326370ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------- * SlidingCategoryDatasetTest.java * ------------------------------- * (C) Copyright 2008-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.category; import java.util.List; import org.jfree.chart.TestUtils; import org.jfree.data.UnknownKeyException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link SlidingCategoryDataset} class. */ public class SlidingCategoryDatasetTest { /** * Some checks for the equals() method. */ @Test public void testEquals() { DefaultCategoryDataset u1 = new DefaultCategoryDataset(); u1.addValue(1.0, "R1", "C1"); u1.addValue(2.0, "R1", "C2"); SlidingCategoryDataset d1 = new SlidingCategoryDataset(u1, 0, 5); DefaultCategoryDataset u2 = new DefaultCategoryDataset(); u2.addValue(1.0, "R1", "C1"); u2.addValue(2.0, "R1", "C2"); SlidingCategoryDataset d2 = new SlidingCategoryDataset(u2, 0, 5); assertEquals(d1, d2); d1.setFirstCategoryIndex(1); assertNotEquals(d1, d2); d2.setFirstCategoryIndex(1); assertEquals(d1, d2); d1.setMaximumCategoryCount(99); assertNotEquals(d1, d2); d2.setMaximumCategoryCount(99); assertEquals(d1, d2); u1.addValue(3.0, "R1", "C3"); assertNotEquals(d1, d2); u2.addValue(3.0, "R1", "C3"); assertEquals(d1, d2); } /** * Confirm that cloning works. * * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { DefaultCategoryDataset u1 = new DefaultCategoryDataset(); u1.addValue(1.0, "R1", "C1"); u1.addValue(2.0, "R1", "C2"); SlidingCategoryDataset d1 = new SlidingCategoryDataset(u1, 0, 5); SlidingCategoryDataset d2; d2 = (SlidingCategoryDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); // basic check for independence u1.addValue(3.0, "R1", "C3"); assertNotEquals(d1, d2); DefaultCategoryDataset u2 = (DefaultCategoryDataset) d2.getUnderlyingDataset(); u2.addValue(3.0, "R1", "C3"); assertEquals(d1, d2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DefaultCategoryDataset u1 = new DefaultCategoryDataset(); u1.addValue(1.0, "R1", "C1"); u1.addValue(2.0, "R1", "C2"); SlidingCategoryDataset d1 = new SlidingCategoryDataset(u1, 0, 5); SlidingCategoryDataset d2 = TestUtils.serialised(d1); assertEquals(d1, d2); // basic check for independence u1.addValue(3.0, "R1", "C3"); assertNotEquals(d1, d2); DefaultCategoryDataset u2 = (DefaultCategoryDataset) d2.getUnderlyingDataset(); u2.addValue(3.0, "R1", "C3"); assertEquals(d1, d2); } /** * Some checks for the getColumnCount() method. */ @Test public void testGetColumnCount() { DefaultCategoryDataset underlying = new DefaultCategoryDataset(); SlidingCategoryDataset dataset = new SlidingCategoryDataset(underlying, 10, 2); assertEquals(0, dataset.getColumnCount()); underlying.addValue(1.0, "R1", "C1"); assertEquals(0, dataset.getColumnCount()); underlying.addValue(1.0, "R1", "C2"); assertEquals(0, dataset.getColumnCount()); dataset.setFirstCategoryIndex(0); assertEquals(2, dataset.getColumnCount()); underlying.addValue(1.0, "R1", "C3"); assertEquals(2, dataset.getColumnCount()); dataset.setFirstCategoryIndex(2); assertEquals(1, dataset.getColumnCount()); underlying.clear(); assertEquals(0, dataset.getColumnCount()); } /** * Some checks for the getRowCount() method. */ @Test public void testGetRowCount() { DefaultCategoryDataset underlying = new DefaultCategoryDataset(); SlidingCategoryDataset dataset = new SlidingCategoryDataset(underlying, 10, 5); assertEquals(0, dataset.getRowCount()); underlying.addValue(1.0, "R1", "C1"); assertEquals(1, dataset.getRowCount()); underlying.clear(); assertEquals(0, dataset.getRowCount()); } /** * Some checks for the getColumnIndex() method. */ @Test public void testGetColumnIndex() { DefaultCategoryDataset underlying = new DefaultCategoryDataset(); underlying.addValue(1.0, "R1", "C1"); underlying.addValue(2.0, "R1", "C2"); underlying.addValue(3.0, "R1", "C3"); underlying.addValue(4.0, "R1", "C4"); SlidingCategoryDataset dataset = new SlidingCategoryDataset(underlying, 1, 2); assertEquals(-1, dataset.getColumnIndex("C1")); assertEquals(0, dataset.getColumnIndex("C2")); assertEquals(1, dataset.getColumnIndex("C3")); assertEquals(-1, dataset.getColumnIndex("C4")); } /** * Some checks for the getRowIndex() method. */ @Test public void testGetRowIndex() { DefaultCategoryDataset underlying = new DefaultCategoryDataset(); underlying.addValue(1.0, "R1", "C1"); underlying.addValue(2.0, "R2", "C1"); underlying.addValue(3.0, "R3", "C1"); underlying.addValue(4.0, "R4", "C1"); SlidingCategoryDataset dataset = new SlidingCategoryDataset(underlying, 1, 2); assertEquals(0, dataset.getRowIndex("R1")); assertEquals(1, dataset.getRowIndex("R2")); assertEquals(2, dataset.getRowIndex("R3")); assertEquals(3, dataset.getRowIndex("R4")); } /** * Some checks for the getValue() method. */ @Test public void testGetValue() { DefaultCategoryDataset underlying = new DefaultCategoryDataset(); underlying.addValue(1.0, "R1", "C1"); underlying.addValue(2.0, "R1", "C2"); underlying.addValue(3.0, "R1", "C3"); underlying.addValue(4.0, "R1", "C4"); SlidingCategoryDataset dataset = new SlidingCategoryDataset(underlying, 1, 2); assertEquals(2.0, dataset.getValue("R1", "C2")); assertEquals(3.0, dataset.getValue("R1", "C3")); boolean pass = false; try { dataset.getValue("R1", "C1"); } catch (UnknownKeyException e) { pass = true; } assertTrue(pass); pass = false; try { dataset.getValue("R1", "C4"); } catch (UnknownKeyException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getColumnKeys() method. */ @Test public void testGetColumnKeys() { DefaultCategoryDataset underlying = new DefaultCategoryDataset(); underlying.addValue(1.0, "R1", "C1"); underlying.addValue(2.0, "R1", "C2"); underlying.addValue(3.0, "R1", "C3"); underlying.addValue(4.0, "R1", "C4"); SlidingCategoryDataset dataset = new SlidingCategoryDataset(underlying, 1, 2); List keys = dataset.getColumnKeys(); assertTrue(keys.contains("C2")); assertTrue(keys.contains("C3")); assertEquals(2, keys.size()); dataset.setFirstCategoryIndex(3); keys = dataset.getColumnKeys(); assertTrue(keys.contains("C4")); assertEquals(1, keys.size()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/flow/000077500000000000000000000000001463604235500236705ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/flow/DefaultFlowDatasetTest.java000066400000000000000000000101461463604235500311170ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * DefaultFlowDatasetTest.java * --------------------------- * (C) Copyright 2022, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.flow; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link DefaultFlowDataset} class. */ public class DefaultFlowDatasetTest { /** * Some checks for the getValue() method. */ @Test public void testGetFlow() { DefaultFlowDataset d = new DefaultFlowDataset<>(); d.setFlow(0, "A", "Z", 1.5); assertEquals(1.5, d.getFlow(0, "A", "Z")); } /** * Some tests for the getStageCount() method. */ @Test public void testGetStageCount() { DefaultFlowDataset d = new DefaultFlowDataset<>(); assertEquals(1, d.getStageCount()); d.setFlow(0, "A", "Z", 11.1); assertEquals(1, d.getStageCount()); // a row of all null values is still counted... d.setFlow(1, "Z", "P", 5.0); assertEquals(2, d.getStageCount()); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { DefaultFlowDataset d1 = new DefaultFlowDataset<>(); DefaultFlowDataset d2 = new DefaultFlowDataset<>(); assertEquals(d1, d2); d1.setFlow(0, "A", "Z", 1.0); assertNotEquals(d1, d2); d2.setFlow(0, "A", "Z", 1.0); assertEquals(d1, d2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DefaultFlowDataset d1 = new DefaultFlowDataset<>(); d1.setFlow(0, "A", "Z", 1.0); DefaultFlowDataset d2 = TestUtils.serialised(d1); assertEquals(d1, d2); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { DefaultFlowDataset d1 = new DefaultFlowDataset<>(); d1.setFlow(0, "A", "Z", 1.0); DefaultFlowDataset d2 = (DefaultFlowDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); // check that the clone doesn't share the same underlying arrays. d1.setFlow(0, "A", "Y", 8.0); assertNotEquals(d1, d2); d2.setFlow(0, "A", "Y", 8.0); assertEquals(d1, d2); } /** * Check that this class implements PublicCloneable. */ @Test public void testPublicCloneable() { DefaultFlowDataset d = new DefaultFlowDataset<>(); assertTrue(d instanceof PublicCloneable); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/flow/FlowKeyTest.java000066400000000000000000000054701463604235500267610ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * FlowKeyTest.java * ---------------- * (C) Copyright 2022, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.flow; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link FlowKey} class. */ public class FlowKeyTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { FlowKey k1 = new FlowKey<>(0, "A", "B"); FlowKey k2 = new FlowKey<>(0, "A", "B"); assertEquals(k1, k2); assertEquals(k2, k1); k1 = new FlowKey<>(1, "A", "B"); assertNotEquals(k1, k2); k2 = new FlowKey<>(1, "A", "B"); assertEquals(k1, k2); k1 = new FlowKey<>(1, "C", "B"); assertNotEquals(k1, k2); k2 = new FlowKey<>(1, "C", "B"); assertEquals(k1, k2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { FlowKey k1 = new FlowKey<>(0, "A", "B"); FlowKey k2 = (FlowKey) k1.clone(); assertNotSame(k1, k2); assertSame(k1.getClass(), k2.getClass()); assertEquals(k1, k2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { FlowKey k1 = new FlowKey<>(1, "S1", "D1"); FlowKey k2 = TestUtils.serialised(k1); assertEquals(k1, k2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/flow/NodeKeyTest.java000066400000000000000000000055021463604235500267330ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * NodeKeyTest.java * ---------------- * (C) Copyright 2022, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.flow; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link NodeKey} class. */ public class NodeKeyTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { NodeKey k1 = new NodeKey<>(0, "A"); NodeKey k2 = new NodeKey<>(0, "A"); assertEquals(k1, k2); assertEquals(k2, k1); k1 = new NodeKey<>(1, "A"); assertNotEquals(k1, k2); k2 = new NodeKey<>(1, "A"); assertEquals(k1, k2); k1 = new NodeKey<>(1, "B"); assertNotEquals(k1, k2); k2 = new NodeKey<>(1, "B"); assertEquals(k1, k2); } /** * Confirm that cloning works. * * @throws CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { NodeKey k1 = new NodeKey<>(2, "A"); NodeKey k2 = (NodeKey) k1.clone(); assertNotSame(k1, k2); assertSame(k1.getClass(), k2.getClass()); assertEquals(k1, k2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { NodeKey k1 = new NodeKey<>(1, "S1"); NodeKey k2 = TestUtils.serialised(k1); assertEquals(k1, k2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/function/000077500000000000000000000000001463604235500245465ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/function/LineFunction2DTest.java000066400000000000000000000060741463604235500310430ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * LineFunction2DTest.java * ----------------------- * (C) Copyright 2009-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.function; import org.jfree.chart.TestUtils; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import org.junit.jupiter.api.Test; /** * Tests for the {@link LineFunction2D} class. */ public class LineFunction2DTest { private static final double EPSILON = 0.000000001; /** * Some tests for the constructor. */ @Test public void testConstructor() { LineFunction2D f = new LineFunction2D(1.0, 2.0); assertEquals(1.0, f.getIntercept(), EPSILON); assertEquals(2.0, f.getSlope(), EPSILON); } /** * For datasets, the equals() method just checks keys and values. */ @Test public void testEquals() { LineFunction2D f1 = new LineFunction2D(1.0, 2.0); LineFunction2D f2 = new LineFunction2D(1.0, 2.0); assertEquals(f1, f2); f1 = new LineFunction2D(2.0, 3.0); assertNotEquals(f1, f2); f2 = new LineFunction2D(2.0, 3.0); assertEquals(f1, f2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { LineFunction2D f1 = new LineFunction2D(1.0, 2.0); LineFunction2D f2 = TestUtils.serialised(f1); assertEquals(f1, f2); } /** * Objects that are equal should have the same hash code otherwise FindBugs * will tell on us... */ @Test public void testHashCode() { LineFunction2D f1 = new LineFunction2D(1.0, 2.0); LineFunction2D f2 = new LineFunction2D(1.0, 2.0); assertEquals(f1.hashCode(), f2.hashCode()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/function/NormalDistributionFunction2DTest.java000066400000000000000000000066721463604235500340100ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------------- * NormalDistributionFunction2DTest.java * ------------------------------------- * (C) Copyright 2009-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.function; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; /** * Tests for the {@link NormalDistributionFunction2D} class. */ public class NormalDistributionFunction2DTest { private static final double EPSILON = 0.000000001; /** * Some tests for the constructor. */ @Test public void testConstructor() { NormalDistributionFunction2D f = new NormalDistributionFunction2D(1.0, 2.0); assertEquals(1.0, f.getMean(), EPSILON); assertEquals(2.0, f.getStandardDeviation(), EPSILON); } /** * For datasets, the equals() method just checks keys and values. */ @Test public void testEquals() { NormalDistributionFunction2D f1 = new NormalDistributionFunction2D(1.0, 2.0); NormalDistributionFunction2D f2 = new NormalDistributionFunction2D(1.0, 2.0); assertEquals(f1, f2); f1 = new NormalDistributionFunction2D(2.0, 3.0); assertNotEquals(f1, f2); f2 = new NormalDistributionFunction2D(2.0, 3.0); assertEquals(f1, f2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { NormalDistributionFunction2D f1 = new NormalDistributionFunction2D(1.0, 2.0); NormalDistributionFunction2D f2 = TestUtils.serialised(f1); assertEquals(f1, f2); } /** * Objects that are equal should have the same hash code otherwise FindBugs * will tell on us... */ @Test public void testHashCode() { NormalDistributionFunction2D f1 = new NormalDistributionFunction2D(1.0, 2.0); NormalDistributionFunction2D f2 = new NormalDistributionFunction2D(1.0, 2.0); assertEquals(f1.hashCode(), f2.hashCode()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/function/PolynomialFunction2DTest.java000066400000000000000000000102561463604235500322740ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------ * PolynomialFunction2DTests.java * ------------------------------ * (C) Copyright 2022, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.function; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link PolynomialFunction2D} class. */ public class PolynomialFunction2DTest { /** * Some tests for the constructor. */ @Test public void testConstructor() { PolynomialFunction2D f = new PolynomialFunction2D(new double[] {1.0, 2.0}); assertArrayEquals(new double[]{1.0, 2.0}, f.getCoefficients()); boolean pass = false; try { f = new PolynomialFunction2D(null); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getCoefficients() method. */ @Test public void testGetCoefficients() { PolynomialFunction2D f = new PolynomialFunction2D(new double[] {1.0, 2.0}); double[] c = f.getCoefficients(); assertArrayEquals(new double[]{1.0, 2.0}, c); // make sure that modifying the returned array doesn't change the // function c[0] = 99.9; assertArrayEquals(new double[]{1.0, 2.0}, f.getCoefficients()); } /** * Some checks for the getOrder() method. */ @Test public void testGetOrder() { PolynomialFunction2D f = new PolynomialFunction2D(new double[] {1.0, 2.0}); assertEquals(1, f.getOrder()); } /** * For datasets, the equals() method just checks keys and values. */ @Test public void testEquals() { PolynomialFunction2D f1 = new PolynomialFunction2D(new double[] {1.0, 2.0}); PolynomialFunction2D f2 = new PolynomialFunction2D(new double[] {1.0, 2.0}); assertEquals(f1, f2); f1 = new PolynomialFunction2D(new double[] {2.0, 3.0}); assertNotEquals(f1, f2); f2 = new PolynomialFunction2D(new double[] {2.0, 3.0}); assertEquals(f1, f2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { PolynomialFunction2D f1 = new PolynomialFunction2D(new double[] {1.0, 2.0}); PolynomialFunction2D f2 = TestUtils.serialised(f1); assertEquals(f1, f2); } /** * Objects that are equal should have the same hash code otherwise FindBugs * will tell on us... */ @Test public void testHashCode() { PolynomialFunction2D f1 = new PolynomialFunction2D(new double[] {1.0, 2.0}); PolynomialFunction2D f2 = new PolynomialFunction2D(new double[] {1.0, 2.0}); assertEquals(f1.hashCode(), f2.hashCode()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/function/PowerFunction2DTest.java000066400000000000000000000057711463604235500312530ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * PowerFunction2DTest.java * ------------------------ * (C) Copyright 2009-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.function; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link PowerFunction2D} class. */ public class PowerFunction2DTest { private static final double EPSILON = 0.000000001; /** * Some tests for the constructor. */ @Test public void testConstructor() { PowerFunction2D f = new PowerFunction2D(1.0, 2.0); assertEquals(1.0, f.getA(), EPSILON); assertEquals(2.0, f.getB(), EPSILON); } /** * For datasets, the equals() method just checks keys and values. */ @Test public void testEquals() { PowerFunction2D f1 = new PowerFunction2D(1.0, 2.0); PowerFunction2D f2 = new PowerFunction2D(1.0, 2.0); assertEquals(f1, f2); f1 = new PowerFunction2D(2.0, 3.0); assertNotEquals(f1, f2); f2 = new PowerFunction2D(2.0, 3.0); assertEquals(f1, f2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { PowerFunction2D f1 = new PowerFunction2D(1.0, 2.0); PowerFunction2D f2 = TestUtils.serialised(f1); assertEquals(f1, f2); } /** * Objects that are equal should have the same hash code otherwise FindBugs * will tell on us... */ @Test public void testHashCode() { PowerFunction2D f1 = new PowerFunction2D(1.0, 2.0); PowerFunction2D f2 = new PowerFunction2D(1.0, 2.0); assertEquals(f1.hashCode(), f2.hashCode()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/gantt/000077500000000000000000000000001463604235500240365ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/gantt/SlidingGanttCategoryDatasetTest.java000066400000000000000000000141161463604235500331370ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------------ * SlidingGanttCategoryDatasetTest.java * ------------------------------------ * (C) Copyright 2008-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.data.gantt; import java.util.Date; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.data.general.SeriesChangeEvent; import org.junit.jupiter.api.Test; import javax.swing.event.EventListenerList; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link SlidingGanttCategoryDataset} class. */ public class SlidingGanttCategoryDatasetTest { @Test public void testEqualsHashCode() { EqualsVerifier.forClass(SlidingGanttCategoryDataset.class) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .withPrefabValues(EventListenerList.class, new EventListenerList(), new EventListenerList()) .withRedefinedSuperclass() .verify(); } /** * Some checks for the equals() method. */ @Test public void testEquals() { TaskSeries s1 = new TaskSeries("Series"); s1.add(new Task("Task 1", new Date(0L), new Date(1L))); s1.add(new Task("Task 2", new Date(10L), new Date(11L))); s1.add(new Task("Task 3", new Date(20L), new Date(21L))); TaskSeriesCollection u1 = new TaskSeriesCollection(); u1.add(s1); SlidingGanttCategoryDataset d1 = new SlidingGanttCategoryDataset( u1, 0, 5); TaskSeries s2 = new TaskSeries("Series"); s2.add(new Task("Task 1", new Date(0L), new Date(1L))); s2.add(new Task("Task 2", new Date(10L), new Date(11L))); s2.add(new Task("Task 3", new Date(20L), new Date(21L))); TaskSeriesCollection u2 = new TaskSeriesCollection(); u2.add(s2); SlidingGanttCategoryDataset d2 = new SlidingGanttCategoryDataset( u2, 0, 5); assertEquals(d1, d2); d1.setFirstCategoryIndex(1); assertNotEquals(d1, d2); d2.setFirstCategoryIndex(1); assertEquals(d1, d2); d1.setMaximumCategoryCount(99); assertNotEquals(d1, d2); d2.setMaximumCategoryCount(99); assertEquals(d1, d2); s1.add(new Task("Task 2", new Date(10L), new Date(11L))); assertNotEquals(d1, d2); s2.add(new Task("Task 2", new Date(10L), new Date(11L))); assertEquals(d1, d2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { TaskSeries s1 = new TaskSeries("Series"); s1.add(new Task("Task 1", new Date(0L), new Date(1L))); TaskSeriesCollection u1 = new TaskSeriesCollection(); u1.add(s1); SlidingGanttCategoryDataset d1 = new SlidingGanttCategoryDataset( u1, 0, 5); SlidingGanttCategoryDataset d2 = (SlidingGanttCategoryDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); // basic check for independence s1.add(new Task("Task 2", new Date(10L), new Date(11L))); assertNotEquals(d1, d2); TaskSeriesCollection u2 = (TaskSeriesCollection) d2.getUnderlyingDataset(); TaskSeries s2 = u2.getSeries("Series"); s2.add(new Task("Task 2", new Date(10L), new Date(11L))); // equals checks the keys - make sure they get updated u1.seriesChanged(new SeriesChangeEvent(this)); u2.seriesChanged(new SeriesChangeEvent(this)); assertEquals(d1, d2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { TaskSeries s1 = new TaskSeries("Series"); s1.add(new Task("Task 1", new Date(0L), new Date(1L))); TaskSeriesCollection c1 = new TaskSeriesCollection(); c1.add(s1); SlidingGanttCategoryDataset d1 = new SlidingGanttCategoryDataset( c1, 0, 5); SlidingGanttCategoryDataset d2 = TestUtils.serialised(d1); assertEquals(d1, d2); // basic check for independence s1.add(new Task("Task 2", new Date(10L), new Date(11L))); assertNotEquals(d1, d2); TaskSeriesCollection c2 = (TaskSeriesCollection) d2.getUnderlyingDataset(); TaskSeries s2 = c2.getSeries("Series"); s2.add(new Task("Task 2", new Date(10L), new Date(11L))); assertEquals(d1, d2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/gantt/TaskSeriesCollectionTest.java000066400000000000000000000547661463604235500316540ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------- * TaskSeriesCollectionTest.java * ----------------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.data.gantt; import java.util.Date; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.data.general.SeriesChangeEvent; import org.jfree.data.time.SimpleTimePeriod; import org.junit.jupiter.api.Test; import javax.swing.event.EventListenerList; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link TaskSeriesCollection} class. */ public class TaskSeriesCollectionTest { @Test public void testEqualsHashCode() { EqualsVerifier.forClass(TaskSeriesCollection.class) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .withPrefabValues(EventListenerList.class, new EventListenerList(), new EventListenerList()) .withPrefabValues(Task.class, new Task("T1", new Date(1), new Date(2)), new Task("T2", new Date(3), new Date(4))) .withRedefinedSuperclass() .verify(); } /** * Creates a sample collection for testing purposes. * * @return A sample collection. */ private TaskSeriesCollection createCollection1() { TaskSeriesCollection result = new TaskSeriesCollection(); TaskSeries s1 = new TaskSeries("S1"); s1.add(new Task("Task 1", new Date(1), new Date(2))); s1.add(new Task("Task 2", new Date(3), new Date(4))); result.add(s1); TaskSeries s2 = new TaskSeries("S2"); s2.add(new Task("Task 3", new Date(5), new Date(6))); result.add(s2); return result; } /** * Creates a sample collection for testing purposes. * * @return A sample collection. */ private TaskSeriesCollection createCollection2() { TaskSeriesCollection result = new TaskSeriesCollection(); TaskSeries s1 = new TaskSeries("S1"); Task t1 = new Task("Task 1", new Date(10), new Date(20)); t1.addSubtask(new Task("Task 1A", new Date(10), new Date(15))); t1.addSubtask(new Task("Task 1B", new Date(16), new Date(20))); t1.setPercentComplete(0.10); s1.add(t1); Task t2 = new Task("Task 2", new Date(30), new Date(40)); t2.addSubtask(new Task("Task 2A", new Date(30), new Date(35))); t2.addSubtask(new Task("Task 2B", new Date(36), new Date(40))); t2.setPercentComplete(0.20); s1.add(t2); result.add(s1); TaskSeries s2 = new TaskSeries("S2"); Task t3 = new Task("Task 3", new Date(50), new Date(60)); t3.addSubtask(new Task("Task 3A", new Date(50), new Date(55))); t3.addSubtask(new Task("Task 3B", new Date(56), new Date(60))); t3.setPercentComplete(0.30); s2.add(t3); result.add(s2); return result; } /** * Creates a sample collection for testing purposes. * * @return A sample collection. */ private TaskSeriesCollection createCollection3() { // define subtasks Task sub1 = new Task("Sub1", new Date(11), new Date(111)); Task sub2 = new Task("Sub2", new Date(22), new Date(222)); Task sub3 = new Task("Sub3", new Date(33), new Date(333)); Task sub4 = new Task("Sub4", new Date(44), new Date(444)); Task sub5 = new Task("Sub5", new Date(55), new Date(555)); Task sub6 = new Task("Sub6", new Date(66), new Date(666)); sub1.setPercentComplete(0.111); sub2.setPercentComplete(0.222); sub3.setPercentComplete(0.333); sub4.setPercentComplete(0.444); sub5.setPercentComplete(0.555); sub6.setPercentComplete(0.666); TaskSeries seriesA = new TaskSeries("Series A"); Task taskA1 = new Task("Task 1", new SimpleTimePeriod(new Date(100), new Date(200))); taskA1.setPercentComplete(0.1); taskA1.addSubtask(sub1); Task taskA2 = new Task("Task 2", new SimpleTimePeriod(new Date(220), new Date(350))); taskA2.setPercentComplete(0.2); taskA2.addSubtask(sub2); taskA2.addSubtask(sub3); seriesA.add(taskA1); seriesA.add(taskA2); TaskSeries seriesB = new TaskSeries("Series B"); // note that we don't define taskB1 Task taskB2 = new Task("Task 2", new SimpleTimePeriod(new Date(2220), new Date(3350))); taskB2.setPercentComplete(0.3); taskB2.addSubtask(sub4); taskB2.addSubtask(sub5); taskB2.addSubtask(sub6); seriesB.add(taskB2); TaskSeriesCollection tsc = new TaskSeriesCollection(); tsc.add(seriesA); tsc.add(seriesB); return tsc; } /** * A test for the getSeriesCount() method. */ @Test public void testGetSeriesCount() { TaskSeriesCollection c = createCollection1(); assertEquals(2, c.getSeriesCount()); } /** * Some tests for the getSeriesKey() method. */ @Test public void testGetSeriesKey() { TaskSeriesCollection c = createCollection1(); assertEquals("S1", c.getSeriesKey(0)); assertEquals("S2", c.getSeriesKey(1)); } /** * A test for the getRowCount() method. */ @Test public void testGetRowCount() { TaskSeriesCollection c = createCollection1(); assertEquals(2, c.getRowCount()); } /** * Some tests for the getRowKey() method. */ @Test public void testGetRowKey() { TaskSeriesCollection c = createCollection1(); assertEquals("S1", c.getRowKey(0)); assertEquals("S2", c.getRowKey(1)); } /** * Some tests for the getRowIndex() method. */ @Test public void testGetRowIndex() { TaskSeriesCollection c = createCollection1(); assertEquals(0, c.getRowIndex("S1")); assertEquals(1, c.getRowIndex("S2")); } /** * Some tests for the getValue() method. */ @Test public void testGetValue() { TaskSeriesCollection c = createCollection1(); assertEquals(1L, c.getValue("S1", "Task 1")); assertEquals(3L, c.getValue("S1", "Task 2")); assertEquals(5L, c.getValue("S2", "Task 3")); assertEquals(1L, c.getValue(0, 0)); assertEquals(3L, c.getValue(0, 1)); assertNull(c.getValue(0, 2)); assertNull(c.getValue(1, 0)); assertNull(c.getValue(1, 1)); assertEquals(5L, c.getValue(1, 2)); } /** * Some tests for the getStartValue() method. */ @Test public void testGetStartValue() { TaskSeriesCollection c = createCollection1(); assertEquals(1L, c.getStartValue("S1", "Task 1")); assertEquals(3L, c.getStartValue("S1", "Task 2")); assertEquals(5L, c.getStartValue("S2", "Task 3")); assertEquals(1L, c.getStartValue(0, 0)); assertEquals(3L, c.getStartValue(0, 1)); assertNull(c.getStartValue(0, 2)); assertNull(c.getStartValue(1, 0)); assertNull(c.getStartValue(1, 1)); assertEquals(5L, c.getStartValue(1, 2)); // test collection 3, which doesn't define all tasks in all series TaskSeriesCollection c3 = createCollection3(); assertEquals(100L, c3.getStartValue(0, 0)); assertEquals(220L, c3.getStartValue(0, 1)); assertNull(c3.getStartValue(1, 0)); assertEquals(2220L, c3.getStartValue(1, 1)); } /** * Some tests for the getStartValue() method for sub-intervals. */ @Test public void testGetStartValue2() { TaskSeriesCollection c = createCollection2(); assertEquals(10L, c.getStartValue("S1", "Task 1", 0)); assertEquals(16L, c.getStartValue("S1", "Task 1", 1)); assertEquals(30L, c.getStartValue("S1", "Task 2", 0)); assertEquals(36L, c.getStartValue("S1", "Task 2", 1)); assertEquals(50L, c.getStartValue("S2", "Task 3", 0)); assertEquals(56L, c.getStartValue("S2", "Task 3", 1)); assertEquals(10L, c.getStartValue(0, 0, 0)); assertEquals(16L, c.getStartValue(0, 0, 1)); assertEquals(30L, c.getStartValue(0, 1, 0)); assertEquals(36L, c.getStartValue(0, 1, 1)); assertEquals(50L, c.getStartValue(1, 2, 0)); assertEquals(56L, c.getStartValue(1, 2, 1)); TaskSeriesCollection c3 = createCollection3(); assertEquals(11L, c3.getStartValue(0, 0, 0)); assertEquals(22L, c3.getStartValue(0, 1, 0)); assertEquals(33L, c3.getStartValue(0, 1, 1)); assertNull(c3.getStartValue(1, 0, 0)); assertEquals(44L, c3.getStartValue(1, 1, 0)); assertEquals(55L, c3.getStartValue(1, 1, 1)); assertEquals(66L, c3.getStartValue(1, 1, 2)); } /** * A check for a null task duration. */ @Test public void testGetStartValue3() { TaskSeriesCollection c = new TaskSeriesCollection(); TaskSeries s = new TaskSeries("Series 1"); s.add(new Task("Task with null duration", null)); c.add(s); Number millis = c.getStartValue("Series 1", "Task with null duration"); assertNull(millis); } /** * Some tests for the getEndValue() method. */ @Test public void testGetEndValue() { TaskSeriesCollection c = createCollection1(); assertEquals(2L, c.getEndValue("S1", "Task 1")); assertEquals(4L, c.getEndValue("S1", "Task 2")); assertEquals(6L, c.getEndValue("S2", "Task 3")); assertEquals(2L, c.getEndValue(0, 0)); assertEquals(4L, c.getEndValue(0, 1)); assertNull(c.getEndValue(0, 2)); assertNull(c.getEndValue(1, 0)); assertNull(c.getEndValue(1, 1)); assertEquals(6L, c.getEndValue(1, 2)); // test collection 3, which doesn't define all tasks in all series TaskSeriesCollection c3 = createCollection3(); assertEquals(200L, c3.getEndValue(0, 0)); assertEquals(350L, c3.getEndValue(0, 1)); assertNull(c3.getEndValue(1, 0)); assertEquals(3350L, c3.getEndValue(1, 1)); } /** * Some tests for the getEndValue() method for sub-intervals. */ @Test public void testGetEndValue2() { TaskSeriesCollection c = createCollection2(); assertEquals(15L, c.getEndValue("S1", "Task 1", 0)); assertEquals(20L, c.getEndValue("S1", "Task 1", 1)); assertEquals(35L, c.getEndValue("S1", "Task 2", 0)); assertEquals(40L, c.getEndValue("S1", "Task 2", 1)); assertEquals(55L, c.getEndValue("S2", "Task 3", 0)); assertEquals(60L, c.getEndValue("S2", "Task 3", 1)); assertEquals(15L, c.getEndValue(0, 0, 0)); assertEquals(20L, c.getEndValue(0, 0, 1)); assertEquals(35L, c.getEndValue(0, 1, 0)); assertEquals(40L, c.getEndValue(0, 1, 1)); assertEquals(55L, c.getEndValue(1, 2, 0)); assertEquals(60L, c.getEndValue(1, 2, 1)); TaskSeriesCollection c3 = createCollection3(); assertEquals(111L, c3.getEndValue(0, 0, 0)); assertEquals(222L, c3.getEndValue(0, 1, 0)); assertEquals(333L, c3.getEndValue(0, 1, 1)); assertNull(c3.getEndValue(1, 0, 0)); assertEquals(444L, c3.getEndValue(1, 1, 0)); assertEquals(555L, c3.getEndValue(1, 1, 1)); assertEquals(666L, c3.getEndValue(1, 1, 2)); } /** * A check for a null task duration. */ @Test public void testGetEndValue3() { TaskSeriesCollection c = new TaskSeriesCollection(); TaskSeries s = new TaskSeries("Series 1"); s.add(new Task("Task with null duration", null)); c.add(s); Number millis = c.getEndValue("Series 1", "Task with null duration"); assertNull(millis); } /** * Some tests for the getPercentComplete() method. */ @Test public void testGetPercentComplete() { TaskSeriesCollection c = createCollection2(); assertEquals(0.10, c.getPercentComplete("S1", "Task 1")); assertEquals(0.20, c.getPercentComplete("S1", "Task 2")); assertEquals(0.30, c.getPercentComplete("S2", "Task 3")); assertEquals(0.10, c.getPercentComplete(0, 0)); assertEquals(0.20, c.getPercentComplete(0, 1)); assertNull(c.getPercentComplete(0, 2)); assertNull(c.getPercentComplete(1, 0)); assertNull(c.getPercentComplete(1, 1)); assertEquals(0.30, c.getPercentComplete(1, 2)); // test collection 3, which doesn't define all tasks in all series TaskSeriesCollection c3 = createCollection3(); assertEquals(0.1, c3.getPercentComplete(0, 0)); assertEquals(0.2, c3.getPercentComplete(0, 1)); assertNull(c3.getPercentComplete(1, 0)); assertEquals(0.3, c3.getPercentComplete(1, 1)); assertEquals(0.111, c3.getPercentComplete(0, 0, 0)); assertEquals(0.222, c3.getPercentComplete(0, 1, 0)); assertEquals(0.333, c3.getPercentComplete(0, 1, 1)); assertEquals(0.444, c3.getPercentComplete(1, 1, 0)); assertEquals(0.555, c3.getPercentComplete(1, 1, 1)); assertEquals(0.666, c3.getPercentComplete(1, 1, 2)); } /** * A test for the getColumnCount() method. */ @Test public void testGetColumnCount() { TaskSeriesCollection c = createCollection1(); assertEquals(3, c.getColumnCount()); } /** * Some tests for the getColumnKey() method. */ @Test public void testGetColumnKey() { TaskSeriesCollection c = createCollection1(); assertEquals("Task 1", c.getColumnKey(0)); assertEquals("Task 2", c.getColumnKey(1)); assertEquals("Task 3", c.getColumnKey(2)); } /** * Some tests for the getColumnIndex() method. */ @Test public void testGetColumnIndex() { TaskSeriesCollection c = createCollection1(); assertEquals(0, c.getColumnIndex("Task 1")); assertEquals(1, c.getColumnIndex("Task 2")); assertEquals(2, c.getColumnIndex("Task 3")); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { TaskSeries s1 = new TaskSeries("S"); s1.add(new Task("T1", new Date(1), new Date(2))); s1.add(new Task("T2", new Date(11), new Date(22))); TaskSeries s2 = new TaskSeries("S"); s2.add(new Task("T1", new Date(1), new Date(2))); s2.add(new Task("T2", new Date(11), new Date(22))); TaskSeriesCollection c1 = new TaskSeriesCollection(); c1.add(s1); c1.add(s2); TaskSeries s1b = new TaskSeries("S"); s1b.add(new Task("T1", new Date(1), new Date(2))); s1b.add(new Task("T2", new Date(11), new Date(22))); TaskSeries s2b = new TaskSeries("S"); s2b.add(new Task("T1", new Date(1), new Date(2))); s2b.add(new Task("T2", new Date(11), new Date(22))); TaskSeriesCollection c2 = new TaskSeriesCollection(); c2.add(s1b); c2.add(s2b); assertEquals(c1, c2); assertEquals(c2, c1); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { TaskSeries s1 = new TaskSeries("S1"); s1.add(new Task("T1", new Date(1), new Date(2))); s1.add(new Task("T2", new Date(11), new Date(22))); TaskSeries s2 = new TaskSeries("S2"); s2.add(new Task("T1", new Date(33), new Date(44))); s2.add(new Task("T2", new Date(55), new Date(66))); TaskSeriesCollection c1 = new TaskSeriesCollection(); c1.add(s1); c1.add(s2); TaskSeriesCollection c2 = (TaskSeriesCollection) c1.clone(); assertNotSame(c1, c2); assertSame(c1.getClass(), c2.getClass()); assertEquals(c1, c2); // basic check for independence s1.add(new Task("T3", new Date(21), new Date(33))); assertNotEquals(c1, c2); TaskSeries series = c2.getSeries("S1"); series.add(new Task("T3", new Date(21), new Date(33))); // equals checks the keys - make sure they get updated c1.seriesChanged(new SeriesChangeEvent(this)); c2.seriesChanged(new SeriesChangeEvent(this)); assertEquals(c1, c2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { TaskSeries s1 = new TaskSeries("S"); s1.add(new Task("T1", new Date(1), new Date(2))); s1.add(new Task("T2", new Date(11), new Date(22))); TaskSeries s2 = new TaskSeries("S"); s2.add(new Task("T1", new Date(1), new Date(2))); s2.add(new Task("T2", new Date(11), new Date(22))); TaskSeriesCollection c1 = new TaskSeriesCollection(); c1.add(s1); c1.add(s2); TaskSeriesCollection c2 = TestUtils.serialised(c1); assertEquals(c1, c2); } /** * A test for bug report 697153. */ @Test public void test697153() { TaskSeries s1 = new TaskSeries("S1"); s1.add(new Task("Task 1", new SimpleTimePeriod(new Date(), new Date()))); s1.add(new Task("Task 2", new SimpleTimePeriod(new Date(), new Date()))); s1.add(new Task("Task 3", new SimpleTimePeriod(new Date(), new Date()))); TaskSeries s2 = new TaskSeries("S2"); s2.add(new Task("Task 2", new SimpleTimePeriod(new Date(), new Date()))); s2.add(new Task("Task 3", new SimpleTimePeriod(new Date(), new Date()))); s2.add(new Task("Task 4", new SimpleTimePeriod(new Date(), new Date()))); TaskSeriesCollection tsc = new TaskSeriesCollection(); tsc.add(s1); tsc.add(s2); s1.removeAll(); int taskCount = tsc.getColumnCount(); assertEquals(3, taskCount); } /** * A test for bug report 800324. */ @Test public void test800324() { TaskSeries s1 = new TaskSeries("S1"); s1.add(new Task("Task 1", new SimpleTimePeriod(new Date(), new Date()))); s1.add(new Task("Task 2", new SimpleTimePeriod(new Date(), new Date()))); s1.add(new Task("Task 3", new SimpleTimePeriod(new Date(), new Date()))); TaskSeriesCollection tsc = new TaskSeriesCollection(); tsc.add(s1); // these methods should throw an IndexOutOfBoundsException since the // column is too high... try { /* Number start = */ tsc.getStartValue(0, 3); fail(); } catch (IndexOutOfBoundsException e) { // expected } try { /* Number end = */ tsc.getEndValue(0, 3); fail(); } catch (IndexOutOfBoundsException e) { // expected } try { /* int count = */ tsc.getSubIntervalCount(0, 3); fail(); } catch (IndexOutOfBoundsException e) { // expected } } /** * Some tests for the bug report 1099331. We create a TaskSeriesCollection * with two series - the first series has two tasks, but the second has * only one. The key is to ensure that the methods in TaskSeriesCollection * translate the index values to key values *before* accessing the tasks * in the series. */ @Test public void testGetSubIntervalCount() { TaskSeriesCollection tsc = createCollection3(); assertEquals(1, tsc.getSubIntervalCount(0, 0)); assertEquals(2, tsc.getSubIntervalCount(0, 1)); assertEquals(0, tsc.getSubIntervalCount(1, 0)); assertEquals(3, tsc.getSubIntervalCount(1, 1)); } /** * Some basic tests for the getSeries() methods. */ @Test public void testGetSeries() { TaskSeries s1 = new TaskSeries("S1"); TaskSeries s2 = new TaskSeries("S2"); TaskSeriesCollection c = new TaskSeriesCollection(); c.add(s1); assertEquals(c.getSeries(0), s1); assertEquals(c.getSeries("S1"), s1); assertNull(c.getSeries("XX")); c.add(s2); assertEquals(c.getSeries(1), s2); assertEquals(c.getSeries("S2"), s2); boolean pass = false; try { c.getSeries(null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some basic checks for the remove() method. */ @Test public void testRemove() { TaskSeriesCollection c = new TaskSeriesCollection(); TaskSeries s1 = new TaskSeries("S1"); c.add(s1); assertEquals("S1", c.getSeries(0).getKey()); c.remove(0); assertEquals(0, c.getSeriesCount()); c.add(s1); boolean pass = false; try { c.remove(-1); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); pass = false; try { c.remove(1); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/gantt/TaskSeriesTest.java000066400000000000000000000126511463604235500276230ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * TaskSeriesTests.java * -------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.data.gantt; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import javax.swing.event.EventListenerList; import java.beans.PropertyChangeSupport; import java.beans.VetoableChangeSupport; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link TaskSeries} class. */ public class TaskSeriesTest { @Test public void testEqualsHashCode() { EqualsVerifier.forClass(TaskSeries.class) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .withRedefinedSuperclass() .withPrefabValues(EventListenerList.class, new EventListenerList(), new EventListenerList()) .withPrefabValues(PropertyChangeSupport.class, new PropertyChangeSupport("A"), new PropertyChangeSupport("B")) .withPrefabValues(VetoableChangeSupport.class, new VetoableChangeSupport("A"), new VetoableChangeSupport("B")) .withPrefabValues(Map.class, new HashMap(), new HashMap(Collections.singletonMap("K", "V"))) .withPrefabValues(Task.class, new Task("T1", new Date(1), new Date(2)), new Task("T2", new Date(3), new Date(4))) .withIgnoredFields("listeners") .withIgnoredFields("propertyChangeSupport") .withIgnoredFields("vetoableChangeSupport") .withIgnoredFields("notify") .verify(); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { TaskSeries s1 = new TaskSeries("S"); s1.add(new Task("T1", new Date(1), new Date(2))); s1.add(new Task("T2", new Date(11), new Date(22))); TaskSeries s2 = new TaskSeries("S"); s2.add(new Task("T1", new Date(1), new Date(2))); s2.add(new Task("T2", new Date(11), new Date(22))); assertEquals(s1, s2); assertEquals(s2, s1); s1.add(new Task("T3", new Date(22), new Date(33))); assertNotEquals(s1, s2); s2.add(new Task("T3", new Date(22), new Date(33))); assertEquals(s1, s2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { TaskSeries s1 = new TaskSeries("S"); s1.add(new Task("T1", new Date(1), new Date(2))); s1.add(new Task("T2", new Date(11), new Date(22))); TaskSeries s2 = (TaskSeries) s1.clone(); assertNotSame(s1, s2); assertSame(s1.getClass(), s2.getClass()); assertEquals(s1, s2); // basic check for independence s1.add(new Task("T3", new Date(22), new Date(33))); assertNotEquals(s1, s2); s2.add(new Task("T3", new Date(22), new Date(33))); assertEquals(s1, s2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { TaskSeries s1 = new TaskSeries("S"); s1.add(new Task("T1", new Date(1), new Date(2))); s1.add(new Task("T2", new Date(11), new Date(22))); TaskSeries s2 = TestUtils.serialised(s1); assertEquals(s1, s2); } /** * Some checks for the getTask() method. */ @Test public void testGetTask() { TaskSeries s1 = new TaskSeries("S"); s1.add(new Task("T1", new Date(1), new Date(2))); s1.add(new Task("T2", new Date(11), new Date(22))); Task t1 = s1.get("T1"); assertEquals(t1, new Task("T1", new Date(1), new Date(2))); Task t2 = s1.get("T2"); assertEquals(t2, new Task("T2", new Date(11), new Date(22))); Task t3 = s1.get("T3"); assertNull(t3); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/gantt/TaskTest.java000066400000000000000000000106771463604235500264560ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------- * TaskTest.java * ------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.data.gantt; import java.util.Date; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.data.time.SimpleTimePeriod; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link Task} class. */ public class TaskTest { @Test public void testEqualsHashCode() { EqualsVerifier.forClass(Task.class) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .withPrefabValues(Task.class, new Task("T1", new Date(1), new Date(2)), new Task("T2", new Date(3), new Date(4))) .verify(); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { Task t1 = new Task("T", new Date(1), new Date(2)); Task t2 = new Task("T", new Date(1), new Date(2)); assertEquals(t1, t2); assertEquals(t2, t1); t1.setDescription("X"); assertNotEquals(t1, t2); t2.setDescription("X"); assertEquals(t1, t2); t1.setDuration(new SimpleTimePeriod(new Date(2), new Date(3))); assertNotEquals(t1, t2); t2.setDuration(new SimpleTimePeriod(new Date(2), new Date(3))); assertEquals(t1, t2); t1.setPercentComplete(0.5); assertNotEquals(t1, t2); t2.setPercentComplete(0.5); assertEquals(t1, t2); t1.addSubtask(new Task("T", new Date(22), new Date(33))); assertNotEquals(t1, t2); t2.addSubtask(new Task("T", new Date(22), new Date(33))); assertEquals(t1, t2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { Task t1 = new Task("T", new Date(1), new Date(2)); Task t2 = (Task) t1.clone(); assertNotSame(t1, t2); assertSame(t1.getClass(), t2.getClass()); assertEquals(t1, t2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { Task t1 = new Task("T", new Date(1), new Date(2)); Task t2 = TestUtils.serialised(t1); assertEquals(t1, t2); } /** * Check the getSubTaskCount() method. */ @Test public void testGetSubTaskCount() { Task t1 = new Task("T", new Date(100), new Date(200)); assertEquals(0, t1.getSubtaskCount()); t1.addSubtask(new Task("S1", new Date(100), new Date(110))); assertEquals(1, t1.getSubtaskCount()); Task s2 = new Task("S2", new Date(111), new Date(120)); t1.addSubtask(s2); assertEquals(2, t1.getSubtaskCount()); t1.addSubtask(new Task("S3", new Date(121), new Date(130))); assertEquals(3, t1.getSubtaskCount()); t1.removeSubtask(s2); assertEquals(2, t1.getSubtaskCount()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/gantt/XYTaskDatasetTest.java000066400000000000000000000134451463604235500302410ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * XYTaskDatasetTest.java * ---------------------- * (C) Copyright 2008-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.data.gantt; import java.util.Date; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.jfree.data.general.SeriesChangeEvent; import org.junit.jupiter.api.Test; import javax.swing.event.EventListenerList; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XYTaskDataset} class. */ public class XYTaskDatasetTest { @Test public void testEqualsHashCode() { EqualsVerifier.forClass(XYTaskDataset.class) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .withRedefinedSuperclass() .withPrefabValues(EventListenerList.class, new EventListenerList(), new EventListenerList()) .withPrefabValues(Task.class, new Task("T1", new Date(1), new Date(2)), new Task("T2", new Date(3), new Date(4))) .verify(); } /** * Some checks for the equals() method. */ @Test public void testEquals() { TaskSeries s1 = new TaskSeries("Series"); s1.add(new Task("Task 1", new Date(0L), new Date(1L))); s1.add(new Task("Task 2", new Date(10L), new Date(11L))); s1.add(new Task("Task 3", new Date(20L), new Date(21L))); TaskSeriesCollection u1 = new TaskSeriesCollection(); u1.add(s1); XYTaskDataset d1 = new XYTaskDataset(u1); TaskSeries s2 = new TaskSeries("Series"); s2.add(new Task("Task 1", new Date(0L), new Date(1L))); s2.add(new Task("Task 2", new Date(10L), new Date(11L))); s2.add(new Task("Task 3", new Date(20L), new Date(21L))); TaskSeriesCollection u2 = new TaskSeriesCollection(); u2.add(s2); XYTaskDataset d2 = new XYTaskDataset(u2); assertEquals(d1, d2); d1.setSeriesWidth(0.123); assertNotEquals(d1, d2); d2.setSeriesWidth(0.123); assertEquals(d1, d2); d1.setTransposed(true); assertNotEquals(d1, d2); d2.setTransposed(true); assertEquals(d1, d2); s1.add(new Task("Task 2", new Date(10L), new Date(11L))); assertNotEquals(d1, d2); s2.add(new Task("Task 2", new Date(10L), new Date(11L))); assertEquals(d1, d2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { TaskSeries s1 = new TaskSeries("Series"); s1.add(new Task("Task 1", new Date(0L), new Date(1L))); TaskSeriesCollection u1 = new TaskSeriesCollection(); u1.add(s1); XYTaskDataset d1 = new XYTaskDataset(u1); XYTaskDataset d2 = (XYTaskDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); // basic check for independence s1.add(new Task("Task 2", new Date(10L), new Date(11L))); assertNotEquals(d1, d2); TaskSeriesCollection u2 = d2.getTasks(); TaskSeries s2 = u2.getSeries("Series"); s2.add(new Task("Task 2", new Date(10L), new Date(11L))); // equals checks the keys - make sure they get updated u1.seriesChanged(new SeriesChangeEvent(this)); u2.seriesChanged(new SeriesChangeEvent(this)); assertEquals(d1, d2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { TaskSeries s1 = new TaskSeries("Series"); s1.add(new Task("Task 1", new Date(0L), new Date(1L))); TaskSeriesCollection u1 = new TaskSeriesCollection(); u1.add(s1); XYTaskDataset d1 = new XYTaskDataset(u1); XYTaskDataset d2 = TestUtils.serialised(d1); assertEquals(d1, d2); // basic check for independence s1.add(new Task("Task 2", new Date(10L), new Date(11L))); assertNotEquals(d1, d2); TaskSeriesCollection u2 = d2.getTasks(); TaskSeries s2 = u2.getSeries("Series"); s2.add(new Task("Task 2", new Date(10L), new Date(11L))); assertEquals(d1, d2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/general/000077500000000000000000000000001463604235500243365ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/general/DatasetGroupTest.java000066400000000000000000000044411463604235500304460ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * DatasetGroupTest.java * --------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): Tracy Hiltbrand; * */ package org.jfree.data.general; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; /** * Tests for the {@link DatasetGroup} class. */ public class DatasetGroupTest { @Test public void testEqualsHashCode() { EqualsVerifier.forClass(DatasetGroup.class) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DatasetGroup g1 = new DatasetGroup(); DatasetGroup g2 = TestUtils.serialised(g1); assertEquals(g1, g2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/general/DatasetUtilsTest.java000066400000000000000000001544301463604235500304560ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * DatasetUtilsTest.java * --------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.general; import java.util.*; import org.jfree.data.KeyToGroupMap; import org.jfree.data.Range; import org.jfree.data.category.CategoryDataset; import org.jfree.data.category.DefaultCategoryDataset; import org.jfree.data.category.DefaultIntervalCategoryDataset; import org.jfree.data.function.Function2D; import org.jfree.data.function.LineFunction2D; import org.jfree.data.statistics.BoxAndWhiskerItem; import org.jfree.data.statistics.DefaultBoxAndWhiskerXYDataset; import org.jfree.data.statistics.DefaultMultiValueCategoryDataset; import org.jfree.data.statistics.DefaultStatisticalCategoryDataset; import org.jfree.data.statistics.MultiValueCategoryDataset; import org.jfree.data.xy.DefaultIntervalXYDataset; import org.jfree.data.xy.DefaultTableXYDataset; import org.jfree.data.xy.DefaultXYDataset; import org.jfree.data.xy.IntervalXYDataset; import org.jfree.data.xy.TableXYDataset; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYIntervalSeries; import org.jfree.data.xy.XYIntervalSeriesCollection; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.jfree.data.xy.YIntervalSeries; import org.jfree.data.xy.YIntervalSeriesCollection; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; /** * Tests for the {@link DatasetUtils} class. */ public class DatasetUtilsTest { private static final double EPSILON = 0.0000000001; /** * Some tests to verify that Java does what I think it does! */ @Test public void testJava() { assertTrue(Double.isNaN(Math.min(1.0, Double.NaN))); assertTrue(Double.isNaN(Math.max(1.0, Double.NaN))); } /** * Some tests for the calculatePieDatasetTotal() method. */ @Test public void testCalculatePieDatasetTotal() { DefaultPieDataset d = new DefaultPieDataset(); assertEquals(0.0, DatasetUtils.calculatePieDatasetTotal(d), EPSILON); d.setValue("A", 1.0); assertEquals(1.0, DatasetUtils.calculatePieDatasetTotal(d), EPSILON); d.setValue("B", 3.0); assertEquals(4.0, DatasetUtils.calculatePieDatasetTotal(d), EPSILON); } /** * Some tests for the findDomainBounds() method. */ @Test public void testFindDomainBounds() { XYDataset dataset = createXYDataset1(); Range r = DatasetUtils.findDomainBounds(dataset); assertEquals(1.0, r.getLowerBound(), EPSILON); assertEquals(3.0, r.getUpperBound(), EPSILON); } /** * This test checks that the standard method has 'includeInterval' * defaulting to true. */ @Test public void testFindDomainBounds2() { DefaultIntervalXYDataset dataset = new DefaultIntervalXYDataset(); double[] x1 = new double[] {1.0, 2.0, 3.0}; double[] x1Start = new double[] {0.9, 1.9, 2.9}; double[] x1End = new double[] {1.1, 2.1, 3.1}; double[] y1 = new double[] {4.0, 5.0, 6.0}; double[] y1Start = new double[] {1.09, 2.09, 3.09}; double[] y1End = new double[] {1.11, 2.11, 3.11}; double[][] data1 = new double[][] {x1, x1Start, x1End, y1, y1Start, y1End}; dataset.addSeries("S1", data1); Range r = DatasetUtils.findDomainBounds(dataset); assertEquals(0.9, r.getLowerBound(), EPSILON); assertEquals(3.1, r.getUpperBound(), EPSILON); } /** * This test checks that when the 'includeInterval' flag is false, the * bounds come from the regular x-values. */ @Test public void testFindDomainBounds3() { DefaultIntervalXYDataset dataset = new DefaultIntervalXYDataset(); double[] x1 = new double[] {1.0, 2.0, 3.0}; double[] x1Start = new double[] {0.9, 1.9, 2.9}; double[] x1End = new double[] {1.1, 2.1, 3.1}; double[] y1 = new double[] {4.0, 5.0, 6.0}; double[] y1Start = new double[] {1.09, 2.09, 3.09}; double[] y1End = new double[] {1.11, 2.11, 3.11}; double[][] data1 = new double[][] {x1, x1Start, x1End, y1, y1Start, y1End}; dataset.addSeries("S1", data1); Range r = DatasetUtils.findDomainBounds(dataset, false); assertEquals(1.0, r.getLowerBound(), EPSILON); assertEquals(3.0, r.getUpperBound(), EPSILON); } /** * This test checks that the correct values are returned if the x and * y values fall outside the intervals. */ @Test public void testFindDomainBounds4() { DefaultIntervalXYDataset dataset = new DefaultIntervalXYDataset(); double[] x1 = new double[] {0.8, 3.2, 3.0}; double[] x1Start = new double[] {0.9, 1.9, 2.9}; double[] x1End = new double[] {1.1, 2.1, 3.1}; double[] y1 = new double[] {4.0, 5.0, 6.0}; double[] y1Start = new double[] {1.09, 2.09, 3.09}; double[] y1End = new double[] {1.11, 2.11, 3.11}; double[][] data1 = new double[][] {x1, x1Start, x1End, y1, y1Start, y1End}; dataset.addSeries("S1", data1); Range r = DatasetUtils.findDomainBounds(dataset); assertEquals(0.8, r.getLowerBound(), EPSILON); assertEquals(3.2, r.getUpperBound(), EPSILON); } /** * This test checks that NaN values are ignored. */ @Test public void testFindDomainBounds_NaN() { DefaultIntervalXYDataset dataset = new DefaultIntervalXYDataset(); double[] x1 = new double[] {1.0, 2.0, Double.NaN}; double[] x1Start = new double[] {0.9, 1.9, Double.NaN}; double[] x1End = new double[] {1.1, 2.1, Double.NaN}; double[] y1 = new double[] {4.0, 5.0, 6.0}; double[] y1Start = new double[] {1.09, 2.09, 3.09}; double[] y1End = new double[] {1.11, 2.11, 3.11}; double[][] data1 = new double[][] {x1, x1Start, x1End, y1, y1Start, y1End}; dataset.addSeries("S1", data1); Range r = DatasetUtils.findDomainBounds(dataset); assertEquals(0.9, r.getLowerBound(), EPSILON); assertEquals(2.1, r.getUpperBound(), EPSILON); r = DatasetUtils.findDomainBounds(dataset, false); assertEquals(1.0, r.getLowerBound(), EPSILON); assertEquals(2.0, r.getUpperBound(), EPSILON); } /** * Some tests for the iterateDomainBounds() method. */ @Test public void testIterateDomainBounds() { XYDataset dataset = createXYDataset1(); Range r = DatasetUtils.iterateDomainBounds(dataset); assertEquals(1.0, r.getLowerBound(), EPSILON); assertEquals(3.0, r.getUpperBound(), EPSILON); } /** * Check that NaN values in the dataset are ignored. */ @Test public void testIterateDomainBounds_NaN() { DefaultXYDataset dataset = new DefaultXYDataset(); double[] x = new double[] {1.0, 2.0, Double.NaN, 3.0}; double[] y = new double[] {9.0, 8.0, 7.0, 6.0}; dataset.addSeries("S1", new double[][] {x, y}); Range r = DatasetUtils.iterateDomainBounds(dataset); assertEquals(1.0, r.getLowerBound(), EPSILON); assertEquals(3.0, r.getUpperBound(), EPSILON); } /** * Check that NaN values in the IntervalXYDataset are ignored. */ @Test public void testIterateDomainBounds_NaN2() { DefaultIntervalXYDataset dataset = new DefaultIntervalXYDataset(); double[] x1 = new double[] {Double.NaN, 2.0, 3.0}; double[] x1Start = new double[] {0.9, Double.NaN, 2.9}; double[] x1End = new double[] {1.1, Double.NaN, 3.1}; double[] y1 = new double[] {4.0, 5.0, 6.0}; double[] y1Start = new double[] {1.09, 2.09, 3.09}; double[] y1End = new double[] {1.11, 2.11, 3.11}; double[][] data1 = new double[][] {x1, x1Start, x1End, y1, y1Start, y1End}; dataset.addSeries("S1", data1); Range r = DatasetUtils.iterateDomainBounds(dataset, false); assertEquals(2.0, r.getLowerBound(), EPSILON); assertEquals(3.0, r.getUpperBound(), EPSILON); r = DatasetUtils.iterateDomainBounds(dataset, true); assertEquals(0.9, r.getLowerBound(), EPSILON); assertEquals(3.1, r.getUpperBound(), EPSILON); } /** * Some tests for the findRangeBounds() for a CategoryDataset method. */ @Test public void testFindRangeBounds_CategoryDataset() { CategoryDataset dataset = createCategoryDataset1(); Range r = DatasetUtils.findRangeBounds(dataset); assertEquals(1.0, r.getLowerBound(), EPSILON); assertEquals(6.0, r.getUpperBound(), EPSILON); } /** * Some tests for the findRangeBounds() method on an XYDataset. */ @Test public void testFindRangeBounds() { XYDataset dataset = createXYDataset1(); Range r = DatasetUtils.findRangeBounds(dataset); assertEquals(100.0, r.getLowerBound(), EPSILON); assertEquals(105.0, r.getUpperBound(), EPSILON); } /** * A test for the findRangeBounds(XYDataset) method using * an IntervalXYDataset. */ @Test public void testFindRangeBounds2() { YIntervalSeriesCollection dataset = new YIntervalSeriesCollection(); Range r = DatasetUtils.findRangeBounds(dataset); assertNull(r); YIntervalSeries s1 = new YIntervalSeries("S1"); dataset.addSeries(s1); r = DatasetUtils.findRangeBounds(dataset); assertNull(r); // try a single item s1.add(1.0, 2.0, 1.5, 2.5); r = DatasetUtils.findRangeBounds(dataset); assertEquals(1.5, r.getLowerBound(), EPSILON); assertEquals(2.5, r.getUpperBound(), EPSILON); r = DatasetUtils.findRangeBounds(dataset, false); assertEquals(2.0, r.getLowerBound(), EPSILON); assertEquals(2.0, r.getUpperBound(), EPSILON); // another item s1.add(2.0, 2.0, 1.4, 2.1); r = DatasetUtils.findRangeBounds(dataset); assertEquals(1.4, r.getLowerBound(), EPSILON); assertEquals(2.5, r.getUpperBound(), EPSILON); // another empty series YIntervalSeries s2 = new YIntervalSeries("S2"); dataset.addSeries(s2); r = DatasetUtils.findRangeBounds(dataset); assertEquals(1.4, r.getLowerBound(), EPSILON); assertEquals(2.5, r.getUpperBound(), EPSILON); // an item in series 2 s2.add(1.0, 2.0, 1.9, 2.6); r = DatasetUtils.findRangeBounds(dataset); assertEquals(1.4, r.getLowerBound(), EPSILON); assertEquals(2.6, r.getUpperBound(), EPSILON); // what if we don't want the interval? r = DatasetUtils.findRangeBounds(dataset, false); assertEquals(2.0, r.getLowerBound(), EPSILON); assertEquals(2.0, r.getUpperBound(), EPSILON); } /** * Some tests for the iterateRangeBounds() method. */ @Test public void testIterateRangeBounds_CategoryDataset() { CategoryDataset dataset = createCategoryDataset1(); Range r = DatasetUtils.iterateRangeBounds(dataset, false); assertEquals(1.0, r.getLowerBound(), EPSILON); assertEquals(6.0, r.getUpperBound(), EPSILON); } /** * Some checks for the iterateRangeBounds() method. */ @Test public void testIterateRangeBounds2_CategoryDataset() { // an empty dataset should return a null range DefaultCategoryDataset dataset = new DefaultCategoryDataset(); Range r = DatasetUtils.iterateRangeBounds(dataset, false); assertNull(r); // a dataset with a single value dataset.addValue(1.23, "R1", "C1"); r = DatasetUtils.iterateRangeBounds(dataset, false); assertEquals(1.23, r.getLowerBound(), EPSILON); assertEquals(1.23, r.getUpperBound(), EPSILON); // null is ignored dataset.addValue(null, "R2", "C1"); r = DatasetUtils.iterateRangeBounds(dataset, false); assertEquals(1.23, r.getLowerBound(), EPSILON); assertEquals(1.23, r.getUpperBound(), EPSILON); // a Double.NaN should be ignored dataset.addValue(Double.NaN, "R2", "C1"); r = DatasetUtils.iterateRangeBounds(dataset, false); assertEquals(1.23, r.getLowerBound(), EPSILON); assertEquals(1.23, r.getUpperBound(), EPSILON); } /** * Some checks for the iterateRangeBounds() method using an * IntervalCategoryDataset. */ @Test public void testIterateRangeBounds3_CategoryDataset() { Number[][] starts = new Double[2][3]; Number[][] ends = new Double[2][3]; starts[0][0] = 1.0; starts[0][1] = 2.0; starts[0][2] = 3.0; starts[1][0] = 11.0; starts[1][1] = 12.0; starts[1][2] = 13.0; ends[0][0] = 4.0; ends[0][1] = 5.0; ends[0][2] = 6.0; ends[1][0] = 16.0; ends[1][1] = 15.0; ends[1][2] = 14.0; DefaultIntervalCategoryDataset d = new DefaultIntervalCategoryDataset( starts, ends); Range r = DatasetUtils.iterateRangeBounds(d, false); assertEquals(4.0, r.getLowerBound(), EPSILON); assertEquals(16.0, r.getUpperBound(), EPSILON); r = DatasetUtils.iterateRangeBounds(d, true); assertEquals(1.0, r.getLowerBound(), EPSILON); assertEquals(16.0, r.getUpperBound(), EPSILON); } /** * Some tests for the iterateRangeBounds() method. */ @Test public void testIterateRangeBounds() { XYDataset dataset = createXYDataset1(); Range r = DatasetUtils.iterateRangeBounds(dataset); assertEquals(100.0, r.getLowerBound(), EPSILON); assertEquals(105.0, r.getUpperBound(), EPSILON); } /** * Check the range returned when a series contains a null value. */ @Test public void testIterateRangeBounds2() { XYSeries s1 = new XYSeries("S1"); s1.add(1.0, 1.1); s1.add(2.0, null); s1.add(3.0, 3.3); XYSeriesCollection dataset = new XYSeriesCollection(s1); Range r = DatasetUtils.iterateRangeBounds(dataset); assertEquals(1.1, r.getLowerBound(), EPSILON); assertEquals(3.3, r.getUpperBound(), EPSILON); } /** * Some checks for the iterateRangeBounds() method. */ @Test public void testIterateRangeBounds3() { // an empty dataset should return a null range XYSeriesCollection dataset = new XYSeriesCollection(); Range r = DatasetUtils.iterateRangeBounds(dataset); assertNull(r); XYSeries s1 = new XYSeries("S1"); dataset.addSeries(s1); r = DatasetUtils.iterateRangeBounds(dataset); assertNull(r); // a dataset with a single value s1.add(1.0, 1.23); r = DatasetUtils.iterateRangeBounds(dataset); assertEquals(1.23, r.getLowerBound(), EPSILON); assertEquals(1.23, r.getUpperBound(), EPSILON); // null is ignored s1.add(2.0, null); r = DatasetUtils.iterateRangeBounds(dataset); assertEquals(1.23, r.getLowerBound(), EPSILON); assertEquals(1.23, r.getUpperBound(), EPSILON); // Double.NaN DOESN'T mess things up s1.add(3.0, Double.NaN); r = DatasetUtils.iterateRangeBounds(dataset); assertEquals(1.23, r.getLowerBound(), EPSILON); assertEquals(1.23, r.getUpperBound(), EPSILON); } /** * Some checks for the range bounds of a dataset that implements the * {@link IntervalXYDataset} interface. */ @Test public void testIterateRangeBounds4() { YIntervalSeriesCollection dataset = new YIntervalSeriesCollection(); Range r = DatasetUtils.iterateRangeBounds(dataset); assertNull(r); YIntervalSeries s1 = new YIntervalSeries("S1"); dataset.addSeries(s1); r = DatasetUtils.iterateRangeBounds(dataset); assertNull(r); // try a single item s1.add(1.0, 2.0, 1.5, 2.5); r = DatasetUtils.iterateRangeBounds(dataset); assertEquals(1.5, r.getLowerBound(), EPSILON); assertEquals(2.5, r.getUpperBound(), EPSILON); // another item s1.add(2.0, 2.0, 1.4, 2.1); r = DatasetUtils.iterateRangeBounds(dataset); assertEquals(1.4, r.getLowerBound(), EPSILON); assertEquals(2.5, r.getUpperBound(), EPSILON); // another empty series YIntervalSeries s2 = new YIntervalSeries("S2"); dataset.addSeries(s2); r = DatasetUtils.iterateRangeBounds(dataset); assertEquals(1.4, r.getLowerBound(), EPSILON); assertEquals(2.5, r.getUpperBound(), EPSILON); // an item in series 2 s2.add(1.0, 2.0, 1.9, 2.6); r = DatasetUtils.iterateRangeBounds(dataset); assertEquals(1.4, r.getLowerBound(), EPSILON); assertEquals(2.6, r.getUpperBound(), EPSILON); } /** * Some tests for the findMinimumDomainValue() method. */ @Test public void testFindMinimumDomainValue() { XYDataset dataset = createXYDataset1(); Number minimum = DatasetUtils.findMinimumDomainValue(dataset); assertEquals(1.0, minimum); } /** * Some tests for the findMaximumDomainValue() method. */ @Test public void testFindMaximumDomainValue() { XYDataset dataset = createXYDataset1(); Number maximum = DatasetUtils.findMaximumDomainValue(dataset); assertEquals(3.0, maximum); } /** * Some tests for the findMinimumRangeValue() method. */ @Test public void testFindMinimumRangeValue() { CategoryDataset d1 = createCategoryDataset1(); Number min1 = DatasetUtils.findMinimumRangeValue(d1); assertEquals(1.0, min1); XYDataset d2 = createXYDataset1(); Number min2 = DatasetUtils.findMinimumRangeValue(d2); assertEquals(100.0, min2); } /** * Some tests for the findMaximumRangeValue() method. */ @Test public void testFindMaximumRangeValue() { CategoryDataset d1 = createCategoryDataset1(); Number max1 = DatasetUtils.findMaximumRangeValue(d1); assertEquals(6.0, max1); XYDataset dataset = createXYDataset1(); Number maximum = DatasetUtils.findMaximumRangeValue(dataset); assertEquals(105.0, maximum); } /** * A quick test of the min and max range value methods. */ @Test public void testMinMaxRange() { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.addValue(100.0, "Series 1", "Type 1"); dataset.addValue(101.1, "Series 1", "Type 2"); Number min = DatasetUtils.findMinimumRangeValue(dataset); assertTrue(min.doubleValue() < 100.1); Number max = DatasetUtils.findMaximumRangeValue(dataset); assertTrue(max.doubleValue() > 101.0); } /** * A test to reproduce bug report 803660. */ @Test public void test803660() { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.addValue(100.0, "Series 1", "Type 1"); dataset.addValue(101.1, "Series 1", "Type 2"); Number n = DatasetUtils.findMaximumRangeValue(dataset); assertTrue(n.doubleValue() > 101.0); } /** * A simple test for the cumulative range calculation. The sequence of * "cumulative" values are considered to be { 0.0, 10.0, 25.0, 18.0 } so * the range should be 0.0 -> 25.0. */ @Test public void testCumulativeRange1() { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.addValue(10.0, "Series 1", "Start"); dataset.addValue(15.0, "Series 1", "Delta 1"); dataset.addValue(-7.0, "Series 1", "Delta 2"); Range range = DatasetUtils.findCumulativeRangeBounds(dataset); assertEquals(0.0, range.getLowerBound(), 0.00000001); assertEquals(25.0, range.getUpperBound(), 0.00000001); } /** * A further test for the cumulative range calculation. */ @Test public void testCumulativeRange2() { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.addValue(-21.4, "Series 1", "Start Value"); dataset.addValue(11.57, "Series 1", "Delta 1"); dataset.addValue(3.51, "Series 1", "Delta 2"); dataset.addValue(-12.36, "Series 1", "Delta 3"); dataset.addValue(3.39, "Series 1", "Delta 4"); dataset.addValue(38.68, "Series 1", "Delta 5"); dataset.addValue(-43.31, "Series 1", "Delta 6"); dataset.addValue(-29.59, "Series 1", "Delta 7"); dataset.addValue(35.30, "Series 1", "Delta 8"); dataset.addValue(5.0, "Series 1", "Delta 9"); Range range = DatasetUtils.findCumulativeRangeBounds(dataset); assertEquals(-49.51, range.getLowerBound(), 0.00000001); assertEquals(23.39, range.getUpperBound(), 0.00000001); } /** * A further test for the cumulative range calculation. */ @Test public void testCumulativeRange3() { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.addValue(15.76, "Product 1", "Labour"); dataset.addValue(8.66, "Product 1", "Administration"); dataset.addValue(4.71, "Product 1", "Marketing"); dataset.addValue(3.51, "Product 1", "Distribution"); dataset.addValue(32.64, "Product 1", "Total Expense"); Range range = DatasetUtils.findCumulativeRangeBounds(dataset); assertEquals(0.0, range.getLowerBound(), EPSILON); assertEquals(65.28, range.getUpperBound(), EPSILON); } /** * Check that the findCumulativeRangeBounds() method ignores Double.NaN * values. */ @Test public void testCumulativeRange_NaN() { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.addValue(10.0, "Series 1", "Start"); dataset.addValue(15.0, "Series 1", "Delta 1"); dataset.addValue(Double.NaN, "Series 1", "Delta 2"); Range range = DatasetUtils.findCumulativeRangeBounds(dataset); assertEquals(0.0, range.getLowerBound(), EPSILON); assertEquals(25.0, range.getUpperBound(), EPSILON); } /** * Test the creation of a dataset from an array. */ @Test public void testCreateCategoryDataset1() { String[] rowKeys = {"R1", "R2", "R3"}; String[] columnKeys = {"C1", "C2"}; double[][] data = new double[3][]; data[0] = new double[] {1.1, 1.2}; data[1] = new double[] {2.1, 2.2}; data[2] = new double[] {3.1, 3.2}; CategoryDataset dataset = DatasetUtils.createCategoryDataset( rowKeys, columnKeys, data); assertEquals(3, dataset.getRowCount()); assertEquals(2, dataset.getColumnCount()); } /** * Test the creation of a dataset from an array. This time is should fail * because the array dimensions are around the wrong way. */ @Test public void testCreateCategoryDataset2() { boolean pass = false; String[] rowKeys = {"R1", "R2", "R3"}; String[] columnKeys = {"C1", "C2"}; double[][] data = new double[2][]; data[0] = new double[] {1.1, 1.2, 1.3}; data[1] = new double[] {2.1, 2.2, 2.3}; CategoryDataset dataset = null; try { dataset = DatasetUtils.createCategoryDataset(rowKeys, columnKeys, data); } catch (IllegalArgumentException e) { pass = true; // got it! } assertTrue(pass); assertNull(dataset); } /** * Test for a bug reported in the forum: * * http://www.jfree.org/phpBB2/viewtopic.php?t=7903 */ @Test public void testMaximumStackedRangeValue() { double v1 = 24.3; double v2 = 14.2; double v3 = 33.2; double v4 = 32.4; double v5 = 26.3; double v6 = 22.6; Number answer = Math.max(v1 + v2 + v3, v4 + v5 + v6); DefaultCategoryDataset d = new DefaultCategoryDataset(); d.addValue(v1, "Row 0", "Column 0"); d.addValue(v2, "Row 1", "Column 0"); d.addValue(v3, "Row 2", "Column 0"); d.addValue(v4, "Row 0", "Column 1"); d.addValue(v5, "Row 1", "Column 1"); d.addValue(v6, "Row 2", "Column 1"); Number max = DatasetUtils.findMaximumStackedRangeValue(d); assertEquals(max, answer); } /** * Some checks for the findStackedRangeBounds() method. */ @Test public void testFindStackedRangeBounds_CategoryDataset1() { CategoryDataset d1 = createCategoryDataset1(); Range r = DatasetUtils.findStackedRangeBounds(d1); assertEquals(0.0, r.getLowerBound(), EPSILON); assertEquals(15.0, r.getUpperBound(), EPSILON); d1 = createCategoryDataset2(); r = DatasetUtils.findStackedRangeBounds(d1); assertEquals(-2.0, r.getLowerBound(), EPSILON); assertEquals(2.0, r.getUpperBound(), EPSILON); } /** * Some checks for the findStackedRangeBounds() method. */ @Test public void testFindStackedRangeBounds_CategoryDataset2() { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); Range r = DatasetUtils.findStackedRangeBounds(dataset); assertNull(r); dataset.addValue(5.0, "R1", "C1"); r = DatasetUtils.findStackedRangeBounds(dataset, 3.0); assertEquals(3.0, r.getLowerBound(), EPSILON); assertEquals(8.0, r.getUpperBound(), EPSILON); dataset.addValue(-1.0, "R2", "C1"); r = DatasetUtils.findStackedRangeBounds(dataset, 3.0); assertEquals(2.0, r.getLowerBound(), EPSILON); assertEquals(8.0, r.getUpperBound(), EPSILON); dataset.addValue(null, "R3", "C1"); r = DatasetUtils.findStackedRangeBounds(dataset, 3.0); assertEquals(2.0, r.getLowerBound(), EPSILON); assertEquals(8.0, r.getUpperBound(), EPSILON); dataset.addValue(Double.NaN, "R4", "C1"); r = DatasetUtils.findStackedRangeBounds(dataset, 3.0); assertEquals(2.0, r.getLowerBound(), EPSILON); assertEquals(8.0, r.getUpperBound(), EPSILON); } /** * Some checks for the findStackedRangeBounds(CategoryDataset, * KeyToGroupMap) method. */ @Test public void testFindStackedRangeBounds_CategoryDataset3() { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); KeyToGroupMap map = new KeyToGroupMap("Group A"); Range r = DatasetUtils.findStackedRangeBounds(dataset, map); assertNull(r); dataset.addValue(1.0, "R1", "C1"); dataset.addValue(2.0, "R2", "C1"); dataset.addValue(3.0, "R3", "C1"); dataset.addValue(4.0, "R4", "C1"); map.mapKeyToGroup("R1", "Group A"); map.mapKeyToGroup("R2", "Group A"); map.mapKeyToGroup("R3", "Group B"); map.mapKeyToGroup("R4", "Group B"); r = DatasetUtils.findStackedRangeBounds(dataset, map); assertEquals(0.0, r.getLowerBound(), EPSILON); assertEquals(7.0, r.getUpperBound(), EPSILON); dataset.addValue(null, "R5", "C1"); r = DatasetUtils.findStackedRangeBounds(dataset, map); assertEquals(0.0, r.getLowerBound(), EPSILON); assertEquals(7.0, r.getUpperBound(), EPSILON); dataset.addValue(Double.NaN, "R6", "C1"); r = DatasetUtils.findStackedRangeBounds(dataset, map); assertEquals(0.0, r.getLowerBound(), EPSILON); assertEquals(7.0, r.getUpperBound(), EPSILON); } /** * Some checks for the findStackedRangeBounds() method. */ @Test public void testFindStackedRangeBoundsForTableXYDataset1() { TableXYDataset d2 = createTableXYDataset1(); Range r = DatasetUtils.findStackedRangeBounds(d2); assertEquals(-2.0, r.getLowerBound(), EPSILON); assertEquals(2.0, r.getUpperBound(), EPSILON); } /** * Some checks for the findStackedRangeBounds() method. */ @Test public void testFindStackedRangeBoundsForTableXYDataset2() { DefaultTableXYDataset d = new DefaultTableXYDataset(); Range r = DatasetUtils.findStackedRangeBounds(d); assertEquals(r, new Range(0.0, 0.0)); } /** * Tests the stacked range extent calculation. */ @Test public void testStackedRangeWithMap() { CategoryDataset d = createCategoryDataset1(); KeyToGroupMap map = new KeyToGroupMap("G0"); map.mapKeyToGroup("R2", "G1"); Range r = DatasetUtils.findStackedRangeBounds(d, map); assertEquals(0.0, r.getLowerBound(), EPSILON); assertEquals(9.0, r.getUpperBound(), EPSILON); } /** * Some checks for the isEmptyOrNull(XYDataset) method. */ @Test public void testIsEmptyOrNullXYDataset() { XYSeriesCollection dataset = null; assertTrue(DatasetUtils.isEmptyOrNull(dataset)); dataset = new XYSeriesCollection(); assertTrue(DatasetUtils.isEmptyOrNull(dataset)); XYSeries s1 = new XYSeries("S1"); dataset.addSeries(s1); assertTrue(DatasetUtils.isEmptyOrNull(dataset)); s1.add(1.0, 2.0); assertFalse(DatasetUtils.isEmptyOrNull(dataset)); s1.clear(); assertTrue(DatasetUtils.isEmptyOrNull(dataset)); } /** * Some checks for the limitPieDataset() methods. */ @Test public void testLimitPieDataset() { // check that empty dataset is handled OK DefaultPieDataset d1 = new DefaultPieDataset(); PieDataset d2 = DatasetUtils.createConsolidatedPieDataset(d1, "Other", 0.05); assertEquals(0, d2.getItemCount()); // check that minItem limit is observed d1.setValue("Item 1", 1.0); d1.setValue("Item 2", 49.50); d1.setValue("Item 3", 49.50); d2 = DatasetUtils.createConsolidatedPieDataset(d1, "Other", 0.05); assertEquals(3, d2.getItemCount()); assertEquals("Item 1", d2.getKey(0)); assertEquals("Item 2", d2.getKey(1)); assertEquals("Item 3", d2.getKey(2)); // check that minItem limit is observed d1.setValue("Item 4", 1.0); d2 = DatasetUtils.createConsolidatedPieDataset(d1, "Other", 0.05, 2); // and that simple aggregation works assertEquals(3, d2.getItemCount()); assertEquals("Item 2", d2.getKey(0)); assertEquals("Item 3", d2.getKey(1)); assertEquals("Other", d2.getKey(2)); assertEquals(2.0, d2.getValue("Other")); } /** * Some checks for the sampleFunction2D() method. */ @Test public void testSampleFunction2D() { Function2D f = new LineFunction2D(0, 1); XYDataset dataset = DatasetUtils.sampleFunction2D(f, 0.0, 1.0, 2, "S1"); assertEquals(1, dataset.getSeriesCount()); assertEquals("S1", dataset.getSeriesKey(0)); assertEquals(2, dataset.getItemCount(0)); assertEquals(0.0, dataset.getXValue(0, 0), EPSILON); assertEquals(0.0, dataset.getYValue(0, 0), EPSILON); assertEquals(1.0, dataset.getXValue(0, 1), EPSILON); assertEquals(1.0, dataset.getYValue(0, 1), EPSILON); } /** * A simple check for the findMinimumStackedRangeValue() method. */ @Test public void testFindMinimumStackedRangeValue() { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); // an empty dataset should return a null max Number min = DatasetUtils.findMinimumStackedRangeValue(dataset); assertNull(min); dataset.addValue(1.0, "R1", "C1"); min = DatasetUtils.findMinimumStackedRangeValue(dataset); assertEquals(0.0, min.doubleValue(), EPSILON); dataset.addValue(2.0, "R2", "C1"); min = DatasetUtils.findMinimumStackedRangeValue(dataset); assertEquals(0.0, min.doubleValue(), EPSILON); dataset.addValue(-3.0, "R3", "C1"); min = DatasetUtils.findMinimumStackedRangeValue(dataset); assertEquals(-3.0, min.doubleValue(), EPSILON); dataset.addValue(Double.NaN, "R4", "C1"); min = DatasetUtils.findMinimumStackedRangeValue(dataset); assertEquals(-3.0, min.doubleValue(), EPSILON); } /** * A simple check for the findMaximumStackedRangeValue() method. */ @Test public void testFindMinimumStackedRangeValue2() { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.addValue(-1.0, "R1", "C1"); Number min = DatasetUtils.findMinimumStackedRangeValue(dataset); assertEquals(-1.0, min.doubleValue(), EPSILON); dataset.addValue(-2.0, "R2", "C1"); min = DatasetUtils.findMinimumStackedRangeValue(dataset); assertEquals(-3.0, min.doubleValue(), EPSILON); } /** * A simple check for the findMaximumStackedRangeValue() method. */ @Test public void testFindMaximumStackedRangeValue() { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); // an empty dataset should return a null max Number max = DatasetUtils.findMaximumStackedRangeValue(dataset); assertNull(max); dataset.addValue(1.0, "R1", "C1"); max = DatasetUtils.findMaximumStackedRangeValue(dataset); assertEquals(1.0, max.doubleValue(), EPSILON); dataset.addValue(2.0, "R2", "C1"); max = DatasetUtils.findMaximumStackedRangeValue(dataset); assertEquals(3.0, max.doubleValue(), EPSILON); dataset.addValue(-3.0, "R3", "C1"); max = DatasetUtils.findMaximumStackedRangeValue(dataset); assertEquals(3.0, max.doubleValue(), EPSILON); dataset.addValue(Double.NaN, "R4", "C1"); max = DatasetUtils.findMaximumStackedRangeValue(dataset); assertEquals(3.0, max.doubleValue(), EPSILON); } /** * A simple check for the findMaximumStackedRangeValue() method. */ @Test public void testFindMaximumStackedRangeValue2() { DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.addValue(-1.0, "R1", "C1"); Number max = DatasetUtils.findMaximumStackedRangeValue(dataset); assertEquals(0.0, max.doubleValue(), EPSILON); dataset.addValue(-2.0, "R2", "C1"); max = DatasetUtils.findMaximumStackedRangeValue(dataset); assertEquals(0.0, max.doubleValue(), EPSILON); } /** * Creates a dataset for testing. * * @return A dataset. */ private CategoryDataset createCategoryDataset1() { DefaultCategoryDataset result = new DefaultCategoryDataset(); result.addValue(1.0, "R0", "C0"); result.addValue(1.0, "R1", "C0"); result.addValue(1.0, "R2", "C0"); result.addValue(4.0, "R0", "C1"); result.addValue(5.0, "R1", "C1"); result.addValue(6.0, "R2", "C1"); return result; } /** * Creates a dataset for testing. * * @return A dataset. */ private CategoryDataset createCategoryDataset2() { DefaultCategoryDataset result = new DefaultCategoryDataset(); result.addValue(1.0, "R0", "C0"); result.addValue(-2.0, "R1", "C0"); result.addValue(2.0, "R0", "C1"); result.addValue(-1.0, "R1", "C1"); return result; } /** * Creates a dataset for testing. * * @return A dataset. */ private XYDataset createXYDataset1() { XYSeries series1 = new XYSeries("S1"); series1.add(1.0, 100.0); series1.add(2.0, 101.0); series1.add(3.0, 102.0); XYSeries series2 = new XYSeries("S2"); series2.add(1.0, 103.0); series2.add(2.0, null); series2.add(3.0, 105.0); XYSeriesCollection result = new XYSeriesCollection(); result.addSeries(series1); result.addSeries(series2); result.setIntervalWidth(0.0); return result; } /** * Creates a sample dataset for testing purposes. * * @return A sample dataset. */ private TableXYDataset createTableXYDataset1() { DefaultTableXYDataset dataset = new DefaultTableXYDataset(); XYSeries s1 = new XYSeries("Series 1", true, false); s1.add(1.0, 1.0); s1.add(2.0, 2.0); dataset.addSeries(s1); XYSeries s2 = new XYSeries("Series 2", true, false); s2.add(1.0, -2.0); s2.add(2.0, -1.0); dataset.addSeries(s2); return dataset; } /** * This test checks that the correct values are returned if the x-values * fall outside the intervals (it is not required that they do). */ @Test public void testIterateToFindDomainBounds_IntervalXYDataset() { DefaultIntervalXYDataset dataset = new DefaultIntervalXYDataset(); double[] x1 = new double[] {0.8, 3.2, 3.0}; double[] x1Start = new double[] {0.9, 1.9, 2.9}; double[] x1End = new double[] {1.1, 2.1, 3.1}; double[] y1 = new double[] {4.0, 5.0, 6.0}; double[] y1Start = new double[] {1.09, 2.09, 3.09}; double[] y1End = new double[] {1.11, 2.11, 3.11}; double[][] data1 = new double[][] {x1, x1Start, x1End, y1, y1Start, y1End}; dataset.addSeries("S1", data1); Range r = DatasetUtils.iterateToFindDomainBounds(dataset, Collections.singletonList("S1"), true); assertEquals(0.8, r.getLowerBound(), EPSILON); assertEquals(3.2, r.getUpperBound(), EPSILON); } /** * This test checks that the correct values are returned if the y-values * fall outside the intervals (it is not required that they do). */ @Test public void testIterateToFindRangeBounds_IntervalXYDataset() { DefaultIntervalXYDataset dataset = new DefaultIntervalXYDataset(); double[] x1 = new double[] {0.8, 3.2, 3.0}; double[] x1Start = new double[] {0.9, 1.9, 2.9}; double[] x1End = new double[] {1.1, 2.1, 3.1}; double[] y1 = new double[] {4.0, -5.0, 6.0}; double[] y1Start = new double[] {1.09, 2.09, 3.09}; double[] y1End = new double[] {1.11, 2.11, 3.11}; double[][] data1 = new double[][] {x1, x1Start, x1End, y1, y1Start, y1End}; dataset.addSeries("S1", data1); Range r = DatasetUtils.iterateToFindRangeBounds(dataset, Collections.singletonList("S1"), new Range(0.0, 4.0), true); assertEquals(-5.0, r.getLowerBound(), EPSILON); assertEquals(6.0, r.getUpperBound(), EPSILON); } /** * Some checks for the iteratorToFindRangeBounds(XYDataset...) method. */ @Test public void testIterateToFindRangeBounds1_XYDataset() { // null dataset throws IllegalArgumentException boolean pass = false; try { DatasetUtils.iterateToFindRangeBounds(null, new ArrayList(), new Range(0.0, 1.0), true); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); // null list throws IllegalArgumentException pass = false; try { DatasetUtils.iterateToFindRangeBounds(new XYSeriesCollection(), null, new Range(0.0, 1.0), true); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); // null range throws IllegalArgumentException pass = false; try { DatasetUtils.iterateToFindRangeBounds(new XYSeriesCollection(), new ArrayList(), null, true); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Some tests for the iterateToFindRangeBounds() method. */ @Test public void testIterateToFindRangeBounds2_XYDataset() { List visibleSeriesKeys = new ArrayList<>(); Range xRange = new Range(0.0, 10.0); // empty dataset returns null XYSeriesCollection dataset = new XYSeriesCollection(); Range r = DatasetUtils.iterateToFindRangeBounds(dataset, visibleSeriesKeys, xRange, false); assertNull(r); // add an empty series XYSeries s1 = new XYSeries("A"); dataset.addSeries(s1); visibleSeriesKeys.add("A"); r = DatasetUtils.iterateToFindRangeBounds(dataset, visibleSeriesKeys, xRange, false); assertNull(r); // check a null value s1.add(1.0, null); r = DatasetUtils.iterateToFindRangeBounds(dataset, visibleSeriesKeys, xRange, false); assertNull(r); // check a NaN s1.add(2.0, Double.NaN); r = DatasetUtils.iterateToFindRangeBounds(dataset, visibleSeriesKeys, xRange, false); assertNull(r); // check a regular value s1.add(3.0, 5.0); r = DatasetUtils.iterateToFindRangeBounds(dataset, visibleSeriesKeys, xRange, false); assertEquals(new Range(5.0, 5.0), r); // check another regular value s1.add(4.0, 6.0); r = DatasetUtils.iterateToFindRangeBounds(dataset, visibleSeriesKeys, xRange, false); assertEquals(new Range(5.0, 6.0), r); // add a second series XYSeries s2 = new XYSeries("B"); dataset.addSeries(s2); r = DatasetUtils.iterateToFindRangeBounds(dataset, visibleSeriesKeys, xRange, false); assertEquals(new Range(5.0, 6.0), r); visibleSeriesKeys.add("B"); r = DatasetUtils.iterateToFindRangeBounds(dataset, visibleSeriesKeys, xRange, false); assertEquals(new Range(5.0, 6.0), r); // add a value to the second series s2.add(5.0, 15.0); r = DatasetUtils.iterateToFindRangeBounds(dataset, visibleSeriesKeys, xRange, false); assertEquals(new Range(5.0, 15.0), r); // add a value that isn't in the xRange s2.add(15.0, 150.0); r = DatasetUtils.iterateToFindRangeBounds(dataset, visibleSeriesKeys, xRange, false); assertEquals(new Range(5.0, 15.0), r); r = DatasetUtils.iterateToFindRangeBounds(dataset, visibleSeriesKeys, new Range(0.0, 20.0), false); assertEquals(new Range(5.0, 150.0), r); } /** * Some checks for the iterateToFindRangeBounds() method when applied to * a BoxAndWhiskerXYDataset. */ @Test public void testIterateToFindRangeBounds_BoxAndWhiskerXYDataset() { DefaultBoxAndWhiskerXYDataset dataset = new DefaultBoxAndWhiskerXYDataset("Series 1"); List visibleSeriesKeys = new ArrayList<>(); visibleSeriesKeys.add("Series 1"); Range xRange = new Range(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); assertNull(DatasetUtils.iterateToFindRangeBounds(dataset, visibleSeriesKeys, xRange, false)); dataset.add(new Date(50L), new BoxAndWhiskerItem(5.0, 4.9, 2.0, 8.0, 1.0, 9.0, 0.0, 10.0, new ArrayList())); assertEquals(new Range(5.0, 5.0), DatasetUtils.iterateToFindRangeBounds(dataset, visibleSeriesKeys, xRange, false)); assertEquals(new Range(1.0, 9.0), DatasetUtils.iterateToFindRangeBounds(dataset, visibleSeriesKeys, xRange, true)); } /** * Some checks for the iterateToFindRangeBounds(CategoryDataset...) * method. */ @Test public void testIterateToFindRangeBounds_StatisticalCategoryDataset() { DefaultStatisticalCategoryDataset dataset = new DefaultStatisticalCategoryDataset(); List visibleSeriesKeys = new ArrayList<>(); assertNull(DatasetUtils.iterateToFindRangeBounds(dataset, visibleSeriesKeys, false)); dataset.add(1.0, 0.5, "R1", "C1"); visibleSeriesKeys.add("R1"); assertEquals(new Range(1.0, 1.0), DatasetUtils.iterateToFindRangeBounds(dataset, visibleSeriesKeys, false)); assertEquals(new Range(0.5, 1.5), DatasetUtils.iterateToFindRangeBounds(dataset, visibleSeriesKeys, true)); } /** * Some checks for the iterateToFindRangeBounds(CategoryDataset...) method * with a {@link MultiValueCategoryDataset}. */ @Test public void testIterateToFindRangeBounds_MultiValueCategoryDataset() { DefaultMultiValueCategoryDataset dataset = new DefaultMultiValueCategoryDataset(); List visibleSeriesKeys = new ArrayList<>(); assertNull(DatasetUtils.iterateToFindRangeBounds(dataset, visibleSeriesKeys, true)); List values = Collections.singletonList(1.0); dataset.add(values, "R1", "C1"); visibleSeriesKeys.add("R1"); assertEquals(new Range(1.0, 1.0), DatasetUtils.iterateToFindRangeBounds(dataset, visibleSeriesKeys, true)); values = Arrays.asList(2.0, 3.0); dataset.add(values, "R1", "C2"); assertEquals(new Range(1.0, 3.0), DatasetUtils.iterateToFindRangeBounds(dataset, visibleSeriesKeys, true)); values = Arrays.asList(-1.0, -2.0); dataset.add(values, "R2", "C1"); assertEquals(new Range(1.0, 3.0), DatasetUtils.iterateToFindRangeBounds(dataset, visibleSeriesKeys, true)); visibleSeriesKeys.add("R2"); assertEquals(new Range(-2.0, 3.0), DatasetUtils.iterateToFindRangeBounds(dataset, visibleSeriesKeys, true)); } /** * Some checks for the iterateRangeBounds() method when passed an * IntervalCategoryDataset. */ @Test public void testIterateRangeBounds_IntervalCategoryDataset() { TestIntervalCategoryDataset d = new TestIntervalCategoryDataset(); d.addItem(1.0, 2.0, 3.0, "R1", "C1"); assertEquals(new Range(1.0, 3.0), DatasetUtils.iterateRangeBounds(d)); d = new TestIntervalCategoryDataset(); d.addItem(2.5, 2.0, 3.0, "R1", "C1"); assertEquals(new Range(2.0, 3.0), DatasetUtils.iterateRangeBounds(d)); d = new TestIntervalCategoryDataset(); d.addItem(4.0, 2.0, 3.0, "R1", "C1"); assertEquals(new Range(2.0, 4.0), DatasetUtils.iterateRangeBounds(d)); d = new TestIntervalCategoryDataset(); d.addItem(null, 2.0, 3.0, "R1", "C1"); assertEquals(new Range(2.0, 3.0), DatasetUtils.iterateRangeBounds(d)); // try some nulls d = new TestIntervalCategoryDataset(); d.addItem(null, null, null, "R1", "C1"); assertNull(DatasetUtils.iterateRangeBounds(d)); d = new TestIntervalCategoryDataset(); d.addItem(1.0, null, null, "R1", "C1"); assertEquals(new Range(1.0, 1.0), DatasetUtils.iterateRangeBounds(d)); d = new TestIntervalCategoryDataset(); d.addItem(null, 1.0, null, "R1", "C1"); assertEquals(new Range(1.0, 1.0), DatasetUtils.iterateRangeBounds(d)); d = new TestIntervalCategoryDataset(); d.addItem(null, null, 1.0, "R1", "C1"); assertEquals(new Range(1.0, 1.0), DatasetUtils.iterateRangeBounds(d)); } /** * A test for bug 2849731. */ @Test public void testBug2849731() { TestIntervalCategoryDataset d = new TestIntervalCategoryDataset(); d.addItem(2.5, 2.0, 3.0, "R1", "C1"); d.addItem(4.0, null, null, "R2", "C1"); assertEquals(new Range(2.0, 4.0), DatasetUtils.iterateRangeBounds(d)); } /** * Another test for bug 2849731. */ @Test public void testBug2849731_2() { XYIntervalSeriesCollection d = new XYIntervalSeriesCollection(); XYIntervalSeries s = new XYIntervalSeries("S1"); s.add(1.0, Double.NaN, Double.NaN, Double.NaN, 1.5, Double.NaN); d.addSeries(s); Range r = DatasetUtils.iterateDomainBounds(d); assertEquals(1.0, r.getLowerBound(), EPSILON); assertEquals(1.0, r.getUpperBound(), EPSILON); s.add(1.0, 1.5, Double.NaN, Double.NaN, 1.5, Double.NaN); r = DatasetUtils.iterateDomainBounds(d); assertEquals(1.0, r.getLowerBound(), EPSILON); assertEquals(1.5, r.getUpperBound(), EPSILON); s.add(1.0, Double.NaN, 0.5, Double.NaN, 1.5, Double.NaN); r = DatasetUtils.iterateDomainBounds(d); assertEquals(0.5, r.getLowerBound(), EPSILON); assertEquals(1.5, r.getUpperBound(), EPSILON); } /** * Yet another test for bug 2849731. */ @Test public void testBug2849731_3() { XYIntervalSeriesCollection d = new XYIntervalSeriesCollection(); XYIntervalSeries s = new XYIntervalSeries("S1"); s.add(1.0, Double.NaN, Double.NaN, 1.5, Double.NaN, Double.NaN); d.addSeries(s); Range r = DatasetUtils.iterateRangeBounds(d); assertEquals(1.5, r.getLowerBound(), EPSILON); assertEquals(1.5, r.getUpperBound(), EPSILON); s.add(1.0, 1.5, Double.NaN, Double.NaN, Double.NaN, 2.5); r = DatasetUtils.iterateRangeBounds(d); assertEquals(1.5, r.getLowerBound(), EPSILON); assertEquals(2.5, r.getUpperBound(), EPSILON); s.add(1.0, Double.NaN, 0.5, Double.NaN, 3.5, Double.NaN); r = DatasetUtils.iterateRangeBounds(d); assertEquals(1.5, r.getLowerBound(), EPSILON); assertEquals(3.5, r.getUpperBound(), EPSILON); } /** * Check the findYValue() method with a dataset that is in ascending order * of x-values. */ @Test public void testFindYValue() { XYSeries series = new XYSeries("S1"); XYSeriesCollection dataset = new XYSeriesCollection(series); assertTrue(Double.isNaN(DatasetUtils.findYValue(dataset, 0, 100.0))); series.add(1.0, 5.0); assertTrue(Double.isNaN(DatasetUtils.findYValue(dataset, 0, 0.0))); assertEquals(5.0, DatasetUtils.findYValue(dataset, 0, 1.0), EPSILON); assertTrue(Double.isNaN(DatasetUtils.findYValue(dataset, 0, 2.0))); series.add(2.0, 10.0); assertTrue(Double.isNaN(DatasetUtils.findYValue(dataset, 0, 0.0))); assertEquals(5.0, DatasetUtils.findYValue(dataset, 0, 1.0), EPSILON); assertEquals(6.25, DatasetUtils.findYValue(dataset, 0, 1.25), EPSILON); assertEquals(7.5, DatasetUtils.findYValue(dataset, 0, 1.5), EPSILON); assertEquals(10.0, DatasetUtils.findYValue(dataset, 0, 2.0), EPSILON); assertTrue(Double.isNaN(DatasetUtils.findYValue(dataset, 0, 3.0))); } /** * Check the findYValue() method with a dataset that is not sorted. */ @Test public void testFindYValueNonSorted() { XYSeries series = new XYSeries("S1", false); XYSeriesCollection dataset = new XYSeriesCollection(series); assertTrue(Double.isNaN(DatasetUtils.findYValue(dataset, 0, 100.0))); series.add(1.0, 5.0); assertTrue(Double.isNaN(DatasetUtils.findYValue(dataset, 0, 0.0))); assertEquals(5.0, DatasetUtils.findYValue(dataset, 0, 1.0), EPSILON); assertTrue(Double.isNaN(DatasetUtils.findYValue(dataset, 0, 2.0))); series.add(0.0, 10.0); series.add(4.0, 20.0); assertTrue(Double.isNaN(DatasetUtils.findYValue(dataset, 0, -0.5))); assertEquals(10.0, DatasetUtils.findYValue(dataset, 0, 0.0), EPSILON); assertEquals(5.0, DatasetUtils.findYValue(dataset, 0, 1.0), EPSILON); assertEquals(15.0, DatasetUtils.findYValue(dataset, 0, 2.0), EPSILON); assertEquals(20.0, DatasetUtils.findYValue(dataset, 0, 4.0), EPSILON); assertEquals(17.5, DatasetUtils.findYValue(dataset, 0, 3.0), EPSILON); assertTrue(Double.isNaN(DatasetUtils.findYValue(dataset, 0, 5.0))); } /** * Check the findYValue() method with a dataset that allows duplicate * values. */ @Test public void testFindYValueWithDuplicates() { XYSeries series = new XYSeries("S1", true, true); XYSeriesCollection dataset = new XYSeriesCollection(series); assertTrue(Double.isNaN(DatasetUtils.findYValue(dataset, 0, 100.0))); series.add(1.0, 5.0); assertTrue(Double.isNaN(DatasetUtils.findYValue(dataset, 0, 0.0))); assertEquals(5.0, DatasetUtils.findYValue(dataset, 0, 1.0), EPSILON); assertTrue(Double.isNaN(DatasetUtils.findYValue(dataset, 0, 2.0))); series.add(1.0, 10.0); assertTrue(Double.isNaN(DatasetUtils.findYValue(dataset, 0, 0.0))); assertEquals(5.0, DatasetUtils.findYValue(dataset, 0, 1.0), EPSILON); assertTrue(Double.isNaN(DatasetUtils.findYValue(dataset, 0, 2.0))); series.add(2.0, 10.0); assertTrue(Double.isNaN(DatasetUtils.findYValue(dataset, 0, 0.0))); assertEquals(5.0, DatasetUtils.findYValue(dataset, 0, 1.0), EPSILON); assertEquals(10.0, DatasetUtils.findYValue(dataset, 0, 1.25), EPSILON); assertEquals(10.0, DatasetUtils.findYValue(dataset, 0, 1.5), EPSILON); assertEquals(10.0, DatasetUtils.findYValue(dataset, 0, 2.0), EPSILON); assertTrue(Double.isNaN(DatasetUtils.findYValue(dataset, 0, 3.0))); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/general/DefaultHeatMapDatasetTest.java000066400000000000000000000143651463604235500322040ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------ * DefaultHeatMapDatasetTest.java * ------------------------------ * (C) Copyright 2009-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.general; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Some tests for the {@link DefaultHeatMapDataset} class. */ public class DefaultHeatMapDatasetTest implements DatasetChangeListener { /** The last event received. */ private DatasetChangeEvent lastEvent; /** * Records the last event. * * @param event the last event. */ @Override public void datasetChanged(DatasetChangeEvent event) { this.lastEvent = event; } private static final double EPSILON = 0.0000000001; /** * Some general tests. */ @Test public void testGeneral() { DefaultHeatMapDataset d = new DefaultHeatMapDataset(10, 5, 0.0, 9.0, 0.0, 5.0); assertEquals(10, d.getXSampleCount()); assertEquals(5, d.getYSampleCount()); assertEquals(0.0, d.getMinimumXValue(), EPSILON); assertEquals(9.0, d.getMaximumXValue(), EPSILON); assertEquals(0.0, d.getMinimumYValue(), EPSILON); assertEquals(5.0, d.getMaximumYValue(), EPSILON); assertEquals(0.0, d.getZValue(0, 0), EPSILON); d.addChangeListener(this); d.setZValue(0, 0, 1.0, false); assertEquals(1.0, d.getZValue(0, 0), EPSILON); assertNull(this.lastEvent); d.setZValue(1, 2, 2.0); assertEquals(2.0, d.getZValue(1, 2), EPSILON); assertNotNull(this.lastEvent); } /** * Some tests for the equals() method. */ @Test public void testEquals() { DefaultHeatMapDataset d1 = new DefaultHeatMapDataset(5, 10, 1.0, 2.0, 3.0, 4.0); DefaultHeatMapDataset d2 = new DefaultHeatMapDataset(5, 10, 1.0, 2.0, 3.0, 4.0); assertEquals(d1, d2); d1 = new DefaultHeatMapDataset(6, 10, 1.0, 2.0, 3.0, 4.0); assertNotEquals(d1, d2); d2 = new DefaultHeatMapDataset(6, 10, 1.0, 2.0, 3.0, 4.0); assertEquals(d1, d2); d1 = new DefaultHeatMapDataset(6, 11, 1.0, 2.0, 3.0, 4.0); assertNotEquals(d1, d2); d2 = new DefaultHeatMapDataset(6, 11, 1.0, 2.0, 3.0, 4.0); assertEquals(d1, d2); d1 = new DefaultHeatMapDataset(6, 11, 2.0, 2.0, 3.0, 4.0); assertNotEquals(d1, d2); d2 = new DefaultHeatMapDataset(6, 11, 2.0, 2.0, 3.0, 4.0); assertEquals(d1, d2); d1 = new DefaultHeatMapDataset(6, 11, 2.0, 3.0, 3.0, 4.0); assertNotEquals(d1, d2); d2 = new DefaultHeatMapDataset(6, 11, 2.0, 3.0, 3.0, 4.0); assertEquals(d1, d2); d1 = new DefaultHeatMapDataset(6, 11, 2.0, 3.0, 4.0, 4.0); assertNotEquals(d1, d2); d2 = new DefaultHeatMapDataset(6, 11, 2.0, 3.0, 4.0, 4.0); assertEquals(d1, d2); d1 = new DefaultHeatMapDataset(6, 11, 2.0, 3.0, 4.0, 5.0); assertNotEquals(d1, d2); d2 = new DefaultHeatMapDataset(6, 11, 2.0, 3.0, 4.0, 5.0); assertEquals(d1, d2); d1.setZValue(1, 2, 3.0); assertNotEquals(d1, d2); d2.setZValue(1, 2, 3.0); assertEquals(d1, d2); d1.setZValue(0, 0, Double.NEGATIVE_INFINITY); assertNotEquals(d1, d2); d2.setZValue(0, 0, Double.NEGATIVE_INFINITY); assertEquals(d1, d2); d1.setZValue(0, 1, Double.POSITIVE_INFINITY); assertNotEquals(d1, d2); d2.setZValue(0, 1, Double.POSITIVE_INFINITY); assertEquals(d1, d2); d1.setZValue(0, 2, Double.NaN); assertNotEquals(d1, d2); d2.setZValue(0, 2, Double.NaN); assertEquals(d1, d2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { DefaultHeatMapDataset d1 = new DefaultHeatMapDataset(2, 3, -1.0, 4.0, -2.0, 5.0); d1.setZValue(0, 0, 10.0); d1.setZValue(0, 1, Double.NEGATIVE_INFINITY); d1.setZValue(0, 2, Double.POSITIVE_INFINITY); d1.setZValue(1, 0, Double.NaN); DefaultHeatMapDataset d2 = (DefaultHeatMapDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); // simple check for independence d1.setZValue(0, 0, 11.0); assertNotEquals(d1, d2); d2.setZValue(0, 0, 11.0); assertEquals(d1, d2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DefaultHeatMapDataset d1 = new DefaultHeatMapDataset(2, 3, -1.0, 4.0, -2.0, 5.0); d1.setZValue(0, 0, 10.0); d1.setZValue(0, 1, Double.NEGATIVE_INFINITY); d1.setZValue(0, 2, Double.POSITIVE_INFINITY); d1.setZValue(1, 0, Double.NaN); DefaultHeatMapDataset d2 = TestUtils.serialised(d1); assertEquals(d1, d2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/general/DefaultKeyedValueDatasetTest.java000066400000000000000000000073041463604235500327160ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------------- * DefaultKeyedValueDatasetTest.java * --------------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.general; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link DefaultKeyedValueDataset} class. */ public class DefaultKeyedValueDatasetTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { DefaultKeyedValueDataset d1 = new DefaultKeyedValueDataset("Test", 45.5); DefaultKeyedValueDataset d2 = new DefaultKeyedValueDataset("Test", 45.5); assertEquals(d1, d2); assertEquals(d2, d1); d1 = new DefaultKeyedValueDataset("Test 1", 45.5); d2 = new DefaultKeyedValueDataset("Test 2", 45.5); assertNotEquals(d1, d2); d1 = new DefaultKeyedValueDataset("Test", 45.5); d2 = new DefaultKeyedValueDataset("Test", 45.6); assertNotEquals(d1, d2); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { DefaultKeyedValueDataset d1 = new DefaultKeyedValueDataset("Test", 45.5); DefaultKeyedValueDataset d2 = (DefaultKeyedValueDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); } /** * Confirm that the clone is independent of the original. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloneIndependence() throws CloneNotSupportedException { DefaultKeyedValueDataset d1 = new DefaultKeyedValueDataset("Key", 10.0); DefaultKeyedValueDataset d2 = (DefaultKeyedValueDataset) d1.clone(); assertEquals(d1, d2); d2.updateValue(99.9); assertNotEquals(d1, d2); d2.updateValue(10.0); assertEquals(d1, d2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DefaultKeyedValueDataset d1 = new DefaultKeyedValueDataset("Test", 25.3); DefaultKeyedValueDataset d2 = TestUtils.serialised(d1); assertEquals(d1, d2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/general/DefaultKeyedValues2DDatasetTest.java000066400000000000000000000054131463604235500332660ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------------ * DefaultKeyedValues2DDatasetTest.java * ------------------------------------ * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * * */ package org.jfree.data.general; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link DefaultKeyedValues2DDataset} class. */ public class DefaultKeyedValues2DDatasetTest { /** * Confirm that cloning works. * * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { DefaultKeyedValues2DDataset d1 = new DefaultKeyedValues2DDataset(); d1.setValue(1, "V1", "C1"); d1.setValue(null, "V2", "C1"); d1.setValue(3, "V3", "C2"); DefaultKeyedValues2DDataset d2 = (DefaultKeyedValues2DDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DefaultKeyedValues2DDataset d1 = new DefaultKeyedValues2DDataset(); d1.addValue(234.2, "Row1", "Col1"); d1.addValue(null, "Row1", "Col2"); d1.addValue(345.9, "Row2", "Col1"); d1.addValue(452.7, "Row2", "Col2"); DefaultKeyedValues2DDataset d2 = TestUtils.serialised(d1); assertEquals(d1, d2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/general/DefaultKeyedValuesDatasetTest.java000066400000000000000000000052261463604235500331020ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------------- * DefaultKeyedValuesDatasetTest.java * ---------------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.general; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link DefaultKeyedValuesDataset} class. */ public class DefaultKeyedValuesDatasetTest { /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { DefaultKeyedValuesDataset d1 = new DefaultKeyedValuesDataset(); d1.setValue("V1", 1); d1.setValue("V2", null); d1.setValue("V3", 3); DefaultKeyedValuesDataset d2 = (DefaultKeyedValuesDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DefaultKeyedValuesDataset d1 = new DefaultKeyedValuesDataset(); d1.setValue("C1", 234.2); d1.setValue("C2", null); d1.setValue("C3", 345.9); d1.setValue("C4", 452.7); KeyedValuesDataset d2 = TestUtils.serialised(d1); assertEquals(d1, d2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/general/DefaultPieDatasetTest.java000066400000000000000000000132461463604235500313770ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * PieDatasetTest.java * ------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.general; import org.jfree.chart.TestUtils; import org.jfree.data.DefaultKeyedValues; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link org.jfree.data.general.PieDataset} class. */ public class DefaultPieDatasetTest implements DatasetChangeListener { private DatasetChangeEvent lastEvent; /** * Records the last event. * * @param event the last event. */ @Override public void datasetChanged(DatasetChangeEvent event) { this.lastEvent = event; } /** * Some tests for the clear() method. */ @Test public void testClear() { DefaultPieDataset d = new DefaultPieDataset<>(); d.addChangeListener(this); // no event is generated if the dataset is already empty d.clear(); assertNull(this.lastEvent); d.setValue("A", 1.0); assertEquals(1, d.getItemCount()); this.lastEvent = null; d.clear(); assertNotNull(this.lastEvent); assertEquals(0, d.getItemCount()); } /** * Some checks for the getKey(int) method. */ @Test public void testGetKey() { DefaultPieDataset d = new DefaultPieDataset<>(); d.setValue("A", 1.0); d.setValue("B", 2.0); assertEquals("A", d.getKey(0)); assertEquals("B", d.getKey(1)); boolean pass = false; try { d.getKey(-1); } catch (IndexOutOfBoundsException e) { pass = true; } assertTrue(pass); pass = false; try { d.getKey(2); } catch (IndexOutOfBoundsException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getIndex() method. */ @Test public void testGetIndex() { DefaultPieDataset d = new DefaultPieDataset<>(); d.setValue("A", 1.0); d.setValue("B", 2.0); assertEquals(0, d.getIndex("A")); assertEquals(1, d.getIndex("B")); assertEquals(-1, d.getIndex("XX")); boolean pass = false; try { d.getIndex(null); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Confirm that cloning works. * * @throws java.lang.CloneNotSupportedException if there is a cloning issue. */ @Test public void testCloning() throws CloneNotSupportedException { DefaultPieDataset d1 = new DefaultPieDataset<>(); d1.setValue("V1", 1); d1.setValue("V2", null); d1.setValue("V3", 3); DefaultPieDataset d2 = (DefaultPieDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DefaultPieDataset d1 = new DefaultPieDataset<>(); d1.setValue("C1", 234.2); d1.setValue("C2", null); d1.setValue("C3", 345.9); d1.setValue("C4", 452.7); DefaultPieDataset d2 = TestUtils.serialised(d1); assertEquals(d1, d2); } /** * A test for bug report https://github.com/jfree/jfreechart/issues/212 */ @Test public void testBug212() { DefaultPieDataset d = new DefaultPieDataset<>(); assertThrows(IndexOutOfBoundsException.class, () -> d.getValue(-1)); assertThrows(IndexOutOfBoundsException.class, () -> d.getValue(0)); d.setValue("A", 1.0); assertEquals(1.0, d.getValue(0)); assertThrows(IndexOutOfBoundsException.class, () -> d.getValue(1)); } /** * A test for https://github.com/jfree/jfreechart/issues/216 */ @Test public void testBug216() { DefaultKeyedValues kvs = new DefaultKeyedValues<>(); kvs.addValue("A", 1.0); kvs.addValue("B", 2.0); DefaultPieDataset d = new DefaultPieDataset<>(kvs); assertEquals(1.0, d.getValue("A")); assertEquals(2.0, d.getValue("B")); kvs.setValue("B", 3.0); assertEquals(2.0, d.getValue("B")); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/general/IntervalDataItem.java000066400000000000000000000040411463604235500303750ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * IntervalDataItem.java * --------------------- * (C) Copyright 2022, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.general; /** * A data holder for a value and an associated range or interval. Used by * the TestIntervalCategoryDataset class. */ public class IntervalDataItem { private Number value; private Number lowerBound; private Number upperBound; public IntervalDataItem(Number value, Number lowerBound, Number upperBound) { this.value = value; this.lowerBound = lowerBound; this.upperBound = upperBound; } public Number getLowerBound() { return lowerBound; } public Number getUpperBound() { return upperBound; } public Number getValue() { return value; } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/general/TestIntervalCategoryDataset.java000066400000000000000000000273571463604235500326470ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------------- * TestIntervalCategoryDataset.java * -------------------------------- * (C) Copyright 2009-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.general; import java.io.Serializable; import java.util.List; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.KeyedObjects2D; import org.jfree.data.UnknownKeyException; import org.jfree.data.category.IntervalCategoryDataset; /** * A test implementation of the {@link IntervalCategoryDataset} interface. */ public class TestIntervalCategoryDataset extends AbstractDataset implements IntervalCategoryDataset, PublicCloneable, Serializable { /** For serialization. */ private static final long serialVersionUID = -8168173757291644622L; /** A storage structure for the data. */ private KeyedObjects2D data; /** * Creates a new (empty) dataset. */ public TestIntervalCategoryDataset() { this.data = new KeyedObjects2D(); } /** * Returns the number of rows in the table. * * @return The row count. * * @see #getColumnCount() */ @Override public int getRowCount() { return this.data.getRowCount(); } /** * Returns the number of columns in the table. * * @return The column count. * * @see #getRowCount() */ @Override public int getColumnCount() { return this.data.getColumnCount(); } /** * Returns a value from the table. * * @param row the row index (zero-based). * @param column the column index (zero-based). * * @return The value (possibly {@code null}). */ @Override public Number getValue(int row, int column) { IntervalDataItem item = (IntervalDataItem) this.data.getObject(row, column); if (item == null) { return null; } return item.getValue(); } /** * Returns the key for the specified row. * * @param row the row index (zero-based). * * @return The row key. * * @see #getRowIndex(Comparable) * @see #getRowKeys() * @see #getColumnKey(int) */ @Override public Comparable getRowKey(int row) { return this.data.getRowKey(row); } /** * Returns the row index for a given key. * * @param key the row key ({@code null} not permitted). * * @return The row index. * * @see #getRowKey(int) */ @Override public int getRowIndex(Comparable key) { // defer null argument check return this.data.getRowIndex(key); } /** * Returns the row keys. * * @return The keys. * * @see #getRowKey(int) */ @Override public List getRowKeys() { return this.data.getRowKeys(); } /** * Returns a column key. * * @param column the column index (zero-based). * * @return The column key. * * @see #getColumnIndex(Comparable) */ @Override public Comparable getColumnKey(int column) { return this.data.getColumnKey(column); } /** * Returns the column index for a given key. * * @param key the column key ({@code null} not permitted). * * @return The column index. * * @see #getColumnKey(int) */ @Override public int getColumnIndex(Comparable key) { // defer null argument check return this.data.getColumnIndex(key); } /** * Returns the column keys. * * @return The keys. * * @see #getColumnKey(int) */ @Override public List getColumnKeys() { return this.data.getColumnKeys(); } /** * Returns the value for a pair of keys. * * @param rowKey the row key ({@code null} not permitted). * @param columnKey the column key ({@code null} not permitted). * * @return The value (possibly {@code null}). * * @throws UnknownKeyException if either key is not defined in the dataset. */ @Override public Number getValue(Comparable rowKey, Comparable columnKey) { IntervalDataItem item = (IntervalDataItem) this.data.getObject(rowKey, columnKey); if (item == null) { return null; } return item.getValue(); } /** * Adds a value to the table. Performs the same function as setValue(). * * @param value the value. * @param rowKey the row key. * @param columnKey the column key. * * @see #getValue(Comparable, Comparable) */ public void addItem(Number value, Number lower, Number upper, Comparable rowKey, Comparable columnKey) { IntervalDataItem item = new IntervalDataItem(value, lower, upper); this.data.addObject(item, rowKey, columnKey); fireDatasetChanged(); } /** * Adds or updates a value in the table and sends a * {@link DatasetChangeEvent} to all registered listeners. * * @param value the value ({@code null} permitted). * @param rowKey the row key ({@code null} not permitted). * @param columnKey the column key ({@code null} not permitted). * * @see #getValue(Comparable, Comparable) */ public void setItem(Number value, Number lower, Number upper, Comparable rowKey, Comparable columnKey) { IntervalDataItem item = new IntervalDataItem(value, lower, upper); this.data.addObject(item, rowKey, columnKey); fireDatasetChanged(); } /** * Removes a value from the dataset and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param rowKey the row key. * @param columnKey the column key. */ public void removeItem(Comparable rowKey, Comparable columnKey) { this.data.removeObject(rowKey, columnKey); fireDatasetChanged(); } /** * Removes a row from the dataset and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param rowIndex the row index. * * @see #removeColumn(int) */ public void removeRow(int rowIndex) { this.data.removeRow(rowIndex); fireDatasetChanged(); } /** * Removes a row from the dataset and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param rowKey the row key. * * @see #removeColumn(Comparable) */ public void removeRow(Comparable rowKey) { this.data.removeRow(rowKey); fireDatasetChanged(); } /** * Removes a column from the dataset and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param columnIndex the column index. * * @see #removeRow(int) */ public void removeColumn(int columnIndex) { this.data.removeColumn(columnIndex); fireDatasetChanged(); } /** * Removes a column from the dataset and sends a {@link DatasetChangeEvent} * to all registered listeners. * * @param columnKey the column key ({@code null} not permitted). * * @see #removeRow(Comparable) * * @throws UnknownKeyException if {@code columnKey} is not defined * in the dataset. */ public void removeColumn(Comparable columnKey) { this.data.removeColumn(columnKey); fireDatasetChanged(); } /** * Clears all data from the dataset and sends a {@link DatasetChangeEvent} * to all registered listeners. */ public void clear() { this.data.clear(); fireDatasetChanged(); } /** * Tests this dataset for equality with an arbitrary object. * * @param obj the object ({@code null} permitted). * * @return A boolean. */ @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (!(obj instanceof TestIntervalCategoryDataset)) { return false; } TestIntervalCategoryDataset that = (TestIntervalCategoryDataset) obj; if (!getRowKeys().equals(that.getRowKeys())) { return false; } if (!getColumnKeys().equals(that.getColumnKeys())) { return false; } int rowCount = getRowCount(); int colCount = getColumnCount(); for (int r = 0; r < rowCount; r++) { for (int c = 0; c < colCount; c++) { Number v1 = getValue(r, c); Number v2 = that.getValue(r, c); if (v1 == null) { if (v2 != null) { return false; } } else if (!v1.equals(v2)) { return false; } } } return true; } /** * Returns a hash code for the dataset. * * @return A hash code. */ @Override public int hashCode() { return this.data.hashCode(); } /** * Returns a clone of the dataset. * * @return A clone. * * @throws CloneNotSupportedException if there is a problem cloning the * dataset. */ @Override public Object clone() throws CloneNotSupportedException { TestIntervalCategoryDataset clone = (TestIntervalCategoryDataset) super.clone(); clone.data = (KeyedObjects2D) this.data.clone(); return clone; } @Override public Number getStartValue(int series, int category) { IntervalDataItem item = (IntervalDataItem) this.data.getObject(series, category); if (item == null) { return null; } return item.getLowerBound(); } @Override public Number getStartValue(Comparable series, Comparable category) { IntervalDataItem item = (IntervalDataItem) this.data.getObject(series, category); if (item == null) { return null; } return item.getLowerBound(); } @Override public Number getEndValue(int series, int category) { IntervalDataItem item = (IntervalDataItem) this.data.getObject(series, category); if (item == null) { return null; } return item.getUpperBound(); } @Override public Number getEndValue(Comparable series, Comparable category) { IntervalDataItem item = (IntervalDataItem) this.data.getObject(series, category); if (item == null) { return null; } return item.getUpperBound(); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/package.html000066400000000000000000000002321463604235500251770ustar00rootroot00000000000000 Test cases for the classes in com.jrefinery.data.*. jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/statistics/000077500000000000000000000000001463604235500251135ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/statistics/BoxAndWhiskerCalculatorTest.java000066400000000000000000000123751463604235500333500ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------------- * BoxAndWhiskerCalculatorTest.java * -------------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.statistics; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.Test; /** * Tests for the {@link BoxAndWhiskerCalculator} class. */ public class BoxAndWhiskerCalculatorTest { /** * Some checks for the calculateBoxAndWhiskerStatistics() method. */ @Test public void testCalculateBoxAndWhiskerStatistics() { // try null list boolean pass = false; try { BoxAndWhiskerCalculator.calculateBoxAndWhiskerStatistics(null); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); // try a list containing a single value List values = new ArrayList<>(); values.add(1.1); BoxAndWhiskerItem item = BoxAndWhiskerCalculator.calculateBoxAndWhiskerStatistics(values); assertEquals(1.1, item.getMean().doubleValue(), EPSILON); assertEquals(1.1, item.getMedian().doubleValue(), EPSILON); assertEquals(1.1, item.getQ1().doubleValue(), EPSILON); assertEquals(1.1, item.getQ3().doubleValue(), EPSILON); } private static final double EPSILON = 0.000000001; /** * Tests the Q1 calculation. */ @Test public void testCalculateQ1() { // try null argument boolean pass = false; try { BoxAndWhiskerCalculator.calculateQ1(null); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); List values = new ArrayList<>(); double q1 = BoxAndWhiskerCalculator.calculateQ1(values); assertTrue(Double.isNaN(q1)); values.add(1.0); q1 = BoxAndWhiskerCalculator.calculateQ1(values); assertEquals(q1, 1.0, EPSILON); values.add(2.0); q1 = BoxAndWhiskerCalculator.calculateQ1(values); assertEquals(q1, 1.0, EPSILON); values.add(3.0); q1 = BoxAndWhiskerCalculator.calculateQ1(values); assertEquals(q1, 1.5, EPSILON); values.add(4.0); q1 = BoxAndWhiskerCalculator.calculateQ1(values); assertEquals(q1, 1.5, EPSILON); } /** * Tests the Q3 calculation. */ @Test public void testCalculateQ3() { // try null argument boolean pass = false; try { BoxAndWhiskerCalculator.calculateQ3(null); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); List values = new ArrayList<>(); double q3 = BoxAndWhiskerCalculator.calculateQ3(values); assertTrue(Double.isNaN(q3)); values.add(1.0); q3 = BoxAndWhiskerCalculator.calculateQ3(values); assertEquals(q3, 1.0, EPSILON); values.add(2.0); q3 = BoxAndWhiskerCalculator.calculateQ3(values); assertEquals(q3, 2.0, EPSILON); values.add(3.0); q3 = BoxAndWhiskerCalculator.calculateQ3(values); assertEquals(q3, 2.5, EPSILON); values.add(4.0); q3 = BoxAndWhiskerCalculator.calculateQ3(values); assertEquals(q3, 3.5, EPSILON); } /** * The test case included in bug report 1593149. */ @Test public void test1593149() { ArrayList theList = new ArrayList<>(5); theList.add(0, 1.0); theList.add(1, 2.0); theList.add(2, Double.NaN); theList.add(3, 3.0); theList.add(4, 4.0); BoxAndWhiskerItem theItem = BoxAndWhiskerCalculator.calculateBoxAndWhiskerStatistics(theList); assertEquals(1.0, theItem.getMinRegularValue().doubleValue(), EPSILON); assertEquals(4.0, theItem.getMaxRegularValue().doubleValue(), EPSILON); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/statistics/BoxAndWhiskerItemTest.java000066400000000000000000000050131463604235500321440ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * BoxAndWhiskerItemTest.java * -------------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.statistics; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.ArrayList; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; /** * Tests for the {@link BoxAndWhiskerItem} class. */ public class BoxAndWhiskerItemTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { BoxAndWhiskerItem i1 = new BoxAndWhiskerItem(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, new ArrayList()); BoxAndWhiskerItem i2 = new BoxAndWhiskerItem(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, new ArrayList()); assertEquals(i1, i2); assertEquals(i2, i1); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { BoxAndWhiskerItem i1 = new BoxAndWhiskerItem(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, new ArrayList()); BoxAndWhiskerItem i2 = TestUtils.serialised(i1); assertEquals(i1, i2); } } DefaultBoxAndWhiskerCategoryDatasetTest.java000066400000000000000000000246151463604235500355700ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/statistics/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------------------------- * DefaultBoxAndWhiskerCategoryDatasetTest.java * -------------------------------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.statistics; import java.util.ArrayList; import org.jfree.chart.TestUtils; import org.jfree.data.Range; import org.jfree.data.UnknownKeyException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link DefaultBoxAndWhiskerCategoryDataset} class. */ public class DefaultBoxAndWhiskerCategoryDatasetTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { DefaultBoxAndWhiskerCategoryDataset d1 = new DefaultBoxAndWhiskerCategoryDataset(); d1.add(new BoxAndWhiskerItem(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, new ArrayList()), "ROW1", "COLUMN1"); DefaultBoxAndWhiskerCategoryDataset d2 = new DefaultBoxAndWhiskerCategoryDataset(); d2.add(new BoxAndWhiskerItem(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, new ArrayList()), "ROW1", "COLUMN1"); assertEquals(d1, d2); assertEquals(d2, d1); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DefaultBoxAndWhiskerCategoryDataset d1 = new DefaultBoxAndWhiskerCategoryDataset(); d1.add(new BoxAndWhiskerItem(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, new ArrayList()), "ROW1", "COLUMN1"); DefaultBoxAndWhiskerCategoryDataset d2 = TestUtils.serialised(d1); assertEquals(d1, d2); } /** * Confirm that cloning works. * * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { DefaultBoxAndWhiskerCategoryDataset d1 = new DefaultBoxAndWhiskerCategoryDataset(); d1.add(new BoxAndWhiskerItem(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, new ArrayList()), "ROW1", "COLUMN1"); DefaultBoxAndWhiskerCategoryDataset d2 = (DefaultBoxAndWhiskerCategoryDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); // test independence d1.add(new BoxAndWhiskerItem(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, new ArrayList()), "ROW2", "COLUMN1"); assertNotEquals(d1, d2); } /** * A simple test for bug report 1701822. */ @Test public void test1701822() { DefaultBoxAndWhiskerCategoryDataset dataset = new DefaultBoxAndWhiskerCategoryDataset(); try { dataset.add(new BoxAndWhiskerItem(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, null, 8.0, new ArrayList()), "ROW1", "COLUMN1"); dataset.add(new BoxAndWhiskerItem(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, null, new ArrayList()), "ROW1", "COLUMN2"); } catch (NullPointerException e) { fail(); } } private static final double EPSILON = 0.0000000001; /** * Some checks for the add() method. */ @Test public void testAdd() { DefaultBoxAndWhiskerCategoryDataset dataset = new DefaultBoxAndWhiskerCategoryDataset(); BoxAndWhiskerItem item1 = new BoxAndWhiskerItem(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, new ArrayList()); dataset.add(item1, "R1", "C1"); assertEquals(2.0, dataset.getValue("R1", "C1").doubleValue(), EPSILON); assertEquals(1.0, dataset.getMeanValue("R1", "C1").doubleValue(), EPSILON); assertEquals(2.0, dataset.getMedianValue("R1", "C1").doubleValue(), EPSILON); assertEquals(3.0, dataset.getQ1Value("R1", "C1").doubleValue(), EPSILON); assertEquals(4.0, dataset.getQ3Value("R1", "C1").doubleValue(), EPSILON); assertEquals(5.0, dataset.getMinRegularValue("R1", "C1").doubleValue(), EPSILON); assertEquals(6.0, dataset.getMaxRegularValue("R1", "C1").doubleValue(), EPSILON); assertEquals(7.0, dataset.getMinOutlier("R1", "C1").doubleValue(), EPSILON); assertEquals(8.0, dataset.getMaxOutlier("R1", "C1").doubleValue(), EPSILON); assertEquals(new Range(7.0, 8.0), dataset.getRangeBounds(false)); } /** * Some checks for the add() method. */ @Test public void testAddUpdatesCachedRange() { DefaultBoxAndWhiskerCategoryDataset dataset = new DefaultBoxAndWhiskerCategoryDataset(); BoxAndWhiskerItem item1 = new BoxAndWhiskerItem(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, new ArrayList()); dataset.add(item1, "R1", "C1"); // now overwrite this item with another BoxAndWhiskerItem item2 = new BoxAndWhiskerItem(1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, new ArrayList()); dataset.add(item2, "R1", "C1"); assertEquals(2.5, dataset.getValue("R1", "C1").doubleValue(), EPSILON); assertEquals(1.5, dataset.getMeanValue("R1", "C1").doubleValue(), EPSILON); assertEquals(2.5, dataset.getMedianValue("R1", "C1").doubleValue(), EPSILON); assertEquals(3.5, dataset.getQ1Value("R1", "C1").doubleValue(), EPSILON); assertEquals(4.5, dataset.getQ3Value("R1", "C1").doubleValue(), EPSILON); assertEquals(5.5, dataset.getMinRegularValue("R1", "C1").doubleValue(), EPSILON); assertEquals(6.5, dataset.getMaxRegularValue("R1", "C1").doubleValue(), EPSILON); assertEquals(7.5, dataset.getMinOutlier("R1", "C1").doubleValue(), EPSILON); assertEquals(8.5, dataset.getMaxOutlier("R1", "C1").doubleValue(), EPSILON); assertEquals(new Range(7.5, 8.5), dataset.getRangeBounds(false)); } /** * Some basic checks for the constructor. */ @Test public void testConstructor() { DefaultBoxAndWhiskerCategoryDataset dataset = new DefaultBoxAndWhiskerCategoryDataset(); assertEquals(0, dataset.getColumnCount()); assertEquals(0, dataset.getRowCount()); assertTrue(Double.isNaN(dataset.getRangeLowerBound(false))); assertTrue(Double.isNaN(dataset.getRangeUpperBound(false))); } /** * Some checks for the getRangeBounds() method. */ @Test public void testGetRangeBounds() { DefaultBoxAndWhiskerCategoryDataset d1 = new DefaultBoxAndWhiskerCategoryDataset(); d1.add(new BoxAndWhiskerItem(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, new ArrayList()), "R1", "C1"); assertEquals(new Range(7.0, 8.0), d1.getRangeBounds(false)); assertEquals(new Range(7.0, 8.0), d1.getRangeBounds(true)); d1.add(new BoxAndWhiskerItem(1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, new ArrayList()), "R1", "C1"); assertEquals(new Range(7.5, 8.5), d1.getRangeBounds(false)); assertEquals(new Range(7.5, 8.5), d1.getRangeBounds(true)); d1.add(new BoxAndWhiskerItem(2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, new ArrayList()), "R2", "C1"); assertEquals(new Range(7.5, 9.5), d1.getRangeBounds(false)); assertEquals(new Range(7.5, 9.5), d1.getRangeBounds(true)); // this replaces the entry with the current minimum value, but the new // minimum value is now in a different item d1.add(new BoxAndWhiskerItem(1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 8.6, 9.6, new ArrayList()), "R1", "C1"); assertEquals(new Range(8.5, 9.6), d1.getRangeBounds(false)); assertEquals(new Range(8.5, 9.6), d1.getRangeBounds(true)); } /** * Some checks for the remove method. */ @Test public void testRemove() { DefaultBoxAndWhiskerCategoryDataset data = new DefaultBoxAndWhiskerCategoryDataset(); boolean pass = false; try { data.remove("R1", "R2"); } catch (UnknownKeyException e) { pass = true; } assertTrue(pass); data.add(new BoxAndWhiskerItem(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, new ArrayList()), "R1", "C1"); assertEquals(new Range(7.0, 8.0), data.getRangeBounds(false)); assertEquals(new Range(7.0, 8.0), data.getRangeBounds(true)); data.add(new BoxAndWhiskerItem(2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, new ArrayList()), "R2", "C1"); assertEquals(new Range(7.0, 9.5), data.getRangeBounds(false)); assertEquals(new Range(7.0, 9.5), data.getRangeBounds(true)); data.remove("R1", "C1"); assertEquals(new Range(8.5, 9.5), data.getRangeBounds(false)); assertEquals(new Range(8.5, 9.5), data.getRangeBounds(true)); } } DefaultBoxAndWhiskerXYDatasetTest.java000066400000000000000000000153251463604235500343510ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/statistics/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------------------- * DefaultBoxAndWhiskerXYDatasetTest.java * -------------------------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.statistics; import java.util.ArrayList; import java.util.Date; import org.jfree.chart.TestUtils; import org.jfree.data.Range; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link DefaultBoxAndWhiskerXYDataset} class. */ public class DefaultBoxAndWhiskerXYDatasetTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { DefaultBoxAndWhiskerXYDataset d1 = new DefaultBoxAndWhiskerXYDataset( "Series"); DefaultBoxAndWhiskerXYDataset d2 = new DefaultBoxAndWhiskerXYDataset( "Series"); assertEquals(d1, d2); d1.add(new Date(1L), new BoxAndWhiskerItem(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, new ArrayList())); assertNotEquals(d1, d2); d2.add(new Date(1L), new BoxAndWhiskerItem(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, new ArrayList())); assertEquals(d1, d2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DefaultBoxAndWhiskerXYDataset d1 = new DefaultBoxAndWhiskerXYDataset( "Series"); d1.add(new Date(1L), new BoxAndWhiskerItem(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, new ArrayList())); DefaultBoxAndWhiskerXYDataset d2 = (DefaultBoxAndWhiskerXYDataset) TestUtils.serialised(d1); assertEquals(d1, d2); // test independence d1.add(new Date(2L), new BoxAndWhiskerItem(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, new ArrayList())); assertNotEquals(d1, d2); } /** * Confirm that cloning works. * * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { DefaultBoxAndWhiskerXYDataset d1 = new DefaultBoxAndWhiskerXYDataset( "Series"); d1.add(new Date(1L), new BoxAndWhiskerItem(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, new ArrayList())); DefaultBoxAndWhiskerXYDataset d2 = (DefaultBoxAndWhiskerXYDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); // test independence d1.add(new Date(2L), new BoxAndWhiskerItem(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, new ArrayList())); assertNotEquals(d1, d2); } private static final double EPSILON = 0.0000000001; /** * Some checks for the add() method. */ @Test public void testAdd() { DefaultBoxAndWhiskerXYDataset dataset = new DefaultBoxAndWhiskerXYDataset("S1"); BoxAndWhiskerItem item1 = new BoxAndWhiskerItem(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, new ArrayList()); dataset.add(new Date(33L), item1); assertEquals(1.0, dataset.getY(0, 0).doubleValue(), EPSILON); assertEquals(1.0, dataset.getMeanValue(0, 0).doubleValue(), EPSILON); assertEquals(2.0, dataset.getMedianValue(0, 0).doubleValue(), EPSILON); assertEquals(3.0, dataset.getQ1Value(0, 0).doubleValue(), EPSILON); assertEquals(4.0, dataset.getQ3Value(0, 0).doubleValue(), EPSILON); assertEquals(5.0, dataset.getMinRegularValue(0, 0).doubleValue(), EPSILON); assertEquals(6.0, dataset.getMaxRegularValue(0, 0).doubleValue(), EPSILON); assertEquals(7.0, dataset.getMinOutlier(0, 0).doubleValue(), EPSILON); assertEquals(8.0, dataset.getMaxOutlier(0, 0).doubleValue(), EPSILON); assertEquals(new Range(5.0, 6.0), dataset.getRangeBounds(false)); } /** * Some basic checks for the constructor. */ @Test public void testConstructor() { DefaultBoxAndWhiskerXYDataset dataset = new DefaultBoxAndWhiskerXYDataset("S1"); assertEquals(1, dataset.getSeriesCount()); assertEquals(0, dataset.getItemCount(0)); assertTrue(Double.isNaN(dataset.getRangeLowerBound(false))); assertTrue(Double.isNaN(dataset.getRangeUpperBound(false))); } /** * Some checks for the getRangeBounds() method. */ @Test public void testGetRangeBounds() { DefaultBoxAndWhiskerXYDataset d1 = new DefaultBoxAndWhiskerXYDataset("S"); d1.add(new Date(1L), new BoxAndWhiskerItem(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, new ArrayList())); assertEquals(new Range(5.0, 6.0), d1.getRangeBounds(false)); assertEquals(new Range(5.0, 6.0), d1.getRangeBounds(true)); d1.add(new Date(1L), new BoxAndWhiskerItem(1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, new ArrayList())); assertEquals(new Range(5.0, 6.5), d1.getRangeBounds(false)); assertEquals(new Range(5.0, 6.5), d1.getRangeBounds(true)); d1.add(new Date(2L), new BoxAndWhiskerItem(2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, new ArrayList())); assertEquals(new Range(5.0, 7.5), d1.getRangeBounds(false)); assertEquals(new Range(5.0, 7.5), d1.getRangeBounds(true)); } } DefaultMultiValueCategoryDatasetTest.java000066400000000000000000000162021463604235500351400ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/statistics/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------------------- * DefaultMultiValueCategoryDatasetTest.java * ----------------------------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.statistics; import java.util.ArrayList; import java.util.List; import org.jfree.chart.TestUtils; import org.jfree.data.UnknownKeyException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link DefaultMultiValueCategoryDataset} class. */ public class DefaultMultiValueCategoryDatasetTest { /** * Some checks for the getValue() method. */ @Test public void testGetValue() { DefaultMultiValueCategoryDataset d = new DefaultMultiValueCategoryDataset(); List values = new ArrayList<>(); values.add(1); values.add(2); d.add(values, "R1", "C1"); assertEquals(1.5, d.getValue("R1", "C1")); boolean pass = false; try { d.getValue("XX", "C1"); } catch (UnknownKeyException e) { pass = true; } assertTrue(pass); pass = false; try { d.getValue("R1", "XX"); } catch (UnknownKeyException e) { pass = true; } assertTrue(pass); } /** * A simple check for the getValue(int, int) method. */ @Test public void testGetValue2() { DefaultMultiValueCategoryDataset d = new DefaultMultiValueCategoryDataset(); boolean pass = false; try { /* Number n =*/ d.getValue(0, 0); } catch (IndexOutOfBoundsException e) { pass = true; } assertTrue(pass); } /** * Some tests for the getRowCount() method. */ @Test public void testGetRowCount() { DefaultMultiValueCategoryDataset d = new DefaultMultiValueCategoryDataset(); assertEquals(0, d.getRowCount()); List values = new ArrayList<>(); d.add(values, "R1", "C1"); assertEquals(1, d.getRowCount()); d.add(values, "R2", "C1"); assertEquals(2, d.getRowCount()); d.add(values, "R2", "C1"); assertEquals(2, d.getRowCount()); } /** * Some tests for the getColumnCount() method. */ @Test public void testGetColumnCount() { DefaultMultiValueCategoryDataset d = new DefaultMultiValueCategoryDataset(); assertEquals(0, d.getColumnCount()); List values = new ArrayList<>(); d.add(values, "R1", "C1"); assertEquals(1, d.getColumnCount()); d.add(values, "R1", "C2"); assertEquals(2, d.getColumnCount()); d.add(values, "R1", "C2"); assertEquals(2, d.getColumnCount()); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { DefaultMultiValueCategoryDataset d1 = new DefaultMultiValueCategoryDataset(); DefaultMultiValueCategoryDataset d2 = new DefaultMultiValueCategoryDataset(); assertEquals(d1, d2); assertEquals(d2, d1); List values = new ArrayList<>(); d1.add(values, "R1", "C1"); assertNotEquals(d1, d2); d2.add(values, "R1", "C1"); assertEquals(d1, d2); values.add(99); d1.add(values, "R1", "C1"); assertNotEquals(d1, d2); d2.add(values, "R1", "C1"); assertEquals(d1, d2); values.add(99); d1.add(values, "R1", "C2"); assertNotEquals(d1, d2); d2.add(values, "R1", "C2"); assertEquals(d1, d2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DefaultMultiValueCategoryDataset d1 = new DefaultMultiValueCategoryDataset(); DefaultMultiValueCategoryDataset d2 = TestUtils.serialised(d1); assertEquals(d1, d2); } /** * Some checks for the add() method. */ @Test public void testAddValue() { DefaultMultiValueCategoryDataset d1 = new DefaultMultiValueCategoryDataset(); boolean pass = false; try { d1.add(null, "R1", "C1"); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); List values = new ArrayList<>(); d1.add(values, "R2", "C1"); assertEquals(values, d1.getValues("R2", "C1")); pass = false; try { d1.add(values, null, "C2"); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { DefaultMultiValueCategoryDataset d1 = new DefaultMultiValueCategoryDataset(); DefaultMultiValueCategoryDataset d2 = (DefaultMultiValueCategoryDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); // try a dataset with some content... List values = new ArrayList<>(); values.add(99); d1.add(values, "R1", "C1"); d2 = (DefaultMultiValueCategoryDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); // check that the clone doesn't share the same underlying arrays. List values2 = new ArrayList<>(); values2.add(111); d1.add(values2, "R2", "C2"); assertNotEquals(d1, d2); d2.add(values2, "R2", "C2"); assertEquals(d1, d2); } } DefaultStatisticalCategoryDatasetTest.java000066400000000000000000000222241463604235500353360ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/statistics/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------------------ * DefaultStatisticalCategoryDatasetTest.java * ------------------------------------------ * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.statistics; import org.jfree.chart.TestUtils; import org.jfree.data.Range; import org.jfree.data.UnknownKeyException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link DefaultStatisticalCategoryDataset} class. */ public class DefaultStatisticalCategoryDatasetTest { /** * Some checks for the getRangeBounds() method. */ @Test public void testGetRangeBounds() { DefaultStatisticalCategoryDataset d = new DefaultStatisticalCategoryDataset(); // an empty dataset should return null for bounds assertNull(d.getRangeBounds(true)); // try a dataset with a single value d.add(4.5, 1.0, "R1", "C1"); assertEquals(new Range(4.5, 4.5), d.getRangeBounds(false)); assertEquals(new Range(3.5, 5.5), d.getRangeBounds(true)); // try a dataset with two values d.add(0.5, 2.0, "R1", "C2"); assertEquals(new Range(0.5, 4.5), d.getRangeBounds(false)); assertEquals(new Range(-1.5, 5.5), d.getRangeBounds(true)); // try a Double.NaN d.add(Double.NaN, 0.0, "R1", "C3"); assertEquals(new Range(0.5, 4.5), d.getRangeBounds(false)); assertEquals(new Range(-1.5, 5.5), d.getRangeBounds(true)); // try a Double.NEGATIVE_INFINITY d.add(Double.NEGATIVE_INFINITY, 0.0, "R1", "C3"); assertEquals(new Range(Double.NEGATIVE_INFINITY, 4.5), d.getRangeBounds(false)); assertEquals(new Range(Double.NEGATIVE_INFINITY, 5.5), d.getRangeBounds(true)); // try a Double.POSITIVE_INFINITY d.add(Double.POSITIVE_INFINITY, 0.0, "R1", "C3"); assertEquals(new Range(0.5, Double.POSITIVE_INFINITY), d.getRangeBounds(false)); assertEquals(new Range(-1.5, Double.POSITIVE_INFINITY), d.getRangeBounds(true)); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { DefaultStatisticalCategoryDataset d1 = new DefaultStatisticalCategoryDataset(); DefaultStatisticalCategoryDataset d2 = new DefaultStatisticalCategoryDataset(); assertEquals(d1, d2); assertEquals(d2, d1); } /** * Some checks for cloning. */ @Test public void testCloning() { DefaultStatisticalCategoryDataset d1 = new DefaultStatisticalCategoryDataset(); d1.add(1.1, 2.2, "R1", "C1"); d1.add(3.3, 4.4, "R1", "C2"); d1.add(null, 5.5, "R1", "C3"); d1.add(6.6, null, "R2", "C3"); DefaultStatisticalCategoryDataset d2 = null; try { d2 = (DefaultStatisticalCategoryDataset) d1.clone(); } catch (CloneNotSupportedException e) { fail(e.toString()); } assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); // check independence d1.add(1.1, 2.2, "R3", "C1"); assertNotEquals(d1, d2); } /** * Check serialization of a default instance. */ @Test public void testSerialization1() { DefaultStatisticalCategoryDataset d1 = new DefaultStatisticalCategoryDataset(); d1.add(1.1, 2.2, "R1", "C1"); d1.add(3.3, 4.4, "R1", "C2"); d1.add(null, 5.5, "R1", "C3"); d1.add(6.6, null, "R2", "C3"); DefaultStatisticalCategoryDataset d2 = TestUtils.serialised(d1); assertEquals(d1, d2); } /** * Check serialization of a more complex instance. */ @Test public void testSerialization2() { DefaultStatisticalCategoryDataset d1 = new DefaultStatisticalCategoryDataset(); d1.add(1.2, 3.4, "Row 1", "Column 1"); DefaultStatisticalCategoryDataset d2 = TestUtils.serialised(d1); assertEquals(d1, d2); } private static final double EPSILON = 0.0000000001; /** * Some checks for the add() method. */ @Test public void testAdd() { DefaultStatisticalCategoryDataset d1 = new DefaultStatisticalCategoryDataset(); d1.add(1.0, 2.0, "R1", "C1"); assertEquals(1.0, d1.getValue("R1", "C1").doubleValue(), EPSILON); assertEquals(2.0, d1.getStdDevValue("R1", "C1").doubleValue(), EPSILON); // overwrite the value d1.add(10.0, 20.0, "R1", "C1"); assertEquals(10.0, d1.getValue("R1", "C1").doubleValue(), EPSILON); assertEquals(20.0, d1.getStdDevValue("R1", "C1").doubleValue(), EPSILON); } /** * Some checks for the getRangeLowerBound() method. */ @Test public void testGetRangeLowerBound() { DefaultStatisticalCategoryDataset d1 = new DefaultStatisticalCategoryDataset(); d1.add(1.0, 2.0, "R1", "C1"); assertEquals(1.0, d1.getRangeLowerBound(false), EPSILON); assertEquals(-1.0, d1.getRangeLowerBound(true), EPSILON); } /** * Some checks for the getRangeUpperBound() method. */ @Test public void testGetRangeUpperBound() { DefaultStatisticalCategoryDataset d1 = new DefaultStatisticalCategoryDataset(); d1.add(1.0, 2.0, "R1", "C1"); assertEquals(1.0, d1.getRangeUpperBound(false), EPSILON); assertEquals(3.0, d1.getRangeUpperBound(true), EPSILON); } /** * Some checks for the getRangeBounds() method. */ @Test public void testGetRangeBounds2() { DefaultStatisticalCategoryDataset d1 = new DefaultStatisticalCategoryDataset(); d1.add(1.0, 2.0, "R1", "C1"); assertEquals(new Range(1.0, 1.0), d1.getRangeBounds(false)); assertEquals(new Range(-1.0, 3.0), d1.getRangeBounds(true)); d1.add(10.0, 20.0, "R1", "C1"); assertEquals(new Range(10.0, 10.0), d1.getRangeBounds(false)); assertEquals(new Range(-10.0, 30.0), d1.getRangeBounds(true)); } /** * Some checks for the remove method. */ @Test public void testRemove() { DefaultStatisticalCategoryDataset data = new DefaultStatisticalCategoryDataset(); boolean pass = false; try { data.remove("R1", "R2"); } catch (UnknownKeyException e) { pass = true; } assertTrue(pass); data.add(1.0, 0.5, "R1", "C1"); assertEquals(new Range(1.0, 1.0), data.getRangeBounds(false)); assertEquals(new Range(0.5, 1.5), data.getRangeBounds(true)); data.add(1.4, 0.2, "R2", "C1"); assertEquals(1.0, data.getRangeLowerBound(false), EPSILON); assertEquals(1.4, data.getRangeUpperBound(false), EPSILON); assertEquals(0.5, data.getRangeLowerBound(true), EPSILON); assertEquals(1.6, data.getRangeUpperBound(true), EPSILON); data.remove("R1", "C1"); assertEquals(1.4, data.getRangeLowerBound(false), EPSILON); assertEquals(1.4, data.getRangeUpperBound(false), EPSILON); assertEquals(1.2, data.getRangeLowerBound(true), EPSILON); assertEquals(1.6, data.getRangeUpperBound(true), EPSILON); } /** * A test for bug 3072674. */ @Test public void test3072674() { DefaultStatisticalCategoryDataset dataset = new DefaultStatisticalCategoryDataset(); dataset.add(1.0, Double.NaN, "R1", "C1"); assertEquals(1.0, dataset.getRangeLowerBound(true), EPSILON); assertEquals(1.0, dataset.getRangeUpperBound(true), EPSILON); Range r = dataset.getRangeBounds(true); assertEquals(1.0, r.getLowerBound(), EPSILON); assertEquals(1.0, r.getUpperBound(), EPSILON); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/statistics/HistogramBinTest.java000066400000000000000000000052631463604235500312120ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * HistogramBinTest.java * --------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.statistics; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link HistogramBin} class. */ public class HistogramBinTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { double start = 10.0; double end = 20.0; HistogramBin b1 = new HistogramBin(start, end); HistogramBin b2 = new HistogramBin(start, end); assertEquals(b1, b2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { double start = 10.0; double end = 20.0; HistogramBin b1 = new HistogramBin(start, end); HistogramBin b2 = (HistogramBin) b1.clone(); assertNotSame(b1, b2); assertSame(b1.getClass(), b2.getClass()); assertEquals(b1, b2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { double start = 10.0; double end = 20.0; HistogramBin b1 = new HistogramBin(start, end); HistogramBin b2 = TestUtils.serialised(b1); assertEquals(b1, b2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/statistics/HistogramDatasetTest.java000066400000000000000000000211541463604235500320640ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * HistogramDatasetTest.java * ------------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.statistics; import org.jfree.chart.TestUtils; import org.jfree.data.general.DatasetChangeEvent; import org.jfree.data.general.DatasetChangeListener; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link HistogramDataset} class. */ public class HistogramDatasetTest implements DatasetChangeListener { private static final double EPSILON = 0.0000000001; /** * Some checks that the correct values are assigned to bins. */ @Test public void testBins() { double[] values = {1.0, 2.0, 3.0, 4.0, 6.0, 12.0, 5.0, 6.3, 4.5}; HistogramDataset hd = new HistogramDataset(); hd.addSeries("Series 1", values, 5); assertEquals(hd.getYValue(0, 0), 3.0, EPSILON); assertEquals(hd.getYValue(0, 1), 3.0, EPSILON); assertEquals(hd.getYValue(0, 2), 2.0, EPSILON); assertEquals(hd.getYValue(0, 3), 0.0, EPSILON); assertEquals(hd.getYValue(0, 4), 1.0, EPSILON); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { double[] values = {1.0, 2.0, 3.0, 4.0, 6.0, 12.0, 5.0, 6.3, 4.5}; HistogramDataset d1 = new HistogramDataset(); d1.addSeries("Series 1", values, 5); HistogramDataset d2 = new HistogramDataset(); d2.addSeries("Series 1", values, 5); assertEquals(d1, d2); assertEquals(d2, d1); d1.addSeries("Series 2", new double[] {1.0, 2.0, 3.0}, 2); assertNotEquals(d1, d2); d2.addSeries("Series 2", new double[] {1.0, 2.0, 3.0}, 2); assertEquals(d1, d2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { double[] values = {1.0, 2.0, 3.0, 4.0, 6.0, 12.0, 5.0, 6.3, 4.5}; HistogramDataset d1 = new HistogramDataset(); d1.addSeries("Series 1", values, 5); HistogramDataset d2 = (HistogramDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); // simple check for independence d1.addSeries("Series 2", new double[] {1.0, 2.0, 3.0}, 2); assertNotEquals(d1, d2); d2.addSeries("Series 2", new double[] {1.0, 2.0, 3.0}, 2); assertEquals(d1, d2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { double[] values = {1.0, 2.0, 3.0, 4.0, 6.0, 12.0, 5.0, 6.3, 4.5}; HistogramDataset d1 = new HistogramDataset(); d1.addSeries("Series 1", values, 5); HistogramDataset d2 = TestUtils.serialised(d1); assertEquals(d1, d2); // simple check for independence d1.addSeries("Series 2", new double[] {1.0, 2.0, 3.0}, 2); assertNotEquals(d1, d2); d2.addSeries("Series 2", new double[] {1.0, 2.0, 3.0}, 2); assertEquals(d1, d2); } /** * A test for a bug reported in the forum where the series name isn't being * returned correctly. */ @Test public void testGetSeriesKey() { double[] values = {1.0, 2.0, 3.0, 4.0, 6.0, 12.0, 5.0, 6.3, 4.5}; HistogramDataset d1 = new HistogramDataset(); d1.addSeries("Series 1", values, 5); assertEquals("Series 1", d1.getSeriesKey(0)); } /** * Some checks for the addSeries() method. */ @Test public void testAddSeries() { double[] values = {-1.0, 0.0, 0.1, 0.9, 1.0, 1.1, 1.9, 2.0, 3.0}; HistogramDataset d = new HistogramDataset(); d.addSeries("S1", values, 2, 0.0, 2.0); assertEquals(0.0, d.getStartXValue(0, 0), EPSILON); assertEquals(1.0, d.getEndXValue(0, 0), EPSILON); assertEquals(4.0, d.getYValue(0, 0), EPSILON); assertEquals(1.0, d.getStartXValue(0, 1), EPSILON); assertEquals(2.0, d.getEndXValue(0, 1), EPSILON); assertEquals(5.0, d.getYValue(0, 1), EPSILON); } /** * Another check for the addSeries() method. */ @Test public void testAddSeries2() { double[] values = {0.0, 1.0, 2.0, 3.0, 4.0, 5.0}; HistogramDataset hd = new HistogramDataset(); hd.addSeries("S1", values, 5); assertEquals(0.0, hd.getStartXValue(0, 0), EPSILON); assertEquals(1.0, hd.getEndXValue(0, 0), EPSILON); assertEquals(1.0, hd.getYValue(0, 0), EPSILON); assertEquals(1.0, hd.getStartXValue(0, 1), EPSILON); assertEquals(2.0, hd.getEndXValue(0, 1), EPSILON); assertEquals(1.0, hd.getYValue(0, 1), EPSILON); assertEquals(2.0, hd.getStartXValue(0, 2), EPSILON); assertEquals(3.0, hd.getEndXValue(0, 2), EPSILON); assertEquals(1.0, hd.getYValue(0, 2), EPSILON); assertEquals(3.0, hd.getStartXValue(0, 3), EPSILON); assertEquals(4.0, hd.getEndXValue(0, 3), EPSILON); assertEquals(1.0, hd.getYValue(0, 3), EPSILON); assertEquals(4.0, hd.getStartXValue(0, 4), EPSILON); assertEquals(5.0, hd.getEndXValue(0, 4), EPSILON); assertEquals(2.0, hd.getYValue(0, 4), EPSILON); } /** * This test is derived from a reported bug. */ @Test public void testBinBoundaries() { double[] values = {-5.000000000000286E-5}; int bins = 1260; double minimum = -0.06307522528160199; double maximum = 0.06297522528160199; HistogramDataset d = new HistogramDataset(); d.addSeries("S1", values, bins, minimum, maximum); assertEquals(0.0, d.getYValue(0, 629), EPSILON); assertEquals(1.0, d.getYValue(0, 630), EPSILON); assertEquals(0.0, d.getYValue(0, 631), EPSILON); assertTrue(values[0] > d.getStartXValue(0, 630)); assertTrue(values[0] < d.getEndXValue(0, 630)); } /** * Some checks for bug 1553088. An IndexOutOfBoundsException is thrown * when a data value is *very* close to the upper limit of the last bin. */ @Test public void test1553088() { double[] values = {-1.0, 0.0, -Double.MIN_VALUE, 3.0}; HistogramDataset d = new HistogramDataset(); d.addSeries("S1", values, 2, -1.0, 0.0); assertEquals(-1.0, d.getStartXValue(0, 0), EPSILON); assertEquals(-0.5, d.getEndXValue(0, 0), EPSILON); assertEquals(1.0, d.getYValue(0, 0), EPSILON); assertEquals(-0.5, d.getStartXValue(0, 1), EPSILON); assertEquals(0.0, d.getEndXValue(0, 1), EPSILON); assertEquals(3.0, d.getYValue(0, 1), EPSILON); } /** * A test to show the limitation addressed by patch 2902842. */ @Test public void test2902842() { this.lastEvent = null; double[] values = {0.0, 1.0, 2.0, 3.0, 4.0, 5.0}; HistogramDataset hd = new HistogramDataset(); hd.addChangeListener(this); hd.addSeries("S1", values, 5); assertNotNull(this.lastEvent); } /** * A reference to the last event received by the datasetChanged() method. */ private DatasetChangeEvent lastEvent; /** * Receives event notification. * * @param event the event. */ @Override public void datasetChanged(DatasetChangeEvent event) { this.lastEvent = event; } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/statistics/MeanAndStandardDeviationTest.java000066400000000000000000000056441463604235500334560ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------------- * MeanAndStandardDeviationTest.java * --------------------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.statistics; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link MeanAndStandardDeviation} class. */ public class MeanAndStandardDeviationTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { MeanAndStandardDeviation m1 = new MeanAndStandardDeviation(1.2, 3.4); MeanAndStandardDeviation m2 = new MeanAndStandardDeviation(1.2, 3.4); assertEquals(m1, m2); assertEquals(m2, m1); m1 = new MeanAndStandardDeviation(1.0, 3.4); assertNotEquals(m1, m2); m2 = new MeanAndStandardDeviation(1.0, 3.4); assertEquals(m1, m2); m1 = new MeanAndStandardDeviation(1.0, 3.0); assertNotEquals(m1, m2); m2 = new MeanAndStandardDeviation(1.0, 3.0); assertEquals(m1, m2); } /** * Immutable class - should not be cloneable. */ @Test public void testCloning() { MeanAndStandardDeviation m1 = new MeanAndStandardDeviation(1.2, 3.4); assertFalse(m1 instanceof Cloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { MeanAndStandardDeviation m1 = new MeanAndStandardDeviation(1.2, 3.4); MeanAndStandardDeviation m2 = TestUtils.serialised(m1); assertEquals(m1, m2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/statistics/RegressionTest.java000066400000000000000000000164241463604235500307450ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * RegressionTest.java * ------------------- * (C) Copyright 2002-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.statistics; import static org.junit.jupiter.api.Assertions.assertEquals; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.junit.jupiter.api.Test; /** * Tests for the {@link Regression} class. */ public class RegressionTest { /** * Checks the results of an OLS regression on sample dataset 1. */ @Test public void testOLSRegression1a() { double[][] data = createSampleData1(); double[] result1 = Regression.getOLSRegression(data); assertEquals(.25680930, result1[0], 0.0000001); assertEquals(0.72792106, result1[1], 0.0000001); } /** * Checks the results of an OLS regression on sample dataset 1 AFTER * converting it to an XYSeries. */ @Test public void testOLSRegression1b() { double[][] data = createSampleData1(); XYSeries series = new XYSeries("Test"); for (int i = 0; i < 11; i++) { series.add(data[i][0], data[i][1]); } XYDataset ds = new XYSeriesCollection(series); double[] result2 = Regression.getOLSRegression(ds, 0); assertEquals(.25680930, result2[0], 0.0000001); assertEquals(0.72792106, result2[1], 0.0000001); } /** * Checks the results of a power regression on sample dataset 1. */ @Test public void testPowerRegression1a() { double[][] data = createSampleData1(); double[] result = Regression.getPowerRegression(data); assertEquals(0.91045813, result[0], 0.0000001); assertEquals(0.88918346, result[1], 0.0000001); } /** * Checks the results of a power regression on sample dataset 1 AFTER * converting it to an XYSeries. */ @Test public void testPowerRegression1b() { double[][] data = createSampleData1(); XYSeries series = new XYSeries("Test"); for (int i = 0; i < 11; i++) { series.add(data[i][0], data[i][1]); } XYDataset ds = new XYSeriesCollection(series); double[] result = Regression.getPowerRegression(ds, 0); assertEquals(0.91045813, result[0], 0.0000001); assertEquals(0.88918346, result[1], 0.0000001); } /** * Checks the results of an OLS regression on sample dataset 2. */ @Test public void testOLSRegression2a() { double[][] data = createSampleData2(); double[] result = Regression.getOLSRegression(data); assertEquals(53.9729697, result[0], 0.0000001); assertEquals(-4.1823030, result[1], 0.0000001); } /** * Checks the results of an OLS regression on sample dataset 2 AFTER * converting it to an XYSeries. */ @Test public void testOLSRegression2b() { double[][] data = createSampleData2(); XYSeries series = new XYSeries("Test"); for (int i = 0; i < 10; i++) { series.add(data[i][0], data[i][1]); } XYDataset ds = new XYSeriesCollection(series); double[] result = Regression.getOLSRegression(ds, 0); assertEquals(53.9729697, result[0], 0.0000001); assertEquals(-4.1823030, result[1], 0.0000001); } /** * Checks the results of a power regression on sample dataset 2. */ @Test public void testPowerRegression2a() { double[][] data = createSampleData2(); double[] result = Regression.getPowerRegression(data); assertEquals(106.1241681, result[0], 0.0000001); assertEquals(-0.8466615, result[1], 0.0000001); } /** * Checks the results of a power regression on sample dataset 2 AFTER * converting it to an XYSeries. */ @Test public void testPowerRegression2b() { double[][] data = createSampleData2(); XYSeries series = new XYSeries("Test"); for (int i = 0; i < 10; i++) { series.add(data[i][0], data[i][1]); } XYDataset ds = new XYSeriesCollection(series); double[] result = Regression.getPowerRegression(ds, 0); assertEquals(106.1241681, result[0], 0.0000001); assertEquals(-0.8466615, result[1], 0.0000001); } /** * Creates and returns a sample dataset. *

* The data is taken from Table 11.2, page 313 of "Understanding Statistics" * by Ott and Mendenhall (Duxbury Press). * * @return The sample data. */ private double[][] createSampleData1() { double[][] result = new double[11][2]; result[0][0] = 2.00; result[0][1] = 1.60; result[1][0] = 2.25; result[1][1] = 2.00; result[2][0] = 2.60; result[2][1] = 1.80; result[3][0] = 2.65; result[3][1] = 2.80; result[4][0] = 2.80; result[4][1] = 2.10; result[5][0] = 3.10; result[5][1] = 2.00; result[6][0] = 2.90; result[6][1] = 2.65; result[7][0] = 3.25; result[7][1] = 2.25; result[8][0] = 3.30; result[8][1] = 2.60; result[9][0] = 3.60; result[9][1] = 3.00; result[10][0] = 3.25; result[10][1] = 3.10; return result; } /** * Creates a sample data set. * * @return The sample data. */ private double[][] createSampleData2() { double[][] result = new double[10][2]; result[0][0] = 2; result[0][1] = 56.27; result[1][0] = 3; result[1][1] = 41.32; result[2][0] = 4; result[2][1] = 31.45; result[3][0] = 5; result[3][1] = 30.05; result[4][0] = 6; result[4][1] = 24.69; result[5][0] = 7; result[5][1] = 19.78; result[6][0] = 8; result[6][1] = 20.94; result[7][0] = 9; result[7][1] = 16.73; result[8][0] = 10; result[8][1] = 14.21; result[9][0] = 11; result[9][1] = 12.44; return result; } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/statistics/SimpleHistogramBinTest.java000066400000000000000000000125561463604235500323670ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * SimpleHistogramBinTest.java * --------------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.statistics; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link SimpleHistogramBin} class. */ public class SimpleHistogramBinTest { /** * Some checks for the accepts() method. */ @Test public void testAccepts() { SimpleHistogramBin bin1 = new SimpleHistogramBin(1.0, 2.0); assertFalse(bin1.accepts(0.0)); assertTrue(bin1.accepts(1.0)); assertTrue(bin1.accepts(1.5)); assertTrue(bin1.accepts(2.0)); assertFalse(bin1.accepts(2.1)); assertFalse(bin1.accepts(Double.NaN)); SimpleHistogramBin bin2 = new SimpleHistogramBin(1.0, 2.0, false, false); assertFalse(bin2.accepts(0.0)); assertFalse(bin2.accepts(1.0)); assertTrue(bin2.accepts(1.5)); assertFalse(bin2.accepts(2.0)); assertFalse(bin2.accepts(2.1)); assertFalse(bin2.accepts(Double.NaN)); } /** * Some checks for the overlapsWith() method. */ @Test public void testOverlapsWidth() { SimpleHistogramBin b1 = new SimpleHistogramBin(1.0, 2.0); SimpleHistogramBin b2 = new SimpleHistogramBin(2.0, 3.0); SimpleHistogramBin b3 = new SimpleHistogramBin(3.0, 4.0); SimpleHistogramBin b4 = new SimpleHistogramBin(0.0, 5.0); SimpleHistogramBin b5 = new SimpleHistogramBin(2.0, 3.0, false, true); SimpleHistogramBin b6 = new SimpleHistogramBin(2.0, 3.0, true, false); assertTrue(b1.overlapsWith(b2)); assertTrue(b2.overlapsWith(b1)); assertFalse(b1.overlapsWith(b3)); assertFalse(b3.overlapsWith(b1)); assertTrue(b1.overlapsWith(b4)); assertTrue(b4.overlapsWith(b1)); assertFalse(b1.overlapsWith(b5)); assertFalse(b5.overlapsWith(b1)); assertTrue(b1.overlapsWith(b6)); assertTrue(b6.overlapsWith(b1)); } /** * Ensure that the equals() method can distinguish all fields. */ @Test public void testEquals() { SimpleHistogramBin b1 = new SimpleHistogramBin(1.0, 2.0); SimpleHistogramBin b2 = new SimpleHistogramBin(1.0, 2.0); assertEquals(b1, b2); assertEquals(b2, b1); b1 = new SimpleHistogramBin(1.1, 2.0, true, true); assertNotEquals(b1, b2); b2 = new SimpleHistogramBin(1.1, 2.0, true, true); assertEquals(b1, b2); b1 = new SimpleHistogramBin(1.1, 2.2, true, true); assertNotEquals(b1, b2); b2 = new SimpleHistogramBin(1.1, 2.2, true, true); assertEquals(b1, b2); b1 = new SimpleHistogramBin(1.1, 2.2, false, true); assertNotEquals(b1, b2); b2 = new SimpleHistogramBin(1.1, 2.2, false, true); assertEquals(b1, b2); b1 = new SimpleHistogramBin(1.1, 2.2, false, false); assertNotEquals(b1, b2); b2 = new SimpleHistogramBin(1.1, 2.2, false, false); assertEquals(b1, b2); b1.setItemCount(99); assertNotEquals(b1, b2); b2.setItemCount(99); assertEquals(b1, b2); } /** * Some checks for the clone() method. */ @Test public void testCloning() throws CloneNotSupportedException { SimpleHistogramBin b1 = new SimpleHistogramBin(1.1, 2.2, false, true); b1.setItemCount(99); SimpleHistogramBin b2 = (SimpleHistogramBin) b1.clone(); assertNotSame(b1, b2); assertSame(b1.getClass(), b2.getClass()); assertEquals(b1, b2); // check that clone is independent of the original b2.setItemCount(111); assertNotEquals(b1, b2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { SimpleHistogramBin b1 = new SimpleHistogramBin(1.0, 2.0, false, true); b1.setItemCount(123); SimpleHistogramBin b2 = TestUtils.serialised(b1); assertEquals(b1, b2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/statistics/SimpleHistogramDatasetTest.java000066400000000000000000000076541463604235500332470ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------- * SimpleHistogramDatasetTest.java * ------------------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.statistics; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link SimpleHistogramDataset} class. */ public class SimpleHistogramDatasetTest { /** * Ensure that the equals() method can distinguish all fields. */ @Test public void testEquals() { SimpleHistogramDataset d1 = new SimpleHistogramDataset("Dataset 1"); SimpleHistogramDataset d2 = new SimpleHistogramDataset("Dataset 1"); assertEquals(d1, d2); d1.addBin(new SimpleHistogramBin(1.0, 2.0)); assertNotEquals(d1, d2); d2.addBin(new SimpleHistogramBin(1.0, 2.0)); assertEquals(d1, d2); } /** * Some checks for the clone() method. */ @Test public void testCloning() throws CloneNotSupportedException { SimpleHistogramDataset d1 = new SimpleHistogramDataset("Dataset 1"); SimpleHistogramDataset d2 = (SimpleHistogramDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); // check that clone is independent of the original d2.addBin(new SimpleHistogramBin(2.0, 3.0)); d2.addObservation(2.3); assertNotEquals(d1, d2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { SimpleHistogramDataset d1 = new SimpleHistogramDataset("D1"); SimpleHistogramDataset d2 = TestUtils.serialised(d1); assertEquals(d1, d2); } private static final double EPSILON = 0.0000000001; /** * Some checks for the clearObservations() method. */ @Test public void testClearObservations() { SimpleHistogramDataset d1 = new SimpleHistogramDataset("D1"); d1.clearObservations(); assertEquals(0, d1.getItemCount(0)); d1.addBin(new SimpleHistogramBin(0.0, 1.0)); d1.addObservation(0.5); assertEquals(1.0, d1.getYValue(0, 0), EPSILON); } /** * Some checks for the removeAllBins() method. */ @Test public void testRemoveAllBins() { SimpleHistogramDataset d1 = new SimpleHistogramDataset("D1"); d1.addBin(new SimpleHistogramBin(0.0, 1.0)); d1.addObservation(0.5); d1.addBin(new SimpleHistogramBin(2.0, 3.0)); assertEquals(2, d1.getItemCount(0)); d1.removeAllBins(); assertEquals(0, d1.getItemCount(0)); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/statistics/StatisticsTest.java000066400000000000000000000340361463604235500307560ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * StatisticsTest.java * ------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.statistics; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import org.junit.jupiter.api.Test; /** * Tests for the {@link Statistics} class. */ public class StatisticsTest { /** * Some checks for the calculateMean(Number[]) and * calculateMean(Number[], boolean) methods. */ @Test public void testCalculateMean_Array() { // try null array boolean pass = false; try { Statistics.calculateMean((Number[]) null); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); pass = false; try { Statistics.calculateMean((Number[]) null, false); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); // try an array containing no items assertTrue(Double.isNaN(Statistics.calculateMean(new Number[0]))); assertTrue(Double.isNaN(Statistics.calculateMean(new Number[0], false))); // try an array containing a single Number Number[] values = new Number[] {1.0}; assertEquals(1.0, Statistics.calculateMean(values), EPSILON); assertEquals(1.0, Statistics.calculateMean(values, true), EPSILON); assertEquals(1.0, Statistics.calculateMean(values, false), EPSILON); // try an array containing a single Number and a null values = new Number[] {1.0, null}; assertTrue(Double.isNaN(Statistics.calculateMean(values))); assertTrue(Double.isNaN(Statistics.calculateMean(values, true))); assertEquals(1.0, Statistics.calculateMean(values, false), EPSILON); // try an array containing a single Number and a NaN values = new Number[] {1.0, Double.NaN}; assertTrue(Double.isNaN(Statistics.calculateMean(values))); assertTrue(Double.isNaN(Statistics.calculateMean(values, true))); assertEquals(1.0, Statistics.calculateMean(values, false), EPSILON); } /** * Some checks for the calculateMean(Collection) and * calculateMean(Collection, boolean) methods. */ @Test public void testCalculateMean_Collection() { // try a null collection boolean pass = false; try { Statistics.calculateMean((Collection) null); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); pass = false; try { Statistics.calculateMean((Collection) null, false); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); // try an empty collection List values = new ArrayList<>(); assertTrue(Double.isNaN(Statistics.calculateMean(values))); assertTrue(Double.isNaN(Statistics.calculateMean(values, true))); assertTrue(Double.isNaN(Statistics.calculateMean(values, false))); // try a collection with a single number values.add(9.0); assertEquals(9.0, Statistics.calculateMean(values), EPSILON); assertEquals(9.0, Statistics.calculateMean(values, true), EPSILON); assertEquals(9.0, Statistics.calculateMean(values, false), EPSILON); // try a collection with a single number plus a null values.add(null); assertTrue(Double.isNaN(Statistics.calculateMean(values))); assertTrue(Double.isNaN(Statistics.calculateMean(values, true))); assertEquals(9.0, Statistics.calculateMean(values, false), EPSILON); // try a collection with a single number plus a NaN values.clear(); values.add(9.0); values.add(Double.NaN); assertTrue(Double.isNaN(Statistics.calculateMean(values))); assertTrue(Double.isNaN(Statistics.calculateMean(values, true))); assertEquals(9.0, Statistics.calculateMean(values, false), EPSILON); // try a collection with several numbers values = new ArrayList<>(); values.add(9.0); values.add(3.0); values.add(2.0); values.add(2.0); double mean = Statistics.calculateMean(values); assertEquals(4.0, mean, EPSILON); // a Collection containing a NaN will return Double.NaN for the result values.add(Double.NaN); assertTrue(Double.isNaN(Statistics.calculateMean(values))); } static final double EPSILON = 0.0000000001; /** * Some checks for the calculateMedian(List, boolean) method. */ @Test public void testCalculateMedian() { // check null list assertTrue(Double.isNaN(Statistics.calculateMedian(null, false))); assertTrue(Double.isNaN(Statistics.calculateMedian(null, true))); // check empty list List list = new ArrayList<>(); assertTrue(Double.isNaN(Statistics.calculateMedian(list, false))); assertTrue(Double.isNaN(Statistics.calculateMedian(list, true))); // check list containing null list.add(null); boolean pass = false; try { Statistics.calculateMedian(list, false); } catch (NullPointerException e) { pass = true; } assertTrue(pass); pass = false; try { Statistics.calculateMedian(list, true); } catch (NullPointerException e) { pass = true; } assertTrue(pass); // check a list containing a non-Number object List badList = new ArrayList<>(); badList.add("Not a number"); pass = false; try { Statistics.calculateMedian(badList, false); } catch (ClassCastException e) { pass = true; } assertTrue(pass); pass = false; try { Statistics.calculateMedian(badList, true); } catch (ClassCastException e) { pass = true; } assertTrue(pass); } /** * A test for the calculateMedian() method. */ @Test public void testCalculateMedian1() { List values = new ArrayList<>(); values.add(1.0); double median = Statistics.calculateMedian(values); assertEquals(1.0, median, 0.0000001); } /** * A test for the calculateMedian() method. */ @Test public void testCalculateMedian2() { List values = new ArrayList<>(); values.add(2.0); values.add(1.0); double median = Statistics.calculateMedian(values); assertEquals(1.5, median, 0.0000001); } /** * A test for the calculateMedian() method. */ @Test public void testCalculateMedian3() { List values = new ArrayList<>(); values.add(1.0); values.add(2.0); values.add(3.0); values.add(6.0); values.add(5.0); values.add(4.0); double median = Statistics.calculateMedian(values); assertEquals(3.5, median, 0.0000001); } /** * A test for the calculateMedian() method. */ @Test public void testCalculateMedian4() { List values = new ArrayList<>(); values.add(7.0); values.add(2.0); values.add(3.0); values.add(5.0); values.add(4.0); values.add(6.0); values.add(1.0); double median = Statistics.calculateMedian(values); assertEquals(4.0, median, 0.0000001); } /** * A test using some real data that caused a problem at one point. */ @Test public void testCalculateMedian5() { List values = new ArrayList<>(); values.add(11.228692993861783); values.add(11.30823353859889); values.add(11.75312904769314); values.add(11.825102897465314); values.add(10.184252778401783); values.add(12.207951828057766); values.add(10.68841994040566); values.add(12.099522004479438); values.add(11.508874945056881); values.add(12.052517729558513); values.add(12.401481645578734); values.add(12.185377793028543); values.add(10.666372951930315); values.add(11.680978041499548); values.add(11.06528277406718); values.add(11.36876492904596); values.add(11.927565516175939); values.add(11.39307785978655); values.add(11.989603679523857); values.add(12.009834360354864); values.add(10.653351822461559); values.add(11.851776254376754); values.add(11.045441544755946); values.add(11.993674040560624); values.add(12.898219965238944); values.add(11.97095782819647); values.add(11.73234406745488); values.add(11.649006017243991); values.add(12.20549704915365); values.add(11.799723639384919); values.add(11.896208658005628); values.add(12.164149111823424); values.add(12.042795103513766); values.add(12.114839532596426); values.add(12.166609097075824); values.add(12.183017546225935); values.add(11.622009125845342); values.add(11.289365786738633); values.add(12.462984323671568); values.add(11.573494921030598); values.add(10.862867940485804); values.add(12.018186939664872); values.add(10.418046849313018); values.add(11.326344465881341); double median = Statistics.calculateMedian(values, true); assertEquals(11.812413268425116, median, 0.000001); Collections.sort(values); double median2 = Statistics.calculateMedian(values, false); assertEquals(11.812413268425116, median2, 0.000001); } /** * A test for the calculateMedian() method. */ @Test public void testCalculateMedian6() { List values = new ArrayList<>(); values.add(7.0); values.add(2.0); values.add(3.0); values.add(5.0); values.add(4.0); values.add(6.0); values.add(1.0); double median = Statistics.calculateMedian(values, 0, 2); assertEquals(3.0, median, 0.0000001); } /** * A simple test for the correlation calculation. */ @Test public void testCorrelation1() { Number[] data1 = new Number[3]; data1[0] = 1.0; data1[1] = 2.0; data1[2] = 3.0; Number[] data2 = new Number[3]; data2[0] = 1.0; data2[1] = 2.0; data2[2] = 3.0; double r = Statistics.getCorrelation(data1, data2); assertEquals(1.0, r, 0.00000001); } /** * A simple test for the correlation calculation. * * http://trochim.human.cornell.edu/kb/statcorr.htm */ @Test public void testCorrelation2() { Number[] data1 = new Number[20]; data1[0] = 68.0; data1[1] = 71.0; data1[2] = 62.0; data1[3] = 75.0; data1[4] = 58.0; data1[5] = 60.0; data1[6] = 67.0; data1[7] = 68.0; data1[8] = 71.0; data1[9] = 69.0; data1[10] = 68.0; data1[11] = 67.0; data1[12] = 63.0; data1[13] = 62.0; data1[14] = 60.0; data1[15] = 63.0; data1[16] = 65.0; data1[17] = 67.0; data1[18] = 63.0; data1[19] = 61.0; Number[] data2 = new Number[20]; data2[0] = 4.1; data2[1] = 4.6; data2[2] = 3.8; data2[3] = 4.4; data2[4] = 3.2; data2[5] = 3.1; data2[6] = 3.8; data2[7] = 4.1; data2[8] = 4.3; data2[9] = 3.7; data2[10] = 3.5; data2[11] = 3.2; data2[12] = 3.7; data2[13] = 3.3; data2[14] = 3.4; data2[15] = 4.0; data2[16] = 4.1; data2[17] = 3.8; data2[18] = 3.4; data2[19] = 3.6; double r = Statistics.getCorrelation(data1, data2); assertEquals(0.7306356862792885, r, 0.000000000001); } /** * Some checks for the getStdDev() method. */ @Test public void testGetStdDev() { // try null argument boolean pass = false; try { Statistics.getStdDev(null); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); // try zero length array pass = false; try { Statistics.getStdDev(new Double[0]); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); // try single value assertTrue(Double.isNaN(Statistics.getStdDev(new Double[]{1.0}))); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/statistics/package.html000066400000000000000000000002631463604235500273750ustar00rootroot00000000000000 Tests for the classes in the org.jfree.data.statistics package. jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/time/000077500000000000000000000000001463604235500236575ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/time/DateRangeTest.java000066400000000000000000000064441463604235500272240ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * DateRangeTest.java * ------------------ * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import java.util.Date; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Some tests for the {@link DateRange} class. */ public class DateRangeTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { DateRange r1 = new DateRange(new Date(1000L), new Date(2000L)); DateRange r2 = new DateRange(new Date(1000L), new Date(2000L)); assertEquals(r1, r2); assertEquals(r2, r1); r1 = new DateRange(new Date(1111L), new Date(2000L)); assertNotEquals(r1, r2); r2 = new DateRange(new Date(1111L), new Date(2000L)); assertEquals(r1, r2); r1 = new DateRange(new Date(1111L), new Date(2222L)); assertNotEquals(r1, r2); r2 = new DateRange(new Date(1111L), new Date(2222L)); assertEquals(r1, r2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DateRange r1 = new DateRange(new Date(1000L), new Date(2000L)); DateRange r2 = TestUtils.serialised(r1); assertEquals(r1, r2); } /** * The {@link DateRange} class is immutable, so it doesn't need to * be cloneable. */ @Test public void testClone() { DateRange r1 = new DateRange(new Date(1000L), new Date(2000L)); assertFalse(r1 instanceof Cloneable); } /** * Confirm that a DateRange is immutable. */ @Test public void testImmutable() { Date d1 = new Date(10L); Date d2 = new Date(20L); DateRange r = new DateRange(d1, d2); d1.setTime(11L); assertEquals(new Date(10L), r.getLowerDate()); r.getUpperDate().setTime(22L); assertEquals(new Date(20L), r.getUpperDate()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/time/DayTest.java000066400000000000000000000450621463604235500261060ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------ * DayTest.java * ------------ * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.ZoneOffset; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.Locale; import java.util.TimeZone; import java.util.function.Consumer; import org.jfree.chart.TestUtils; import org.jfree.chart.date.MonthConstants; import org.junit.jupiter.api.Test; import org.jfree.chart.date.SerialDate; /** * Tests for the {@link Day} class. */ public class DayTest { /** * Check that a Day instance is equal to itself. * * SourceForge Bug ID: 558850. */ @Test public void testEqualsSelf() { Day day = new Day(); assertEquals(day, day); } /** * Tests the equals method. */ @Test public void testEquals() { Day day1 = new Day(29, MonthConstants.MARCH, 2002); Day day2 = new Day(29, MonthConstants.MARCH, 2002); assertEquals(day1, day2); } /** * In GMT, the end of 29 Feb 2004 is java.util.Date(1,078,099,199,999L). * Use this to check the day constructor. */ @Test public void testDateConstructor1() { TimeZone zone = TimeZone.getTimeZone("GMT"); Calendar cal = Calendar.getInstance(zone); Locale locale = Locale.UK; Day d1 = new Day(new Date(1078099199999L), zone, locale); Day d2 = new Day(new Date(1078099200000L), zone, locale); assertEquals(MonthConstants.FEBRUARY, d1.getMonth()); assertEquals(1078099199999L, d1.getLastMillisecond(cal)); assertEquals(MonthConstants.MARCH, d2.getMonth()); assertEquals(1078099200000L, d2.getFirstMillisecond(cal)); } /** * In Helsinki, the end of 29 Feb 2004 is * java.util.Date(1,078,091,999,999L). Use this to check the Day * constructor. */ @Test public void testDateConstructor2() { TimeZone zone = TimeZone.getTimeZone("Europe/Helsinki"); Calendar cal = Calendar.getInstance(zone); Locale locale = Locale.getDefault(); // locale shouldn't matter here Day d1 = new Day(new Date(1078091999999L), zone, locale); Day d2 = new Day(new Date(1078092000000L), zone, locale); assertEquals(MonthConstants.FEBRUARY, d1.getMonth()); assertEquals(1078091999999L, d1.getLastMillisecond(cal)); assertEquals(MonthConstants.MARCH, d2.getMonth()); assertEquals(1078092000000L, d2.getFirstMillisecond(cal)); } /** * If a thread-local calendar was set, the Date constructor should use it. */ @Test public void testDateConstructorWithThreadLocalCalendar() { Consumer calendarSetup = hours -> RegularTimePeriod.setThreadLocalCalendarInstance( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testDateConstructorWithCustomCalendar(3, calendarSetup); testDateConstructorWithCustomCalendar(4, calendarSetup); } /** * If a calendar prototype was set, the Date constructor should use it. */ @Test public void testDateConstructorWithCalendarPrototype() { Consumer calendarSetup = hours -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testDateConstructorWithCustomCalendar(3, calendarSetup); testDateConstructorWithCustomCalendar(4, calendarSetup); } private void testDateConstructorWithCustomCalendar(int hoursOffset, Consumer calendarSetup) { try { calendarSetup.accept(hoursOffset); long ms = 86_400_000L - 3_600_000L * hoursOffset; Day d = new Day(new Date(ms)); assertEquals(1970, d.getYear()); assertEquals(1, d.getMonth()); assertEquals(2, d.getDayOfMonth()); assertEquals(ms, d.getFirstMillisecond()); } finally { // reset everything, to avoid affecting other tests RegularTimePeriod.setThreadLocalCalendarInstance(null); RegularTimePeriod.setCalendarInstancePrototype(null); } } /** * If a thread-local calendar was set, the minute-hour constructor should use it. */ @Test public void testDMYConstructorWithThreadLocalCalendar() { Consumer calendarSetup = hours -> RegularTimePeriod.setThreadLocalCalendarInstance( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testDMYConstructorWithCustomCalendar(3, calendarSetup); testDMYConstructorWithCustomCalendar(4, calendarSetup); } /** * If a calendar prototype was set, the DMY constructor should use it. */ @Test public void testDMYConstructorWithCalendarPrototype() { Consumer calendarSetup = hours -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testDMYConstructorWithCustomCalendar(3, calendarSetup); testDMYConstructorWithCustomCalendar(4, calendarSetup); } private void testDMYConstructorWithCustomCalendar(int hoursOffset, Consumer calendarSetup) { try { calendarSetup.accept(hoursOffset); Day d = new Day(1, 1, 1970); assertEquals(1970, d.getYear()); assertEquals(1, d.getMonth()); assertEquals(1, d.getDayOfMonth()); assertEquals(-3_600_000L * hoursOffset, d.getFirstMillisecond()); } finally { // reset everything, to avoid affecting other tests RegularTimePeriod.setThreadLocalCalendarInstance(null); RegularTimePeriod.setCalendarInstancePrototype(null); } } /** * If a thread-local calendar was set, the SerialDate constructor should use it. */ @Test public void testSerialDateConstructorWithThreadLocalCalendar() { Consumer calendarSetup = hours -> RegularTimePeriod.setThreadLocalCalendarInstance( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testSerialDateConstructorWithCustomCalendar(3, calendarSetup); testSerialDateConstructorWithCustomCalendar(4, calendarSetup); } /** * If a calendar prototype was set, the SerialDate constructor should use it. */ @Test public void testSerialDateConstructorWithCalendarPrototype() { Consumer calendarSetup = hours -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testSerialDateConstructorWithCustomCalendar(3, calendarSetup); testSerialDateConstructorWithCustomCalendar(4, calendarSetup); } private void testSerialDateConstructorWithCustomCalendar(int hoursOffset, Consumer calendarSetup) { try { calendarSetup.accept(hoursOffset); Day d = new Day(SerialDate.createInstance(1, 1, 1970)); assertEquals(1970, d.getYear()); assertEquals(1, d.getMonth()); assertEquals(1, d.getDayOfMonth()); assertEquals(-3_600_000L * hoursOffset, d.getFirstMillisecond()); } finally { // reset everything, to avoid affecting other tests RegularTimePeriod.setThreadLocalCalendarInstance(null); RegularTimePeriod.setCalendarInstancePrototype(null); } } /** * Set up a day equal to 1 January 1900. Request the previous day, it * should be null. */ @Test public void test1Jan1900Previous() { Day jan1st1900 = new Day(1, MonthConstants.JANUARY, 1900); Day previous = (Day) jan1st1900.previous(); assertNull(previous); } /** * Set up a day equal to 1 January 1900. Request the next day, it should * be 2 January 1900. */ @Test public void test1Jan1900Next() { Day jan1st1900 = new Day(1, MonthConstants.JANUARY, 1900); Day next = (Day) jan1st1900.next(); assertEquals(2, next.getDayOfMonth()); } /** * Set up a day equal to 31 December 9999. Request the previous day, it * should be 30 December 9999. */ @Test public void test31Dec9999Previous() { Day dec31st9999 = new Day(31, MonthConstants.DECEMBER, 9999); Day previous = (Day) dec31st9999.previous(); assertEquals(30, previous.getDayOfMonth()); } /** * Set up a day equal to 31 December 9999. Request the next day, it should * be null. */ @Test public void test31Dec9999Next() { Day dec31st9999 = new Day(31, MonthConstants.DECEMBER, 9999); Day next = (Day) dec31st9999.next(); assertNull(next); } /** * Problem for date parsing. *

* This test works only correct if the short pattern of the date * format is "dd/MM/yyyy". If not, this test will result in a * false negative. * * @throws ParseException on parsing errors. */ @Test public void testParseDay() throws ParseException { GregorianCalendar gc = new GregorianCalendar(2001, 12, 31); SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy"); Date reference = format.parse("31/12/2001"); if (reference.equals(gc.getTime())) { // test 1... Day d = Day.parseDay("31/12/2001"); assertEquals(37256, d.getSerialDate().toSerial()); } // test 2... Day d = Day.parseDay("2001-12-31"); assertEquals(37256, d.getSerialDate().toSerial()); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { Day d1 = new Day(15, 4, 2000); Day d2 = TestUtils.serialised(d1); assertEquals(d1, d2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { Day d1 = new Day(1, 2, 2003); Day d2 = new Day(1, 2, 2003); assertEquals(d1, d2); int h1 = d1.hashCode(); int h2 = d2.hashCode(); assertEquals(h1, h2); } /** * The {@link Day} class is immutable, so should not be {@link Cloneable}. */ @Test public void testNotCloneable() { Day d = new Day(1, 2, 2003); assertFalse(d instanceof Cloneable); } /** * Some checks for the getSerialIndex() method. */ @Test public void testGetSerialIndex() { Day d = new Day(1, 1, 1900); assertEquals(2, d.getSerialIndex()); d = new Day(15, 4, 2000); assertEquals(36631, d.getSerialIndex()); } /** * Some checks for the getFirstMillisecond() method. */ @Test public void testGetFirstMillisecond() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.UK); TimeZone savedZone = TimeZone.getDefault(); TimeZone.setDefault(TimeZone.getTimeZone("Europe/London")); Day d = new Day(1, 3, 1970); assertEquals(5094000000L, d.getFirstMillisecond()); Locale.setDefault(saved); TimeZone.setDefault(savedZone); } /** * Some checks for the getFirstMillisecond(TimeZone) method. */ @Test public void testGetFirstMillisecondWithTimeZone() { Day d = new Day(26, 4, 1950); TimeZone zone = TimeZone.getTimeZone("America/Los_Angeles"); Calendar cal = Calendar.getInstance(zone); assertEquals(-621187200000L, d.getFirstMillisecond(cal)); // try null calendar boolean pass = false; try { d.getFirstMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getFirstMillisecond(TimeZone) method. */ @Test public void testGetFirstMillisecondWithCalendar() { Day d = new Day(1, 12, 2001); GregorianCalendar calendar = new GregorianCalendar(Locale.GERMANY); calendar.setTimeZone(TimeZone.getTimeZone("Europe/Frankfurt")); assertEquals(1007164800000L, d.getFirstMillisecond(calendar)); // try null calendar boolean pass = false; try { d.getFirstMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getLastMillisecond() method. */ @Test public void testGetLastMillisecond() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.UK); TimeZone savedZone = TimeZone.getDefault(); TimeZone.setDefault(TimeZone.getTimeZone("Europe/London")); Day d = new Day(1, 1, 1970); assertEquals(82799999L, d.getLastMillisecond()); Locale.setDefault(saved); TimeZone.setDefault(savedZone); } /** * Some checks for the getLastMillisecond(TimeZone) method. */ @Test public void testGetLastMillisecondWithTimeZone() { Day d = new Day(1, 2, 1950); TimeZone zone = TimeZone.getTimeZone("America/Los_Angeles"); Calendar cal = Calendar.getInstance(zone); assertEquals(-628358400001L, d.getLastMillisecond(cal)); // try null calendar boolean pass = false; try { d.getLastMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getLastMillisecond(TimeZone) method. */ @Test public void testGetLastMillisecondWithCalendar() { Day d = new Day(4, 5, 2001); Calendar calendar = Calendar.getInstance( TimeZone.getTimeZone("Europe/London"), Locale.UK); assertEquals(989017199999L, d.getLastMillisecond(calendar)); // try null calendar boolean pass = false; try { d.getLastMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the testNext() method. */ @Test public void testNext() { Day d = new Day(25, 12, 2000); d = (Day) d.next(); assertEquals(2000, d.getYear()); assertEquals(12, d.getMonth()); assertEquals(26, d.getDayOfMonth()); d = new Day(31, 12, 9999); assertNull(d.next()); } /** * If a thread-local calendar was set, next() should use its time zone. */ @Test public void testNextWithThreadLocalCalendar() { Consumer calendarSetup = hours -> RegularTimePeriod.setThreadLocalCalendarInstance( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testNextWithCustomCalendar(3, calendarSetup); testNextWithCustomCalendar(4, calendarSetup); } /** * If a calendar prototype was set, next() should use its time zone. */ @Test public void testNextWithCalendarPrototype() { Consumer calendarSetup = hours -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testNextWithCustomCalendar(3, calendarSetup); testNextWithCustomCalendar(4, calendarSetup); } private void testNextWithCustomCalendar(int hoursOffset, Consumer calendarSetup) { try { calendarSetup.accept(hoursOffset); long ms = 86_400_000L - hoursOffset * 3_600_000L; Day d = new Day(new Date(ms)); d = (Day) d.next(); assertEquals(1970, d.getYear()); assertEquals(1, d.getMonth()); assertEquals(3, d.getDayOfMonth()); assertEquals(ms + 86_400_000L, d.getFirstMillisecond()); } finally { // reset everything, to avoid affecting other tests RegularTimePeriod.setThreadLocalCalendarInstance(null); RegularTimePeriod.setCalendarInstancePrototype(null); } } /** * Some checks for the getStart() method. */ @Test public void testGetStart() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.ITALY); Calendar cal = Calendar.getInstance(Locale.ITALY); cal.set(2006, Calendar.NOVEMBER, 3, 0, 0, 0); cal.set(Calendar.MILLISECOND, 0); Day d = new Day(3, 11, 2006); assertEquals(cal.getTime(), d.getStart()); Locale.setDefault(saved); } /** * Some checks for the getEnd() method. */ @Test public void testGetEnd() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.ITALY); Calendar cal = Calendar.getInstance(Locale.ITALY); cal.set(1900, Calendar.JANUARY, 1, 23, 59, 59); cal.set(Calendar.MILLISECOND, 999); Day d = new Day(1, 1, 1900); assertEquals(cal.getTime(), d.getEnd()); Locale.setDefault(saved); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/time/FixedMillisecondTest.java000066400000000000000000000056331463604235500306130ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * FixedMillisecondTest.java * ------------------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Date; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; /** * Tests for the {@link FixedMillisecond} class. */ public class FixedMillisecondTest { /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { FixedMillisecond m1 = new FixedMillisecond(); FixedMillisecond m2 = TestUtils.serialised(m1); assertEquals(m1, m2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { FixedMillisecond m1 = new FixedMillisecond(500000L); FixedMillisecond m2 = new FixedMillisecond(500000L); assertEquals(m1, m2); int h1 = m1.hashCode(); int h2 = m2.hashCode(); assertEquals(h1, h2); } /** * The {@link FixedMillisecond} class is immutable, so should not be * {@link Cloneable}. */ @Test public void testNotCloneable() { FixedMillisecond m = new FixedMillisecond(500000L); assertFalse(m instanceof Cloneable); } /** * A check for immutability. */ @Test public void testImmutability() { Date d = new Date(20L); FixedMillisecond fm = new FixedMillisecond(d); d.setTime(22L); assertEquals(20L, fm.getFirstMillisecond()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/time/HourTest.java000066400000000000000000000410011463604235500262730ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------- * HourTest.java * ------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import java.time.ZoneOffset; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.Locale; import java.util.TimeZone; import java.util.function.Consumer; import org.jfree.chart.TestUtils; import org.jfree.chart.date.MonthConstants; import org.junit.jupiter.api.Test; /** * Tests for the {@link Hour} class. */ public class HourTest { /** * Check that an Hour instance is equal to itself. * * SourceForge Bug ID: 558850. */ @Test public void testEqualsSelf() { Hour hour = new Hour(); assertEquals(hour, hour); } /** * Tests the equals method. */ @Test public void testEquals() { Hour hour1 = new Hour(15, new Day(29, MonthConstants.MARCH, 2002)); Hour hour2 = new Hour(15, new Day(29, MonthConstants.MARCH, 2002)); assertEquals(hour1, hour2); } /** * In GMT, the 4pm on 21 Mar 2002 is java.util.Date(1,014,307,200,000L). * Use this to check the hour constructor. */ @Test public void testDateConstructor1() { TimeZone zone = TimeZone.getTimeZone("GMT"); Calendar cal = Calendar.getInstance(zone); Locale locale = Locale.getDefault(); // locale should not matter here Hour h1 = new Hour(new Date(1014307199999L), zone, locale); Hour h2 = new Hour(new Date(1014307200000L), zone, locale); assertEquals(15, h1.getHour()); assertEquals(1014307199999L, h1.getLastMillisecond(cal)); assertEquals(16, h2.getHour()); assertEquals(1014307200000L, h2.getFirstMillisecond(cal)); } /** * In Sydney, the 4pm on 21 Mar 2002 is java.util.Date(1,014,267,600,000L). * Use this to check the hour constructor. */ @Test public void testDateConstructor2() { TimeZone zone = TimeZone.getTimeZone("Australia/Sydney"); Calendar cal = Calendar.getInstance(zone); Locale locale = Locale.getDefault(); // locale should not matter here Hour h1 = new Hour(new Date(1014267599999L), zone, locale); Hour h2 = new Hour (new Date(1014267600000L), zone, locale); assertEquals(15, h1.getHour()); assertEquals(1014267599999L, h1.getLastMillisecond(cal)); assertEquals(16, h2.getHour()); assertEquals(1014267600000L, h2.getFirstMillisecond(cal)); } /** * If a thread-local calendar was set, the Date constructor should use it. */ @Test public void testDateConstructorWithThreadLocalCalendar() { Consumer calendarSetup = hours -> RegularTimePeriod.setThreadLocalCalendarInstance( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testDateConstructorWithCustomCalendar(3, calendarSetup); testDateConstructorWithCustomCalendar(4, calendarSetup); } /** * If a calendar prototype was set, the Date constructor should use it. */ @Test public void testDateConstructorWithCalendarPrototype() { Consumer calendarSetup = hours -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testDateConstructorWithCustomCalendar(3, calendarSetup); testDateConstructorWithCustomCalendar(4, calendarSetup); } private void testDateConstructorWithCustomCalendar(int hoursOffset, Consumer calendarSetup) { try { calendarSetup.accept(hoursOffset); Hour h = new Hour(new Date(0L)); assertEquals(1970, h.getYear()); assertEquals(1, h.getMonth()); assertEquals(1, h.getDayOfMonth()); assertEquals(hoursOffset, h.getHour()); assertEquals(0L, h.getFirstMillisecond()); } finally { // reset everything, to avoid affecting other tests RegularTimePeriod.setThreadLocalCalendarInstance(null); RegularTimePeriod.setCalendarInstancePrototype(null); } } /** * If a thread-local calendar was set, the hour-day constructor should use it. */ @Test public void testHourDayConstructorWithThreadLocalCalendar() { Consumer calendarSetup = hours -> RegularTimePeriod.setThreadLocalCalendarInstance( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testHourDayConstructorWithCustomCalendar(3, calendarSetup); testHourDayConstructorWithCustomCalendar(4, calendarSetup); } /** * If a calendar prototype was set, the hour-day constructor should use it. */ @Test public void testHourDayConstructorWithCalendarPrototype() { Consumer calendarSetup = hours -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testHourDayConstructorWithCustomCalendar(3, calendarSetup); testHourDayConstructorWithCustomCalendar(4, calendarSetup); } private void testHourDayConstructorWithCustomCalendar(int hoursOffset, Consumer calendarSetup) { try { calendarSetup.accept(hoursOffset); long ms = -3_600_000L * hoursOffset; Hour h = new Hour(0, new Day(new Date(ms))); assertEquals(1970, h.getYear()); assertEquals(1, h.getMonth()); assertEquals(1, h.getDayOfMonth()); assertEquals(0, h.getHour()); assertEquals(ms, h.getFirstMillisecond()); } finally { // reset everything, to avoid affecting other tests RegularTimePeriod.setThreadLocalCalendarInstance(null); RegularTimePeriod.setCalendarInstancePrototype(null); } } /** * Set up an hour equal to hour zero, 1 January 1900. Request the * previous hour, it should be null. */ @Test public void testFirstHourPrevious() { Hour first = new Hour(0, new Day(1, MonthConstants.JANUARY, 1900)); Hour previous = (Hour) first.previous(); assertNull(previous); } /** * Set up an hour equal to hour zero, 1 January 1900. Request the next * hour, it should be null. */ @Test public void testFirstHourNext() { Hour first = new Hour(0, new Day(1, MonthConstants.JANUARY, 1900)); Hour next = (Hour) first.next(); assertEquals(1, next.getHour()); assertEquals(1900, next.getYear()); } /** * Set up an hour equal to hour zero, 1 January 1900. Request the previous * hour, it should be null. */ @Test public void testLastHourPrevious() { Hour last = new Hour(23, new Day(31, MonthConstants.DECEMBER, 9999)); Hour previous = (Hour) last.previous(); assertEquals(22, previous.getHour()); assertEquals(9999, previous.getYear()); } /** * Set up an hour equal to hour zero, 1 January 1900. Request the next * hour, it should be null. */ @Test public void testLastHourNext() { Hour last = new Hour(23, new Day(31, MonthConstants.DECEMBER, 9999)); Hour next = (Hour) last.next(); assertNull(next); } /** * Problem for date parsing. */ @Test public void testParseHour() { // test 1... Hour h = Hour.parseHour("2002-01-29 13"); assertEquals(13, h.getHour()); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { Hour h1 = new Hour(); Hour h2 = TestUtils.serialised(h1); assertEquals(h1, h2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { Hour h1 = new Hour(7, 9, 10, 1999); Hour h2 = new Hour(7, 9, 10, 1999); assertEquals(h1, h2); int hash1 = h1.hashCode(); int hash2 = h2.hashCode(); assertEquals(hash1, hash2); } /** * The {@link Hour} class is immutable, so should not be {@link Cloneable}. */ @Test public void testNotCloneable() { Hour h = new Hour(7, 9, 10, 1999); assertFalse(h instanceof Cloneable); } /** * Some checks for the getFirstMillisecond() method. */ @Test public void testGetFirstMillisecond() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.UK); TimeZone savedZone = TimeZone.getDefault(); TimeZone.setDefault(TimeZone.getTimeZone("Europe/London")); Hour h = new Hour(15, 1, 4, 2006); assertEquals(1143900000000L, h.getFirstMillisecond()); Locale.setDefault(saved); TimeZone.setDefault(savedZone); } /** * Some checks for the getFirstMillisecond(TimeZone) method. */ @Test public void testGetFirstMillisecondWithTimeZone() { Hour h = new Hour(15, 1, 4, 1950); TimeZone zone = TimeZone.getTimeZone("America/Los_Angeles"); Calendar cal = Calendar.getInstance(zone); assertEquals(-623293200000L, h.getFirstMillisecond(cal)); // try null calendar boolean pass = false; try { h.getFirstMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getFirstMillisecond(TimeZone) method. */ @Test public void testGetFirstMillisecondWithCalendar() { Hour h = new Hour(2, 15, 4, 2000); GregorianCalendar calendar = new GregorianCalendar(Locale.GERMANY); calendar.setTimeZone(TimeZone.getTimeZone("Europe/Frankfurt")); assertEquals(955764000000L, h.getFirstMillisecond(calendar)); // try null calendar boolean pass = false; try { h.getFirstMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getLastMillisecond() method. */ @Test public void testGetLastMillisecond() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.UK); TimeZone savedZone = TimeZone.getDefault(); TimeZone.setDefault(TimeZone.getTimeZone("Europe/London")); Hour h = new Hour(1, 1, 1, 1970); assertEquals(3599999L, h.getLastMillisecond()); Locale.setDefault(saved); TimeZone.setDefault(savedZone); } /** * Some checks for the getLastMillisecond(TimeZone) method. */ @Test public void testGetLastMillisecondWithTimeZone() { Hour h = new Hour(2, 7, 7, 1950); TimeZone zone = TimeZone.getTimeZone("America/Los_Angeles"); Calendar cal = Calendar.getInstance(zone); assertEquals(-614959200001L, h.getLastMillisecond(cal)); // try null calendar boolean pass = false; try { h.getLastMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getLastMillisecond(TimeZone) method. */ @Test public void testGetLastMillisecondWithCalendar() { Hour h = new Hour(21, 21, 4, 2001); GregorianCalendar calendar = new GregorianCalendar(Locale.GERMANY); calendar.setTimeZone(TimeZone.getTimeZone("Europe/Frankfurt")); assertEquals(987890399999L, h.getLastMillisecond(calendar)); // try null calendar boolean pass = false; try { h.getLastMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getSerialIndex() method. */ @Test public void testGetSerialIndex() { Hour h = new Hour(1, 1, 1, 2000); assertEquals(876625L, h.getSerialIndex()); h = new Hour(1, 1, 1, 1900); assertEquals(49L, h.getSerialIndex()); } /** * Some checks for the testNext() method. */ @Test public void testNext() { Hour h = new Hour(1, 12, 12, 2000); h = (Hour) h.next(); assertEquals(2000, h.getYear()); assertEquals(12, h.getMonth()); assertEquals(12, h.getDayOfMonth()); assertEquals(2, h.getHour()); h = new Hour(23, 31, 12, 9999); assertNull(h.next()); } /** * If a thread-local calendar was set, next() should use its time zone. */ @Test public void testNextWithThreadLocalCalendar() { Consumer calendarSetup = hours -> RegularTimePeriod.setThreadLocalCalendarInstance( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testNextWithCustomCalendar(3, calendarSetup); testNextWithCustomCalendar(4, calendarSetup); } /** * If a calendar prototype was set, next() should use its time zone. */ @Test public void testNextWithCalendarPrototype() { Consumer calendarSetup = hours -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testNextWithCustomCalendar(3, calendarSetup); testNextWithCustomCalendar(4, calendarSetup); } private void testNextWithCustomCalendar(int hoursOffset, Consumer calendarSetup) { try { calendarSetup.accept(hoursOffset); Hour h = new Hour(new Date(0L)); h = (Hour) h.next(); assertEquals(1970, h.getYear()); assertEquals(1, h.getMonth()); assertEquals(1, h.getDayOfMonth()); assertEquals(hoursOffset + 1, h.getHour()); assertEquals(3_600_000L, h.getFirstMillisecond()); } finally { // reset everything, to avoid affecting other tests RegularTimePeriod.setThreadLocalCalendarInstance(null); RegularTimePeriod.setCalendarInstancePrototype(null); } } /** * Some checks for the getStart() method. */ @Test public void testGetStart() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.ITALY); Calendar cal = Calendar.getInstance(Locale.ITALY); cal.set(2006, Calendar.JANUARY, 16, 3, 0, 0); cal.set(Calendar.MILLISECOND, 0); Hour h = new Hour(3, 16, 1, 2006); assertEquals(cal.getTime(), h.getStart()); Locale.setDefault(saved); } /** * Some checks for the getEnd() method. */ @Test public void testGetEnd() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.ITALY); Calendar cal = Calendar.getInstance(Locale.ITALY); cal.set(2006, Calendar.JANUARY, 8, 1, 59, 59); cal.set(Calendar.MILLISECOND, 999); Hour h = new Hour(1, 8, 1, 2006); assertEquals(cal.getTime(), h.getEnd()); Locale.setDefault(saved); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/time/MillisecondTest.java000066400000000000000000000435561463604235500276410ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------- * MillisecondTest.java * -------------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import java.time.ZoneOffset; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.Locale; import java.util.TimeZone; import java.util.function.Consumer; import org.jfree.chart.TestUtils; import org.jfree.chart.date.MonthConstants; import org.junit.jupiter.api.Test; /** * Tests for the {@link Millisecond} class. */ public class MillisecondTest { /** * Check that a {@link Millisecond} instance is equal to itself. * * SourceForge Bug ID: 558850. */ @Test public void testEqualsSelf() { Millisecond millisecond = new Millisecond(); assertEquals(millisecond, millisecond); } /** * Tests the equals method. */ @Test public void testEquals() { Day day1 = new Day(29, MonthConstants.MARCH, 2002); Hour hour1 = new Hour(15, day1); Minute minute1 = new Minute(15, hour1); Second second1 = new Second(34, minute1); Millisecond milli1 = new Millisecond(999, second1); Day day2 = new Day(29, MonthConstants.MARCH, 2002); Hour hour2 = new Hour(15, day2); Minute minute2 = new Minute(15, hour2); Second second2 = new Second(34, minute2); Millisecond milli2 = new Millisecond(999, second2); assertEquals(milli1, milli2); } /** * In GMT, the 4.55:59.123pm on 21 Mar 2002 is * java.util.Date(1016729759123L). Use this to check the Millisecond * constructor. */ @Test public void testDateConstructor1() { TimeZone zone = TimeZone.getTimeZone("GMT"); Calendar cal = Calendar.getInstance(zone); Locale locale = Locale.getDefault(); // locale should not matter here Millisecond m1 = new Millisecond(new Date(1016729759122L), zone, locale); Millisecond m2 = new Millisecond(new Date(1016729759123L), zone, locale); assertEquals(122, m1.getMillisecond()); assertEquals(1016729759122L, m1.getLastMillisecond(cal)); assertEquals(123, m2.getMillisecond()); assertEquals(1016729759123L, m2.getFirstMillisecond(cal)); } /** * In Tallinn, the 4.55:59.123pm on 21 Mar 2002 is * java.util.Date(1016722559123L). Use this to check the Millisecond * constructor. */ @Test public void testDateConstructor2() { TimeZone zone = TimeZone.getTimeZone("Europe/Tallinn"); Calendar cal = Calendar.getInstance(zone); Locale locale = Locale.getDefault(); // locale should not matter here Millisecond m1 = new Millisecond(new Date(1016722559122L), zone, locale); Millisecond m2 = new Millisecond(new Date(1016722559123L), zone, locale); assertEquals(122, m1.getMillisecond()); assertEquals(1016722559122L, m1.getLastMillisecond(cal)); assertEquals(123, m2.getMillisecond()); assertEquals(1016722559123L, m2.getFirstMillisecond(cal)); } /** * If a thread-local calendar was set, the Date constructor should use it. */ @Test public void testDateConstructorWithThreadLocalCalendar() { Consumer calendarSetup = hours -> RegularTimePeriod.setThreadLocalCalendarInstance( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testDateConstructorWithCustomCalendar(3, calendarSetup); testDateConstructorWithCustomCalendar(4, calendarSetup); } /** * If a calendar prototype was set, the Date constructor should use it. */ @Test public void testDateConstructorWithCalendarPrototype() { Consumer calendarSetup = hours -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testDateConstructorWithCustomCalendar(3, calendarSetup); testDateConstructorWithCustomCalendar(4, calendarSetup); } private void testDateConstructorWithCustomCalendar(int hoursOffset, Consumer calendarSetup) { try { calendarSetup.accept(hoursOffset); Millisecond m = new Millisecond(new Date(0L)); assertEquals(1970, m.getSecond().getMinute().getHour().getYear()); assertEquals(1, m.getSecond().getMinute().getHour().getMonth()); assertEquals(1, m.getSecond().getMinute().getHour().getDayOfMonth()); assertEquals(hoursOffset, m.getSecond().getMinute().getHour().getHour()); assertEquals(0, m.getSecond().getMinute().getMinute()); assertEquals(0, m.getSecond().getSecond()); assertEquals(0, m.getMillisecond()); assertEquals(0L, m.getFirstMillisecond()); } finally { // reset everything, to avoid affecting other tests RegularTimePeriod.setThreadLocalCalendarInstance(null); RegularTimePeriod.setCalendarInstancePrototype(null); } } /** * If a thread-local calendar was set, the millisecond-second constructor should use it. */ @Test public void testMillisecondSecondConstructorWithThreadLocalCalendar() { Consumer calendarSetup = hours -> RegularTimePeriod.setThreadLocalCalendarInstance( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testMillisecondSecondConstructorWithCustomCalendar(3, calendarSetup); testMillisecondSecondConstructorWithCustomCalendar(4, calendarSetup); } /** * If a calendar prototype was set, the millisecond-second constructor should use it. */ @Test public void testMillisecondSecondConstructorWithCalendarPrototype() { Consumer calendarSetup = hours -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testMillisecondSecondConstructorWithCustomCalendar(3, calendarSetup); testMillisecondSecondConstructorWithCustomCalendar(4, calendarSetup); } private void testMillisecondSecondConstructorWithCustomCalendar(int hoursOffset, Consumer calendarSetup) { try { calendarSetup.accept(hoursOffset); Millisecond m = new Millisecond(0, new Second(new Date(0L))); assertEquals(1970, m.getSecond().getMinute().getHour().getYear()); assertEquals(1, m.getSecond().getMinute().getHour().getMonth()); assertEquals(1, m.getSecond().getMinute().getHour().getDayOfMonth()); assertEquals(hoursOffset, m.getSecond().getMinute().getHour().getHour()); assertEquals(0, m.getSecond().getMinute().getMinute()); assertEquals(0, m.getSecond().getSecond()); assertEquals(0, m.getMillisecond()); assertEquals(0L, m.getFirstMillisecond()); } finally { // reset everything, to avoid affecting other tests RegularTimePeriod.setThreadLocalCalendarInstance(null); RegularTimePeriod.setCalendarInstancePrototype(null); } } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { Millisecond m1 = new Millisecond(); Millisecond m2 = TestUtils.serialised(m1); assertEquals(m1, m2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { Millisecond m1 = new Millisecond(599, 23, 45, 7, 9, 10, 2007); Millisecond m2 = new Millisecond(599, 23, 45, 7, 9, 10, 2007); assertEquals(m1, m2); int hash1 = m1.hashCode(); int hash2 = m2.hashCode(); assertEquals(hash1, hash2); } /** * A test for bug report 943985 - the calculation for the middle * millisecond is incorrect for odd milliseconds. */ @Test public void test943985() { Millisecond ms = new Millisecond(new java.util.Date(4)); assertEquals(ms.getFirstMillisecond(), ms.getMiddleMillisecond()); assertEquals(ms.getMiddleMillisecond(), ms.getLastMillisecond()); ms = new Millisecond(new java.util.Date(5)); assertEquals(ms.getFirstMillisecond(), ms.getMiddleMillisecond()); assertEquals(ms.getMiddleMillisecond(), ms.getLastMillisecond()); } /** * The {@link Millisecond} class is immutable, so should not be * {@link Cloneable}. */ @Test public void testNotCloneable() { Millisecond m = new Millisecond(599, 23, 45, 7, 9, 10, 2007); assertFalse(m instanceof Cloneable); } /** * Some checks for the getFirstMillisecond() method. */ @Test public void testGetFirstMillisecond() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.UK); TimeZone savedZone = TimeZone.getDefault(); TimeZone.setDefault(TimeZone.getTimeZone("Europe/London")); Millisecond m = new Millisecond(500, 15, 43, 15, 1, 4, 2006); assertEquals(1143902595500L, m.getFirstMillisecond()); Locale.setDefault(saved); TimeZone.setDefault(savedZone); } /** * Some checks for the getFirstMillisecond(TimeZone) method. */ @Test public void testGetFirstMillisecondWithTimeZone() { Millisecond m = new Millisecond(500, 50, 59, 15, 1, 4, 1950); TimeZone zone = TimeZone.getTimeZone("America/Los_Angeles"); Calendar cal = Calendar.getInstance(zone); assertEquals(-623289609500L, m.getFirstMillisecond(cal)); // try null calendar boolean pass = false; try { m.getFirstMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getFirstMillisecond(TimeZone) method. */ @Test public void testGetFirstMillisecondWithCalendar() { Millisecond m = new Millisecond(500, 55, 40, 2, 15, 4, 2000); GregorianCalendar calendar = new GregorianCalendar(Locale.GERMANY); calendar.setTimeZone(TimeZone.getTimeZone("Europe/Frankfurt")); assertEquals(955766455500L, m.getFirstMillisecond(calendar)); // try null calendar boolean pass = false; try { m.getFirstMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getLastMillisecond() method. */ @Test public void testGetLastMillisecond() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.UK); TimeZone savedZone = TimeZone.getDefault(); TimeZone.setDefault(TimeZone.getTimeZone("Europe/London")); Millisecond m = new Millisecond(750, 1, 1, 1, 1, 1, 1970); assertEquals(61750L, m.getLastMillisecond()); Locale.setDefault(saved); TimeZone.setDefault(savedZone); } /** * Some checks for the getLastMillisecond(TimeZone) method. */ @Test public void testGetLastMillisecondWithTimeZone() { Millisecond m = new Millisecond(750, 55, 1, 2, 7, 7, 1950); TimeZone zone = TimeZone.getTimeZone("America/Los_Angeles"); Calendar cal = Calendar.getInstance(zone); assertEquals(-614962684250L, m.getLastMillisecond(cal)); // try null calendar boolean pass = false; try { m.getLastMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getLastMillisecond(TimeZone) method. */ @Test public void testGetLastMillisecondWithCalendar() { Millisecond m = new Millisecond(250, 50, 45, 21, 21, 4, 2001); GregorianCalendar calendar = new GregorianCalendar(Locale.GERMANY); calendar.setTimeZone(TimeZone.getTimeZone("Europe/Frankfurt")); assertEquals(987889550250L, m.getLastMillisecond(calendar)); // try null calendar boolean pass = false; try { m.getLastMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getSerialIndex() method. */ @Test public void testGetSerialIndex() { Millisecond m = new Millisecond(500, 1, 1, 1, 1, 1, 2000); assertEquals(3155850061500L, m.getSerialIndex()); m = new Millisecond(500, 1, 1, 1, 1, 1, 1900); // TODO: this must be wrong... assertEquals(176461500L, m.getSerialIndex()); } /** * Some checks for the testNext() method. */ @Test public void testNext() { Millisecond m = new Millisecond(555, 55, 30, 1, 12, 12, 2000); m = (Millisecond) m.next(); assertEquals(2000, m.getSecond().getMinute().getHour().getYear()); assertEquals(12, m.getSecond().getMinute().getHour().getMonth()); assertEquals(12, m.getSecond().getMinute().getHour().getDayOfMonth()); assertEquals(1, m.getSecond().getMinute().getHour().getHour()); assertEquals(30, m.getSecond().getMinute().getMinute()); assertEquals(55, m.getSecond().getSecond()); assertEquals(556, m.getMillisecond()); m = new Millisecond(999, 59, 59, 23, 31, 12, 9999); assertNull(m.next()); } /** * If a thread-local calendar was set, next() should use its time zone. */ @Test public void testNextWithThreadLocalCalendar() { Consumer calendarSetup = hours -> RegularTimePeriod.setThreadLocalCalendarInstance( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testNextWithCustomCalendar(3, calendarSetup); testNextWithCustomCalendar(4, calendarSetup); } /** * If a calendar prototype was set, next() should use its time zone. */ @Test public void testNextWithCalendarPrototype() { Consumer calendarSetup = hours -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testNextWithCustomCalendar(3, calendarSetup); testNextWithCustomCalendar(4, calendarSetup); } private void testNextWithCustomCalendar(int hoursOffset, Consumer calendarSetup) { try { calendarSetup.accept(hoursOffset); Millisecond m = new Millisecond(new Date(0L)); m = (Millisecond) m.next(); assertEquals(1970, m.getSecond().getMinute().getHour().getYear()); assertEquals(1, m.getSecond().getMinute().getHour().getMonth()); assertEquals(1, m.getSecond().getMinute().getHour().getDayOfMonth()); assertEquals(hoursOffset, m.getSecond().getMinute().getHour().getHour()); assertEquals(0, m.getSecond().getMinute().getMinute()); assertEquals(0, m.getSecond().getSecond()); assertEquals(1L, m.getMillisecond()); assertEquals(1L, m.getFirstMillisecond()); } finally { // reset everything, to avoid affecting other tests RegularTimePeriod.setThreadLocalCalendarInstance(null); RegularTimePeriod.setCalendarInstancePrototype(null); } } /** * Some checks for the getStart() method. */ @Test public void testGetStart() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.ITALY); Calendar cal = Calendar.getInstance(Locale.ITALY); cal.set(2006, Calendar.JANUARY, 16, 3, 47, 55); cal.set(Calendar.MILLISECOND, 555); Millisecond m = new Millisecond(555, 55, 47, 3, 16, 1, 2006); assertEquals(cal.getTime(), m.getStart()); Locale.setDefault(saved); } /** * Some checks for the getEnd() method. */ @Test public void testGetEnd() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.ITALY); Calendar cal = Calendar.getInstance(Locale.ITALY); cal.set(2006, Calendar.JANUARY, 16, 3, 47, 55); cal.set(Calendar.MILLISECOND, 555); Millisecond m = new Millisecond(555, 55, 47, 3, 16, 1, 2006); assertEquals(cal.getTime(), m.getEnd()); Locale.setDefault(saved); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/time/MinuteTest.java000066400000000000000000000404011463604235500266220ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------- * MinuteTest.java * --------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import java.time.ZoneOffset; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.Locale; import java.util.TimeZone; import java.util.function.Consumer; import org.jfree.chart.TestUtils; import org.jfree.chart.date.MonthConstants; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link Minute} class. */ public class MinuteTest { /** * Check that a Minute instance is equal to itself. * * SourceForge Bug ID: 558850. */ @Test public void testEqualsSelf() { Minute minute = new Minute(); assertEquals(minute, minute); } /** * Tests the equals method. */ @Test public void testEquals() { Day day1 = new Day(29, MonthConstants.MARCH, 2002); Hour hour1 = new Hour(15, day1); Minute minute1 = new Minute(15, hour1); Day day2 = new Day(29, MonthConstants.MARCH, 2002); Hour hour2 = new Hour(15, day2); Minute minute2 = new Minute(15, hour2); assertEquals(minute1, minute2); } @Test public void comparison() { Minute m1 = new Minute(30, 6, 10, 8, 2021); Minute m2 = new Minute(30, 6, 11, 8, 2021); assertNotEquals(m1, m2); assertEquals(-1, m1.compareTo(m2)); assertEquals(1, m2.compareTo(m1)); assertEquals(0, m1.compareTo(m1)); } /** * In GMT, the 4.55pm on 21 Mar 2002 is java.util.Date(1016729700000L). * Use this to check the Minute constructor. */ @Test public void testDateConstructor1() { TimeZone zone = TimeZone.getTimeZone("GMT"); Calendar cal = Calendar.getInstance(zone); Locale locale = Locale.getDefault(); // locale should not matter here Minute m1 = new Minute(new Date(1016729699999L), zone, locale); Minute m2 = new Minute(new Date(1016729700000L), zone, locale); assertEquals(54, m1.getMinute()); assertEquals(1016729699999L, m1.getLastMillisecond(cal)); assertEquals(55, m2.getMinute()); assertEquals(1016729700000L, m2.getFirstMillisecond(cal)); } /** * In Singapore, the 4.55pm on 21 Mar 2002 is * java.util.Date(1,014,281,700,000L). Use this to check the Minute * constructor. */ @Test public void testDateConstructor2() { TimeZone zone = TimeZone.getTimeZone("Asia/Singapore"); Calendar cal = Calendar.getInstance(zone); Locale locale = Locale.getDefault(); // locale should not matter here Minute m1 = new Minute(new Date(1016700899999L), zone, locale); Minute m2 = new Minute(new Date(1016700900000L), zone, locale); assertEquals(54, m1.getMinute()); assertEquals(1016700899999L, m1.getLastMillisecond(cal)); assertEquals(55, m2.getMinute()); assertEquals(1016700900000L, m2.getFirstMillisecond(cal)); } /** * If a thread-local calendar was set, the Date constructor should use it. */ @Test public void testDateConstructorWithThreadLocalCalendar() { Consumer calendarSetup = hours -> RegularTimePeriod.setThreadLocalCalendarInstance( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testDateConstructorWithCustomCalendar(3, calendarSetup); testDateConstructorWithCustomCalendar(4, calendarSetup); } /** * If a calendar prototype was set, the Date constructor should use it. */ @Test public void testDateConstructorWithCalendarPrototype() { Consumer calendarSetup = hours -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testDateConstructorWithCustomCalendar(3, calendarSetup); testDateConstructorWithCustomCalendar(4, calendarSetup); } private void testDateConstructorWithCustomCalendar(int hoursOffset, Consumer calendarSetup) { try { calendarSetup.accept(hoursOffset); Minute m = new Minute(new Date(0L)); assertEquals(1970, m.getHour().getYear()); assertEquals(1, m.getHour().getMonth()); assertEquals(1, m.getHour().getDayOfMonth()); assertEquals(hoursOffset, m.getHour().getHour()); assertEquals(0, m.getMinute()); assertEquals(0L, m.getFirstMillisecond()); } finally { // reset everything, to avoid affecting other tests RegularTimePeriod.setThreadLocalCalendarInstance(null); RegularTimePeriod.setCalendarInstancePrototype(null); } } /** * If a thread-local calendar was set, the minute-hour constructor should use it. */ @Test public void testMinuteHourConstructorWithThreadLocalCalendar() { Consumer calendarSetup = hours -> RegularTimePeriod.setThreadLocalCalendarInstance( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testMinuteHourConstructorWithCustomCalendar(3, calendarSetup); testMinuteHourConstructorWithCustomCalendar(4, calendarSetup); } /** * If a calendar prototype was set, the minute-hour constructor should use it. */ @Test public void testMinuteHourConstructorWithCalendarPrototype() { Consumer calendarSetup = hours -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testMinuteHourConstructorWithCustomCalendar(3, calendarSetup); testMinuteHourConstructorWithCustomCalendar(4, calendarSetup); } private void testMinuteHourConstructorWithCustomCalendar(int hoursOffset, Consumer calendarSetup) { try { calendarSetup.accept(hoursOffset); Minute m = new Minute(0, new Hour(new Date(0L))); assertEquals(1970, m.getHour().getYear()); assertEquals(1, m.getHour().getMonth()); assertEquals(1, m.getHour().getDayOfMonth()); assertEquals(hoursOffset, m.getHour().getHour()); assertEquals(0, m.getMinute()); assertEquals(0L, m.getFirstMillisecond()); } finally { // reset everything, to avoid affecting other tests RegularTimePeriod.setThreadLocalCalendarInstance(null); RegularTimePeriod.setCalendarInstancePrototype(null); } } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { Minute m1 = new Minute(); Minute m2 = TestUtils.serialised(m1); assertEquals(m1, m2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { Minute m1 = new Minute(45, 5, 1, 2, 2003); Minute m2 = new Minute(45, 5, 1, 2, 2003); assertEquals(m1, m2); int h1 = m1.hashCode(); int h2 = m2.hashCode(); assertEquals(h1, h2); } /** * The {@link Minute} class is immutable, so should not be * {@link Cloneable}. */ @Test public void testNotCloneable() { Minute m = new Minute(45, 5, 1, 2, 2003); assertFalse(m instanceof Cloneable); } /** * Some checks for the getFirstMillisecond() method. */ @Test public void testGetFirstMillisecond() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.UK); TimeZone savedZone = TimeZone.getDefault(); TimeZone.setDefault(TimeZone.getTimeZone("Europe/London")); Minute m = new Minute(43, 15, 1, 4, 2006); assertEquals(1143902580000L, m.getFirstMillisecond()); Locale.setDefault(saved); TimeZone.setDefault(savedZone); } /** * Some checks for the getFirstMillisecond(TimeZone) method. */ @Test public void testGetFirstMillisecondWithTimeZone() { Minute m = new Minute(59, 15, 1, 4, 1950); TimeZone zone = TimeZone.getTimeZone("America/Los_Angeles"); Calendar cal = Calendar.getInstance(zone); assertEquals(-623289660000L, m.getFirstMillisecond(cal)); // try null calendar boolean pass = false; try { m.getFirstMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getFirstMillisecond(TimeZone) method. */ @Test public void testGetFirstMillisecondWithCalendar() { Minute m = new Minute(40, 2, 15, 4, 2000); GregorianCalendar calendar = new GregorianCalendar(Locale.GERMANY); calendar.setTimeZone(TimeZone.getTimeZone("Europe/Frankfurt")); assertEquals(955766400000L, m.getFirstMillisecond(calendar)); // try null calendar boolean pass = false; try { m.getFirstMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getLastMillisecond() method. */ @Test public void testGetLastMillisecond() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.UK); TimeZone savedZone = TimeZone.getDefault(); TimeZone.setDefault(TimeZone.getTimeZone("Europe/London")); Minute m = new Minute(1, 1, 1, 1, 1970); assertEquals(119999L, m.getLastMillisecond()); Locale.setDefault(saved); TimeZone.setDefault(savedZone); } /** * Some checks for the getLastMillisecond(TimeZone) method. */ @Test public void testGetLastMillisecondWithTimeZone() { Minute m = new Minute(1, 2, 7, 7, 1950); TimeZone zone = TimeZone.getTimeZone("America/Los_Angeles"); Calendar cal = Calendar.getInstance(zone); assertEquals(-614962680001L, m.getLastMillisecond(cal)); // try null calendar boolean pass = false; try { m.getLastMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getLastMillisecond(TimeZone) method. */ @Test public void testGetLastMillisecondWithCalendar() { Minute m = new Minute(45, 21, 21, 4, 2001); GregorianCalendar calendar = new GregorianCalendar(Locale.GERMANY); calendar.setTimeZone(TimeZone.getTimeZone("Europe/Frankfurt")); assertEquals(987889559999L, m.getLastMillisecond(calendar)); // try null calendar boolean pass = false; try { m.getLastMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getSerialIndex() method. */ @Test public void testGetSerialIndex() { Minute m = new Minute(1, 1, 1, 1, 2000); assertEquals(52597501L, m.getSerialIndex()); m = new Minute(1, 1, 1, 1, 1900); assertEquals(2941L, m.getSerialIndex()); } /** * Some checks for the testNext() method. */ @Test public void testNext() { Minute m = new Minute(30, 1, 12, 12, 2000); m = (Minute) m.next(); assertEquals(2000, m.getHour().getYear()); assertEquals(12, m.getHour().getMonth()); assertEquals(12, m.getHour().getDayOfMonth()); assertEquals(1, m.getHour().getHour()); assertEquals(31, m.getMinute()); m = new Minute(59, 23, 31, 12, 9999); assertNull(m.next()); } /** * If a thread-local calendar was set, next() should use its time zone. */ @Test public void testNextWithThreadLocalCalendar() { Consumer calendarSetup = hours -> RegularTimePeriod.setThreadLocalCalendarInstance( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testNextWithCustomCalendar(3, calendarSetup); testNextWithCustomCalendar(4, calendarSetup); } /** * If a calendar prototype was set, next() should use its time zone. */ @Test public void testNextWithCalendarPrototype() { Consumer calendarSetup = hours -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testNextWithCustomCalendar(3, calendarSetup); testNextWithCustomCalendar(4, calendarSetup); } private void testNextWithCustomCalendar(int hoursOffset, Consumer calendarSetup) { try { calendarSetup.accept(hoursOffset); Minute m = new Minute(new Date(0L)); m = (Minute) m.next(); assertEquals(1970, m.getHour().getYear()); assertEquals(1, m.getHour().getMonth()); assertEquals(1, m.getHour().getDayOfMonth()); assertEquals(hoursOffset, m.getHour().getHour()); assertEquals(1, m.getMinute()); assertEquals(60_000L, m.getFirstMillisecond()); } finally { // reset everything, to avoid affecting other tests RegularTimePeriod.setThreadLocalCalendarInstance(null); RegularTimePeriod.setCalendarInstancePrototype(null); } } /** * Some checks for the getStart() method. */ @Test public void testGetStart() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.ITALY); TimeZone savedZone = TimeZone.getDefault(); TimeZone.setDefault(TimeZone.getTimeZone("Europe/Rome")); Calendar cal = Calendar.getInstance(Locale.ITALY); cal.set(2006, Calendar.JANUARY, 16, 3, 47, 0); cal.set(Calendar.MILLISECOND, 0); Minute m = new Minute(47, 3, 16, 1, 2006); assertEquals(cal.getTime(), m.getStart()); Locale.setDefault(saved); TimeZone.setDefault(savedZone); } /** * Some checks for the getEnd() method. */ @Test public void testGetEnd() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.ITALY); TimeZone savedZone = TimeZone.getDefault(); TimeZone.setDefault(TimeZone.getTimeZone("Europe/Rome")); Calendar cal = Calendar.getInstance(Locale.ITALY); cal.set(2006, Calendar.JANUARY, 16, 3, 47, 59); cal.set(Calendar.MILLISECOND, 999); Minute m = new Minute(47, 3, 16, 1, 2006); assertEquals(cal.getTime(), m.getEnd()); Locale.setDefault(saved); TimeZone.setDefault(savedZone); } /** * Test for bug 1611872 - previous() fails for first minute in hour. */ @Test public void test1611872() { Minute m1 = new Minute(0, 10, 15, 4, 2000); Minute m2 = (Minute) m1.previous(); assertEquals(m2, new Minute(59, 9, 15, 4, 2000)); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/time/MonthTest.java000066400000000000000000000456261463604235500264640ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------- * MonthTest.java * -------------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import java.time.ZoneOffset; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.Locale; import java.util.TimeZone; import java.util.function.Consumer; import org.jfree.chart.TestUtils; import org.jfree.chart.date.MonthConstants; import org.jfree.chart.date.SerialDate; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * Tests for the {@link Month} class. */ public class MonthTest { /** A month. */ private Month jan1900; /** A month. */ private Month feb1900; /** A month. */ private Month nov9999; /** A month. */ private Month dec9999; /** * Common test setup. */ @BeforeEach public void setUp() { this.jan1900 = new Month(MonthConstants.JANUARY, 1900); this.feb1900 = new Month(MonthConstants.FEBRUARY, 1900); this.nov9999 = new Month(MonthConstants.NOVEMBER, 9999); this.dec9999 = new Month(MonthConstants.DECEMBER, 9999); } /** * Check that a Month instance is equal to itself. * * SourceForge Bug ID: 558850. */ @Test public void testEqualsSelf() { Month month = new Month(); assertEquals(month, month); } /** * Tests the equals method. */ @Test public void testEquals() { Month m1 = new Month(MonthConstants.MAY, 2002); Month m2 = new Month(MonthConstants.MAY, 2002); assertEquals(m1, m2); } /** * In GMT, the end of Feb 2000 is java.util.Date(951,868,799,999L). Use * this to check the Month constructor. */ @Test public void testDateConstructor1() { TimeZone zone = TimeZone.getTimeZone("GMT"); Calendar cal = Calendar.getInstance(zone); Month m1 = new Month(new Date(951868799999L), zone, Locale.getDefault()); Month m2 = new Month(new Date(951868800000L), zone, Locale.getDefault()); assertEquals(MonthConstants.FEBRUARY, m1.getMonth()); assertEquals(951868799999L, m1.getLastMillisecond(cal)); assertEquals(MonthConstants.MARCH, m2.getMonth()); assertEquals(951868800000L, m2.getFirstMillisecond(cal)); } /** * In Auckland, the end of Feb 2000 is java.util.Date(951,821,999,999L). * Use this to check the Month constructor. */ @Test public void testDateConstructor2() { TimeZone zone = TimeZone.getTimeZone("Pacific/Auckland"); Calendar cal = Calendar.getInstance(zone); Month m1 = new Month(new Date(951821999999L), zone, Locale.getDefault()); Month m2 = new Month(new Date(951822000000L), zone, Locale.getDefault()); assertEquals(MonthConstants.FEBRUARY, m1.getMonth()); assertEquals(951821999999L, m1.getLastMillisecond(cal)); assertEquals(MonthConstants.MARCH, m2.getMonth()); assertEquals(951822000000L, m2.getFirstMillisecond(cal)); } /** * If a thread-local calendar was set, the Date constructor should use it. */ @Test public void testDateConstructorWithThreadLocalCalendar() { Consumer calendarSetup = hours -> RegularTimePeriod.setThreadLocalCalendarInstance( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testDateConstructorWithCustomCalendar(3, calendarSetup); testDateConstructorWithCustomCalendar(4, calendarSetup); } /** * If a calendar prototype was set, the Date constructor should use it. */ @Test public void testDateConstructorWithCalendarPrototype() { Consumer calendarSetup = hours -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testDateConstructorWithCustomCalendar(3, calendarSetup); testDateConstructorWithCustomCalendar(4, calendarSetup); } private void testDateConstructorWithCustomCalendar(int hoursOffset, Consumer calendarSetup) { try { calendarSetup.accept(hoursOffset); long ms = -3_600_000L * hoursOffset; Month m = new Month(new Date(ms)); assertEquals(1970, m.getYear().getYear()); assertEquals(1, m.getMonth()); assertEquals(ms, m.getFirstMillisecond()); } finally { // reset everything, to avoid affecting other tests RegularTimePeriod.setThreadLocalCalendarInstance(null); RegularTimePeriod.setCalendarInstancePrototype(null); } } /** * If a thread-local calendar was set, the (int, int) month-year constructor should use it. */ @Test public void testMonthIntYearConstructorWithThreadLocalCalendar() { Consumer calendarSetup = hours -> RegularTimePeriod.setThreadLocalCalendarInstance( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testMonthIntYearConstructorWithCustomCalendar(3, calendarSetup); testMonthIntYearConstructorWithCustomCalendar(4, calendarSetup); } /** * If a calendar prototype was set, the the (int, int) month-year constructor should use it. */ @Test public void testMonthIntYearConstructorWithCalendarPrototype() { Consumer calendarSetup = hours -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testMonthIntYearConstructorWithCustomCalendar(3, calendarSetup); testMonthIntYearConstructorWithCustomCalendar(4, calendarSetup); } private void testMonthIntYearConstructorWithCustomCalendar(int hoursOffset, Consumer calendarSetup) { try { calendarSetup.accept(hoursOffset); Month m = new Month(1, 1970); assertEquals(1970, m.getYear().getYear()); assertEquals(1, m.getMonth()); assertEquals(-3_600_000L * hoursOffset, m.getFirstMillisecond()); } finally { // reset everything, to avoid affecting other tests RegularTimePeriod.setThreadLocalCalendarInstance(null); RegularTimePeriod.setCalendarInstancePrototype(null); } } /** * If a thread-local calendar was set, the (int, Year) month-year constructor should use it. */ @Test public void testMonthYearConstructorWithThreadLocalCalendar() { Consumer calendarSetup = hours -> RegularTimePeriod.setThreadLocalCalendarInstance( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testMonthYearConstructorWithCustomCalendar(3, calendarSetup); testMonthYearConstructorWithCustomCalendar(4, calendarSetup); } /** * If a calendar prototype was set, the the (int, Year) month-year constructor should use it. */ @Test public void testMonthYearConstructorWithCalendarPrototype() { Consumer calendarSetup = hours -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testMonthYearConstructorWithCustomCalendar(3, calendarSetup); testMonthYearConstructorWithCustomCalendar(4, calendarSetup); } private void testMonthYearConstructorWithCustomCalendar(int hoursOffset, Consumer calendarSetup) { try { calendarSetup.accept(hoursOffset); Month m = new Month(1, new Year(1970)); assertEquals(1970, m.getYear().getYear()); assertEquals(1, m.getMonth()); assertEquals(-3_600_000L * hoursOffset, m.getFirstMillisecond()); } finally { // reset everything, to avoid affecting other tests RegularTimePeriod.setThreadLocalCalendarInstance(null); RegularTimePeriod.setCalendarInstancePrototype(null); } } /** * Set up a month equal to Jan 1900. Request the previous month, it should * be null. */ @Test public void testJan1900Previous() { Month previous = (Month) this.jan1900.previous(); assertNull(previous); } /** * Set up a month equal to Jan 1900. Request the next month, it should be * Feb 1900. */ @Test public void testJan1900Next() { Month next = (Month) this.jan1900.next(); assertEquals(this.feb1900, next); } /** * Set up a month equal to Dec 9999. Request the previous month, it should * be Nov 9999. */ @Test public void testDec9999Previous() { Month previous = (Month) this.dec9999.previous(); assertEquals(this.nov9999, previous); } /** * Set up a month equal to Dec 9999. Request the next month, it should be * null. */ @Test public void testDec9999Next() { Month next = (Month) this.dec9999.next(); assertNull(next); } /** * Tests the string parsing code... */ @Test public void testParseMonth() { Month month = null; // test 1... try { month = Month.parseMonth("1990-01"); } catch (TimePeriodFormatException e) { month = new Month(1, 1900); } assertEquals(1, month.getMonth()); assertEquals(1990, month.getYear().getYear()); // test 2... try { month = Month.parseMonth("02-1991"); } catch (TimePeriodFormatException e) { month = new Month(1, 1900); } assertEquals(2, month.getMonth()); assertEquals(1991, month.getYear().getYear()); // test 3... try { String monthName = SerialDate.DATE_FORMAT_SYMBOLS.getMonths()[2]; month = Month.parseMonth(monthName + " 1993"); } catch (TimePeriodFormatException e) { month = new Month(1, 1900); } assertEquals(3, month.getMonth()); assertEquals(1993, month.getYear().getYear()); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { Month m1 = new Month(12, 1999); Month m2 = TestUtils.serialised(m1); assertEquals(m1, m2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { Month m1 = new Month(2, 2003); Month m2 = new Month(2, 2003); assertEquals(m1, m2); int h1 = m1.hashCode(); int h2 = m2.hashCode(); assertEquals(h1, h2); } /** * The {@link Month} class is immutable, so should not be {@link Cloneable}. */ @Test public void testNotCloneable() { Month m = new Month(2, 2003); assertFalse(m instanceof Cloneable); } /** * Some checks for the getFirstMillisecond() method. */ @Test public void testGetFirstMillisecond() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.UK); TimeZone savedZone = TimeZone.getDefault(); TimeZone.setDefault(TimeZone.getTimeZone("Europe/London")); Month m = new Month(3, 1970); assertEquals(5094000000L, m.getFirstMillisecond()); Locale.setDefault(saved); TimeZone.setDefault(savedZone); } /** * Some checks for the getFirstMillisecond(TimeZone) method. */ @Test public void testGetFirstMillisecondWithTimeZone() { Month m = new Month(2, 1950); TimeZone zone = TimeZone.getTimeZone("America/Los_Angeles"); Calendar cal = Calendar.getInstance(zone); assertEquals(-628444800000L, m.getFirstMillisecond(cal)); // try null calendar boolean pass = false; try { m.getFirstMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getFirstMillisecond(TimeZone) method. */ @Test public void testGetFirstMillisecondWithCalendar() { Month m = new Month(1, 2001); GregorianCalendar calendar = new GregorianCalendar(Locale.GERMANY); calendar.setTimeZone(TimeZone.getTimeZone("Europe/Frankfurt")); assertEquals(978307200000L, m.getFirstMillisecond(calendar)); // try null calendar boolean pass = false; try { m.getFirstMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getLastMillisecond() method. */ @Test public void testGetLastMillisecond() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.UK); TimeZone savedZone = TimeZone.getDefault(); TimeZone.setDefault(TimeZone.getTimeZone("Europe/London")); Month m = new Month(3, 1970); assertEquals(7772399999L, m.getLastMillisecond()); Locale.setDefault(saved); TimeZone.setDefault(savedZone); } /** * Some checks for the getLastMillisecond(TimeZone) method. */ @Test public void testGetLastMillisecondWithTimeZone() { Month m = new Month(2, 1950); TimeZone zone = TimeZone.getTimeZone("America/Los_Angeles"); Calendar cal = Calendar.getInstance(zone); assertEquals(-626025600001L, m.getLastMillisecond(cal)); // try null calendar boolean pass = false; try { m.getLastMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getLastMillisecond(TimeZone) method. */ @Test public void testGetLastMillisecondWithCalendar() { Month m = new Month(3, 2001); GregorianCalendar calendar = new GregorianCalendar(Locale.GERMANY); calendar.setTimeZone(TimeZone.getTimeZone("Europe/Frankfurt")); assertEquals(986083199999L, m.getLastMillisecond(calendar)); // try null calendar boolean pass = false; try { m.getLastMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getSerialIndex() method. */ @Test public void testGetSerialIndex() { Month m = new Month(1, 2000); assertEquals(24001L, m.getSerialIndex()); m = new Month(1, 1900); assertEquals(22801L, m.getSerialIndex()); } /** * Some checks for the testNext() method. */ @Test public void testNext() { Month m = new Month(12, 2000); m = (Month) m.next(); assertEquals(new Year(2001), m.getYear()); assertEquals(1, m.getMonth()); m = new Month(12, 9999); assertNull(m.next()); } /** * If a thread-local calendar was set, next() should use its time zone. */ @Test public void testNextWithThreadLocalCalendar() { Consumer calendarSetup = hours -> RegularTimePeriod.setThreadLocalCalendarInstance( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testNextWithCustomCalendar(3, calendarSetup); testNextWithCustomCalendar(4, calendarSetup); } /** * If a calendar prototype was set, next() should use its time zone. */ @Test public void testNextWithCalendarPrototype() { Consumer calendarSetup = hours -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testNextWithCustomCalendar(3, calendarSetup); testNextWithCustomCalendar(4, calendarSetup); } private void testNextWithCustomCalendar(int hoursOffset, Consumer calendarSetup) { try { calendarSetup.accept(hoursOffset); long ms = -hoursOffset * 3_600_000L; Month m = new Month(new Date(ms)); m = (Month) m.next(); assertEquals(1970, m.getYear().getYear()); assertEquals(2, m.getMonth()); assertEquals(ms + 86_400_000L * 31, m.getFirstMillisecond()); } finally { // reset everything, to avoid affecting other tests RegularTimePeriod.setThreadLocalCalendarInstance(null); RegularTimePeriod.setCalendarInstancePrototype(null); } } /** * Some checks for the getStart() method. */ @Test public void testGetStart() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.ITALY); Calendar cal = Calendar.getInstance(Locale.ITALY); cal.set(2006, Calendar.MARCH, 1, 0, 0, 0); cal.set(Calendar.MILLISECOND, 0); Month m = new Month(3, 2006); assertEquals(cal.getTime(), m.getStart()); Locale.setDefault(saved); } /** * Some checks for the getEnd() method. */ @Test public void testGetEnd() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.ITALY); Calendar cal = Calendar.getInstance(Locale.ITALY); cal.set(2006, Calendar.JANUARY, 31, 23, 59, 59); cal.set(Calendar.MILLISECOND, 999); Month m = new Month(1, 2006); assertEquals(cal.getTime(), m.getEnd()); Locale.setDefault(saved); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/time/MovingAverageTest.java000066400000000000000000000072341463604235500301220ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------- * MovingAverageTest.java * ---------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import org.jfree.chart.date.MonthConstants; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; /** * Tests for the {@link MovingAverage} class. */ public class MovingAverageTest { private static final double EPSILON = 0.0000000001; /** * A test for the values calculated from a time series. */ @Test public void test1() { TimeSeries source = createDailyTimeSeries1(); TimeSeries maverage = MovingAverage.createMovingAverage(source, "Moving Average", 3, 3); // the moving average series has 7 items, the first three // days (11, 12, 13 August are skipped) assertEquals(7, maverage.getItemCount()); double value = maverage.getValue(0).doubleValue(); assertEquals(14.1, value, EPSILON); value = maverage.getValue(1).doubleValue(); assertEquals(13.4, value, EPSILON); value = maverage.getValue(2).doubleValue(); assertEquals(14.433333333333, value, EPSILON); value = maverage.getValue(3).doubleValue(); assertEquals(14.933333333333, value, EPSILON); value = maverage.getValue(4).doubleValue(); assertEquals(19.8, value, EPSILON); value = maverage.getValue(5).doubleValue(); assertEquals(15.25, value, EPSILON); value = maverage.getValue(6).doubleValue(); assertEquals(12.5, value, EPSILON); } /** * Creates a sample series. * * @return A sample series. */ private TimeSeries createDailyTimeSeries1() { TimeSeries series = new TimeSeries("Series 1"); series.add(new Day(11, MonthConstants.AUGUST, 2003), 11.2); series.add(new Day(13, MonthConstants.AUGUST, 2003), 13.8); series.add(new Day(17, MonthConstants.AUGUST, 2003), 14.1); series.add(new Day(18, MonthConstants.AUGUST, 2003), 12.7); series.add(new Day(19, MonthConstants.AUGUST, 2003), 16.5); series.add(new Day(20, MonthConstants.AUGUST, 2003), 15.6); series.add(new Day(25, MonthConstants.AUGUST, 2003), 19.8); series.add(new Day(27, MonthConstants.AUGUST, 2003), 10.7); series.add(new Day(28, MonthConstants.AUGUST, 2003), 14.3); return series; } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/time/QuarterTest.java000066400000000000000000000466271463604235500270240ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * QuarterTest.java * ---------------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import java.time.ZoneOffset; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.Locale; import java.util.TimeZone; import java.util.function.Consumer; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; /** * Tests for the {link Quarter} class. */ public class QuarterTest { /** A quarter. */ private Quarter q1Y1900; /** A quarter. */ private Quarter q2Y1900; /** A quarter. */ private Quarter q3Y9999; /** A quarter. */ private Quarter q4Y9999; /** * Common test setup. */ @BeforeEach public void setUp() { this.q1Y1900 = new Quarter(1, 1900); this.q2Y1900 = new Quarter(2, 1900); this.q3Y9999 = new Quarter(3, 9999); this.q4Y9999 = new Quarter(4, 9999); } /** * Check that a Quarter instance is equal to itself. * * SourceForge Bug ID: 558850. */ @Test public void testEqualsSelf() { Quarter quarter = new Quarter(); assertEquals(quarter, quarter); } /** * Tests the equals method. */ @Test public void testEquals() { Quarter q1 = new Quarter(2, 2002); Quarter q2 = new Quarter(2, 2002); assertEquals(q1, q2); } /** * In GMT, the end of Q1 2002 is java.util.Date(1017619199999L). Use this * to check the quarter constructor. */ @Test public void testDateConstructor1() { TimeZone zone = TimeZone.getTimeZone("GMT"); Calendar cal = Calendar.getInstance(zone); Quarter q1 = new Quarter(new Date(1017619199999L), zone, Locale.getDefault()); Quarter q2 = new Quarter(new Date(1017619200000L), zone, Locale.getDefault()); assertEquals(1, q1.getQuarter()); assertEquals(1017619199999L, q1.getLastMillisecond(cal)); assertEquals(2, q2.getQuarter()); assertEquals(1017619200000L, q2.getFirstMillisecond(cal)); } /** * In Istanbul, the end of Q1 2002 is java.util.Date(1017608399999L). Use * this to check the quarter constructor. */ @Test public void testDateConstructor2() { TimeZone zone = TimeZone.getTimeZone("Europe/Istanbul"); Calendar cal = Calendar.getInstance(zone); Quarter q1 = new Quarter(new Date(1017608399999L), zone, Locale.getDefault()); Quarter q2 = new Quarter(new Date(1017608400000L), zone, Locale.getDefault()); assertEquals(1, q1.getQuarter()); assertEquals(1017608399999L, q1.getLastMillisecond(cal)); assertEquals(2, q2.getQuarter()); assertEquals(1017608400000L, q2.getFirstMillisecond(cal)); } /** * If a thread-local calendar was set, the Date constructor should use it. */ @Test public void testDateConstructorWithThreadLocalCalendar() { Consumer calendarSetup = hours -> RegularTimePeriod.setThreadLocalCalendarInstance( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testDateConstructorWithCustomCalendar(3, calendarSetup); testDateConstructorWithCustomCalendar(4, calendarSetup); } /** * If a calendar prototype was set, the Date constructor should use it. */ @Test public void testDateConstructorWithCalendarPrototype() { Consumer calendarSetup = hours -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testDateConstructorWithCustomCalendar(3, calendarSetup); testDateConstructorWithCustomCalendar(4, calendarSetup); } private void testDateConstructorWithCustomCalendar(int hoursOffset, Consumer calendarSetup) { try { calendarSetup.accept(hoursOffset); long ms = -3_600_000L * hoursOffset; Quarter q = new Quarter(new Date(ms)); assertEquals(1970, q.getYear().getYear()); assertEquals(1, q.getQuarter()); assertEquals(ms, q.getFirstMillisecond()); } finally { // reset everything, to avoid affecting other tests RegularTimePeriod.setThreadLocalCalendarInstance(null); RegularTimePeriod.setCalendarInstancePrototype(null); } } /** * If a thread-local calendar was set, the (int, int) quarter-year constructor should use it. */ @Test public void testQuarterIntYearConstructorWithThreadLocalCalendar() { Consumer calendarSetup = hours -> RegularTimePeriod.setThreadLocalCalendarInstance( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testQuarterIntYearConstructorWithCustomCalendar(3, calendarSetup); testQuarterIntYearConstructorWithCustomCalendar(4, calendarSetup); } /** * If a calendar prototype was set, the the (int, int) quarter-year constructor should use it. */ @Test public void testQuarterIntYearConstructorWithCalendarPrototype() { Consumer calendarSetup = hours -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testQuarterIntYearConstructorWithCustomCalendar(3, calendarSetup); testQuarterIntYearConstructorWithCustomCalendar(4, calendarSetup); } private void testQuarterIntYearConstructorWithCustomCalendar(int hoursOffset, Consumer calendarSetup) { try { calendarSetup.accept(hoursOffset); Quarter q = new Quarter(1, 1970); assertEquals(1970, q.getYear().getYear()); assertEquals(1, q.getQuarter()); assertEquals(-3_600_000L * hoursOffset, q.getFirstMillisecond()); } finally { // reset everything, to avoid affecting other tests RegularTimePeriod.setThreadLocalCalendarInstance(null); RegularTimePeriod.setCalendarInstancePrototype(null); } } /** * If a thread-local calendar was set, the (int, Year) quarter-year constructor should use it. */ @Test public void testQuarterYearConstructorWithThreadLocalCalendar() { Consumer calendarSetup = hours -> RegularTimePeriod.setThreadLocalCalendarInstance( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testQuarterYearConstructorWithCustomCalendar(3, calendarSetup); testQuarterYearConstructorWithCustomCalendar(4, calendarSetup); } /** * If a calendar prototype was set, the the (int, Year) quarter-year constructor should use it. */ @Test public void testQuarterYearConstructorWithCalendarPrototype() { Consumer calendarSetup = hours -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testQuarterYearConstructorWithCustomCalendar(3, calendarSetup); testQuarterYearConstructorWithCustomCalendar(4, calendarSetup); } private void testQuarterYearConstructorWithCustomCalendar(int hoursOffset, Consumer calendarSetup) { try { calendarSetup.accept(hoursOffset); Quarter q = new Quarter(1, new Year(1970)); assertEquals(1970, q.getYear().getYear()); assertEquals(1, q.getQuarter()); assertEquals(-3_600_000L * hoursOffset, q.getFirstMillisecond()); } finally { // reset everything, to avoid affecting other tests RegularTimePeriod.setThreadLocalCalendarInstance(null); RegularTimePeriod.setCalendarInstancePrototype(null); } } /** * Set up a quarter equal to Q1 1900. Request the previous quarter, it * should be null. */ @Test public void testQ1Y1900Previous() { Quarter previous = (Quarter) this.q1Y1900.previous(); assertNull(previous); } /** * Set up a quarter equal to Q1 1900. Request the next quarter, it should * be Q2 1900. */ @Test public void testQ1Y1900Next() { Quarter next = (Quarter) this.q1Y1900.next(); assertEquals(this.q2Y1900, next); } /** * Set up a quarter equal to Q4 9999. Request the previous quarter, it * should be Q3 9999. */ @Test public void testQ4Y9999Previous() { Quarter previous = (Quarter) this.q4Y9999.previous(); assertEquals(this.q3Y9999, previous); } /** * Set up a quarter equal to Q4 9999. Request the next quarter, it should * be null. */ @Test public void testQ4Y9999Next() { Quarter next = (Quarter) this.q4Y9999.next(); assertNull(next); } /** * Test the string parsing code... */ @Test public void testParseQuarter() { Quarter quarter = null; // test 1... try { quarter = Quarter.parseQuarter("Q1-2000"); } catch (TimePeriodFormatException e) { quarter = new Quarter(1, 1900); } assertEquals(1, quarter.getQuarter()); assertEquals(2000, quarter.getYear().getYear()); // test 2... try { quarter = Quarter.parseQuarter("2001-Q2"); } catch (TimePeriodFormatException e) { quarter = new Quarter(1, 1900); } assertEquals(2, quarter.getQuarter()); assertEquals(2001, quarter.getYear().getYear()); // test 3... try { quarter = Quarter.parseQuarter("Q3, 2002"); } catch (TimePeriodFormatException e) { quarter = new Quarter(1, 1900); } assertEquals(3, quarter.getQuarter()); assertEquals(2002, quarter.getYear().getYear()); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { Quarter q1 = new Quarter(4, 1999); Quarter q2 = TestUtils.serialised(q1); assertEquals(q1, q2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { Quarter q1 = new Quarter(2, 2003); Quarter q2 = new Quarter(2, 2003); assertEquals(q1, q2); int h1 = q1.hashCode(); int h2 = q2.hashCode(); assertEquals(h1, h2); } /** * The {@link Quarter} class is immutable, so should not be * {@link Cloneable}. */ @Test public void testNotCloneable() { Quarter q = new Quarter(2, 2003); assertFalse(q instanceof Cloneable); } /** * Some tests for the constructor with (int, int) arguments. Covers bug * report 1377239. */ @Test public void testConstructor() { boolean pass = false; try { /*Quarter q =*/ new Quarter(0, 2005); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); pass = false; try { /*Quarter q =*/ new Quarter(5, 2005); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getFirstMillisecond() method. */ @Test public void testGetFirstMillisecond() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.UK); TimeZone savedZone = TimeZone.getDefault(); TimeZone.setDefault(TimeZone.getTimeZone("Europe/London")); Quarter q = new Quarter(3, 1970); assertEquals(15634800000L, q.getFirstMillisecond()); Locale.setDefault(saved); TimeZone.setDefault(savedZone); } /** * Some checks for the getFirstMillisecond(TimeZone) method. */ @Test public void testGetFirstMillisecondWithTimeZone() { Quarter q = new Quarter(2, 1950); TimeZone zone = TimeZone.getTimeZone("America/Los_Angeles"); Calendar cal = Calendar.getInstance(zone); assertEquals(-623347200000L, q.getFirstMillisecond(cal)); // try null calendar boolean pass = false; try { q.getFirstMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getFirstMillisecond(TimeZone) method. */ @Test public void testGetFirstMillisecondWithCalendar() { Quarter q = new Quarter(1, 2001); GregorianCalendar calendar = new GregorianCalendar(Locale.GERMANY); calendar.setTimeZone(TimeZone.getTimeZone("Europe/Frankfurt")); assertEquals(978307200000L, q.getFirstMillisecond(calendar)); // try null calendar boolean pass = false; try { q.getFirstMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getLastMillisecond() method. */ @Test public void testGetLastMillisecond() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.UK); TimeZone savedZone = TimeZone.getDefault(); TimeZone.setDefault(TimeZone.getTimeZone("Europe/London")); Quarter q = new Quarter(3, 1970); assertEquals(23583599999L, q.getLastMillisecond()); Locale.setDefault(saved); TimeZone.setDefault(savedZone); } /** * Some checks for the getLastMillisecond(TimeZone) method. */ @Test public void testGetLastMillisecondWithTimeZone() { Quarter q = new Quarter(2, 1950); TimeZone zone = TimeZone.getTimeZone("America/Los_Angeles"); Calendar cal = Calendar.getInstance(zone); assertEquals(-615488400001L, q.getLastMillisecond(cal)); // try null calendar boolean pass = false; try { q.getLastMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getLastMillisecond(TimeZone) method. */ @Test public void testGetLastMillisecondWithCalendar() { Quarter q = new Quarter(3, 2001); GregorianCalendar calendar = new GregorianCalendar(Locale.GERMANY); calendar.setTimeZone(TimeZone.getTimeZone("Europe/Frankfurt")); assertEquals(1001894399999L, q.getLastMillisecond(calendar)); // try null calendar boolean pass = false; try { q.getLastMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getSerialIndex() method. */ @Test public void testGetSerialIndex() { Quarter q = new Quarter(1, 2000); assertEquals(8001L, q.getSerialIndex()); q = new Quarter(1, 1900); assertEquals(7601L, q.getSerialIndex()); } /** * Some checks for the testNext() method. */ @Test public void testNext() { Quarter q = new Quarter(1, 2000); q = (Quarter) q.next(); assertEquals(new Year(2000), q.getYear()); assertEquals(2, q.getQuarter()); q = new Quarter(4, 9999); assertNull(q.next()); } /** * If a thread-local calendar was set, next() should use its time zone. */ @Test public void testNextWithThreadLocalCalendar() { Consumer calendarSetup = hours -> RegularTimePeriod.setThreadLocalCalendarInstance( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testNextWithCustomCalendar(3, calendarSetup); testNextWithCustomCalendar(4, calendarSetup); } /** * If a calendar prototype was set, next() should use its time zone. */ @Test public void testNextWithCalendarPrototype() { Consumer calendarSetup = hours -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testNextWithCustomCalendar(3, calendarSetup); testNextWithCustomCalendar(4, calendarSetup); } private void testNextWithCustomCalendar(int hoursOffset, Consumer calendarSetup) { try { calendarSetup.accept(hoursOffset); long ms = -hoursOffset * 3_600_000L; Quarter q = new Quarter(new Date(ms)); q = (Quarter) q.next(); assertEquals(1970, q.getYear().getYear()); assertEquals(2, q.getQuarter()); assertEquals(ms + 86_400_000L * (31 + 28 + 31), q.getFirstMillisecond()); } finally { // reset everything, to avoid affecting other tests RegularTimePeriod.setThreadLocalCalendarInstance(null); RegularTimePeriod.setCalendarInstancePrototype(null); } } /** * Some checks for the getStart() method. */ @Test public void testGetStart() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.ITALY); Calendar cal = Calendar.getInstance(Locale.ITALY); cal.set(2006, Calendar.JULY, 1, 0, 0, 0); cal.set(Calendar.MILLISECOND, 0); Quarter q = new Quarter(3, 2006); assertEquals(cal.getTime(), q.getStart()); Locale.setDefault(saved); } /** * Some checks for the getEnd() method. */ @Test public void testGetEnd() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.ITALY); Calendar cal = Calendar.getInstance(Locale.ITALY); cal.set(2006, Calendar.MARCH, 31, 23, 59, 59); cal.set(Calendar.MILLISECOND, 999); Quarter q = new Quarter(1, 2006); assertEquals(cal.getTime(), q.getEnd()); Locale.setDefault(saved); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/time/SecondTest.java000066400000000000000000000401361463604235500266010ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * SecondTest.java * ---------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import java.time.ZoneOffset; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.Locale; import java.util.TimeZone; import java.util.function.Consumer; import org.jfree.chart.TestUtils; import org.jfree.chart.date.MonthConstants; import org.junit.jupiter.api.Test; /** * Tests for the {@link Second} class. */ public class SecondTest { /** * Test that a Second instance is equal to itself. * * SourceForge Bug ID: 558850. */ @Test public void testEqualsSelf() { Second second = new Second(); assertEquals(second, second); } /** * Tests the equals method. */ @Test public void testEquals() { Day day1 = new Day(29, MonthConstants.MARCH, 2002); Hour hour1 = new Hour(15, day1); Minute minute1 = new Minute(15, hour1); Second second1 = new Second(34, minute1); Day day2 = new Day(29, MonthConstants.MARCH, 2002); Hour hour2 = new Hour(15, day2); Minute minute2 = new Minute(15, hour2); Second second2 = new Second(34, minute2); assertEquals(second1, second2); } /** * In GMT, the 4.55:59pm on 21 Mar 2002 is java.util.Date(1016729759000L). * Use this to check the Second constructor. */ @Test public void testDateConstructor1() { TimeZone zone = TimeZone.getTimeZone("GMT"); Calendar cal = Calendar.getInstance(zone); Locale locale = Locale.getDefault(); // locale shouldn't matter here Second s1 = new Second(new Date(1016729758999L), zone, locale); Second s2 = new Second(new Date(1016729759000L), zone, locale); assertEquals(58, s1.getSecond()); assertEquals(1016729758999L, s1.getLastMillisecond(cal)); assertEquals(59, s2.getSecond()); assertEquals(1016729759000L, s2.getFirstMillisecond(cal)); } /** * In Chicago, the 4.55:59pm on 21 Mar 2002 is * java.util.Date(1016751359000L). Use this to check the Second constructor. */ @Test public void testDateConstructor2() { TimeZone zone = TimeZone.getTimeZone("America/Chicago"); Calendar cal = Calendar.getInstance(zone); Locale locale = Locale.getDefault(); // locale shouldn't matter here Second s1 = new Second(new Date(1016751358999L), zone, locale); Second s2 = new Second(new Date(1016751359000L), zone, locale); assertEquals(58, s1.getSecond()); assertEquals(1016751358999L, s1.getLastMillisecond(cal)); assertEquals(59, s2.getSecond()); assertEquals(1016751359000L, s2.getFirstMillisecond(cal)); } /** * If a thread-local calendar was set, the Date constructor should use it. */ @Test public void testDateConstructorWithThreadLocalCalendar() { Consumer calendarSetup = hours -> RegularTimePeriod.setThreadLocalCalendarInstance( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testDateConstructorWithCustomCalendar(3, calendarSetup); testDateConstructorWithCustomCalendar(4, calendarSetup); } /** * If a calendar prototype was set, the Date constructor should use it. */ @Test public void testDateConstructorWithCalendarPrototype() { Consumer calendarSetup = hours -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testDateConstructorWithCustomCalendar(3, calendarSetup); testDateConstructorWithCustomCalendar(4, calendarSetup); } private void testDateConstructorWithCustomCalendar(int hoursOffset, Consumer calendarSetup) { try { calendarSetup.accept(hoursOffset); Second s = new Second(new Date(0L)); assertEquals(1970, s.getMinute().getHour().getYear()); assertEquals(1, s.getMinute().getHour().getMonth()); assertEquals(1, s.getMinute().getHour().getDayOfMonth()); assertEquals(hoursOffset, s.getMinute().getHour().getHour()); assertEquals(0, s.getMinute().getMinute()); assertEquals(0, s.getSecond()); assertEquals(0L, s.getFirstMillisecond()); } finally { // reset everything, to avoid affecting other tests RegularTimePeriod.setThreadLocalCalendarInstance(null); RegularTimePeriod.setCalendarInstancePrototype(null); } } /** * If a thread-local calendar was set, the second-minute constructor should use it. */ @Test public void testSecondMinuteConstructorWithThreadLocalCalendar() { Consumer calendarSetup = hours -> RegularTimePeriod.setThreadLocalCalendarInstance( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testSecondMinuteConstructorWithCustomCalendar(3, calendarSetup); testSecondMinuteConstructorWithCustomCalendar(4, calendarSetup); } /** * If a calendar prototype was set, the second-minute constructor should use it. */ @Test public void testSecondMinuteConstructorWithCalendarPrototype() { Consumer calendarSetup = hours -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testSecondMinuteConstructorWithCustomCalendar(3, calendarSetup); testSecondMinuteConstructorWithCustomCalendar(4, calendarSetup); } private void testSecondMinuteConstructorWithCustomCalendar(int hoursOffset, Consumer calendarSetup) { try { calendarSetup.accept(hoursOffset); Second s = new Second(0, new Minute(new Date(0L))); assertEquals(1970, s.getMinute().getHour().getYear()); assertEquals(1, s.getMinute().getHour().getMonth()); assertEquals(1, s.getMinute().getHour().getDayOfMonth()); assertEquals(hoursOffset, s.getMinute().getHour().getHour()); assertEquals(0, s.getMinute().getMinute()); assertEquals(0, s.getSecond()); assertEquals(0L, s.getFirstMillisecond()); } finally { // reset everything, to avoid affecting other tests RegularTimePeriod.setThreadLocalCalendarInstance(null); RegularTimePeriod.setCalendarInstancePrototype(null); } } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { Second s1 = new Second(); Second s2 = TestUtils.serialised(s1); assertEquals(s1, s2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { Second s1 = new Second(13, 45, 5, 1, 2, 2003); Second s2 = new Second(13, 45, 5, 1, 2, 2003); assertEquals(s1, s2); int h1 = s1.hashCode(); int h2 = s2.hashCode(); assertEquals(h1, h2); } /** * The {@link Second} class is immutable, so should not be * {@link Cloneable}. */ @Test public void testNotCloneable() { Second s = new Second(13, 45, 5, 1, 2, 2003); assertFalse(s instanceof Cloneable); } /** * Some checks for the getFirstMillisecond() method. */ @Test public void testGetFirstMillisecond() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.UK); TimeZone savedZone = TimeZone.getDefault(); TimeZone.setDefault(TimeZone.getTimeZone("Europe/London")); Second s = new Second(15, 43, 15, 1, 4, 2006); assertEquals(1143902595000L, s.getFirstMillisecond()); Locale.setDefault(saved); TimeZone.setDefault(savedZone); } /** * Some checks for the getFirstMillisecond(TimeZone) method. */ @Test public void testGetFirstMillisecondWithTimeZone() { Second s = new Second(50, 59, 15, 1, 4, 1950); TimeZone zone = TimeZone.getTimeZone("America/Los_Angeles"); Calendar cal = Calendar.getInstance(zone); assertEquals(-623289610000L, s.getFirstMillisecond(cal)); // try null calendar boolean pass = false; try { s.getFirstMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getFirstMillisecond(TimeZone) method. */ @Test public void testGetFirstMillisecondWithCalendar() { Second s = new Second(55, 40, 2, 15, 4, 2000); GregorianCalendar calendar = new GregorianCalendar(Locale.GERMANY); calendar.setTimeZone(TimeZone.getTimeZone("Europe/Frankfurt")); assertEquals(955766455000L, s.getFirstMillisecond(calendar)); // try null calendar boolean pass = false; try { s.getFirstMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getLastMillisecond() method. */ @Test public void testGetLastMillisecond() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.UK); TimeZone savedZone = TimeZone.getDefault(); TimeZone.setDefault(TimeZone.getTimeZone("Europe/London")); Second s = new Second(1, 1, 1, 1, 1, 1970); assertEquals(61999L, s.getLastMillisecond()); Locale.setDefault(saved); TimeZone.setDefault(savedZone); } /** * Some checks for the getLastMillisecond(TimeZone) method. */ @Test public void testGetLastMillisecondWithTimeZone() { Second s = new Second(55, 1, 2, 7, 7, 1950); TimeZone zone = TimeZone.getTimeZone("America/Los_Angeles"); Calendar cal = Calendar.getInstance(zone); assertEquals(-614962684001L, s.getLastMillisecond(cal)); // try null calendar boolean pass = false; try { s.getLastMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getLastMillisecond(TimeZone) method. */ @Test public void testGetLastMillisecondWithCalendar() { Second s = new Second(50, 45, 21, 21, 4, 2001); GregorianCalendar calendar = new GregorianCalendar(Locale.GERMANY); calendar.setTimeZone(TimeZone.getTimeZone("Europe/Frankfurt")); assertEquals(987889550999L, s.getLastMillisecond(calendar)); // try null calendar boolean pass = false; try { s.getLastMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getSerialIndex() method. */ @Test public void testGetSerialIndex() { Second s = new Second(1, 1, 1, 1, 1, 2000); assertEquals(3155850061L, s.getSerialIndex()); s = new Second(1, 1, 1, 1, 1, 1900); assertEquals(176461L, s.getSerialIndex()); } /** * Some checks for the testNext() method. */ @Test public void testNext() { Second s = new Second(55, 30, 1, 12, 12, 2000); s = (Second) s.next(); assertEquals(2000, s.getMinute().getHour().getYear()); assertEquals(12, s.getMinute().getHour().getMonth()); assertEquals(12, s.getMinute().getHour().getDayOfMonth()); assertEquals(1, s.getMinute().getHour().getHour()); assertEquals(30, s.getMinute().getMinute()); assertEquals(56, s.getSecond()); s = new Second(59, 59, 23, 31, 12, 9999); assertNull(s.next()); } /** * If a thread-local calendar was set, next() should use its time zone. */ @Test public void testNextWithThreadLocalCalendar() { Consumer calendarSetup = hours -> RegularTimePeriod.setThreadLocalCalendarInstance( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testNextWithCustomCalendar(3, calendarSetup); testNextWithCustomCalendar(4, calendarSetup); } /** * If a calendar prototype was set, next() should use its time zone. */ @Test public void testNextWithCalendarPrototype() { Consumer calendarSetup = hours -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testNextWithCustomCalendar(3, calendarSetup); testNextWithCustomCalendar(4, calendarSetup); } private void testNextWithCustomCalendar(int hoursOffset, Consumer calendarSetup) { try { calendarSetup.accept(hoursOffset); Second s = new Second(new Date(0L)); s = (Second) s.next(); assertEquals(1970, s.getMinute().getHour().getYear()); assertEquals(1, s.getMinute().getHour().getMonth()); assertEquals(1, s.getMinute().getHour().getDayOfMonth()); assertEquals(hoursOffset, s.getMinute().getHour().getHour()); assertEquals(0, s.getMinute().getMinute()); assertEquals(1, s.getSecond()); assertEquals(1000L, s.getFirstMillisecond()); } finally { // reset everything, to avoid affecting other tests RegularTimePeriod.setThreadLocalCalendarInstance(null); RegularTimePeriod.setCalendarInstancePrototype(null); } } /** * Some checks for the getStart() method. */ @Test public void testGetStart() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.ITALY); Calendar cal = Calendar.getInstance(Locale.ITALY); cal.set(2006, Calendar.JANUARY, 16, 3, 47, 55); cal.set(Calendar.MILLISECOND, 0); Second s = new Second(55, 47, 3, 16, 1, 2006); assertEquals(cal.getTime(), s.getStart()); Locale.setDefault(saved); } /** * Some checks for the getEnd() method. */ @Test public void testGetEnd() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.ITALY); Calendar cal = Calendar.getInstance(Locale.ITALY); cal.set(2006, Calendar.JANUARY, 16, 3, 47, 55); cal.set(Calendar.MILLISECOND, 999); Second s = new Second(55, 47, 3, 16, 1, 2006); assertEquals(cal.getTime(), s.getEnd()); Locale.setDefault(saved); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/time/SimpleTimePeriodTest.java000066400000000000000000000140421463604235500305760ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * SimpleTimePeriodTest.java * ------------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import java.util.Date; import nl.jqno.equalsverifier.EqualsVerifier; import nl.jqno.equalsverifier.Warning; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link SimpleTimePeriod} class. */ public class SimpleTimePeriodTest { @Test public void testEqualsHashCode() { EqualsVerifier.forClass(SimpleTimePeriod.class) .suppress(Warning.STRICT_INHERITANCE) .suppress(Warning.NONFINAL_FIELDS) .suppress(Warning.TRANSIENT_FIELDS) .verify(); } /** * Check that an instance is equal to itself. * * SourceForge Bug ID: 558850. */ @Test public void testEqualsSelf() { SimpleTimePeriod p = new SimpleTimePeriod(new Date(1000L), new Date(1001L)); assertEquals(p, p); } /** * Test the equals() method. */ @Test public void testEquals() { SimpleTimePeriod p1 = new SimpleTimePeriod(new Date(1000L), new Date(1004L)); SimpleTimePeriod p2 = new SimpleTimePeriod(new Date(1000L), new Date(1004L)); assertEquals(p1, p2); assertEquals(p2, p1); p1 = new SimpleTimePeriod(new Date(1002L), new Date(1004L)); assertNotEquals(p1, p2); p2 = new SimpleTimePeriod(new Date(1002L), new Date(1004L)); assertEquals(p1, p2); p1 = new SimpleTimePeriod(new Date(1002L), new Date(1003L)); assertNotEquals(p1, p2); p2 = new SimpleTimePeriod(new Date(1002L), new Date(1003L)); assertEquals(p1, p2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { SimpleTimePeriod p1 = new SimpleTimePeriod(new Date(1000L), new Date(1001L)); SimpleTimePeriod p2 = TestUtils.serialised(p1); assertEquals(p1, p2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { SimpleTimePeriod s1 = new SimpleTimePeriod(new Date(10L), new Date(20L)); SimpleTimePeriod s2 = new SimpleTimePeriod(new Date(10L), new Date(20L)); assertEquals(s1, s2); int h1 = s1.hashCode(); int h2 = s2.hashCode(); assertEquals(h1, h2); } /** * This class is immutable, so it should not implement Cloneable. */ @Test public void testClone() { SimpleTimePeriod s1 = new SimpleTimePeriod(new Date(10L), new Date(20)); assertFalse(s1 instanceof Cloneable); } /** * Some simple checks for immutability. */ @Test public void testImmutable() { SimpleTimePeriod p1 = new SimpleTimePeriod(new Date(10L), new Date(20L)); SimpleTimePeriod p2 = new SimpleTimePeriod(new Date(10L), new Date(20L)); assertEquals(p1, p2); p1.getStart().setTime(11L); assertEquals(p1, p2); Date d1 = new Date(10L); Date d2 = new Date(20L); p1 = new SimpleTimePeriod(d1, d2); d1.setTime(11L); assertEquals(new Date(10L), p1.getStart()); } /** * Some checks for the compareTo() method. */ @Test public void testCompareTo() { SimpleTimePeriod s1 = new SimpleTimePeriod(new Date(10L), new Date(20L)); SimpleTimePeriod s2 = new SimpleTimePeriod(new Date(10L), new Date(20L)); assertEquals(0, s1.compareTo(s2)); s1 = new SimpleTimePeriod(new Date(9L), new Date(21L)); s2 = new SimpleTimePeriod(new Date(10L), new Date(20L)); assertEquals(-1, s1.compareTo(s2)); s1 = new SimpleTimePeriod(new Date(11L), new Date(19L)); s2 = new SimpleTimePeriod(new Date(10L), new Date(20L)); assertEquals(1, s1.compareTo(s2)); s1 = new SimpleTimePeriod(new Date(9L), new Date(19L)); s2 = new SimpleTimePeriod(new Date(10L), new Date(20L)); assertEquals(-1, s1.compareTo(s2)); s1 = new SimpleTimePeriod(new Date(11L), new Date(21)); s2 = new SimpleTimePeriod(new Date(10L), new Date(20L)); assertEquals(1, s1.compareTo(s2)); s1 = new SimpleTimePeriod(new Date(10L), new Date(18)); s2 = new SimpleTimePeriod(new Date(10L), new Date(20L)); assertEquals(-1, s1.compareTo(s2)); s1 = new SimpleTimePeriod(new Date(10L), new Date(22)); s2 = new SimpleTimePeriod(new Date(10L), new Date(20L)); assertEquals(1, s1.compareTo(s2)); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/time/TimePeriodAnchorTest.java000066400000000000000000000043341463604235500305620ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * TimePeriodAnchorTest.java * ------------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link TimePeriodAnchor} class. */ public class TimePeriodAnchorTest { /** * Test the equals() method. */ @Test public void testEquals() { assertEquals(TimePeriodAnchor.START, TimePeriodAnchor.START); assertEquals(TimePeriodAnchor.MIDDLE, TimePeriodAnchor.MIDDLE); assertEquals(TimePeriodAnchor.END, TimePeriodAnchor.END); } /** * Serialize an instance, restore it, and check for identity. */ @Test public void testSerialization() { TimePeriodAnchor a1 = TimePeriodAnchor.START; TimePeriodAnchor a2 = TestUtils.serialised(a1); assertSame(a1, a2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/time/TimePeriodValueTest.java000066400000000000000000000047461463604235500304330ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * TimePeriodValueTest.java * ------------------------ * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import static org.junit.jupiter.api.Assertions.assertEquals; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; /** * Tests for the {@link TimePeriodValue} class. */ public class TimePeriodValueTest { /** * Test that an instance is equal to itself. */ @Test public void testEqualsSelf() { TimePeriodValue tpv = new TimePeriodValue(new Day(), 55.75); assertEquals(tpv, tpv); } /** * Tests the equals() method. */ @Test public void testEquals() { TimePeriodValue tpv1 = new TimePeriodValue(new Day(30, 7, 2003), 55.75); TimePeriodValue tpv2 = new TimePeriodValue(new Day(30, 7, 2003), 55.75); assertEquals(tpv1, tpv2); assertEquals(tpv2, tpv1); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { TimePeriodValue tpv1 = new TimePeriodValue(new Day(30, 7, 2003), 55.75); TimePeriodValue tpv2 = TestUtils.serialised(tpv1); assertEquals(tpv1, tpv2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/time/TimePeriodValuesCollectionTest.java000066400000000000000000000153161463604235500326250ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------------ * TimePeriodValuesCollectionTests.java * ------------------------------------ * (C) Copyright 2005-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import org.jfree.chart.TestUtils; import org.jfree.data.Range; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Some tests for the {@link TimePeriodValuesCollection} class. */ public class TimePeriodValuesCollectionTest { /** * A test for bug report 1161340. I wasn't able to reproduce the problem * with this test. */ @Test public void test1161340() { TimePeriodValuesCollection dataset = new TimePeriodValuesCollection(); TimePeriodValues v1 = new TimePeriodValues("V1"); v1.add(new Day(11, 3, 2005), 1.2); v1.add(new Day(12, 3, 2005), 3.4); dataset.addSeries(v1); assertEquals(1, dataset.getSeriesCount()); dataset.removeSeries(v1); assertEquals(0, dataset.getSeriesCount()); TimePeriodValues v2 = new TimePeriodValues("V2"); v1.add(new Day(5, 3, 2005), 1.2); v1.add(new Day(6, 3, 2005), 3.4); dataset.addSeries(v2); assertEquals(1, dataset.getSeriesCount()); } /** * Tests the equals() method. */ @Test public void testEquals() { TimePeriodValuesCollection c1 = new TimePeriodValuesCollection(); TimePeriodValuesCollection c2 = new TimePeriodValuesCollection(); assertEquals(c1, c2); c1.setXPosition(TimePeriodAnchor.END); assertNotEquals(c1, c2); c2.setXPosition(TimePeriodAnchor.END); assertEquals(c1, c2); TimePeriodValues v1 = new TimePeriodValues("Test"); TimePeriodValues v2 = new TimePeriodValues("Test"); c1.addSeries(v1); assertNotEquals(c1, c2); c2.addSeries(v2); assertEquals(c1, c2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { TimePeriodValuesCollection c1 = new TimePeriodValuesCollection(); TimePeriodValuesCollection c2 = TestUtils.serialised(c1); assertEquals(c1, c2); } /** * Some basic checks for the getSeries() method. */ @Test public void testGetSeries() { TimePeriodValuesCollection c1 = new TimePeriodValuesCollection(); TimePeriodValues s1 = new TimePeriodValues("Series 1"); c1.addSeries(s1); assertEquals("Series 1", c1.getSeries(0).getKey()); boolean pass = false; try { c1.getSeries(-1); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); pass = false; try { c1.getSeries(1); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } private static final double EPSILON = 0.0000000001; /** * Some checks for the getDomainBounds() method. */ @Test public void testGetDomainBoundsWithoutInterval() { // check empty dataset TimePeriodValuesCollection dataset = new TimePeriodValuesCollection(); Range r = dataset.getDomainBounds(false); assertNull(r); // check dataset with one time period TimePeriodValues s1 = new TimePeriodValues("S1"); s1.add(new SimpleTimePeriod(1000L, 2000L), 1.0); dataset.addSeries(s1); r = dataset.getDomainBounds(false); assertEquals(1500.0, r.getLowerBound(), EPSILON); assertEquals(1500.0, r.getUpperBound(), EPSILON); // check dataset with two time periods s1.add(new SimpleTimePeriod(1500L, 3000L), 2.0); r = dataset.getDomainBounds(false); assertEquals(1500.0, r.getLowerBound(), EPSILON); assertEquals(2250.0, r.getUpperBound(), EPSILON); } /** * Some more checks for the getDomainBounds() method. * * @see #testGetDomainBoundsWithoutInterval() */ @Test public void testGetDomainBoundsWithInterval() { // check empty dataset TimePeriodValuesCollection dataset = new TimePeriodValuesCollection(); Range r = dataset.getDomainBounds(true); assertNull(r); // check dataset with one time period TimePeriodValues s1 = new TimePeriodValues("S1"); s1.add(new SimpleTimePeriod(1000L, 2000L), 1.0); dataset.addSeries(s1); r = dataset.getDomainBounds(true); assertEquals(1000.0, r.getLowerBound(), EPSILON); assertEquals(2000.0, r.getUpperBound(), EPSILON); // check dataset with two time periods s1.add(new SimpleTimePeriod(1500L, 3000L), 2.0); r = dataset.getDomainBounds(true); assertEquals(1000.0, r.getLowerBound(), EPSILON); assertEquals(3000.0, r.getUpperBound(), EPSILON); // add a third time period s1.add(new SimpleTimePeriod(6000L, 7000L), 1.5); r = dataset.getDomainBounds(true); assertEquals(1000.0, r.getLowerBound(), EPSILON); assertEquals(7000.0, r.getUpperBound(), EPSILON); // add a fourth time period s1.add(new SimpleTimePeriod(4000L, 5000L), 1.4); r = dataset.getDomainBounds(true); assertEquals(1000.0, r.getLowerBound(), EPSILON); assertEquals(7000.0, r.getUpperBound(), EPSILON); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/time/TimePeriodValuesTest.java000066400000000000000000000266551463604235500306210ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * TimePeriodValuesTest.java * ------------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import java.util.Date; import org.jfree.chart.TestUtils; import org.jfree.chart.date.MonthConstants; import org.jfree.data.general.SeriesChangeEvent; import org.jfree.data.general.SeriesChangeListener; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * A collection of test cases for the {@link TimePeriodValues} class. */ public class TimePeriodValuesTest { /** Series A. */ private TimePeriodValues seriesA; /** Series B. */ private TimePeriodValues seriesB; /** Series C. */ private TimePeriodValues seriesC; /** * Common test setup. */ @BeforeEach public void setUp() { this.seriesA = new TimePeriodValues("Series A"); this.seriesA.add(new Year(2000), 102000); this.seriesA.add(new Year(2001), 102001); this.seriesA.add(new Year(2002), 102002); this.seriesA.add(new Year(2003), 102003); this.seriesA.add(new Year(2004), 102004); this.seriesA.add(new Year(2005), 102005); this.seriesB = new TimePeriodValues("Series B"); this.seriesB.add(new Year(2006), 202006); this.seriesB.add(new Year(2007), 202007); this.seriesB.add(new Year(2008), 202008); this.seriesC = new TimePeriodValues("Series C"); this.seriesC.add(new Year(1999), 301999); this.seriesC.add(new Year(2000), 302000); this.seriesC.add(new Year(2002), 302002); } /** * Set up a quarter equal to Q1 1900.Request the previous quarter, it * should be null. * * @throws java.lang.CloneNotSupportedException */ @Test public void testClone() throws CloneNotSupportedException { TimePeriodValues series = new TimePeriodValues("Test Series"); RegularTimePeriod jan1st2002 = new Day(1, MonthConstants.JANUARY, 2002); series.add(jan1st2002, 42); TimePeriodValues clone = (TimePeriodValues) series.clone(); clone.setKey("Clone Series"); clone.update(0, 10); int seriesValue = series.getValue(0).intValue(); int cloneValue = clone.getValue(0).intValue(); assertEquals(42, seriesValue); assertEquals(10, cloneValue); assertEquals("Test Series", series.getKey()); assertEquals("Clone Series", clone.getKey()); } /** * Add a value to series A for 1999. It should be added at index 0. */ @Test public void testAddValue() { TimePeriodValues tpvs = new TimePeriodValues("Test"); tpvs.add(new Year(1999), 1); int value = tpvs.getValue(0).intValue(); assertEquals(1, value); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { TimePeriodValues s1 = new TimePeriodValues("A test"); s1.add(new Year(2000), 13.75); s1.add(new Year(2001), 11.90); s1.add(new Year(2002), null); s1.add(new Year(2005), 19.32); s1.add(new Year(2007), 16.89); TimePeriodValues s2 = TestUtils.serialised(s1); assertEquals(s1, s2); } /** * Tests the equals method. */ @Test public void testEquals() { TimePeriodValues s1 = new TimePeriodValues("Time Series 1"); TimePeriodValues s2 = new TimePeriodValues("Time Series 2"); boolean b1 = s1.equals(s2); assertFalse(b1, "b1"); s2.setKey("Time Series 1"); boolean b2 = s1.equals(s2); assertTrue(b2, "b2"); // domain description s1.setDomainDescription("XYZ"); assertNotEquals(s1, s2); s2.setDomainDescription("XYZ"); assertEquals(s1, s2); // domain description - null s1.setDomainDescription(null); assertNotEquals(s1, s2); s2.setDomainDescription(null); assertEquals(s1, s2); // range description s1.setRangeDescription("XYZ"); assertNotEquals(s1, s2); s2.setRangeDescription("XYZ"); assertEquals(s1, s2); // range description - null s1.setRangeDescription(null); assertNotEquals(s1, s2); s2.setRangeDescription(null); assertEquals(s1, s2); RegularTimePeriod p1 = new Day(); RegularTimePeriod p2 = p1.next(); s1.add(p1, 100.0); s1.add(p2, 200.0); boolean b3 = s1.equals(s2); assertFalse(b3, "b3"); s2.add(p1, 100.0); s2.add(p2, 200.0); boolean b4 = s1.equals(s2); assertTrue(b4, "b4"); } /** * A test for bug report 1161329. */ @Test public void test1161329() { TimePeriodValues tpv = new TimePeriodValues("Test"); RegularTimePeriod t = new Day(); tpv.add(t, 1.0); t = t.next(); tpv.add(t, 2.0); tpv.delete(0, 1); assertEquals(0, tpv.getItemCount()); tpv.add(t, 2.0); assertEquals(1, tpv.getItemCount()); } static final double EPSILON = 0.0000000001; /** * Some checks for the add() methods. */ @Test public void testAdd() { TimePeriodValues tpv = new TimePeriodValues("Test"); MySeriesChangeListener listener = new MySeriesChangeListener(); tpv.addChangeListener(listener); tpv.add(new TimePeriodValue(new SimpleTimePeriod(new Date(1L), new Date(3L)), 99.0)); assertEquals(99.0, tpv.getValue(0).doubleValue(), EPSILON); assertEquals(tpv, listener.getLastEvent().getSource()); // a null item should throw an IllegalArgumentException boolean pass = false; try { tpv.add((TimePeriodValue) null); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Some tests for the getMinStartIndex() method. */ @Test public void testGetMinStartIndex() { TimePeriodValues s = new TimePeriodValues("Test"); assertEquals(-1, s.getMinStartIndex()); s.add(new SimpleTimePeriod(100L, 200L), 1.0); assertEquals(0, s.getMinStartIndex()); s.add(new SimpleTimePeriod(300L, 400L), 2.0); assertEquals(0, s.getMinStartIndex()); s.add(new SimpleTimePeriod(0L, 50L), 3.0); assertEquals(2, s.getMinStartIndex()); } /** * Some tests for the getMaxStartIndex() method. */ @Test public void testGetMaxStartIndex() { TimePeriodValues s = new TimePeriodValues("Test"); assertEquals(-1, s.getMaxStartIndex()); s.add(new SimpleTimePeriod(100L, 200L), 1.0); assertEquals(0, s.getMaxStartIndex()); s.add(new SimpleTimePeriod(300L, 400L), 2.0); assertEquals(1, s.getMaxStartIndex()); s.add(new SimpleTimePeriod(0L, 50L), 3.0); assertEquals(1, s.getMaxStartIndex()); } /** * Some tests for the getMinMiddleIndex() method. */ @Test public void testGetMinMiddleIndex() { TimePeriodValues s = new TimePeriodValues("Test"); assertEquals(-1, s.getMinMiddleIndex()); s.add(new SimpleTimePeriod(100L, 200L), 1.0); assertEquals(0, s.getMinMiddleIndex()); s.add(new SimpleTimePeriod(300L, 400L), 2.0); assertEquals(0, s.getMinMiddleIndex()); s.add(new SimpleTimePeriod(0L, 50L), 3.0); assertEquals(2, s.getMinMiddleIndex()); } /** * Some tests for the getMaxMiddleIndex() method. */ @Test public void testGetMaxMiddleIndex() { TimePeriodValues s = new TimePeriodValues("Test"); assertEquals(-1, s.getMaxMiddleIndex()); s.add(new SimpleTimePeriod(100L, 200L), 1.0); assertEquals(0, s.getMaxMiddleIndex()); s.add(new SimpleTimePeriod(300L, 400L), 2.0); assertEquals(1, s.getMaxMiddleIndex()); s.add(new SimpleTimePeriod(0L, 50L), 3.0); assertEquals(1, s.getMaxMiddleIndex()); s.add(new SimpleTimePeriod(150L, 200L), 4.0); assertEquals(1, s.getMaxMiddleIndex()); } /** * Some tests for the getMinEndIndex() method. */ @Test public void getMinEndIndex() { TimePeriodValues s = new TimePeriodValues("Test"); assertEquals(-1, s.getMinEndIndex()); s.add(new SimpleTimePeriod(100L, 200L), 1.0); assertEquals(0, s.getMinEndIndex()); s.add(new SimpleTimePeriod(300L, 400L), 2.0); assertEquals(0, s.getMinEndIndex()); s.add(new SimpleTimePeriod(0L, 50L), 3.0); assertEquals(2, s.getMinEndIndex()); } /** * Some tests for the getMaxEndIndex() method. */ @Test public void getMaxEndIndex() { TimePeriodValues s = new TimePeriodValues("Test"); assertEquals(-1, s.getMaxEndIndex()); s.add(new SimpleTimePeriod(100L, 200L), 1.0); assertEquals(0, s.getMaxEndIndex()); s.add(new SimpleTimePeriod(300L, 400L), 2.0); assertEquals(1, s.getMaxEndIndex()); s.add(new SimpleTimePeriod(0L, 50L), 3.0); assertEquals(1, s.getMaxEndIndex()); } /** * A listener used for detecting series change events. */ static class MySeriesChangeListener implements SeriesChangeListener { SeriesChangeEvent lastEvent; /** * Creates a new listener. */ public MySeriesChangeListener() { this.lastEvent = null; } /** * Returns the last event. * * @return The last event (possibly {@code null}). */ public SeriesChangeEvent getLastEvent() { return this.lastEvent; } /** * Clears the last event (sets it to {@code null}). */ public void clearLastEvent() { this.lastEvent = null; } /** * Callback method for series change events. * * @param event the event. */ @Override public void seriesChanged(SeriesChangeEvent event) { this.lastEvent = event; } } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/time/TimeSeriesCollectionTest.java000066400000000000000000000403611463604235500314530ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------- * TimeSeriesCollectionTest.java * ----------------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import org.jfree.chart.TestUtils; import org.jfree.data.DatasetChangeConfirmation; import org.jfree.data.Range; import org.jfree.data.general.DatasetUtils; import org.junit.jupiter.api.Test; import java.util.*; import static org.junit.jupiter.api.Assertions.*; /** * A collection of test cases for the {@link TimeSeriesCollection} class. */ public class TimeSeriesCollectionTest { /** * Some tests for the equals() method. */ @Test public void testEquals() { TimeSeriesCollection c1 = new TimeSeriesCollection(); TimeSeriesCollection c2 = new TimeSeriesCollection(); TimeSeries s1 = new TimeSeries("Series 1"); TimeSeries s2 = new TimeSeries("Series 2"); // newly created collections should be equal boolean b1 = c1.equals(c2); assertTrue(b1, "b1"); // add series to collection 1, should be not equal c1.addSeries(s1); c1.addSeries(s2); boolean b2 = c1.equals(c2); assertFalse(b2, "b2"); // now add the same series to collection 2 to make them equal again... c2.addSeries(s1); c2.addSeries(s2); boolean b3 = c1.equals(c2); assertTrue(b3, "b3"); // now remove series 2 from collection 2 c2.removeSeries(s2); boolean b4 = c1.equals(c2); assertFalse(b4, "b4"); // now remove series 2 from collection 1 to make them equal again c1.removeSeries(s2); boolean b5 = c1.equals(c2); assertTrue(b5, "b5"); } /** * Tests the remove series method. */ @Test public void testRemoveSeries() { TimeSeriesCollection c1 = new TimeSeriesCollection(); TimeSeries s1 = new TimeSeries("Series 1"); TimeSeries s2 = new TimeSeries("Series 2"); TimeSeries s3 = new TimeSeries("Series 3"); TimeSeries s4 = new TimeSeries("Series 4"); c1.addSeries(s1); c1.addSeries(s2); c1.addSeries(s3); c1.addSeries(s4); c1.removeSeries(s3); TimeSeries s = c1.getSeries(2); boolean b1 = s.equals(s4); assertTrue(b1); } /** * Some checks for the {@link TimeSeriesCollection#removeSeries(int)} * method. */ @Test public void testRemoveSeries_int() { TimeSeriesCollection c1 = new TimeSeriesCollection(); TimeSeries s1 = new TimeSeries("Series 1"); TimeSeries s2 = new TimeSeries("Series 2"); TimeSeries s3 = new TimeSeries("Series 3"); TimeSeries s4 = new TimeSeries("Series 4"); c1.addSeries(s1); c1.addSeries(s2); c1.addSeries(s3); c1.addSeries(s4); c1.removeSeries(2); assertEquals(c1.getSeries(2), s4); c1.removeSeries(0); assertEquals(c1.getSeries(0), s2); assertEquals(2, c1.getSeriesCount()); } /** * Test the getSurroundingItems() method to ensure it is returning the * values we expect. */ @Test public void testGetSurroundingItems() { TimeSeries series = new TimeSeries("Series 1"); TimeSeriesCollection collection = new TimeSeriesCollection(series); collection.setXPosition(TimePeriodAnchor.MIDDLE); // for a series with no data, we expect {-1, -1}... int[] result = collection.getSurroundingItems(0, 1000L); assertEquals(-1, result[0]); assertEquals(-1, result[1]); // now test with a single value in the series... Day today = new Day(); long start1 = today.getFirstMillisecond(); long middle1 = today.getMiddleMillisecond(); long end1 = today.getLastMillisecond(); series.add(today, 99.9); result = collection.getSurroundingItems(0, start1); assertEquals(-1, result[0]); assertEquals(0, result[1]); result = collection.getSurroundingItems(0, middle1); assertEquals(0, result[0]); assertEquals(0, result[1]); result = collection.getSurroundingItems(0, end1); assertEquals(0, result[0]); assertEquals(-1, result[1]); // now add a second value to the series... Day tomorrow = (Day) today.next(); long start2 = tomorrow.getFirstMillisecond(); long middle2 = tomorrow.getMiddleMillisecond(); long end2 = tomorrow.getLastMillisecond(); series.add(tomorrow, 199.9); result = collection.getSurroundingItems(0, start2); assertEquals(0, result[0]); assertEquals(1, result[1]); result = collection.getSurroundingItems(0, middle2); assertEquals(1, result[0]); assertEquals(1, result[1]); result = collection.getSurroundingItems(0, end2); assertEquals(1, result[0]); assertEquals(-1, result[1]); // now add a third value to the series... Day yesterday = (Day) today.previous(); long start3 = yesterday.getFirstMillisecond(); long middle3 = yesterday.getMiddleMillisecond(); long end3 = yesterday.getLastMillisecond(); series.add(yesterday, 1.23); result = collection.getSurroundingItems(0, start3); assertEquals(-1, result[0]); assertEquals(0, result[1]); result = collection.getSurroundingItems(0, middle3); assertEquals(0, result[0]); assertEquals(0, result[1]); result = collection.getSurroundingItems(0, end3); assertEquals(0, result[0]); assertEquals(1, result[1]); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { TimeSeries s1 = createSeries(); TimeSeriesCollection c1 = new TimeSeriesCollection(s1); TimeSeriesCollection c2 = TestUtils.serialised(c1); assertEquals(c1, c2); // check independence s1.add(new Day(26, 4, 2000), 99.9); assertNotEquals(c1, c2); TimeSeries s2 = c2.getSeries(0); s2.add(new Day(26, 4, 2000), 99.9); assertEquals(c1, c2); // check that c2 gets notifications when s2 is changed DatasetChangeConfirmation listener = new DatasetChangeConfirmation(); c2.addChangeListener(listener); s2.add(new Day(27, 4, 2000), 99.9); assertNotNull(listener.event); } /** * Creates a time series for testing. * * @return A time series. */ private TimeSeries createSeries() { RegularTimePeriod t = new Day(); TimeSeries series = new TimeSeries("Test"); series.add(t, 1.0); t = t.next(); series.add(t, 2.0); t = t.next(); series.add(t, null); t = t.next(); series.add(t, 4.0); return series; } /** * A test for bug report 1170825. */ @Test public void test1170825() { TimeSeries s1 = new TimeSeries("Series1"); TimeSeriesCollection dataset = new TimeSeriesCollection(); dataset.addSeries(s1); try { /* TimeSeries s = */ dataset.getSeries(1); } catch (IllegalArgumentException e) { // correct outcome } catch (IndexOutOfBoundsException e) { fail(); // wrong outcome } } /** * Some tests for the indexOf() method. */ @Test public void testIndexOf() { TimeSeries s1 = new TimeSeries("S1"); TimeSeries s2 = new TimeSeries("S2"); TimeSeriesCollection dataset = new TimeSeriesCollection(); assertEquals(-1, dataset.indexOf(s1)); assertEquals(-1, dataset.indexOf(s2)); dataset.addSeries(s1); assertEquals(0, dataset.indexOf(s1)); assertEquals(-1, dataset.indexOf(s2)); dataset.addSeries(s2); assertEquals(0, dataset.indexOf(s1)); assertEquals(1, dataset.indexOf(s2)); dataset.removeSeries(s1); assertEquals(-1, dataset.indexOf(s1)); assertEquals(0, dataset.indexOf(s2)); TimeSeries s2b = new TimeSeries("S2"); assertEquals(0, dataset.indexOf(s2b)); } private static final double EPSILON = 0.0000000001; /** * This method provides a check for the bounds calculated using the * {@link DatasetUtils#findDomainBounds(org.jfree.data.xy.XYDataset, * java.util.List, boolean)} method. */ @Test public void testFindDomainBounds() { // store the current time zone TimeZone saved = TimeZone.getDefault(); TimeZone.setDefault(TimeZone.getTimeZone("Europe/Paris")); TimeSeriesCollection dataset = new TimeSeriesCollection(); List visibleSeriesKeys = new ArrayList<>(); Range r = DatasetUtils.findDomainBounds(dataset, visibleSeriesKeys, true); assertNull(r); TimeSeries s1 = new TimeSeries("S1"); dataset.addSeries(s1); visibleSeriesKeys.add("S1"); r = DatasetUtils.findDomainBounds(dataset, visibleSeriesKeys, true); assertNull(r); s1.add(new Year(2008), 8.0); r = DatasetUtils.findDomainBounds(dataset, visibleSeriesKeys, true); assertEquals(1199142000000.0, r.getLowerBound(), EPSILON); assertEquals(1230764399999.0, r.getUpperBound(), EPSILON); TimeSeries s2 = new TimeSeries("S2"); dataset.addSeries(s2); s2.add(new Year(2009), 9.0); s2.add(new Year(2010), 10.0); r = DatasetUtils.findDomainBounds(dataset, visibleSeriesKeys, true); assertEquals(1199142000000.0, r.getLowerBound(), EPSILON); assertEquals(1230764399999.0, r.getUpperBound(), EPSILON); visibleSeriesKeys.add("S2"); r = DatasetUtils.findDomainBounds(dataset, visibleSeriesKeys, true); assertEquals(1199142000000.0, r.getLowerBound(), EPSILON); assertEquals(1293836399999.0, r.getUpperBound(), EPSILON); // restore the default time zone TimeZone.setDefault(saved); } /** * Basic checks for cloning. * * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { TimeSeries s1 = new TimeSeries("Series"); s1.add(new Year(2009), 1.1); TimeSeriesCollection c1 = new TimeSeriesCollection(); c1.addSeries(s1); TimeSeriesCollection c2 = (TimeSeriesCollection) c1.clone(); assertNotSame(c1, c2); assertSame(c1.getClass(), c2.getClass()); assertEquals(c1, c2); // check independence s1.setDescription("XYZ"); assertNotEquals(c1, c2); c2.getSeries(0).setDescription("XYZ"); assertEquals(c1, c2); } /** * A test to cover bug 3445507. */ @Test public void testBug3445507() { TimeSeries s1 = new TimeSeries("S1"); s1.add(new Year(2011), null); s1.add(new Year(2012), null); TimeSeries s2 = new TimeSeries("S2"); s2.add(new Year(2011), 5.0); s2.add(new Year(2012), 6.0); TimeSeriesCollection dataset = new TimeSeriesCollection(); dataset.addSeries(s1); dataset.addSeries(s2); List keys = new ArrayList<>(); keys.add("S1"); keys.add("S2"); Range r = dataset.getRangeBounds(keys, new Range( Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY), false); assertEquals(5.0, r.getLowerBound(), EPSILON); assertEquals(6.0, r.getUpperBound(), EPSILON); } /** * Some checks for the getRangeBounds() method. */ @Test public void testGetRangeBounds() { TimeSeriesCollection dataset = new TimeSeriesCollection(); // when the dataset contains no series, we expect the range to be null assertNull(dataset.getRangeBounds(false)); assertNull(dataset.getRangeBounds(true)); // when the dataset contains one or more series, but those series // contain no items, we still expect the range to be null TimeSeries s1 = new TimeSeries("S1"); dataset.addSeries(s1); assertNull(dataset.getRangeBounds(false)); assertNull(dataset.getRangeBounds(true)); // tests with values s1.add(new Year(2012), 1.0); assertEquals(new Range(1.0, 1.0), dataset.getRangeBounds(false)); assertEquals(new Range(1.0, 1.0), dataset.getRangeBounds(true)); s1.add(new Year(2013), -1.0); assertEquals(new Range(-1.0, 1.0), dataset.getRangeBounds(false)); assertEquals(new Range(-1.0, 1.0), dataset.getRangeBounds(true)); s1.add(new Year(2014), null); assertEquals(new Range(-1.0, 1.0), dataset.getRangeBounds(false)); assertEquals(new Range(-1.0, 1.0), dataset.getRangeBounds(true)); // adding a second series TimeSeries s2 = new TimeSeries("S2"); dataset.addSeries(s2); assertEquals(new Range(-1.0, 1.0), dataset.getRangeBounds(false)); assertEquals(new Range(-1.0, 1.0), dataset.getRangeBounds(true)); s2.add(new Year(2014), 5.0); assertEquals(new Range(-1.0, 5.0), dataset.getRangeBounds(false)); assertEquals(new Range(-1.0, 5.0), dataset.getRangeBounds(true)); dataset.removeAllSeries(); assertNull(dataset.getRangeBounds(false)); assertNull(dataset.getRangeBounds(true)); s1 = new TimeSeries("s1"); s2 = new TimeSeries("s2"); dataset.addSeries(s1); dataset.addSeries(s2); assertNull(dataset.getRangeBounds(false)); assertNull(dataset.getRangeBounds(true)); s2.add(new Year(2014), 100.0); assertEquals(new Range(100.0, 100.0), dataset.getRangeBounds(false)); assertEquals(new Range(100.0, 100.0), dataset.getRangeBounds(true)); } @Test public void testGetRangeBounds2() { TimeZone tzone = TimeZone.getTimeZone("Europe/London"); Calendar calendar = new GregorianCalendar(tzone, Locale.UK); calendar.clear(); calendar.set(2014, Calendar.FEBRUARY, 23, 6, 0); long start = calendar.getTimeInMillis(); calendar.clear(); calendar.set(2014, Calendar.FEBRUARY, 24, 18, 0); long end = calendar.getTimeInMillis(); Range range = new Range(start, end); TimeSeriesCollection collection = new TimeSeriesCollection(tzone); assertNull(collection.getRangeBounds(Collections.EMPTY_LIST, range, true)); TimeSeries s1 = new TimeSeries("S1"); s1.add(new Day(24, 2, 2014), 10.0); collection.addSeries(s1); assertEquals(new Range(10.0, 10.0), collection.getRangeBounds( Collections.singletonList("S1"), range, true)); collection.setXPosition(TimePeriodAnchor.MIDDLE); assertEquals(new Range(10.0, 10.0), collection.getRangeBounds( Collections.singletonList("S1"), range, true)); collection.setXPosition(TimePeriodAnchor.END); assertNull(collection.getRangeBounds(Collections.singletonList("S1"), range, true)); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/time/TimeSeriesDataItemTest.java000066400000000000000000000054111463604235500310450ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * TimeSeriesDataItemTest.java * --------------------------- * (C) Copyright 2003-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link TimeSeriesDataItem} class. */ public class TimeSeriesDataItemTest { /** * Test that an instance is equal to itself. * * SourceForge Bug ID: 558850. */ @Test public void testEqualsSelf() { TimeSeriesDataItem item = new TimeSeriesDataItem(new Day(23, 9, 2001), 99.7); assertEquals(item, item); } /** * Test the equals() method. */ @Test public void testEquals() { TimeSeriesDataItem item1 = new TimeSeriesDataItem(new Day(23, 9, 2001), 99.7); TimeSeriesDataItem item2 = new TimeSeriesDataItem(new Day(23, 9, 2001), 99.7); assertEquals(item1, item2); assertEquals(item2, item1); item1.setValue(5); assertNotEquals(item1, item2); item2.setValue(5); assertEquals(item1, item2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { TimeSeriesDataItem item1 = new TimeSeriesDataItem(new Day(23, 9, 2001), 99.7); TimeSeriesDataItem item2 = TestUtils.serialised(item1); assertEquals(item1, item2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/time/TimeSeriesTest.java000066400000000000000000001200101463604235500274250ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * TimeSeriesTest.java * ------------------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.Locale; import java.util.TimeZone; import org.jfree.chart.TestUtils; import org.jfree.chart.date.MonthConstants; import org.jfree.data.Range; import org.jfree.data.general.SeriesChangeEvent; import org.jfree.data.general.SeriesChangeListener; import org.jfree.data.general.SeriesException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * A collection of test cases for the {@link TimeSeries} class. */ public class TimeSeriesTest implements SeriesChangeListener { /** A time series. */ private TimeSeries seriesA; /** A flag that indicates whether a change event was fired. */ private boolean gotSeriesChangeEvent = false; /** * Common test setup. */ @BeforeEach public void setUp() { this.seriesA = new TimeSeries("Series A"); this.seriesA.add(new Year(2000), 102000); this.seriesA.add(new Year(2001), 102001); this.seriesA.add(new Year(2002), 102002); this.seriesA.add(new Year(2003), 102003); this.seriesA.add(new Year(2004), 102004); this.seriesA.add(new Year(2005), 102005); } /** * Sets the flag to indicate that a {@link SeriesChangeEvent} has been * received. * * @param event the event. */ @Override public void seriesChanged(SeriesChangeEvent event) { this.gotSeriesChangeEvent = true; } /** * Check that cloning works. * @throws java.lang.CloneNotSupportedException */ @Test public void testClone() throws CloneNotSupportedException { TimeSeries series = new TimeSeries("Test Series"); RegularTimePeriod jan1st2002 = new Day(1, MonthConstants.JANUARY, 2002); series.add(jan1st2002, 42); TimeSeries clone; clone = (TimeSeries) series.clone(); clone.setKey("Clone Series"); clone.update(jan1st2002, 10); int seriesValue = series.getValue(jan1st2002).intValue(); int cloneValue = clone.getValue(jan1st2002).intValue(); assertEquals(42, seriesValue); assertEquals(10, cloneValue); assertEquals("Test Series", series.getKey()); assertEquals("Clone Series", clone.getKey()); } /** * Another test of the clone() method. * * @throws java.lang.CloneNotSupportedException if there is a problem cloning. */ @Test public void testClone2() throws CloneNotSupportedException { TimeSeries s1 = new TimeSeries("S1"); s1.add(new Year(2007), 100.0); s1.add(new Year(2008), null); s1.add(new Year(2009), 200.0); TimeSeries s2 = (TimeSeries) s1.clone(); assertEquals(s1, s2); // check independence s2.addOrUpdate(new Year(2009), 300.0); assertNotEquals(s1, s2); s1.addOrUpdate(new Year(2009), 300.0); assertEquals(s1, s2); } /** * Add a value to series A for 1999. It should be added at index 0. */ @Test public void testAddValue() { this.seriesA.add(new Year(1999), 1); int value = this.seriesA.getValue(0).intValue(); assertEquals(1, value); } /** * Tests the retrieval of values. */ @Test public void testGetValue() { assertNull(seriesA.getValue(new Year(1999))); assertEquals(102000.0, seriesA.getValue(new Year(2000))); } /** * Tests the deletion of values. */ @Test public void testDelete() { seriesA.delete(0, 0); assertEquals(5, seriesA.getItemCount()); assertNull(seriesA.getValue(new Year(2000))); } /** * Basic tests for the delete() method. */ @Test public void testDelete2() { TimeSeries s1 = new TimeSeries("Series"); s1.add(new Year(2000), 13.75); s1.add(new Year(2001), 11.90); s1.add(new Year(2002), null); s1.addChangeListener(this); this.gotSeriesChangeEvent = false; s1.delete(new Year(2001)); assertTrue(this.gotSeriesChangeEvent); assertEquals(2, s1.getItemCount()); assertNull(s1.getValue(new Year(2001))); // try deleting a time period that doesn't exist... this.gotSeriesChangeEvent = false; s1.delete(new Year(2006)); assertFalse(this.gotSeriesChangeEvent); // try deleting null try { s1.delete(null); fail("Expected IllegalArgumentException."); } catch (IllegalArgumentException e) { // expected } } /** * Some checks for the delete(int, int) method. */ @Test public void testDelete3() { TimeSeries s1 = new TimeSeries("S1"); s1.add(new Year(2011), 1.1); s1.add(new Year(2012), 2.2); s1.add(new Year(2013), 3.3); s1.add(new Year(2014), 4.4); s1.add(new Year(2015), 5.5); s1.add(new Year(2016), 6.6); s1.delete(2, 5); assertEquals(2, s1.getItemCount()); assertEquals(new Year(2011), s1.getTimePeriod(0)); assertEquals(new Year(2012), s1.getTimePeriod(1)); assertEquals(1.1, s1.getMinY(), EPSILON); assertEquals(2.2, s1.getMaxY(), EPSILON); } /** * Check that the item bounds are determined correctly when there is a * maximum item count and a new value is added. */ @Test public void testDelete_RegularTimePeriod() { TimeSeries s1 = new TimeSeries("S1"); s1.add(new Year(2010), 1.1); s1.add(new Year(2011), 2.2); s1.add(new Year(2012), 3.3); s1.add(new Year(2013), 4.4); s1.delete(new Year(2010)); s1.delete(new Year(2013)); assertEquals(2.2, s1.getMinY(), EPSILON); assertEquals(3.3, s1.getMaxY(), EPSILON); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { TimeSeries s1 = new TimeSeries("A test"); s1.add(new Year(2000), 13.75); s1.add(new Year(2001), 11.90); s1.add(new Year(2002), null); s1.add(new Year(2005), 19.32); s1.add(new Year(2007), 16.89); TimeSeries s2 = TestUtils.serialised(s1); assertEquals(s1, s2); // test independence (and also serialization mechanism for change listeners) s2.setKey("New Series Key"); assertNotEquals(s1, s2); s1.setKey("New Series Key"); assertEquals(s1, s2); s2.setDomainDescription("X"); assertNotEquals(s1, s2); s1.setDomainDescription("X"); assertEquals(s1, s2); s2.setRangeDescription("Y"); assertNotEquals(s1, s2); s1.setRangeDescription("Y"); assertEquals(s1, s2); } /** * Tests the equals() method. */ @Test public void testEquals() { TimeSeries s1 = new TimeSeries("Time Series 1"); TimeSeries s2 = new TimeSeries("Time Series 2"); boolean b1 = s1.equals(s2); assertFalse(b1, "b1"); s2.setKey("Time Series 1"); boolean b2 = s1.equals(s2); assertTrue(b2, "b2"); RegularTimePeriod p1 = new Day(); RegularTimePeriod p2 = p1.next(); s1.add(p1, 100.0); s1.add(p2, 200.0); assertNotEquals(s1, s2); s2.add(p1, 100.0); s2.add(p2, 200.0); assertEquals(s1, s2); s1.setMaximumItemCount(100); assertNotEquals(s1, s2); s2.setMaximumItemCount(100); assertEquals(s1, s2); s1.setMaximumItemAge(100); assertNotEquals(s1, s2); s2.setMaximumItemAge(100); assertEquals(s1, s2); } /** * Tests a specific bug report where null arguments in the constructor * cause the equals() method to fail. Fixed for 0.9.21. */ @Test public void testEquals2() { TimeSeries s1 = new TimeSeries("Series", null, null); TimeSeries s2 = new TimeSeries("Series", null, null); assertEquals(s1, s2); } /** * Some tests to ensure that the createCopy(RegularTimePeriod, * RegularTimePeriod) method is functioning correctly. */ @Test public void testCreateCopy1() { TimeSeries series = new TimeSeries("Series"); series.add(new Month(MonthConstants.JANUARY, 2003), 45.0); series.add(new Month(MonthConstants.FEBRUARY, 2003), 55.0); series.add(new Month(MonthConstants.JUNE, 2003), 35.0); series.add(new Month(MonthConstants.NOVEMBER, 2003), 85.0); series.add(new Month(MonthConstants.DECEMBER, 2003), 75.0); try { // copy a range before the start of the series data... TimeSeries result1 = series.createCopy( new Month(MonthConstants.NOVEMBER, 2002), new Month(MonthConstants.DECEMBER, 2002)); assertEquals(0, result1.getItemCount()); // copy a range that includes only the first item in the series... TimeSeries result2 = series.createCopy( new Month(MonthConstants.NOVEMBER, 2002), new Month(MonthConstants.JANUARY, 2003)); assertEquals(1, result2.getItemCount()); // copy a range that begins before and ends in the middle of the // series... TimeSeries result3 = series.createCopy( new Month(MonthConstants.NOVEMBER, 2002), new Month(MonthConstants.APRIL, 2003)); assertEquals(2, result3.getItemCount()); TimeSeries result4 = series.createCopy( new Month(MonthConstants.NOVEMBER, 2002), new Month(MonthConstants.DECEMBER, 2003)); assertEquals(5, result4.getItemCount()); TimeSeries result5 = series.createCopy( new Month(MonthConstants.NOVEMBER, 2002), new Month(MonthConstants.MARCH, 2004)); assertEquals(5, result5.getItemCount()); TimeSeries result6 = series.createCopy( new Month(MonthConstants.JANUARY, 2003), new Month(MonthConstants.JANUARY, 2003)); assertEquals(1, result6.getItemCount()); TimeSeries result7 = series.createCopy( new Month(MonthConstants.JANUARY, 2003), new Month(MonthConstants.APRIL, 2003)); assertEquals(2, result7.getItemCount()); TimeSeries result8 = series.createCopy( new Month(MonthConstants.JANUARY, 2003), new Month(MonthConstants.DECEMBER, 2003)); assertEquals(5, result8.getItemCount()); TimeSeries result9 = series.createCopy( new Month(MonthConstants.JANUARY, 2003), new Month(MonthConstants.MARCH, 2004)); assertEquals(5, result9.getItemCount()); TimeSeries result10 = series.createCopy( new Month(MonthConstants.MAY, 2003), new Month(MonthConstants.DECEMBER, 2003)); assertEquals(3, result10.getItemCount()); TimeSeries result11 = series.createCopy( new Month(MonthConstants.MAY, 2003), new Month(MonthConstants.MARCH, 2004)); assertEquals(3, result11.getItemCount()); TimeSeries result12 = series.createCopy( new Month(MonthConstants.DECEMBER, 2003), new Month(MonthConstants.DECEMBER, 2003)); assertEquals(1, result12.getItemCount()); TimeSeries result13 = series.createCopy( new Month(MonthConstants.DECEMBER, 2003), new Month(MonthConstants.MARCH, 2004)); assertEquals(1, result13.getItemCount()); TimeSeries result14 = series.createCopy( new Month(MonthConstants.JANUARY, 2004), new Month(MonthConstants.MARCH, 2004)); assertEquals(0, result14.getItemCount()); } catch (CloneNotSupportedException e) { fail(); } } /** * Some tests to ensure that the createCopy(int, int) method is * functioning correctly. */ @Test public void testCreateCopy2() { TimeSeries series = new TimeSeries("Series"); series.add(new Month(MonthConstants.JANUARY, 2003), 45.0); series.add(new Month(MonthConstants.FEBRUARY, 2003), 55.0); series.add(new Month(MonthConstants.JUNE, 2003), 35.0); series.add(new Month(MonthConstants.NOVEMBER, 2003), 85.0); series.add(new Month(MonthConstants.DECEMBER, 2003), 75.0); try { // copy just the first item... TimeSeries result1 = series.createCopy(0, 0); assertEquals(new Month(1, 2003), result1.getTimePeriod(0)); // copy the first two items... result1 = series.createCopy(0, 1); assertEquals(new Month(2, 2003), result1.getTimePeriod(1)); // copy the middle three items... result1 = series.createCopy(1, 3); assertEquals(new Month(2, 2003), result1.getTimePeriod(0)); assertEquals(new Month(11, 2003), result1.getTimePeriod(2)); // copy the last two items... result1 = series.createCopy(3, 4); assertEquals(new Month(11, 2003), result1.getTimePeriod(0)); assertEquals(new Month(12, 2003), result1.getTimePeriod(1)); // copy the last item... result1 = series.createCopy(4, 4); assertEquals(new Month(12, 2003), result1.getTimePeriod(0)); } catch (CloneNotSupportedException e) { fail(); } // check negative first argument boolean pass = false; try { /* TimeSeries result = */ series.createCopy(-1, 1); } catch (IllegalArgumentException e) { pass = true; } catch (CloneNotSupportedException e) { pass = false; } assertTrue(pass); // check second argument less than first argument pass = false; try { /* TimeSeries result = */ series.createCopy(1, 0); } catch (IllegalArgumentException e) { pass = true; } catch (CloneNotSupportedException e) { pass = false; } assertTrue(pass); TimeSeries series2 = new TimeSeries("Series 2"); try { TimeSeries series3 = series2.createCopy(99, 999); assertEquals(0, series3.getItemCount()); } catch (CloneNotSupportedException e) { fail(); } } /** * Checks that the min and max y values are updated correctly when copying * a subset. * * @throws java.lang.CloneNotSupportedException if there is a problem cloning. */ @Test public void testCreateCopy3() throws CloneNotSupportedException { TimeSeries s1 = new TimeSeries("S1"); s1.add(new Year(2009), 100.0); s1.add(new Year(2010), 101.0); s1.add(new Year(2011), 102.0); assertEquals(100.0, s1.getMinY(), EPSILON); assertEquals(102.0, s1.getMaxY(), EPSILON); TimeSeries s2 = s1.createCopy(0, 1); assertEquals(100.0, s2.getMinY(), EPSILON); assertEquals(101.0, s2.getMaxY(), EPSILON); TimeSeries s3 = s1.createCopy(1, 2); assertEquals(101.0, s3.getMinY(), EPSILON); assertEquals(102.0, s3.getMaxY(), EPSILON); } /** * Test the setMaximumItemCount() method to ensure that it removes items * from the series if necessary. */ @Test public void testSetMaximumItemCount() { TimeSeries s1 = new TimeSeries("S1"); s1.add(new Year(2000), 13.75); s1.add(new Year(2001), 11.90); s1.add(new Year(2002), null); s1.add(new Year(2005), 19.32); s1.add(new Year(2007), 16.89); assertEquals(5, s1.getItemCount()); s1.setMaximumItemCount(3); assertEquals(3, s1.getItemCount()); TimeSeriesDataItem item = s1.getDataItem(0); assertEquals(item.getPeriod(), new Year(2002)); assertEquals(16.89, s1.getMinY(), EPSILON); assertEquals(19.32, s1.getMaxY(), EPSILON); } /** * Some checks for the addOrUpdate() method. */ @Test public void testAddOrUpdate() { TimeSeries s1 = new TimeSeries("S1"); s1.setMaximumItemCount(2); s1.addOrUpdate(new Year(2000), 100.0); assertEquals(1, s1.getItemCount()); s1.addOrUpdate(new Year(2001), 101.0); assertEquals(2, s1.getItemCount()); s1.addOrUpdate(new Year(2001), 102.0); assertEquals(2, s1.getItemCount()); s1.addOrUpdate(new Year(2002), 103.0); assertEquals(2, s1.getItemCount()); } /** * Test the add branch of the addOrUpdate() method. */ @Test public void testAddOrUpdate2() { TimeSeries s1 = new TimeSeries("S1"); s1.setMaximumItemCount(2); s1.addOrUpdate(new Year(2010), 1.1); s1.addOrUpdate(new Year(2011), 2.2); s1.addOrUpdate(new Year(2012), 3.3); assertEquals(2, s1.getItemCount()); assertEquals(2.2, s1.getMinY(), EPSILON); assertEquals(3.3, s1.getMaxY(), EPSILON); } /** * Test that the addOrUpdate() method won't allow multiple time period * classes. */ @Test public void testAddOrUpdate3() { TimeSeries s1 = new TimeSeries("S1"); s1.addOrUpdate(new Year(2010), 1.1); assertEquals(Year.class, s1.getTimePeriodClass()); boolean pass = false; try { s1.addOrUpdate(new Month(1, 2009), 0.0); } catch (SeriesException e) { pass = true; } assertTrue(pass); } /** * Some more checks for the addOrUpdate() method. */ @Test public void testAddOrUpdate4() { TimeSeries ts = new TimeSeries("S"); TimeSeriesDataItem overwritten = ts.addOrUpdate(new Year(2009), 20.09); assertNull(overwritten); overwritten = ts.addOrUpdate(new Year(2009), 1.0); assertEquals(20.09, overwritten.getValue()); assertEquals(1.0, ts.getValue(new Year(2009))); // changing the overwritten record shouldn't affect the series overwritten.setValue(null); assertEquals(1.0, ts.getValue(new Year(2009))); TimeSeriesDataItem item = new TimeSeriesDataItem(new Year(2010), 20.10); overwritten = ts.addOrUpdate(item); assertNull(overwritten); assertEquals(20.10, ts.getValue(new Year(2010))); // changing the item that was added should not change the series item.setValue(null); assertEquals(20.10, ts.getValue(new Year(2010))); } /** * A test for the bug report 1075255. */ @Test public void testBug1075255() { TimeSeries ts = new TimeSeries("dummy"); ts.add(new FixedMillisecond(0L), 0.0); TimeSeries ts2 = new TimeSeries("dummy2"); ts2.add(new FixedMillisecond(0L), 1.0); try { ts.addAndOrUpdate(ts2); } catch (Exception e) { fail("No exceptions should be thrown."); } assertEquals(1, ts.getItemCount()); } /** * A test for bug 1832432. * * @throws java.lang.CloneNotSupportedException if there is a problem cloning. */ @Test public void testBug1832432() throws CloneNotSupportedException { TimeSeries s1 = new TimeSeries("Series"); TimeSeries s2 = (TimeSeries) s1.clone(); assertNotSame(s1, s2); assertSame(s1.getClass(), s2.getClass()); assertEquals(s1, s2); // test independence s1.add(new Day(1, 1, 2007), 100.0); assertNotEquals(s1, s2); } /** * Some checks for the getIndex() method. */ @Test public void testGetIndex() { TimeSeries series = new TimeSeries("Series"); assertEquals(-1, series.getIndex(new Month(1, 2003))); series.add(new Month(1, 2003), 45.0); assertEquals(0, series.getIndex(new Month(1, 2003))); assertEquals(-1, series.getIndex(new Month(12, 2002))); assertEquals(-2, series.getIndex(new Month(2, 2003))); series.add(new Month(3, 2003), 55.0); assertEquals(-1, series.getIndex(new Month(12, 2002))); assertEquals(0, series.getIndex(new Month(1, 2003))); assertEquals(-2, series.getIndex(new Month(2, 2003))); assertEquals(1, series.getIndex(new Month(3, 2003))); assertEquals(-3, series.getIndex(new Month(4, 2003))); } /** * Some checks for the getDataItem(int) method. */ @Test public void testGetDataItem1() { TimeSeries series = new TimeSeries("S"); // can't get anything yet...just an exception boolean pass = false; try { /*TimeSeriesDataItem item =*/ series.getDataItem(0); } catch (IndexOutOfBoundsException e) { pass = true; } assertTrue(pass); series.add(new Year(2006), 100.0); TimeSeriesDataItem item = series.getDataItem(0); assertEquals(new Year(2006), item.getPeriod()); pass = false; try { /*item = */series.getDataItem(-1); } catch (IndexOutOfBoundsException e) { pass = true; } assertTrue(pass); pass = false; try { /*item = */series.getDataItem(1); } catch (IndexOutOfBoundsException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getDataItem(RegularTimePeriod) method. */ @Test public void testGetDataItem2() { TimeSeries series = new TimeSeries("S"); assertNull(series.getDataItem(new Year(2006))); // try a null argument boolean pass = false; try { /* TimeSeriesDataItem item = */ series.getDataItem(null); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Some checks for the removeAgedItems() method. */ @Test public void testRemoveAgedItems() { TimeSeries series = new TimeSeries("Test Series"); series.addChangeListener(this); assertEquals(Long.MAX_VALUE, series.getMaximumItemAge()); assertEquals(Integer.MAX_VALUE, series.getMaximumItemCount()); this.gotSeriesChangeEvent = false; // test empty series series.removeAgedItems(true); assertEquals(0, series.getItemCount()); assertFalse(this.gotSeriesChangeEvent); // test series with one item series.add(new Year(1999), 1.0); series.setMaximumItemAge(0); this.gotSeriesChangeEvent = false; series.removeAgedItems(true); assertEquals(1, series.getItemCount()); assertFalse(this.gotSeriesChangeEvent); // test series with two items series.setMaximumItemAge(10); series.add(new Year(2001), 2.0); this.gotSeriesChangeEvent = false; series.setMaximumItemAge(2); assertEquals(2, series.getItemCount()); assertEquals(0, series.getIndex(new Year(1999))); assertFalse(this.gotSeriesChangeEvent); series.setMaximumItemAge(1); assertEquals(1, series.getItemCount()); assertEquals(0, series.getIndex(new Year(2001))); assertTrue(this.gotSeriesChangeEvent); } /** * Some checks for the removeAgedItems(long, boolean) method. */ @Test public void testRemoveAgedItems2() { long y2006 = 1157087372534L; // milliseconds somewhere in 2006 TimeSeries series = new TimeSeries("Test Series"); series.addChangeListener(this); assertEquals(Long.MAX_VALUE, series.getMaximumItemAge()); assertEquals(Integer.MAX_VALUE, series.getMaximumItemCount()); this.gotSeriesChangeEvent = false; // test empty series series.removeAgedItems(y2006, true); assertEquals(0, series.getItemCount()); assertFalse(this.gotSeriesChangeEvent); // test a series with 1 item series.add(new Year(2004), 1.0); series.setMaximumItemAge(1); this.gotSeriesChangeEvent = false; series.removeAgedItems(new Year(2005).getMiddleMillisecond(), true); assertEquals(1, series.getItemCount()); assertFalse(this.gotSeriesChangeEvent); series.removeAgedItems(y2006, true); assertEquals(0, series.getItemCount()); assertTrue(this.gotSeriesChangeEvent); // test a series with two items series.setMaximumItemAge(2); series.add(new Year(2003), 1.0); series.add(new Year(2005), 2.0); assertEquals(2, series.getItemCount()); this.gotSeriesChangeEvent = false; assertEquals(2, series.getItemCount()); series.removeAgedItems(new Year(2005).getMiddleMillisecond(), true); assertEquals(2, series.getItemCount()); assertFalse(this.gotSeriesChangeEvent); series.removeAgedItems(y2006, true); assertEquals(1, series.getItemCount()); assertTrue(this.gotSeriesChangeEvent); } /** * Calling removeAgedItems() on an empty series should not throw any * exception. */ @Test public void testRemoveAgedItems3() { TimeSeries s = new TimeSeries("Test"); boolean pass = true; try { s.removeAgedItems(0L, true); } catch (Exception e) { pass = false; } assertTrue(pass); } /** * Check that the item bounds are determined correctly when there is a * maximum item count. */ @Test public void testRemoveAgedItems4() { TimeSeries s1 = new TimeSeries("S1"); s1.setMaximumItemAge(2); s1.add(new Year(2010), 1.1); s1.add(new Year(2011), 2.2); s1.add(new Year(2012), 3.3); s1.add(new Year(2013), 2.5); assertEquals(3, s1.getItemCount()); assertEquals(2.2, s1.getMinY(), EPSILON); assertEquals(3.3, s1.getMaxY(), EPSILON); } /** * Check that the item bounds are determined correctly after a call to * removeAgedItems(). */ @Test public void testRemoveAgedItems5() { TimeSeries s1 = new TimeSeries("S1"); s1.setMaximumItemAge(4); s1.add(new Year(2010), 1.1); s1.add(new Year(2011), 2.2); s1.add(new Year(2012), 3.3); s1.add(new Year(2013), 2.5); s1.removeAgedItems(new Year(2015).getMiddleMillisecond(), true); assertEquals(3, s1.getItemCount()); assertEquals(2.2, s1.getMinY(), EPSILON); assertEquals(3.3, s1.getMaxY(), EPSILON); } /** * Some simple checks for the hashCode() method. */ @Test public void testHashCode() { TimeSeries s1 = new TimeSeries("Test"); TimeSeries s2 = new TimeSeries("Test"); assertEquals(s1, s2); assertEquals(s1.hashCode(), s2.hashCode()); s1.add(new Day(1, 1, 2007), 500.0); s2.add(new Day(1, 1, 2007), 500.0); assertEquals(s1, s2); assertEquals(s1.hashCode(), s2.hashCode()); s1.add(new Day(2, 1, 2007), null); s2.add(new Day(2, 1, 2007), null); assertEquals(s1, s2); assertEquals(s1.hashCode(), s2.hashCode()); s1.add(new Day(5, 1, 2007), 111.0); s2.add(new Day(5, 1, 2007), 111.0); assertEquals(s1, s2); assertEquals(s1.hashCode(), s2.hashCode()); s1.add(new Day(9, 1, 2007), 1.0); s2.add(new Day(9, 1, 2007), 1.0); assertEquals(s1, s2); assertEquals(s1.hashCode(), s2.hashCode()); } /** * Test for bug report 1864222. */ @Test public void testBug1864222() { TimeSeries s = new TimeSeries("S"); s.add(new Day(19, 8, 2005), 1); s.add(new Day(31, 1, 2006), 1); boolean pass = true; try { s.createCopy(new Day(1, 12, 2005), new Day(18, 1, 2006)); } catch (CloneNotSupportedException e) { pass = false; } assertTrue(pass); } /** * Test for bug report 3446965. */ @Test public void testBug3446965() { TimeSeries s = new TimeSeries("s"); s.addOrUpdate(new Year(2011), 100.0); s.addOrUpdate(new Year(2012), 150.0); s.addOrUpdate(new Year(2013), 200.0); s.addOrUpdate(new Year(2012), 250.0); // this line triggers the defect assertEquals(100.0, s.getMinY(), EPSILON); assertEquals(250.0, s.getMaxY(), EPSILON); } private static final double EPSILON = 0.0000000001; /** * Some checks for the getMinY() method. */ @Test public void testGetMinY() { TimeSeries s1 = new TimeSeries("S1"); assertTrue(Double.isNaN(s1.getMinY())); s1.add(new Year(2008), 1.1); assertEquals(1.1, s1.getMinY(), EPSILON); s1.add(new Year(2009), 2.2); assertEquals(1.1, s1.getMinY(), EPSILON); s1.add(new Year(2000), 99.9); assertEquals(1.1, s1.getMinY(), EPSILON); s1.add(new Year(2002), -1.1); assertEquals(-1.1, s1.getMinY(), EPSILON); s1.add(new Year(2003), null); assertEquals(-1.1, s1.getMinY(), EPSILON); s1.addOrUpdate(new Year(2002), null); assertEquals(1.1, s1.getMinY(), EPSILON); } @Test public void testGetMinY2() { TimeSeries ts = new TimeSeries("Time Series"); assertTrue(Double.isNaN(ts.getMinY())); ts.add(new Year(2014), 1.0); assertEquals(1.0, ts.getMinY(), EPSILON); ts.addOrUpdate(new Year(2014), null); assertTrue(Double.isNaN(ts.getMinY())); ts.addOrUpdate(new Year(2014), 1.0); assertEquals(1.0, ts.getMinY(), EPSILON); ts.clear(); assertTrue(Double.isNaN(ts.getMinY())); } /** * Some checks for the getMaxY() method. */ @Test public void testGetMaxY() { TimeSeries s1 = new TimeSeries("S1"); assertTrue(Double.isNaN(s1.getMaxY())); s1.add(new Year(2008), 1.1); assertEquals(1.1, s1.getMaxY(), EPSILON); s1.add(new Year(2009), 2.2); assertEquals(2.2, s1.getMaxY(), EPSILON); s1.add(new Year(2000), 99.9); assertEquals(99.9, s1.getMaxY(), EPSILON); s1.add(new Year(2002), -1.1); assertEquals(99.9, s1.getMaxY(), EPSILON); s1.add(new Year(2003), null); assertEquals(99.9, s1.getMaxY(), EPSILON); s1.addOrUpdate(new Year(2000), null); assertEquals(2.2, s1.getMaxY(), EPSILON); } @Test public void testGetMaxY2() { TimeSeries ts = new TimeSeries("Time Series"); assertTrue(Double.isNaN(ts.getMaxY())); ts.add(new Year(2014), 1.0); assertEquals(1.0, ts.getMaxY(), EPSILON); ts.addOrUpdate(new Year(2014), null); assertTrue(Double.isNaN(ts.getMaxY())); ts.addOrUpdate(new Year(2014), 1.0); assertEquals(1.0, ts.getMaxY(), EPSILON); ts.clear(); assertTrue(Double.isNaN(ts.getMaxY())); } /** * A test for the clear method. */ @Test public void testClear() { TimeSeries s1 = new TimeSeries("S1"); s1.add(new Year(2009), 1.1); s1.add(new Year(2010), 2.2); assertEquals(2, s1.getItemCount()); s1.clear(); assertEquals(0, s1.getItemCount()); assertTrue(Double.isNaN(s1.getMinY())); assertTrue(Double.isNaN(s1.getMaxY())); } /** * Check that the item bounds are determined correctly when there is a * maximum item count and a new value is added. */ @Test public void testAdd() { TimeSeries s1 = new TimeSeries("S1"); s1.setMaximumItemCount(2); s1.add(new Year(2010), 1.1); s1.add(new Year(2011), 2.2); s1.add(new Year(2012), 3.3); assertEquals(2, s1.getItemCount()); assertEquals(2.2, s1.getMinY(), EPSILON); assertEquals(3.3, s1.getMaxY(), EPSILON); } /** * Some checks for the update(RegularTimePeriod...method). */ @Test public void testUpdate_RegularTimePeriod() { TimeSeries s1 = new TimeSeries("S1"); s1.add(new Year(2010), 1.1); s1.add(new Year(2011), 2.2); s1.add(new Year(2012), 3.3); s1.update(new Year(2012), 4.4); assertEquals(4.4, s1.getMaxY(), EPSILON); s1.update(new Year(2010), 0.5); assertEquals(0.5, s1.getMinY(), EPSILON); s1.update(new Year(2012), null); assertEquals(2.2, s1.getMaxY(), EPSILON); s1.update(new Year(2010), null); assertEquals(2.2, s1.getMinY(), EPSILON); } /** * Create a TimeSeriesDataItem, add it to a TimeSeries. Now, modifying * the original TimeSeriesDataItem should NOT affect the TimeSeries. */ @Test public void testAdd_TimeSeriesDataItem() { TimeSeriesDataItem item = new TimeSeriesDataItem(new Year(2009), 1.0); TimeSeries series = new TimeSeries("S1"); series.add(item); assertEquals(item, series.getDataItem(0)); item.setValue(99.9); assertNotEquals(item, series.getDataItem(0)); } @Test public void testSetKey() { TimeSeries s1 = new TimeSeries("S"); s1.setKey("S1"); assertEquals("S1", s1.getKey()); TimeSeriesCollection c = new TimeSeriesCollection(); c.addSeries(s1); TimeSeries s2 = new TimeSeries("S2"); c.addSeries(s2); // now we should be allowed to change s1's key to anything but "S2" s1.setKey("OK"); assertEquals("OK", s1.getKey()); try { s1.setKey("S2"); fail("Expect an exception here."); } catch (IllegalArgumentException e) { // OK } // after s1 is removed from the collection, we should be able to set // the key to anything we want... c.removeSeries(s1); s1.setKey("S2"); // check that removing by index also works s1.setKey("S1"); c.addSeries(s1); c.removeSeries(1); s1.setKey("S2"); } @Test public void testFindValueRange() { TimeSeries ts = new TimeSeries("Time Series"); assertNull(ts.findValueRange()); ts.add(new Year(2014), 1.0); assertEquals(new Range(1.0, 1.0), ts.findValueRange()); ts.add(new Year(2015), 2.0); assertEquals(new Range(1.0, 2.0), ts.findValueRange()); // null items are ignored ts.add(new Year(2016), null); assertEquals(new Range(1.0, 2.0), ts.findValueRange()); // Double.NaN values are also ignored ts.add(new Year(2017), Double.NaN); assertEquals(new Range(1.0, 2.0), ts.findValueRange()); ts.clear(); assertNull(ts.findValueRange()); // if there are only null items, we get a NaNRange ts.add(new Year(2014), null); assertTrue(ts.findValueRange().isNaNRange()); } /** * Tests for: * public Range findValueRange(Range, TimePeriodAnchor, TimeZone) */ @Test public void testFindValueRange2() { TimeZone tzone = TimeZone.getTimeZone("Europe/London"); Calendar calendar = new GregorianCalendar(tzone, Locale.UK); calendar.clear(); calendar.set(2014, Calendar.FEBRUARY, 23, 6, 0); long start = calendar.getTimeInMillis(); calendar.clear(); calendar.set(2014, Calendar.FEBRUARY, 24, 18, 0); long end = calendar.getTimeInMillis(); Range range = new Range(start, end); TimeSeries ts = new TimeSeries("Time Series"); assertNull(ts.findValueRange(range, TimePeriodAnchor.START, tzone)); assertNull(ts.findValueRange(range, TimePeriodAnchor.MIDDLE, tzone)); assertNull(ts.findValueRange(range, TimePeriodAnchor.END, tzone)); ts.add(new Day(23, 2, 2014), 5.0); assertTrue(ts.findValueRange(range, TimePeriodAnchor.START, tzone).isNaNRange()); assertEquals(new Range(5.0, 5.0), ts.findValueRange(range, TimePeriodAnchor.MIDDLE, tzone)); assertEquals(new Range(5.0, 5.0), ts.findValueRange(range, TimePeriodAnchor.END, tzone)); ts.add(new Day(24, 2, 2014), 6.0); assertEquals(new Range(6.0, 6.0), ts.findValueRange(range, TimePeriodAnchor.START, tzone)); assertEquals(new Range(5.0, 6.0), ts.findValueRange(range, TimePeriodAnchor.MIDDLE, tzone)); assertEquals(new Range(5.0, 5.0), ts.findValueRange(range, TimePeriodAnchor.END, tzone)); ts.clear(); ts.add(new Day(24, 2, 2014), null); assertTrue(ts.findValueRange(range, TimePeriodAnchor.START, tzone).isNaNRange()); assertTrue(ts.findValueRange(range, TimePeriodAnchor.MIDDLE, tzone).isNaNRange()); assertTrue(ts.findValueRange(range, TimePeriodAnchor.END, tzone).isNaNRange()); } /** * Test findValueRange() method when there are Double.NaN values present. */ @Test public void testFindValueRange3() { TimeZone tzone = TimeZone.getTimeZone("Europe/London"); Calendar calendar = new GregorianCalendar(tzone, Locale.UK); calendar.clear(); calendar.set(2015, Calendar.SEPTEMBER, 1, 6, 0); long start = calendar.getTimeInMillis(); calendar.clear(); calendar.set(2015, Calendar.SEPTEMBER, 30, 18, 0); long end = calendar.getTimeInMillis(); Range range = new Range(start, end); TimeSeries ts = new TimeSeries("Time Series"); assertNull(ts.findValueRange(range, TimePeriodAnchor.START, tzone)); assertNull(ts.findValueRange(range, TimePeriodAnchor.MIDDLE, tzone)); assertNull(ts.findValueRange(range, TimePeriodAnchor.END, tzone)); ts.add(new Day(1, 9, 2015), 1.0); ts.add(new Day(2, 9, 2015), 99.0); ts.add(new Day(30, 9, 2015), 2.0); assertEquals(new Range(2.0, 99.0), ts.findValueRange(range, TimePeriodAnchor.START, tzone)); assertEquals(new Range(1.0, 99.0), ts.findValueRange(range, TimePeriodAnchor.MIDDLE, tzone)); assertEquals(new Range(1.0, 99.0), ts.findValueRange(range, TimePeriodAnchor.END, tzone)); ts.add(new Day(10, 9, 2015), Double.NaN); assertEquals(new Range(2.0, 99.0), ts.findValueRange(range, TimePeriodAnchor.START, tzone)); assertEquals(new Range(1.0, 99.0), ts.findValueRange(range, TimePeriodAnchor.MIDDLE, tzone)); assertEquals(new Range(1.0, 99.0), ts.findValueRange(range, TimePeriodAnchor.END, tzone)); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/time/TimeTableXYDatasetTest.java000066400000000000000000000120501463604235500310150ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------- * TimeTableXYDatasetTests.java * ---------------------------- * (C) Copyright 2004-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): Rob Eden; * */ package org.jfree.data.time; import java.util.TimeZone; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * A collection of test cases for the {@link TimeTableXYDataset} class. */ public class TimeTableXYDatasetTest { private static final double DELTA = 0.0000000001; /** * Some checks for a simple dataset. */ @Test public void testStandard() { TimeTableXYDataset d = new TimeTableXYDataset(); d.add(new Year(1999), 1.0, "Series 1"); assertEquals(d.getItemCount(), 1); assertEquals(d.getSeriesCount(), 1); d.add(new Year(2000), 2.0, "Series 2"); assertEquals(d.getItemCount(), 2); assertEquals(d.getSeriesCount(), 2); assertEquals(d.getYValue(0, 0), 1.0, DELTA); assertTrue(Double.isNaN(d.getYValue(0, 1))); assertTrue(Double.isNaN(d.getYValue(1, 0))); assertEquals(d.getYValue(1, 1), 2.0, DELTA); } /** * Some checks for the getTimePeriod() method. */ @Test public void testGetTimePeriod() { TimeTableXYDataset d = new TimeTableXYDataset(); d.add(new Year(1999), 1.0, "Series 1"); d.add(new Year(1998), 2.0, "Series 1"); d.add(new Year(1996), 3.0, "Series 1"); assertEquals(d.getTimePeriod(0), new Year(1996)); assertEquals(d.getTimePeriod(1), new Year(1998)); assertEquals(d.getTimePeriod(2), new Year(1999)); } /** * Some checks for the equals() method. */ @Test public void testEquals() { TimeTableXYDataset d1 = new TimeTableXYDataset(); TimeTableXYDataset d2 = new TimeTableXYDataset(); assertEquals(d1, d2); assertEquals(d2, d1); d1.add(new Year(1999), 123.4, "S1"); assertNotEquals(d1, d2); d2.add(new Year(1999), 123.4, "S1"); assertEquals(d1, d2); d1.setDomainIsPointsInTime(!d1.getDomainIsPointsInTime()); assertNotEquals(d1, d2); d2.setDomainIsPointsInTime(!d2.getDomainIsPointsInTime()); assertEquals(d1, d2); d1 = new TimeTableXYDataset(TimeZone.getTimeZone("GMT")); d2 = new TimeTableXYDataset(TimeZone.getTimeZone( "America/Los_Angeles")); assertNotEquals(d1, d2); } /** * Some checks for cloning. */ @Test public void testClone() { TimeTableXYDataset d = new TimeTableXYDataset(); d.add(new Year(1999), 25.0, "Series"); TimeTableXYDataset clone = null; try { clone = (TimeTableXYDataset) d.clone(); } catch (CloneNotSupportedException e) { fail(); } assertEquals(clone, d); // now test that the clone is independent of the original clone.add(new Year(2004), 1.2, "SS"); assertNotEquals(clone, d); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { TimeTableXYDataset d1 = new TimeTableXYDataset(); d1.add(new Year(1999), 123.4, "S1"); TimeTableXYDataset d2 = TestUtils.serialised(d1); assertEquals(d1, d2); } /** * Test clearing data. */ @Test public void testClear() { TimeTableXYDataset d = new TimeTableXYDataset(); d.add(new Year(1999), 1.0, "Series 1"); assertEquals(d.getItemCount(), 1); assertEquals(d.getSeriesCount(), 1); d.add(new Year(2000), 2.0, "Series 2"); d.clear(); // Make sure there's nothing left assertEquals(0, d.getItemCount()); assertEquals(0, d.getSeriesCount()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/time/WeekTest.java000066400000000000000000000626351463604235500262710ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------- * WeekTest.java * ------------- * (C) Copyright 2002-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import java.time.ZoneOffset; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.Locale; import java.util.TimeZone; import java.util.function.BiConsumer; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link Week} class. */ public class WeekTest { /** A week. */ private Week w1Y1900; /** A week. */ private Week w2Y1900; /** A week. */ private Week w51Y9999; /** A week. */ private Week w52Y9999; /** * Common test setup. */ @BeforeEach public void setUp() { this.w1Y1900 = new Week(1, 1900); this.w2Y1900 = new Week(2, 1900); this.w51Y9999 = new Week(51, 9999); this.w52Y9999 = new Week(52, 9999); } /** * Test for bug reported via pull request #138. */ @Test public void testConstructorArgumentChecks() { Exception exception = assertThrows(IllegalArgumentException.class, () -> new Week(0, 2020)); assertTrue(exception.getMessage().contains("week")); Exception exception2 = assertThrows(IllegalArgumentException.class, () -> new Week(54, 2020)); assertTrue(exception2.getMessage().contains("week")); Exception exception3 = assertThrows(IllegalArgumentException.class, () -> new Week(0, new Year(2020))); assertTrue(exception3.getMessage().contains("week")); Exception exception4 = assertThrows(IllegalArgumentException.class, () -> new Week(54, new Year(2020))); assertTrue(exception2.getMessage().contains("week")); } /** * Tests the equals method. */ @Test public void testEquals() { Week w1 = new Week(1, 2002); Week w2 = new Week(1, 2002); assertEquals(w1, w2); assertEquals(w2, w1); w1 = new Week(2, 2002); assertNotEquals(w1, w2); w2 = new Week(2, 2002); assertEquals(w1, w2); w1 = new Week(2, 2003); assertNotEquals(w1, w2); w2 = new Week(2, 2003); assertEquals(w1, w2); } /** * Request the week before week 1, 1900: it should be {@code null}. */ @Test public void testW1Y1900Previous() { Week previous = (Week) this.w1Y1900.previous(); assertNull(previous); } /** * Request the week after week 1, 1900: it should be week 2, 1900. */ @Test public void testW1Y1900Next() { Week next = (Week) this.w1Y1900.next(); assertEquals(this.w2Y1900, next); } /** * Request the week before w52, 9999: it should be week 51, 9999. */ @Test public void testW52Y9999Previous() { Week previous = (Week) this.w52Y9999.previous(); assertEquals(this.w51Y9999, previous); } /** * Request the week after w52, 9999: it should be {@code null}. */ @Test public void testW52Y9999Next() { Week next = (Week) this.w52Y9999.next(); assertNull(next); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { Week w1 = new Week(24, 1999); Week w2 = TestUtils.serialised(w1); assertEquals(w1, w2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { Week w1 = new Week(2, 2003); Week w2 = new Week(2, 2003); assertEquals(w1, w2); int h1 = w1.hashCode(); int h2 = w2.hashCode(); assertEquals(h1, h2); } /** * The {@link Week} class is immutable, so should not be {@link Cloneable}. */ @Test public void testNotCloneable() { Week w = new Week(1, 1999); assertFalse(w instanceof Cloneable); } /** * The first week in 2005 should span the range: * * TimeZone | Start Millis | End Millis | Start Date | End Date * -----------------+---------------+---------------+-------------+------------ * Europe/London | 1104710400000 | 1105315199999 | 3-Jan-2005 | 9-Jan-2005 * Europe/Paris | 1104706800000 | 1105311599999 | 3-Jan-2005 | 2-Jan-2005 * America/New_York | 1104037200000 | 1104641999999 | 26-Dec-2004 | 1-Jan-2005 * * In London and Paris, Monday is the first day of the week, while in the * US it is Sunday. * * Previously, we were using these values, but see Java Bug ID 4960215: * * TimeZone | Start Millis | End Millis | Start Date | End Date * -----------------+---------------+---------------+-------------+------------ * Europe/London | 1104105600000 | 1104710399999 | 27-Dec-2004 | 2-Jan-2005 * Europe/Paris | 1104102000000 | 1104706799999 | 27-Dec-2004 | 2-Jan-2005 * America/New_York | 1104037200000 | 1104641999999 | 26-Dec-2004 | 1-Jan-2005 */ @Test public void testWeek12005() { Week w1 = new Week(1, 2005); Calendar c1 = Calendar.getInstance( TimeZone.getTimeZone("Europe/London"), Locale.UK); c1.setMinimalDaysInFirstWeek(4); // see Java Bug ID 4960215 assertEquals(1104710400000L, w1.getFirstMillisecond(c1)); assertEquals(1105315199999L, w1.getLastMillisecond(c1)); Calendar c2 = Calendar.getInstance( TimeZone.getTimeZone("Europe/Paris"), Locale.FRANCE); c2.setMinimalDaysInFirstWeek(4); // see Java Bug ID 4960215 assertEquals(1104706800000L, w1.getFirstMillisecond(c2)); assertEquals(1105311599999L, w1.getLastMillisecond(c2)); Calendar c3 = Calendar.getInstance( TimeZone.getTimeZone("America/New_York"), Locale.US); assertEquals(1104037200000L, w1.getFirstMillisecond(c3)); assertEquals(1104641999999L, w1.getLastMillisecond(c3)); } /** * The 53rd week in 2004 in London and Paris should span the range: * * TimeZone | Start Millis | End Millis | Start Date | End Date * -----------------+---------------+---------------+-------------+------------ * Europe/London | 1104105600000 | 1104710399999 | 27-Dec-2004 | 02-Jan-2005 * Europe/Paris | 1104102000000 | 1104706799999 | 27-Dec-2004 | 02-Jan-2005 * * The 53rd week in 2005 in New York should span the range: * * TimeZone | Start Millis | End Millis | Start Date | End Date * -----------------+---------------+---------------+-------------+------------ * America/New_York | 1135486800000 | 1136091599999 | 25-Dec-2005 | 31-Dec-2005 * * In London and Paris, Monday is the first day of the week, while in the * US it is Sunday. */ @Test public void testWeek532005() { Week w1 = new Week(53, 2004); Calendar c1 = Calendar.getInstance( TimeZone.getTimeZone("Europe/London"), Locale.UK); c1.setMinimalDaysInFirstWeek(4); // see Java Bug ID 4960215 assertEquals(1104105600000L, w1.getFirstMillisecond(c1)); assertEquals(1104710399999L, w1.getLastMillisecond(c1)); Calendar c2 = Calendar.getInstance( TimeZone.getTimeZone("Europe/Paris"), Locale.FRANCE); c2.setMinimalDaysInFirstWeek(4); // see Java Bug ID 4960215 assertEquals(1104102000000L, w1.getFirstMillisecond(c2)); assertEquals(1104706799999L, w1.getLastMillisecond(c2)); w1 = new Week(53, 2005); Calendar c3 = Calendar.getInstance( TimeZone.getTimeZone("America/New_York"), Locale.US); assertEquals(1135486800000L, w1.getFirstMillisecond(c3)); assertEquals(1136091599999L, w1.getLastMillisecond(c3)); } /** * A test case for bug 1448828. */ @Test public void testBug1448828() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.UK); try { Week w = new Week(new Date(1136109830000L), TimeZone.getTimeZone("GMT"), Locale.UK); assertEquals(2005, w.getYearValue()); assertEquals(52, w.getWeek()); } finally { Locale.setDefault(saved); } } /** * A test case for bug 1498805. */ @Test public void testBug1498805() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.UK); try { TimeZone zone = TimeZone.getTimeZone("GMT"); GregorianCalendar gc = new GregorianCalendar(zone); gc.set(2005, Calendar.JANUARY, 1, 12, 0, 0); Week w = new Week(gc.getTime(), zone, Locale.UK); assertEquals(53, w.getWeek()); assertEquals(new Year(2004), w.getYear()); } finally { Locale.setDefault(saved); } } /** * Some checks for the getFirstMillisecond() method. */ @Test public void testGetFirstMillisecond() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.UK); TimeZone savedZone = TimeZone.getDefault(); TimeZone.setDefault(TimeZone.getTimeZone("Europe/London")); Week w = new Week(3, 1970); assertEquals(946800000L, w.getFirstMillisecond()); Locale.setDefault(saved); TimeZone.setDefault(savedZone); } /** * Some checks for the getFirstMillisecond(TimeZone) method. */ @Test public void testGetFirstMillisecondWithTimeZone() { Week w = new Week(47, 1950); Locale saved = Locale.getDefault(); Locale.setDefault(Locale.US); try { TimeZone zone = TimeZone.getTimeZone("America/Los_Angeles"); Calendar cal = Calendar.getInstance(zone); assertEquals(-603302400000L, w.getFirstMillisecond(cal)); } finally { Locale.setDefault(saved); } // try null calendar boolean pass = false; try { w.getFirstMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getFirstMillisecond(TimeZone) method. */ @Test public void testGetFirstMillisecondWithCalendar() { Week w = new Week(1, 2001); GregorianCalendar calendar = new GregorianCalendar(Locale.GERMANY); calendar.setTimeZone(TimeZone.getTimeZone("Europe/Frankfurt")); assertEquals(978307200000L, w.getFirstMillisecond(calendar)); // try null calendar boolean pass = false; try { w.getFirstMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getLastMillisecond() method. */ @Test public void testGetLastMillisecond() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.UK); TimeZone savedZone = TimeZone.getDefault(); TimeZone.setDefault(TimeZone.getTimeZone("Europe/London")); Week w = new Week(31, 1970); assertEquals(18485999999L, w.getLastMillisecond()); Locale.setDefault(saved); TimeZone.setDefault(savedZone); } /** * Some checks for the getLastMillisecond(TimeZone) method. */ @Test public void testGetLastMillisecondWithTimeZone() { Week w = new Week(2, 1950); Locale saved = Locale.getDefault(); Locale.setDefault(Locale.US); try { TimeZone zone = TimeZone.getTimeZone("America/Los_Angeles"); Calendar cal = Calendar.getInstance(zone); assertEquals(-629913600001L, w.getLastMillisecond(cal)); } finally { Locale.setDefault(saved); } // try null zone boolean pass = false; try { w.getLastMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getLastMillisecond(TimeZone) method. */ @Test public void testGetLastMillisecondWithCalendar() { Week w = new Week(52, 2001); GregorianCalendar calendar = new GregorianCalendar(Locale.GERMANY); calendar.setTimeZone(TimeZone.getTimeZone("Europe/Frankfurt")); assertEquals(1009756799999L, w.getLastMillisecond(calendar)); // try null calendar boolean pass = false; try { w.getLastMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getSerialIndex() method. */ @Test public void testGetSerialIndex() { Week w = new Week(1, 2000); assertEquals(106001L, w.getSerialIndex()); w = new Week(1, 1900); assertEquals(100701L, w.getSerialIndex()); } /** * Some checks for the testNext() method. */ @Test public void testNext() { Week w = new Week(12, 2000); w = (Week) w.next(); assertEquals(new Year(2000), w.getYear()); assertEquals(13, w.getWeek()); w = new Week(53, 9999); assertNull(w.next()); } /** * If a thread-local calendar was set, next() should use its time zone. */ @Test public void testNextWithThreadLocalCalendar() { BiConsumer calendarSetup = (hours, locale) -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours)), Locale.forLanguageTag(locale)) ); testNextWithCustomCalendar(3, "ru-RU", 4, calendarSetup); testNextWithCustomCalendar(-6, "en-US", 3, calendarSetup); } /** * If a calendar prototype was set, next() should use its time zone. */ @Test public void testNextWithCalendarPrototype() { BiConsumer calendarSetup = (hours, locale) -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours)), Locale.forLanguageTag(locale)) ); testNextWithCustomCalendar(3, "ru-RU", 4, calendarSetup); testNextWithCustomCalendar(-6, "en-US", 3, calendarSetup); } private void testNextWithCustomCalendar(int hoursOffset, String locale, int secondWeekOffsetInDays, BiConsumer calendarSetup) { try { calendarSetup.accept(hoursOffset, locale); long ms = secondWeekOffsetInDays * 86_400_000L - hoursOffset * 3_600_000L; Week w = new Week(new Date(ms)); w = (Week) w.next(); assertEquals(1970, w.getYear().getYear()); assertEquals(3, w.getWeek()); assertEquals(ms + 86_400_000L * 7, w.getFirstMillisecond()); } finally { // reset everything, to avoid affecting other tests RegularTimePeriod.setThreadLocalCalendarInstance(null); RegularTimePeriod.setCalendarInstancePrototype(null); } } /** * Some checks for the getStart() method. */ @Test public void testGetStart() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.ITALY); Calendar cal = Calendar.getInstance(Locale.ITALY); cal.set(2006, Calendar.JANUARY, 16, 0, 0, 0); cal.set(Calendar.MILLISECOND, 0); Week w = new Week(3, 2006); assertEquals(cal.getTime(), w.getStart()); Locale.setDefault(saved); } /** * Some checks for the getEnd() method. */ @Test public void testGetEnd() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.ITALY); Calendar cal = Calendar.getInstance(Locale.ITALY); cal.set(2006, Calendar.JANUARY, 8, 23, 59, 59); cal.set(Calendar.MILLISECOND, 999); Week w = new Week(1, 2006); assertEquals(cal.getTime(), w.getEnd()); Locale.setDefault(saved); } /** * A test for a problem in constructing a new Week instance. */ @Test public void testConstructor() { Locale savedLocale = Locale.getDefault(); TimeZone savedZone = TimeZone.getDefault(); Locale.setDefault(new Locale("da", "DK")); TimeZone.setDefault(TimeZone.getTimeZone("Europe/Copenhagen")); GregorianCalendar cal = (GregorianCalendar) Calendar.getInstance( TimeZone.getDefault(), Locale.getDefault()); // first day of week is monday assertEquals(Calendar.MONDAY, cal.getFirstDayOfWeek()); cal.set(2007, Calendar.AUGUST, 26, 1, 0, 0); cal.set(Calendar.MILLISECOND, 0); Date t = cal.getTime(); Week w = new Week(t, TimeZone.getTimeZone("Europe/Copenhagen"), Locale.getDefault()); assertEquals(34, w.getWeek()); Locale.setDefault(Locale.US); TimeZone.setDefault(TimeZone.getTimeZone("US/Detroit")); cal = (GregorianCalendar) Calendar.getInstance(TimeZone.getDefault()); // first day of week is Sunday assertEquals(Calendar.SUNDAY, cal.getFirstDayOfWeek()); cal.set(2007, Calendar.AUGUST, 26, 1, 0, 0); cal.set(Calendar.MILLISECOND, 0); t = cal.getTime(); w = new Week(t, TimeZone.getTimeZone("Europe/Copenhagen"), Locale.getDefault()); assertEquals(35, w.getWeek()); w = new Week(t, TimeZone.getTimeZone("Europe/Copenhagen"), new Locale("da", "DK")); assertEquals(34, w.getWeek()); Locale.setDefault(savedLocale); TimeZone.setDefault(savedZone); } /** * If a thread-local calendar was set, the Date constructor should use it. */ @Test public void testDateConstructorWithThreadLocalCalendar() { BiConsumer calendarSetup = (hours, locale) -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours)), Locale.forLanguageTag(locale)) ); testDateConstructorWithCustomCalendar(3, "ru-RU", 4, calendarSetup); testDateConstructorWithCustomCalendar(-6, "en-US", 3, calendarSetup); } /** * If a calendar prototype was set, the Date constructor should use it. */ @Test public void testDateConstructorWithCalendarPrototype() { BiConsumer calendarSetup = (hours, locale) -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours)), Locale.forLanguageTag(locale)) ); testDateConstructorWithCustomCalendar(3, "ru-RU", 4, calendarSetup); testDateConstructorWithCustomCalendar(-6, "en-US", 3, calendarSetup); } private void testDateConstructorWithCustomCalendar(int hoursOffset, String locale, int secondWeekOffsetInDays, BiConsumer calendarSetup) { try { calendarSetup.accept(hoursOffset, locale); long ms = secondWeekOffsetInDays * 86_400_000L - 3_600_000L * hoursOffset; Week w = new Week(new Date(ms)); assertEquals(1970, w.getYear().getYear()); assertEquals(2, w.getWeek()); assertEquals(ms, w.getFirstMillisecond()); } finally { // reset everything, to avoid affecting other tests RegularTimePeriod.setThreadLocalCalendarInstance(null); RegularTimePeriod.setCalendarInstancePrototype(null); } } /** * If a thread-local calendar was set, the (int, int) week-year constructor should use it. */ @Test public void testWeekIntYearConstructorWithThreadLocalCalendar() { BiConsumer calendarSetup = (hours, locale) -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours)), Locale.forLanguageTag(locale)) ); testWeekIntYearConstructorWithCustomCalendar(3, "ru-RU", 4, calendarSetup); testWeekIntYearConstructorWithCustomCalendar(-6, "en-US", 3, calendarSetup); } /** * If a calendar prototype was set, the (int, int) week-year constructor should use it. */ @Test public void testWeekIntYearConstructorWithCalendarPrototype() { BiConsumer calendarSetup = (hours, locale) -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours)), Locale.forLanguageTag(locale)) ); testWeekIntYearConstructorWithCustomCalendar(3, "ru-RU", 4, calendarSetup); testWeekIntYearConstructorWithCustomCalendar(-6, "en-US", 3, calendarSetup); } private void testWeekIntYearConstructorWithCustomCalendar(int hoursOffset, String locale, int secondWeekOffsetInDays, BiConsumer calendarSetup) { try { calendarSetup.accept(hoursOffset, locale); long ms = secondWeekOffsetInDays * 86_400_000L - 3_600_000L * hoursOffset; Week w = new Week(2, 1970); assertEquals(1970, w.getYear().getYear()); assertEquals(2, w.getWeek()); assertEquals(ms, w.getFirstMillisecond()); } finally { // reset everything, to avoid affecting other tests RegularTimePeriod.setThreadLocalCalendarInstance(null); RegularTimePeriod.setCalendarInstancePrototype(null); } } /** * If a thread-local calendar was set, the (int, Year) week-year constructor should use it. */ @Test public void testWeekYearConstructorWithThreadLocalCalendar() { BiConsumer calendarSetup = (hours, locale) -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours)), Locale.forLanguageTag(locale)) ); testWeekYearConstructorWithCustomCalendar(3, "ru-RU", 4, calendarSetup); testWeekYearConstructorWithCustomCalendar(-6, "en-US", 3, calendarSetup); } /** * If a calendar prototype was set, the (int, Year) week-year constructor should use it. */ @Test public void testWeekYearConstructorWithCalendarPrototype() { BiConsumer calendarSetup = (hours, locale) -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours)), Locale.forLanguageTag(locale)) ); testWeekYearConstructorWithCustomCalendar(3, "ru-RU", 4, calendarSetup); testWeekYearConstructorWithCustomCalendar(-6, "en-US", 3, calendarSetup); } private void testWeekYearConstructorWithCustomCalendar(int hoursOffset, String locale, int secondWeekOffsetInDays, BiConsumer calendarSetup) { try { calendarSetup.accept(hoursOffset, locale); long ms = secondWeekOffsetInDays * 86_400_000L - 3_600_000L * hoursOffset; Week w = new Week(2, new Year(1970)); assertEquals(1970, w.getYear().getYear()); assertEquals(2, w.getWeek()); assertEquals(ms, w.getFirstMillisecond()); } finally { // reset everything, to avoid affecting other tests RegularTimePeriod.setThreadLocalCalendarInstance(null); RegularTimePeriod.setCalendarInstancePrototype(null); } } @Test public void testBug134() { boolean pass = false; try { Week w = new Week(0, 2020); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); pass = false; try { Week w = new Week(54, 2020); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/time/YearTest.java000066400000000000000000000376161463604235500262770ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------- * YearTest.java * ------------- * (C) Copyright 2001-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time; import java.time.ZoneOffset; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.Locale; import java.util.TimeZone; import java.util.function.Consumer; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link Year} class. */ public class YearTest { /** * Check that a Year instance is equal to itself. * * SourceForge Bug ID: 558850. */ @Test public void testEqualsSelf() { Year year = new Year(); assertEquals(year, year); } /** * Tests the equals method. */ @Test public void testEquals() { Year year1 = new Year(2002); Year year2 = new Year(2002); assertEquals(year1, year2); year1 = new Year(1999); assertNotEquals(year1, year2); year2 = new Year(1999); assertEquals(year1, year2); } /** * In GMT, the end of 2001 is java.util.Date(1009843199999L). Use this to * check the year constructor. */ @Test public void testDateConstructor1() { TimeZone zone = TimeZone.getTimeZone("GMT"); Calendar cal = Calendar.getInstance(zone); Date d1 = new Date(1009843199999L); Date d2 = new Date(1009843200000L); Year y1 = new Year(d1, zone, Locale.getDefault()); Year y2 = new Year(d2, zone, Locale.getDefault()); assertEquals(2001, y1.getYear()); assertEquals(1009843199999L, y1.getLastMillisecond(cal)); assertEquals(2002, y2.getYear()); assertEquals(1009843200000L, y2.getFirstMillisecond(cal)); } /** * In Los Angeles, the end of 2001 is java.util.Date(1009871999999L). Use * this to check the year constructor. */ @Test public void testDateConstructor2() { TimeZone zone = TimeZone.getTimeZone("America/Los_Angeles"); Calendar cal = Calendar.getInstance(zone); Year y1 = new Year(new Date(1009871999999L), zone, Locale.getDefault()); Year y2 = new Year(new Date(1009872000000L), zone, Locale.getDefault()); assertEquals(2001, y1.getYear()); assertEquals(1009871999999L, y1.getLastMillisecond(cal)); assertEquals(2002, y2.getYear()); assertEquals(1009872000000L, y2.getFirstMillisecond(cal)); } /** * If a thread-local calendar was set, the Date constructor should use it. */ @Test public void testDateConstructorWithThreadLocalCalendar() { Consumer calendarSetup = hours -> RegularTimePeriod.setThreadLocalCalendarInstance( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testDateConstructorWithCustomCalendar(3, calendarSetup); testDateConstructorWithCustomCalendar(4, calendarSetup); } /** * If a calendar prototype was set, the Date constructor should use it. */ @Test public void testDateConstructorWithCalendarPrototype() { Consumer calendarSetup = hours -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testDateConstructorWithCustomCalendar(3, calendarSetup); testDateConstructorWithCustomCalendar(4, calendarSetup); } private void testDateConstructorWithCustomCalendar(int hoursOffset, Consumer calendarSetup) { try { calendarSetup.accept(hoursOffset); long ms = -3_600_000L * hoursOffset; Year y = new Year(new Date(ms)); assertEquals(1970, y.getYear()); assertEquals(ms, y.getFirstMillisecond()); } finally { // reset everything, to avoid affecting other tests RegularTimePeriod.setThreadLocalCalendarInstance(null); RegularTimePeriod.setCalendarInstancePrototype(null); } } /** * If a thread-local calendar was set, the year constructor should use it. */ @Test public void testYearConstructorWithThreadLocalCalendar() { Consumer calendarSetup = hours -> RegularTimePeriod.setThreadLocalCalendarInstance( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testYearConstructorWithCustomCalendar(3, calendarSetup); testYearConstructorWithCustomCalendar(4, calendarSetup); } /** * If a calendar prototype was set, the year constructor should use it. */ @Test public void testYearConstructorWithCalendarPrototype() { Consumer calendarSetup = hours -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testYearConstructorWithCustomCalendar(3, calendarSetup); testYearConstructorWithCustomCalendar(4, calendarSetup); } private void testYearConstructorWithCustomCalendar(int hoursOffset, Consumer calendarSetup) { try { calendarSetup.accept(hoursOffset); long ms = -3_600_000L * hoursOffset; Year y = new Year(1970); assertEquals(1970, y.getYear()); assertEquals(ms, y.getFirstMillisecond()); } finally { // reset everything, to avoid affecting other tests RegularTimePeriod.setThreadLocalCalendarInstance(null); RegularTimePeriod.setCalendarInstancePrototype(null); } } /** * Set up a year equal to 1900. Request the previous year, it should be * null. */ @Test public void testMinuss9999Previous() { Year current = new Year(-9999); Year previous = (Year) current.previous(); assertNull(previous); } /** * Set up a year equal to 1900. Request the next year, it should be 1901. */ @Test public void test1900Next() { Year current = new Year(1900); Year next = (Year) current.next(); assertEquals(1901, next.getYear()); } /** * Set up a year equal to 9999. Request the previous year, it should be * 9998. */ @Test public void test9999Previous() { Year current = new Year(9999); Year previous = (Year) current.previous(); assertEquals(9998, previous.getYear()); } /** * Set up a year equal to 9999. Request the next year, it should be null. */ @Test public void test9999Next() { Year current = new Year(9999); Year next = (Year) current.next(); assertNull(next); } /** * Tests the year string parser. */ @Test public void testParseYear() { Year year = null; // test 1... try { year = Year.parseYear("2000"); } catch (TimePeriodFormatException e) { year = new Year(1900); } assertEquals(2000, year.getYear()); // test 2... try { year = Year.parseYear(" 2001 "); } catch (TimePeriodFormatException e) { year = new Year(1900); } assertEquals(2001, year.getYear()); // test 3... try { year = Year.parseYear("99"); } catch (TimePeriodFormatException e) { year = new Year(1900); } assertEquals(99, year.getYear()); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { Year y1 = new Year(1999); Year y2 = TestUtils.serialised(y1); assertEquals(y1, y2); } /** * The {@link Year} class is immutable, so should not be {@link Cloneable}. */ @Test public void testNotCloneable() { Year y = new Year(1999); assertFalse(y instanceof Cloneable); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { Year y1 = new Year(1988); Year y2 = new Year(1988); assertEquals(y1, y2); int h1 = y1.hashCode(); int h2 = y2.hashCode(); assertEquals(h1, h2); } /** * Some checks for the getFirstMillisecond() method. */ @Test public void testGetFirstMillisecond() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.UK); TimeZone savedZone = TimeZone.getDefault(); TimeZone.setDefault(TimeZone.getTimeZone("Europe/London")); Year y = new Year(1970); // TODO: Check this result... assertEquals(-3600000L, y.getFirstMillisecond()); Locale.setDefault(saved); TimeZone.setDefault(savedZone); } /** * Some checks for the getFirstMillisecond(TimeZone) method. */ @Test public void testGetFirstMillisecondWithTimeZone() { Year y = new Year(1950); TimeZone zone = TimeZone.getTimeZone("America/Los_Angeles"); Calendar cal = Calendar.getInstance(zone); assertEquals(-631123200000L, y.getFirstMillisecond(cal)); // try null calendar boolean pass = false; try { y.getFirstMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getFirstMillisecond(TimeZone) method. */ @Test public void testGetFirstMillisecondWithCalendar() { Year y = new Year(2001); GregorianCalendar calendar = new GregorianCalendar(Locale.GERMANY); calendar.setTimeZone(TimeZone.getTimeZone("Europe/Frankfurt")); assertEquals(978307200000L, y.getFirstMillisecond(calendar)); // try null calendar boolean pass = false; try { y.getFirstMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getLastMillisecond() method. */ @Test public void testGetLastMillisecond() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.UK); TimeZone savedZone = TimeZone.getDefault(); TimeZone.setDefault(TimeZone.getTimeZone("Europe/London")); Year y = new Year(1970); // TODO: Check this result... assertEquals(31532399999L, y.getLastMillisecond()); Locale.setDefault(saved); TimeZone.setDefault(savedZone); } /** * Some checks for the getLastMillisecond(TimeZone) method. */ @Test public void testGetLastMillisecondWithTimeZone() { Year y = new Year(1950); TimeZone zone = TimeZone.getTimeZone("America/Los_Angeles"); Calendar cal = Calendar.getInstance(zone); assertEquals(-599587200001L, y.getLastMillisecond(cal)); // try null calendar boolean pass = false; try { y.getLastMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getLastMillisecond(TimeZone) method. */ @Test public void testGetLastMillisecondWithCalendar() { Year y = new Year(2001); GregorianCalendar calendar = new GregorianCalendar(Locale.GERMANY); calendar.setTimeZone(TimeZone.getTimeZone("Europe/Frankfurt")); assertEquals(1009843199999L, y.getLastMillisecond(calendar)); // try null calendar boolean pass = false; try { y.getLastMillisecond((Calendar) null); } catch (NullPointerException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getSerialIndex() method. */ @Test public void testGetSerialIndex() { Year y = new Year(2000); assertEquals(2000L, y.getSerialIndex()); } /** * Some checks for the testNext() method. */ @Test public void testNext() { Year y = new Year(2000); y = (Year) y.next(); assertEquals(2001, y.getYear()); y = new Year(9999); assertNull(y.next()); } /** * If a thread-local calendar was set, next() should use its time zone. */ @Test public void testNextWithThreadLocalCalendar() { Consumer calendarSetup = hours -> RegularTimePeriod.setThreadLocalCalendarInstance( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testNextWithCustomCalendar(3, calendarSetup); testNextWithCustomCalendar(4, calendarSetup); } /** * If a calendar prototype was set, next() should use its time zone. */ @Test public void testNextWithCalendarPrototype() { Consumer calendarSetup = hours -> RegularTimePeriod.setCalendarInstancePrototype( Calendar.getInstance(TimeZone.getTimeZone(ZoneOffset.ofHours(hours))) ); testNextWithCustomCalendar(3, calendarSetup); testNextWithCustomCalendar(4, calendarSetup); } private void testNextWithCustomCalendar(int hoursOffset, Consumer calendarSetup) { try { calendarSetup.accept(hoursOffset); long ms = -hoursOffset * 3_600_000L; Year y = new Year(new Date(ms)); y = (Year) y.next(); assertEquals(1971, y.getYear()); assertEquals(ms + 86_400_000L * 365, y.getFirstMillisecond()); } finally { // reset everything, to avoid affecting other tests RegularTimePeriod.setThreadLocalCalendarInstance(null); RegularTimePeriod.setCalendarInstancePrototype(null); } } /** * Some checks for the getStart() method. */ @Test public void testGetStart() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.ITALY); Calendar cal = Calendar.getInstance(Locale.ITALY); cal.set(2006, Calendar.JANUARY, 1, 0, 0, 0); cal.set(Calendar.MILLISECOND, 0); Year y = new Year(2006); assertEquals(cal.getTime(), y.getStart()); Locale.setDefault(saved); } /** * Some checks for the getEnd() method. */ @Test public void testGetEnd() { Locale saved = Locale.getDefault(); Locale.setDefault(Locale.ITALY); Calendar cal = Calendar.getInstance(Locale.ITALY); cal.set(2006, Calendar.DECEMBER, 31, 23, 59, 59); cal.set(Calendar.MILLISECOND, 999); Year y = new Year(2006); assertEquals(cal.getTime(), y.getEnd()); Locale.setDefault(saved); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/time/ohlc/000077500000000000000000000000001463604235500246045ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/time/ohlc/OHLCItemTest.java000066400000000000000000000113171463604235500276560ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * OHLCItemTest.java * ----------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time.ohlc; import org.jfree.chart.TestUtils; import org.jfree.data.time.Year; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link OHLCItem} class. */ public class OHLCItemTest { private static final double EPSILON = 0.00000000001; /** * Some checks for the constructor. */ @Test public void testConstructor1() { OHLCItem item1 = new OHLCItem(new Year(2006), 2.0, 4.0, 1.0, 3.0); assertEquals(new Year(2006), item1.getPeriod()); assertEquals(2.0, item1.getOpenValue(), EPSILON); assertEquals(4.0, item1.getHighValue(), EPSILON); assertEquals(1.0, item1.getLowValue(), EPSILON); assertEquals(3.0, item1.getCloseValue(), EPSILON); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { OHLCItem item1 = new OHLCItem(new Year(2006), 2.0, 4.0, 1.0, 3.0); OHLCItem item2 = new OHLCItem(new Year(2006), 2.0, 4.0, 1.0, 3.0); assertEquals(item1, item2); assertEquals(item2, item1); // period item1 = new OHLCItem(new Year(2007), 2.0, 4.0, 1.0, 3.0); assertNotEquals(item1, item2); item2 = new OHLCItem(new Year(2007), 2.0, 4.0, 1.0, 3.0); assertEquals(item1, item2); // open item1 = new OHLCItem(new Year(2007), 2.2, 4.0, 1.0, 3.0); assertNotEquals(item1, item2); item2 = new OHLCItem(new Year(2007), 2.2, 4.0, 1.0, 3.0); assertEquals(item1, item2); // high item1 = new OHLCItem(new Year(2007), 2.2, 4.4, 1.0, 3.0); assertNotEquals(item1, item2); item2 = new OHLCItem(new Year(2007), 2.2, 4.4, 1.0, 3.0); assertEquals(item1, item2); // low item1 = new OHLCItem(new Year(2007), 2.2, 4.4, 1.1, 3.0); assertNotEquals(item1, item2); item2 = new OHLCItem(new Year(2007), 2.2, 4.4, 1.1, 3.0); assertEquals(item1, item2); // close item1 = new OHLCItem(new Year(2007), 2.2, 4.4, 1.1, 3.3); assertNotEquals(item1, item2); item2 = new OHLCItem(new Year(2007), 2.2, 4.4, 1.1, 3.3); assertEquals(item1, item2); } /** * Some checks for the clone() method. */ @Test public void testCloning() throws CloneNotSupportedException { OHLCItem item1 = new OHLCItem(new Year(2006), 2.0, 4.0, 1.0, 3.0); OHLCItem item2 = (OHLCItem) item1.clone(); assertNotSame(item1, item2); assertSame(item1.getClass(), item2.getClass()); assertEquals(item1, item2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { OHLCItem item1 = new OHLCItem(new Year(2006), 2.0, 4.0, 1.0, 3.0); OHLCItem item2 = TestUtils.serialised(item1); assertEquals(item1, item2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { OHLCItem i1 = new OHLCItem(new Year(2009), 2.0, 4.0, 1.0, 3.0); OHLCItem i2 = new OHLCItem(new Year(2009), 2.0, 4.0, 1.0, 3.0); assertEquals(i1, i2); int h1 = i1.hashCode(); int h2 = i2.hashCode(); assertEquals(h1, h2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/time/ohlc/OHLCSeriesCollectionTest.java000066400000000000000000000167141463604235500322340ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------- * OHLCSeriesCollectionTest.java * ----------------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time.ohlc; import org.jfree.chart.TestUtils; import org.jfree.data.general.DatasetChangeEvent; import org.jfree.data.general.DatasetChangeListener; import org.jfree.data.time.TimePeriodAnchor; import org.jfree.data.time.Year; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link OHLCSeriesCollection} class. */ public class OHLCSeriesCollectionTest implements DatasetChangeListener { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { OHLCSeriesCollection c1 = new OHLCSeriesCollection(); OHLCSeriesCollection c2 = new OHLCSeriesCollection(); assertEquals(c1, c2); // add a series OHLCSeries s1 = new OHLCSeries("Series"); s1.add(new Year(2006), 1.0, 1.1, 1.2, 1.3); c1.addSeries(s1); assertNotEquals(c1, c2); OHLCSeries s2 = new OHLCSeries("Series"); s2.add(new Year(2006), 1.0, 1.1, 1.2, 1.3); c2.addSeries(s2); assertEquals(c1, c2); // add an empty series c1.addSeries(new OHLCSeries("Empty Series")); assertNotEquals(c1, c2); c2.addSeries(new OHLCSeries("Empty Series")); assertEquals(c1, c2); c1.setXPosition(TimePeriodAnchor.END); assertNotEquals(c1, c2); c2.setXPosition(TimePeriodAnchor.END); assertEquals(c1, c2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { OHLCSeriesCollection c1 = new OHLCSeriesCollection(); OHLCSeries s1 = new OHLCSeries("Series"); s1.add(new Year(2006), 1.0, 1.1, 1.2, 1.3); c1.addSeries(s1); OHLCSeriesCollection c2 = (OHLCSeriesCollection) c1.clone(); assertNotSame(c1, c2); assertSame(c1.getClass(), c2.getClass()); assertEquals(c1, c2); // check independence s1.setDescription("XYZ"); assertNotEquals(c1, c2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { OHLCSeriesCollection c1 = new OHLCSeriesCollection(); OHLCSeries s1 = new OHLCSeries("Series"); s1.add(new Year(2006), 1.0, 1.1, 1.2, 1.3); c1.addSeries(s1); OHLCSeriesCollection c2 = TestUtils.serialised(c1); assertEquals(c1, c2); } /** * A test for bug report 1170825 (originally affected XYSeriesCollection, * this test is just copied over). */ @Test public void test1170825() { OHLCSeries s1 = new OHLCSeries("Series1"); OHLCSeriesCollection dataset = new OHLCSeriesCollection(); dataset.addSeries(s1); try { /* XYSeries s = */ dataset.getSeries(1); } catch (IllegalArgumentException e) { // correct outcome } catch (IndexOutOfBoundsException e) { fail(); // wrong outcome } } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { OHLCSeriesCollection c1 = new OHLCSeriesCollection(); OHLCSeries s1 = new OHLCSeries("S"); s1.add(new Year(2009), 1.0, 4.0, 0.5, 2.0); c1.addSeries(s1); OHLCSeriesCollection c2 = new OHLCSeriesCollection(); OHLCSeries s2 = new OHLCSeries("S"); s2.add(new Year(2009), 1.0, 4.0, 0.5, 2.0); c2.addSeries(s2); assertEquals(c1, c2); int h1 = c1.hashCode(); int h2 = c2.hashCode(); assertEquals(h1, h2); } /** * Some checks for the {@link OHLCSeriesCollection#removeSeries(int)} * method. */ @Test public void testRemoveSeries_int() { OHLCSeriesCollection c1 = new OHLCSeriesCollection(); OHLCSeries s1 = new OHLCSeries("Series 1"); OHLCSeries s2 = new OHLCSeries("Series 2"); OHLCSeries s3 = new OHLCSeries("Series 3"); OHLCSeries s4 = new OHLCSeries("Series 4"); c1.addSeries(s1); c1.addSeries(s2); c1.addSeries(s3); c1.addSeries(s4); c1.removeSeries(2); assertEquals(c1.getSeries(2), s4); c1.removeSeries(0); assertEquals(c1.getSeries(0), s2); assertEquals(2, c1.getSeriesCount()); } /** * Some checks for the * {@link OHLCSeriesCollection#removeSeries(OHLCSeries)} method. */ @Test public void testRemoveSeries() { OHLCSeriesCollection c1 = new OHLCSeriesCollection(); OHLCSeries s1 = new OHLCSeries("Series 1"); OHLCSeries s2 = new OHLCSeries("Series 2"); OHLCSeries s3 = new OHLCSeries("Series 3"); OHLCSeries s4 = new OHLCSeries("Series 4"); c1.addSeries(s1); c1.addSeries(s2); c1.addSeries(s3); c1.addSeries(s4); c1.removeSeries(s3); assertEquals(c1.getSeries(2), s4); c1.removeSeries(s1); assertEquals(c1.getSeries(0), s2); assertEquals(2, c1.getSeriesCount()); } /** * A simple check for the removeAllSeries() method. */ @Test public void testRemoveAllSeries() { OHLCSeriesCollection c1 = new OHLCSeriesCollection(); c1.addChangeListener(this); // there should be no change event when clearing an empty series this.lastEvent = null; c1.removeAllSeries(); assertNull(this.lastEvent); OHLCSeries s1 = new OHLCSeries("Series 1"); OHLCSeries s2 = new OHLCSeries("Series 2"); c1.addSeries(s1); c1.addSeries(s2); c1.removeAllSeries(); assertEquals(0, c1.getSeriesCount()); assertNotNull(this.lastEvent); this.lastEvent = null; // clean up } /** The last received event. */ private DatasetChangeEvent lastEvent; /** * Receives dataset change events. * * @param event the event. */ @Override public void datasetChanged(DatasetChangeEvent event) { this.lastEvent = event; } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/time/ohlc/OHLCSeriesTest.java000066400000000000000000000171431463604235500302150ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * OHLCSeriesTest.java * ------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time.ohlc; import org.jfree.chart.TestUtils; import org.jfree.data.general.SeriesChangeEvent; import org.jfree.data.general.SeriesChangeListener; import org.jfree.data.general.SeriesException; import org.jfree.data.time.Year; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link OHLCSeries} class. */ public class OHLCSeriesTest implements SeriesChangeListener { SeriesChangeEvent lastEvent; /** * Records a change event. * * @param event the event. */ @Override public void seriesChanged(SeriesChangeEvent event) { this.lastEvent = event; } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { OHLCSeries s1 = new OHLCSeries("s1"); OHLCSeries s2 = new OHLCSeries("s1"); assertEquals(s1, s2); // seriesKey s1 = new OHLCSeries("s2"); assertNotEquals(s1, s2); s2 = new OHLCSeries("s2"); assertEquals(s1, s2); // add a value s1.add(new Year(2006), 2.0, 4.0, 1.0, 3.0); assertNotEquals(s1, s2); s2.add(new Year(2006), 2.0, 4.0, 1.0, 3.0); assertEquals(s2, s1); // add another value s1.add(new Year(2008), 2.0, 4.0, 1.0, 3.0); assertNotEquals(s1, s2); s2.add(new Year(2008), 2.0, 4.0, 1.0, 3.0); assertEquals(s2, s1); // remove a value s1.remove(new Year(2008)); assertNotEquals(s1, s2); s2.remove(new Year(2008)); assertEquals(s2, s1); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { OHLCSeries s1 = new OHLCSeries("Test"); s1.add(new Year(2009), 1.0, 3.0, 2.0, 1.4); OHLCSeries s2 = new OHLCSeries("Test"); s2.add(new Year(2009), 1.0, 3.0, 2.0, 1.4); assertEquals(s1, s2); int h1 = s1.hashCode(); int h2 = s2.hashCode(); assertEquals(h1, h2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { OHLCSeries s1 = new OHLCSeries("s1"); s1.add(new Year(2006), 2.0, 4.0, 1.0, 3.0); OHLCSeries s2 = (OHLCSeries) s1.clone(); assertNotSame(s1, s2); assertSame(s1.getClass(), s2.getClass()); assertEquals(s1, s2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { OHLCSeries s1 = new OHLCSeries("s1"); s1.add(new Year(2006), 2.0, 4.0, 1.0, 3.0); OHLCSeries s2 = TestUtils.serialised(s1); assertEquals(s1, s2); } /** * Simple test for the indexOf() method. */ @Test public void testIndexOf() { OHLCSeries s1 = new OHLCSeries("s1"); s1.add(new Year(2006), 2.0, 4.0, 1.0, 3.0); s1.add(new Year(2011), 2.0, 4.0, 1.0, 3.0); s1.add(new Year(2010), 2.0, 4.0, 1.0, 3.0); assertEquals(0, s1.indexOf(new Year(2006))); assertEquals(1, s1.indexOf(new Year(2010))); assertEquals(2, s1.indexOf(new Year(2011))); } /** * Simple test for the remove() method. */ @Test public void testRemove() { OHLCSeries s1 = new OHLCSeries("s1"); s1.add(new Year(2006), 2.0, 4.0, 1.0, 3.0); s1.add(new Year(2011), 2.1, 4.1, 1.1, 3.1); s1.add(new Year(2010), 2.2, 4.2, 1.2, 3.2); assertEquals(3, s1.getItemCount()); s1.remove(new Year(2010)); assertEquals(new Year(2011), s1.getPeriod(1)); s1.remove(new Year(2006)); assertEquals(new Year(2011), s1.getPeriod(0)); } /** * A check for the remove(int) method. */ @Test public void testRemove_int() { OHLCSeries s1 = new OHLCSeries("s1"); s1.add(new Year(2006), 2.0, 4.0, 1.0, 3.0); s1.add(new Year(2011), 2.1, 4.1, 1.1, 3.1); s1.add(new Year(2010), 2.2, 4.2, 1.2, 3.2); assertEquals(3, s1.getItemCount()); s1.remove(s1.getItemCount() - 1); assertEquals(2, s1.getItemCount()); assertEquals(new Year(2010), s1.getPeriod(1)); } /** * If you add a duplicate period, an exception should be thrown. */ @Test public void testAdditionOfDuplicatePeriod() { OHLCSeries s1 = new OHLCSeries("s1"); s1.add(new Year(2006), 1.0, 1.0, 1.0, 1.0); boolean pass = false; try { s1.add(new Year(2006), 1.0, 1.0, 1.0, 1.0); } catch (SeriesException e) { pass = true; } assertTrue(pass); } /** * A simple check that the maximumItemCount attribute is working. */ @Test public void testSetMaximumItemCount() { OHLCSeries s1 = new OHLCSeries("s1"); assertEquals(Integer.MAX_VALUE, s1.getMaximumItemCount()); s1.setMaximumItemCount(2); assertEquals(2, s1.getMaximumItemCount()); s1.add(new Year(2006), 1.0, 1.1, 1.1, 1.1); s1.add(new Year(2007), 2.0, 2.2, 2.2, 2.2); s1.add(new Year(2008), 3.0, 3.3, 3.3, 3.3); assertEquals(new Year(2007), s1.getPeriod(0)); assertEquals(new Year(2008), s1.getPeriod(1)); } /** * Check that the maximum item count can be applied retrospectively. */ @Test public void testSetMaximumItemCount2() { OHLCSeries s1 = new OHLCSeries("s1"); s1.add(new Year(2006), 1.0, 1.1, 1.1, 1.1); s1.add(new Year(2007), 2.0, 2.2, 2.2, 2.2); s1.add(new Year(2008), 3.0, 3.3, 3.3, 3.3); s1.setMaximumItemCount(2); assertEquals(new Year(2007), s1.getPeriod(0)); assertEquals(new Year(2008), s1.getPeriod(1)); } /** * Some checks for the clear() method. */ @Test public void testClear() { OHLCSeries s1 = new OHLCSeries("S1"); s1.addChangeListener(this); s1.clear(); assertNull(this.lastEvent); assertTrue(s1.isEmpty()); s1.add(new Year(2006), 1.0, 1.1, 1.1, 1.1); assertFalse(s1.isEmpty()); s1.clear(); assertNotNull(this.lastEvent); assertTrue(s1.isEmpty()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/time/ohlc/OHLCTest.java000066400000000000000000000063621463604235500270430ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------- * OHLCTest.java * ------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.time.ohlc; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link OHLC} class. */ public class OHLCTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { OHLC i1 = new OHLC(2.0, 4.0, 1.0, 3.0); OHLC i2 = new OHLC(2.0, 4.0, 1.0, 3.0); assertEquals(i1, i2); i1 = new OHLC(2.2, 4.0, 1.0, 3.0); assertNotEquals(i1, i2); i2 = new OHLC(2.2, 4.0, 1.0, 3.0); assertEquals(i1, i2); i1 = new OHLC(2.2, 4.4, 1.0, 3.0); assertNotEquals(i1, i2); i2 = new OHLC(2.2, 4.4, 1.0, 3.0); assertEquals(i1, i2); i1 = new OHLC(2.2, 4.4, 1.1, 3.0); assertNotEquals(i1, i2); i2 = new OHLC(2.2, 4.4, 1.1, 3.0); assertEquals(i1, i2); i1 = new OHLC(2.2, 4.4, 1.1, 3.3); assertNotEquals(i1, i2); i2 = new OHLC(2.2, 4.4, 1.1, 3.3); assertEquals(i1, i2); } /** * This class is immutable. */ @Test public void testCloning() { OHLC i1 = new OHLC(2.0, 4.0, 1.0, 3.0); assertFalse(i1 instanceof Cloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { OHLC i1 = new OHLC(2.0, 4.0, 1.0, 3.0); OHLC i2 = TestUtils.serialised(i1); assertEquals(i1, i2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { OHLC i1 = new OHLC(2.0, 4.0, 1.0, 3.0); OHLC i2 = new OHLC(2.0, 4.0, 1.0, 3.0); assertEquals(i1, i2); int h1 = i1.hashCode(); int h2 = i2.hashCode(); assertEquals(h1, h2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/time/package.html000066400000000000000000000001631463604235500261400ustar00rootroot00000000000000 JUnit tests. jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/000077500000000000000000000000001463604235500233615ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/CategoryTableXYDatasetTest.java000066400000000000000000000142511463604235500314030ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------- * CategoryTableXYDatasetTest.java * ------------------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link CategoryTableXYDataset} class. */ public class CategoryTableXYDatasetTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { CategoryTableXYDataset d1 = new CategoryTableXYDataset(); d1.add(1.0, 1.1, "Series 1"); d1.add(2.0, 2.2, "Series 1"); CategoryTableXYDataset d2 = new CategoryTableXYDataset(); d2.add(1.0, 1.1, "Series 1"); d2.add(2.0, 2.2, "Series 1"); assertEquals(d1, d2); assertEquals(d2, d1); d1.add(3.0, 3.3, "Series 1"); assertNotEquals(d1, d2); d2.add(3.0, 3.3, "Series 1"); assertEquals(d1, d2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { CategoryTableXYDataset d1 = new CategoryTableXYDataset(); d1.add(1.0, 1.1, "Series 1"); d1.add(2.0, 2.2, "Series 1"); CategoryTableXYDataset d2 = (CategoryTableXYDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); d1.add(3.0, 3.3, "Series 1"); assertNotEquals(d1, d2); d2.add(3.0, 3.3, "Series 1"); assertEquals(d1, d2); d1.setIntervalPositionFactor(0.33); assertNotEquals(d1, d2); d2.setIntervalPositionFactor(0.33); assertEquals(d1, d2); } /** * Another check for cloning - making sure it works for a customised * interval delegate. */ @Test public void testCloning2() throws CloneNotSupportedException { CategoryTableXYDataset d1 = new CategoryTableXYDataset(); d1.add(1.0, 1.1, "Series 1"); d1.add(2.0, 2.2, "Series 1"); d1.setIntervalWidth(1.23); CategoryTableXYDataset d2 = (CategoryTableXYDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); d1.add(3.0, 3.3, "Series 1"); assertNotEquals(d1, d2); d2.add(3.0, 3.3, "Series 1"); assertEquals(d1, d2); d1.setIntervalPositionFactor(0.33); assertNotEquals(d1, d2); d2.setIntervalPositionFactor(0.33); assertEquals(d1, d2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { CategoryTableXYDataset d1 = new CategoryTableXYDataset(); assertTrue(d1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { CategoryTableXYDataset d1 = new CategoryTableXYDataset(); d1.add(1.0, 1.1, "Series 1"); d1.add(2.0, 2.2, "Series 1"); CategoryTableXYDataset d2 = TestUtils.serialised(d1); assertEquals(d1, d2); } private static final double EPSILON = 0.0000000001; /** * This is a test for bug 1312066 - adding a new series should trigger a * recalculation of the interval width, if it is being automatically * calculated. */ @Test public void testAddSeries() { CategoryTableXYDataset d1 = new CategoryTableXYDataset(); d1.setAutoWidth(true); d1.add(3.0, 1.1, "Series 1"); d1.add(7.0, 2.2, "Series 1"); assertEquals(3.0, d1.getXValue(0, 0), EPSILON); assertEquals(7.0, d1.getXValue(0, 1), EPSILON); assertEquals(1.0, d1.getStartXValue(0, 0), EPSILON); assertEquals(5.0, d1.getStartXValue(0, 1), EPSILON); assertEquals(5.0, d1.getEndXValue(0, 0), EPSILON); assertEquals(9.0, d1.getEndXValue(0, 1), EPSILON); // now add some more data d1.add(7.5, 1.1, "Series 2"); d1.add(9.0, 2.2, "Series 2"); assertEquals(3.0, d1.getXValue(1, 0), EPSILON); assertEquals(7.0, d1.getXValue(1, 1), EPSILON); assertEquals(7.5, d1.getXValue(1, 2), EPSILON); assertEquals(9.0, d1.getXValue(1, 3), EPSILON); assertEquals(7.25, d1.getStartXValue(1, 2), EPSILON); assertEquals(8.75, d1.getStartXValue(1, 3), EPSILON); assertEquals(7.75, d1.getEndXValue(1, 2), EPSILON); assertEquals(9.25, d1.getEndXValue(1, 3), EPSILON); // and check the first series too... assertEquals(2.75, d1.getStartXValue(0, 0), EPSILON); assertEquals(6.75, d1.getStartXValue(0, 1), EPSILON); assertEquals(3.25, d1.getEndXValue(0, 0), EPSILON); assertEquals(7.25, d1.getEndXValue(0, 1), EPSILON); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/DefaultHighLowDatasetTest.java000066400000000000000000000157351463604235500312530ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------ * DefaultHighLowDatasetTest.java * ------------------------------ * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import java.util.Date; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link DefaultHighLowDataset} class. */ public class DefaultHighLowDatasetTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { DefaultHighLowDataset d1 = new DefaultHighLowDataset("Series 1", new Date[0], new double[0], new double[0], new double[0], new double[0], new double[0]); DefaultHighLowDataset d2 = new DefaultHighLowDataset("Series 1", new Date[0], new double[0], new double[0], new double[0], new double[0], new double[0]); assertEquals(d1, d2); assertEquals(d2, d1); d1 = new DefaultHighLowDataset("Series 2", new Date[0], new double[0], new double[0], new double[0], new double[0], new double[0]); assertNotEquals(d1, d2); d2 = new DefaultHighLowDataset("Series 2", new Date[0], new double[0], new double[0], new double[0], new double[0], new double[0]); assertEquals(d1, d2); d1 = new DefaultHighLowDataset("Series 2", new Date[] {new Date(123L)}, new double[1], new double[1], new double[1], new double[1], new double[1]); assertNotEquals(d1, d2); d2 = new DefaultHighLowDataset("Series 2", new Date[] {new Date(123L)}, new double[1], new double[1], new double[1], new double[1], new double[1]); assertEquals(d1, d2); d1 = new DefaultHighLowDataset("Series 2", new Date[] {new Date(123L)}, new double[] {1.2}, new double[1], new double[1], new double[1], new double[1]); assertNotEquals(d1, d2); d2 = new DefaultHighLowDataset("Series 2", new Date[] {new Date(123L)}, new double[] {1.2}, new double[1], new double[1], new double[1], new double[1]); assertEquals(d1, d2); d1 = new DefaultHighLowDataset("Series 2", new Date[] {new Date(123L)}, new double[] {1.2}, new double[] {3.4}, new double[1], new double[1], new double[1]); assertNotEquals(d1, d2); d2 = new DefaultHighLowDataset("Series 2", new Date[] {new Date(123L)}, new double[] {1.2}, new double[] {3.4}, new double[1], new double[1], new double[1]); assertEquals(d1, d2); d1 = new DefaultHighLowDataset("Series 2", new Date[] {new Date(123L)}, new double[] {1.2}, new double[] {3.4}, new double[] {5.6}, new double[1], new double[1]); assertNotEquals(d1, d2); d2 = new DefaultHighLowDataset("Series 2", new Date[] {new Date(123L)}, new double[] {1.2}, new double[] {3.4}, new double[] {5.6}, new double[1], new double[1]); assertEquals(d1, d2); d1 = new DefaultHighLowDataset("Series 2", new Date[] {new Date(123L)}, new double[] {1.2}, new double[] {3.4}, new double[] {5.6}, new double[] {7.8}, new double[1]); assertNotEquals(d1, d2); d2 = new DefaultHighLowDataset("Series 2", new Date[] {new Date(123L)}, new double[] {1.2}, new double[] {3.4}, new double[] {5.6}, new double[] {7.8}, new double[1]); assertEquals(d1, d2); d1 = new DefaultHighLowDataset("Series 2", new Date[] {new Date(123L)}, new double[] {1.2}, new double[] {3.4}, new double[] {5.6}, new double[] {7.8}, new double[] {99.9}); assertNotEquals(d1, d2); d2 = new DefaultHighLowDataset("Series 2", new Date[] {new Date(123L)}, new double[] {1.2}, new double[] {3.4}, new double[] {5.6}, new double[] {7.8}, new double[] {99.9}); assertEquals(d1, d2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { DefaultHighLowDataset d1 = new DefaultHighLowDataset("Series 1", new Date[] {new Date(123L)}, new double[] {1.2}, new double[] {3.4}, new double[] {5.6}, new double[] {7.8}, new double[] {99.9}); DefaultHighLowDataset d2 = (DefaultHighLowDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { DefaultHighLowDataset d1 = new DefaultHighLowDataset("Series 1", new Date[0], new double[0], new double[0], new double[0], new double[0], new double[0]); assertTrue(d1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DefaultHighLowDataset d1 = new DefaultHighLowDataset("Series 1", new Date[] {new Date(123L)}, new double[] {1.2}, new double[] {3.4}, new double[] {5.6}, new double[] {7.8}, new double[] {99.9}); DefaultHighLowDataset d2 = TestUtils.serialised(d1); assertEquals(d1, d2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/DefaultIntervalXYDatasetTest.java000066400000000000000000000271641463604235500317560ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------------- * DefaultIntervalXYDatasetTest.java * --------------------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Some tests for the {@link DefaultIntervalXYDataset} class. */ public class DefaultIntervalXYDatasetTest { /** * Some checks for the getSeriesCount() method. */ @Test public void testGetSeriesCount() { DefaultIntervalXYDataset d = new DefaultIntervalXYDataset(); assertEquals(0, d.getSeriesCount()); d = createSampleDataset1(); assertEquals(2, d.getSeriesCount()); } /** * Some checks for the getSeriesKey(int) method. */ @Test public void testGetSeriesKey() { DefaultIntervalXYDataset d = createSampleDataset1(); assertEquals("S1", d.getSeriesKey(0)); assertEquals("S2", d.getSeriesKey(1)); // check for series key out of bounds boolean pass = false; try { /*Comparable k =*/ d.getSeriesKey(-1); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); pass = false; try { /*Comparable k =*/ d.getSeriesKey(2); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Some checks for the getItemCount() method. */ @Test public void testGetItemCount() { DefaultIntervalXYDataset d = createSampleDataset1(); assertEquals(3, d.getItemCount(0)); assertEquals(3, d.getItemCount(1)); // try an index out of bounds boolean pass = false; try { d.getItemCount(2); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } private static final double EPSILON = 0.0000000001; /** * Some checks for the getXValue() method. */ @Test public void testGetXValue() { DefaultIntervalXYDataset d = createSampleDataset1(); assertEquals(1.0, d.getXValue(0, 0), EPSILON); assertEquals(2.0, d.getXValue(0, 1), EPSILON); assertEquals(3.0, d.getXValue(0, 2), EPSILON); assertEquals(11.0, d.getXValue(1, 0), EPSILON); assertEquals(12.0, d.getXValue(1, 1), EPSILON); assertEquals(13.0, d.getXValue(1, 2), EPSILON); } /** * Some checks for the getYValue() method. */ @Test public void testGetYValue() { DefaultIntervalXYDataset d = createSampleDataset1(); assertEquals(4.0, d.getYValue(0, 0), EPSILON); assertEquals(5.0, d.getYValue(0, 1), EPSILON); assertEquals(6.0, d.getYValue(0, 2), EPSILON); assertEquals(14.0, d.getYValue(1, 0), EPSILON); assertEquals(15.0, d.getYValue(1, 1), EPSILON); assertEquals(16.0, d.getYValue(1, 2), EPSILON); } /** * Some checks for the getStartXValue() method. */ @Test public void testGetStartXValue() { DefaultIntervalXYDataset d = createSampleDataset1(); assertEquals(0.9, d.getStartXValue(0, 0), EPSILON); assertEquals(1.9, d.getStartXValue(0, 1), EPSILON); assertEquals(2.9, d.getStartXValue(0, 2), EPSILON); assertEquals(10.9, d.getStartXValue(1, 0), EPSILON); assertEquals(11.9, d.getStartXValue(1, 1), EPSILON); assertEquals(12.9, d.getStartXValue(1, 2), EPSILON); } /** * Some checks for the getEndXValue() method. */ @Test public void testGetEndXValue() { DefaultIntervalXYDataset d = createSampleDataset1(); assertEquals(1.1, d.getEndXValue(0, 0), EPSILON); assertEquals(2.1, d.getEndXValue(0, 1), EPSILON); assertEquals(3.1, d.getEndXValue(0, 2), EPSILON); assertEquals(11.1, d.getEndXValue(1, 0), EPSILON); assertEquals(12.1, d.getEndXValue(1, 1), EPSILON); assertEquals(13.1, d.getEndXValue(1, 2), EPSILON); } /** * Some checks for the getStartYValue() method. */ @Test public void testGetStartYValue() { DefaultIntervalXYDataset d = createSampleDataset1(); assertEquals(1.09, d.getStartYValue(0, 0), EPSILON); assertEquals(2.09, d.getStartYValue(0, 1), EPSILON); assertEquals(3.09, d.getStartYValue(0, 2), EPSILON); assertEquals(11.09, d.getStartYValue(1, 0), EPSILON); assertEquals(12.09, d.getStartYValue(1, 1), EPSILON); assertEquals(13.09, d.getStartYValue(1, 2), EPSILON); } /** * Some checks for the getEndYValue() method. */ @Test public void testGetEndYValue() { DefaultIntervalXYDataset d = createSampleDataset1(); assertEquals(1.11, d.getEndYValue(0, 0), EPSILON); assertEquals(2.11, d.getEndYValue(0, 1), EPSILON); assertEquals(3.11, d.getEndYValue(0, 2), EPSILON); assertEquals(11.11, d.getEndYValue(1, 0), EPSILON); assertEquals(12.11, d.getEndYValue(1, 1), EPSILON); assertEquals(13.11, d.getEndYValue(1, 2), EPSILON); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { DefaultIntervalXYDataset d1 = new DefaultIntervalXYDataset(); DefaultIntervalXYDataset d2 = new DefaultIntervalXYDataset(); assertEquals(d1, d2); assertEquals(d2, d1); d1 = createSampleDataset1(); assertNotEquals(d1, d2); d2 = createSampleDataset1(); assertEquals(d1, d2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { DefaultIntervalXYDataset d1 = new DefaultIntervalXYDataset(); DefaultIntervalXYDataset d2 = (DefaultIntervalXYDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); // try a dataset with some content... d1 = createSampleDataset1(); d2 = (DefaultIntervalXYDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); } /** * Another test for cloning. */ @Test public void testCloning2() throws CloneNotSupportedException { DefaultIntervalXYDataset d1 = new DefaultIntervalXYDataset(); double[] x1 = new double[] {1.0, 2.0, 3.0}; double[] x1Start = new double[] {0.9, 1.9, 2.9}; double[] x1End = new double[] {1.1, 2.1, 3.1}; double[] y1 = new double[] {4.0, 5.0, 6.0}; double[] y1Start = new double[] {1.09, 2.09, 3.09}; double[] y1End = new double[] {1.11, 2.11, 3.11}; double[][] data1 = new double[][] {x1, x1Start, x1End, y1, y1Start, y1End}; d1.addSeries("S1", data1); DefaultIntervalXYDataset d2 = (DefaultIntervalXYDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); // check independence x1[0] = 111.1; assertNotEquals(d1, d2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { DefaultIntervalXYDataset d1 = new DefaultIntervalXYDataset(); assertTrue(d1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DefaultIntervalXYDataset d1 = new DefaultIntervalXYDataset(); DefaultIntervalXYDataset d2 = TestUtils.serialised(d1); assertEquals(d1, d2); // try a dataset with some content... d1 = createSampleDataset1(); d2 = TestUtils.serialised(d1); assertEquals(d1, d2); } /** * Some checks for the indexOf(Comparable) method. */ @Test public void testIndexOf() { DefaultIntervalXYDataset d = createSampleDataset1(); assertEquals(0, d.indexOf("S1")); assertEquals(1, d.indexOf("S2")); assertEquals(-1, d.indexOf("Green Eggs and Ham")); assertEquals(-1, d.indexOf(null)); } /** * Some tests for the addSeries() method. */ @Test public void testAddSeries() { DefaultIntervalXYDataset d = new DefaultIntervalXYDataset(); d.addSeries("S1", new double[][] {{1.0}, {0.5}, {1.5}, {2.0}, {2.5}, {1.5}}); assertEquals(1, d.getSeriesCount()); assertEquals("S1", d.getSeriesKey(0)); // check that adding a series will overwrite the old series d.addSeries("S1", new double[][] {{1.1}, {0.6}, {1.6}, {2.1}, {2.6}, {1.6}}); assertEquals(1, d.getSeriesCount()); assertEquals(2.1, d.getYValue(0, 0), EPSILON); // check null key boolean pass = false; try { d.addSeries(null, new double[][] {{1.1}, {0.6}, {1.6}, {2.1}, {2.6}, {1.6}}); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Creates a sample dataset for testing. * * @return A sample dataset. */ public DefaultIntervalXYDataset createSampleDataset1() { DefaultIntervalXYDataset d = new DefaultIntervalXYDataset(); double[] x1 = new double[] {1.0, 2.0, 3.0}; double[] x1Start = new double[] {0.9, 1.9, 2.9}; double[] x1End = new double[] {1.1, 2.1, 3.1}; double[] y1 = new double[] {4.0, 5.0, 6.0}; double[] y1Start = new double[] {1.09, 2.09, 3.09}; double[] y1End = new double[] {1.11, 2.11, 3.11}; double[][] data1 = new double[][] {x1, x1Start, x1End, y1, y1Start, y1End}; d.addSeries("S1", data1); double[] x2 = new double[] {11.0, 12.0, 13.0}; double[] x2Start = new double[] {10.9, 11.9, 12.9}; double[] x2End = new double[] {11.1, 12.1, 13.1}; double[] y2 = new double[] {14.0, 15.0, 16.0}; double[] y2Start = new double[] {11.09, 12.09, 13.09}; double[] y2End = new double[] {11.11, 12.11, 13.11}; double[][] data2 = new double[][] {x2, x2Start, x2End, y2, y2Start, y2End}; d.addSeries("S2", data2); return d; } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/DefaultOHLCDatasetTest.java000066400000000000000000000123321463604235500304250ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * DefaultOHLCDatasetTest.java * --------------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import java.util.Date; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.Range; import org.jfree.data.general.DatasetUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link DefaultOHLCDataset} class. */ public class DefaultOHLCDatasetTest { private static final double EPSILON = 0.0000000001; /** * A small test for the data range calculated on this dataset. */ @Test public void testDataRange() { OHLCDataItem[] data = new OHLCDataItem[3]; data[0] = new OHLCDataItem(new Date(11L), 2.0, 4.0, 1.0, 3.0, 100.0); data[1] = new OHLCDataItem(new Date(22L), 4.0, 9.0, 2.0, 5.0, 120.0); data[2] = new OHLCDataItem(new Date(33L), 3.0, 7.0, 3.0, 6.0, 140.0); DefaultOHLCDataset d = new DefaultOHLCDataset("S1", data); Range r = DatasetUtils.findRangeBounds(d, true); assertEquals(1.0, r.getLowerBound(), EPSILON); assertEquals(9.0, r.getUpperBound(), EPSILON); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { DefaultOHLCDataset d1 = new DefaultOHLCDataset("Series 1", new OHLCDataItem[0]); DefaultOHLCDataset d2 = new DefaultOHLCDataset("Series 1", new OHLCDataItem[0]); assertEquals(d1, d2); assertEquals(d2, d1); d1 = new DefaultOHLCDataset("Series 2", new OHLCDataItem[0]); assertNotEquals(d1, d2); d2 = new DefaultOHLCDataset("Series 2", new OHLCDataItem[0]); assertEquals(d1, d2); d1 = new DefaultOHLCDataset("Series 2", new OHLCDataItem[] { new OHLCDataItem(new Date(123L), 1.2, 3.4, 5.6, 7.8, 99.9)}); assertNotEquals(d1, d2); d2 = new DefaultOHLCDataset("Series 2", new OHLCDataItem[] { new OHLCDataItem(new Date(123L), 1.2, 3.4, 5.6, 7.8, 99.9)}); assertEquals(d1, d2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { DefaultOHLCDataset d1 = new DefaultOHLCDataset("Series 1", new OHLCDataItem[0]); DefaultOHLCDataset d2 = (DefaultOHLCDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); } /** * Confirm that cloning works. */ @Test public void testCloning2() throws CloneNotSupportedException { OHLCDataItem item1 = new OHLCDataItem(new Date(1L), 1.0, 2.0, 3.0, 4.0, 5.0); OHLCDataItem item2 = new OHLCDataItem(new Date(2L), 6.0, 7.0, 8.0, 9.0, 10.0); // create an array of items in reverse order OHLCDataItem[] items = new OHLCDataItem[] {item2, item1}; DefaultOHLCDataset d1 = new DefaultOHLCDataset("Series 1", items); DefaultOHLCDataset d2 = (DefaultOHLCDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); d1.sortDataByDate(); assertNotEquals(d1, d2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { DefaultOHLCDataset d1 = new DefaultOHLCDataset("Series 1", new OHLCDataItem[0]); assertTrue(d1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DefaultOHLCDataset d1 = new DefaultOHLCDataset("Series 1", new OHLCDataItem[0]); DefaultOHLCDataset d2 = TestUtils.serialised(d1); assertEquals(d1, d2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/DefaultTableXYDatasetTest.java000066400000000000000000000141751463604235500312170ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------ * DefaultTableXYDatasetTest.java * ------------------------------ * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link DefaultTableXYDataset} class. */ public class DefaultTableXYDatasetTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { DefaultTableXYDataset d1 = new DefaultTableXYDataset(); XYSeries s1 = new XYSeries("Series 1", true, false); s1.add(1.0, 1.1); s1.add(2.0, 2.2); d1.addSeries(s1); DefaultTableXYDataset d2 = new DefaultTableXYDataset(); XYSeries s2 = new XYSeries("Series 1", true, false); s2.add(1.0, 1.1); s2.add(2.0, 2.2); d2.addSeries(s2); assertEquals(d1, d2); assertEquals(d2, d1); s1.add(3.0, 3.3); assertNotEquals(d1, d2); s2.add(3.0, 3.3); assertEquals(d1, d2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { DefaultTableXYDataset d1 = new DefaultTableXYDataset(); XYSeries s1 = new XYSeries("Series 1", true, false); s1.add(1.0, 1.1); s1.add(2.0, 2.2); d1.addSeries(s1); DefaultTableXYDataset d2 = (DefaultTableXYDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); s1.add(3.0, 3.3); assertNotEquals(d1, d2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { DefaultTableXYDataset d1 = new DefaultTableXYDataset(); assertTrue(d1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DefaultTableXYDataset d1 = new DefaultTableXYDataset(); XYSeries s1 = new XYSeries("Series 1", true, false); s1.add(1.0, 1.1); s1.add(2.0, 2.2); d1.addSeries(s1); DefaultTableXYDataset d2 = TestUtils.serialised(d1); assertEquals(d1, d2); } private static final double EPSILON = 0.0000000001; /** * This is a test for bug 1312066 - adding a new series should trigger a * recalculation of the interval width, if it is being automatically * calculated. */ @Test public void testAddSeries() { DefaultTableXYDataset d1 = new DefaultTableXYDataset(); d1.setAutoWidth(true); XYSeries s1 = new XYSeries("Series 1", true, false); s1.add(3.0, 1.1); s1.add(7.0, 2.2); d1.addSeries(s1); assertEquals(3.0, d1.getXValue(0, 0), EPSILON); assertEquals(7.0, d1.getXValue(0, 1), EPSILON); assertEquals(1.0, d1.getStartXValue(0, 0), EPSILON); assertEquals(5.0, d1.getStartXValue(0, 1), EPSILON); assertEquals(5.0, d1.getEndXValue(0, 0), EPSILON); assertEquals(9.0, d1.getEndXValue(0, 1), EPSILON); // now add another series XYSeries s2 = new XYSeries("Series 2", true, false); s2.add(7.5, 1.1); s2.add(9.0, 2.2); d1.addSeries(s2); assertEquals(3.0, d1.getXValue(1, 0), EPSILON); assertEquals(7.0, d1.getXValue(1, 1), EPSILON); assertEquals(7.5, d1.getXValue(1, 2), EPSILON); assertEquals(9.0, d1.getXValue(1, 3), EPSILON); assertEquals(7.25, d1.getStartXValue(1, 2), EPSILON); assertEquals(8.75, d1.getStartXValue(1, 3), EPSILON); assertEquals(7.75, d1.getEndXValue(1, 2), EPSILON); assertEquals(9.25, d1.getEndXValue(1, 3), EPSILON); // and check the first series too... assertEquals(2.75, d1.getStartXValue(0, 0), EPSILON); assertEquals(6.75, d1.getStartXValue(0, 1), EPSILON); assertEquals(3.25, d1.getEndXValue(0, 0), EPSILON); assertEquals(7.25, d1.getEndXValue(0, 1), EPSILON); } /** * Some basic checks for the getSeries() method. */ @Test public void testGetSeries() { DefaultTableXYDataset d1 = new DefaultTableXYDataset(); XYSeries s1 = new XYSeries("Series 1", true, false); d1.addSeries(s1); assertEquals("Series 1", d1.getSeries(0).getKey()); boolean pass = false; try { d1.getSeries(-1); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); pass = false; try { d1.getSeries(1); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/DefaultWindDatasetTest.java000066400000000000000000000134031463604235500306010ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * DefaultWindDatasetTest.java * --------------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.time.Day; import org.jfree.data.time.RegularTimePeriod; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for {@link DefaultWindDataset}. */ public class DefaultWindDatasetTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { DefaultWindDataset d1 = new DefaultWindDataset(); DefaultWindDataset d2 = new DefaultWindDataset(); assertEquals(d1, d2); assertEquals(d2, d1); d1 = createSampleDataset1(); assertNotEquals(d1, d2); d2 = createSampleDataset1(); assertEquals(d1, d2); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { DefaultWindDataset d1 = new DefaultWindDataset(); DefaultWindDataset d2 = (DefaultWindDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); // try a dataset with some content... d1 = createSampleDataset1(); d2 = (DefaultWindDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { DefaultWindDataset d1 = new DefaultWindDataset(); assertTrue(d1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DefaultWindDataset d1 = new DefaultWindDataset(); DefaultWindDataset d2 = TestUtils.serialised(d1); assertEquals(d1, d2); // try a dataset with some content... d1 = createSampleDataset1(); d2 = TestUtils.serialised(d1); assertEquals(d1, d2); } /** * Some checks for the getSeriesKey(int) method. */ @Test public void testGetSeriesKey() { DefaultWindDataset d = createSampleDataset1(); assertEquals("Series 1", d.getSeriesKey(0)); assertEquals("Series 2", d.getSeriesKey(1)); // check for series key out of bounds boolean pass = false; try { /*Comparable k =*/ d.getSeriesKey(-1); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); pass = false; try { /*Comparable k =*/ d.getSeriesKey(2); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Some checks for the indexOf(Comparable) method. */ @Test public void testIndexOf() { DefaultWindDataset d = createSampleDataset1(); assertEquals(0, d.indexOf("Series 1")); assertEquals(1, d.indexOf("Series 2")); assertEquals(-1, d.indexOf("Green Eggs and Ham")); assertEquals(-1, d.indexOf(null)); } /** * Creates a sample dataset for testing. * * @return A sample dataset. */ public DefaultWindDataset createSampleDataset1() { Day t = new Day(1, 4, 2006); Object[] item1 = createItem(t, 3, 7); Object[] item2 = createItem(t.next(), 4, 8); Object[] item3 = createItem(t.next(), 5, 9); Object[][] series1 = new Object[][] {item1, item2, item3}; Object[] item1b = createItem(t, 6, 10); Object[] item2b = createItem(t.next(), 7, 11); Object[] item3b = createItem(t.next(), 8, 12); Object[][] series2 = new Object[][] {item1b, item2b, item3b}; Object[][][] data = new Object[][][] {series1, series2}; return new DefaultWindDataset(data); } /** * Creates an array representing one item in a series. * * @param t the time period. * @param dir the wind direction. * @param force the wind force. * * @return An array containing the specified items. */ private Object[] createItem(RegularTimePeriod t, int dir, int force) { return new Object[] {t.getMiddleMillisecond(), dir, force}; } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/DefaultXYDatasetTest.java000066400000000000000000000152241463604235500302430ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * DefaultXYDatasetTests.java * -------------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for {@link DefaultXYDataset}. */ public class DefaultXYDatasetTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { DefaultXYDataset d1 = new DefaultXYDataset(); DefaultXYDataset d2 = new DefaultXYDataset(); assertEquals(d1, d2); assertEquals(d2, d1); double[] x1 = new double[] {1.0, 2.0, 3.0}; double[] y1 = new double[] {4.0, 5.0, 6.0}; double[][] data1 = new double[][] {x1, y1}; double[] x2 = new double[] {1.0, 2.0, 3.0}; double[] y2 = new double[] {4.0, 5.0, 6.0}; double[][] data2 = new double[][] {x2, y2}; d1.addSeries("S1", data1); assertNotEquals(d1, d2); d2.addSeries("S1", data2); assertEquals(d1, d2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { DefaultXYDataset d1 = new DefaultXYDataset(); DefaultXYDataset d2 = (DefaultXYDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); // try a dataset with some content... double[] x1 = new double[] {1.0, 2.0, 3.0}; double[] y1 = new double[] {4.0, 5.0, 6.0}; double[][] data1 = new double[][] {x1, y1}; d1.addSeries("S1", data1); d2 = (DefaultXYDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); // check that the clone doesn't share the same underlying arrays. x1[1] = 2.2; assertNotEquals(d1, d2); x1[1] = 2.0; assertEquals(d1, d2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { DefaultXYDataset d1 = new DefaultXYDataset(); assertTrue(d1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DefaultXYDataset d1 = new DefaultXYDataset(); DefaultXYDataset d2 = TestUtils.serialised(d1); assertEquals(d1, d2); // try a dataset with some content... double[] x1 = new double[] {1.0, 2.0, 3.0}; double[] y1 = new double[] {4.0, 5.0, 6.0}; double[][] data1 = new double[][] {x1, y1}; d1.addSeries("S1", data1); d2 = TestUtils.serialised(d1); assertEquals(d1, d2); } /** * Some checks for the getSeriesKey(int) method. */ @Test public void testGetSeriesKey() { DefaultXYDataset d = createSampleDataset1(); assertEquals("S1", d.getSeriesKey(0)); assertEquals("S2", d.getSeriesKey(1)); // check for series key out of bounds boolean pass = false; try { /*Comparable k =*/ d.getSeriesKey(-1); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); pass = false; try { /*Comparable k =*/ d.getSeriesKey(2); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Some checks for the indexOf(Comparable) method. */ @Test public void testIndexOf() { DefaultXYDataset d = createSampleDataset1(); assertEquals(0, d.indexOf("S1")); assertEquals(1, d.indexOf("S2")); assertEquals(-1, d.indexOf("Green Eggs and Ham")); assertEquals(-1, d.indexOf(null)); } static final double EPSILON = 0.0000000001; /** * Some tests for the addSeries() method. */ @Test public void testAddSeries() { DefaultXYDataset d = new DefaultXYDataset(); d.addSeries("S1", new double[][] {{1.0}, {2.0}}); assertEquals(1, d.getSeriesCount()); assertEquals("S1", d.getSeriesKey(0)); // check that adding a series will overwrite the old series d.addSeries("S1", new double[][] {{11.0}, {12.0}}); assertEquals(1, d.getSeriesCount()); assertEquals(12.0, d.getYValue(0, 0), EPSILON); // check null key boolean pass = false; try { d.addSeries(null, new double[][] {{1.0}, {2.0}}); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Creates a sample dataset for testing. * * @return A sample dataset. */ public DefaultXYDataset createSampleDataset1() { DefaultXYDataset d = new DefaultXYDataset(); double[] x1 = new double[] {1.0, 2.0, 3.0}; double[] y1 = new double[] {4.0, 5.0, 6.0}; double[][] data1 = new double[][] {x1, y1}; d.addSeries("S1", data1); double[] x2 = new double[] {1.0, 2.0, 3.0}; double[] y2 = new double[] {4.0, 5.0, 6.0}; double[][] data2 = new double[][] {x2, y2}; d.addSeries("S2", data2); return d; } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/DefaultXYZDatasetTest.java000066400000000000000000000157771463604235500304120ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * DefaultXYZDatasetTest.java * -------------------------- * (C) Copyright 2006-present, by David Gilbert. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for {@link DefaultXYZDataset}. */ public class DefaultXYZDatasetTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { DefaultXYZDataset d1 = new DefaultXYZDataset(); DefaultXYZDataset d2 = new DefaultXYZDataset(); assertEquals(d1, d2); assertEquals(d2, d1); double[] x1 = new double[] {1.0, 2.0, 3.0}; double[] y1 = new double[] {4.0, 5.0, 6.0}; double[] z1 = new double[] {7.0, 8.0, 9.0}; double[][] data1 = new double[][] {x1, y1, z1}; double[] x2 = new double[] {1.0, 2.0, 3.0}; double[] y2 = new double[] {4.0, 5.0, 6.0}; double[] z2 = new double[] {7.0, 8.0, 9.0}; double[][] data2 = new double[][] {x2, y2, z2}; d1.addSeries("S1", data1); assertNotEquals(d1, d2); d2.addSeries("S1", data2); assertEquals(d1, d2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { DefaultXYZDataset d1 = new DefaultXYZDataset(); DefaultXYZDataset d2 = (DefaultXYZDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); // try a dataset with some content... double[] x1 = new double[] {1.0, 2.0, 3.0}; double[] y1 = new double[] {4.0, 5.0, 6.0}; double[] z1 = new double[] {7.0, 8.0, 9.0}; double[][] data1 = new double[][] {x1, y1, z1}; d1.addSeries("S1", data1); d2 = (DefaultXYZDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); // check that the clone doesn't share the same underlying arrays. x1[1] = 2.2; assertNotEquals(d1, d2); x1[1] = 2.0; assertEquals(d1, d2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { DefaultXYZDataset d1 = new DefaultXYZDataset(); assertTrue(d1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DefaultXYZDataset d1 = new DefaultXYZDataset(); DefaultXYZDataset d2 = TestUtils.serialised(d1); assertEquals(d1, d2); // try a dataset with some content... double[] x1 = new double[] {1.0, 2.0, 3.0}; double[] y1 = new double[] {4.0, 5.0, 6.0}; double[] z1 = new double[] {7.0, 8.0, 9.0}; double[][] data1 = new double[][] {x1, y1, z1}; d1.addSeries("S1", data1); d2 = TestUtils.serialised(d1); assertEquals(d1, d2); } /** * Some checks for the getSeriesKey(int) method. */ @Test public void testGetSeriesKey() { DefaultXYZDataset d = createSampleDataset1(); assertEquals("S1", d.getSeriesKey(0)); assertEquals("S2", d.getSeriesKey(1)); // check for series key out of bounds boolean pass = false; try { /*Comparable k =*/ d.getSeriesKey(-1); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); pass = false; try { /*Comparable k =*/ d.getSeriesKey(2); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Some checks for the indexOf(Comparable) method. */ @Test public void testIndexOf() { DefaultXYZDataset d = createSampleDataset1(); assertEquals(0, d.indexOf("S1")); assertEquals(1, d.indexOf("S2")); assertEquals(-1, d.indexOf("Green Eggs and Ham")); assertEquals(-1, d.indexOf(null)); } static final double EPSILON = 0.0000000001; /** * Some tests for the addSeries() method. */ @Test public void testAddSeries() { DefaultXYZDataset d = new DefaultXYZDataset(); d.addSeries("S1", new double[][] {{1.0}, {2.0}, {3.0}}); assertEquals(1, d.getSeriesCount()); assertEquals("S1", d.getSeriesKey(0)); // check that adding a series will overwrite the old series d.addSeries("S1", new double[][] {{11.0}, {12.0}, {13.0}}); assertEquals(1, d.getSeriesCount()); assertEquals(12.0, d.getYValue(0, 0), EPSILON); // check null key boolean pass = false; try { d.addSeries(null, new double[][] {{1.0}, {2.0}, {3.0}}); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * Creates a sample dataset for testing. * * @return A sample dataset. */ public DefaultXYZDataset createSampleDataset1() { DefaultXYZDataset d = new DefaultXYZDataset(); double[] x1 = new double[] {1.0, 2.0, 3.0}; double[] y1 = new double[] {4.0, 5.0, 6.0}; double[] z1 = new double[] {7.0, 8.0, 9.0}; double[][] data1 = new double[][] {x1, y1, z1}; d.addSeries("S1", data1); double[] x2 = new double[] {1.0, 2.0, 3.0}; double[] y2 = new double[] {4.0, 5.0, 6.0}; double[] z2 = new double[] {7.0, 8.0, 9.0}; double[][] data2 = new double[][] {x2, y2, z2}; d.addSeries("S2", data2); return d; } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/IntervalXYDelegateTest.java000066400000000000000000000072041463604235500305670ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * IntervalXYDelegateTest.java * --------------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Some checks for the {@link IntervalXYDelegate} class. */ public class IntervalXYDelegateTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { XYSeries s1 = new XYSeries("Series"); s1.add(1.2, 3.4); XYSeriesCollection c1 = new XYSeriesCollection(); c1.addSeries(s1); IntervalXYDelegate d1 = new IntervalXYDelegate(c1); XYSeries s2 = new XYSeries("Series"); XYSeriesCollection c2 = new XYSeriesCollection(); s2.add(1.2, 3.4); c2.addSeries(s2); IntervalXYDelegate d2 = new IntervalXYDelegate(c2); assertEquals(d1, d2); assertEquals(d2, d1); d1.setAutoWidth(false); assertNotEquals(d1, d2); d2.setAutoWidth(false); assertEquals(d1, d2); d1.setIntervalPositionFactor(0.123); assertNotEquals(d1, d2); d2.setIntervalPositionFactor(0.123); assertEquals(d1, d2); d1.setFixedIntervalWidth(1.23); assertNotEquals(d1, d2); d2.setFixedIntervalWidth(1.23); assertEquals(d1, d2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { XYSeries s1 = new XYSeries("Series"); s1.add(1.2, 3.4); XYSeriesCollection c1 = new XYSeriesCollection(); c1.addSeries(s1); IntervalXYDelegate d1 = new IntervalXYDelegate(c1); IntervalXYDelegate d2 = (IntervalXYDelegate) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XYSeries s1 = new XYSeries("Series"); s1.add(1.2, 3.4); XYSeriesCollection c1 = new XYSeriesCollection(); c1.addSeries(s1); IntervalXYDelegate d1 = new IntervalXYDelegate(c1); IntervalXYDelegate d2 = TestUtils.serialised(d1); assertEquals(d1, d2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/MatrixSeriesCollectionTest.java000066400000000000000000000073101463604235500315200ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------- * MatrixSeriesCollectionTest.java * ------------------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link MatrixSeriesCollection} class. */ public class MatrixSeriesCollectionTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { MatrixSeries s1 = new MatrixSeries("Series", 2, 3); s1.update(0, 0, 1.1); MatrixSeriesCollection c1 = new MatrixSeriesCollection(); c1.addSeries(s1); MatrixSeries s2 = new MatrixSeries("Series", 2, 3); s2.update(0, 0, 1.1); MatrixSeriesCollection c2 = new MatrixSeriesCollection(); c2.addSeries(s2); assertEquals(c1, c2); assertEquals(c2, c1); c1.addSeries(new MatrixSeries("Empty Series", 1, 1)); assertNotEquals(c1, c2); c2.addSeries(new MatrixSeries("Empty Series", 1, 1)); assertEquals(c1, c2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { MatrixSeries s1 = new MatrixSeries("Series", 2, 3); s1.update(0, 0, 1.1); MatrixSeriesCollection c1 = new MatrixSeriesCollection(); c1.addSeries(s1); MatrixSeriesCollection c2 = (MatrixSeriesCollection) c1.clone(); assertNotSame(c1, c2); assertSame(c1.getClass(), c2.getClass()); assertEquals(c1, c2); // check independence s1.setDescription("XYZ"); assertNotEquals(c1, c2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { MatrixSeriesCollection c1 = new MatrixSeriesCollection(); assertTrue(c1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { MatrixSeries s1 = new MatrixSeries("Series", 2, 3); s1.update(0, 0, 1.1); MatrixSeriesCollection c1 = new MatrixSeriesCollection(); c1.addSeries(s1); MatrixSeriesCollection c2 = TestUtils.serialised(c1); assertEquals(c1, c2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/MatrixSeriesTest.java000066400000000000000000000116371463604235500275130ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * MatrixSeriesTest.java * --------------------- * (C) Copyright 2004-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link MatrixSeries} class. */ public class MatrixSeriesTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { MatrixSeries m1 = new MatrixSeries("Test", 8, 3); m1.update(0, 0, 11.0); m1.update(7, 2, 22.0); MatrixSeries m2 = new MatrixSeries("Test", 8, 3); m2.update(0, 0, 11.0); m2.update(7, 2, 22.0); assertEquals(m1, m2); assertEquals(m2, m1); m1 = new MatrixSeries("Test 2", 8, 3); assertNotEquals(m1, m2); m2 = new MatrixSeries("Test 2", 8, 3); assertEquals(m1, m2); m1 = new MatrixSeries("Test 2", 10, 3); assertNotEquals(m1, m2); m2 = new MatrixSeries("Test 2", 10, 3); assertEquals(m1, m2); m1 = new MatrixSeries("Test 2", 10, 5); assertNotEquals(m1, m2); m2 = new MatrixSeries("Test 2", 10, 5); assertEquals(m1, m2); m1.update(0, 0, 99); assertNotEquals(m1, m2); m2.update(0, 0, 99); assertEquals(m1, m2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { MatrixSeries m1 = new MatrixSeries("Test", 8, 3); m1.update(0, 0, 11.0); m1.update(7, 2, 22.0); MatrixSeries m2 = (MatrixSeries) m1.clone(); assertNotSame(m1, m2); assertSame(m1.getClass(), m2.getClass()); assertEquals(m1, m2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { MatrixSeries m1 = new MatrixSeries("Test", 8, 3); m1.update(0, 0, 11.0); m1.update(7, 2, 22.0); MatrixSeries m2 = TestUtils.serialised(m1); assertEquals(m1, m2); } /** * Tests the getItemColumn() method. */ @Test public void testGetItemColumn() { MatrixSeries m = new MatrixSeries("Test", 3, 2); assertEquals(0, m.getItemColumn(0)); assertEquals(1, m.getItemColumn(1)); assertEquals(0, m.getItemColumn(2)); assertEquals(1, m.getItemColumn(3)); assertEquals(0, m.getItemColumn(4)); assertEquals(1, m.getItemColumn(5)); } /** * Tests the getItemRow() method. */ @Test public void testGetItemRow() { MatrixSeries m = new MatrixSeries("Test", 3, 2); assertEquals(0, m.getItemRow(0)); assertEquals(0, m.getItemRow(1)); assertEquals(1, m.getItemRow(2)); assertEquals(1, m.getItemRow(3)); assertEquals(2, m.getItemRow(4)); assertEquals(2, m.getItemRow(5)); } /** * Tests the getItem() method. */ @Test public void testGetItem() { MatrixSeries m = new MatrixSeries("Test", 3, 2); m.update(0, 0, 0.0); m.update(0, 1, 1.0); m.update(1, 0, 2.0); m.update(1, 1, 3.0); m.update(2, 0, 4.0); m.update(2, 1, 5.0); assertEquals(0.0, m.getItem(0).doubleValue(), 0.001); assertEquals(1.0, m.getItem(1).doubleValue(), 0.001); assertEquals(2.0, m.getItem(2).doubleValue(), 0.001); assertEquals(3.0, m.getItem(3).doubleValue(), 0.001); assertEquals(4.0, m.getItem(4).doubleValue(), 0.001); assertEquals(5.0, m.getItem(5).doubleValue(), 0.001); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/OHLCDataItemTest.java000066400000000000000000000053201463604235500272220ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * OHLCDataItemTest.java * --------------------- * (C) Copyright 2005-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Date; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; /** * Tests for the {@link OHLCDataItem} class. */ public class OHLCDataItemTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { OHLCDataItem i1 = new OHLCDataItem(new Date(1L), 1.0, 2.0, 3.0, 4.0, 5.0); OHLCDataItem i2 = new OHLCDataItem(new Date(1L), 1.0, 2.0, 3.0, 4.0, 5.0); assertEquals(i1, i2); assertEquals(i2, i1); } /** * Instances of this class are immutable - cloning not required. */ @Test public void testCloning() { OHLCDataItem i1 = new OHLCDataItem( new Date(1L), 1.0, 2.0, 3.0, 4.0, 5.0 ); assertFalse(i1 instanceof Cloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { OHLCDataItem i1 = new OHLCDataItem(new Date(1L), 1.0, 2.0, 3.0, 4.0, 5.0); OHLCDataItem i2 = TestUtils.serialised(i1); assertEquals(i1, i2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/TableXYDatasetTest.java000066400000000000000000000177411463604235500277140ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * TableXYDatasetTest.java * ----------------------- * (C) Copyright 2003-present, by Richard Atkinson and Contributors. * * Original Author: Richard Atkinson; * Contributor(s): David Gilbert; * */ package org.jfree.data.xy; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for {@link DefaultTableXYDataset}. */ public class TableXYDatasetTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { DefaultTableXYDataset d1 = new DefaultTableXYDataset(); DefaultTableXYDataset d2 = new DefaultTableXYDataset(); assertEquals(d1, d2); assertEquals(d2, d1); d1.addSeries(createSeries1()); assertNotEquals(d1, d2); d2.addSeries(createSeries1()); assertEquals(d1, d2); } /** * Confirm that cloning works. * * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { DefaultTableXYDataset d1 = new DefaultTableXYDataset(); d1.addSeries(createSeries1()); DefaultTableXYDataset d2 = (DefaultTableXYDataset) d1.clone(); assertNotSame(d1, d2); assertSame(d1.getClass(), d2.getClass()); assertEquals(d1, d2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { DefaultTableXYDataset d1 = new DefaultTableXYDataset(); assertTrue(d1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DefaultTableXYDataset d1 = new DefaultTableXYDataset(); d1.addSeries(createSeries2()); DefaultTableXYDataset d2 = TestUtils.serialised(d1); assertEquals(d1, d2); } /** * Assorted tests. */ @Test public void testTableXYDataset() { XYSeries series1 = createSeries1(); XYSeries series2 = createSeries2(); DefaultTableXYDataset dataset = new DefaultTableXYDataset(); dataset.addSeries(series1); dataset.addSeries(series2); // Test that there are 6 X points and some specific values assertEquals(6, dataset.getItemCount()); assertEquals(6, dataset.getX(0, 5).intValue()); assertNull(dataset.getY(0, 5)); assertEquals(6, dataset.getX(1, 5).intValue()); assertEquals(2, dataset.getY(1, 5).intValue()); // after adding a point to a series, check that there are now 7 // items in each series series2.add(7, 2); assertEquals(7, dataset.getItemCount()); assertNull(dataset.getY(0, 6)); assertEquals(2, dataset.getY(1, 6).intValue()); // Remove series 1 dataset.removeSeries(series1); // Test that there are still 7 X points assertEquals(7, dataset.getItemCount()); // Remove series 2 and add new series dataset.removeSeries(series2); series1 = createSeries1(); dataset.addSeries(series1); // Test that there are now 4 X points assertEquals(4, dataset.getItemCount()); } /** * A test for bug report 788597. */ @Test public void test788597() { DefaultTableXYDataset dataset = new DefaultTableXYDataset(); dataset.addSeries(createSeries1()); assertEquals(4, dataset.getItemCount()); dataset.removeAllSeries(); assertEquals(0, dataset.getItemCount()); } /** * Test that removing all values for a given x works. */ @Test public void testRemoveAllValuesForX() { DefaultTableXYDataset dataset = new DefaultTableXYDataset(); dataset.addSeries(createSeries1()); dataset.addSeries(createSeries2()); dataset.removeAllValuesForX(2.0); assertEquals(5, dataset.getItemCount()); assertEquals(1.0, dataset.getX(0, 0)); assertEquals(3.0, dataset.getX(0, 1)); assertEquals(4.0, dataset.getX(0, 2)); assertEquals(5.0, dataset.getX(0, 3)); assertEquals(6.0, dataset.getX(0, 4)); } /** * Tests to see that pruning removes unwanted x values. */ @Test public void testPrune() { DefaultTableXYDataset dataset = new DefaultTableXYDataset(); dataset.addSeries(createSeries1()); dataset.addSeries(createSeries2()); dataset.removeSeries(1); dataset.prune(); assertEquals(4, dataset.getItemCount()); } /** * Tests the auto-pruning feature. */ @Test public void testAutoPrune() { // WITH AUTOPRUNING DefaultTableXYDataset dataset = new DefaultTableXYDataset(true); dataset.addSeries(createSeriesA()); assertEquals(2, dataset.getItemCount()); // should be 2 items dataset.addSeries(createSeriesB()); assertEquals(2, dataset.getItemCount()); // still 2 dataset.removeSeries(1); assertEquals(1, dataset.getItemCount()); // 1 value pruned. // WITHOUT AUTOPRUNING DefaultTableXYDataset dataset2 = new DefaultTableXYDataset(true); dataset2.addSeries(createSeriesA()); assertEquals(2, dataset2.getItemCount()); // should be 2 items dataset2.addSeries(createSeriesB()); assertEquals(2, dataset2.getItemCount()); // still 2 dataset2.removeSeries(1); assertEquals(1, dataset2.getItemCount()); // still 2. } /** * Creates a series for testing. * * @return A series. */ private XYSeries createSeriesA() { XYSeries s = new XYSeries("A", true, false); s.add(1.0, 1.1); s.add(2.0, null); return s; } /** * Creates a series for testing. * * @return A series. */ private XYSeries createSeriesB() { XYSeries s = new XYSeries("B", true, false); s.add(1.0, null); s.add(2.0, 2.2); return s; } /** * Creates a series for testing. * * @return A series. */ private XYSeries createSeries1() { XYSeries series1 = new XYSeries("Series 1", true, false); series1.add(1.0, 1.0); series1.add(2.0, 1.0); series1.add(4.0, 1.0); series1.add(5.0, 1.0); return series1; } /** * Creates a series for testing. * * @return A series. */ private XYSeries createSeries2() { XYSeries series2 = new XYSeries("Series 2", true, false); series2.add(2.0, 2.0); series2.add(3.0, 2.0); series2.add(4.0, 2.0); series2.add(5.0, 2.0); series2.add(6.0, 2.0); return series2; } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/VectorDataItemTest.java000066400000000000000000000072541463604235500277470ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * VectorDataItemTest.java * ----------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link VectorDataItem} class. */ public class VectorDataItemTest { /** * Test that the equals() method distinguishes all fields. */ @Test public void testEquals() { // default instances VectorDataItem v1 = new VectorDataItem(1.0, 2.0, 3.0, 4.0); VectorDataItem v2 = new VectorDataItem(1.0, 2.0, 3.0, 4.0); assertEquals(v1, v2); assertEquals(v2, v1); v1 = new VectorDataItem(1.1, 2.0, 3.0, 4.0); assertNotEquals(v1, v2); v2 = new VectorDataItem(1.1, 2.0, 3.0, 4.0); assertEquals(v1, v2); v1 = new VectorDataItem(1.1, 2.2, 3.0, 4.0); assertNotEquals(v1, v2); v2 = new VectorDataItem(1.1, 2.2, 3.0, 4.0); assertEquals(v1, v2); v1 = new VectorDataItem(1.1, 2.2, 3.3, 4.0); assertNotEquals(v1, v2); v2 = new VectorDataItem(1.1, 2.2, 3.3, 4.0); assertEquals(v1, v2); v1 = new VectorDataItem(1.1, 2.2, 3.3, 4.4); assertNotEquals(v1, v2); v2 = new VectorDataItem(1.1, 2.2, 3.3, 4.4); assertEquals(v1, v2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { VectorDataItem v1 = new VectorDataItem(1.0, 2.0, 3.0, 4.0); VectorDataItem v2 = new VectorDataItem(1.0, 2.0, 3.0, 4.0); assertEquals(v1, v2); int h1 = v1.hashCode(); int h2 = v2.hashCode(); assertEquals(h1, h2); } /** * Check cloning. */ @Test public void testCloning() throws CloneNotSupportedException { VectorDataItem v1 = new VectorDataItem(1.0, 2.0, 3.0, 4.0); VectorDataItem v2 = (VectorDataItem) v1.clone(); assertNotSame(v1, v2); assertSame(v1.getClass(), v2.getClass()); assertEquals(v1, v2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { VectorDataItem v1 = new VectorDataItem(1.0, 2.0, 3.0, 4.0); VectorDataItem v2 = TestUtils.serialised(v1); assertEquals(v1, v2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/VectorSeriesCollectionTest.java000066400000000000000000000107171463604235500315230ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------------- * VectorSeriesCollectionTest.java * ------------------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link VectorSeriesCollection} class. */ public class VectorSeriesCollectionTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { VectorSeries s1 = new VectorSeries("Series"); s1.add(1.0, 1.1, 1.2, 1.3); VectorSeriesCollection c1 = new VectorSeriesCollection(); c1.addSeries(s1); VectorSeries s2 = new VectorSeries("Series"); s2.add(1.0, 1.1, 1.2, 1.3); VectorSeriesCollection c2 = new VectorSeriesCollection(); c2.addSeries(s2); assertEquals(c1, c2); assertEquals(c2, c1); c1.addSeries(new VectorSeries("Empty Series")); assertNotEquals(c1, c2); c2.addSeries(new VectorSeries("Empty Series")); assertEquals(c1, c2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { VectorSeries s1 = new VectorSeries("Series"); s1.add(1.0, 1.1, 1.2, 1.3); VectorSeriesCollection c1 = new VectorSeriesCollection(); c1.addSeries(s1); VectorSeriesCollection c2 = (VectorSeriesCollection) c1.clone(); assertNotSame(c1, c2); assertSame(c1.getClass(), c2.getClass()); assertEquals(c1, c2); // check independence s1.setDescription("XYZ"); assertNotEquals(c1, c2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { VectorSeriesCollection d1 = new VectorSeriesCollection(); assertTrue(d1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { VectorSeries s1 = new VectorSeries("Series"); s1.add(1.0, 1.1, 1.2, 1.3); VectorSeriesCollection c1 = new VectorSeriesCollection(); c1.addSeries(s1); VectorSeriesCollection c2 = TestUtils.serialised(c1); assertEquals(c1, c2); } /** * Some checks for the removeSeries() method. */ @Test public void testRemoveSeries() { VectorSeries s1 = new VectorSeries("S1"); VectorSeries s2 = new VectorSeries("S2"); VectorSeriesCollection vsc = new VectorSeriesCollection(); vsc.addSeries(s1); vsc.addSeries(s2); assertEquals(2, vsc.getSeriesCount()); boolean b = vsc.removeSeries(s1); assertTrue(b); assertEquals(1, vsc.getSeriesCount()); assertEquals("S2", vsc.getSeriesKey(0)); b = vsc.removeSeries(new VectorSeries("NotInDataset")); assertFalse(b); assertEquals(1, vsc.getSeriesCount()); b = vsc.removeSeries(s2); assertTrue(b); assertEquals(0, vsc.getSeriesCount()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/VectorSeriesTest.java000066400000000000000000000210511463604235500275000ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * VectorSeriesTest.java * --------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.chart.TestUtils; import org.jfree.data.general.SeriesChangeEvent; import org.jfree.data.general.SeriesChangeListener; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link VectorSeries} class. */ public class VectorSeriesTest implements SeriesChangeListener { SeriesChangeEvent lastEvent; /** * Records the last event. * * @param event the event. */ @Override public void seriesChanged(SeriesChangeEvent event) { this.lastEvent = event; } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { VectorSeries s1 = new VectorSeries("s1"); VectorSeries s2 = new VectorSeries("s1"); assertEquals(s1, s2); // seriesKey s1 = new VectorSeries("s2"); assertNotEquals(s1, s2); s2 = new VectorSeries("s2"); assertEquals(s1, s2); // autoSort s1 = new VectorSeries("s2", true, true); assertNotEquals(s1, s2); s2 = new VectorSeries("s2", true, true); assertEquals(s1, s2); // allowDuplicateValues s1 = new VectorSeries("s2", false, false); assertNotEquals(s1, s2); s2 = new VectorSeries("s2", false, false); assertEquals(s1, s2); // add a value s1.add(1.0, 0.5, 1.5, 2.0); assertNotEquals(s1, s2); s2.add(1.0, 0.5, 1.5, 2.0); assertEquals(s2, s1); // add another value s1.add(2.0, 0.5, 1.5, 2.0); assertNotEquals(s1, s2); s2.add(2.0, 0.5, 1.5, 2.0); assertEquals(s2, s1); // remove a value s1.remove(new XYCoordinate(1.0, 0.5)); assertNotEquals(s1, s2); s2.remove(new XYCoordinate(1.0, 0.5)); assertEquals(s2, s1); } /** * Confirm that cloning works. * * @throws java.lang.CloneNotSupportedException if there is a problem cloning. */ @Test public void testCloning() throws CloneNotSupportedException { VectorSeries s1 = new VectorSeries("s1"); s1.add(1.0, 0.5, 1.5, 2.0); VectorSeries s2 = (VectorSeries) s1.clone(); assertNotSame(s1, s2); assertSame(s1.getClass(), s2.getClass()); assertEquals(s1, s2); // check independence s1.add(4.0, 5.0, 6.0, 7.0); assertNotEquals(s1, s2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { VectorSeries s1 = new VectorSeries("s1"); s1.add(1.0, 0.5, 1.5, 2.0); VectorSeries s2 = TestUtils.serialised(s1); assertEquals(s1, s2); } /** * Simple test for the indexOf() method. */ @Test public void testIndexOf() { VectorSeries s1 = new VectorSeries("Series 1"); s1.add(1.0, 1.0, 1.0, 2.0); s1.add(2.0, 2.0, 2.0, 3.0); s1.add(3.0, 3.0, 3.0, 4.0); assertEquals(0, s1.indexOf(new XYCoordinate(1.0, 1.0))); } /** * A check for the indexOf() method for an unsorted series. */ @Test public void testIndexOf2() { VectorSeries s1 = new VectorSeries("Series 1"); s1.add(1.0, 1.0, 1.0, 2.0); s1.add(3.0, 3.0, 3.0, 3.0); s1.add(2.0, 2.0, 2.0, 2.0); assertEquals(0, s1.indexOf(new XYCoordinate(1.0, 1.0))); assertEquals(1, s1.indexOf(new XYCoordinate(3.0, 3.0))); assertEquals(2, s1.indexOf(new XYCoordinate(2.0, 2.0))); } /** * Simple test for the remove() method. */ @Test public void testRemove() { VectorSeries s1 = new VectorSeries("Series 1"); s1.add(1.0, 1.0, 1.0, 2.0); s1.add(3.0, 3.0, 3.0, 3.0); s1.add(2.0, 2.0, 2.0, 2.0); assertEquals(3, s1.getItemCount()); s1.remove(new XYCoordinate(2.0, 2.0)); assertEquals(3.0, s1.getXValue(1), EPSILON); s1.remove(new XYCoordinate(1.0, 1.0)); assertEquals(3.0, s1.getXValue(0), EPSILON); } private static final double EPSILON = 0.0000000001; /** * When items are added with duplicate x-values, we expect them to remain * in the order they were added. */ @Test public void testAdditionOfDuplicateXValues() { VectorSeries s1 = new VectorSeries("Series 1"); s1.add(1.0, 1.0, 1.0, 1.0); s1.add(2.0, 2.0, 2.0, 2.0); s1.add(2.0, 2.0, 3.0, 3.0); s1.add(2.0, 3.0, 4.0, 4.0); s1.add(3.0, 5.0, 5.0, 5.0); assertEquals(1.0, s1.getVectorXValue(0), EPSILON); assertEquals(2.0, s1.getVectorXValue(1), EPSILON); assertEquals(3.0, s1.getVectorXValue(2), EPSILON); assertEquals(4.0, s1.getVectorXValue(3), EPSILON); assertEquals(5.0, s1.getVectorXValue(4), EPSILON); } /** * Some checks for the add() method for an UNSORTED series. */ @Test public void testAdd() { VectorSeries series = new VectorSeries("Series", false, true); series.add(5.0, 5.50, 5.50, 5.50); series.add(5.1, 5.51, 5.51, 5.51); series.add(6.0, 6.6, 6.6, 6.6); series.add(3.0, 3.3, 3.3, 3.3); series.add(4.0, 4.4, 4.4, 4.4); series.add(2.0, 2.2, 2.2, 2.2); series.add(1.0, 1.1, 1.1, 1.1); assertEquals(5.5, series.getVectorXValue(0), EPSILON); assertEquals(5.51, series.getVectorXValue(1), EPSILON); assertEquals(6.6, series.getVectorXValue(2), EPSILON); assertEquals(3.3, series.getVectorXValue(3), EPSILON); assertEquals(4.4, series.getVectorXValue(4), EPSILON); assertEquals(2.2, series.getVectorXValue(5), EPSILON); assertEquals(1.1, series.getVectorXValue(6), EPSILON); } /** * A simple check that the maximumItemCount attribute is working. */ @Test public void testSetMaximumItemCount() { VectorSeries s1 = new VectorSeries("S1"); assertEquals(Integer.MAX_VALUE, s1.getMaximumItemCount()); s1.setMaximumItemCount(2); assertEquals(2, s1.getMaximumItemCount()); s1.add(1.0, 1.1, 1.1, 1.1); s1.add(2.0, 2.2, 2.2, 2.2); s1.add(3.0, 3.3, 3.3, 3.3); assertEquals(2.0, s1.getXValue(0), EPSILON); assertEquals(3.0, s1.getXValue(1), EPSILON); } /** * Check that the maximum item count can be applied retrospectively. */ @Test public void testSetMaximumItemCount2() { VectorSeries s1 = new VectorSeries("S1"); s1.add(1.0, 1.1, 1.1, 1.1); s1.add(2.0, 2.2, 2.2, 2.2); s1.add(3.0, 3.3, 3.3, 3.3); s1.setMaximumItemCount(2); assertEquals(2.0, s1.getXValue(0), EPSILON); assertEquals(3.0, s1.getXValue(1), EPSILON); } /** * Some checks for the clear() method. */ @Test public void testClear() { VectorSeries s1 = new VectorSeries("S1"); s1.addChangeListener(this); s1.clear(); assertNull(this.lastEvent); assertTrue(s1.isEmpty()); s1.add(1.0, 2.0, 3.0, 4.0); assertFalse(s1.isEmpty()); s1.clear(); assertNotNull(this.lastEvent); assertTrue(s1.isEmpty()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/VectorTest.java000066400000000000000000000056761463604235500263440ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------- * VectorTests.java * ---------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link Vector} class. */ public class VectorTest { /** * Test that the equals() method distinguishes all fields. */ @Test public void testEquals() { // default instances Vector v1 = new Vector(1.0, 2.0); Vector v2 = new Vector(1.0, 2.0); assertEquals(v1, v2); assertEquals(v2, v1); v1 = new Vector(1.1, 2.0); assertNotEquals(v1, v2); v2 = new Vector(1.1, 2.0); assertEquals(v1, v2); v1 = new Vector(1.1, 2.2); assertNotEquals(v1, v2); v2 = new Vector(1.1, 2.2); assertEquals(v1, v2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { Vector v1 = new Vector(1.0, 2.0); Vector v2 = new Vector(1.0, 2.0); assertEquals(v1, v2); int h1 = v1.hashCode(); int h2 = v2.hashCode(); assertEquals(h1, h2); } /** * Immutable class is not cloneable. */ @Test public void testCloning() { Vector v1 = new Vector(1.0, 2.0); assertFalse(v1 instanceof Cloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { Vector v1 = new Vector(1.0, 2.0); Vector v2 = TestUtils.serialised(v1); assertEquals(v1, v2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/XIntervalDataItemTest.java000066400000000000000000000100601463604235500304060ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * XIntervalDataItemTest.java * -------------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XIntervalDataItem} class. */ public class XIntervalDataItemTest { private static final double EPSILON = 0.00000000001; /** * Some checks for the constructor. */ @Test public void testConstructor1() { XIntervalDataItem item1 = new XIntervalDataItem(1.0, 2.0, 3.0, 4.0); assertEquals(1.0, item1.getX()); assertEquals(2.0, item1.getXLowValue(), EPSILON); assertEquals(3.0, item1.getXHighValue(), EPSILON); assertEquals(4.0, item1.getYValue(), EPSILON); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { XIntervalDataItem item1 = new XIntervalDataItem(1.0, 2.0, 3.0, 4.0); XIntervalDataItem item2 = new XIntervalDataItem(1.0, 2.0, 3.0, 4.0); assertEquals(item1, item2); assertEquals(item2, item1); // x item1 = new XIntervalDataItem(1.1, 2.0, 3.0, 4.0); assertNotEquals(item1, item2); item2 = new XIntervalDataItem(1.1, 2.0, 3.0, 4.0); assertEquals(item1, item2); // xLow item1 = new XIntervalDataItem(1.1, 2.2, 3.0, 4.0); assertNotEquals(item1, item2); item2 = new XIntervalDataItem(1.1, 2.2, 3.0, 4.0); assertEquals(item1, item2); // xHigh item1 = new XIntervalDataItem(1.1, 2.2, 3.3, 4.0); assertNotEquals(item1, item2); item2 = new XIntervalDataItem(1.1, 2.2, 3.3, 4.0); assertEquals(item1, item2); // y item1 = new XIntervalDataItem(1.1, 2.2, 3.3, 4.4); assertNotEquals(item1, item2); item2 = new XIntervalDataItem(1.1, 2.2, 3.3, 4.4); assertEquals(item1, item2); } /** * Some checks for the clone() method. * * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { XIntervalDataItem item1 = new XIntervalDataItem(1.0, 2.0, 3.0, 4.0); XIntervalDataItem item2 = (XIntervalDataItem) item1.clone(); assertNotSame(item1, item2); assertSame(item1.getClass(), item2.getClass()); assertEquals(item1, item2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XIntervalDataItem item1 = new XIntervalDataItem(1.0, 2.0, 3.0, 4.0); XIntervalDataItem item2 = TestUtils.serialised(item1); assertEquals(item1, item2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/XIntervalSeriesCollectionTest.java000066400000000000000000000122351463604235500321720ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------------- * XIntervalSeriesCollectionTest.java * ---------------------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XIntervalSeriesCollection} class. */ public class XIntervalSeriesCollectionTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { XIntervalSeriesCollection c1 = new XIntervalSeriesCollection(); XIntervalSeriesCollection c2 = new XIntervalSeriesCollection(); assertEquals(c1, c2); // add a series XIntervalSeries s1 = new XIntervalSeries("Series"); s1.add(1.0, 1.1, 1.2, 1.3); c1.addSeries(s1); assertNotEquals(c1, c2); XIntervalSeries s2 = new XIntervalSeries("Series"); s2.add(1.0, 1.1, 1.2, 1.3); c2.addSeries(s2); assertEquals(c1, c2); // add an empty series c1.addSeries(new XIntervalSeries("Empty Series")); assertNotEquals(c1, c2); c2.addSeries(new XIntervalSeries("Empty Series")); assertEquals(c1, c2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { XIntervalSeriesCollection c1 = new XIntervalSeriesCollection(); XIntervalSeries s1 = new XIntervalSeries("Series"); s1.add(1.0, 1.1, 1.2, 1.3); c1.addSeries(s1); XIntervalSeriesCollection c2 = (XIntervalSeriesCollection) c1.clone(); assertNotSame(c1, c2); assertSame(c1.getClass(), c2.getClass()); assertEquals(c1, c2); // check independence s1.setDescription("XYZ"); assertNotEquals(c1, c2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { XIntervalSeriesCollection c1 = new XIntervalSeriesCollection(); assertTrue(c1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XIntervalSeriesCollection c1 = new XIntervalSeriesCollection(); XIntervalSeries s1 = new XIntervalSeries("Series"); s1.add(1.0, 1.1, 1.2, 1.3); XIntervalSeriesCollection c2 = TestUtils.serialised(c1); assertEquals(c1, c2); } /** * Some basic checks for the removeSeries() method. */ @Test public void testRemoveSeries() { XIntervalSeriesCollection c = new XIntervalSeriesCollection(); XIntervalSeries s1 = new XIntervalSeries("s1"); c.addSeries(s1); c.removeSeries(0); assertEquals(0, c.getSeriesCount()); c.addSeries(s1); boolean pass = false; try { c.removeSeries(-1); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); pass = false; try { c.removeSeries(1); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * A test for bug report 1170825 (originally affected XYSeriesCollection, * this test is just copied over). */ @Test public void test1170825() { XIntervalSeries s1 = new XIntervalSeries("Series1"); XIntervalSeriesCollection dataset = new XIntervalSeriesCollection(); dataset.addSeries(s1); try { /* XYSeries s = */ dataset.getSeries(1); } catch (IllegalArgumentException e) { // correct outcome } catch (IndexOutOfBoundsException e) { fail(); // wrong outcome } } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/XIntervalSeriesTest.java000066400000000000000000000217661463604235500301670ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * XIntervalSeriesTest.java * ------------------------ * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.chart.TestUtils; import org.jfree.data.general.SeriesChangeEvent; import org.jfree.data.general.SeriesChangeListener; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XIntervalSeries} class. */ public class XIntervalSeriesTest implements SeriesChangeListener { SeriesChangeEvent lastEvent; /** * Records the last event. * * @param event the event. */ @Override public void seriesChanged(SeriesChangeEvent event) { this.lastEvent = event; } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { XIntervalSeries s1 = new XIntervalSeries("s1"); XIntervalSeries s2 = new XIntervalSeries("s1"); assertEquals(s1, s2); // seriesKey s1 = new XIntervalSeries("s2"); assertNotEquals(s1, s2); s2 = new XIntervalSeries("s2"); assertEquals(s1, s2); // autoSort s1 = new XIntervalSeries("s2", false, true); assertNotEquals(s1, s2); s2 = new XIntervalSeries("s2", false, true); assertEquals(s1, s2); // allowDuplicateValues s1 = new XIntervalSeries("s2", false, false); assertNotEquals(s1, s2); s2 = new XIntervalSeries("s2", false, false); assertEquals(s1, s2); // add a value s1.add(1.0, 0.5, 1.5, 2.0); assertNotEquals(s1, s2); s2.add(1.0, 0.5, 1.5, 2.0); assertEquals(s2, s1); // add another value s1.add(2.0, 0.5, 1.5, 2.0); assertNotEquals(s1, s2); s2.add(2.0, 0.5, 1.5, 2.0); assertEquals(s2, s1); // remove a value s1.remove(1.0); assertNotEquals(s1, s2); s2.remove(1.0); assertEquals(s2, s1); } /** * Confirm that cloning works. * * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { XIntervalSeries s1 = new XIntervalSeries("s1"); s1.add(1.0, 0.5, 1.5, 2.0); XIntervalSeries s2 = (XIntervalSeries) s1.clone(); assertNotSame(s1, s2); assertSame(s1.getClass(), s2.getClass()); assertEquals(s1, s2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XIntervalSeries s1 = new XIntervalSeries("s1"); s1.add(1.0, 0.5, 1.5, 2.0); XIntervalSeries s2 = TestUtils.serialised(s1); assertEquals(s1, s2); } /** * Simple test for the indexOf() method. */ @Test public void testIndexOf() { XIntervalSeries s1 = new XIntervalSeries("Series 1"); s1.add(1.0, 1.0, 1.0, 2.0); s1.add(2.0, 2.0, 2.0, 3.0); s1.add(3.0, 3.0, 3.0, 4.0); assertEquals(0, s1.indexOf(1.0)); } /** * A check for the indexOf() method for an unsorted series. */ @Test public void testIndexOf2() { XIntervalSeries s1 = new XIntervalSeries("Series 1", false, true); s1.add(1.0, 1.0, 1.0, 2.0); s1.add(3.0, 3.0, 3.0, 3.0); s1.add(2.0, 2.0, 2.0, 2.0); assertEquals(0, s1.indexOf(1.0)); assertEquals(1, s1.indexOf(3.0)); assertEquals(2, s1.indexOf(2.0)); } /** * Simple test for the remove() method. */ @Test public void testRemove() { XIntervalSeries s1 = new XIntervalSeries("Series 1"); s1.add(1.0, 1.0, 1.0, 2.0); s1.add(2.0, 2.0, 2.0, 2.0); s1.add(3.0, 3.0, 3.0, 3.0); assertEquals(3, s1.getItemCount()); s1.remove(2.0); assertEquals(3.0, s1.getX(1)); s1.remove(1.0); assertEquals(3.0, s1.getX(0)); } private static final double EPSILON = 0.0000000001; /** * When items are added with duplicate x-values, we expect them to remain * in the order they were added. */ @Test public void testAdditionOfDuplicateXValues() { XIntervalSeries s1 = new XIntervalSeries("Series 1"); s1.add(1.0, 1.0, 1.0, 1.0); s1.add(2.0, 2.0, 2.0, 2.0); s1.add(2.0, 3.0, 3.0, 3.0); s1.add(2.0, 4.0, 4.0, 4.0); s1.add(3.0, 5.0, 5.0, 5.0); assertEquals(1.0, s1.getYValue(0), EPSILON); assertEquals(2.0, s1.getYValue(1), EPSILON); assertEquals(3.0, s1.getYValue(2), EPSILON); assertEquals(4.0, s1.getYValue(3), EPSILON); assertEquals(5.0, s1.getYValue(4), EPSILON); } /** * Some checks for the add() method for an UNSORTED series. */ @Test public void testAdd() { XIntervalSeries series = new XIntervalSeries("Series", false, true); series.add(5.0, 5.50, 5.50, 5.50); series.add(5.1, 5.51, 5.51, 5.51); series.add(6.0, 6.6, 6.6, 6.6); series.add(3.0, 3.3, 3.3, 3.3); series.add(4.0, 4.4, 4.4, 4.4); series.add(2.0, 2.2, 2.2, 2.2); series.add(1.0, 1.1, 1.1, 1.1); assertEquals(5.5, series.getYValue(0), EPSILON); assertEquals(5.51, series.getYValue(1), EPSILON); assertEquals(6.6, series.getYValue(2), EPSILON); assertEquals(3.3, series.getYValue(3), EPSILON); assertEquals(4.4, series.getYValue(4), EPSILON); assertEquals(2.2, series.getYValue(5), EPSILON); assertEquals(1.1, series.getYValue(6), EPSILON); } /** * A simple check that the maximumItemCount attribute is working. */ @Test public void testSetMaximumItemCount() { XIntervalSeries s1 = new XIntervalSeries("S1"); assertEquals(Integer.MAX_VALUE, s1.getMaximumItemCount()); s1.setMaximumItemCount(2); assertEquals(2, s1.getMaximumItemCount()); s1.add(1.0, 1.1, 1.1, 1.1); s1.add(2.0, 2.2, 2.2, 2.2); s1.add(3.0, 3.3, 3.3, 3.3); assertEquals(2.0, s1.getX(0).doubleValue(), EPSILON); assertEquals(3.0, s1.getX(1).doubleValue(), EPSILON); } /** * Check that the maximum item count can be applied retrospectively. */ @Test public void testSetMaximumItemCount2() { XIntervalSeries s1 = new XIntervalSeries("S1"); s1.add(1.0, 1.1, 1.1, 1.1); s1.add(2.0, 2.2, 2.2, 2.2); s1.add(3.0, 3.3, 3.3, 3.3); s1.setMaximumItemCount(2); assertEquals(2.0, s1.getX(0).doubleValue(), EPSILON); assertEquals(3.0, s1.getX(1).doubleValue(), EPSILON); } /** * Some checks for the clear() method. */ @Test public void testClear() { XIntervalSeries s1 = new XIntervalSeries("S1"); s1.addChangeListener(this); s1.clear(); assertNull(this.lastEvent); assertTrue(s1.isEmpty()); s1.add(1.0, 2.0, 3.0, 4.0); assertFalse(s1.isEmpty()); s1.clear(); assertNotNull(this.lastEvent); assertTrue(s1.isEmpty()); } /** * A simple check for getXLowValue(). */ @Test public void testGetXLowValue() { XIntervalSeries s1 = new XIntervalSeries("S1"); s1.add(1.0, 2.0, 3.0, 4.0); assertEquals(2.0, s1.getXLowValue(0), EPSILON); s1.add(2.0, 1.0, 4.0, 2.5); assertEquals(1.0, s1.getXLowValue(1), EPSILON); } /** * A simple check for getXHighValue(). */ @Test public void testGetXHighValue() { XIntervalSeries s1 = new XIntervalSeries("S1"); s1.add(1.0, 2.0, 3.0, 4.0); assertEquals(3.0, s1.getXHighValue(0), EPSILON); s1.add(2.0, 1.0, 4.0, 2.5); assertEquals(4.0, s1.getXHighValue(1), EPSILON); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/XYBarDatasetTest.java000066400000000000000000000106151463604235500273620ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * XYBarDatasetTest.java * --------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Some tests for the {@link XYBarDataset} class. */ public class XYBarDatasetTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { DefaultXYDataset d1 = new DefaultXYDataset(); double[] x1 = new double[] {1.0, 2.0, 3.0}; double[] y1 = new double[] {4.0, 5.0, 6.0}; double[][] data1 = new double[][] {x1, y1}; d1.addSeries("S1", data1); DefaultXYDataset d2 = new DefaultXYDataset(); double[] x2 = new double[] {1.0, 2.0, 3.0}; double[] y2 = new double[] {4.0, 5.0, 6.0}; double[][] data2 = new double[][] {x2, y2}; d2.addSeries("S1", data2); XYBarDataset bd1 = new XYBarDataset(d1, 5.0); XYBarDataset bd2 = new XYBarDataset(d2, 5.0); assertEquals(bd1, bd2); assertEquals(bd2, bd1); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { DefaultXYDataset d1 = new DefaultXYDataset(); double[] x1 = new double[] {1.0, 2.0, 3.0}; double[] y1 = new double[] {4.0, 5.0, 6.0}; double[][] data1 = new double[][] {x1, y1}; d1.addSeries("S1", data1); XYBarDataset bd1 = new XYBarDataset(d1, 5.0); XYBarDataset bd2 = (XYBarDataset) bd1.clone(); assertNotSame(bd1, bd2); assertSame(bd1.getClass(), bd2.getClass()); assertEquals(bd1, bd2); // check independence d1 = (DefaultXYDataset) bd1.getUnderlyingDataset(); d1.addSeries("S2", new double[][] {{1.0}, {2.0}}); assertNotEquals(bd1, bd2); DefaultXYDataset d2 = (DefaultXYDataset) bd2.getUnderlyingDataset(); d2.addSeries("S2", new double[][] {{1.0}, {2.0}}); assertEquals(bd1, bd2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { DefaultXYDataset d1 = new DefaultXYDataset(); double[] x1 = new double[] {1.0, 2.0, 3.0}; double[] y1 = new double[] {4.0, 5.0, 6.0}; double[][] data1 = new double[][] {x1, y1}; d1.addSeries("S1", data1); XYBarDataset bd1 = new XYBarDataset(d1, 5.0); assertTrue(bd1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { DefaultXYDataset d1 = new DefaultXYDataset(); double[] x1 = new double[] {1.0, 2.0, 3.0}; double[] y1 = new double[] {4.0, 5.0, 6.0}; double[][] data1 = new double[][] {x1, y1}; d1.addSeries("S1", data1); XYBarDataset bd1 = new XYBarDataset(d1, 5.0); XYBarDataset bd2 = TestUtils.serialised(bd1); assertEquals(bd1, bd2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/XYCoordinateTest.java000066400000000000000000000060771463604235500274460ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------- * XYCoordinateTest.java * --------------------- * (C) Copyright 2007-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XYCoordinate} class. */ public class XYCoordinateTest { /** * Test that the equals() method distinguishes all fields. */ @Test public void testEquals() { // default instances XYCoordinate v1 = new XYCoordinate(1.0, 2.0); XYCoordinate v2 = new XYCoordinate(1.0, 2.0); assertEquals(v1, v2); assertEquals(v2, v1); v1 = new XYCoordinate(1.1, 2.0); assertNotEquals(v1, v2); v2 = new XYCoordinate(1.1, 2.0); assertEquals(v1, v2); v1 = new XYCoordinate(1.1, 2.2); assertNotEquals(v1, v2); v2 = new XYCoordinate(1.1, 2.2); assertEquals(v1, v2); } /** * Two objects that are equal are required to return the same hashCode. */ @Test public void testHashcode() { XYCoordinate v1 = new XYCoordinate(1.0, 2.0); XYCoordinate v2 = new XYCoordinate(1.0, 2.0); assertEquals(v1, v2); int h1 = v1.hashCode(); int h2 = v2.hashCode(); assertEquals(h1, h2); } /** * Immutable class is not cloneable. */ @Test public void testCloning() { XYCoordinate v1 = new XYCoordinate(1.0, 2.0); assertFalse(v1 instanceof Cloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XYCoordinate v1 = new XYCoordinate(1.0, 2.0); XYCoordinate v2 = TestUtils.serialised(v1); assertEquals(v1, v2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/XYDataItemTest.java000066400000000000000000000051251463604235500270400ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * XYDataItemTest.java * ------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XYDataItem} class. */ public class XYDataItemTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { XYDataItem i1 = new XYDataItem(1.0, 1.1); XYDataItem i2 = new XYDataItem(1.0, 1.1); assertEquals(i1, i2); assertEquals(i2, i1); i1.setY(9.9); assertNotEquals(i1, i2); i2.setY(9.9); assertEquals(i1, i2); } /** * Confirm that cloning works. */ @Test public void testCloning() { XYDataItem i1 = new XYDataItem(1.0, 1.1); XYDataItem i2 = (XYDataItem) i1.clone(); assertNotSame(i1, i2); assertSame(i1.getClass(), i2.getClass()); assertEquals(i1, i2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XYDataItem i1 = new XYDataItem(1.0, 1.1); XYDataItem i2 = TestUtils.serialised(i1); assertEquals(i1, i2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/XYIntervalDataItemTest.java000066400000000000000000000115771463604235500305550ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * XYIntervalDataItemTest.java * --------------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XYIntervalDataItem} class. */ public class XYIntervalDataItemTest { private static final double EPSILON = 0.000000001; /** * Some checks for the constructor. */ @Test public void testConstructor1() { XYIntervalDataItem item1 = new XYIntervalDataItem(1.0, 0.5, 1.5, 2.0, 1.9, 2.1); assertEquals(1.0, item1.getX(), EPSILON); assertEquals(0.5, item1.getXLowValue(), EPSILON); assertEquals(1.5, item1.getXHighValue(), EPSILON); assertEquals(2.0, item1.getYValue(), EPSILON); assertEquals(1.9, item1.getYLowValue(), EPSILON); assertEquals(2.1, item1.getYHighValue(), EPSILON); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { XYIntervalDataItem item1 = new XYIntervalDataItem(1.0, 0.5, 1.5, 2.0, 1.9, 2.1); XYIntervalDataItem item2 = new XYIntervalDataItem(1.0, 0.5, 1.5, 2.0, 1.9, 2.1); assertEquals(item1, item2); assertEquals(item2, item1); // x item1 = new XYIntervalDataItem(1.1, 0.5, 1.5, 2.0, 1.9, 2.1); assertNotEquals(item1, item2); item2 = new XYIntervalDataItem(1.1, 0.5, 1.5, 2.0, 1.9, 2.1); assertEquals(item1, item2); // xLow item1 = new XYIntervalDataItem(1.1, 0.55, 1.5, 2.0, 1.9, 2.1); assertNotEquals(item1, item2); item2 = new XYIntervalDataItem(1.1, 0.55, 1.5, 2.0, 1.9, 2.1); assertEquals(item1, item2); // xHigh item1 = new XYIntervalDataItem(1.1, 0.55, 1.55, 2.0, 1.9, 2.1); assertNotEquals(item1, item2); item2 = new XYIntervalDataItem(1.1, 0.55, 1.55, 2.0, 1.9, 2.1); assertEquals(item1, item2); // y item1 = new XYIntervalDataItem(1.1, 0.55, 1.55, 2.2, 1.9, 2.1); assertNotEquals(item1, item2); item2 = new XYIntervalDataItem(1.1, 0.55, 1.55, 2.2, 1.9, 2.1); assertEquals(item1, item2); // yLow item1 = new XYIntervalDataItem(1.1, 0.55, 1.55, 2.2, 1.99, 2.1); assertNotEquals(item1, item2); item2 = new XYIntervalDataItem(1.1, 0.55, 1.55, 2.2, 1.99, 2.1); assertEquals(item1, item2); // yHigh item1 = new XYIntervalDataItem(1.1, 0.55, 1.55, 2.2, 1.99, 2.11); assertNotEquals(item1, item2); item2 = new XYIntervalDataItem(1.1, 0.55, 1.55, 2.2, 1.99, 2.11); assertEquals(item1, item2); } /** * Some checks for the clone() method. * * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { XYIntervalDataItem item1 = new XYIntervalDataItem(1.0, 0.5, 1.5, 2.0, 1.9, 2.1); XYIntervalDataItem item2 = (XYIntervalDataItem) item1.clone(); assertNotSame(item1, item2); assertSame(item1.getClass(), item2.getClass()); assertEquals(item1, item2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XYIntervalDataItem item1 = new XYIntervalDataItem(1.0, 0.5, 1.5, 2.0, 1.9, 2.1); XYIntervalDataItem item2 = TestUtils.serialised(item1); assertEquals(item1, item2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/XYIntervalSeriesCollectionTest.java000066400000000000000000000127751463604235500323340ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------------------- * XYIntervalSeriesCollectionTest.java * ----------------------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XYIntervalSeriesCollection} class. */ public class XYIntervalSeriesCollectionTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { XYIntervalSeriesCollection c1 = new XYIntervalSeriesCollection(); XYIntervalSeriesCollection c2 = new XYIntervalSeriesCollection(); assertEquals(c1, c2); // add a series XYIntervalSeries s1 = new XYIntervalSeries("Series"); s1.add(1.0, 1.1, 1.2, 1.3, 1.4, 1.5); c1.addSeries(s1); assertNotEquals(c1, c2); XYIntervalSeries s2 = new XYIntervalSeries("Series"); s2.add(1.0, 1.1, 1.2, 1.3, 1.4, 1.5); c2.addSeries(s2); assertEquals(c1, c2); // add an empty series c1.addSeries(new XYIntervalSeries("Empty Series")); assertNotEquals(c1, c2); c2.addSeries(new XYIntervalSeries("Empty Series")); assertEquals(c1, c2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { XYIntervalSeriesCollection c1 = new XYIntervalSeriesCollection(); XYIntervalSeries s1 = new XYIntervalSeries("Series"); s1.add(1.0, 1.1, 1.2, 1.3, 1.4, 1.5); XYIntervalSeriesCollection c2 = (XYIntervalSeriesCollection) c1.clone(); assertNotSame(c1, c2); assertSame(c1.getClass(), c2.getClass()); assertEquals(c1, c2); // check independence c1.addSeries(new XYIntervalSeries("Empty")); assertNotEquals(c1, c2); c2.addSeries(new XYIntervalSeries("Empty")); assertEquals(c1, c2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { XYIntervalSeriesCollection c1 = new XYIntervalSeriesCollection(); assertTrue(c1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XYIntervalSeriesCollection c1 = new XYIntervalSeriesCollection(); XYIntervalSeries s1 = new XYIntervalSeries("Series"); s1.add(1.0, 1.1, 1.2, 1.3, 1.4, 1.5); XYIntervalSeriesCollection c2 = TestUtils.serialised(c1); assertEquals(c1, c2); // check independence c1.addSeries(new XYIntervalSeries("Empty")); assertNotEquals(c1, c2); c2.addSeries(new XYIntervalSeries("Empty")); assertEquals(c1, c2); } /** * Some basic checks for the removeSeries() method. */ @Test public void testRemoveSeries() { XYIntervalSeriesCollection c = new XYIntervalSeriesCollection(); XYIntervalSeries s1 = new XYIntervalSeries("s1"); c.addSeries(s1); c.removeSeries(0); assertEquals(0, c.getSeriesCount()); c.addSeries(s1); boolean pass = false; try { c.removeSeries(-1); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); pass = false; try { c.removeSeries(1); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * A test for bug report 1170825 (originally affected XYSeriesCollection, * this test is just copied over). */ @Test public void test1170825() { XYIntervalSeries s1 = new XYIntervalSeries("Series1"); XYIntervalSeriesCollection dataset = new XYIntervalSeriesCollection(); dataset.addSeries(s1); try { /* XYSeries s = */ dataset.getSeries(1); } catch (IllegalArgumentException e) { // correct outcome } catch (IndexOutOfBoundsException e) { fail(); // wrong outcome } } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/XYIntervalSeriesTest.java000066400000000000000000000223451463604235500303120ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------- * XYIntervalSeriesTest.java * ------------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.chart.TestUtils; import org.jfree.data.general.SeriesChangeEvent; import org.jfree.data.general.SeriesChangeListener; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XYIntervalSeries} class. */ public class XYIntervalSeriesTest implements SeriesChangeListener { SeriesChangeEvent lastEvent; /** * Records the last event. * * @param event the event. */ @Override public void seriesChanged(SeriesChangeEvent event) { this.lastEvent = event; } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { XYIntervalSeries s1 = new XYIntervalSeries("s1"); XYIntervalSeries s2 = new XYIntervalSeries("s1"); assertEquals(s1, s2); // seriesKey s1 = new XYIntervalSeries("s2"); assertNotEquals(s1, s2); s2 = new XYIntervalSeries("s2"); assertEquals(s1, s2); // autoSort s1 = new XYIntervalSeries("s2", false, true); assertNotEquals(s1, s2); s2 = new XYIntervalSeries("s2", false, true); assertEquals(s1, s2); // allowDuplicateValues s1 = new XYIntervalSeries("s2", false, false); assertNotEquals(s1, s2); s2 = new XYIntervalSeries("s2", false, false); assertEquals(s1, s2); // add a value s1.add(1.0, 0.5, 1.5, 2.0, 1.9, 2.1); assertNotEquals(s1, s2); s2.add(1.0, 0.5, 1.5, 2.0, 1.9, 2.1); assertEquals(s2, s1); // add another value s1.add(2.0, 0.5, 1.5, 2.0, 1.9, 2.1); assertNotEquals(s1, s2); s2.add(2.0, 0.5, 1.5, 2.0, 1.9, 2.1); assertEquals(s2, s1); // remove a value s1.remove(1.0); assertNotEquals(s1, s2); s2.remove(1.0); assertEquals(s2, s1); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { XYIntervalSeries s1 = new XYIntervalSeries("s1"); s1.add(1.0, 0.5, 1.5, 2.0, 1.9, 2.01); XYIntervalSeries s2 = (XYIntervalSeries) s1.clone(); assertNotSame(s1, s2); assertSame(s1.getClass(), s2.getClass()); assertEquals(s1, s2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XYIntervalSeries s1 = new XYIntervalSeries("s1"); s1.add(1.0, 0.5, 1.5, 2.0, 1.9, 2.1); XYIntervalSeries s2 = TestUtils.serialised(s1); assertEquals(s1, s2); } /** * Simple test for the indexOf() method. */ @Test public void testIndexOf() { XYIntervalSeries s1 = new XYIntervalSeries("Series 1"); s1.add(1.0, 1.0, 1.0, 2.0, 1.9, 2.1); s1.add(2.0, 2.0, 2.0, 3.0, 2.9, 3.1); s1.add(3.0, 3.0, 3.0, 4.0, 3.9, 4.1); assertEquals(0, s1.indexOf(1.0)); } /** * A check for the indexOf() method for an unsorted series. */ @Test public void testIndexOf2() { XYIntervalSeries s1 = new XYIntervalSeries("Series 1", false, true); s1.add(1.0, 1.0, 1.0, 2.0, 1.9, 2.1); s1.add(3.0, 3.0, 3.0, 3.0, 2.9, 3.1); s1.add(2.0, 2.0, 2.0, 2.0, 1.9, 2.1); assertEquals(0, s1.indexOf(1.0)); assertEquals(1, s1.indexOf(3.0)); assertEquals(2, s1.indexOf(2.0)); } /** * Simple test for the remove() method. */ @Test public void testRemove() { XYIntervalSeries s1 = new XYIntervalSeries("Series 1"); s1.add(1.0, 1.0, 1.0, 2.0, 1.9, 2.1); s1.add(2.0, 2.0, 2.0, 2.0, 1.9, 2.1); s1.add(3.0, 3.0, 3.0, 3.0, 2.9, 3.1); assertEquals(3, s1.getItemCount()); s1.remove(2.0); assertEquals(3.0, s1.getX(1)); s1.remove(1.0); assertEquals(3.0, s1.getX(0)); } private static final double EPSILON = 0.0000000001; /** * When items are added with duplicate x-values, we expect them to remain * in the order they were added. */ @Test public void testAdditionOfDuplicateXValues() { XYIntervalSeries s1 = new XYIntervalSeries("Series 1"); s1.add(1.0, 1.0, 1.0, 1.0, 1.0, 1.0); s1.add(2.0, 2.0, 2.0, 2.0, 2.0, 2.0); s1.add(2.0, 3.0, 3.0, 3.0, 3.0, 3.0); s1.add(2.0, 4.0, 4.0, 4.0, 4.0, 4.0); s1.add(3.0, 5.0, 5.0, 5.0, 5.0, 5.0); assertEquals(1.0, s1.getYValue(0), EPSILON); assertEquals(2.0, s1.getYValue(1), EPSILON); assertEquals(3.0, s1.getYValue(2), EPSILON); assertEquals(4.0, s1.getYValue(3), EPSILON); assertEquals(5.0, s1.getYValue(4), EPSILON); } /** * Some checks for the add() method for an UNSORTED series. */ @Test public void testAdd() { XYIntervalSeries series = new XYIntervalSeries("Series", false, true); series.add(5.0, 5.50, 5.50, 5.50, 5.50, 5.50); series.add(5.1, 5.51, 5.51, 5.51, 5.51, 5.51); series.add(6.0, 6.6, 6.6, 6.6, 6.6, 6.6); series.add(3.0, 3.3, 3.3, 3.3, 3.3, 3.3); series.add(4.0, 4.4, 4.4, 4.4, 4.4, 4.4); series.add(2.0, 2.2, 2.2, 2.2, 2.2, 2.2); series.add(1.0, 1.1, 1.1, 1.1, 1.1, 1.1); assertEquals(5.5, series.getYValue(0), EPSILON); assertEquals(5.51, series.getYValue(1), EPSILON); assertEquals(6.6, series.getYValue(2), EPSILON); assertEquals(3.3, series.getYValue(3), EPSILON); assertEquals(4.4, series.getYValue(4), EPSILON); assertEquals(2.2, series.getYValue(5), EPSILON); assertEquals(1.1, series.getYValue(6), EPSILON); } /** * A simple check that the maximumItemCount attribute is working. */ @Test public void testSetMaximumItemCount() { XYIntervalSeries s1 = new XYIntervalSeries("S1"); assertEquals(Integer.MAX_VALUE, s1.getMaximumItemCount()); s1.setMaximumItemCount(2); assertEquals(2, s1.getMaximumItemCount()); s1.add(1.0, 1.1, 1.1, 1.1, 1.1, 1.1); s1.add(2.0, 2.2, 2.2, 2.2, 2.2, 2.2); s1.add(3.0, 3.3, 3.3, 3.3, 3.3, 3.3); assertEquals(2.0, s1.getX(0).doubleValue(), EPSILON); assertEquals(3.0, s1.getX(1).doubleValue(), EPSILON); } /** * Check that the maximum item count can be applied retrospectively. */ @Test public void testSetMaximumItemCount2() { XYIntervalSeries s1 = new XYIntervalSeries("S1"); s1.add(1.0, 1.1, 1.1, 1.1, 1.1, 1.1); s1.add(2.0, 2.2, 2.2, 2.2, 2.2, 2.2); s1.add(3.0, 3.3, 3.3, 3.3, 2.2, 2.2); s1.setMaximumItemCount(2); assertEquals(2.0, s1.getX(0).doubleValue(), EPSILON); assertEquals(3.0, s1.getX(1).doubleValue(), EPSILON); } /** * Some checks for the new accessor methods added in 1.0.5. */ @Test public void testValues() { XYIntervalSeries s1 = new XYIntervalSeries("S1"); s1.add(2.0, 1.0, 3.0, 5.0, 4.0, 6.0); assertEquals(2.0, s1.getX(0).doubleValue(), EPSILON); assertEquals(1.0, s1.getXLowValue(0), EPSILON); assertEquals(3.0, s1.getXHighValue(0), EPSILON); assertEquals(5.0, s1.getYValue(0), EPSILON); assertEquals(4.0, s1.getYLowValue(0), EPSILON); assertEquals(6.0, s1.getYHighValue(0), EPSILON); } /** * Some checks for the clear() method. */ @Test public void testClear() { XYIntervalSeries s1 = new XYIntervalSeries("S1"); s1.addChangeListener(this); s1.clear(); assertNull(this.lastEvent); assertTrue(s1.isEmpty()); s1.add(1.0, 2.0, 3.0, 4.0, 5.0, 6.0); assertFalse(s1.isEmpty()); s1.clear(); assertNotNull(this.lastEvent); assertTrue(s1.isEmpty()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/XYIntervalTest.java000066400000000000000000000063551463604235500271420ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------- * XYIntervalTest.java * ------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XYInterval} class. */ public class XYIntervalTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { XYInterval i1 = new XYInterval(1.0, 2.0, 3.0, 2.5, 3.5); XYInterval i2 = new XYInterval(1.0, 2.0, 3.0, 2.5, 3.5); assertEquals(i1, i2); i1 = new XYInterval(1.1, 2.0, 3.0, 2.5, 3.5); assertNotEquals(i1, i2); i2 = new XYInterval(1.1, 2.0, 3.0, 2.5, 3.5); assertEquals(i1, i2); i1 = new XYInterval(1.1, 2.2, 3.0, 2.5, 3.5); assertNotEquals(i1, i2); i2 = new XYInterval(1.1, 2.2, 3.0, 2.5, 3.5); assertEquals(i1, i2); i1 = new XYInterval(1.1, 2.2, 3.3, 2.5, 3.5); assertNotEquals(i1, i2); i2 = new XYInterval(1.1, 2.2, 3.3, 2.5, 3.5); assertEquals(i1, i2); i1 = new XYInterval(1.1, 2.2, 3.3, 2.6, 3.5); assertNotEquals(i1, i2); i2 = new XYInterval(1.1, 2.2, 3.3, 2.6, 3.5); assertEquals(i1, i2); i1 = new XYInterval(1.1, 2.2, 3.3, 2.6, 3.6); assertNotEquals(i1, i2); i2 = new XYInterval(1.1, 2.2, 3.3, 2.6, 3.6); assertEquals(i1, i2); } /** * This class is immutable. */ @Test public void testCloning() { XYInterval i1 = new XYInterval(1.0, 2.0, 3.0, 2.5, 3.5); assertFalse(i1 instanceof Cloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XYInterval i1 = new XYInterval(1.0, 2.0, 3.0, 2.5, 3.5); XYInterval i2 = TestUtils.serialised(i1); assertEquals(i1, i2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/XYSeriesCollectionTest.java000066400000000000000000000430071463604235500306170ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * --------------------------- * XYSeriesCollectionTest.java * --------------------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.jfree.data.DatasetChangeConfirmation; import org.jfree.data.Range; import org.jfree.data.UnknownKeyException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XYSeriesCollection} class. */ public class XYSeriesCollectionTest { private static final double EPSILON = 0.0000000001; /** * Some checks for the constructor. */ @Test public void testConstructor() { XYSeriesCollection xysc = new XYSeriesCollection(); assertEquals(0, xysc.getSeriesCount()); assertEquals(1.0, xysc.getIntervalWidth(), EPSILON); assertEquals(0.5, xysc.getIntervalPositionFactor(), EPSILON); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { XYSeries s1 = new XYSeries("Series"); s1.add(1.0, 1.1); XYSeriesCollection c1 = new XYSeriesCollection(); c1.addSeries(s1); XYSeries s2 = new XYSeries("Series"); s2.add(1.0, 1.1); XYSeriesCollection c2 = new XYSeriesCollection(); c2.addSeries(s2); assertEquals(c1, c2); assertEquals(c2, c1); c1.addSeries(new XYSeries("Empty Series")); assertNotEquals(c1, c2); c2.addSeries(new XYSeries("Empty Series")); assertEquals(c1, c2); c1.setIntervalWidth(5.0); assertNotEquals(c1, c2); c2.setIntervalWidth(5.0); assertEquals(c1, c2); c1.setIntervalPositionFactor(0.75); assertNotEquals(c1, c2); c2.setIntervalPositionFactor(0.75); assertEquals(c1, c2); c1.setAutoWidth(true); assertNotEquals(c1, c2); c2.setAutoWidth(true); assertEquals(c1, c2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { XYSeries s1 = new XYSeries("Series"); s1.add(1.0, 1.1); XYSeriesCollection c1 = new XYSeriesCollection(); c1.addSeries(s1); XYSeriesCollection c2 = (XYSeriesCollection) c1.clone(); assertNotSame(c1, c2); assertSame(c1.getClass(), c2.getClass()); assertEquals(c1, c2); // check independence s1.setDescription("XYZ"); assertNotEquals(c1, c2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { Object c1 = new XYSeriesCollection(); assertTrue(c1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XYSeries s1 = new XYSeries("Series"); s1.add(1.0, 1.1); XYSeriesCollection c1 = new XYSeriesCollection(); c1.addSeries(s1); XYSeriesCollection c2 = TestUtils.serialised(c1); assertEquals(c1, c2); // check independence s1.add(2.0, 2.2); assertNotEquals(c1, c2); XYSeries s2 = c2.getSeries(0); s2.add(2.0, 2.2); assertEquals(c1, c2); // check that c2 gets notifications when s2 is changed DatasetChangeConfirmation listener = new DatasetChangeConfirmation(); c2.addChangeListener(listener); s2.add(3.0, 3.3); assertNotNull(listener.event); } /** * A test for bug report 1170825. */ @Test public void test1170825() { XYSeries s1 = new XYSeries("Series1"); XYSeriesCollection dataset = new XYSeriesCollection(); dataset.addSeries(s1); try { /* XYSeries s = */ dataset.getSeries(1); } catch (IllegalArgumentException e) { // correct outcome } catch (IndexOutOfBoundsException e) { fail(); // wrong outcome } } /** * Some basic checks for the getSeries() method. */ @Test public void testGetSeries() { XYSeriesCollection c = new XYSeriesCollection(); XYSeries s1 = new XYSeries("s1"); c.addSeries(s1); assertEquals("s1", c.getSeries(0).getKey()); try { c.getSeries(-1); fail("Should have thrown IndexOutOfBoundsException on negative key"); } catch (IllegalArgumentException e) { assertEquals("Series index out of bounds", e.getMessage()); } try { c.getSeries(1); fail("Should have thrown IndexOutOfBoundsException on key out of range"); } catch (IllegalArgumentException e) { assertEquals("Series index out of bounds", e.getMessage()); } } /** * Some checks for the getSeries(Comparable) method. */ @Test public void testGetSeriesByKey() { XYSeriesCollection c = new XYSeriesCollection(); XYSeries s1 = new XYSeries("s1"); c.addSeries(s1); assertEquals("s1", c.getSeries("s1").getKey()); try { c.getSeries("s2"); fail("Should have thrown UnknownKeyException on unknown key"); } catch (UnknownKeyException e) { assertEquals("Key not found: s2", e.getMessage()); } try { c.getSeries(null); fail("Should have thrown IndexOutOfBoundsException on null key"); } catch (IllegalArgumentException e) { assertEquals("Null 'key' argument.", e.getMessage()); } } /** * Some basic checks for the addSeries() method. */ @Test public void testAddSeries() { XYSeriesCollection c = new XYSeriesCollection(); XYSeries s1 = new XYSeries("s1"); c.addSeries(s1); // the dataset should prevent the addition of a series with the // same name as an existing series in the dataset XYSeries s2 = new XYSeries("s1"); try { c.addSeries(s2); fail("Should have thrown IllegalArgumentException on duplicate key"); } catch (IllegalArgumentException e) { assertEquals("This dataset already contains a series with the key s1", e.getMessage()); } assertEquals(1, c.getSeriesCount()); } /** * Some basic checks for the removeSeries() method. */ @Test public void testRemoveSeries() { XYSeriesCollection c = new XYSeriesCollection(); XYSeries s1 = new XYSeries("s1"); c.addSeries(s1); c.removeSeries(0); assertEquals(0, c.getSeriesCount()); c.addSeries(s1); try { c.removeSeries(-1); fail("Should have thrown IndexOutOfBoundsException on negative key"); } catch (IllegalArgumentException e) { assertEquals("Series index out of bounds.", e.getMessage()); } try { c.removeSeries(1); fail("Should have thrown IndexOutOfBoundsException on key out of range"); } catch (IllegalArgumentException e) { assertEquals("Series index out of bounds.", e.getMessage()); } } /** * Some tests for the indexOf() method. */ @Test public void testIndexOf() { XYSeries s1 = new XYSeries("S1"); XYSeries s2 = new XYSeries("S2"); XYSeriesCollection dataset = new XYSeriesCollection(); assertEquals(-1, dataset.indexOf(s1)); assertEquals(-1, dataset.indexOf(s2)); dataset.addSeries(s1); assertEquals(0, dataset.indexOf(s1)); assertEquals(-1, dataset.indexOf(s2)); dataset.addSeries(s2); assertEquals(0, dataset.indexOf(s1)); assertEquals(1, dataset.indexOf(s2)); dataset.removeSeries(s1); assertEquals(-1, dataset.indexOf(s1)); assertEquals(0, dataset.indexOf(s2)); XYSeries s2b = new XYSeries("S2"); assertEquals(0, dataset.indexOf(s2b)); } /** * Some checks for the getDomainBounds() method. */ @Test public void testGetDomainBounds() { XYSeriesCollection dataset = new XYSeriesCollection(); Range r = dataset.getDomainBounds(false); assertNull(r); r = dataset.getDomainBounds(true); assertNull(r); XYSeries series = new XYSeries("S1"); dataset.addSeries(series); r = dataset.getDomainBounds(false); assertNull(r); r = dataset.getDomainBounds(true); assertNull(r); series.add(1.0, 1.1); r = dataset.getDomainBounds(false); assertEquals(new Range(1.0, 1.0), r); r = dataset.getDomainBounds(true); assertEquals(new Range(0.5, 1.5), r); series.add(-1.0, -1.1); r = dataset.getDomainBounds(false); assertEquals(new Range(-1.0, 1.0), r); r = dataset.getDomainBounds(true); assertEquals(new Range(-1.5, 1.5), r); } /** * Some checks for the getRangeBounds() method. */ @Test public void testGetRangeBounds() { XYSeriesCollection dataset = new XYSeriesCollection(); // when the dataset contains no series, we expect the value range to // be null assertNull(dataset.getRangeBounds(false)); assertNull(dataset.getRangeBounds(true)); // when the dataset contains one or more series, but those series // contain no items, we expect the value range to be null XYSeries series = new XYSeries("S1"); dataset.addSeries(series); assertNull(dataset.getRangeBounds(false)); assertNull(dataset.getRangeBounds(true)); // tests with values series.add(1.0, 1.1); assertEquals(new Range(1.1, 1.1), dataset.getRangeBounds(false)); assertEquals(new Range(1.1, 1.1), dataset.getRangeBounds(true)); series.add(-1.0, -1.1); assertEquals(new Range(-1.1, 1.1), dataset.getRangeBounds(false)); assertEquals(new Range(-1.1, 1.1), dataset.getRangeBounds(true)); series.add(0.0, null); assertEquals(new Range(-1.1, 1.1), dataset.getRangeBounds(false)); assertEquals(new Range(-1.1, 1.1), dataset.getRangeBounds(true)); XYSeries s2 = new XYSeries("S2"); dataset.addSeries(s2); assertEquals(new Range(-1.1, 1.1), dataset.getRangeBounds(false)); assertEquals(new Range(-1.1, 1.1), dataset.getRangeBounds(true)); s2.add(2.0, 5.0); assertEquals(new Range(-1.1, 5.0), dataset.getRangeBounds(false)); assertEquals(new Range(-1.1, 5.0), dataset.getRangeBounds(true)); } @Test public void testGetRangeLowerBound() { XYSeriesCollection dataset = new XYSeriesCollection(); // when the dataset contains no series, we expect the value range to // be null assertTrue(Double.isNaN(dataset.getRangeLowerBound(false))); assertTrue(Double.isNaN(dataset.getRangeLowerBound(true))); // when the dataset contains one or more series, but those series // contain no items, we expect the value range to be null XYSeries series = new XYSeries("S1"); dataset.addSeries(series); assertTrue(Double.isNaN(dataset.getRangeLowerBound(false))); assertTrue(Double.isNaN(dataset.getRangeLowerBound(true))); // tests with values series.add(1.0, 1.1); assertEquals(1.1, dataset.getRangeLowerBound(false), EPSILON); assertEquals(1.1, dataset.getRangeLowerBound(true), EPSILON); series.add(-1.0, -1.1); assertEquals(-1.1, dataset.getRangeLowerBound(false), EPSILON); assertEquals(-1.1, dataset.getRangeLowerBound(true), EPSILON); series.add(0.0, null); assertEquals(-1.1, dataset.getRangeLowerBound(false), EPSILON); assertEquals(-1.1, dataset.getRangeLowerBound(true), EPSILON); XYSeries s2 = new XYSeries("S2"); dataset.addSeries(s2); assertEquals(-1.1, dataset.getRangeLowerBound(false), EPSILON); assertEquals(-1.1, dataset.getRangeLowerBound(true), EPSILON); s2.add(2.0, 5.0); assertEquals(-1.1, dataset.getRangeLowerBound(false), EPSILON); assertEquals(-1.1, dataset.getRangeLowerBound(true), EPSILON); } @Test public void testGetRangeUpperBound() { XYSeriesCollection dataset = new XYSeriesCollection(); // when the dataset contains no series, we expect the value range to // be null assertTrue(Double.isNaN(dataset.getRangeUpperBound(false))); assertTrue(Double.isNaN(dataset.getRangeUpperBound(true))); // when the dataset contains one or more series, but those series // contain no items, we expect the value range to be null XYSeries series = new XYSeries("S1"); dataset.addSeries(series); assertTrue(Double.isNaN(dataset.getRangeUpperBound(false))); assertTrue(Double.isNaN(dataset.getRangeUpperBound(true))); // tests with values series.add(1.0, 1.1); assertEquals(1.1, dataset.getRangeUpperBound(false), EPSILON); assertEquals(1.1, dataset.getRangeUpperBound(true), EPSILON); series.add(-1.0, -1.1); assertEquals(1.1, dataset.getRangeUpperBound(false), EPSILON); assertEquals(1.1, dataset.getRangeUpperBound(true), EPSILON); series.add(0.0, null); assertEquals(1.1, dataset.getRangeUpperBound(false), EPSILON); assertEquals(1.1, dataset.getRangeUpperBound(true), EPSILON); XYSeries s2 = new XYSeries("S2"); dataset.addSeries(s2); assertEquals(1.1, dataset.getRangeUpperBound(false), EPSILON); assertEquals(1.1, dataset.getRangeUpperBound(true), EPSILON); s2.add(2.0, 5.0); assertEquals(5.0, dataset.getRangeUpperBound(false), EPSILON); assertEquals(5.0, dataset.getRangeUpperBound(true), EPSILON); } /** * A check that the dataset prevents renaming a series to the name of an * existing series in the dataset. */ @Test public void testRenameSeries() { XYSeries s1 = new XYSeries("S1"); XYSeries s2 = new XYSeries("S2"); XYSeriesCollection dataset = new XYSeriesCollection(); dataset.addSeries(s1); dataset.addSeries(s2); try { s2.setKey("S1"); fail("Should have thrown IllegalArgumentException on negative key"); } catch (IllegalArgumentException e) { assertEquals("Duplicate key2", e.getMessage()); } } /** * A test to cover bug 3445507. The issue does not affect * XYSeriesCollection. */ @Test public void testBug3445507() { XYSeries s1 = new XYSeries("S1"); s1.add(1.0, null); s1.add(2.0, null); XYSeries s2 = new XYSeries("S2"); s1.add(1.0, 5.0); s1.add(2.0, 6.0); XYSeriesCollection dataset = new XYSeriesCollection(); dataset.addSeries(s1); dataset.addSeries(s2); Range r = dataset.getRangeBounds(false); assertEquals(5.0, r.getLowerBound(), EPSILON); assertEquals(6.0, r.getUpperBound(), EPSILON); } /** * Test that a series belonging to a collection can be renamed (in fact, * because of a bug this was not possible in JFreeChart 1.0.14). */ @Test public void testSeriesRename() { // first check that a valid renaming works XYSeries series1 = new XYSeries("A"); XYSeries series2 = new XYSeries("B"); XYSeriesCollection collection = new XYSeriesCollection(); collection.addSeries(series1); collection.addSeries(series2); series1.setKey("C"); assertEquals("C", collection.getSeries(0).getKey()); // next, check that setting a duplicate key fails try { series2.setKey("C"); fail("Expected an IllegalArgumentException."); } catch (IllegalArgumentException e) { // expected } assertEquals("B", series2.getKey()); // the series name should not // change because "C" is already the key for the other series in the // collection } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/XYSeriesTest.java000066400000000000000000000572551463604235500266150ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------- * XYSeriesTest.java * ----------------- * (C) Copyright 2003-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.chart.TestUtils; import org.jfree.data.general.SeriesException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link XYSeries} class. */ public class XYSeriesTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { XYSeries s1 = new XYSeries("Series"); s1.add(1.0, 1.1); XYSeries s2 = new XYSeries("Series"); s2.add(1.0, 1.1); assertEquals(s1, s2); assertEquals(s2, s1); s1.setKey("Series X"); assertNotEquals(s1, s2); s2.setKey("Series X"); assertEquals(s1, s2); s1.add(2.0, 2.2); assertNotEquals(s1, s2); s2.add(2.0, 2.2); assertEquals(s1, s2); } /** * Some simple checks for the hashCode() method. */ @Test public void testHashCode() { XYSeries s1 = new XYSeries("Test"); XYSeries s2 = new XYSeries("Test"); assertEquals(s1, s2); assertEquals(s1.hashCode(), s2.hashCode()); s1.add(1.0, 500.0); s2.add(1.0, 500.0); assertEquals(s1, s2); assertEquals(s1.hashCode(), s2.hashCode()); s1.add(2.0, null); s2.add(2.0, null); assertEquals(s1, s2); assertEquals(s1.hashCode(), s2.hashCode()); s1.add(5.0, 111.0); s2.add(5.0, 111.0); assertEquals(s1, s2); assertEquals(s1.hashCode(), s2.hashCode()); s1.add(9.0, 1.0); s2.add(9.0, 1.0); assertEquals(s1, s2); assertEquals(s1.hashCode(), s2.hashCode()); } /** * Confirm that cloning works. * * @throws java.lang.CloneNotSupportedException if there is a problem cloning. */ @Test public void testCloning() throws CloneNotSupportedException { XYSeries s1 = new XYSeries("Series"); s1.add(1.0, 1.1); XYSeries s2 = (XYSeries) s1.clone(); assertNotSame(s1, s2); assertSame(s1.getClass(), s2.getClass()); assertEquals(s1, s2); } /** * Another test of the clone() method. * * @throws java.lang.CloneNotSupportedException if there is a problem cloning. */ @Test public void testCloning2() throws CloneNotSupportedException { XYSeries s1 = new XYSeries("S1"); s1.add(1.0, 100.0); s1.add(2.0, null); s1.add(3.0, 200.0); XYSeries s2 = (XYSeries) s1.clone(); assertEquals(s1, s2); // check independence s2.add(4.0, 300.0); assertNotEquals(s1, s2); s1.add(4.0, 300.0); assertEquals(s1, s2); } /** * Another test of the clone() method. * * @throws java.lang.CloneNotSupportedException if there is a problem cloning. */ @Test public void testCloning3() throws CloneNotSupportedException { XYSeries s1 = new XYSeries("S1"); XYSeries s2 = (XYSeries) s1.clone(); assertEquals(s1, s2); // check independence s2.add(4.0, 300.0); assertNotEquals(s1, s2); s1.add(4.0, 300.0); assertEquals(s1, s2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { XYSeries s1 = new XYSeries("Series"); s1.add(1.0, 1.1); XYSeries s2 = TestUtils.serialised(s1); assertEquals(s1, s2); // check independence (and also serialization mechanism for change listeners) s2.setKey("New Series Key"); assertNotEquals(s1, s2); s1.setKey("New Series Key"); assertEquals(s1, s2); } /** * Simple test for the indexOf() method. */ @Test public void testIndexOf() { XYSeries s1 = new XYSeries("Series 1"); s1.add(1.0, 1.0); s1.add(2.0, 2.0); s1.add(3.0, 3.0); assertEquals(0, s1.indexOf(1.0)); assertEquals(1, s1.indexOf(2.0)); assertEquals(2, s1.indexOf(3.0)); assertEquals(-4, s1.indexOf(99.9)); } /** * A check for the indexOf() method for an unsorted series. */ @Test public void testIndexOf2() { XYSeries s1 = new XYSeries("Series 1", false, true); s1.add(1.0, 1.0); s1.add(3.0, 3.0); s1.add(2.0, 2.0); assertEquals(0, s1.indexOf(1.0)); assertEquals(1, s1.indexOf(3.0)); assertEquals(2, s1.indexOf(2.0)); } /** * A check for the indexOf(Number) method when the series has duplicate * x-values. */ @Test public void testIndexOf3() { XYSeries s1 = new XYSeries("Series 1"); s1.add(1.0, 1.0); s1.add(2.0, 2.0); s1.add(2.0, 3.0); assertEquals(0, s1.indexOf(1.0)); assertEquals(1, s1.indexOf(2.0)); } /** * Simple test for the remove() method. */ @Test public void testRemove() { XYSeries s1 = new XYSeries("Series 1"); s1.add(1.0, 1.0); s1.add(2.0, 2.0); s1.add(3.0, 3.0); assertEquals(3, s1.getItemCount()); s1.remove(2.0); assertEquals(3.0, s1.getX(1)); s1.remove(0); assertEquals(3.0, s1.getX(0)); } /** * Some checks for the remove(int) method. */ @Test public void testRemove2() { XYSeries s1 = new XYSeries("S1"); s1.add(1.0, 1.1); s1.add(2.0, 2.2); s1.add(3.0, 3.3); s1.add(4.0, 4.4); s1.add(5.0, 5.5); s1.add(6.0, 6.6); assertEquals(6, s1.getItemCount()); assertEquals(1.0, s1.getMinX(), EPSILON); assertEquals(6.0, s1.getMaxX(), EPSILON); assertEquals(1.1, s1.getMinY(), EPSILON); assertEquals(6.6, s1.getMaxY(), EPSILON); s1.remove(5); assertEquals(5, s1.getItemCount()); assertEquals(1.0, s1.getMinX(), EPSILON); assertEquals(5.0, s1.getMaxX(), EPSILON); assertEquals(1.1, s1.getMinY(), EPSILON); assertEquals(5.5, s1.getMaxY(), EPSILON); } private static final double EPSILON = 0.0000000001; /** * When items are added with duplicate x-values, we expect them to remain * in the order they were added. */ @Test public void testAdditionOfDuplicateXValues() { XYSeries s1 = new XYSeries("Series 1"); s1.add(1.0, 1.0); s1.add(2.0, 2.0); s1.add(2.0, 3.0); s1.add(2.0, 4.0); s1.add(3.0, 5.0); assertEquals(1.0, s1.getY(0).doubleValue(), EPSILON); assertEquals(2.0, s1.getY(1).doubleValue(), EPSILON); assertEquals(3.0, s1.getY(2).doubleValue(), EPSILON); assertEquals(4.0, s1.getY(3).doubleValue(), EPSILON); assertEquals(5.0, s1.getY(4).doubleValue(), EPSILON); } /** * Some checks for the update(Number, Number) method. */ @Test public void testUpdate() { XYSeries series = new XYSeries("S1"); series.add(Integer.valueOf(1), Integer.valueOf(2)); assertEquals(2, series.getY(0)); series.update(1, 3); assertEquals(3, series.getY(0)); try { series.update(2, 99); fail(); } catch (SeriesException e) { // got the required exception } } /** * Some checks for the update() method for an unsorted series. */ @Test public void testUpdate2() { XYSeries series = new XYSeries("Series", false, true); series.add(5.0, 55.0); series.add(4.0, 44.0); series.add(6.0, 66.0); series.update(4.0, 99.0); assertEquals(99.0, series.getY(1)); } /** * Some checks for the addOrUpdate() method. */ @Test public void testAddOrUpdate() { XYSeries series = new XYSeries("S1", true, false); XYDataItem old = series.addOrUpdate(Long.valueOf(1), Long.valueOf(2)); assertNull(old); assertEquals(1, series.getItemCount()); assertEquals(2L, series.getY(0)); old = series.addOrUpdate(Long.valueOf(2), Long.valueOf(3)); assertNull(old); assertEquals(2, series.getItemCount()); assertEquals(3L, series.getY(1)); old = series.addOrUpdate(Long.valueOf(1), Long.valueOf(99)); assertEquals(new XYDataItem(Long.valueOf(1), Long.valueOf(2)), old); assertEquals(2, series.getItemCount()); assertEquals(99L, series.getY(0)); assertEquals(3L, series.getY(1)); } /** * Some checks for the addOrUpdate() method for an UNSORTED series. */ @Test public void testAddOrUpdate2() { XYSeries series = new XYSeries("Series", false, false); series.add(5.0, 5.5); series.add(6.0, 6.6); series.add(3.0, 3.3); series.add(4.0, 4.4); series.add(2.0, 2.2); series.add(1.0, 1.1); series.addOrUpdate(3.0, 33.3); series.addOrUpdate(2.0, 22.2); assertEquals(33.3, series.getY(2).doubleValue(), EPSILON); assertEquals(22.2, series.getY(4).doubleValue(), EPSILON); } /** * Another test for the addOrUpdate() method. */ @Test public void testAddOrUpdate3() { XYSeries series = new XYSeries("Series", false, true); series.addOrUpdate(1.0, 1.0); series.addOrUpdate(1.0, 2.0); series.addOrUpdate(1.0, 3.0); assertEquals(1.0, series.getY(0)); assertEquals(2.0, series.getY(1)); assertEquals(3.0, series.getY(2)); assertEquals(3, series.getItemCount()); } /** * Some checks for the add() method for an UNSORTED series. */ @Test public void testAdd() { XYSeries series = new XYSeries("Series", false, true); series.add(5.0, 5.50); series.add(5.1, 5.51); series.add(6.0, 6.6); series.add(3.0, 3.3); series.add(4.0, 4.4); series.add(2.0, 2.2); series.add(1.0, 1.1); assertEquals(5.5, series.getY(0).doubleValue(), EPSILON); assertEquals(5.51, series.getY(1).doubleValue(), EPSILON); assertEquals(6.6, series.getY(2).doubleValue(), EPSILON); assertEquals(3.3, series.getY(3).doubleValue(), EPSILON); assertEquals(4.4, series.getY(4).doubleValue(), EPSILON); assertEquals(2.2, series.getY(5).doubleValue(), EPSILON); assertEquals(1.1, series.getY(6).doubleValue(), EPSILON); } /** * A simple check that the maximumItemCount attribute is working. */ @Test public void testSetMaximumItemCount() { XYSeries s1 = new XYSeries("S1"); assertEquals(Integer.MAX_VALUE, s1.getMaximumItemCount()); s1.setMaximumItemCount(2); assertEquals(2, s1.getMaximumItemCount()); s1.add(1.0, 1.1); s1.add(2.0, 2.2); s1.add(3.0, 3.3); assertEquals(2.0, s1.getX(0).doubleValue(), EPSILON); assertEquals(3.0, s1.getX(1).doubleValue(), EPSILON); } /** * Check that the maximum item count can be applied retrospectively. */ @Test public void testSetMaximumItemCount2() { XYSeries s1 = new XYSeries("S1"); s1.add(1.0, 1.1); s1.add(2.0, 2.2); s1.add(3.0, 3.3); s1.setMaximumItemCount(2); assertEquals(2.0, s1.getX(0).doubleValue(), EPSILON); assertEquals(3.0, s1.getX(1).doubleValue(), EPSILON); } /** * Check that the item bounds are determined correctly when there is a * maximum item count. */ @Test public void testSetMaximumItemCount3() { XYSeries s1 = new XYSeries("S1"); s1.add(1.0, 1.1); s1.add(2.0, 2.2); s1.add(3.0, 3.3); s1.add(4.0, 4.4); s1.add(5.0, 5.5); s1.add(6.0, 6.6); s1.setMaximumItemCount(2); assertEquals(5.0, s1.getX(0).doubleValue(), EPSILON); assertEquals(6.0, s1.getX(1).doubleValue(), EPSILON); assertEquals(5.0, s1.getMinX(), EPSILON); assertEquals(6.0, s1.getMaxX(), EPSILON); assertEquals(5.5, s1.getMinY(), EPSILON); assertEquals(6.6, s1.getMaxY(), EPSILON); } /** * Check that the item bounds are determined correctly when there is a * maximum item count. */ @Test public void testSetMaximumItemCount4() { XYSeries s1 = new XYSeries("S1"); s1.setMaximumItemCount(2); s1.add(1.0, 1.1); s1.add(2.0, 2.2); s1.add(3.0, 3.3); assertEquals(2.0, s1.getX(0).doubleValue(), EPSILON); assertEquals(3.0, s1.getX(1).doubleValue(), EPSILON); assertEquals(2.0, s1.getMinX(), EPSILON); assertEquals(3.0, s1.getMaxX(), EPSILON); assertEquals(2.2, s1.getMinY(), EPSILON); assertEquals(3.3, s1.getMaxY(), EPSILON); } /** * Some checks for the toArray() method. */ @Test public void testToArray() { XYSeries s = new XYSeries("S1"); double[][] array = s.toArray(); assertEquals(2, array.length); assertEquals(0, array[0].length); assertEquals(0, array[1].length); s.add(1.0, 2.0); array = s.toArray(); assertEquals(1, array[0].length); assertEquals(1, array[1].length); assertEquals(2, array.length); assertEquals(1.0, array[0][0], EPSILON); assertEquals(2.0, array[1][0], EPSILON); s.add(2.0, null); array = s.toArray(); assertEquals(2, array.length); assertEquals(2, array[0].length); assertEquals(2, array[1].length); assertEquals(2.0, array[0][1], EPSILON); assertTrue(Double.isNaN(array[1][1])); } /** * Some checks for an example using the toArray() method. */ @Test public void testToArrayExample() { XYSeries s = new XYSeries("S"); s.add(1.0, 11.0); s.add(2.0, 22.0); s.add(3.5, 35.0); s.add(5.0, null); DefaultXYDataset dataset = new DefaultXYDataset(); dataset.addSeries("S", s.toArray()); assertEquals(1, dataset.getSeriesCount()); assertEquals(4, dataset.getItemCount(0)); assertEquals("S", dataset.getSeriesKey(0)); assertEquals(1.0, dataset.getXValue(0, 0), EPSILON); assertEquals(2.0, dataset.getXValue(0, 1), EPSILON); assertEquals(3.5, dataset.getXValue(0, 2), EPSILON); assertEquals(5.0, dataset.getXValue(0, 3), EPSILON); assertEquals(11.0, dataset.getYValue(0, 0), EPSILON); assertEquals(22.0, dataset.getYValue(0, 1), EPSILON); assertEquals(35.0, dataset.getYValue(0, 2), EPSILON); assertTrue(Double.isNaN(dataset.getYValue(0, 3))); } /** * Another test for the addOrUpdate() method. */ @Test public void testBug1955483() { XYSeries series = new XYSeries("Series", true, true); series.addOrUpdate(1.0, 1.0); series.addOrUpdate(1.0, 2.0); assertEquals(1.0, series.getY(0)); assertEquals(2.0, series.getY(1)); assertEquals(2, series.getItemCount()); } /** * Some checks for the delete(int, int) method. */ @Test public void testDelete() { XYSeries s1 = new XYSeries("S1"); s1.add(1.0, 1.1); s1.add(2.0, 2.2); s1.add(3.0, 3.3); s1.add(4.0, 4.4); s1.add(5.0, 5.5); s1.add(6.0, 6.6); s1.delete(2, 5); assertEquals(2, s1.getItemCount()); assertEquals(1.0, s1.getX(0).doubleValue(), EPSILON); assertEquals(2.0, s1.getX(1).doubleValue(), EPSILON); assertEquals(1.0, s1.getMinX(), EPSILON); assertEquals(2.0, s1.getMaxX(), EPSILON); assertEquals(1.1, s1.getMinY(), EPSILON); assertEquals(2.2, s1.getMaxY(), EPSILON); } /** * Some checks for the getMinX() method. */ @Test public void testGetMinX() { XYSeries s1 = new XYSeries("S1"); assertTrue(Double.isNaN(s1.getMinX())); s1.add(1.0, 1.1); assertEquals(1.0, s1.getMinX(), EPSILON); s1.add(2.0, 2.2); assertEquals(1.0, s1.getMinX(), EPSILON); s1.add(Double.NaN, 99.9); assertEquals(1.0, s1.getMinX(), EPSILON); s1.add(-1.0, -1.1); assertEquals(-1.0, s1.getMinX(), EPSILON); s1.add(0.0, null); assertEquals(-1.0, s1.getMinX(), EPSILON); } /** * Some checks for the getMaxX() method. */ @Test public void testGetMaxX() { XYSeries s1 = new XYSeries("S1"); assertTrue(Double.isNaN(s1.getMaxX())); s1.add(1.0, 1.1); assertEquals(1.0, s1.getMaxX(), EPSILON); s1.add(2.0, 2.2); assertEquals(2.0, s1.getMaxX(), EPSILON); s1.add(Double.NaN, 99.9); assertEquals(2.0, s1.getMaxX(), EPSILON); s1.add(-1.0, -1.1); assertEquals(2.0, s1.getMaxX(), EPSILON); s1.add(0.0, null); assertEquals(2.0, s1.getMaxX(), EPSILON); } /** * Some checks for the getMinY() method. */ @Test public void testGetMinY() { XYSeries s1 = new XYSeries("S1"); assertTrue(Double.isNaN(s1.getMinY())); s1.add(1.0, 1.1); assertEquals(1.1, s1.getMinY(), EPSILON); s1.add(2.0, 2.2); assertEquals(1.1, s1.getMinY(), EPSILON); s1.add(Double.NaN, 99.9); assertEquals(1.1, s1.getMinY(), EPSILON); s1.add(-1.0, -1.1); assertEquals(-1.1, s1.getMinY(), EPSILON); s1.add(0.0, null); assertEquals(-1.1, s1.getMinY(), EPSILON); } /** * Some checks for the getMaxY() method. */ @Test public void testGetMaxY() { XYSeries s1 = new XYSeries("S1"); assertTrue(Double.isNaN(s1.getMaxY())); s1.add(1.0, 1.1); assertEquals(1.1, s1.getMaxY(), EPSILON); s1.add(2.0, 2.2); assertEquals(2.2, s1.getMaxY(), EPSILON); s1.add(Double.NaN, 99.9); assertEquals(99.9, s1.getMaxY(), EPSILON); s1.add(-1.0, -1.1); assertEquals(99.9, s1.getMaxY(), EPSILON); s1.add(0.0, null); assertEquals(99.9, s1.getMaxY(), EPSILON); } /** * A test for a bug reported in the forum: * * http://www.jfree.org/forum/viewtopic.php?f=3&t=116601 */ @Test public void testGetMaxY2() { XYSeries series = new XYSeries(1, true, false); series.addOrUpdate(1, 20); series.addOrUpdate(2, 30); series.addOrUpdate(3, 40); assertEquals(40.0, series.getMaxY(), EPSILON); series.addOrUpdate(2, 22); assertEquals(40.0, series.getMaxY(), EPSILON); } /** * A test for the clear method. */ @Test public void testClear() { XYSeries s1 = new XYSeries("S1"); s1.add(1.0, 1.1); s1.add(2.0, 2.2); s1.add(3.0, 3.3); assertEquals(3, s1.getItemCount()); s1.clear(); assertEquals(0, s1.getItemCount()); assertTrue(Double.isNaN(s1.getMinX())); assertTrue(Double.isNaN(s1.getMaxX())); assertTrue(Double.isNaN(s1.getMinY())); assertTrue(Double.isNaN(s1.getMaxY())); } /** * Some checks for the updateByIndex() method. */ @Test public void testUpdateByIndex() { XYSeries s1 = new XYSeries("S1"); s1.add(1.0, 1.1); s1.add(2.0, 2.2); s1.add(3.0, 3.3); assertEquals(1.1, s1.getMinY(), EPSILON); assertEquals(3.3, s1.getMaxY(), EPSILON); s1.updateByIndex(0, -5.0); assertEquals(-5.0, s1.getMinY(), EPSILON); assertEquals(3.3, s1.getMaxY(), EPSILON); s1.updateByIndex(0, null); assertEquals(2.2, s1.getMinY(), EPSILON); assertEquals(3.3, s1.getMaxY(), EPSILON); s1.updateByIndex(2, null); assertEquals(2.2, s1.getMinY(), EPSILON); assertEquals(2.2, s1.getMaxY(), EPSILON); s1.updateByIndex(1, null); assertTrue(Double.isNaN(s1.getMinY())); assertTrue(Double.isNaN(s1.getMaxY())); } /** * Some checks for the updateByIndex() method. */ @Test public void testUpdateByIndex2() { XYSeries s1 = new XYSeries("S1"); s1.add(1.0, Double.NaN); assertTrue(Double.isNaN(s1.getMinY())); assertTrue(Double.isNaN(s1.getMaxY())); s1.updateByIndex(0, 1.0); assertEquals(1.0, s1.getMinY(), EPSILON); assertEquals(1.0, s1.getMaxY(), EPSILON); s1.updateByIndex(0, 2.0); assertEquals(2.0, s1.getMinY(), EPSILON); assertEquals(2.0, s1.getMaxY(), EPSILON); s1.add(-1.0, -1.0); s1.updateByIndex(0, 0.0); assertEquals(0.0, s1.getMinY(), EPSILON); assertEquals(2.0, s1.getMaxY(), EPSILON); } /** * Some checks for the updateByIndex() method. */ @Test public void testUpdateByIndex3() { XYSeries s1 = new XYSeries("S1"); s1.add(1.0, 1.1); s1.add(2.0, 2.2); s1.add(3.0, 3.3); s1.updateByIndex(1, 2.05); assertEquals(1.1, s1.getMinY(), EPSILON); assertEquals(3.3, s1.getMaxY(), EPSILON); } /** * Some checks for the update(Number, Number) method. */ @Test public void testUpdateXY() { XYSeries s1 = new XYSeries("S1"); s1.add(1.0, Double.NaN); assertTrue(Double.isNaN(s1.getMinY())); assertTrue(Double.isNaN(s1.getMaxY())); s1.update(1.0, 1.0); assertEquals(1.0, s1.getMinY(), EPSILON); assertEquals(1.0, s1.getMaxY(), EPSILON); s1.update(1.0, 2.0); assertEquals(2.0, s1.getMinY(), EPSILON); assertEquals(2.0, s1.getMaxY(), EPSILON); } @Test public void testSetKey() { XYSeries s1 = new XYSeries("S"); s1.setKey("S1"); assertEquals("S1", s1.getKey()); XYSeriesCollection c = new XYSeriesCollection(); c.addSeries(s1); XYSeries s2 = new XYSeries("S2"); c.addSeries(s2); // now we should be allowed to change s1's key to anything but "S2" s1.setKey("OK"); assertEquals("OK", s1.getKey()); try { s1.setKey("S2"); fail("Expect an exception here."); } catch (IllegalArgumentException e) { // OK } // after s1 is removed from the collection, we should be able to set // the key to anything we want... c.removeSeries(s1); s1.setKey("S2"); // check that removing by index also works s1.setKey("S1"); c.addSeries(s1); c.removeSeries(1); s1.setKey("S2"); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/YIntervalDataItemTest.java000066400000000000000000000100651463604235500304140ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * -------------------------- * YIntervalDataItemTest.java * -------------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link YIntervalDataItem} class. */ public class YIntervalDataItemTest { private static final double EPSILON = 0.00000000001; /** * Some checks for the constructor. */ @Test public void testConstructor1() { YIntervalDataItem item1 = new YIntervalDataItem(1.0, 2.0, 3.0, 4.0); assertEquals(1.0, item1.getX()); assertEquals(2.0, item1.getYValue(), EPSILON); assertEquals(3.0, item1.getYLowValue(), EPSILON); assertEquals(4.0, item1.getYHighValue(), EPSILON); } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { YIntervalDataItem item1 = new YIntervalDataItem(1.0, 2.0, 1.5, 2.5); YIntervalDataItem item2 = new YIntervalDataItem(1.0, 2.0, 1.5, 2.5); assertEquals(item1, item2); assertEquals(item2, item1); // x item1 = new YIntervalDataItem(1.1, 2.0, 1.5, 2.5); assertNotEquals(item1, item2); item2 = new YIntervalDataItem(1.1, 2.0, 1.5, 2.5); assertEquals(item1, item2); // y item1 = new YIntervalDataItem(1.1, 2.2, 1.5, 2.5); assertNotEquals(item1, item2); item2 = new YIntervalDataItem(1.1, 2.2, 1.5, 2.5); assertEquals(item1, item2); // yLow item1 = new YIntervalDataItem(1.1, 2.2, 1.55, 2.5); assertNotEquals(item1, item2); item2 = new YIntervalDataItem(1.1, 2.2, 1.55, 2.5); assertEquals(item1, item2); // yHigh item1 = new YIntervalDataItem(1.1, 2.2, 1.55, 2.55); assertNotEquals(item1, item2); item2 = new YIntervalDataItem(1.1, 2.2, 1.55, 2.55); assertEquals(item1, item2); } /** * Some checks for the clone() method. * * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { YIntervalDataItem item1 = new YIntervalDataItem(1.0, 2.0, 1.5, 2.5); YIntervalDataItem item2 = (YIntervalDataItem) item1.clone(); assertNotSame(item1, item2); assertSame(item1.getClass(), item2.getClass()); assertEquals(item1, item2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { YIntervalDataItem item1 = new YIntervalDataItem(1.0, 2.0, 1.5, 2.5); YIntervalDataItem item2 = TestUtils.serialised(item1); assertEquals(item1, item2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/YIntervalSeriesCollectionTest.java000066400000000000000000000122351463604235500321730ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ---------------------------------- * YIntervalSeriesCollectionTest.java * ---------------------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.chart.TestUtils; import org.jfree.chart.util.PublicCloneable; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link YIntervalSeriesCollection} class. */ public class YIntervalSeriesCollectionTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { YIntervalSeriesCollection c1 = new YIntervalSeriesCollection(); YIntervalSeriesCollection c2 = new YIntervalSeriesCollection(); assertEquals(c1, c2); // add a series YIntervalSeries s1 = new YIntervalSeries("Series"); s1.add(1.0, 1.1, 1.2, 1.3); c1.addSeries(s1); assertNotEquals(c1, c2); YIntervalSeries s2 = new YIntervalSeries("Series"); s2.add(1.0, 1.1, 1.2, 1.3); c2.addSeries(s2); assertEquals(c1, c2); // add an empty series c1.addSeries(new YIntervalSeries("Empty Series")); assertNotEquals(c1, c2); c2.addSeries(new YIntervalSeries("Empty Series")); assertEquals(c1, c2); } /** * Confirm that cloning works. */ @Test public void testCloning() throws CloneNotSupportedException { YIntervalSeriesCollection c1 = new YIntervalSeriesCollection(); YIntervalSeries s1 = new YIntervalSeries("Series"); s1.add(1.0, 1.1, 1.2, 1.3); c1.addSeries(s1); YIntervalSeriesCollection c2 = (YIntervalSeriesCollection) c1.clone(); assertNotSame(c1, c2); assertSame(c1.getClass(), c2.getClass()); assertEquals(c1, c2); // check independence s1.setDescription("XYZ"); assertNotEquals(c1, c2); } /** * Verify that this class implements {@link PublicCloneable}. */ @Test public void testPublicCloneable() { YIntervalSeriesCollection c1 = new YIntervalSeriesCollection(); assertTrue(c1 instanceof PublicCloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { YIntervalSeriesCollection c1 = new YIntervalSeriesCollection(); YIntervalSeries s1 = new YIntervalSeries("Series"); s1.add(1.0, 1.1, 1.2, 1.3); YIntervalSeriesCollection c2 = TestUtils.serialised(c1); assertEquals(c1, c2); } /** * Some basic checks for the removeSeries() method. */ @Test public void testRemoveSeries() { YIntervalSeriesCollection c = new YIntervalSeriesCollection(); YIntervalSeries s1 = new YIntervalSeries("s1"); c.addSeries(s1); c.removeSeries(0); assertEquals(0, c.getSeriesCount()); c.addSeries(s1); boolean pass = false; try { c.removeSeries(-1); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); pass = false; try { c.removeSeries(1); } catch (IllegalArgumentException e) { pass = true; } assertTrue(pass); } /** * A test for bug report 1170825 (originally affected XYSeriesCollection, * this test is just copied over). */ @Test public void test1170825() { YIntervalSeries s1 = new YIntervalSeries("Series1"); YIntervalSeriesCollection dataset = new YIntervalSeriesCollection(); dataset.addSeries(s1); try { /* XYSeries s = */ dataset.getSeries(1); } catch (IllegalArgumentException e) { // correct outcome } catch (IndexOutOfBoundsException e) { fail(); // wrong outcome } } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/YIntervalSeriesTest.java000066400000000000000000000204521463604235500301570ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------------ * YIntervalSeriesTest.java * ------------------------ * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.chart.TestUtils; import org.jfree.data.general.SeriesChangeEvent; import org.jfree.data.general.SeriesChangeListener; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link YIntervalSeries} class. */ public class YIntervalSeriesTest implements SeriesChangeListener { SeriesChangeEvent lastEvent; /** * Records the last event. * * @param event the event. */ @Override public void seriesChanged(SeriesChangeEvent event) { this.lastEvent = event; } /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { YIntervalSeries s1 = new YIntervalSeries("s1"); YIntervalSeries s2 = new YIntervalSeries("s1"); assertEquals(s1, s2); // seriesKey s1 = new YIntervalSeries("s2"); assertNotEquals(s1, s2); s2 = new YIntervalSeries("s2"); assertEquals(s1, s2); // autoSort s1 = new YIntervalSeries("s2", false, true); assertNotEquals(s1, s2); s2 = new YIntervalSeries("s2", false, true); assertEquals(s1, s2); // allowDuplicateValues s1 = new YIntervalSeries("s2", false, false); assertNotEquals(s1, s2); s2 = new YIntervalSeries("s2", false, false); assertEquals(s1, s2); // add a value s1.add(1.0, 0.5, 1.5, 2.0); assertNotEquals(s1, s2); s2.add(1.0, 0.5, 1.5, 2.0); assertEquals(s2, s1); // add another value s1.add(2.0, 0.5, 1.5, 2.0); assertNotEquals(s1, s2); s2.add(2.0, 0.5, 1.5, 2.0); assertEquals(s2, s1); // remove a value s1.remove(1.0); assertNotEquals(s1, s2); s2.remove(1.0); assertEquals(s2, s1); } /** * Confirm that cloning works. * @throws java.lang.CloneNotSupportedException */ @Test public void testCloning() throws CloneNotSupportedException { YIntervalSeries s1 = new YIntervalSeries("s1"); s1.add(1.0, 0.5, 1.5, 2.0); YIntervalSeries s2 = (YIntervalSeries) s1.clone(); assertNotSame(s1, s2); assertSame(s1.getClass(), s2.getClass()); assertEquals(s1, s2); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { YIntervalSeries s1 = new YIntervalSeries("s1"); s1.add(1.0, 0.5, 1.5, 2.0); YIntervalSeries s2 = TestUtils.serialised(s1); assertEquals(s1, s2); } /** * Simple test for the indexOf() method. */ @Test public void testIndexOf() { YIntervalSeries s1 = new YIntervalSeries("Series 1"); s1.add(1.0, 1.0, 1.0, 2.0); s1.add(2.0, 2.0, 2.0, 3.0); s1.add(3.0, 3.0, 3.0, 4.0); assertEquals(0, s1.indexOf(1.0)); } /** * A check for the indexOf() method for an unsorted series. */ @Test public void testIndexOf2() { YIntervalSeries s1 = new YIntervalSeries("Series 1", false, true); s1.add(1.0, 1.0, 1.0, 2.0); s1.add(3.0, 3.0, 3.0, 3.0); s1.add(2.0, 2.0, 2.0, 2.0); assertEquals(0, s1.indexOf(1.0)); assertEquals(1, s1.indexOf(3.0)); assertEquals(2, s1.indexOf(2.0)); } /** * Simple test for the remove() method. */ @Test public void testRemove() { YIntervalSeries s1 = new YIntervalSeries("Series 1"); s1.add(1.0, 1.0, 1.0, 2.0); s1.add(2.0, 2.0, 2.0, 2.0); s1.add(3.0, 3.0, 3.0, 3.0); assertEquals(3, s1.getItemCount()); s1.remove(2.0); assertEquals(3.0, s1.getX(1)); s1.remove(1.0); assertEquals(3.0, s1.getX(0)); } private static final double EPSILON = 0.0000000001; /** * When items are added with duplicate x-values, we expect them to remain * in the order they were added. */ @Test public void testAdditionOfDuplicateXValues() { YIntervalSeries s1 = new YIntervalSeries("Series 1"); s1.add(1.0, 1.0, 1.0, 1.0); s1.add(2.0, 2.0, 2.0, 2.0); s1.add(2.0, 3.0, 3.0, 3.0); s1.add(2.0, 4.0, 4.0, 4.0); s1.add(3.0, 5.0, 5.0, 5.0); assertEquals(1.0, s1.getYValue(0), EPSILON); assertEquals(2.0, s1.getYValue(1), EPSILON); assertEquals(3.0, s1.getYValue(2), EPSILON); assertEquals(4.0, s1.getYValue(3), EPSILON); assertEquals(5.0, s1.getYValue(4), EPSILON); } /** * Some checks for the add() method for an UNSORTED series. */ @Test public void testAdd() { YIntervalSeries series = new YIntervalSeries("Series", false, true); series.add(5.0, 5.50, 5.50, 5.50); series.add(5.1, 5.51, 5.51, 5.51); series.add(6.0, 6.6, 6.6, 6.6); series.add(3.0, 3.3, 3.3, 3.3); series.add(4.0, 4.4, 4.4, 4.4); series.add(2.0, 2.2, 2.2, 2.2); series.add(1.0, 1.1, 1.1, 1.1); assertEquals(5.5, series.getYValue(0), EPSILON); assertEquals(5.51, series.getYValue(1), EPSILON); assertEquals(6.6, series.getYValue(2), EPSILON); assertEquals(3.3, series.getYValue(3), EPSILON); assertEquals(4.4, series.getYValue(4), EPSILON); assertEquals(2.2, series.getYValue(5), EPSILON); assertEquals(1.1, series.getYValue(6), EPSILON); } /** * A simple check that the maximumItemCount attribute is working. */ @Test public void testSetMaximumItemCount() { YIntervalSeries s1 = new YIntervalSeries("S1"); assertEquals(Integer.MAX_VALUE, s1.getMaximumItemCount()); s1.setMaximumItemCount(2); assertEquals(2, s1.getMaximumItemCount()); s1.add(1.0, 1.1, 1.1, 1.1); s1.add(2.0, 2.2, 2.2, 2.2); s1.add(3.0, 3.3, 3.3, 3.3); assertEquals(2.0, s1.getX(0).doubleValue(), EPSILON); assertEquals(3.0, s1.getX(1).doubleValue(), EPSILON); } /** * Check that the maximum item count can be applied retrospectively. */ @Test public void testSetMaximumItemCount2() { YIntervalSeries s1 = new YIntervalSeries("S1"); s1.add(1.0, 1.1, 1.1, 1.1); s1.add(2.0, 2.2, 2.2, 2.2); s1.add(3.0, 3.3, 3.3, 3.3); s1.setMaximumItemCount(2); assertEquals(2.0, s1.getX(0).doubleValue(), EPSILON); assertEquals(3.0, s1.getX(1).doubleValue(), EPSILON); } /** * Some checks for the clear() method. */ @Test public void testClear() { YIntervalSeries s1 = new YIntervalSeries("S1"); s1.addChangeListener(this); s1.clear(); assertNull(this.lastEvent); assertTrue(s1.isEmpty()); s1.add(1.0, 2.0, 3.0, 4.0); assertFalse(s1.isEmpty()); s1.clear(); assertNotNull(this.lastEvent); assertTrue(s1.isEmpty()); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/YIntervalTest.java000066400000000000000000000054431463604235500270070ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ------------------ * YIntervalTest.java * ------------------ * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link YInterval} class. */ public class YIntervalTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { YInterval i1 = new YInterval(1.0, 0.5, 1.5); YInterval i2 = new YInterval(1.0, 0.5, 1.5); assertEquals(i1, i2); i1 = new YInterval(1.1, 0.5, 1.5); assertNotEquals(i1, i2); i2 = new YInterval(1.1, 0.5, 1.5); assertEquals(i1, i2); i1 = new YInterval(1.1, 0.55, 1.5); assertNotEquals(i1, i2); i2 = new YInterval(1.1, 0.55, 1.5); assertEquals(i1, i2); i1 = new YInterval(1.1, 0.55, 1.55); assertNotEquals(i1, i2); i2 = new YInterval(1.1, 0.55, 1.55); assertEquals(i1, i2); } /** * This class is immutable. */ @Test public void testCloning() { YInterval i1 = new YInterval(1.0, 0.5, 1.5); assertFalse(i1 instanceof Cloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { YInterval i1 = new YInterval(1.0, 0.5, 1.5); YInterval i2 = TestUtils.serialised(i1); assertEquals(i1, i2); } } jfree-jfreechart-cb8ff67/src/test/java/org/jfree/data/xy/YWithXIntervalTest.java000066400000000000000000000056071463604235500277750ustar00rootroot00000000000000/* =========================================================== * JFreeChart : a free chart library for the Java(tm) platform * =========================================================== * * (C) Copyright 2000-present, by David Gilbert and Contributors. * * Project Info: http://www.jfree.org/jfreechart/index.html * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners.] * * ----------------------- * YWithXIntervalTest.java * ----------------------- * (C) Copyright 2006-present, by David Gilbert and Contributors. * * Original Author: David Gilbert; * Contributor(s): -; * */ package org.jfree.data.xy; import org.jfree.chart.TestUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; /** * Tests for the {@link YWithXInterval} class. */ public class YWithXIntervalTest { /** * Confirm that the equals method can distinguish all the required fields. */ @Test public void testEquals() { YWithXInterval i1 = new YWithXInterval(1.0, 0.5, 1.5); YWithXInterval i2 = new YWithXInterval(1.0, 0.5, 1.5); assertEquals(i1, i2); i1 = new YWithXInterval(1.1, 0.5, 1.5); assertNotEquals(i1, i2); i2 = new YWithXInterval(1.1, 0.5, 1.5); assertEquals(i1, i2); i1 = new YWithXInterval(1.1, 0.55, 1.5); assertNotEquals(i1, i2); i2 = new YWithXInterval(1.1, 0.55, 1.5); assertEquals(i1, i2); i1 = new YWithXInterval(1.1, 0.55, 1.55); assertNotEquals(i1, i2); i2 = new YWithXInterval(1.1, 0.55, 1.55); assertEquals(i1, i2); } /** * This class is immutable. */ @Test public void testCloning() { YWithXInterval i1 = new YWithXInterval(1.0, 0.5, 1.5); assertFalse(i1 instanceof Cloneable); } /** * Serialize an instance, restore it, and check for equality. */ @Test public void testSerialization() { YWithXInterval i1 = new YWithXInterval(1.0, 0.5, 1.5); YWithXInterval i2 = TestUtils.serialised(i1); assertEquals(i1, i2); } } jfree-jfreechart-cb8ff67/svg/000077500000000000000000000000001463604235500162365ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/svg/EQTBarChartDemo1.html000066400000000000000000000506331463604235500220610ustar00rootroot00000000000000 EQTBarChartDemo1

EQTBarChartDemo1

Click on an item in the chart or just hover and look at the tooltip (the reference is a string in JSON format that should contain enough information to identify the chart element):

Performance: JFreeSVG vs BatikJFreeSVGBatikTime to generate 1000 charts in SVG format (lower bars = better performance)Warm-upTest02,0004,0006,0008,00010,00012,00014,00016,00018,00020,00022,00024,000Milliseconds

jfree-jfreechart-cb8ff67/svg/js/000077500000000000000000000000001463604235500166525ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/svg/js/README.txt000066400000000000000000000007311463604235500203510ustar00rootroot00000000000000JavaScript Utilities -------------------- This directory contains some JavaScript utility code that can be used when exporting charts to SVG format with JFreeSVG. The build.sh script will build the library, but note that it requires the Google Closure Compiler jar file to be on the classpath (please edit the script accordingly). https://developers.google.com/closure/compiler/ Look in the '???' directory (open ???.html) to see a demo that uses this JavaScript code. jfree-jfreechart-cb8ff67/svg/js/build.sh000077500000000000000000000003411463604235500203060ustar00rootroot00000000000000#!/bin/bash java -jar ~/jars/compiler.jar --warning_level VERBOSE --compilation_level WHITESPACE_ONLY \ --js src/Utils.js \ --js src/KeyedValuesDataset.js \ --js src/KeyedValueLabels.js \ --js_output_file jfreechart_utils.jsjfree-jfreechart-cb8ff67/svg/js/jfreechart_utils.js000066400000000000000000000103061463604235500225450ustar00rootroot00000000000000var jfc;if(!jfc)jfc={};jfc.Utils={};jfc.Utils.makeArrayOf=function(value,length){var arr=[],i=length;while(i--)arr[i]=value;return arr};jfc.Utils.findChartRef=function(element){var id=element.getAttribute("jfreesvg:ref");var found=false;var current=element;while(!found){current=current.parentNode;if(current!=null){id=current.getAttribute("jfreesvg:ref");found=id!=null}else found=true}return id}; jfc.Utils.findChartId=function(element){var id=null;var found=false;var current=element;while(!found){current=current.parentNode;if(current!=null){var ref=current.getAttribute("jfreesvg:ref");if(ref=="JFREECHART_TOP_LEVEL"){found=true;id=current.getAttribute("id")}}else found=true}return id};if(!jfc)jfc={};orsoncharts.KeyedValuesDataset=function(){if(!(this instanceof jfc.KeyedValuesDataset))return new orsoncharts.KeyedValuesDataset;this.data=[];this.listeners=[]};orsoncharts.KeyedValuesDataset.prototype.itemCount=function(){return this.data.length};orsoncharts.KeyedValuesDataset.prototype.isEmpty=function(){return this.data.length===0};orsoncharts.KeyedValuesDataset.prototype.key=function(index){return this.data[index][0]};orsoncharts.KeyedValuesDataset.prototype.keys=function(){return this.data.map(function(d){return d[0]})}; orsoncharts.KeyedValuesDataset.prototype.indexOf=function(sectionKey){var arrayLength=this.data.length;for(var i=0;i=0)this.listeners.splice(i,1)};orsoncharts.KeyedValuesDataset.prototype.notifyListeners=function(){};orsoncharts.KeyedValuesDataset.prototype.add=function(sectionKey,value){this.data.push([sectionKey,value])}; orsoncharts.KeyedValuesDataset.prototype.remove=function(sectionKey){if(!sectionKey)throw new Error("The 'sectionKey' must be defined.");var i=this.indexOf(sectionKey);if(i<0)throw new Error("The sectionKey '"+sectionKey.toString()+"' is not recognised.");this.data.splice(i,1)};orsoncharts.KeyedValuesDataset.prototype.dataFromJSON=function(jsonStr){this.data=JSON.parse(jsonStr);this.notifyListeners()}; orsoncharts.KeyedValuesDataset.prototype.removeByIndex=function(itemIndex){this.data.splice(itemIndex,1)};orsoncharts.KeyedValuesDataset.prototype.totalForDataset=function(dataset){var total=0;var itemCount=dataset.itemCount();for(var i=0;i= 0) { this.listeners.splice(i, 1); } }; // Notifies all registered listeners that there has been a change to this dataset orsoncharts.KeyedValuesDataset.prototype.notifyListeners = function() { // TODO: call each listenerMethod }; // adds the specified (key, value) pair to the dataset or, if the key exists // already, updates the value orsoncharts.KeyedValuesDataset.prototype.add = function(sectionKey, value) { this.data.push([sectionKey, value]); }; // removes the item with the specified key orsoncharts.KeyedValuesDataset.prototype.remove = function(sectionKey) { if (!sectionKey) throw new Error("The 'sectionKey' must be defined."); var i = this.indexOf(sectionKey); if (i < 0) throw new Error("The sectionKey '" + sectionKey.toString() + "' is not recognised."); this.data.splice(i, 1); }; // sets the data array based on the supplied JSON string orsoncharts.KeyedValuesDataset.prototype.dataFromJSON = function(jsonStr) { this.data = JSON.parse(jsonStr); this.notifyListeners(); }; orsoncharts.KeyedValuesDataset.prototype.removeByIndex = function(itemIndex) { this.data.splice(itemIndex, 1); }; // returns the total of all non-null values for the specified dataset orsoncharts.KeyedValuesDataset.prototype.totalForDataset = function(dataset) { var total = 0.0; var itemCount = dataset.itemCount(); for (var i = 0; i < itemCount; i++) { var v = dataset.valueByIndex(i); if (v) { total = total + v; } } return total; }; // returns the minimum value for the specified dataset orsoncharts.KeyedValuesDataset.prototype.minForDataset = function(dataset) { var min = null; var itemCount = dataset.itemCount(); for (var i = 0; i < itemCount; i++) { var v = dataset.valueByIndex(i); if (v) { if (min) { min = Math.min(min, v); } else { min = v; } } } return min; }; // returns the maximum value for the specified dataset orsoncharts.KeyedValuesDataset.prototype.maxForDataset = function(dataset) { var max = null; var itemCount = dataset.itemCount(); for (var i = 0; i < itemCount; i++) { var v = dataset.valueByIndex(i); if (v) { if (max) { max = Math.max(max, v); } else { max = v; } } } return max; }; // returns the total of all values in this dataset (ignoring null values) orsoncharts.KeyedValuesDataset.prototype.total = function() { return this.totalForDataset(this); }; // returns the minimum value in this dataset (ignoring null values) orsoncharts.KeyedValuesDataset.prototype.min = function() { return this.minForDataset(this); }; // returns the maximum value in this dataset (ignoring null values) orsoncharts.KeyedValuesDataset.prototype.max = function() { return this.maxForDataset(this); }; jfree-jfreechart-cb8ff67/svg/js/src/Utils.js000066400000000000000000000023601463604235500211000ustar00rootroot00000000000000/* * Copyright (C) 2014-present, by David Gilbert */ var jfc; if (!jfc) jfc = {}; jfc.Utils = {}; jfc.Utils.makeArrayOf = function(value, length) { var arr = [], i = length; while (i--) { arr[i] = value; } return arr; }; // returns the chart entity reference for this element jfc.Utils.findChartRef = function(element) { var id = element.getAttribute("jfreesvg:ref"); var found = false; var current = element; while (!found) { current = current.parentNode; if (current != null) { id = current.getAttribute("jfreesvg:ref"); found = (id != null); } else { found = true; } } return id; } // find the chart id by finding the group that is written for the entire chart jfc.Utils.findChartId = function(element) { var id = null; var found = false; var current = element; while (!found) { current = current.parentNode; if (current != null) { var ref = current.getAttribute("jfreesvg:ref"); if (ref == 'JFREECHART_TOP_LEVEL') { found = true; id = current.getAttribute("id"); } } else { found = true; } } return id; }jfree-jfreechart-cb8ff67/svg/lib/000077500000000000000000000000001463604235500170045ustar00rootroot00000000000000jfree-jfreechart-cb8ff67/svg/lib/jfreechart_utils.js000066400000000000000000000103061463604235500226770ustar00rootroot00000000000000var jfc;if(!jfc)jfc={};jfc.Utils={};jfc.Utils.makeArrayOf=function(value,length){var arr=[],i=length;while(i--)arr[i]=value;return arr};jfc.Utils.findChartRef=function(element){var id=element.getAttribute("jfreesvg:ref");var found=false;var current=element;while(!found){current=current.parentNode;if(current!=null){id=current.getAttribute("jfreesvg:ref");found=id!=null}else found=true}return id}; jfc.Utils.findChartId=function(element){var id=null;var found=false;var current=element;while(!found){current=current.parentNode;if(current!=null){var ref=current.getAttribute("jfreesvg:ref");if(ref=="JFREECHART_TOP_LEVEL"){found=true;id=current.getAttribute("id")}}else found=true}return id};if(!jfc)jfc={};orsoncharts.KeyedValuesDataset=function(){if(!(this instanceof jfc.KeyedValuesDataset))return new orsoncharts.KeyedValuesDataset;this.data=[];this.listeners=[]};orsoncharts.KeyedValuesDataset.prototype.itemCount=function(){return this.data.length};orsoncharts.KeyedValuesDataset.prototype.isEmpty=function(){return this.data.length===0};orsoncharts.KeyedValuesDataset.prototype.key=function(index){return this.data[index][0]};orsoncharts.KeyedValuesDataset.prototype.keys=function(){return this.data.map(function(d){return d[0]})}; orsoncharts.KeyedValuesDataset.prototype.indexOf=function(sectionKey){var arrayLength=this.data.length;for(var i=0;i=0)this.listeners.splice(i,1)};orsoncharts.KeyedValuesDataset.prototype.notifyListeners=function(){};orsoncharts.KeyedValuesDataset.prototype.add=function(sectionKey,value){this.data.push([sectionKey,value])}; orsoncharts.KeyedValuesDataset.prototype.remove=function(sectionKey){if(!sectionKey)throw new Error("The 'sectionKey' must be defined.");var i=this.indexOf(sectionKey);if(i<0)throw new Error("The sectionKey '"+sectionKey.toString()+"' is not recognised.");this.data.splice(i,1)};orsoncharts.KeyedValuesDataset.prototype.dataFromJSON=function(jsonStr){this.data=JSON.parse(jsonStr);this.notifyListeners()}; orsoncharts.KeyedValuesDataset.prototype.removeByIndex=function(itemIndex){this.data.splice(itemIndex,1)};orsoncharts.KeyedValuesDataset.prototype.totalForDataset=function(dataset){var total=0;var itemCount=dataset.itemCount();for(var i=0;i